Merge changes from topic "scdocs"

* changes:
  Use AVsyncId typedef in hwui.
  Use AVsyncId typedef.
diff --git a/Android.bp b/Android.bp
index cf3c017..83878aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -62,12 +62,8 @@
         "SPDX-license-identifier-Apache-2.0",
         "SPDX-license-identifier-BSD",
         "SPDX-license-identifier-CC-BY",
-        "SPDX-license-identifier-CPL-1.0",
-        "SPDX-license-identifier-GPL",
-        "SPDX-license-identifier-GPL-2.0",
         "SPDX-license-identifier-MIT",
         "SPDX-license-identifier-Unicode-DFS",
-        "SPDX-license-identifier-W3C",
         "legacy_unencumbered",
     ],
     license_text: [
@@ -81,6 +77,7 @@
         // Java/AIDL sources under frameworks/base
         ":framework-annotations",
         ":framework-blobstore-sources",
+        ":framework-bluetooth-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready
         ":framework-connectivity-tiramisu-sources",
         ":framework-core-sources",
         ":framework-drm-sources",
@@ -93,7 +90,7 @@
         ":framework-mca-effect-sources",
         ":framework-mca-filterfw-sources",
         ":framework-mca-filterpacks-sources",
-        ":framework-media-sources",
+        ":framework-media-non-updatable-sources",
         ":framework-mms-sources",
         ":framework-omapi-sources",
         ":framework-opengl-sources",
@@ -155,37 +152,6 @@
 }
 
 java_library_with_nonpublic_deps {
-    name: "framework-updatable-stubs-module_libs_api",
-    static_libs: [
-        "android.net.ipsec.ike.stubs.module_lib",
-        "framework-appsearch.stubs.module_lib",
-        "framework-connectivity.stubs.module_lib",
-        "framework-connectivity-tiramisu.stubs.module_lib",
-        "framework-graphics.stubs.module_lib",
-        "framework-media.stubs.module_lib",
-        "framework-mediaprovider.stubs.module_lib",
-        "framework-permission.stubs.module_lib",
-        "framework-permission-s.stubs.module_lib",
-        "framework-scheduling.stubs.module_lib",
-        "framework-sdkextensions.stubs.module_lib",
-        "framework-statsd.stubs.module_lib",
-        "framework-supplementalprocess.stubs.module_lib",
-        "framework-tethering.stubs.module_lib",
-        "framework-uwb.stubs.module_lib",
-        "framework-wifi.stubs.module_lib",
-    ],
-    soong_config_variables: {
-        include_nonpublic_framework_api: {
-            static_libs: [
-                "framework-supplementalapi.stubs.module_lib",
-            ],
-        },
-    },
-    sdk_version: "module_current",
-    visibility: ["//visibility:private"],
-}
-
-java_library_with_nonpublic_deps {
     name: "framework-all",
     installable: false,
     static_libs: [
@@ -203,6 +169,7 @@
         "framework-statsd.impl",
         "framework-supplementalprocess.impl",
         "framework-tethering.impl",
+        "framework-nearby.impl",
         "framework-uwb.impl",
         "framework-wifi.impl",
         "updatable-media",
@@ -210,7 +177,7 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
-                "framework-supplementalapi.stubs.module_lib",
+                "framework-supplementalapi.impl",
             ],
         },
     },
@@ -464,7 +431,6 @@
     name: "framework-ike-shared-srcs",
     visibility: ["//packages/modules/IPsec"],
     srcs: [
-        "core/java/android/net/annotations/PolicyDirection.java",
         "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
@@ -572,7 +538,9 @@
     visibility: ["//visibility:private"],
 }
 
-// These defaults are used for both the jar stubs and the doc stubs.
+// Defaults for all stubs that include the non-updatable framework. These defaults do not include
+// module symbols, so will not compile correctly on their own. Users must add module APIs to the
+// classpath (or sources) somehow.
 stubs_defaults {
     name: "android-non-updatable-stubs-defaults",
     srcs: [":android-non-updatable-stub-sources"],
@@ -580,17 +548,14 @@
     system_modules: "none",
     java_version: "1.8",
     arg_files: ["core/res/AndroidManifest.xml"],
-    // TODO(b/147699819): remove below aidl includes.
     aidl: {
         local_include_dirs: [
-            "apex/media/aidl/stable",
             "media/aidl",
             "telephony/java",
         ],
         include_dirs: [
             "frameworks/av/aidl",
             "frameworks/native/libs/permission/aidl",
-            "packages/modules/Connectivity/framework/aidl-export",
         ],
     },
     // These are libs from framework-internal-utils that are required (i.e. being referenced)
@@ -610,6 +575,30 @@
         "android.hardware.usb.gadget-V1.0-java",
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
+    ],
+    filter_packages: packages_to_document,
+    high_mem: true, // Lots of sources => high memory use, see b/170701554
+    installable: false,
+    annotations_enabled: true,
+    previous_api: ":android.api.public.latest",
+    merge_annotations_dirs: ["metalava-manual"],
+    defaults_visibility: ["//visibility:private"],
+    visibility: ["//frameworks/base/api"],
+}
+
+// Defaults with module APIs in the classpath (mostly from prebuilts).
+// Suitable for compiling android-non-updatable.
+stubs_defaults {
+    name: "module-classpath-stubs-defaults",
+    aidl: {
+        local_include_dirs: [
+            "apex/media/aidl/stable",
+        ],
+        include_dirs: [
+            "packages/modules/Connectivity/framework/aidl-export",
+        ],
+    },
+    libs: [
         "art.module.public.api",
         "sdk_module-lib_current_framework-tethering",
         // There are a few classes from modules used by the core that
@@ -620,14 +609,7 @@
         // NOTE: The below can be removed once the prebuilt stub contains IKE.
         "sdk_system_current_android.net.ipsec.ike",
     ],
-    filter_packages: packages_to_document,
-    high_mem: true, // Lots of sources => high memory use, see b/170701554
-    installable: false,
-    annotations_enabled: true,
-    previous_api: ":android.api.public.latest",
-    merge_annotations_dirs: ["metalava-manual"],
     defaults_visibility: ["//visibility:private"],
-    visibility: ["//frameworks/base/api"],
 }
 
 build = [
diff --git a/ApiDocs.bp b/ApiDocs.bp
index feb43d1..98d071e 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -57,12 +57,15 @@
 
 stubs_defaults {
     name: "android-non-updatable-doc-stubs-defaults",
-    defaults: ["android-non-updatable-stubs-defaults"],
+    defaults: [
+        "android-non-updatable-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     srcs: [
         // No longer part of the stubs, but are included in the docs.
-        "test-base/src/**/*.java",
-        "test-mock/src/**/*.java",
-        "test-runner/src/**/*.java",
+        ":android-test-base-sources",
+        ":android-test-mock-sources",
+        ":android-test-runner-sources",
     ],
     libs: framework_docs_only_libs,
     create_doc_stubs: true,
@@ -74,11 +77,6 @@
     srcs: [
         ":android-non-updatable-stub-sources",
 
-        // Module sources
-        ":art.module.public.api{.public.stubs.source}",
-        ":conscrypt.module.public.api{.public.stubs.source}",
-        ":i18n.module.public.api{.public.stubs.source}",
-
         // No longer part of the stubs, but are included in the docs.
         ":android-test-base-sources",
         ":android-test-mock-sources",
@@ -116,6 +114,10 @@
     name: "framework-doc-stubs-sources-default",
     defaults: ["framework-doc-stubs-default"],
     srcs: [
+        ":art.module.public.api{.public.stubs.source}",
+        ":conscrypt.module.public.api{.public.stubs.source}",
+        ":i18n.module.public.api{.public.stubs.source}",
+
         ":framework-appsearch-sources",
         ":framework-connectivity-sources",
         ":framework-connectivity-tiramisu-updatable-sources",
@@ -160,26 +162,8 @@
 droidstubs {
     name: "framework-doc-stubs",
     defaults: ["framework-doc-stubs-default"],
+    srcs: [":all-modules-public-stubs-source"],
     args: metalava_framework_docs_args,
-    srcs: [
-        ":android.net.ipsec.ike{.public.stubs.source}",
-        ":framework-appsearch{.public.stubs.source}",
-        ":framework-connectivity{.public.stubs.source}",
-        ":framework-connectivity-tiramisu{.public.stubs.source}",
-        ":framework-graphics{.public.stubs.source}",
-        ":framework-media{.public.stubs.source}",
-        ":framework-mediaprovider{.public.stubs.source}",
-        ":framework-nearby{.public.stubs.source}",
-        ":framework-permission{.public.stubs.source}",
-        ":framework-permission-s{.public.stubs.source}",
-        ":framework-scheduling{.public.stubs.source}",
-        ":framework-sdkextensions{.public.stubs.source}",
-        ":framework-statsd{.public.stubs.source}",
-        ":framework-supplementalprocess{.public.stubs.source}",
-        ":framework-tethering{.public.stubs.source}",
-        ":framework-uwb{.public.stubs.source}",
-        ":framework-wifi{.public.stubs.source}",
-    ],
     aidl: {
         local_include_dirs: [
             "apex/media/aidl/stable",
@@ -190,46 +174,6 @@
     },
 }
 
-// This produces the same annotations.zip as framework-doc-stubs, but by using
-// outputs from individual modules instead of all the source code.
-genrule {
-    name: "sdk-annotations.zip",
-    srcs: [
-        ":android-non-updatable-doc-stubs{.annotations.zip}",
-
-        // Conscrypt and i18n currently do not enable annotations
-        // ":conscrypt.module.public.api{.public.annotations.zip}",
-        // ":i18n.module.public.api{.public.annotations.zip}",
-
-        // Modules that enable annotations below
-        ":android.net.ipsec.ike{.public.annotations.zip}",
-        ":art.module.public.api{.public.annotations.zip}",
-        ":framework-appsearch{.public.annotations.zip}",
-        ":framework-connectivity{.public.annotations.zip}",
-        ":framework-connectivity-tiramisu{.public.annotations.zip}",
-        ":framework-graphics{.public.annotations.zip}",
-        ":framework-media{.public.annotations.zip}",
-        ":framework-mediaprovider{.public.annotations.zip}",
-        ":framework-nearby{.public.annotations.zip}",
-        ":framework-permission{.public.annotations.zip}",
-        ":framework-permission-s{.public.annotations.zip}",
-        ":framework-scheduling{.public.annotations.zip}",
-        ":framework-sdkextensions{.public.annotations.zip}",
-        ":framework-statsd{.public.annotations.zip}",
-        ":framework-supplementalprocess{.public.annotations.zip}",
-        ":framework-tethering{.public.annotations.zip}",
-        ":framework-uwb{.public.annotations.zip}",
-        ":framework-wifi{.public.annotations.zip}",
-    ],
-    out: ["annotations.zip"],
-    tools: [
-        "merge_annotation_zips",
-        "soong_zip",
-    ],
-    cmd: "$(location merge_annotation_zips) $(genDir)/out $(in) && " +
-        "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out",
-}
-
 /////////////////////////////////////////////////////////////////////
 // API docs are created from the generated stub source files
 // using droiddoc
diff --git a/METADATA b/METADATA
index 95577d8..5c3f89c 100644
--- a/METADATA
+++ b/METADATA
@@ -1,4 +1,3 @@
 third_party {
-  # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
-  license_type: RESTRICTED
+  license_type: RECIPROCAL
 }
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 1e16f94..8a15758 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -32,23 +32,15 @@
 }
 
 /////////////////////////////////////////////////////////////////////
-// Common metalava configs
-/////////////////////////////////////////////////////////////////////
-
-stubs_defaults {
-    name: "metalava-non-updatable-api-stubs-default",
-    defaults: ["android-non-updatable-stubs-defaults"],
-    api_levels_annotations_enabled: false,
-    defaults_visibility: ["//visibility:private"],
-}
-
-/////////////////////////////////////////////////////////////////////
 // These modules provide source files for the stub libraries
 /////////////////////////////////////////////////////////////////////
 
 droidstubs {
     name: "api-stubs-docs-non-updatable",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
+    defaults: [
+        "android-non-updatable-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     args: metalava_framework_docs_args,
     check_api: {
         current: {
@@ -97,7 +89,10 @@
 
 droidstubs {
     name: "system-api-stubs-docs-non-updatable",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
+    defaults: [
+        "android-non-updatable-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     args: metalava_framework_docs_args + priv_apps,
     check_api: {
         current: {
@@ -133,7 +128,10 @@
 
 droidstubs {
     name: "test-api-stubs-docs-non-updatable",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
+    defaults: [
+        "android-non-updatable-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     args: metalava_framework_docs_args + test + priv_apps_in_stubs,
     check_api: {
         current: {
@@ -175,7 +173,10 @@
 
 droidstubs {
     name: "module-lib-api-stubs-docs-non-updatable",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
+    defaults: [
+        "android-non-updatable-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
     check_api: {
         current: {
@@ -185,6 +186,7 @@
         last_released: {
             api_file: ":android-non-updatable.api.module-lib.latest",
             removed_api_file: ":android-non-updatable-removed.api.module-lib.latest",
+            baseline_file: ":android-non-updatable-incompatibilities.api.module-lib.latest",
         },
         api_lint: {
             enabled: true,
diff --git a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
deleted file mode 100644
index f9ddf9a..0000000
--- a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os
-
-import android.content.pm.PackageParser
-import android.content.pm.PackageParserCacheHelper.ReadHelper
-import android.content.pm.PackageParserCacheHelper.WriteHelper
-import android.content.pm.parsing.ParsingPackageImpl
-import android.content.pm.parsing.ParsingPackageRead
-import android.content.pm.parsing.ParsingPackageUtils
-import android.content.pm.parsing.result.ParseInput
-import android.content.pm.parsing.result.ParseTypeImpl
-import android.content.res.TypedArray
-import android.perftests.utils.BenchmarkState
-import android.perftests.utils.PerfStatusReporter
-import androidx.test.filters.LargeTest
-import com.android.internal.util.ConcurrentUtils
-import libcore.io.IoUtils
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import java.io.File
-import java.io.FileOutputStream
-import java.util.concurrent.ArrayBlockingQueue
-import java.util.concurrent.TimeUnit
-
-@LargeTest
-@RunWith(Parameterized::class)
-class PackageParsingPerfTest {
-
-    companion object {
-        private const val PARALLEL_QUEUE_CAPACITY = 10
-        private const val PARALLEL_MAX_THREADS = 4
-
-        private const val QUEUE_POLL_TIMEOUT_SECONDS = 5L
-
-        // TODO: Replace this with core version of SYSTEM_PARTITIONS
-        val FOLDERS_TO_TEST = listOf(
-            Environment.getRootDirectory(),
-            Environment.getVendorDirectory(),
-            Environment.getOdmDirectory(),
-            Environment.getOemDirectory(),
-            Environment.getOemDirectory(),
-            Environment.getSystemExtDirectory()
-        )
-
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun parameters(): Array<Params> {
-            val apks = FOLDERS_TO_TEST
-                .filter(File::exists)
-                .map(File::walkTopDown)
-                .flatMap(Sequence<File>::asIterable)
-                .filter { it.name.endsWith(".apk") }
-
-            return arrayOf(
-                Params(1, apks) { ParallelParser1(it?.let(::PackageCacher1)) },
-                Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) }
-            )
-        }
-
-        data class Params(
-            val version: Int,
-            val apks: List<File>,
-            val cacheDirToParser: (File?) -> ParallelParser<*>
-        ) {
-            // For test name formatting
-            override fun toString() = "v$version"
-        }
-    }
-
-    @get:Rule
-    var perfStatusReporter = PerfStatusReporter()
-
-    @get:Rule
-    var testFolder = TemporaryFolder()
-
-    @Parameterized.Parameter(0)
-    lateinit var params: Params
-
-    private val state: BenchmarkState get() = perfStatusReporter.benchmarkState
-    private val apks: List<File> get() = params.apks
-
-    private fun safeParse(parser: ParallelParser<*>, file: File) {
-        try {
-            parser.parse(file)
-        } catch (e: Exception) {
-            // ignore
-        }
-    }
-
-    @Test
-    fun sequentialNoCache() {
-        params.cacheDirToParser(null).use { parser ->
-            while (state.keepRunning()) {
-                apks.forEach {
-                    safeParse(parser, it)
-                }
-            }
-        }
-    }
-
-    @Test
-    fun sequentialCached() {
-        params.cacheDirToParser(testFolder.newFolder()).use { parser ->
-            // Fill the cache
-            apks.forEach { safeParse(parser, it) }
-
-            while (state.keepRunning()) {
-                apks.forEach { safeParse(parser, it) }
-            }
-        }
-    }
-
-    @Test
-    fun parallelNoCache() {
-        params.cacheDirToParser(null).use { parser ->
-            while (state.keepRunning()) {
-                apks.forEach { parser.submit(it) }
-                repeat(apks.size) { parser.take() }
-            }
-        }
-    }
-
-    @Test
-    fun parallelCached() {
-        params.cacheDirToParser(testFolder.newFolder()).use { parser ->
-            // Fill the cache
-            apks.forEach { safeParse(parser, it) }
-
-            while (state.keepRunning()) {
-                apks.forEach { parser.submit(it) }
-                repeat(apks.size) { parser.take() }
-            }
-        }
-    }
-
-    abstract class ParallelParser<PackageType : Parcelable>(
-        private val cacher: PackageCacher<PackageType>? = null
-    ) : AutoCloseable {
-        private val queue = ArrayBlockingQueue<Any>(PARALLEL_QUEUE_CAPACITY)
-        private val service = ConcurrentUtils.newFixedThreadPool(
-            PARALLEL_MAX_THREADS, "package-parsing-test",
-            Process.THREAD_PRIORITY_FOREGROUND)
-
-        fun submit(file: File) {
-                service.submit {
-                    try {
-                        queue.put(parse(file))
-                    } catch (e: Exception) {
-                        queue.put(e)
-                    }
-                }
-        }
-
-        fun take() = queue.poll(QUEUE_POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
-
-        override fun close() {
-            service.shutdownNow()
-        }
-
-        fun parse(file: File) = cacher?.getCachedResult(file)
-            ?: parseImpl(file).also { cacher?.cacheResult(file, it) }
-
-        protected abstract fun parseImpl(file: File): PackageType
-    }
-
-    class ParallelParser1(private val cacher: PackageCacher1? = null)
-        : ParallelParser<PackageParser.Package>(cacher) {
-        val parser = PackageParser().apply {
-            setCallback { true }
-        }
-
-        override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null)
-    }
-
-    class ParallelParser2(cacher: PackageCacher2? = null)
-        : ParallelParser<ParsingPackageImpl>(cacher) {
-        val input = ThreadLocal.withInitial {
-            // For testing, just disable enforcement to avoid hooking up to compat framework
-            ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
-        }
-        val parser = ParsingPackageUtils(false, null, null, emptyList(),
-            object : ParsingPackageUtils.Callback {
-                override fun hasFeature(feature: String) = true
-
-                override fun startParsingPackage(
-                    packageName: String,
-                    baseApkPath: String,
-                    path: String,
-                    manifestArray: TypedArray,
-                    isCoreApp: Boolean
-                ) = ParsingPackageImpl(packageName, baseApkPath, path, manifestArray)
-            })
-
-        override fun parseImpl(file: File) =
-                parser.parsePackage(input.get()!!.reset(), file, 0).result
-                        as ParsingPackageImpl
-    }
-
-    abstract class PackageCacher<PackageType : Parcelable>(private val cacheDir: File) {
-
-        fun getCachedResult(file: File): PackageType? {
-            val cacheFile = File(cacheDir, file.name)
-            if (!cacheFile.exists()) {
-                return null
-            }
-
-            val bytes = IoUtils.readFileAsByteArray(cacheFile.absolutePath)
-            val parcel = Parcel.obtain().apply {
-                unmarshall(bytes, 0, bytes.size)
-                setDataPosition(0)
-            }
-            ReadHelper(parcel).apply { startAndInstall() }
-            return fromParcel(parcel).also {
-                parcel.recycle()
-            }
-        }
-
-        fun cacheResult(file: File, parsed: Parcelable) {
-            val cacheFile = File(cacheDir, file.name)
-            if (cacheFile.exists()) {
-                if (!cacheFile.delete()) {
-                    throw IllegalStateException("Unable to delete cache file: $cacheFile")
-                }
-            }
-            val cacheEntry = toCacheEntry(parsed)
-            return FileOutputStream(cacheFile).use { fos -> fos.write(cacheEntry) }
-        }
-
-        private fun toCacheEntry(pkg: Parcelable): ByteArray {
-            val parcel = Parcel.obtain()
-            val helper = WriteHelper(parcel)
-            pkg.writeToParcel(parcel, 0 /* flags */)
-            helper.finishAndUninstall()
-            return parcel.marshall().also {
-                parcel.recycle()
-            }
-        }
-
-        protected abstract fun fromParcel(parcel: Parcel): PackageType
-    }
-
-    /**
-     * Re-implementation of v1's cache, since that's gone in R+.
-     */
-    class PackageCacher1(cacheDir: File) : PackageCacher<PackageParser.Package>(cacheDir) {
-        override fun fromParcel(parcel: Parcel) = PackageParser.Package(parcel)
-    }
-
-    /**
-     * Re-implementation of the server side PackageCacher, as it's inaccessible here.
-     */
-    class PackageCacher2(cacheDir: File) : PackageCacher<ParsingPackageImpl>(cacheDir) {
-        override fun fromParcel(parcel: Parcel) = ParsingPackageImpl(parcel)
-    }
-}
diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
new file mode 100644
index 0000000..4cd9741
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
+import static android.view.MotionEvent.TOOL_TYPE_STYLUS;
+
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark tests for {@link HandwritingInitiator}
+ *
+ * Build/Install/Run:
+ * atest CorePerfTests:android.view.HandwritingInitiatorPerfTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class HandwritingInitiatorPerfTest {
+    private Context mContext;
+    private HandwritingInitiator mHandwritingInitiator;
+    private int mTouchSlop;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Before
+    public void setup() {
+        final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+        ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);
+        mTouchSlop = viewConfiguration.getScaledTouchSlop();
+        InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
+        mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager);
+    }
+
+    @Test
+    public void onTouchEvent_actionDown_toolTypeStylus() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final MotionEvent downEvent =
+                createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, 10, 10, 0);
+        final MotionEvent upEvent =
+                createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, 11, 11, 1);
+
+        while (state.keepRunning()) {
+            mHandwritingInitiator.onTouchEvent(downEvent);
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(upEvent);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void onTouchEvent_actionUp_toolTypeStylus() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final MotionEvent downEvent =
+                createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, 10, 10, 0);
+        final MotionEvent upEvent =
+                createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, 11, 11, 1);
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(downEvent);
+            state.resumeTiming();
+            mHandwritingInitiator.onTouchEvent(upEvent);
+        }
+    }
+
+    @Test
+    public void onTouchEvent_actionMove_toolTypeStylus() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final int initX = 10;
+        final int initY = 10;
+        final MotionEvent downEvent =
+                createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, initX, initY, 0);
+
+        final int x = initX + mTouchSlop;
+        final int y = initY + mTouchSlop;
+        final MotionEvent moveEvent =
+                createMotionEvent(ACTION_MOVE, TOOL_TYPE_STYLUS, x, y, 1);
+        final MotionEvent upEvent =
+                createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, x, y, 1);
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(downEvent);
+            state.resumeTiming();
+
+            mHandwritingInitiator.onTouchEvent(moveEvent);
+
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(upEvent);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void onTouchEvent_actionDown_toolTypeFinger() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final MotionEvent downEvent =
+                createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, 10, 10, 0);
+        final MotionEvent upEvent =
+                createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, 11, 11, 1);
+
+        while (state.keepRunning()) {
+            mHandwritingInitiator.onTouchEvent(downEvent);
+            mHandwritingInitiator.onTouchEvent(upEvent);
+        }
+    }
+
+    @Test
+    public void onTouchEvent_actionUp_toolTypeFinger() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final MotionEvent downEvent =
+                createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, 10, 10, 0);
+        final MotionEvent upEvent =
+                createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, 11, 11, 1);
+
+        while (state.keepRunning()) {
+            mHandwritingInitiator.onTouchEvent(downEvent);
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(upEvent);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void onTouchEvent_actionMove_toolTypeFinger() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final int initX = 10;
+        final int initY = 10;
+        final MotionEvent downEvent =
+                createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, initX, initY, 0);
+
+        final int x = initX + mTouchSlop;
+        final int y = initY + mTouchSlop;
+        final MotionEvent moveEvent =
+                createMotionEvent(ACTION_MOVE, TOOL_TYPE_FINGER, x, y, 1);
+        final MotionEvent upEvent =
+                createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, x, y, 1);
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(downEvent);
+            state.resumeTiming();
+
+            mHandwritingInitiator.onTouchEvent(moveEvent);
+
+            state.pauseTiming();
+            mHandwritingInitiator.onTouchEvent(upEvent);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void onInputConnectionCreated() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final View view = new View(mContext);
+        final EditorInfo editorInfo = new EditorInfo();
+        while (state.keepRunning()) {
+            mHandwritingInitiator.onInputConnectionCreated(view);
+            state.pauseTiming();
+            mHandwritingInitiator.onInputConnectionClosed(view);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void onInputConnectionClosed() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final View view = new View(mContext);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            mHandwritingInitiator.onInputConnectionCreated(view);
+            state.resumeTiming();
+            mHandwritingInitiator.onInputConnectionClosed(view);
+        }
+    }
+
+    private MotionEvent createMotionEvent(int action, int toolType, int x, int y, long eventTime) {
+        MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
+        properties[0].toolType = toolType;
+
+        MotionEvent.PointerCoords[] coords = MotionEvent.PointerCoords.createArray(1);
+        coords[0].x = x;
+        coords[0].y = y;
+
+        return MotionEvent.obtain(0 /* downTime */, eventTime /* eventTime */, action, 1,
+                properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */,
+                1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+                InputDevice.SOURCE_TOUCHSCREEN, 0 /* flags */);
+    }
+}
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index fc70219..81cec91 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -10,7 +10,10 @@
 android_test {
     name: "PackageManagerPerfTests",
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
 
     static_libs: [
         "platform-compat-test-rules",
@@ -21,6 +24,7 @@
         "apct-perftests-utils",
         "collector-device-lib-platform",
         "cts-install-lib-java",
+        "services.core",
     ],
 
     libs: ["android.test.base"],
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
new file mode 100644
index 0000000..e873514
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os
+
+import android.content.pm.PackageParser
+import android.content.pm.PackageParserCacheHelper.ReadHelper
+import android.content.pm.PackageParserCacheHelper.WriteHelper
+import android.content.pm.parsing.result.ParseInput
+import android.content.pm.parsing.result.ParseTypeImpl
+import android.content.res.TypedArray
+import android.perftests.utils.BenchmarkState
+import android.perftests.utils.PerfStatusReporter
+import androidx.test.filters.LargeTest
+import com.android.internal.util.ConcurrentUtils
+import com.android.server.pm.pkg.parsing.ParsingPackageImpl
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
+import libcore.io.IoUtils
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.io.File
+import java.io.FileOutputStream
+import java.util.concurrent.ArrayBlockingQueue
+import java.util.concurrent.TimeUnit
+
+@LargeTest
+@RunWith(Parameterized::class)
+public class PackageParsingPerfTest {
+
+    companion object {
+        private const val PARALLEL_QUEUE_CAPACITY = 10
+        private const val PARALLEL_MAX_THREADS = 4
+
+        private const val QUEUE_POLL_TIMEOUT_SECONDS = 5L
+
+        // TODO: Replace this with core version of SYSTEM_PARTITIONS
+        val FOLDERS_TO_TEST = listOf(
+            Environment.getRootDirectory(),
+            Environment.getVendorDirectory(),
+            Environment.getOdmDirectory(),
+            Environment.getOemDirectory(),
+            Environment.getOemDirectory(),
+            Environment.getSystemExtDirectory()
+        )
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun parameters(): Array<Params> {
+            val apks = FOLDERS_TO_TEST
+                .filter(File::exists)
+                .map(File::walkTopDown)
+                .flatMap(Sequence<File>::asIterable)
+                .filter { it.name.endsWith(".apk") }
+
+            return arrayOf(
+                Params(1, apks) { ParallelParser1(it?.let(::PackageCacher1)) },
+                Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) }
+            )
+        }
+
+        data class Params(
+            val version: Int,
+            val apks: List<File>,
+            val cacheDirToParser: (File?) -> ParallelParser<*>
+        ) {
+            // For test name formatting
+            override fun toString() = "v$version"
+        }
+    }
+
+    @get:Rule
+    var perfStatusReporter = PerfStatusReporter()
+
+    @get:Rule
+    var testFolder = TemporaryFolder()
+
+    @Parameterized.Parameter(0)
+    lateinit var params: Params
+
+    private val state: BenchmarkState get() = perfStatusReporter.benchmarkState
+    private val apks: List<File> get() = params.apks
+
+    private fun safeParse(parser: ParallelParser<*>, file: File) {
+        try {
+            parser.parse(file)
+        } catch (e: Exception) {
+            // ignore
+        }
+    }
+
+    @Test
+    fun sequentialNoCache() {
+        params.cacheDirToParser(null).use { parser ->
+            while (state.keepRunning()) {
+                apks.forEach {
+                    safeParse(parser, it)
+                }
+            }
+        }
+    }
+
+    @Test
+    fun sequentialCached() {
+        params.cacheDirToParser(testFolder.newFolder()).use { parser ->
+            // Fill the cache
+            apks.forEach { safeParse(parser, it) }
+
+            while (state.keepRunning()) {
+                apks.forEach { safeParse(parser, it) }
+            }
+        }
+    }
+
+    @Test
+    fun parallelNoCache() {
+        params.cacheDirToParser(null).use { parser ->
+            while (state.keepRunning()) {
+                apks.forEach { parser.submit(it) }
+                repeat(apks.size) { parser.take() }
+            }
+        }
+    }
+
+    @Test
+    fun parallelCached() {
+        params.cacheDirToParser(testFolder.newFolder()).use { parser ->
+            // Fill the cache
+            apks.forEach { safeParse(parser, it) }
+
+            while (state.keepRunning()) {
+                apks.forEach { parser.submit(it) }
+                repeat(apks.size) { parser.take() }
+            }
+        }
+    }
+
+    abstract class ParallelParser<PackageType : Parcelable>(
+        private val cacher: PackageCacher<PackageType>? = null
+    ) : AutoCloseable {
+        private val queue = ArrayBlockingQueue<Any>(PARALLEL_QUEUE_CAPACITY)
+        private val service = ConcurrentUtils.newFixedThreadPool(
+            PARALLEL_MAX_THREADS, "package-parsing-test",
+            Process.THREAD_PRIORITY_FOREGROUND)
+
+        fun submit(file: File) {
+                service.submit {
+                    try {
+                        queue.put(parse(file))
+                    } catch (e: Exception) {
+                        queue.put(e)
+                    }
+                }
+        }
+
+        fun take() = queue.poll(QUEUE_POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+        override fun close() {
+            service.shutdownNow()
+        }
+
+        fun parse(file: File) = cacher?.getCachedResult(file)
+            ?: parseImpl(file).also { cacher?.cacheResult(file, it) }
+
+        protected abstract fun parseImpl(file: File): PackageType
+    }
+
+    class ParallelParser1(private val cacher: PackageCacher1? = null)
+        : ParallelParser<PackageParser.Package>(cacher) {
+        val parser = PackageParser().apply {
+            setCallback { true }
+        }
+
+        override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null)
+    }
+
+    class ParallelParser2(cacher: PackageCacher2? = null)
+        : ParallelParser<ParsingPackageImpl>(cacher) {
+        val input = ThreadLocal.withInitial {
+            // For testing, just disable enforcement to avoid hooking up to compat framework
+            ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
+        }
+        val parser = ParsingPackageUtils(false,
+            null,
+            null,
+            emptyList(),
+            object :
+                ParsingPackageUtils.Callback {
+                override fun hasFeature(feature: String) = true
+
+                override fun startParsingPackage(
+                    packageName: String,
+                    baseApkPath: String,
+                    path: String,
+                    manifestArray: TypedArray,
+                    isCoreApp: Boolean
+                ) = ParsingPackageImpl(
+                    packageName,
+                    baseApkPath,
+                    path,
+                    manifestArray
+                )
+            })
+
+        override fun parseImpl(file: File) =
+                parser.parsePackage(input.get()!!.reset(), file, 0).result
+                        as ParsingPackageImpl
+    }
+
+    abstract class PackageCacher<PackageType : Parcelable>(private val cacheDir: File) {
+
+        fun getCachedResult(file: File): PackageType? {
+            val cacheFile = File(cacheDir, file.name)
+            if (!cacheFile.exists()) {
+                return null
+            }
+
+            val bytes = IoUtils.readFileAsByteArray(cacheFile.absolutePath)
+            val parcel = Parcel.obtain().apply {
+                unmarshall(bytes, 0, bytes.size)
+                setDataPosition(0)
+            }
+            ReadHelper(parcel).apply { startAndInstall() }
+            return fromParcel(parcel).also {
+                parcel.recycle()
+            }
+        }
+
+        fun cacheResult(file: File, parsed: Parcelable) {
+            val cacheFile = File(cacheDir, file.name)
+            if (cacheFile.exists()) {
+                if (!cacheFile.delete()) {
+                    throw IllegalStateException("Unable to delete cache file: $cacheFile")
+                }
+            }
+            val cacheEntry = toCacheEntry(parsed)
+            return FileOutputStream(cacheFile).use { fos -> fos.write(cacheEntry) }
+        }
+
+        private fun toCacheEntry(pkg: Parcelable): ByteArray {
+            val parcel = Parcel.obtain()
+            val helper = WriteHelper(parcel)
+            pkg.writeToParcel(parcel, 0 /* flags */)
+            helper.finishAndUninstall()
+            return parcel.marshall().also {
+                parcel.recycle()
+            }
+        }
+
+        protected abstract fun fromParcel(parcel: Parcel): PackageType
+    }
+
+    /**
+     * Re-implementation of v1's cache, since that's gone in R+.
+     */
+    class PackageCacher1(cacheDir: File) : PackageCacher<PackageParser.Package>(cacheDir) {
+        override fun fromParcel(parcel: Parcel) = PackageParser.Package(parcel)
+    }
+
+    /**
+     * Re-implementation of the server side PackageCacher, as it's inaccessible here.
+     */
+    class PackageCacher2(cacheDir: File) : PackageCacher<ParsingPackageImpl>(cacheDir) {
+        override fun fromParcel(parcel: Parcel) =
+            ParsingPackageImpl(parcel)
+    }
+}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index a1a46af..9fb1227 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -235,7 +235,6 @@
     public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
     /**
      * All Bluetooth broadcasts.
-     * @hide
      */
     public static final int REASON_BLUETOOTH_BROADCAST = 203;
     /**
@@ -259,6 +258,12 @@
      * @hide
      */
     public static final int REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = 207;
+    /**
+     * Broadcast {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}.
+     * @hide
+     */
+    public static final int REASON_ACTION_REFRESH_SAFETY_SOURCES = 208;
+
     /* Reason code range 300-399 are reserved for other internal reasons */
     /**
      * Device idle system allow list, including EXCEPT-IDLE
@@ -399,6 +404,7 @@
             REASON_TIME_CHANGED,
             REASON_LOCALE_CHANGED,
             REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
+            REASON_ACTION_REFRESH_SAFETY_SOURCES,
             REASON_SYSTEM_ALLOW_LISTED,
             REASON_ALARM_MANAGER_ALARM_CLOCK,
             REASON_ALARM_MANAGER_WHILE_IDLE,
@@ -682,6 +688,8 @@
                 return "LOCALE_CHANGED";
             case REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
                 return "REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
+            case REASON_ACTION_REFRESH_SAFETY_SOURCES:
+                return "REASON_ACTION_REFRESH_SAFETY_SOURCES";
             case REASON_SYSTEM_ALLOW_LISTED:
                 return "SYSTEM_ALLOW_LISTED";
             case REASON_ALARM_MANAGER_ALARM_CLOCK:
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 86d7a5a..12a8654 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -54,6 +54,8 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
@@ -250,8 +252,6 @@
      */
     private final List<RestrictingController> mRestrictiveControllers;
     /** Need direct access to this for testing. */
-    private final BatteryController mBatteryController;
-    /** Need direct access to this for testing. */
     private final StorageController mStorageController;
     /** Need directly for sending uid state changes */
     private final DeviceIdleJobsController mDeviceIdleJobsController;
@@ -268,6 +268,9 @@
      */
     private final List<JobRestriction> mJobRestrictions;
 
+    @GuardedBy("mLock")
+    private final BatteryStateTracker mBatteryStateTracker;
+
     @NonNull
     private final String mSystemGalleryPackage;
 
@@ -1553,7 +1556,7 @@
                     Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias);
                 }
                 for (int c = 0; c < mControllers.size(); ++c) {
-                    mControllers.get(c).onUidBiasChangedLocked(uid, newBias);
+                    mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
                 }
             }
         }
@@ -1697,6 +1700,9 @@
         // Initialize the job store and set up any persisted jobs
         mJobs = JobStore.initAndGet(this);
 
+        mBatteryStateTracker = new BatteryStateTracker();
+        mBatteryStateTracker.startTracking();
+
         // Create the controllers.
         mControllers = new ArrayList<StateController>();
         final ConnectivityController connectivityController = new ConnectivityController(this);
@@ -1704,8 +1710,8 @@
         mControllers.add(new TimeController(this));
         final IdleController idleController = new IdleController(this);
         mControllers.add(idleController);
-        mBatteryController = new BatteryController(this);
-        mControllers.add(mBatteryController);
+        final BatteryController batteryController = new BatteryController(this);
+        mControllers.add(batteryController);
         mStorageController = new StorageController(this);
         mControllers.add(mStorageController);
         final BackgroundJobsController backgroundJobsController =
@@ -1725,7 +1731,7 @@
         mControllers.add(mTareController);
 
         mRestrictiveControllers = new ArrayList<>();
-        mRestrictiveControllers.add(mBatteryController);
+        mRestrictiveControllers.add(batteryController);
         mRestrictiveControllers.add(connectivityController);
         mRestrictiveControllers.add(idleController);
 
@@ -2818,6 +2824,129 @@
         return adjustJobBias(bias, job);
     }
 
+    private final class BatteryStateTracker extends BroadcastReceiver {
+        /**
+         * Track whether we're "charging", where charging means that we're ready to commit to
+         * doing work.
+         */
+        private boolean mCharging;
+        /** Keep track of whether the battery is charged enough that we want to do work. */
+        private boolean mBatteryNotLow;
+        /** Sequence number of last broadcast. */
+        private int mLastBatterySeq = -1;
+
+        private BroadcastReceiver mMonitor;
+
+        BatteryStateTracker() {
+        }
+
+        public void startTracking() {
+            IntentFilter filter = new IntentFilter();
+
+            // Battery health.
+            filter.addAction(Intent.ACTION_BATTERY_LOW);
+            filter.addAction(Intent.ACTION_BATTERY_OKAY);
+            // Charging/not charging.
+            filter.addAction(BatteryManager.ACTION_CHARGING);
+            filter.addAction(BatteryManager.ACTION_DISCHARGING);
+            getTestableContext().registerReceiver(this, filter);
+
+            // Initialise tracker state.
+            BatteryManagerInternal batteryManagerInternal =
+                    LocalServices.getService(BatteryManagerInternal.class);
+            mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow();
+            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+        }
+
+        public void setMonitorBatteryLocked(boolean enabled) {
+            if (enabled) {
+                if (mMonitor == null) {
+                    mMonitor = new BroadcastReceiver() {
+                        @Override
+                        public void onReceive(Context context, Intent intent) {
+                            onReceiveInternal(intent);
+                        }
+                    };
+                    IntentFilter filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+                    getTestableContext().registerReceiver(mMonitor, filter);
+                }
+            } else if (mMonitor != null) {
+                getTestableContext().unregisterReceiver(mMonitor);
+                mMonitor = null;
+            }
+        }
+
+        public boolean isCharging() {
+            return mCharging;
+        }
+
+        public boolean isBatteryNotLow() {
+            return mBatteryNotLow;
+        }
+
+        public boolean isMonitoring() {
+            return mMonitor != null;
+        }
+
+        public int getSeq() {
+            return mLastBatterySeq;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            onReceiveInternal(intent);
+        }
+
+        @VisibleForTesting
+        public void onReceiveInternal(Intent intent) {
+            synchronized (mLock) {
+                final String action = intent.getAction();
+                boolean changed = false;
+                if (Intent.ACTION_BATTERY_LOW.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Battery life too low @ " + sElapsedRealtimeClock.millis());
+                    }
+                    if (mBatteryNotLow) {
+                        mBatteryNotLow = false;
+                        changed = true;
+                    }
+                } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Battery high enough @ " + sElapsedRealtimeClock.millis());
+                    }
+                    if (!mBatteryNotLow) {
+                        mBatteryNotLow = true;
+                        changed = true;
+                    }
+                } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis());
+                    }
+                    if (!mCharging) {
+                        mCharging = true;
+                        changed = true;
+                    }
+                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Disconnected from power @ " + sElapsedRealtimeClock.millis());
+                    }
+                    if (mCharging) {
+                        mCharging = false;
+                        changed = true;
+                    }
+                }
+                mLastBatterySeq =
+                        intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq);
+                if (changed) {
+                    for (int c = mControllers.size() - 1; c >= 0; --c) {
+                        mControllers.get(c).onBatteryStateChangedLocked();
+                    }
+                }
+            }
+        }
+    }
+
     final class LocalService implements JobSchedulerInternal {
 
         /**
@@ -3417,29 +3546,27 @@
 
     void setMonitorBattery(boolean enabled) {
         synchronized (mLock) {
-            if (mBatteryController != null) {
-                mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
-            }
+            mBatteryStateTracker.setMonitorBatteryLocked(enabled);
         }
     }
 
     int getBatterySeq() {
         synchronized (mLock) {
-            return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
+            return mBatteryStateTracker.getSeq();
         }
     }
 
-    boolean getBatteryCharging() {
+    /** Return {@code true} if the device is currently charging. */
+    public boolean isBatteryCharging() {
         synchronized (mLock) {
-            return mBatteryController != null
-                    ? mBatteryController.getTracker().isOnStablePower() : false;
+            return mBatteryStateTracker.isCharging();
         }
     }
 
-    boolean getBatteryNotLow() {
+    /** Return {@code true} if the battery is not low. */
+    public boolean isBatteryNotLow() {
         synchronized (mLock) {
-            return mBatteryController != null
-                    ? mBatteryController.getTracker().isBatteryNotLow() : false;
+            return mBatteryStateTracker.isBatteryNotLow();
         }
     }
 
@@ -3614,6 +3741,16 @@
             mQuotaTracker.dump(pw);
             pw.println();
 
+            pw.print("Battery charging: ");
+            pw.println(mBatteryStateTracker.isCharging());
+            pw.print("Battery not low: ");
+            pw.println(mBatteryStateTracker.isBatteryNotLow());
+            if (mBatteryStateTracker.isMonitoring()) {
+                pw.print("MONITORING: seq=");
+                pw.println(mBatteryStateTracker.getSeq());
+            }
+            pw.println();
+
             pw.println("Started users: " + Arrays.toString(mStartedUsers));
             pw.print("Registered ");
             pw.print(mJobs.size());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index cc20213..27268d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -293,13 +293,13 @@
     }
 
     private int getBatteryCharging(PrintWriter pw) {
-        boolean val = mInternal.getBatteryCharging();
+        boolean val = mInternal.isBatteryCharging();
         pw.println(val);
         return 0;
     }
 
     private int getBatteryNotLow(PrintWriter pw) {
-        boolean val = mInternal.getBatteryNotLow();
+        boolean val = mInternal.isBatteryNotLow();
         pw.println(val);
         return 0;
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 657f470..63781ae 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -18,6 +18,8 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
+import android.app.job.JobInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,7 +33,8 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.JobSchedulerBackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
@@ -48,18 +51,26 @@
     private static final boolean DEBUG = JobSchedulerService.DEBUG
             || Log.isLoggable(TAG, Log.DEBUG);
 
+    @GuardedBy("mLock")
     private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
-    private ChargingTracker mChargeTracker;
+    /**
+     * List of jobs that started while the UID was in the TOP state.
+     */
+    @GuardedBy("mLock")
+    private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
 
-    @VisibleForTesting
-    public ChargingTracker getTracker() {
-        return mChargeTracker;
-    }
+    private final PowerTracker mPowerTracker;
+
+    /**
+     * Helper set to avoid too much GC churn from frequent calls to
+     * {@link #maybeReportNewChargingStateLocked()}.
+     */
+    private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
 
     public BatteryController(JobSchedulerService service) {
         super(service);
-        mChargeTracker = new ChargingTracker();
-        mChargeTracker.startTracking();
+        mPowerTracker = new PowerTracker();
+        mPowerTracker.startTracking();
     }
 
     @Override
@@ -68,9 +79,16 @@
             final long nowElapsed = sElapsedRealtimeClock.millis();
             mTrackedTasks.add(taskStatus);
             taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
-            taskStatus.setChargingConstraintSatisfied(nowElapsed, mChargeTracker.isOnStablePower());
-            taskStatus.setBatteryNotLowConstraintSatisfied(
-                    nowElapsed, mChargeTracker.isBatteryNotLow());
+            if (taskStatus.hasChargingConstraint()) {
+                if (hasTopExemptionLocked(taskStatus)) {
+                    taskStatus.setChargingConstraintSatisfied(nowElapsed,
+                            mPowerTracker.isPowerConnected());
+                } else {
+                    taskStatus.setChargingConstraintSatisfied(nowElapsed,
+                            mService.isBatteryCharging() && mService.isBatteryNotLow());
+                }
+            }
+            taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow());
         }
     }
 
@@ -80,9 +98,32 @@
     }
 
     @Override
+    @GuardedBy("mLock")
+    public void prepareForExecutionLocked(JobStatus jobStatus) {
+        if (DEBUG) {
+            Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+        }
+
+        final int uid = jobStatus.getSourceUid();
+        if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
+            if (DEBUG) {
+                Slog.d(TAG, jobStatus.toShortString() + " is top started job");
+            }
+            mTopStartedJobs.add(jobStatus);
+        }
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+        mTopStartedJobs.remove(jobStatus);
+    }
+
+    @Override
     public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
             mTrackedTasks.remove(taskStatus);
+            mTopStartedJobs.remove(taskStatus);
         }
     }
 
@@ -93,141 +134,127 @@
         }
     }
 
+    @Override
+    @GuardedBy("mLock")
+    public void onBatteryStateChangedLocked() {
+        // Update job bookkeeping out of band.
+        JobSchedulerBackgroundThread.getHandler().post(() -> {
+            synchronized (mLock) {
+                maybeReportNewChargingStateLocked();
+            }
+        });
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
+        if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) {
+            maybeReportNewChargingStateLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) {
+        return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
+                || mTopStartedJobs.contains(taskStatus);
+    }
+
+    @GuardedBy("mLock")
     private void maybeReportNewChargingStateLocked() {
-        final boolean stablePower = mChargeTracker.isOnStablePower();
-        final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
+        final boolean powerConnected = mPowerTracker.isPowerConnected();
+        final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow();
+        final boolean batteryNotLow = mService.isBatteryNotLow();
         if (DEBUG) {
-            Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
+            Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+                    + powerConnected + "/" + stablePower + "/" + batteryNotLow);
         }
         final long nowElapsed = sElapsedRealtimeClock.millis();
-        boolean reportChange = false;
         for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
             final JobStatus ts = mTrackedTasks.valueAt(i);
-            reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
-            reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
+            if (ts.hasChargingConstraint()) {
+                if (hasTopExemptionLocked(ts)
+                        && ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
+                    // If the job started while the app was on top or the app is currently on top,
+                    // let the job run as long as there's power connected, even if the device isn't
+                    // officially charging.
+                    // For user requested/initiated jobs, users may be confused when the task stops
+                    // running even though the device is plugged in.
+                    // Low priority jobs don't need to be exempted.
+                    if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) {
+                        mChangedJobs.add(ts);
+                    }
+                } else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) {
+                    mChangedJobs.add(ts);
+                }
+            }
+            if (ts.hasBatteryNotLowConstraint()
+                    && ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) {
+                mChangedJobs.add(ts);
+            }
         }
         if (stablePower || batteryNotLow) {
             // If one of our conditions has been satisfied, always schedule any newly ready jobs.
             mStateChangedListener.onRunJobNow(null);
-        } else if (reportChange) {
+        } else if (mChangedJobs.size() > 0) {
             // Otherwise, just let the job scheduler know the state has changed and take care of it
             // as it thinks is best.
-            mStateChangedListener.onControllerStateChanged(mTrackedTasks);
+            mStateChangedListener.onControllerStateChanged(mChangedJobs);
         }
+        mChangedJobs.clear();
     }
 
-    public final class ChargingTracker extends BroadcastReceiver {
+    private final class PowerTracker extends BroadcastReceiver {
         /**
-         * Track whether we're "charging", where charging means that we're ready to commit to
-         * doing work.
+         * Track whether there is power connected. It doesn't mean the device is charging.
+         * Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is
+         * charging.
          */
-        private boolean mCharging;
-        /** Keep track of whether the battery is charged enough that we want to do work. */
-        private boolean mBatteryHealthy;
-        /** Sequence number of last broadcast. */
-        private int mLastBatterySeq = -1;
+        private boolean mPowerConnected;
 
-        private BroadcastReceiver mMonitor;
-
-        public ChargingTracker() {
+        PowerTracker() {
         }
 
-        public void startTracking() {
+        void startTracking() {
             IntentFilter filter = new IntentFilter();
 
-            // Battery health.
-            filter.addAction(Intent.ACTION_BATTERY_LOW);
-            filter.addAction(Intent.ACTION_BATTERY_OKAY);
-            // Charging/not charging.
-            filter.addAction(BatteryManager.ACTION_CHARGING);
-            filter.addAction(BatteryManager.ACTION_DISCHARGING);
+            filter.addAction(Intent.ACTION_POWER_CONNECTED);
+            filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
             mContext.registerReceiver(this, filter);
 
-            // Initialise tracker state.
+            // Initialize tracker state.
             BatteryManagerInternal batteryManagerInternal =
                     LocalServices.getService(BatteryManagerInternal.class);
-            mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
-            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+            mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
         }
 
-        public void setMonitorBatteryLocked(boolean enabled) {
-            if (enabled) {
-                if (mMonitor == null) {
-                    mMonitor = new BroadcastReceiver() {
-                        @Override public void onReceive(Context context, Intent intent) {
-                            ChargingTracker.this.onReceive(context, intent);
-                        }
-                    };
-                    IntentFilter filter = new IntentFilter();
-                    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-                    mContext.registerReceiver(mMonitor, filter);
-                }
-            } else {
-                if (mMonitor != null) {
-                    mContext.unregisterReceiver(mMonitor);
-                    mMonitor = null;
-                }
-            }
-        }
-
-        public boolean isOnStablePower() {
-            return mCharging && mBatteryHealthy;
-        }
-
-        public boolean isBatteryNotLow() {
-            return mBatteryHealthy;
-        }
-
-        public boolean isMonitoring() {
-            return mMonitor != null;
-        }
-
-        public int getSeq() {
-            return mLastBatterySeq;
+        boolean isPowerConnected() {
+            return mPowerConnected;
         }
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            onReceiveInternal(intent);
-        }
-
-        @VisibleForTesting
-        public void onReceiveInternal(Intent intent) {
             synchronized (mLock) {
                 final String action = intent.getAction();
-                if (Intent.ACTION_BATTERY_LOW.equals(action)) {
+
+                if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Battery life too low to do work. @ "
-                                + sElapsedRealtimeClock.millis());
+                        Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis());
                     }
-                    // If we get this action, the battery is discharging => it isn't plugged in so
-                    // there's no work to cancel. We track this variable for the case where it is
-                    // charging, but hasn't been for long enough to be healthy.
-                    mBatteryHealthy = false;
-                    maybeReportNewChargingStateLocked();
-                } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
+                    if (mPowerConnected) {
+                        return;
+                    }
+                    mPowerConnected = true;
+                } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Battery life healthy enough to do work. @ "
-                                + sElapsedRealtimeClock.millis());
+                        Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis());
                     }
-                    mBatteryHealthy = true;
-                    maybeReportNewChargingStateLocked();
-                } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Received charging intent, fired @ "
-                                + sElapsedRealtimeClock.millis());
+                    if (!mPowerConnected) {
+                        return;
                     }
-                    mCharging = true;
-                    maybeReportNewChargingStateLocked();
-                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Disconnected from power.");
-                    }
-                    mCharging = false;
-                    maybeReportNewChargingStateLocked();
+                    mPowerConnected = false;
                 }
-                mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE,
-                        mLastBatterySeq);
+
+                maybeReportNewChargingStateLocked();
             }
         }
     }
@@ -235,14 +262,9 @@
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
-        pw.println("Stable power: " + mChargeTracker.isOnStablePower());
-        pw.println("Not low: " + mChargeTracker.isBatteryNotLow());
-
-        if (mChargeTracker.isMonitoring()) {
-            pw.print("MONITORING: seq=");
-            pw.println(mChargeTracker.getSeq());
-        }
-        pw.println();
+        pw.println("Power connected: " + mPowerTracker.isPowerConnected());
+        pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow()));
+        pw.println("Not low: " + mService.isBatteryNotLow());
 
         for (int i = 0; i < mTrackedTasks.size(); i++) {
             final JobStatus js = mTrackedTasks.valueAt(i);
@@ -264,14 +286,9 @@
         final long mToken = proto.start(StateControllerProto.BATTERY);
 
         proto.write(StateControllerProto.BatteryController.IS_ON_STABLE_POWER,
-                mChargeTracker.isOnStablePower());
+                mService.isBatteryCharging() && mService.isBatteryNotLow());
         proto.write(StateControllerProto.BatteryController.IS_BATTERY_NOT_LOW,
-                mChargeTracker.isBatteryNotLow());
-
-        proto.write(StateControllerProto.BatteryController.IS_MONITORING,
-                mChargeTracker.isMonitoring());
-        proto.write(StateControllerProto.BatteryController.LAST_BROADCAST_SEQUENCE_NUMBER,
-                mChargeTracker.getSeq());
+                mService.isBatteryNotLow());
 
         for (int i = 0; i < mTrackedTasks.size(); i++) {
             final JobStatus js = mTrackedTasks.valueAt(i);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 524d68f..c678755 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -25,18 +25,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.job.JobInfo;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -55,6 +49,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.JobSchedulerBackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
@@ -107,8 +102,6 @@
     private final ConnectivityManager mConnManager;
     private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
 
-    private final ChargingTracker mChargingTracker;
-
     /** List of tracked jobs keyed by source UID. */
     @GuardedBy("mLock")
     private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
@@ -237,9 +230,6 @@
         // network changes against the active network for each UID with jobs.
         final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
         mConnManager.registerNetworkCallback(request, mNetworkCallback);
-
-        mChargingTracker = new ChargingTracker();
-        mChargingTracker.startTracking();
     }
 
     @GuardedBy("mLock")
@@ -527,7 +517,7 @@
 
     @GuardedBy("mLock")
     @Override
-    public void onUidBiasChangedLocked(int uid, int newBias) {
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
         UidStats uidStats = mUidStats.get(uid);
         if (uidStats != null && uidStats.baseBias != newBias) {
             uidStats.baseBias = newBias;
@@ -535,6 +525,17 @@
         }
     }
 
+    @Override
+    @GuardedBy("mLock")
+    public void onBatteryStateChangedLocked() {
+        // Update job bookkeeping out of band to avoid blocking broadcast progress.
+        JobSchedulerBackgroundThread.getHandler().post(() -> {
+            synchronized (mLock) {
+                updateTrackedJobsLocked(-1, null);
+            }
+        });
+    }
+
     private boolean isUsable(NetworkCapabilities capabilities) {
         return capabilities != null
                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
@@ -591,7 +592,7 @@
         // Minimum chunk size isn't defined. Check using the estimated upload/download sizes.
 
         if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
-                && mChargingTracker.isCharging()) {
+                && mService.isBatteryCharging()) {
             // We're charging and on an unmetered network. We don't have to be as conservative about
             // making sure the job will run within its max execution time. Let's just hope the app
             // supports interruptible work.
@@ -1072,51 +1073,6 @@
         }
     }
 
-    private final class ChargingTracker extends BroadcastReceiver {
-        /**
-         * Track whether we're "charging", where charging means that we're ready to commit to
-         * doing work.
-         */
-        private boolean mCharging;
-
-        ChargingTracker() {}
-
-        public void startTracking() {
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(BatteryManager.ACTION_CHARGING);
-            filter.addAction(BatteryManager.ACTION_DISCHARGING);
-            mContext.registerReceiver(this, filter);
-
-            // Initialise tracker state.
-            final BatteryManagerInternal batteryManagerInternal =
-                    LocalServices.getService(BatteryManagerInternal.class);
-            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
-        }
-
-        public boolean isCharging() {
-            return mCharging;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mLock) {
-                final String action = intent.getAction();
-                if (BatteryManager.ACTION_CHARGING.equals(action)) {
-                    if (mCharging) {
-                        return;
-                    }
-                    mCharging = true;
-                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
-                    if (!mCharging) {
-                        return;
-                    }
-                    mCharging = false;
-                }
-                updateTrackedJobsLocked(-1, null);
-            }
-        }
-    }
-
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
         public void onAvailable(Network network) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 9749c80..428f2cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -40,7 +40,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArrayMap;
-import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
@@ -81,9 +80,6 @@
      */
     @GuardedBy("mLock")
     private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>();
-    /** Cached list of UIDs in the TOP state. */
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mTopUids = new SparseBooleanArray();
     private final ThresholdAlarmListener mThresholdAlarmListener;
 
     /**
@@ -186,15 +182,9 @@
 
     @GuardedBy("mLock")
     @Override
-    public void onUidBiasChangedLocked(int uid, int newBias) {
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
         final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP;
-        final boolean wasTop = mTopUids.get(uid);
-        if (isNowTop) {
-            mTopUids.put(uid, true);
-        } else {
-            // Delete entries of non-top apps so the set doesn't get too large.
-            mTopUids.delete(uid);
-        }
+        final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP;
         if (isNowTop != wasTop) {
             mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget();
         }
@@ -314,7 +304,8 @@
         //   3. The app is not open but has an active widget (we can't tell if a widget displays
         //      status/data, so this assumes the prefetch job is to update the data displayed on
         //      the widget).
-        final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid());
+        final boolean appIsOpen =
+                mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP;
         final boolean satisfied;
         if (!appIsOpen) {
             final int userId = jobStatus.getSourceUserId();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index c147ef8..65e1d49 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -39,15 +39,11 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -319,7 +315,6 @@
     private final SparseLongArray mTopAppGraceCache = new SparseLongArray();
 
     private final AlarmManager mAlarmManager;
-    private final ChargingTracker mChargeTracker;
     private final QcHandler mHandler;
     private final QcConstants mQcConstants;
 
@@ -542,8 +537,6 @@
             @NonNull ConnectivityController connectivityController) {
         super(service);
         mHandler = new QcHandler(mContext.getMainLooper());
-        mChargeTracker = new ChargingTracker();
-        mChargeTracker.startTracking();
         mAlarmManager = mContext.getSystemService(AlarmManager.class);
         mQcConstants = new QcConstants();
         mBackgroundJobsController = backgroundJobsController;
@@ -705,6 +698,11 @@
         mTopAppTrackers.delete(userId);
     }
 
+    @Override
+    public void onBatteryStateChangedLocked() {
+        handleNewChargingStateLocked();
+    }
+
     /** Drop all historical stats and stop tracking any active sessions for the specified app. */
     public void clearAppStatsLocked(int userId, @NonNull String packageName) {
         mTrackedJobs.delete(userId, packageName);
@@ -766,7 +764,7 @@
         if (!jobStatus.shouldTreatAsExpeditedJob()) {
             // If quota is currently "free", then the job can run for the full amount of time,
             // regardless of bucket (hence using charging instead of isQuotaFreeLocked()).
-            if (mChargeTracker.isChargingLocked()
+            if (mService.isBatteryCharging()
                     || mTopAppCache.get(jobStatus.getSourceUid())
                     || isTopStartedJobLocked(jobStatus)
                     || isUidInForeground(jobStatus.getSourceUid())) {
@@ -777,7 +775,7 @@
         }
 
         // Expedited job.
-        if (mChargeTracker.isChargingLocked()) {
+        if (mService.isBatteryCharging()) {
             return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
         }
         if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) {
@@ -864,7 +862,7 @@
     @GuardedBy("mLock")
     private boolean isQuotaFreeLocked(final int standbyBucket) {
         // Quota constraint is not enforced while charging.
-        if (mChargeTracker.isChargingLocked()) {
+        if (mService.isBatteryCharging()) {
             // Restricted jobs require additional constraints when charging, so don't immediately
             // mark quota as free when charging.
             return standbyBucket != RESTRICTED_INDEX;
@@ -1538,15 +1536,19 @@
 
     private void handleNewChargingStateLocked() {
         mTimerChargingUpdateFunctor.setStatus(sElapsedRealtimeClock.millis(),
-                mChargeTracker.isChargingLocked());
+                mService.isBatteryCharging());
         if (DEBUG) {
-            Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isChargingLocked());
+            Slog.d(TAG, "handleNewChargingStateLocked: " + mService.isBatteryCharging());
         }
         // Deal with Timers first.
         mEJPkgTimers.forEach(mTimerChargingUpdateFunctor);
         mPkgTimers.forEach(mTimerChargingUpdateFunctor);
-        // Now update jobs.
-        maybeUpdateAllConstraintsLocked();
+        // Now update jobs out of band so broadcast processing can proceed.
+        JobSchedulerBackgroundThread.getHandler().post(() -> {
+            synchronized (mLock) {
+                maybeUpdateAllConstraintsLocked();
+            }
+        });
     }
 
     private void maybeUpdateAllConstraintsLocked() {
@@ -1811,58 +1813,6 @@
         return false;
     }
 
-    private final class ChargingTracker extends BroadcastReceiver {
-        /**
-         * Track whether we're charging. This has a slightly different definition than that of
-         * BatteryController.
-         */
-        @GuardedBy("mLock")
-        private boolean mCharging;
-
-        ChargingTracker() {
-        }
-
-        public void startTracking() {
-            IntentFilter filter = new IntentFilter();
-
-            // Charging/not charging.
-            filter.addAction(BatteryManager.ACTION_CHARGING);
-            filter.addAction(BatteryManager.ACTION_DISCHARGING);
-            mContext.registerReceiver(this, filter);
-
-            // Initialise tracker state.
-            BatteryManagerInternal batteryManagerInternal =
-                    LocalServices.getService(BatteryManagerInternal.class);
-            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
-        }
-
-        @GuardedBy("mLock")
-        public boolean isChargingLocked() {
-            return mCharging;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mLock) {
-                final String action = intent.getAction();
-                if (BatteryManager.ACTION_CHARGING.equals(action)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Received charging intent, fired @ "
-                                + sElapsedRealtimeClock.millis());
-                    }
-                    mCharging = true;
-                    handleNewChargingStateLocked();
-                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Disconnected from power.");
-                    }
-                    mCharging = false;
-                    handleNewChargingStateLocked();
-                }
-            }
-        }
-    }
-
     @VisibleForTesting
     static final class TimingSession {
         // Start timestamp in elapsed realtime timebase.
@@ -2127,6 +2077,12 @@
             final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid);
             final boolean hasTopAppExemption = !mRegularJobTimer
                     && (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed);
+            if (DEBUG) {
+                Slog.d(TAG, "quotaFree=" + isQuotaFreeLocked(standbyBucket)
+                        + " isFG=" + mForegroundUids.get(mUid)
+                        + " tempEx=" + hasTempAllowlistExemption
+                        + " topEx=" + hasTopAppExemption);
+            }
             return !isQuotaFreeLocked(standbyBucket)
                     && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption
                     && !hasTopAppExemption;
@@ -3938,7 +3894,6 @@
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
         pw.println("Is enabled: " + mIsEnabled);
-        pw.println("Is charging: " + mChargeTracker.isChargingLocked());
         pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
         pw.println();
 
@@ -4116,7 +4071,7 @@
         final long mToken = proto.start(StateControllerProto.QUOTA);
 
         proto.write(StateControllerProto.QuotaController.IS_CHARGING,
-                mChargeTracker.isChargingLocked());
+                mService.isBatteryCharging());
         proto.write(StateControllerProto.QuotaController.ELAPSED_REALTIME,
                 sElapsedRealtimeClock.millis());
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 5e73f42..2a2d602 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -133,11 +133,18 @@
     }
 
     /**
+     * Called when the battery status changes.
+     */
+    @GuardedBy("mLock")
+    public void onBatteryStateChangedLocked() {
+    }
+
+    /**
      * Called when a UID's base bias has changed. The more positive the bias, the more
      * important the UID is.
      */
     @GuardedBy("mLock")
-    public void onUidBiasChangedLocked(int uid, int newBias) {
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
     }
 
     protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 8b17512..dd102bd 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -42,6 +42,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -49,6 +50,7 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
 
@@ -80,7 +82,7 @@
     private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
     private static final long ONE_MINUTE = 60 * 1000;
 
-    private static final int STANDBY_BUCKET_UNKNOWN = -1;
+    static final int STANDBY_BUCKET_UNKNOWN = -1;
 
     /**
      * The bucket beyond which apps are considered idle. Any apps in this bucket or lower are
@@ -88,10 +90,35 @@
      */
     static final int IDLE_BUCKET_CUTOFF = STANDBY_BUCKET_RARE;
 
+    /** Initial version of the xml containing the app idle stats ({@link #APP_IDLE_FILENAME}). */
+    private static final int XML_VERSION_INITIAL = 0;
+    /**
+     * Allowed writing expiry times for any standby bucket instead of only active and working set.
+     * In previous version, we used to specify expiry times for active and working set as
+     * attributes:
+     * <pre>
+     *     <package activeTimeoutTime="..." workingSetTimeoutTime="..." />
+     * </pre>
+     * In this version, it is changed to:
+     * <pre>
+     *     <package>
+     *         <expiryTimes>
+     *             <item bucket="..." expiry="..." />
+     *             <item bucket="..." expiry="..." />
+     *         </expiryTimes>
+     *     </package>
+     * </pre>
+     */
+    private static final int XML_VERSION_ADD_BUCKET_EXPIRY_TIMES = 1;
+    /** Current version */
+    private static final int XML_VERSION_CURRENT = XML_VERSION_ADD_BUCKET_EXPIRY_TIMES;
+
     @VisibleForTesting
     static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
     private static final String TAG_PACKAGES = "packages";
     private static final String TAG_PACKAGE = "package";
+    private static final String TAG_BUCKET_EXPIRY_TIMES = "expiryTimes";
+    private static final String TAG_ITEM = "item";
     private static final String ATTR_NAME = "name";
     // Screen on timebase time when app was last used
     private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
@@ -111,6 +138,10 @@
     private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
     // The time when the forced working_set state can be overridden.
     private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
+    // The standby bucket value
+    private static final String ATTR_BUCKET = "bucket";
+    // The time when the forced bucket state can be overridde.
+    private static final String ATTR_EXPIRY_TIME = "expiry";
     // Elapsed timebase time when the app was last marked for restriction.
     private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED =
             "lastRestrictionAttemptElapsedTime";
@@ -119,6 +150,8 @@
             "lastRestrictionAttemptReason";
     // The next estimated launch time of the app, in ms since epoch.
     private static final String ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME = "nextEstimatedAppLaunchTime";
+    // Version of the xml file.
+    private static final String ATTR_VERSION = "version";
 
     // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
     private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -158,15 +191,10 @@
         // The estimated time the app will be launched next, in milliseconds since epoch.
         @CurrentTimeMillisLong
         long nextEstimatedLaunchTime;
-        // When should the bucket active state timeout, in elapsed timebase, if greater than
-        // lastUsedElapsedTime.
-        // This is used to keep the app in a high bucket regardless of other timeouts and
-        // predictions.
-        long bucketActiveTimeoutTime;
-        // If there's a forced working_set state, this is when it times out. This can be sitting
-        // under any active state timeout, so that it becomes applicable after the active state
-        // timeout expires.
-        long bucketWorkingSetTimeoutTime;
+        // Contains standby buckets that apps were forced into and the corresponding expiry times
+        // (in elapsed timebase) for each bucket state. App will stay in the highest bucket until
+        // it's expiry time is elapsed and will be moved to the next highest bucket.
+        SparseLongArray bucketExpiryTimesMs;
         // The last time an agent attempted to put the app into the RESTRICTED bucket.
         long lastRestrictAttemptElapsedTime;
         // The last reason the app was marked to be put into the RESTRICTED bucket.
@@ -249,21 +277,24 @@
     }
 
     /**
-     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
+     * Mark the app as used and update the bucket if necessary. If there is a expiry time specified
      * that's in the future, then the usage event is temporary and keeps the app in the specified
-     * bucket at least until the timeout is reached. This can be used to keep the app in an
+     * bucket at least until the expiry time is reached. This can be used to keep the app in an
      * elevated bucket for a while until some important task gets to run.
+     *
      * @param appUsageHistory the usage record for the app being updated
      * @param packageName name of the app being updated, for logging purposes
      * @param newBucket the bucket to set the app to
      * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
-     * @param elapsedRealtime mark as used time if non-zero
-     * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
-     *                with bucket values of ACTIVE and WORKING_SET.
+     * @param nowElapsedRealtimeMs mark as used time if non-zero (in
+     *                          {@link SystemClock#elapsedRealtime()} time base)
+     * @param expiryElapsedRealtimeMs the expiry time for the specified bucket (in
+     *                         {@link SystemClock#elapsedRealtime()} time base)
      * @return {@code appUsageHistory}
      */
     AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int userId,
-            int newBucket, int usageReason, long elapsedRealtime, long timeout) {
+            int newBucket, int usageReason,
+            long nowElapsedRealtimeMs, long expiryElapsedRealtimeMs) {
         int bucketingReason = REASON_MAIN_USAGE | usageReason;
         final boolean isUserUsage = isUserUsage(bucketingReason);
 
@@ -274,30 +305,27 @@
             newBucket = STANDBY_BUCKET_RESTRICTED;
             bucketingReason = appUsageHistory.bucketingReason;
         } else {
-            // Set the timeout if applicable
-            if (timeout > elapsedRealtime) {
+            // Set the expiry time if applicable
+            if (expiryElapsedRealtimeMs > nowElapsedRealtimeMs) {
                 // Convert to elapsed timebase
-                final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
-                if (newBucket == STANDBY_BUCKET_ACTIVE) {
-                    appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
-                            appUsageHistory.bucketActiveTimeoutTime);
-                } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
-                    appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
-                            appUsageHistory.bucketWorkingSetTimeoutTime);
-                } else {
-                    throw new IllegalArgumentException("Cannot set a timeout on bucket="
-                            + newBucket);
+                final long expiryTimeMs = getElapsedTime(expiryElapsedRealtimeMs);
+                if (appUsageHistory.bucketExpiryTimesMs == null) {
+                    appUsageHistory.bucketExpiryTimesMs = new SparseLongArray();
                 }
+                final long currentExpiryTimeMs = appUsageHistory.bucketExpiryTimesMs.get(newBucket);
+                appUsageHistory.bucketExpiryTimesMs.put(newBucket,
+                        Math.max(expiryTimeMs, currentExpiryTimeMs));
+                removeElapsedExpiryTimes(appUsageHistory, getElapsedTime(nowElapsedRealtimeMs));
             }
         }
 
-        if (elapsedRealtime != 0) {
+        if (nowElapsedRealtimeMs != 0) {
             appUsageHistory.lastUsedElapsedTime = mElapsedDuration
-                    + (elapsedRealtime - mElapsedSnapshot);
+                    + (nowElapsedRealtimeMs - mElapsedSnapshot);
             if (isUserUsage) {
                 appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime;
             }
-            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
+            appUsageHistory.lastUsedScreenTime = getScreenOnTime(nowElapsedRealtimeMs);
         }
 
         if (appUsageHistory.currentBucket > newBucket) {
@@ -309,26 +337,41 @@
         return appUsageHistory;
     }
 
+    private void removeElapsedExpiryTimes(AppUsageHistory appUsageHistory, long elapsedTimeMs) {
+        if (appUsageHistory.bucketExpiryTimesMs == null) {
+            return;
+        }
+        for (int i = appUsageHistory.bucketExpiryTimesMs.size() - 1; i >= 0; --i) {
+            if (appUsageHistory.bucketExpiryTimesMs.valueAt(i) < elapsedTimeMs) {
+                appUsageHistory.bucketExpiryTimesMs.removeAt(i);
+            }
+        }
+    }
+
     /**
-     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
+     * Mark the app as used and update the bucket if necessary. If there is a expiry time specified
      * that's in the future, then the usage event is temporary and keeps the app in the specified
-     * bucket at least until the timeout is reached. This can be used to keep the app in an
+     * bucket at least until the expiry time is reached. This can be used to keep the app in an
      * elevated bucket for a while until some important task gets to run.
-     * @param packageName
-     * @param userId
+     *
+     * @param packageName package name of the app the usage is reported for
+     * @param userId user that the app is running in
      * @param newBucket the bucket to set the app to
      * @param usageReason sub reason for usage
-     * @param nowElapsed mark as used time if non-zero
-     * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
-     *                with bucket values of ACTIVE and WORKING_SET.
-     * @return
+     * @param nowElapsedRealtimeMs mark as used time if non-zero (in
+     *                             {@link SystemClock#elapsedRealtime()} time base).
+     * @param expiryElapsedRealtimeMs the expiry time for the specified bucket (in
+     *                         {@link SystemClock#elapsedRealtime()} time base).
+     * @return the {@link AppUsageHistory} corresponding to the {@code packageName}
+     *         and {@code userId}.
      */
     public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
-            int usageReason, long nowElapsed, long timeout) {
+            int usageReason, long nowElapsedRealtimeMs, long expiryElapsedRealtimeMs) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
-        AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
-        return reportUsage(history, packageName, userId, newBucket, usageReason, nowElapsed,
-                timeout);
+        AppUsageHistory history = getPackageHistory(userHistory, packageName,
+                nowElapsedRealtimeMs, true);
+        return reportUsage(history, packageName, userId, newBucket, usageReason,
+                nowElapsedRealtimeMs, expiryElapsedRealtimeMs);
     }
 
     private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -383,7 +426,7 @@
     }
 
     public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
-            int bucket, int reason, boolean resetTimeout) {
+            int bucket, int reason, boolean resetExpiryTimes) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
                 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
@@ -397,9 +440,8 @@
             appUsageHistory.lastPredictedTime = elapsed;
             appUsageHistory.lastPredictedBucket = bucket;
         }
-        if (resetTimeout) {
-            appUsageHistory.bucketActiveTimeoutTime = elapsed;
-            appUsageHistory.bucketWorkingSetTimeoutTime = elapsed;
+        if (resetExpiryTimes && appUsageHistory.bucketExpiryTimesMs != null) {
+            appUsageHistory.bucketExpiryTimesMs.clear();
         }
         if (changed) {
             logAppStandbyBucketChanged(packageName, userId, bucket, reason);
@@ -622,6 +664,17 @@
     }
 
     @VisibleForTesting
+    long getBucketExpiryTimeMs(String packageName, int userId, int bucket, long elapsedRealtimeMs) {
+        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+                elapsedRealtimeMs, true);
+        if (appUsageHistory.bucketExpiryTimesMs == null) {
+            return 0;
+        }
+        return appUsageHistory.bucketExpiryTimesMs.get(bucket, 0);
+    }
+
+    @VisibleForTesting
     File getUserFile(int userId) {
         return new File(new File(new File(mStorageDir, "users"),
                 Integer.toString(userId)), APP_IDLE_FILENAME);
@@ -657,6 +710,7 @@
             if (!parser.getName().equals(TAG_PACKAGES)) {
                 return;
             }
+            final int version = getIntValue(parser, ATTR_VERSION, XML_VERSION_INITIAL);
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (type == XmlPullParser.START_TAG) {
                     final String name = parser.getName();
@@ -681,10 +735,6 @@
                                 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
                         appUsageHistory.lastJobRunTime = getLongValue(parser,
                                 ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
-                        appUsageHistory.bucketActiveTimeoutTime = getLongValue(parser,
-                                ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
-                        appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
-                                ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
                         appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
                         if (bucketingReason != null) {
                             try {
@@ -710,6 +760,26 @@
                                 ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, 0);
                         appUsageHistory.lastInformedBucket = -1;
                         userHistory.put(packageName, appUsageHistory);
+
+                        if (version >= XML_VERSION_ADD_BUCKET_EXPIRY_TIMES) {
+                            final int outerDepth = parser.getDepth();
+                            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                                if (TAG_BUCKET_EXPIRY_TIMES.equals(parser.getName())) {
+                                    readBucketExpiryTimes(parser, appUsageHistory);
+                                }
+                            }
+                        } else {
+                            final long bucketActiveTimeoutTime = getLongValue(parser,
+                                    ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
+                            final long bucketWorkingSetTimeoutTime = getLongValue(parser,
+                                    ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
+                            if (bucketActiveTimeoutTime != 0 || bucketWorkingSetTimeoutTime != 0) {
+                                insertBucketExpiryTime(appUsageHistory,
+                                        STANDBY_BUCKET_ACTIVE, bucketActiveTimeoutTime);
+                                insertBucketExpiryTime(appUsageHistory,
+                                        STANDBY_BUCKET_WORKING_SET, bucketWorkingSetTimeoutTime);
+                            }
+                        }
                     }
                 }
             }
@@ -720,21 +790,53 @@
         }
     }
 
+    private void readBucketExpiryTimes(XmlPullParser parser, AppUsageHistory appUsageHistory)
+            throws IOException, XmlPullParserException {
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            if (TAG_ITEM.equals(parser.getName())) {
+                final int bucket = getIntValue(parser, ATTR_BUCKET, STANDBY_BUCKET_UNKNOWN);
+                if (bucket == STANDBY_BUCKET_UNKNOWN) {
+                    Slog.e(TAG, "Error reading the buckets expiry times");
+                    continue;
+                }
+                final long expiryTimeMs = getLongValue(parser, ATTR_EXPIRY_TIME, 0 /* default */);
+                insertBucketExpiryTime(appUsageHistory, bucket, expiryTimeMs);
+            }
+        }
+    }
+
+    private void insertBucketExpiryTime(AppUsageHistory appUsageHistory,
+            int bucket, long expiryTimeMs) {
+        if (expiryTimeMs == 0) {
+            return;
+        }
+        if (appUsageHistory.bucketExpiryTimesMs == null) {
+            appUsageHistory.bucketExpiryTimesMs = new SparseLongArray();
+        }
+        appUsageHistory.bucketExpiryTimesMs.put(bucket, expiryTimeMs);
+    }
+
     private long getLongValue(XmlPullParser parser, String attrName, long defValue) {
         String value = parser.getAttributeValue(null, attrName);
         if (value == null) return defValue;
         return Long.parseLong(value);
     }
 
+    private int getIntValue(XmlPullParser parser, String attrName, int defValue) {
+        String value = parser.getAttributeValue(null, attrName);
+        if (value == null) return defValue;
+        return Integer.parseInt(value);
+    }
 
-    public void writeAppIdleTimes() {
+    public void writeAppIdleTimes(long elapsedRealtimeMs) {
         final int size = mIdleHistory.size();
         for (int i = 0; i < size; i++) {
-            writeAppIdleTimes(mIdleHistory.keyAt(i));
+            writeAppIdleTimes(mIdleHistory.keyAt(i), elapsedRealtimeMs);
         }
     }
 
-    public void writeAppIdleTimes(int userId) {
+    public void writeAppIdleTimes(int userId, long elapsedRealtimeMs) {
         FileOutputStream fos = null;
         AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
         try {
@@ -747,7 +849,9 @@
             xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             xml.startTag(null, TAG_PACKAGES);
+            xml.attribute(null, ATTR_VERSION, String.valueOf(XML_VERSION_CURRENT));
 
+            final long elapsedTimeMs = getElapsedTime(elapsedRealtimeMs);
             ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId);
             final int N = userHistory.size();
             for (int i = 0; i < N; i++) {
@@ -772,14 +876,6 @@
                         Integer.toString(history.currentBucket));
                 xml.attribute(null, ATTR_BUCKETING_REASON,
                         Integer.toHexString(history.bucketingReason));
-                if (history.bucketActiveTimeoutTime > 0) {
-                    xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
-                            .bucketActiveTimeoutTime));
-                }
-                if (history.bucketWorkingSetTimeoutTime > 0) {
-                    xml.attribute(null, ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, Long.toString(history
-                            .bucketWorkingSetTimeoutTime));
-                }
                 if (history.lastJobRunTime != Long.MIN_VALUE) {
                     xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
                             .lastJobRunTime));
@@ -794,6 +890,23 @@
                     xml.attribute(null, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME,
                             Long.toString(history.nextEstimatedLaunchTime));
                 }
+                if (history.bucketExpiryTimesMs != null) {
+                    xml.startTag(null, TAG_BUCKET_EXPIRY_TIMES);
+                    final int size = history.bucketExpiryTimesMs.size();
+                    for (int j = 0; j < size; ++j) {
+                        final long expiryTimeMs = history.bucketExpiryTimesMs.valueAt(j);
+                        // Skip writing to disk if the expiry time already elapsed.
+                        if (expiryTimeMs < elapsedTimeMs) {
+                            continue;
+                        }
+                        final int bucket = history.bucketExpiryTimesMs.keyAt(j);
+                        xml.startTag(null, TAG_ITEM);
+                        xml.attribute(null, ATTR_BUCKET, String.valueOf(bucket));
+                        xml.attribute(null, ATTR_EXPIRY_TIME, String.valueOf(expiryTimeMs));
+                        xml.endTag(null, TAG_ITEM);
+                    }
+                    xml.endTag(null, TAG_BUCKET_EXPIRY_TIMES);
+                }
                 xml.endTag(null, TAG_PACKAGE);
             }
 
@@ -846,12 +959,7 @@
             TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
             idpw.print(" lastPred=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
-            idpw.print(" activeLeft=");
-            TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
-                    idpw);
-            idpw.print(" wsLeft=");
-            TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
-                    idpw);
+            dumpBucketExpiryTimes(idpw, appUsageHistory, totalElapsedTime);
             idpw.print(" lastJob=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
             if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) {
@@ -877,4 +985,26 @@
         idpw.println();
         idpw.decreaseIndent();
     }
+
+    private void dumpBucketExpiryTimes(IndentingPrintWriter idpw, AppUsageHistory appUsageHistory,
+            long totalElapsedTimeMs) {
+        idpw.print(" expiryTimes=");
+        if (appUsageHistory.bucketExpiryTimesMs == null
+                || appUsageHistory.bucketExpiryTimesMs.size() == 0) {
+            idpw.print("<none>");
+            return;
+        }
+        idpw.print("(");
+        final int size = appUsageHistory.bucketExpiryTimesMs.size();
+        for (int i = 0; i < size; ++i) {
+            final int bucket = appUsageHistory.bucketExpiryTimesMs.keyAt(i);
+            final long expiryTimeMs = appUsageHistory.bucketExpiryTimesMs.valueAt(i);
+            if (i != 0) {
+                idpw.print(",");
+            }
+            idpw.print(bucket + ":");
+            TimeUtils.formatDuration(totalElapsedTimeMs - expiryTimeMs, idpw);
+        }
+        idpw.print(")");
+    }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index abbae4e..0ad70e4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -49,10 +49,12 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static android.app.usage.UsageStatsManager.standbyBucketToString;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.AppIdleHistory.STANDBY_BUCKET_UNKNOWN;
 
 import android.annotation.CurrentTimeMillisLong;
 import android.annotation.NonNull;
@@ -181,7 +183,7 @@
             COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
             COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
             COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
-            COMPRESS_TIME ? 32 * ONE_MINUTE : 45 * ONE_DAY
+            COMPRESS_TIME ? 32 * ONE_MINUTE : 3 * ONE_DAY
     };
 
     /** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
@@ -298,6 +300,11 @@
     long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT;
     /** Minimum time a notification seen event should keep the bucket elevated. */
     long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT;
+    /** Minimum time a slice pinned event should keep the bucket elevated. */
+    long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_SLICE_PINNED_TIMEOUT;
+    /** The standby bucket that an app will be promoted on a notification-seen event */
+    int mNotificationSeenPromotedBucket =
+            ConstantsObserver.DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET;
     /** Minimum time a system update event should keep the buckets elevated. */
     long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT;
     /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
@@ -773,7 +780,7 @@
                 userId);
         if (DEBUG) {
             Slog.d(TAG, "   Checking idle state for " + packageName
-                    + " minBucket=" + minBucket);
+                    + " minBucket=" + standbyBucketToString(minBucket));
         }
         if (minBucket <= STANDBY_BUCKET_ACTIVE) {
             // No extra processing needed for ACTIVE or higher since apps can't drop into lower
@@ -815,36 +822,34 @@
                         newBucket = app.lastPredictedBucket;
                         reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
                         if (DEBUG) {
-                            Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
+                            Slog.d(TAG, "Restored predicted newBucket = "
+                                    + standbyBucketToString(newBucket));
                         }
                     } else {
                         newBucket = getBucketForLocked(packageName, userId,
                                 elapsedRealtime);
                         if (DEBUG) {
-                            Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+                            Slog.d(TAG, "Evaluated AOSP newBucket = "
+                                    + standbyBucketToString(newBucket));
                         }
                         reason = REASON_MAIN_TIMEOUT;
                     }
                 }
 
-                // Check if the app is within one of the timeouts for forced bucket elevation
+                // Check if the app is within one of the expiry times for forced bucket elevation
                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
-                if (newBucket >= STANDBY_BUCKET_ACTIVE
-                        && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
-                    newBucket = STANDBY_BUCKET_ACTIVE;
-                    reason = app.bucketingReason;
-                    if (DEBUG) {
-                        Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
+                final int bucketWithValidExpiryTime = getMinBucketWithValidExpiryTime(app,
+                        newBucket, elapsedTimeAdjusted);
+                if (bucketWithValidExpiryTime != STANDBY_BUCKET_UNKNOWN) {
+                    newBucket = bucketWithValidExpiryTime;
+                    if (newBucket == STANDBY_BUCKET_ACTIVE || app.currentBucket == newBucket) {
+                        reason = app.bucketingReason;
+                    } else {
+                        reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
                     }
-                } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
-                        && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
-                    newBucket = STANDBY_BUCKET_WORKING_SET;
-                    // If it was already there, keep the reason, else assume timeout to WS
-                    reason = (newBucket == oldBucket)
-                            ? app.bucketingReason
-                            : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
                     if (DEBUG) {
-                        Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
+                        Slog.d(TAG, "    Keeping at " + standbyBucketToString(newBucket)
+                                + " due to min timeout");
                     }
                 }
 
@@ -868,13 +873,14 @@
                     newBucket = minBucket;
                     // Leave the reason alone.
                     if (DEBUG) {
-                        Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket
+                        Slog.d(TAG, "Bringing up from " + standbyBucketToString(newBucket)
+                                + " to " + standbyBucketToString(minBucket)
                                 + " due to min bucketing");
                     }
                 }
                 if (DEBUG) {
-                    Slog.d(TAG, "     Old bucket=" + oldBucket
-                            + ", newBucket=" + newBucket);
+                    Slog.d(TAG, "     Old bucket=" + standbyBucketToString(oldBucket)
+                            + ", newBucket=" + standbyBucketToString(newBucket));
                 }
                 if (oldBucket != newBucket || predictionLate) {
                     mAppIdleHistory.setAppStandbyBucket(packageName, userId,
@@ -967,6 +973,7 @@
         }
     }
 
+    @GuardedBy("mAppIdleLock")
     private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) {
         // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
         // about apps that are on some kind of whitelist anyway.
@@ -980,13 +987,20 @@
         final long nextCheckDelay;
         final int subReason = usageEventToSubReason(eventType);
         final int reason = REASON_MAIN_USAGE | subReason;
-        if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
-                || eventType == UsageEvents.Event.SLICE_PINNED) {
+        if (eventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+            // Notification-seen elevates to a higher bucket (depending on
+            // {@link ConstantsObserver#KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET}) but doesn't
+            // change usage time.
+            mAppIdleHistory.reportUsage(appHistory, pkg, userId,
+                    mNotificationSeenPromotedBucket, subReason,
+                    0, elapsedRealtime + mNotificationSeenTimeoutMillis);
+            nextCheckDelay = mNotificationSeenTimeoutMillis;
+        } else if (eventType == UsageEvents.Event.SLICE_PINNED) {
             // Mild usage elevates to WORKING_SET but doesn't change usage time.
             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
                     STANDBY_BUCKET_WORKING_SET, subReason,
-                    0, elapsedRealtime + mNotificationSeenTimeoutMillis);
-            nextCheckDelay = mNotificationSeenTimeoutMillis;
+                    0, elapsedRealtime + mSlicePinnedTimeoutMillis);
+            nextCheckDelay = mSlicePinnedTimeoutMillis;
         } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
                     STANDBY_BUCKET_ACTIVE, subReason,
@@ -1022,6 +1036,29 @@
     }
 
     /**
+     * Returns the lowest standby bucket that is better than {@code targetBucket} and has an
+     * valid expiry time (i.e. the expiry time is not yet elapsed).
+     */
+    private int getMinBucketWithValidExpiryTime(AppUsageHistory usageHistory,
+            int targetBucket, long elapsedTimeMs) {
+        if (usageHistory.bucketExpiryTimesMs == null) {
+            return STANDBY_BUCKET_UNKNOWN;
+        }
+        final int size = usageHistory.bucketExpiryTimesMs.size();
+        for (int i = 0; i < size; ++i) {
+            final int bucket = usageHistory.bucketExpiryTimesMs.keyAt(i);
+            if (targetBucket <= bucket) {
+                break;
+            }
+            final long expiryTimeMs = usageHistory.bucketExpiryTimesMs.valueAt(i);
+            if (expiryTimeMs > elapsedTimeMs) {
+                return bucket;
+            }
+        }
+        return STANDBY_BUCKET_UNKNOWN;
+    }
+
+    /**
      * Note: don't call this with the lock held since it makes calls to other system services.
      */
     private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) {
@@ -1564,23 +1601,18 @@
                 // ACTIVE or WORKING_SET timeout.
                 mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
 
-                if (newBucket > STANDBY_BUCKET_ACTIVE
-                        && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
-                    newBucket = STANDBY_BUCKET_ACTIVE;
-                    reason = app.bucketingReason;
-                    if (DEBUG) {
-                        Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
-                    }
-                } else if (newBucket > STANDBY_BUCKET_WORKING_SET
-                        && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
-                    newBucket = STANDBY_BUCKET_WORKING_SET;
-                    if (app.currentBucket != newBucket) {
-                        reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
-                    } else {
+                final int bucketWithValidExpiryTime = getMinBucketWithValidExpiryTime(app,
+                        newBucket, elapsedTimeAdjusted);
+                if (bucketWithValidExpiryTime != STANDBY_BUCKET_UNKNOWN) {
+                    newBucket = bucketWithValidExpiryTime;
+                    if (newBucket == STANDBY_BUCKET_ACTIVE || app.currentBucket == newBucket) {
                         reason = app.bucketingReason;
+                    } else {
+                        reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
                     }
                     if (DEBUG) {
-                        Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
+                        Slog.d(TAG, "    Keeping at " + standbyBucketToString(newBucket)
+                                + " due to min timeout");
                     }
                 } else if (newBucket == STANDBY_BUCKET_RARE
                         && mAllowRestrictedBucket
@@ -1746,7 +1778,7 @@
     @Override
     public void flushToDisk() {
         synchronized (mAppIdleLock) {
-            mAppIdleHistory.writeAppIdleTimes();
+            mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime());
             mAppIdleHistory.writeAppIdleDurations();
         }
     }
@@ -1897,7 +1929,7 @@
                 }
             }
             // Immediately persist defaults to disk
-            mAppIdleHistory.writeAppIdleTimes(userId);
+            mAppIdleHistory.writeAppIdleTimes(userId, elapsedRealtime);
         }
     }
 
@@ -1964,6 +1996,12 @@
         pw.print("  mNotificationSeenTimeoutMillis=");
         TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
         pw.println();
+        pw.print("  mNotificationSeenPromotedBucket=");
+        pw.print(standbyBucketToString(mNotificationSeenPromotedBucket));
+        pw.println();
+        pw.print("  mSlicePinnedTimeoutMillis=");
+        TimeUtils.formatDuration(mSlicePinnedTimeoutMillis, pw);
+        pw.println();
         pw.print("  mSyncAdapterTimeoutMillis=");
         TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
         pw.println();
@@ -2386,6 +2424,10 @@
         private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
         private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
                 "notification_seen_duration";
+        private static final String KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET =
+                "notification_seen_promoted_bucket";
+        private static final String KEY_SLICE_PINNED_HOLD_DURATION =
+                "slice_pinned_duration";
         private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
                 "system_update_usage_duration";
         private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
@@ -2428,6 +2470,10 @@
                 COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR;
         public static final long DEFAULT_NOTIFICATION_TIMEOUT =
                 COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
+        public static final long DEFAULT_SLICE_PINNED_TIMEOUT =
+                COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
+        public static final int DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET =
+                STANDBY_BUCKET_WORKING_SET;
         public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT =
                 COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR;
         public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT =
@@ -2494,7 +2540,7 @@
                     switch (name) {
                         case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS:
                             mInjector.mAutoRestrictedBucketDelayMs = Math.max(
-                                    COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
+                                    COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR,
                                     properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
                                             DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
                             break;
@@ -2513,6 +2559,16 @@
                                     KEY_NOTIFICATION_SEEN_HOLD_DURATION,
                                     DEFAULT_NOTIFICATION_TIMEOUT);
                             break;
+                        case KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET:
+                            mNotificationSeenPromotedBucket = properties.getInt(
+                                    KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET,
+                                    DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET);
+                            break;
+                        case KEY_SLICE_PINNED_HOLD_DURATION:
+                            mSlicePinnedTimeoutMillis = properties.getLong(
+                                    KEY_SLICE_PINNED_HOLD_DURATION,
+                                    DEFAULT_SLICE_PINNED_TIMEOUT);
+                            break;
                         case KEY_STRONG_USAGE_HOLD_DURATION:
                             mStrongUsageTimeoutMillis = properties.getLong(
                                     KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT);
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index bed3895..2c5965c 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 1344
 [email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
index 1bd5b36..91489dc 100644
--- a/apex/media/framework/jarjar_rules.txt
+++ b/apex/media/framework/jarjar_rules.txt
@@ -1,4 +1,2 @@
 rule com.android.modules.** android.media.internal.@1
 rule com.google.android.exoplayer2.** android.media.internal.exo.@1
-rule com.google.common.** android.media.internal.guava_common.@1
-rule com.google.thirdparty.** android.media.internal.guava_thirdparty.@1
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 7daebd0..b6f85c7 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -62,8 +62,8 @@
 import com.google.android.exoplayer2.upstream.DataReader;
 import com.google.android.exoplayer2.util.ParsableByteArray;
 import com.google.android.exoplayer2.util.TimestampAdjuster;
+import com.google.android.exoplayer2.util.Util;
 import com.google.android.exoplayer2.video.ColorInfo;
-import com.google.common.base.Ascii;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -978,7 +978,7 @@
     @ParserName
     public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
         String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
-        mimeType = mimeType == null ? null : Ascii.toLowerCase(mimeType);
+        mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
         if (TextUtils.isEmpty(mimeType)) {
             // No MIME type provided. Return all.
             return Collections.unmodifiableList(
@@ -1420,7 +1420,7 @@
         int flags = 0;
         TimestampAdjuster timestampAdjuster = null;
         if (mIgnoreTimestampOffset) {
-            timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.MODE_NO_OFFSET);
+            timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET);
         }
         switch (parserName) {
             case PARSER_NAME_MATROSKA:
diff --git a/apex/media/framework/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
index f6fd509..9f80c43 100644
--- a/apex/media/framework/java/android/media/MediaSession2Service.java
+++ b/apex/media/framework/java/android/media/MediaSession2Service.java
@@ -161,19 +161,19 @@
     public abstract MediaSession2 onGetSession(@NonNull ControllerInfo controllerInfo);
 
     /**
-     * Called when notification UI needs update. Override this method to show or cancel your own
-     * notification UI.
+     * Called to update the media notification when the playback state changes.
      * <p>
-     * This would be called on {@link MediaSession2}'s callback executor when playback state is
-     * changed.
+     * If playback is active and a notification is returned, the service uses it to become a
+     * foreground service. If playback is not active then the notification is still posted, but the
+     * service does not become a foreground service.
      * <p>
-     * With the notification returned here, the service becomes foreground service when the playback
-     * is started. Apps must request the permission
-     * {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use this API. It becomes
-     * background service after the playback is stopped.
+     * Apps must request the {@link android.Manifest.permission#FOREGROUND_SERVICE} permission
+     * in order to use this API. For apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU}
+     * or later, notifications will only be posted if the app has also been granted the
+     * {@link android.Manifest.permission#POST_NOTIFICATIONS} permission.
      *
-     * @param session a session that needs notification update.
-     * @return a {@link MediaNotification}. Can be {@code null}.
+     * @param session the session for which an updated media notification is required.
+     * @return the {@link MediaNotification}. Can be {@code null}.
      */
     @Nullable
     public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session);
diff --git a/apex/media/framework/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
index 13aabfc..af8184a 100644
--- a/apex/media/framework/java/android/media/Session2CommandGroup.java
+++ b/apex/media/framework/java/android/media/Session2CommandGroup.java
@@ -68,7 +68,7 @@
     /**
      * Used by parcelable creator.
      */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @SuppressWarnings({"WeakerAccess", "UnsafeParcelApi"}) /* synthetic access */
     Session2CommandGroup(Parcel in) {
         Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader());
         if (commands != null) {
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index e48f234..7d47e25 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -79,7 +79,7 @@
 
     final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
     @GuardedBy("mLock")
-    final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
+    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<>();
     final NotificationManager mNotificationManager;
     MediaSessionManager mSessionManager;
 
@@ -150,8 +150,8 @@
         return null;
     }
 
-    List<Session2Token> getSession2TokensLocked(int userId) {
-        List<Session2Token> list = new ArrayList<>();
+    ArrayList<Session2Token> getSession2TokensLocked(int userId) {
+        ArrayList<Session2Token> list = new ArrayList<>();
         if (userId == ALL.getIdentifier()) {
             int size = mUserRecords.size();
             for (int i = 0; i < size; i++) {
@@ -237,28 +237,29 @@
     }
 
     void dispatchSession2Changed(int userId) {
-        MediaParceledListSlice<Session2Token> allSession2Tokens;
-        MediaParceledListSlice<Session2Token> userSession2Tokens;
+        ArrayList<Session2Token> allSession2Tokens;
+        ArrayList<Session2Token> userSession2Tokens;
 
         synchronized (mLock) {
-            allSession2Tokens =
-                    new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
-            userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
-        }
-        allSession2Tokens.setInlineCountLimit(1);
-        userSession2Tokens.setInlineCountLimit(1);
+            allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier());
+            userSession2Tokens = getSession2TokensLocked(userId);
 
-        synchronized (mLock) {
             for (CallbackRecord record : mCallbackRecords) {
                 if (record.mUserId == ALL.getIdentifier()) {
                     try {
-                        record.mCallback.onSession2Changed(allSession2Tokens);
+                        MediaParceledListSlice<Session2Token> toSend =
+                                new MediaParceledListSlice<>(allSession2Tokens);
+                        toSend.setInlineCountLimit(0);
+                        record.mCallback.onSession2Changed(toSend);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Failed to notify session2 tokens changed " + record);
                     }
                 } else if (record.mUserId == userId) {
                     try {
-                        record.mCallback.onSession2Changed(userSession2Tokens);
+                        MediaParceledListSlice<Session2Token> toSend =
+                                new MediaParceledListSlice<>(userSession2Tokens);
+                        toSend.setInlineCountLimit(0);
+                        record.mCallback.onSession2Changed(toSend);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Failed to notify session2 tokens changed " + record);
                     }
@@ -382,7 +383,7 @@
             try {
                 // Check that they can make calls on behalf of the user and get the final user id
                 int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
-                List<Session2Token> result;
+                ArrayList<Session2Token> result;
                 synchronized (mLock) {
                     result = getSession2TokensLocked(resolvedUserId);
                 }
diff --git a/api/Android.bp b/api/Android.bp
index 2e49a0c..a22c2f6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,6 +24,20 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+bootstrap_go_package {
+    name: "soong-api",
+    pkgPath: "android/soong/api",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-genrule",
+        "soong-java",
+    ],
+    srcs: ["api.go"],
+    pluginFor: ["soong_build"],
+}
+
 python_defaults {
     name: "python3_version_defaults",
     version: {
@@ -89,47 +103,38 @@
     visibility: ["//visibility:public"],
 }
 
-genrule {
-    name: "frameworks-base-api-current.txt",
-    srcs: [
-        ":android.net.ipsec.ike{.public.api.txt}",
-        ":art.module.public.api{.public.api.txt}",
-        ":conscrypt.module.public.api{.public.api.txt}",
-        ":framework-appsearch{.public.api.txt}",
-        ":framework-connectivity{.public.api.txt}",
-        ":framework-connectivity-tiramisu{.public.api.txt}",
-        ":framework-graphics{.public.api.txt}",
-        ":framework-media{.public.api.txt}",
-        ":framework-mediaprovider{.public.api.txt}",
-        ":framework-nearby{.public.api.txt}",
-        ":framework-permission{.public.api.txt}",
-        ":framework-permission-s{.public.api.txt}",
-        ":framework-scheduling{.public.api.txt}",
-        ":framework-sdkextensions{.public.api.txt}",
-        ":framework-statsd{.public.api.txt}",
-        ":framework-supplementalprocess{.public.api.txt}",
-        ":framework-tethering{.public.api.txt}",
-        ":framework-uwb{.public.api.txt}",
-        ":framework-wifi{.public.api.txt}",
-        ":i18n.module.public.api{.public.api.txt}",
-        ":non-updatable-current.txt",
+combined_apis {
+    name: "frameworks-base-api",
+    bootclasspath: [
+        "android.net.ipsec.ike",
+        "art.module.public.api",
+        "conscrypt.module.public.api",
+        "framework-appsearch",
+        "framework-connectivity",
+        "framework-connectivity-tiramisu",
+        "framework-graphics",
+        "framework-media",
+        "framework-mediaprovider",
+        "framework-nearby",
+        "framework-permission",
+        "framework-permission-s",
+        "framework-scheduling",
+        "framework-sdkextensions",
+        "framework-statsd",
+        "framework-supplementalprocess",
+        "framework-tethering",
+        "framework-uwb",
+        "framework-wifi",
+        "i18n.module.public.api",
     ],
-    out: ["current.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "current.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/public/api",
-            dest: "android.txt",
-        },
+    conditional_bootclasspath: [
+        "framework-supplementalapi",
     ],
-    visibility: ["//visibility:public"],
+    system_server_classpath: [
+        "service-media-s",
+        "service-permission",
+        "service-supplementalprocess",
+    ],
 }
 
 genrule {
@@ -149,120 +154,6 @@
 }
 
 genrule {
-    name: "frameworks-base-api-current.srcjar",
-    srcs: [
-        ":android.net.ipsec.ike{.public.stubs.source}",
-        ":api-stubs-docs-non-updatable",
-        ":art.module.public.api{.public.stubs.source}",
-        ":conscrypt.module.public.api{.public.stubs.source}",
-        ":framework-appsearch{.public.stubs.source}",
-        ":framework-connectivity{.public.stubs.source}",
-        ":framework-connectivity-tiramisu{.public.stubs.source}",
-        ":framework-graphics{.public.stubs.source}",
-        ":framework-media{.public.stubs.source}",
-        ":framework-mediaprovider{.public.stubs.source}",
-        ":framework-nearby{.public.stubs.source}",
-        ":framework-permission{.public.stubs.source}",
-        ":framework-permission-s{.public.stubs.source}",
-        ":framework-scheduling{.public.stubs.source}",
-        ":framework-sdkextensions{.public.stubs.source}",
-        ":framework-statsd{.public.stubs.source}",
-        ":framework-supplementalprocess{.public.stubs.source}",
-        ":framework-tethering{.public.stubs.source}",
-        ":framework-uwb{.public.stubs.source}",
-        ":framework-wifi{.public.stubs.source}",
-        ":i18n.module.public.api{.public.stubs.source}",
-    ],
-    out: ["current.srcjar"],
-    tools: ["merge_zips"],
-    cmd: "$(location merge_zips) $(out) $(in)",
-    visibility: ["//visibility:private"], // Used by make module in //development, mind.
-}
-
-genrule {
-    name: "frameworks-base-api-removed.txt",
-    srcs: [
-        ":android.net.ipsec.ike{.public.removed-api.txt}",
-        ":art.module.public.api{.public.removed-api.txt}",
-        ":conscrypt.module.public.api{.public.removed-api.txt}",
-        ":framework-appsearch{.public.removed-api.txt}",
-        ":framework-connectivity{.public.removed-api.txt}",
-        ":framework-connectivity-tiramisu{.public.removed-api.txt}",
-        ":framework-graphics{.public.removed-api.txt}",
-        ":framework-media{.public.removed-api.txt}",
-        ":framework-mediaprovider{.public.removed-api.txt}",
-        ":framework-nearby{.public.removed-api.txt}",
-        ":framework-permission{.public.removed-api.txt}",
-        ":framework-permission-s{.public.removed-api.txt}",
-        ":framework-scheduling{.public.removed-api.txt}",
-        ":framework-sdkextensions{.public.removed-api.txt}",
-        ":framework-statsd{.public.removed-api.txt}",
-        ":framework-supplementalprocess{.public.removed-api.txt}",
-        ":framework-tethering{.public.removed-api.txt}",
-        ":framework-uwb{.public.removed-api.txt}",
-        ":framework-wifi{.public.removed-api.txt}",
-        ":i18n.module.public.api{.public.removed-api.txt}",
-        ":non-updatable-removed.txt",
-    ],
-    out: ["removed.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "removed.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/public/api",
-            dest: "removed.txt",
-        },
-    ],
-}
-
-genrule {
-    name: "frameworks-base-api-system-current.txt",
-    srcs: [
-        ":art.module.public.api{.system.api.txt}",
-        ":android.net.ipsec.ike{.system.api.txt}",
-        ":framework-appsearch{.system.api.txt}",
-        ":framework-connectivity{.system.api.txt}",
-        ":framework-connectivity-tiramisu{.system.api.txt}",
-        ":framework-graphics{.system.api.txt}",
-        ":framework-media{.system.api.txt}",
-        ":framework-mediaprovider{.system.api.txt}",
-        ":framework-nearby{.system.api.txt}",
-        ":framework-permission{.system.api.txt}",
-        ":framework-permission-s{.system.api.txt}",
-        ":framework-scheduling{.system.api.txt}",
-        ":framework-sdkextensions{.system.api.txt}",
-        ":framework-statsd{.system.api.txt}",
-        ":framework-supplementalprocess{.system.api.txt}",
-        ":framework-tethering{.system.api.txt}",
-        ":framework-uwb{.system.api.txt}",
-        ":framework-wifi{.system.api.txt}",
-        ":non-updatable-system-current.txt",
-    ],
-    out: ["system-current.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "system-current.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system/api",
-            dest: "android.txt",
-        },
-    ],
-    visibility: ["//visibility:public"],
-}
-
-genrule {
     name: "frameworks-base-api-system-current-compat",
     srcs: [
         ":android.api.system.latest",
@@ -281,87 +172,6 @@
 }
 
 genrule {
-    name: "frameworks-base-api-system-removed.txt",
-    srcs: [
-        ":art.module.public.api{.system.removed-api.txt}",
-        ":android.net.ipsec.ike{.system.removed-api.txt}",
-        ":framework-appsearch{.system.removed-api.txt}",
-        ":framework-connectivity{.system.removed-api.txt}",
-        ":framework-connectivity-tiramisu{.system.removed-api.txt}",
-        ":framework-graphics{.system.removed-api.txt}",
-        ":framework-media{.system.removed-api.txt}",
-        ":framework-mediaprovider{.system.removed-api.txt}",
-        ":framework-nearby{.system.removed-api.txt}",
-        ":framework-permission{.system.removed-api.txt}",
-        ":framework-permission-s{.system.removed-api.txt}",
-        ":framework-scheduling{.system.removed-api.txt}",
-        ":framework-sdkextensions{.system.removed-api.txt}",
-        ":framework-statsd{.system.removed-api.txt}",
-        ":framework-supplementalprocess{.system.removed-api.txt}",
-        ":framework-tethering{.system.removed-api.txt}",
-        ":framework-uwb{.system.removed-api.txt}",
-        ":framework-wifi{.system.removed-api.txt}",
-        ":non-updatable-system-removed.txt",
-    ],
-    out: ["system-removed.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "system-removed.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system/api",
-            dest: "removed.txt",
-        },
-    ],
-    visibility: ["//visibility:public"],
-}
-
-genrule {
-    name: "frameworks-base-api-module-lib-current.txt",
-    srcs: [
-        ":art.module.public.api{.module-lib.api.txt}",
-        ":android.net.ipsec.ike{.module-lib.api.txt}",
-        ":framework-appsearch{.module-lib.api.txt}",
-        ":framework-connectivity{.module-lib.api.txt}",
-        ":framework-connectivity-tiramisu{.module-lib.api.txt}",
-        ":framework-graphics{.module-lib.api.txt}",
-        ":framework-media{.module-lib.api.txt}",
-        ":framework-mediaprovider{.module-lib.api.txt}",
-        ":framework-nearby{.module-lib.api.txt}",
-        ":framework-permission{.module-lib.api.txt}",
-        ":framework-permission-s{.module-lib.api.txt}",
-        ":framework-scheduling{.module-lib.api.txt}",
-        ":framework-sdkextensions{.module-lib.api.txt}",
-        ":framework-statsd{.module-lib.api.txt}",
-        ":framework-supplementalprocess{.module-lib.api.txt}",
-        ":framework-tethering{.module-lib.api.txt}",
-        ":framework-uwb{.module-lib.api.txt}",
-        ":framework-wifi{.module-lib.api.txt}",
-        ":non-updatable-module-lib-current.txt",
-    ],
-    out: ["module-lib-current.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "module-lib-current.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/module-lib/api",
-            dest: "android.txt",
-        },
-    ],
-}
-
-genrule {
     name: "frameworks-base-api-module-lib-current-compat",
     srcs: [
         ":android.api.module-lib.latest",
@@ -383,46 +193,6 @@
 }
 
 genrule {
-    name: "frameworks-base-api-module-lib-removed.txt",
-    srcs: [
-        ":art.module.public.api{.module-lib.removed-api.txt}",
-        ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
-        ":framework-appsearch{.module-lib.removed-api.txt}",
-        ":framework-connectivity{.module-lib.removed-api.txt}",
-        ":framework-connectivity-tiramisu{.module-lib.removed-api.txt}",
-        ":framework-graphics{.module-lib.removed-api.txt}",
-        ":framework-media{.module-lib.removed-api.txt}",
-        ":framework-mediaprovider{.module-lib.removed-api.txt}",
-        ":framework-nearby{.module-lib.removed-api.txt}",
-        ":framework-permission{.module-lib.removed-api.txt}",
-        ":framework-permission-s{.module-lib.removed-api.txt}",
-        ":framework-scheduling{.module-lib.removed-api.txt}",
-        ":framework-sdkextensions{.module-lib.removed-api.txt}",
-        ":framework-statsd{.module-lib.removed-api.txt}",
-        ":framework-supplementalprocess{.module-lib.removed-api.txt}",
-        ":framework-tethering{.module-lib.removed-api.txt}",
-        ":framework-uwb{.module-lib.removed-api.txt}",
-        ":framework-wifi{.module-lib.removed-api.txt}",
-        ":non-updatable-module-lib-removed.txt",
-    ],
-    out: ["module-lib-removed.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "module-lib-removed.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/module-lib/api",
-            dest: "removed.txt",
-        },
-    ],
-}
-
-genrule {
     name: "combined-removed-dex",
     visibility: [
         "//frameworks/base/boot",
@@ -438,90 +208,3 @@
     out: ["combined-removed-dex.txt"],
     cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
 }
-
-genrule {
-    name: "frameworks-base-api-system-server-current.txt",
-    srcs: [
-        ":service-media-s{.system-server.api.txt}",
-        ":service-permission{.system-server.api.txt}",
-        ":non-updatable-system-server-current.txt",
-    ],
-    out: ["system-server-current.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "system-server-current.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system-server/api",
-            dest: "android.txt",
-        },
-    ],
-}
-
-genrule {
-    name: "frameworks-base-api-system-server-removed.txt",
-    srcs: [
-        ":service-media-s{.system-server.removed-api.txt}",
-        ":service-permission{.system-server.removed-api.txt}",
-        ":non-updatable-system-server-removed.txt",
-    ],
-    out: ["system-server-removed.txt"],
-    tools: ["metalava"],
-    cmd: metalava_cmd + "$(in) --api $(out)",
-    dists: [
-        {
-            targets: ["droidcore"],
-            dir: "api",
-            dest: "system-server-removed.txt",
-        },
-        {
-            targets: ["sdk"],
-            dir: "apistubs/android/system-server/api",
-            dest: "removed.txt",
-        },
-    ],
-}
-
-// This rule will filter classes present in the jar files of mainline modules
-// from the lint database in api-versions.xml.
-// This is done to reduce the number of false positive NewApi findings in
-// java libraries that compile against the module SDK
-genrule {
-    name: "api-versions-xml-public-filtered",
-    srcs: [
-        // Note: order matters: first parameter is the full api-versions.xml
-        // after that the stubs files in any order
-        // stubs files are all modules that export API surfaces EXCEPT ART
-        ":framework-doc-stubs{.api_versions.xml}",
-        ":android.net.ipsec.ike.stubs{.jar}",
-        ":conscrypt.module.public.api.stubs{.jar}",
-        ":framework-appsearch.stubs{.jar}",
-        ":framework-connectivity.stubs{.jar}",
-        ":framework-connectivity-tiramisu.stubs{.jar}",
-        ":framework-graphics.stubs{.jar}",
-        ":framework-media.stubs{.jar}",
-        ":framework-mediaprovider.stubs{.jar}",
-        ":framework-nearby.stubs{.jar}",
-        ":framework-permission.stubs{.jar}",
-        ":framework-permission-s.stubs{.jar}",
-        ":framework-scheduling.stubs{.jar}",
-        ":framework-sdkextensions.stubs{.jar}",
-        ":framework-statsd.stubs{.jar}",
-        ":framework-supplementalprocess.stubs{.jar}",
-        ":framework-tethering.stubs{.jar}",
-        ":framework-uwb.stubs{.jar}",
-        ":framework-wifi.stubs{.jar}",
-        ":i18n.module.public.api.stubs{.jar}",
-    ],
-    out: ["api-versions-public-filtered.xml"],
-    tools: ["api_versions_trimmer"],
-    cmd: "$(location api_versions_trimmer) $(out) $(in)",
-    dist: {
-        targets: ["sdk"],
-    },
-}
diff --git a/api/api.go b/api/api.go
new file mode 100644
index 0000000..4b6ebc1
--- /dev/null
+++ b/api/api.go
@@ -0,0 +1,304 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package api
+
+import (
+	"sort"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/genrule"
+	"android/soong/java"
+)
+
+const art = "art.module.public.api"
+const conscrypt = "conscrypt.module.public.api"
+const i18n = "i18n.module.public.api"
+
+// The intention behind this soong plugin is to generate a number of "merged"
+// API-related modules that would otherwise require a large amount of very
+// similar Android.bp boilerplate to define. For example, the merged current.txt
+// API definitions (created by merging the non-updatable current.txt with all
+// the module current.txts). This simplifies the addition of new android
+// modules, by reducing the number of genrules etc a new module must be added to.
+
+// The properties of the combined_apis module type.
+type CombinedApisProperties struct {
+	// Module libraries in the bootclasspath
+	Bootclasspath []string
+	// Module libraries on the bootclasspath if include_nonpublic_framework_api is true.
+	Conditional_bootclasspath []string
+	// Module libraries in system server
+	System_server_classpath []string
+}
+
+type CombinedApis struct {
+	android.ModuleBase
+
+	properties CombinedApisProperties
+}
+
+func init() {
+	registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
+}
+
+var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+type genruleProps struct {
+	Name       *string
+	Cmd        *string
+	Dists      []android.Dist
+	Out        []string
+	Srcs       []string
+	Tools      []string
+	Visibility []string
+}
+
+type libraryProps struct {
+	Name        *string
+	Sdk_version *string
+	Static_libs []string
+	Visibility  []string
+}
+
+type fgProps struct {
+	Name       *string
+	Srcs       []string
+	Visibility []string
+}
+
+// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
+type MergedTxtDefinition struct {
+	// "current.txt" or "removed.txt"
+	TxtFilename string
+	// The module for the non-updatable / non-module part of the api.
+	BaseTxt string
+	// The list of modules that are relevant for this merged txt.
+	Modules []string
+	// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
+	ModuleTag string
+	// public, system, module-lib or system-server
+	Scope string
+}
+
+func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
+	metalavaCmd := "$(location metalava)"
+	// Silence reflection warnings. See b/168689341
+	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
+	metalavaCmd += " --quiet --no-banner --format=v2 "
+
+	filename := txt.TxtFilename
+	if txt.Scope != "public" {
+		filename = txt.Scope + "-" + filename
+	}
+
+	props := genruleProps{}
+	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
+	props.Tools = []string{"metalava"}
+	props.Out = []string{filename}
+	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+	props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
+	props.Dists = []android.Dist{
+		{
+			Targets: []string{"droidcore"},
+			Dir:     proptools.StringPtr("api"),
+			Dest:    proptools.StringPtr(filename),
+		},
+		{
+			Targets: []string{"sdk"},
+			Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
+			Dest:    proptools.StringPtr(txt.TxtFilename),
+		},
+	}
+	props.Visibility = []string{"//visibility:public"}
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
+	props := genruleProps{}
+	props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
+	props.Tools = []string{"merge_zips"}
+	props.Out = []string{"current.srcjar"}
+	props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
+	props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...)
+	props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+// This produces the same annotations.zip as framework-doc-stubs, but by using
+// outputs from individual modules instead of all the source code.
+func createMergedAnnotations(ctx android.LoadHookContext, modules []string) {
+	// Conscrypt and i18n currently do not enable annotations
+	modules = removeAll(modules, []string{conscrypt, i18n})
+	props := genruleProps{}
+	props.Name = proptools.StringPtr("sdk-annotations.zip")
+	props.Tools = []string{"merge_annotation_zips", "soong_zip"}
+	props.Out = []string{"annotations.zip"}
+	props.Cmd = proptools.StringPtr("$(location merge_annotation_zips) $(genDir)/out $(in) && " +
+		"$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out")
+	props.Srcs = append([]string{":android-non-updatable-doc-stubs{.annotations.zip}"}, createSrcs(modules, "{.public.annotations.zip}")...)
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
+	// For the filtered api versions, we prune all APIs except art module's APIs. because
+	// 1) ART apis are available by default to all modules, while other module-to-module deps are
+	//    explicit and probably receive more scrutiny anyway
+	// 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
+	// 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
+	//    per-module lint databases that excludes just that module's APIs. Alas, that's more
+	//    difficult to achieve.
+	modules = remove(modules, art)
+
+	props := genruleProps{}
+	props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
+	props.Tools = []string{"api_versions_trimmer"}
+	props.Out = []string{"api-versions-public-filtered.xml"}
+	props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
+	// Note: order matters: first parameter is the full api-versions.xml
+	// after that the stubs files in any order
+	// stubs files are all modules that export API surfaces EXCEPT ART
+	props.Srcs = append([]string{":framework-doc-stubs{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...)
+	props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+	// The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+	modules = removeAll(modules, []string{art, conscrypt, i18n})
+	props := libraryProps{}
+	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
+	props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
+	props.Sdk_version = proptools.StringPtr("module_current")
+	props.Visibility = []string{"//frameworks/base"}
+	ctx.CreateModule(java.LibraryFactory, &props)
+}
+
+func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) {
+	props := fgProps{}
+	props.Name = proptools.StringPtr("all-modules-public-stubs-source")
+	props.Srcs = createSrcs(modules, "{.public.stubs.source}")
+	props.Visibility = []string{"//frameworks/base"}
+	ctx.CreateModule(android.FileGroupFactory, &props)
+}
+
+func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
+	var textFiles []MergedTxtDefinition
+	// Two module libraries currently do not support @SystemApi so only have the public scope.
+	bcpWithSystemApi := removeAll(bootclasspath, []string{conscrypt, i18n})
+
+	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
+	for i, f := range []string{"current.txt", "removed.txt"} {
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-" + f,
+			Modules:     bootclasspath,
+			ModuleTag:   "{.public" + tagSuffix[i],
+			Scope:       "public",
+		})
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-system-" + f,
+			Modules:     bcpWithSystemApi,
+			ModuleTag:   "{.system" + tagSuffix[i],
+			Scope:       "system",
+		})
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-module-lib-" + f,
+			Modules:     bcpWithSystemApi,
+			ModuleTag:   "{.module-lib" + tagSuffix[i],
+			Scope:       "module-lib",
+		})
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-system-server-" + f,
+			Modules:     system_server_classpath,
+			ModuleTag:   "{.system-server" + tagSuffix[i],
+			Scope:       "system-server",
+		})
+	}
+	for _, txt := range textFiles {
+		createMergedTxt(ctx, txt)
+	}
+}
+
+func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
+	bootclasspath := a.properties.Bootclasspath
+	if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
+		bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
+		sort.Strings(bootclasspath)
+	}
+	createMergedTxts(ctx, bootclasspath, a.properties.System_server_classpath)
+
+	createMergedStubsSrcjar(ctx, bootclasspath)
+
+	createMergedModuleLibStubs(ctx, bootclasspath)
+
+	createMergedAnnotations(ctx, bootclasspath)
+
+	createFilteredApiVersions(ctx, bootclasspath)
+
+	createPublicStubsSourceFilegroup(ctx, bootclasspath)
+}
+
+func combinedApisModuleFactory() android.Module {
+	module := &CombinedApis{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
+	return module
+}
+
+// Various utility methods below.
+
+// Creates an array of ":<m><tag>" for each m in <modules>.
+func createSrcs(modules []string, tag string) []string {
+	return transformArray(modules, ":", tag)
+}
+
+// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
+func transformArray(modules []string, prefix, suffix string) []string {
+	a := make([]string, 0, len(modules))
+	for _, module := range modules {
+		a = append(a, prefix+module+suffix)
+	}
+	return a
+}
+
+func removeAll(s []string, vs []string) []string {
+	for _, v := range vs {
+		s = remove(s, v)
+	}
+	return s
+}
+
+func remove(s []string, v string) []string {
+	s2 := make([]string, 0, len(s))
+	for _, sv := range s {
+		if sv != v {
+			s2 = append(s2, sv)
+		}
+	}
+	return s2
+}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 05a06619..1f4a64f 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -81,18 +81,18 @@
 static constexpr const char* OEM_USERSPACE_REBOOT_ANIMATION_FILE = "/oem/media/userspace-reboot.zip";
 static constexpr const char* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE = "/system/media/userspace-reboot.zip";
 
-static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
-static const char SYSTEM_TIME_DIR_NAME[] = "time";
-static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
+static const char BOOTANIM_DATA_DIR_PATH[] = "/data/bootanim";
+static const char BOOTANIM_TIME_DIR_NAME[] = "time";
+static const char BOOTANIM_TIME_DIR_PATH[] = "/data/bootanim/time";
 static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
 static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
 static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
 static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
 static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
-static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
+static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/bootanim/time/last_time_change";
 static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
-static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
-static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/system/time/time_format_12_hour";
+static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/bootanim/time/time_is_accurate";
+static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/bootanim/time/time_format_12_hour";
 // Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
 static const long long ACCURATE_TIME_EPOCH = 946684800000;
 static constexpr char FONT_BEGIN_CHAR = ' ';
@@ -1741,7 +1741,7 @@
 }
 
 BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false),
-    mInotifyFd(-1), mSystemWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
+    mInotifyFd(-1), mBootAnimWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
 
 BootAnimation::TimeCheckThread::~TimeCheckThread() {
     // mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
@@ -1784,7 +1784,7 @@
     const struct inotify_event *event;
     for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) {
         event = (const struct inotify_event *) ptr;
-        if (event->wd == mSystemWd && strcmp(SYSTEM_TIME_DIR_NAME, event->name) == 0) {
+        if (event->wd == mBootAnimWd && strcmp(BOOTANIM_TIME_DIR_NAME, event->name) == 0) {
             addTimeDirWatch();
         } else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0
                 || strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) {
@@ -1796,12 +1796,12 @@
 }
 
 void BootAnimation::TimeCheckThread::addTimeDirWatch() {
-        mTimeWd = inotify_add_watch(mInotifyFd, SYSTEM_TIME_DIR_PATH,
+        mTimeWd = inotify_add_watch(mInotifyFd, BOOTANIM_TIME_DIR_PATH,
                 IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
         if (mTimeWd > 0) {
             // No need to watch for the time directory to be created if it already exists
-            inotify_rm_watch(mInotifyFd, mSystemWd);
-            mSystemWd = -1;
+            inotify_rm_watch(mInotifyFd, mBootAnimWd);
+            mBootAnimWd = -1;
         }
 }
 
@@ -1812,11 +1812,11 @@
         return NO_INIT;
     }
 
-    mSystemWd = inotify_add_watch(mInotifyFd, SYSTEM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
-    if (mSystemWd < 0) {
+    mBootAnimWd = inotify_add_watch(mInotifyFd, BOOTANIM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
+    if (mBootAnimWd < 0) {
         close(mInotifyFd);
         mInotifyFd = -1;
-        SLOGE("Could not add watch for %s: %s", SYSTEM_DATA_DIR_PATH, strerror(errno));
+        SLOGE("Could not add watch for %s: %s", BOOTANIM_DATA_DIR_PATH, strerror(errno));
         return NO_INIT;
     }
 
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 7a597da..4c378cb 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -161,7 +161,7 @@
         void                addTimeDirWatch();
 
         int mInotifyFd;
-        int mSystemWd;
+        int mBootAnimWd;
         int mTimeWd;
         BootAnimation* mBootAnimation;
     };
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 13bf197..fbb99d2 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -43,7 +43,7 @@
 
 #define DEFAULT_DELAY_NS (1000000000LL)
 
-#define DEFAULT_BYTES_SIZE_LIMIT (96 * 1024 * 1024)        // 96MB
+#define DEFAULT_BYTES_SIZE_LIMIT (400 * 1024 * 1024)        // 400MB
 #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000)  // 1 Day
 
 // Skip these sections (for dumpstate only)
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index 23d80d7..dd33fdf 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -533,7 +533,7 @@
 WorkDirectory::WorkDirectory()
         :mDirectory("/data/misc/incidents"),
          mMaxFileCount(100),
-         mMaxDiskUsageBytes(100 * 1024 * 1024) {  // Incident reports can take up to 100MB on disk.
+         mMaxDiskUsageBytes(400 * 1024 * 1024) {  // Incident reports can take up to 400MB on disk.
                                                  // TODO: Should be a flag.
     create_directory(mDirectory.c_str());
 }
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 260c8a4..b384e70 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -259,7 +259,8 @@
     public void runDisableAppDataIsolation() throws RemoteException {
         if (!SystemProperties.getBoolean(
                 ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
-            throw new IllegalStateException("Storage app data isolation is not enabled.");
+            System.err.println("Storage app data isolation is not enabled.");
+            return;
         }
         final String pkgName = nextArg();
         final int pid = Integer.parseInt(nextArg());
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 9c044b5..52f883b 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -71,6 +71,8 @@
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
     private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
     private static final String COMMAND_CLEANUP_STUCK_CALLS = "cleanup-stuck-calls";
+    private static final String COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS =
+            "cleanup-orphan-phone-accounts";
     private static final String COMMAND_RESET_CAR_MODE = "reset-car-mode";
 
     /**
@@ -125,6 +127,9 @@
                         + " provider after a call to emergency services.\n"
                 + "usage: telecom cleanup-stuck-calls: Clear any disconnected calls that have"
                 + " gotten wedged in Telecom.\n"
+                + "usage: telecom cleanup-orphan-phone-accounts: remove any phone accounts that"
+                + " no longer have a valid UserHandle or accounts that no longer belongs to an"
+                + " installed package.\n"
                 + "usage: telecom set-emer-phone-account-filter <PACKAGE>\n"
                 + "\n"
                 + "telecom set-phone-account-enabled: Enables the given phone account, if it has"
@@ -227,6 +232,9 @@
             case COMMAND_CLEANUP_STUCK_CALLS:
                 runCleanupStuckCalls();
                 break;
+            case COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS:
+                runCleanupOrphanPhoneAccounts();
+                break;
             case COMMAND_RESET_CAR_MODE:
                 runResetCarMode();
                 break;
@@ -362,6 +370,11 @@
         mTelecomService.cleanupStuckCalls();
     }
 
+    private void runCleanupOrphanPhoneAccounts() throws RemoteException {
+        System.out.println("Success - cleaned up " + mTelecomService.cleanupOrphanPhoneAccounts()
+                + "  phone accounts.");
+    }
+
     private void runResetCarMode() throws RemoteException {
         mTelecomService.resetCarMode();
     }
diff --git a/core/api/current.txt b/core/api/current.txt
index c05f39b..bc446bf 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17,6 +17,7 @@
     field public static final String ACCESS_MEDIA_LOCATION = "android.permission.ACCESS_MEDIA_LOCATION";
     field public static final String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
     field public static final String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
+    field public static final String ACCESS_SUPPLEMENTAL_APIS = "android.permission.ACCESS_SUPPLEMENTAL_APIS";
     field public static final String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
     field public static final String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
     field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
@@ -124,11 +125,13 @@
     field public static final String POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
     field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
     field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
+    field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA";
     field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE";
     field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
     field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
     field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
     field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
+    field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
     field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
     field public static final String READ_LOGS = "android.permission.READ_LOGS";
     field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
@@ -823,6 +826,7 @@
     field public static final int indicatorRight = 16843022; // 0x101010e
     field public static final int indicatorStart = 16843729; // 0x10103d1
     field public static final int inflatedId = 16842995; // 0x10100f3
+    field public static final int inheritKeyStoreKeys;
     field public static final int inheritShowWhenLocked = 16844188; // 0x101059c
     field public static final int initOrder = 16842778; // 0x101001a
     field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
@@ -948,6 +952,8 @@
     field public static final int left = 16843181; // 0x10101ad
     field public static final int letterSpacing = 16843958; // 0x10104b6
     field public static final int level = 16844032; // 0x1010500
+    field public static final int lineBreakStyle = 16844365; // 0x101064d
+    field public static final int lineBreakWordStyle = 16844366; // 0x101064e
     field public static final int lineHeight = 16844159; // 0x101057f
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
@@ -971,6 +977,7 @@
     field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
     field public static final int listViewStyle = 16842868; // 0x1010074
     field public static final int listViewWhiteStyle = 16842869; // 0x1010075
+    field public static final int localeConfig;
     field public static final int lockTaskMode = 16844013; // 0x10104ed
     field public static final int logo = 16843454; // 0x10102be
     field public static final int logoDescription = 16844009; // 0x10104e9
@@ -1129,6 +1136,7 @@
     field public static final int popupWindowStyle = 16842870; // 0x1010076
     field public static final int port = 16842793; // 0x1010029
     field public static final int positiveButtonText = 16843253; // 0x10101f5
+    field public static final int preferKeepClear;
     field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c
     field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
     field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
@@ -1312,6 +1320,8 @@
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c
     field public static final int showAsAction = 16843481; // 0x10102d9
+    field public static final int showBackground;
+    field public static final int showClockAndComplications;
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
     field public static final int showForAllUsers = 16844015; // 0x10104ef
@@ -2258,6 +2268,7 @@
     field public static final int TextAppearance = 16973886; // 0x103003e
     field public static final int TextAppearance_DeviceDefault = 16974253; // 0x10301ad
     field public static final int TextAppearance_DeviceDefault_DialogWindowTitle = 16974264; // 0x10301b8
+    field public static final int TextAppearance_DeviceDefault_Headline;
     field public static final int TextAppearance_DeviceDefault_Inverse = 16974254; // 0x10301ae
     field public static final int TextAppearance_DeviceDefault_Large = 16974255; // 0x10301af
     field public static final int TextAppearance_DeviceDefault_Large_Inverse = 16974256; // 0x10301b0
@@ -3130,11 +3141,13 @@
     method public void addListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, @Nullable android.os.Handler);
     method public float getCenterX();
     method public float getCenterY();
+    method @NonNull public android.graphics.Region getCurrentMagnificationRegion();
     method @Nullable public android.accessibilityservice.MagnificationConfig getMagnificationConfig();
     method @NonNull public android.graphics.Region getMagnificationRegion();
     method public float getScale();
     method public boolean removeListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public boolean reset(boolean);
+    method public boolean resetCurrentMagnification(boolean);
     method public boolean setCenter(float, float, boolean);
     method public boolean setMagnificationConfig(@NonNull android.accessibilityservice.MagnificationConfig, boolean);
     method public boolean setScale(float, boolean);
@@ -3142,6 +3155,7 @@
 
   public static interface AccessibilityService.MagnificationController.OnMagnificationChangedListener {
     method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float);
+    method public default void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, @NonNull android.accessibilityservice.MagnificationConfig);
   }
 
   public static final class AccessibilityService.ScreenshotResult {
@@ -3986,7 +4000,7 @@
     method @Deprecated public void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction);
   }
 
-  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     ctor public Activity();
     method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void closeContextMenu();
@@ -4029,6 +4043,7 @@
     method public int getMaxNumPictureInPictureActions();
     method public final android.media.session.MediaController getMediaController();
     method @NonNull public android.view.MenuInflater getMenuInflater();
+    method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
     method public final android.app.Activity getParent();
     method @Nullable public android.content.Intent getParentActivityIntent();
     method public android.content.SharedPreferences getPreferences(int);
@@ -4469,6 +4484,7 @@
     method public android.app.ActivityOptions setLaunchDisplayId(int);
     method public android.app.ActivityOptions setLockTaskEnabled(boolean);
     method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+    method @NonNull public android.app.ActivityOptions setSplashScreenStyle(int);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
     field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
@@ -4906,7 +4922,7 @@
     method public void onDateSet(android.widget.DatePicker, int, int, int);
   }
 
-  public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+  public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     ctor public Dialog(@NonNull @UiContext android.content.Context);
     ctor public Dialog(@NonNull @UiContext android.content.Context, @StyleRes int);
     ctor protected Dialog(@NonNull @UiContext android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener);
@@ -4926,6 +4942,7 @@
     method @NonNull @UiContext public final android.content.Context getContext();
     method @Nullable public android.view.View getCurrentFocus();
     method @NonNull public android.view.LayoutInflater getLayoutInflater();
+    method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
     method @Nullable public final android.app.Activity getOwnerActivity();
     method @Nullable public final android.view.SearchEvent getSearchEvent();
     method public final int getVolumeControlStream();
@@ -5690,8 +5707,20 @@
     method @Deprecated public android.view.Window startActivity(String, android.content.Intent);
   }
 
+  public class LocaleConfig {
+    ctor public LocaleConfig(@NonNull android.content.Context);
+    method public int getStatus();
+    method @Nullable public android.os.LocaleList getSupportedLocales();
+    field public static final int STATUS_NOT_SPECIFIED = 1; // 0x1
+    field public static final int STATUS_PARSING_FAILED = 2; // 0x2
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final String TAG_LOCALE = "locale";
+    field public static final String TAG_LOCALE_CONFIG = "locale-config";
+  }
+
   public class LocaleManager {
     method @NonNull public android.os.LocaleList getApplicationLocales();
+    method @NonNull @RequiresPermission(value="android.permission.READ_APP_SPECIFIC_LOCALES", conditional=true) public android.os.LocaleList getApplicationLocales(@NonNull String);
     method public void setApplicationLocales(@NonNull android.os.LocaleList);
   }
 
@@ -7267,6 +7296,10 @@
     method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
     method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
     method public CharSequence getDeviceOwnerLockScreenInfo();
+    method @NonNull public android.graphics.drawable.Drawable getDrawable(int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawable(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
     method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
     method @NonNull public String getEnrollmentSpecificId();
     method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
@@ -7283,6 +7316,7 @@
     method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
     method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
+    method public int getMinimumRequiredWifiSecurityLevel();
     method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy();
     method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy();
     method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
@@ -7323,6 +7357,7 @@
     method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
     method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
     method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
+    method @Nullable public android.app.admin.WifiSsidPolicy getWifiSsidPolicy();
     method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
     method public boolean grantKeyPairToWifiAuth(@NonNull String);
     method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
@@ -7427,6 +7462,7 @@
     method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
     method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
     method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
+    method public void setMinimumRequiredWifiSecurityLevel(int);
     method public void setNearbyAppStreamingPolicy(int);
     method public void setNearbyNotificationStreamingPolicy(int);
     method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
@@ -7475,6 +7511,7 @@
     method public void setUsbDataSignalingEnabled(boolean);
     method public void setUserControlDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
     method public void setUserIcon(@NonNull android.content.ComponentName, android.graphics.Bitmap);
+    method public void setWifiSsidPolicy(@Nullable android.app.admin.WifiSsidPolicy);
     method public int startUserInBackground(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
     method public int stopUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
     method public boolean switchUser(@NonNull android.content.ComponentName, @Nullable android.os.UserHandle);
@@ -7490,6 +7527,7 @@
     field public static final String ACTION_CHECK_POLICY_COMPLIANCE = "android.app.action.CHECK_POLICY_COMPLIANCE";
     field public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
     field public static final String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
+    field public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED = "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED";
     field public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE";
     field public static final String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED";
@@ -7525,6 +7563,7 @@
     field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
     field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
     field public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES";
+    field public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE = "android.app.extra.PROVISIONING_ALLOW_OFFLINE";
     field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
     field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
     field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
@@ -7538,6 +7577,7 @@
     field @Deprecated public static final String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
     field public static final String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI";
     field public static final String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
+    field public static final String EXTRA_PROVISIONING_KEEP_SCREEN_ON = "android.app.extra.PROVISIONING_KEEP_SCREEN_ON";
     field public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
     field public static final String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
     field public static final String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
@@ -7565,6 +7605,9 @@
     field public static final String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
     field public static final String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
     field public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
+    field public static final String EXTRA_RESOURCE_ID = "android.app.extra.RESOURCE_ID";
+    field public static final String EXTRA_RESOURCE_TYPE_DRAWABLE = "android.app.extra.RESOURCE_TYPE_DRAWABLE";
+    field public static final String EXTRA_RESOURCE_TYPE_STRING = "android.app.extra.RESOURCE_TYPE_STRING";
     field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
     field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
     field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -7639,6 +7682,10 @@
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+    field public static final int WIFI_SECURITY_ENTERPRISE_192 = 3; // 0x3
+    field public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2; // 0x2
+    field public static final int WIFI_SECURITY_OPEN = 0; // 0x0
+    field public static final int WIFI_SECURITY_PERSONAL = 1; // 0x1
     field public static final int WIPE_EUICC = 4; // 0x4
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7659,6 +7706,35 @@
     method public void onApplicationUserDataCleared(String, boolean);
   }
 
+  public final class DevicePolicyResources {
+    ctor public DevicePolicyResources();
+  }
+
+  public static final class DevicePolicyResources.Drawable {
+    field public static final int INVALID_ID = -1; // 0xffffffff
+    field public static final int WORK_PROFILE_ICON = 1; // 0x1
+    field public static final int WORK_PROFILE_ICON_BADGE = 0; // 0x0
+    field public static final int WORK_PROFILE_OFF_ICON = 2; // 0x2
+    field public static final int WORK_PROFILE_USER_ICON = 3; // 0x3
+  }
+
+  public static final class DevicePolicyResources.Drawable.Source {
+    field public static final int HOME_WIDGET = 2; // 0x2
+    field public static final int LAUNCHER_OFF_BUTTON = 3; // 0x3
+    field public static final int NOTIFICATION = 0; // 0x0
+    field public static final int PROFILE_SWITCH_ANIMATION = 1; // 0x1
+    field public static final int QUICK_SETTINGS = 4; // 0x4
+    field public static final int STATUS_BAR = 5; // 0x5
+    field public static final int UNDEFINED = -1; // 0xffffffff
+  }
+
+  public static final class DevicePolicyResources.Drawable.Style {
+    field public static final int DEFAULT = -1; // 0xffffffff
+    field public static final int OUTLINE = 2; // 0x2
+    field public static final int SOLID_COLORED = 0; // 0x0
+    field public static final int SOLID_NOT_COLORED = 1; // 0x1
+  }
+
   public final class DnsEvent extends android.app.admin.NetworkEvent implements android.os.Parcelable {
     method public String getHostname();
     method public java.util.List<java.net.InetAddress> getInetAddresses();
@@ -7797,6 +7873,18 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
   }
 
+  public final class WifiSsidPolicy implements android.os.Parcelable {
+    method @NonNull public static android.app.admin.WifiSsidPolicy createAllowlistPolicy(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public static android.app.admin.WifiSsidPolicy createDenylistPolicy(@NonNull java.util.Set<java.lang.String>);
+    method public int describeContents();
+    method public int getPolicyType();
+    method @NonNull public java.util.Set<java.lang.String> getSsids();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.WifiSsidPolicy> CREATOR;
+    field public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0; // 0x0
+    field public static final int WIFI_SSID_POLICY_TYPE_DENYLIST = 1; // 0x1
+  }
+
 }
 
 package android.app.assist {
@@ -8789,11 +8877,12 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
     method public boolean isEnabled();
     method public boolean isLe2MPhySupported();
+    method public int isLeAudioBroadcastAssistantSupported();
+    method public int isLeAudioBroadcastSourceSupported();
     method public int isLeAudioSupported();
     method public boolean isLeCodedPhySupported();
     method public boolean isLeExtendedAdvertisingSupported();
     method public boolean isLePeriodicAdvertisingSupported();
-    method public int isLePeriodicAdvertisingSyncTransferSenderSupported();
     method public boolean isMultipleAdvertisementSupported();
     method public boolean isOffloadedFilteringSupported();
     method public boolean isOffloadedScanBatchingSupported();
@@ -9157,6 +9246,7 @@
     field public static final int AUDIO = 2097152; // 0x200000
     field public static final int CAPTURE = 524288; // 0x80000
     field public static final int INFORMATION = 8388608; // 0x800000
+    field public static final int LE_AUDIO = 16384; // 0x4000
     field public static final int LIMITED_DISCOVERABILITY = 8192; // 0x2000
     field public static final int NETWORKING = 131072; // 0x20000
     field public static final int OBJECT_TRANSFER = 1048576; // 0x100000
@@ -9201,6 +9291,7 @@
     field public static final int SOURCE_CODEC_TYPE_APTX = 2; // 0x2
     field public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; // 0x3
     field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240
+    field public static final int SOURCE_CODEC_TYPE_LC3 = 5; // 0x5
     field public static final int SOURCE_CODEC_TYPE_LDAC = 4; // 0x4
     field public static final int SOURCE_CODEC_TYPE_SBC = 0; // 0x0
   }
@@ -9655,6 +9746,7 @@
     method public void close();
     method protected void finalize();
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothDevice getConnectedGroupLeadDevice(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull android.bluetooth.BluetoothDevice);
@@ -9693,6 +9785,7 @@
     field public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
     field public static final int GATT = 7; // 0x7
     field public static final int GATT_SERVER = 8; // 0x8
+    field public static final int HAP_CLIENT = 28; // 0x1c
     field public static final int HEADSET = 1; // 0x1
     field @Deprecated public static final int HEALTH = 3; // 0x3
     field public static final int HEARING_AID = 21; // 0x15
@@ -9736,13 +9829,14 @@
     field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
     field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
     field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
-    field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa
-    field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65
-    field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66
+    field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200; // 0xc8
+    field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201; // 0xc9
     field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
     field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
     field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
     field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
+    field public static final int FEATURE_NOT_SUPPORTED = 11; // 0xb
+    field public static final int FEATURE_SUPPORTED = 10; // 0xa
     field public static final int SUCCESS = 0; // 0x0
   }
 
@@ -10765,7 +10859,7 @@
     method public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
     method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
     method public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
-    method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL", android.Manifest.permission.INTERACT_ACROSS_PROFILES}, conditional=true) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
     method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
     method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
     method @NonNull public int[] checkCallingOrSelfUriPermissions(@NonNull java.util.List<android.net.Uri>, int);
@@ -10867,6 +10961,8 @@
     method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void removeStickyBroadcast(@RequiresPermission android.content.Intent);
     method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
+    method public void revokeOwnPermissionOnKill(@NonNull String);
+    method public void revokeOwnPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>);
     method public abstract void revokeUriPermission(android.net.Uri, int);
     method public abstract void revokeUriPermission(String, android.net.Uri, int);
     method public abstract void sendBroadcast(@RequiresPermission android.content.Intent);
@@ -10996,8 +11092,8 @@
     field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
     field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
     field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
-    field public static final String TV_IAPP_SERVICE = "tv_iapp";
     field public static final String TV_INPUT_SERVICE = "tv_input";
+    field public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
     field public static final String UI_MODE_SERVICE = "uimode";
     field public static final String USAGE_STATS_SERVICE = "usagestats";
     field public static final String USB_SERVICE = "usb";
@@ -12755,11 +12851,16 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
     field public static final int INVALID_ID = -1; // 0xffffffff
-    field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
-    field public static final int STAGED_SESSION_CONFLICT = 4; // 0x4
-    field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
-    field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
-    field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
+    field public static final int SESSION_ACTIVATION_FAILED = 2; // 0x2
+    field public static final int SESSION_CONFLICT = 4; // 0x4
+    field public static final int SESSION_NO_ERROR = 0; // 0x0
+    field public static final int SESSION_UNKNOWN_ERROR = 3; // 0x3
+    field public static final int SESSION_VERIFICATION_FAILED = 1; // 0x1
+    field @Deprecated public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
+    field @Deprecated public static final int STAGED_SESSION_CONFLICT = 4; // 0x4
+    field @Deprecated public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field @Deprecated public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
+    field @Deprecated public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
 
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -13111,6 +13212,7 @@
     field public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
     field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint";
     field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+    field public static final String FEATURE_WINDOW_MAGNIFICATION = "android.software.window_magnification";
     field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2
     field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1
     field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4
@@ -13403,6 +13505,7 @@
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.content.ComponentName getActivity();
+    method @NonNull public java.util.List<java.lang.String> getCapabilityParameterValues(@NonNull String, @NonNull String);
     method @Nullable public java.util.Set<java.lang.String> getCategories();
     method @Nullable public CharSequence getDisabledMessage();
     method public int getDisabledReason();
@@ -13417,6 +13520,7 @@
     method public int getRank();
     method @Nullable public CharSequence getShortLabel();
     method public android.os.UserHandle getUserHandle();
+    method public boolean hasCapability(@NonNull String);
     method public boolean hasKeyFieldsOnly();
     method public boolean isCached();
     method public boolean isDeclaredInManifest();
@@ -13441,6 +13545,7 @@
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context, String);
+    method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull String, @Nullable String, @Nullable java.util.List<java.lang.String>);
     method @NonNull public android.content.pm.ShortcutInfo build();
     method @NonNull public android.content.pm.ShortcutInfo.Builder setActivity(@NonNull android.content.ComponentName);
     method @NonNull public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
@@ -15214,6 +15319,11 @@
 
   public class BitmapShader extends android.graphics.Shader {
     ctor public BitmapShader(@NonNull android.graphics.Bitmap, @NonNull android.graphics.Shader.TileMode, @NonNull android.graphics.Shader.TileMode);
+    method public int getFilterMode();
+    method public void setFilterMode(int);
+    field public static final int FILTER_MODE_DEFAULT = 0; // 0x0
+    field public static final int FILTER_MODE_LINEAR = 2; // 0x2
+    field public static final int FILTER_MODE_NEAREST = 1; // 0x1
   }
 
   public enum BlendMode {
@@ -15971,6 +16081,8 @@
     method public String getFontFeatureSettings();
     method public float getFontMetrics(android.graphics.Paint.FontMetrics);
     method public android.graphics.Paint.FontMetrics getFontMetrics();
+    method public void getFontMetricsInt(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
+    method public void getFontMetricsInt(@NonNull char[], @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
     method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
     method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
     method public float getFontSpacing();
@@ -16618,6 +16730,7 @@
     method public void setFloatUniform(@NonNull String, float, float, float);
     method public void setFloatUniform(@NonNull String, float, float, float, float);
     method public void setFloatUniform(@NonNull String, @NonNull float[]);
+    method public void setInputBuffer(@NonNull String, @NonNull android.graphics.BitmapShader);
     method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
     method public void setIntUniform(@NonNull String, int);
     method public void setIntUniform(@NonNull String, int, int);
@@ -17503,6 +17616,21 @@
 
 package android.graphics.text {
 
+  public final class LineBreakConfig {
+    ctor public LineBreakConfig();
+    method public int getLineBreakStyle();
+    method public int getLineBreakWordStyle();
+    method public void set(@NonNull android.graphics.text.LineBreakConfig);
+    method public void setLineBreakStyle(int);
+    method public void setLineBreakWordStyle(int);
+    field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
+    field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
+    field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
+    field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3
+    field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0
+    field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1
+  }
+
   public class LineBreaker {
     method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -17558,6 +17686,7 @@
     ctor public MeasuredText.Builder(@NonNull android.graphics.text.MeasuredText);
     method @NonNull public android.graphics.text.MeasuredText.Builder appendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float);
     method @NonNull public android.graphics.text.MeasuredText.Builder appendStyleRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, boolean);
+    method @NonNull public android.graphics.text.MeasuredText.Builder appendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean);
     method @NonNull public android.graphics.text.MeasuredText build();
     method @Deprecated @NonNull public android.graphics.text.MeasuredText.Builder setComputeHyphenation(boolean);
     method @NonNull public android.graphics.text.MeasuredText.Builder setComputeHyphenation(int);
@@ -17955,6 +18084,7 @@
     field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
     field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
     field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L
+    field public static final long USAGE_FRONT_BUFFER = 1L; // 0x1L
     field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L
     field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L
     field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L
@@ -17998,6 +18128,7 @@
     field public static final String STRING_TYPE_GRAVITY = "android.sensor.gravity";
     field public static final String STRING_TYPE_GYROSCOPE = "android.sensor.gyroscope";
     field public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = "android.sensor.gyroscope_uncalibrated";
+    field public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
     field public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
     field public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
     field public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
@@ -18028,6 +18159,7 @@
     field public static final int TYPE_GRAVITY = 9; // 0x9
     field public static final int TYPE_GYROSCOPE = 4; // 0x4
     field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
+    field public static final int TYPE_HEAD_TRACKER = 37; // 0x25
     field public static final int TYPE_HEART_BEAT = 31; // 0x1f
     field public static final int TYPE_HEART_RATE = 21; // 0x15
     field public static final int TYPE_HINGE_ANGLE = 36; // 0x24
@@ -18090,6 +18222,7 @@
     method public void onFlushCompleted(android.hardware.Sensor);
     method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
     method public void onSensorChanged(android.hardware.SensorEvent);
+    method public void onSensorDiscontinuity(@NonNull android.hardware.Sensor);
   }
 
   public interface SensorEventListener {
@@ -18313,10 +18446,12 @@
     ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
-    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+    ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
     method public javax.crypto.Cipher getCipher();
-    method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+    method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method public javax.crypto.Mac getMac();
+    method @Nullable public android.security.identity.PresentationSession getPresentationSession();
     method public java.security.Signature getSignature();
   }
 
@@ -18443,12 +18578,14 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DynamicRangeProfiles> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_PARTIAL_RESULT_COUNT;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> REQUEST_PIPELINE_MAX_DEPTH;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
@@ -18456,6 +18593,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
@@ -18772,6 +18910,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18; // 0x12
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -19125,6 +19264,25 @@
     field public static final long NORMAL = 0L; // 0x0L
   }
 
+  public final class DynamicRangeProfiles {
+    ctor public DynamicRangeProfiles(@NonNull int[]);
+    method @NonNull public java.util.Set<java.lang.Integer> getProfileCaptureRequestConstraints(int);
+    method @NonNull public java.util.Set<java.lang.Integer> getSupportedProfiles();
+    field public static final int DOLBY_VISION_10B_HDR_OEM = 64; // 0x40
+    field public static final int DOLBY_VISION_10B_HDR_OEM_PO = 128; // 0x80
+    field public static final int DOLBY_VISION_10B_HDR_REF = 16; // 0x10
+    field public static final int DOLBY_VISION_10B_HDR_REF_PO = 32; // 0x20
+    field public static final int DOLBY_VISION_8B_HDR_OEM = 1024; // 0x400
+    field public static final int DOLBY_VISION_8B_HDR_OEM_PO = 2048; // 0x800
+    field public static final int DOLBY_VISION_8B_HDR_REF = 256; // 0x100
+    field public static final int DOLBY_VISION_8B_HDR_REF_PO = 512; // 0x200
+    field public static final int HDR10 = 4; // 0x4
+    field public static final int HDR10_PLUS = 8; // 0x8
+    field public static final int HLG10 = 2; // 0x2
+    field public static final int PUBLIC_MAX = 4096; // 0x1000
+    field public static final int STANDARD = 1; // 0x1
+  }
+
   public final class ExtensionSessionConfiguration {
     ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
     method @NonNull public java.util.concurrent.Executor getExecutor();
@@ -19171,8 +19329,10 @@
   }
 
   public static final class MandatoryStreamCombination.MandatoryStreamInformation {
+    method public int get10BitFormat();
     method @NonNull public java.util.List<android.util.Size> getAvailableSizes();
     method public int getFormat();
+    method public boolean is10BitCapable();
     method public boolean isInput();
     method public boolean isMaximumSize();
     method public boolean isUltraHighResolution();
@@ -19226,12 +19386,14 @@
     method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
     method public int describeContents();
     method public void enableSurfaceSharing();
+    method public int getDynamicRangeProfile();
     method public int getMaxSharedSurfaceCount();
     method @Nullable public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
     method @NonNull public java.util.List<android.view.Surface> getSurfaces();
     method public void removeSensorPixelModeUsed(int);
     method public void removeSurface(@NonNull android.view.Surface);
+    method public void setDynamicRangeProfile(int);
     method public void setPhysicalCameraId(@Nullable String);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
@@ -19257,6 +19419,7 @@
     method @Nullable public java.util.Set<java.lang.Integer> getValidOutputFormatsForInput(int);
     method public boolean isOutputSupportedFor(int);
     method public boolean isOutputSupportedFor(@NonNull android.view.Surface);
+    field public static final int USECASE_10BIT_OUTPUT = 8; // 0x8
     field public static final int USECASE_LOW_LATENCY_SNAPSHOT = 6; // 0x6
     field public static final int USECASE_PREVIEW = 0; // 0x0
     field public static final int USECASE_RAW = 5; // 0x5
@@ -19734,6 +19897,7 @@
   @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
     ctor public InputMethodService();
     method @Deprecated public boolean enableHardwareAcceleration();
+    method public final void finishStylusHandwriting();
     method public int getBackDisposition();
     method public int getCandidatesHiddenVisibility();
     method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -19743,6 +19907,7 @@
     method @Deprecated public int getInputMethodWindowRecommendedHeight();
     method public android.view.LayoutInflater getLayoutInflater();
     method public int getMaxWidth();
+    method @Nullable public final android.view.Window getStylusHandwritingWindow();
     method public CharSequence getTextForImeAction(int);
     method public android.app.Dialog getWindow();
     method public void hideStatusIcon();
@@ -19773,6 +19938,7 @@
     method public void onFinishCandidatesView(boolean);
     method public void onFinishInput();
     method public void onFinishInputView(boolean);
+    method public void onFinishStylusHandwriting();
     method public void onInitializeInterface();
     method public boolean onInlineSuggestionsResponse(@NonNull android.view.inputmethod.InlineSuggestionsResponse);
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -19783,6 +19949,7 @@
     method public void onStartCandidatesView(android.view.inputmethod.EditorInfo, boolean);
     method public void onStartInput(android.view.inputmethod.EditorInfo, boolean);
     method public void onStartInputView(android.view.inputmethod.EditorInfo, boolean);
+    method public boolean onStartStylusHandwriting();
     method public void onUnbindInput();
     method @Deprecated public void onUpdateCursor(android.graphics.Rect);
     method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
@@ -20128,6 +20295,24 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SphericalCorrections> CREATOR;
   }
 
+  public final class GnssAutomaticGainControl implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0) public long getCarrierFrequencyHz();
+    method public int getConstellationType();
+    method @FloatRange(from=0xffffd8f0, to=10000) public double getLevelDb();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAutomaticGainControl> CREATOR;
+  }
+
+  public static final class GnssAutomaticGainControl.Builder {
+    ctor public GnssAutomaticGainControl.Builder();
+    ctor public GnssAutomaticGainControl.Builder(@NonNull android.location.GnssAutomaticGainControl);
+    method @NonNull public android.location.GnssAutomaticGainControl build();
+    method @NonNull public android.location.GnssAutomaticGainControl.Builder setCarrierFrequencyHz(@IntRange(from=0) long);
+    method @NonNull public android.location.GnssAutomaticGainControl.Builder setConstellationType(int);
+    method @NonNull public android.location.GnssAutomaticGainControl.Builder setLevelDb(@FloatRange(from=0xffffd8f0, to=10000) double);
+  }
+
   public final class GnssCapabilities implements android.os.Parcelable {
     method public int describeContents();
     method public boolean hasAntennaInfo();
@@ -20184,7 +20369,7 @@
     method public double getAccumulatedDeltaRangeMeters();
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
-    method public double getAutomaticGainControlLevelDb();
+    method @Deprecated public double getAutomaticGainControlLevelDb();
     method @FloatRange(from=0, to=63) public double getBasebandCn0DbHz();
     method @Deprecated public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
@@ -20206,7 +20391,7 @@
     method public int getState();
     method public int getSvid();
     method public double getTimeOffsetNanos();
-    method public boolean hasAutomaticGainControlLevelDb();
+    method @Deprecated public boolean hasAutomaticGainControlLevelDb();
     method public boolean hasBasebandCn0DbHz();
     method @Deprecated public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
@@ -20268,11 +20453,21 @@
   public final class GnssMeasurementsEvent implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.location.GnssClock getClock();
+    method @NonNull public java.util.Collection<android.location.GnssAutomaticGainControl> getGnssAutomaticGainControls();
     method @NonNull public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
   }
 
+  public static final class GnssMeasurementsEvent.Builder {
+    ctor public GnssMeasurementsEvent.Builder();
+    ctor public GnssMeasurementsEvent.Builder(@NonNull android.location.GnssMeasurementsEvent);
+    method @NonNull public android.location.GnssMeasurementsEvent build();
+    method @NonNull public android.location.GnssMeasurementsEvent.Builder setClock(@NonNull android.location.GnssClock);
+    method @NonNull public android.location.GnssMeasurementsEvent.Builder setGnssAutomaticGainControls(@NonNull java.util.Collection<android.location.GnssAutomaticGainControl>);
+    method @NonNull public android.location.GnssMeasurementsEvent.Builder setMeasurements(@NonNull java.util.Collection<android.location.GnssMeasurement>);
+  }
+
   public abstract static class GnssMeasurementsEvent.Callback {
     ctor public GnssMeasurementsEvent.Callback();
     method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
@@ -20766,6 +20961,7 @@
     method public boolean isSink();
     method public boolean isSource();
     field public static final int TYPE_AUX_LINE = 19; // 0x13
+    field public static final int TYPE_BLE_BROADCAST = 30; // 0x1e
     field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
     field public static final int TYPE_BLE_SPEAKER = 27; // 0x1b
     field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
@@ -20944,6 +21140,7 @@
     method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
     method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public int getAllowedCapturePolicy();
+    method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAudioDevicesForAttributes(@NonNull android.media.AudioAttributes);
     method public int getAudioHwSyncForSession(int);
     method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAvailableCommunicationDevices();
     method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
@@ -21885,16 +22082,29 @@
     method public android.media.Image acquireNextImage();
     method public void close();
     method public void discardFreeBuffers();
+    method public long getDataSpace();
+    method public int getHardwareBufferFormat();
     method public int getHeight();
     method public int getImageFormat();
     method public int getMaxImages();
     method public android.view.Surface getSurface();
+    method public long getUsage();
     method public int getWidth();
     method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int);
     method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int, long);
     method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler);
   }
 
+  public static final class ImageReader.Builder {
+    ctor public ImageReader.Builder(@IntRange(from=1) int, @IntRange(from=1) int);
+    method @NonNull public android.media.ImageReader build();
+    method @NonNull public android.media.ImageReader.Builder setDefaultDataSpace(long);
+    method @NonNull public android.media.ImageReader.Builder setDefaultHardwareBufferFormat(int);
+    method @NonNull public android.media.ImageReader.Builder setImageFormat(int);
+    method @NonNull public android.media.ImageReader.Builder setMaxImages(int);
+    method @NonNull public android.media.ImageReader.Builder setUsage(long);
+  }
+
   public static interface ImageReader.OnImageAvailableListener {
     method public void onImageAvailable(android.media.ImageReader);
   }
@@ -21902,14 +22112,30 @@
   public class ImageWriter implements java.lang.AutoCloseable {
     method public void close();
     method public android.media.Image dequeueInputImage();
+    method public long getDataSpace();
     method public int getFormat();
+    method public int getHardwareBufferFormat();
+    method public int getHeight();
     method public int getMaxImages();
+    method public long getUsage();
+    method public int getWidth();
     method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int);
     method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int);
     method public void queueInputImage(android.media.Image);
     method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler);
   }
 
+  public static final class ImageWriter.Builder {
+    ctor public ImageWriter.Builder(@NonNull android.view.Surface);
+    method @NonNull public android.media.ImageWriter build();
+    method @NonNull public android.media.ImageWriter.Builder setDataSpace(long);
+    method @NonNull public android.media.ImageWriter.Builder setHardwareBufferFormat(int);
+    method @NonNull public android.media.ImageWriter.Builder setImageFormat(int);
+    method @NonNull public android.media.ImageWriter.Builder setMaxImages(@IntRange(from=1) int);
+    method @NonNull public android.media.ImageWriter.Builder setUsage(long);
+    method @NonNull public android.media.ImageWriter.Builder setWidthAndHeight(@IntRange(from=1) int, @IntRange(from=1) int);
+  }
+
   public static interface ImageWriter.OnImageReleasedListener {
     method public void onImageReleased(android.media.ImageWriter);
   }
@@ -22321,6 +22547,7 @@
     field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
     field public static final String FEATURE_AdaptivePlayback = "adaptive-playback";
     field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
+    field public static final String FEATURE_EncodingStatistics = "encoding-statistics";
     field public static final String FEATURE_FrameParsing = "frame-parsing";
     field public static final String FEATURE_IntraRefresh = "intra-refresh";
     field public static final String FEATURE_LowLatency = "low-latency";
@@ -23095,6 +23322,7 @@
     field public static final String KEY_OPERATING_RATE = "operating-rate";
     field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
     field public static final String KEY_PCM_ENCODING = "pcm-encoding";
+    field public static final String KEY_PICTURE_TYPE = "picture-type";
     field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height";
     field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width";
     field public static final String KEY_PREPEND_HEADER_TO_SYNC_FRAMES = "prepend-sps-pps-to-idr-frames";
@@ -23112,6 +23340,8 @@
     field public static final String KEY_TILE_HEIGHT = "tile-height";
     field public static final String KEY_TILE_WIDTH = "tile-width";
     field public static final String KEY_TRACK_ID = "track-id";
+    field public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL = "video-encoding-statistics-level";
+    field public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average";
     field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max";
     field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
     field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max";
@@ -23172,12 +23402,18 @@
     field public static final String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
     field public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
     field public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+    field public static final int PICTURE_TYPE_B = 3; // 0x3
+    field public static final int PICTURE_TYPE_I = 1; // 0x1
+    field public static final int PICTURE_TYPE_P = 2; // 0x2
+    field public static final int PICTURE_TYPE_UNKNOWN = 0; // 0x0
     field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
     field public static final int TYPE_FLOAT = 3; // 0x3
     field public static final int TYPE_INTEGER = 1; // 0x1
     field public static final int TYPE_LONG = 2; // 0x2
     field public static final int TYPE_NULL = 0; // 0x0
     field public static final int TYPE_STRING = 4; // 0x4
+    field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1; // 0x1
+    field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0; // 0x0
   }
 
   public final class MediaMetadata implements android.os.Parcelable {
@@ -25540,6 +25776,7 @@
 
   public final class MidiDeviceInfo implements android.os.Parcelable {
     method public int describeContents();
+    method public int getDefaultProtocol();
     method public int getId();
     method public int getInputPortCount();
     method public int getOutputPortCount();
@@ -25556,6 +25793,14 @@
     field public static final String PROPERTY_SERIAL_NUMBER = "serial_number";
     field public static final String PROPERTY_USB_DEVICE = "usb_device";
     field public static final String PROPERTY_VERSION = "version";
+    field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3; // 0x3
+    field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4; // 0x4
+    field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1; // 0x1
+    field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2; // 0x2
+    field public static final int PROTOCOL_UMP_MIDI_2_0 = 17; // 0x11
+    field public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18; // 0x12
+    field public static final int PROTOCOL_UMP_USE_MIDI_CI = 0; // 0x0
+    field public static final int PROTOCOL_UNKNOWN = -1; // 0xffffffff
     field public static final int TYPE_BLUETOOTH = 3; // 0x3
     field public static final int TYPE_USB = 1; // 0x1
     field public static final int TYPE_VIRTUAL = 2; // 0x2
@@ -25596,11 +25841,15 @@
   }
 
   public final class MidiManager {
-    method public android.media.midi.MidiDeviceInfo[] getDevices();
+    method @Deprecated public android.media.midi.MidiDeviceInfo[] getDevices();
+    method @NonNull public java.util.Set<android.media.midi.MidiDeviceInfo> getDevicesForTransport(int);
     method public void openBluetoothDevice(android.bluetooth.BluetoothDevice, android.media.midi.MidiManager.OnDeviceOpenedListener, android.os.Handler);
     method public void openDevice(android.media.midi.MidiDeviceInfo, android.media.midi.MidiManager.OnDeviceOpenedListener, android.os.Handler);
-    method public void registerDeviceCallback(android.media.midi.MidiManager.DeviceCallback, android.os.Handler);
+    method @Deprecated public void registerDeviceCallback(android.media.midi.MidiManager.DeviceCallback, android.os.Handler);
+    method public void registerDeviceCallback(int, @NonNull java.util.concurrent.Executor, @NonNull android.media.midi.MidiManager.DeviceCallback);
     method public void unregisterDeviceCallback(android.media.midi.MidiManager.DeviceCallback);
+    field public static final int TRANSPORT_MIDI_BYTE_STREAM = 1; // 0x1
+    field public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2; // 0x2
   }
 
   public static class MidiManager.DeviceCallback {
@@ -26661,16 +26910,43 @@
 
 package android.media.tv.interactive {
 
-  public final class TvIAppManager {
+  public final class TvInteractiveAppInfo implements android.os.Parcelable {
+    ctor public TvInteractiveAppInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+    method public int describeContents();
+    method @NonNull public String getId();
+    method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
+    method @NonNull public int getSupportedTypes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppInfo> CREATOR;
+    field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2
+    field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4
+    field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1
   }
 
-  public abstract class TvIAppService extends android.app.Service {
-    ctor public TvIAppService();
+  public final class TvInteractiveAppManager {
+    method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppInfo> getTvInteractiveAppServiceList();
+  }
+
+  public abstract class TvInteractiveAppService extends android.app.Service {
+    ctor public TvInteractiveAppService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
+    field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvInteractiveAppService";
     field public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
   }
 
+  public class TvInteractiveAppView extends android.view.ViewGroup {
+    ctor public TvInteractiveAppView(@NonNull android.content.Context);
+    ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+    ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+    method public void clearCallback();
+    method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback);
+    method public void startInteractiveApp();
+  }
+
+  public abstract static class TvInteractiveAppView.TvInteractiveAppCallback {
+    ctor public TvInteractiveAppView.TvInteractiveAppCallback();
+  }
+
 }
 
 package android.mtp {
@@ -27355,11 +27631,13 @@
     method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress);
     method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String);
     method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int);
+    method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix);
     method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int);
     method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String);
     method @NonNull public android.net.VpnService.Builder allowBypass();
     method @NonNull public android.net.VpnService.Builder allowFamily(int);
     method @Nullable public android.os.ParcelFileDescriptor establish();
+    method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix);
     method @NonNull public android.net.VpnService.Builder setBlocking(boolean);
     method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent);
     method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo);
@@ -27677,6 +27955,25 @@
 
 package android.net.vcn {
 
+  public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+    method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
+    method public int getOpportunistic();
+    method public int getRoaming();
+    method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
+  }
+
+  public static final class VcnCellUnderlyingNetworkTemplate.Builder {
+    ctor public VcnCellUnderlyingNetworkTemplate.Builder();
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
   public final class VcnConfig implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
@@ -27695,6 +27992,7 @@
     method @NonNull public String getGatewayConnectionName();
     method @IntRange(from=0x500) public int getMaxMtu();
     method @NonNull public long[] getRetryIntervalsMillis();
+    method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
   }
 
   public static final class VcnGatewayConnectionConfig.Builder {
@@ -27704,6 +28002,7 @@
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
   }
 
   public class VcnManager {
@@ -27727,6 +28026,30 @@
     method public abstract void onStatusChanged(int);
   }
 
+  public abstract class VcnUnderlyingNetworkTemplate {
+    method public int getMetered();
+    method public int getMinEntryDownstreamBandwidthKbps();
+    method public int getMinEntryUpstreamBandwidthKbps();
+    method public int getMinExitDownstreamBandwidthKbps();
+    method public int getMinExitUpstreamBandwidthKbps();
+    field public static final int MATCH_ANY = 0; // 0x0
+    field public static final int MATCH_FORBIDDEN = 2; // 0x2
+    field public static final int MATCH_REQUIRED = 1; // 0x1
+  }
+
+  public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+    method @NonNull public java.util.Set<java.lang.String> getSsids();
+  }
+
+  public static final class VcnWifiUnderlyingNetworkTemplate.Builder {
+    ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
+  }
+
 }
 
 package android.nfc {
@@ -31823,6 +32146,7 @@
     method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale);
     method public boolean isEmpty();
     method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale);
+    method public static boolean matchesLanguageAndScript(@NonNull java.util.Locale, @NonNull java.util.Locale);
     method public static void setDefault(@NonNull @Size(min=1) android.os.LocaleList);
     method @IntRange(from=0) public int size();
     method @NonNull public String toLanguageTags();
@@ -32000,7 +32324,7 @@
     method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
     method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
-    method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
+    method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<? super T>);
     method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
     method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
     method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
@@ -32010,7 +32334,7 @@
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
     method @Deprecated @Nullable public java.io.Serializable readSerializable();
-    method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
+    method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<? super T>);
     method @NonNull public android.util.Size readSize();
     method @NonNull public android.util.SizeF readSizeF();
     method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -32280,6 +32604,7 @@
     method public static final boolean is64Bit();
     method public static boolean isApplicationUid(int);
     method public static final boolean isIsolated();
+    method public static final boolean isSupplemental();
     method public static final void killProcess(int);
     method public static final int myPid();
     method @NonNull public static String myProcessName();
@@ -32484,6 +32809,7 @@
 
   public final class SystemClock {
     method @NonNull public static java.time.Clock currentGnssTimeClock();
+    method @NonNull public static java.time.Clock currentNetworkTimeClock();
     method public static long currentThreadTimeMillis();
     method public static long elapsedRealtime();
     method public static long elapsedRealtimeNanos();
@@ -34290,6 +34616,7 @@
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+    field public static final int PRESENTATION_UNAVAILABLE = 5; // 0x5
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
     field public static final String PRIORITY = "priority";
     field public static final int PRIORITY_NORMAL = 0; // 0x0
@@ -36043,7 +36370,7 @@
     field public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
     field public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
     field public static final String FONT_SCALE = "font_scale";
-    field public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
+    field @Deprecated public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
     field @Deprecated public static final String HTTP_PROXY = "http_proxy";
     field @Deprecated public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field @Deprecated public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
@@ -36087,7 +36414,7 @@
     field public static final String USER_ROTATION = "user_rotation";
     field @Deprecated public static final String USE_GOOGLE_MAIL = "use_google_mail";
     field public static final String VIBRATE_ON = "vibrate_on";
-    field public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
+    field @Deprecated public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
     field @Deprecated public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
     field @Deprecated public static final String WALLPAPER_ACTIVITY = "wallpaper_activity";
     field @Deprecated public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
@@ -37954,6 +38281,51 @@
     ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
   }
 
+  public class CredentialDataRequest {
+    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getDeviceSignedEntriesToRequest();
+    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getIssuerSignedEntriesToRequest();
+    method @Nullable public byte[] getReaderSignature();
+    method @Nullable public byte[] getRequestMessage();
+    method public boolean isAllowUsingExhaustedKeys();
+    method public boolean isAllowUsingExpiredKeys();
+    method public boolean isIncrementUseCount();
+  }
+
+  public static final class CredentialDataRequest.Builder {
+    ctor public CredentialDataRequest.Builder();
+    method @NonNull public android.security.identity.CredentialDataRequest build();
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExhaustedKeys(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExpiredKeys(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setDeviceSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIncrementUseCount(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIssuerSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setReaderSignature(@NonNull byte[]);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setRequestMessage(@NonNull byte[]);
+  }
+
+  public abstract class CredentialDataResult {
+    method @Nullable public abstract byte[] getDeviceMac();
+    method @NonNull public abstract byte[] getDeviceNameSpaces();
+    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
+    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
+    method @NonNull public abstract byte[] getStaticAuthenticationData();
+  }
+
+  public static interface CredentialDataResult.Entries {
+    method @Nullable public byte[] getEntry(@NonNull String, @NonNull String);
+    method @NonNull public java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @NonNull public java.util.Collection<java.lang.String> getNamespaces();
+    method @NonNull public java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method public int getStatus(@NonNull String, @NonNull String);
+    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  }
+
   public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
     ctor public DocTypeNotSupportedException(@NonNull String);
     ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -37965,19 +38337,19 @@
   }
 
   public abstract class IdentityCredential {
-    method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
-    method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+    method @Deprecated @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
+    method @Deprecated @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
     method @NonNull public byte[] delete(@NonNull byte[]);
-    method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
+    method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
     method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
     method @NonNull public abstract int[] getAuthenticationDataUsageCount();
     method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
-    method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+    method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
     method @NonNull public byte[] proveOwnership(@NonNull byte[]);
-    method public abstract void setAllowUsingExhaustedKeys(boolean);
-    method public void setAllowUsingExpiredKeys(boolean);
+    method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
+    method @Deprecated public void setAllowUsingExpiredKeys(boolean);
     method public abstract void setAvailableAuthenticationKeys(int, int);
-    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
     method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
     method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
     method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
@@ -37990,6 +38362,7 @@
 
   public abstract class IdentityCredentialStore {
     method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
+    method @NonNull public android.security.identity.PresentationSession createPresentationSession(int) throws android.security.identity.CipherSuiteNotSupportedException;
     method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
     method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
     method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
@@ -38028,22 +38401,29 @@
     method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
   }
 
-  public abstract class ResultData {
-    method @NonNull public abstract byte[] getAuthenticatedData();
-    method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
-    method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
-    method @Nullable public abstract byte[] getMessageAuthenticationCode();
-    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
-    method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
-    method @NonNull public abstract byte[] getStaticAuthenticationData();
-    method public abstract int getStatus(@NonNull String, @NonNull String);
-    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
-    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
-    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
-    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
-    field public static final int STATUS_OK = 0; // 0x0
-    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
-    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  public abstract class PresentationSession {
+    method @Nullable public abstract android.security.identity.CredentialDataResult getCredentialData(@NonNull String, @NonNull android.security.identity.CredentialDataRequest) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException;
+    method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
+    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method public abstract void setSessionTranscript(@NonNull byte[]);
+  }
+
+  @Deprecated public abstract class ResultData {
+    method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
+    method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
+    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
+    method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
+    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
+    method @Deprecated public abstract int getStatus(@NonNull String, @NonNull String);
+    field @Deprecated public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field @Deprecated public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field @Deprecated public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field @Deprecated public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field @Deprecated public static final int STATUS_OK = 0; // 0x0
+    field @Deprecated public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field @Deprecated public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
   }
 
   public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
@@ -39813,8 +40193,10 @@
     ctor public RecognitionService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method protected abstract void onCancel(android.speech.RecognitionService.Callback);
+    method public void onCheckRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionService.SupportCallback);
     method protected abstract void onStartListening(android.content.Intent, android.speech.RecognitionService.Callback);
     method protected abstract void onStopListening(android.speech.RecognitionService.Callback);
+    method public void triggerModelDownload(@NonNull android.content.Intent);
     field public static final String SERVICE_INTERFACE = "android.speech.RecognitionService";
     field public static final String SERVICE_META_DATA = "android.speech";
   }
@@ -39832,6 +40214,36 @@
     method public void rmsChanged(float) throws android.os.RemoteException;
   }
 
+  public static class RecognitionService.SupportCallback {
+    method public void onError(int);
+    method public void onSupportResult(@NonNull android.speech.RecognitionSupport);
+  }
+
+  public final class RecognitionSupport implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getInstalledLanguages();
+    method @NonNull public java.util.List<java.lang.String> getPendingLanguages();
+    method @NonNull public java.util.List<java.lang.String> getSupportedLanguages();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.speech.RecognitionSupport> CREATOR;
+  }
+
+  public static final class RecognitionSupport.Builder {
+    ctor public RecognitionSupport.Builder();
+    method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguages(@NonNull String);
+    method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguages(@NonNull String);
+    method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguages(@NonNull String);
+    method @NonNull public android.speech.RecognitionSupport build();
+    method @NonNull public android.speech.RecognitionSupport.Builder setInstalledLanguages(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.speech.RecognitionSupport.Builder setPendingLanguages(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.speech.RecognitionSupport.Builder setSupportedLanguages(@NonNull java.util.List<java.lang.String>);
+  }
+
+  public interface RecognitionSupportCallback {
+    method public void onError(int);
+    method public void onSupportResult(@NonNull android.speech.RecognitionSupport);
+  }
+
   public class RecognizerIntent {
     method public static final android.content.Intent getVoiceDetailsIntent(android.content.Context);
     field public static final String ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS";
@@ -39881,6 +40293,7 @@
 
   public class SpeechRecognizer {
     method @MainThread public void cancel();
+    method public void checkRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionSupportCallback);
     method @MainThread @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
     method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
     method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
@@ -39890,8 +40303,10 @@
     method @MainThread public void setRecognitionListener(android.speech.RecognitionListener);
     method @MainThread public void startListening(android.content.Intent);
     method @MainThread public void stopListening();
+    method public void triggerModelDownload(@NonNull android.content.Intent);
     field public static final String CONFIDENCE_SCORES = "confidence_scores";
     field public static final int ERROR_AUDIO = 3; // 0x3
+    field public static final int ERROR_CANNOT_CHECK_SUPPORT = 14; // 0xe
     field public static final int ERROR_CLIENT = 5; // 0x5
     field public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9; // 0x9
     field public static final int ERROR_LANGUAGE_NOT_SUPPORTED = 12; // 0xc
@@ -40257,6 +40672,7 @@
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
+    field public static final int PROPERTY_TETHERED_CALL = 32768; // 0x8000
     field public static final int PROPERTY_VOIP_AUDIO_MODE = 4096; // 0x1000
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
@@ -40285,6 +40701,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallAudioState> CREATOR;
     field public static final int ROUTE_BLUETOOTH = 2; // 0x2
     field public static final int ROUTE_EARPIECE = 1; // 0x1
+    field public static final int ROUTE_EXTERNAL = 16; // 0x10
     field public static final int ROUTE_SPEAKER = 8; // 0x8
     field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
@@ -40559,6 +40976,7 @@
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
+    field public static final int PROPERTY_TETHERED_CALL = 16384; // 0x4000
     field public static final int PROPERTY_WIFI = 8; // 0x8
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
@@ -41063,6 +41481,7 @@
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+    field public static final int PRESENTATION_UNAVAILABLE = 5; // 0x5
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
     field public static final int PRIORITY_NORMAL = 0; // 0x0
     field public static final int PRIORITY_URGENT = 1; // 0x1
@@ -41425,11 +41844,11 @@
     field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
     field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
     field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
-    field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
+    field @Deprecated public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
     field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
     field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
-    field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
-    field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
+    field @Deprecated public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+    field @Deprecated public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
     field public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
     field public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
     field @Deprecated public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
@@ -41481,6 +41900,7 @@
     field public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool";
     field public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int";
     field public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY = "emergency_number_prefix_string_array";
+    field public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL = "enable_cross_sim_calling_on_opportunistic_data_bool";
     field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
     field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
@@ -41650,21 +42070,110 @@
     field public static final String PROTOCOL_IPV6 = "IPV6";
   }
 
+  public static final class CarrierConfigManager.Bsf {
+    field public static final String KEY_BSF_SERVER_FQDN_STRING = "bsf.bsf_server_fqdn_string";
+    field public static final String KEY_BSF_SERVER_PORT_INT = "bsf.bsf_server_port_int";
+    field public static final String KEY_BSF_TRANSPORT_TYPE_INT = "bsf.bsf_transport type_int";
+    field public static final String KEY_PREFIX = "bsf.";
+  }
+
   public static final class CarrierConfigManager.Gps {
     field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
     field public static final String KEY_PREFIX = "gps.";
   }
 
   public static final class CarrierConfigManager.Ims {
+    field public static final int E911_RTCP_INACTIVITY_ON_CONNECTED = 3; // 0x3
+    field public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4; // 0x4
+    field public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR = 4; // 0x4
+    field public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI = 2; // 0x2
+    field public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR = 3; // 0x3
+    field public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI = 1; // 0x1
+    field public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5 = 0; // 0x0
+    field public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1 = 1; // 0x1
+    field public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2; // 0x2
+    field public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1; // 0x1
+    field public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0; // 0x0
+    field public static final String KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY = "ims.key_capability_type_call_composer_int_array";
+    field public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY = "ims.key_capability_type_options_uce_int_array";
+    field public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY = "ims.key_capability_type_presence_uce_int_array";
+    field public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY = "ims.key_capability_type_sms_int_array";
+    field public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY = "ims.key_capability_type_ut_int_array";
+    field public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY = "ims.key_capability_type_video_int_array";
+    field public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY = "ims.key_capability_type_voice_int_array";
     field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
     field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
     field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
+    field public static final String KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY = "ims.geolocation_pidf_in_sip_invite_support_int_array";
+    field public static final String KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY = "ims.geolocation_pidf_in_sip_register_support_int_array";
+    field public static final String KEY_GRUU_ENABLED_BOOL = "ims.gruu_enabled_bool";
+    field public static final String KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY = "ims.ims_pdn_enabled_in_no_vops_support_int_array";
     field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+    field public static final String KEY_IMS_USER_AGENT_STRING = "ims.ims_user_agent_string";
+    field public static final String KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY = "ims.ipsec_authentication_algorithms_int_array";
+    field public static final String KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY = "ims.ipsec_encryption_algorithms_int_array";
+    field public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv4_sip_mtu_size_cellular_int";
+    field public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv6_sip_mtu_size_cellular_int";
+    field public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL = "ims.keep_pdn_up_in_no_vops_bool";
+    field public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE = "ims.mmtel_requires_provisioning_bundle";
     field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
+    field public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING = "ims.phone_context_domain_name_string";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
     field public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = "ims.rcs_feature_tag_allowed_string_array";
+    field public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE = "ims.rcs_requires_provisioning_bundle";
+    field public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL = "ims.registration_event_package_supported_bool";
+    field public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT = "ims.registration_expiry_timer_sec_int";
+    field public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT = "ims.registration_retry_base_timer_millis_int";
+    field public static final String KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT = "ims.registration_retry_max_timer_millis_int";
+    field public static final String KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT = "ims.registration_subscribe_expiry_timer_sec_int";
+    field public static final String KEY_REQUEST_URI_TYPE_INT = "ims.request_uri_type_int";
+    field public static final String KEY_SIP_OVER_IPSEC_ENABLED_BOOL = "ims.sip_over_ipsec_enabled_bool";
+    field public static final String KEY_SIP_PREFERRED_TRANSPORT_INT = "ims.sip_preferred_transport_int";
+    field public static final String KEY_SIP_SERVER_PORT_NUMBER_INT = "ims.sip_server_port_number_int";
+    field public static final String KEY_SIP_TIMER_B_MILLIS_INT = "ims.sip_timer_b_millis_int";
+    field public static final String KEY_SIP_TIMER_C_MILLIS_INT = "ims.sip_timer_c_millis_int";
+    field public static final String KEY_SIP_TIMER_D_MILLIS_INT = "ims.sip_timer_d_millis_int";
+    field public static final String KEY_SIP_TIMER_F_MILLIS_INT = "ims.sip_timer_f_millis_int";
+    field public static final String KEY_SIP_TIMER_H_MILLIS_INT = "ims.sip_timer_h_millis_int";
+    field public static final String KEY_SIP_TIMER_J_MILLIS_INT = "ims.sip_timer_j_millis_int";
+    field public static final String KEY_SIP_TIMER_T1_MILLIS_INT = "ims.sip_timer_t1_millis_int";
+    field public static final String KEY_SIP_TIMER_T2_MILLIS_INT = "ims.sip_timer_t2_millis_int";
+    field public static final String KEY_SIP_TIMER_T4_MILLIS_INT = "ims.sip_timer_t4_millis_int";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
+    field public static final int NETWORK_TYPE_HOME = 0; // 0x0
+    field public static final int NETWORK_TYPE_ROAMING = 1; // 0x1
+    field public static final int PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP = 2; // 0x2
+    field public static final int PREFERRED_TRANSPORT_TCP = 1; // 0x1
+    field public static final int PREFERRED_TRANSPORT_TLS = 3; // 0x3
+    field public static final int PREFERRED_TRANSPORT_UDP = 0; // 0x0
+    field public static final int REQUEST_URI_FORMAT_SIP = 1; // 0x1
+    field public static final int REQUEST_URI_FORMAT_TEL = 0; // 0x0
+    field public static final int RTCP_INACTIVITY_ON_CONNECTED = 1; // 0x1
+    field public static final int RTCP_INACTIVITY_ON_HOLD = 0; // 0x0
+    field public static final int RTP_INACTIVITY_ON_CONNECTED = 2; // 0x2
+  }
+
+  public static final class CarrierConfigManager.ImsEmergency {
+    field public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL = "imsemergency.emergency_callback_mode_supported_bool";
+    field public static final String KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = "imsemergency.emergency_over_ims_supported_rats_int_array";
+    field public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL = "imsemergency.emergency_qos_precondition_supported_bool";
+    field public static final String KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT = "imsemergency.emergency_registration_timer_millis_int";
+    field public static final String KEY_PREFIX = "imsemergency.";
+    field public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL = "imsemergency.retry_emergency_on_ims_pdn_bool";
+  }
+
+  public static final class CarrierConfigManager.ImsRtt {
+    field public static final String KEY_PREFIX = "imsrtt.";
+    field public static final String KEY_RED_PAYLOAD_TYPE_INT = "imsrtt.red_payload_type_int";
+    field public static final String KEY_T140_PAYLOAD_TYPE_INT = "imsrtt.t140_payload_type_int";
+    field public static final String KEY_TEXT_AS_BANDWIDTH_KBPS_INT = "imsrtt.text_as_bandwidth_kbps_int";
+    field public static final String KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = "imsrtt.text_codec_capability_payload_types_bundle";
+    field public static final String KEY_TEXT_INACTIVITY_CALL_END_REASONS_INT_ARRAY = "imsrtt.text_inactivity_call_end_reasons_int_array";
+    field public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL = "imsrtt.text_on_default_bearer_supported_bool";
+    field public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL = "imsrtt.text_qos_precondition_supported_bool";
+    field public static final String KEY_TEXT_RR_BANDWIDTH_BPS_INT = "imsrtt.text_rr_bandwidth_bps_int";
+    field public static final String KEY_TEXT_RS_BANDWIDTH_BPS_INT = "imsrtt.text_rs_bandwidth_bps_int";
   }
 
   public static final class CarrierConfigManager.ImsServiceEntitlement {
@@ -41675,6 +42184,167 @@
     field public static final String KEY_SHOW_VOWIFI_WEBVIEW_BOOL = "imsserviceentitlement.show_vowifi_webview_bool";
   }
 
+  public static final class CarrierConfigManager.ImsSms {
+    field public static final String KEY_PREFIX = "imssms.";
+    field public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL = "imssms.sms_csfb_retry_on_failure_bool";
+    field public static final String KEY_SMS_OVER_IMS_FORMAT_INT = "imssms.sms_over_ims_format_int";
+    field public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL = "imssms.sms_over_ims_supported_bool";
+    field public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = "imssms.sms_over_ims_supported_rats_int_array";
+    field public static final int SMS_FORMAT_3GPP = 0; // 0x0
+    field public static final int SMS_FORMAT_3GPP2 = 1; // 0x1
+  }
+
+  public static final class CarrierConfigManager.ImsSs {
+    field public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL = "imsss.network_initiated_ussd_over_ims_supported_bool";
+    field public static final String KEY_PREFIX = "imsss.";
+    field public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL = "imsss.use_csfb_on_xcap_over_ut_failure_bool";
+    field public static final String KEY_UT_AS_SERVER_FQDN_STRING = "imsss.ut_as_server_fqdn_string";
+    field public static final String KEY_UT_AS_SERVER_PORT_INT = "imsss.ut_as_server_port_int";
+    field public static final String KEY_UT_IPTYPE_HOME_INT = "imsss.ut_iptype_home_int";
+    field public static final String KEY_UT_IPTYPE_ROAMING_INT = "imsss.ut_iptype_roaming_int";
+    field public static final String KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL = "imsss.ut_requires_ims_registration_bool";
+    field public static final String KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY = "imsss.ut_server_based_services_int_array";
+    field public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL = "imsss.ut_supported_when_ps_data_off_bool";
+    field public static final String KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY = "imsss.ut_terminal_based_services_int_array";
+    field public static final String KEY_UT_TRANSPORT_TYPE_INT = "imsss.ut_transport_type_int";
+    field public static final String KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY = "imsss.xcap_over_ut_supported_rats_int_array";
+    field public static final int SUPPLEMENTARY_SERVICE_CB_ACR = 20; // 0x14
+    field public static final int SUPPLEMENTARY_SERVICE_CB_ALL = 12; // 0xc
+    field public static final int SUPPLEMENTARY_SERVICE_CB_BAIC = 18; // 0x12
+    field public static final int SUPPLEMENTARY_SERVICE_CB_BAOC = 14; // 0xe
+    field public static final int SUPPLEMENTARY_SERVICE_CB_BIC_ROAM = 19; // 0x13
+    field public static final int SUPPLEMENTARY_SERVICE_CB_BIL = 21; // 0x15
+    field public static final int SUPPLEMENTARY_SERVICE_CB_BOIC = 15; // 0xf
+    field public static final int SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC = 16; // 0x10
+    field public static final int SUPPLEMENTARY_SERVICE_CB_IBS = 17; // 0x11
+    field public static final int SUPPLEMENTARY_SERVICE_CB_OBS = 13; // 0xd
+    field public static final int SUPPLEMENTARY_SERVICE_CF_ALL = 1; // 0x1
+    field public static final int SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING = 3; // 0x3
+    field public static final int SUPPLEMENTARY_SERVICE_CF_CFB = 4; // 0x4
+    field public static final int SUPPLEMENTARY_SERVICE_CF_CFNL = 7; // 0x7
+    field public static final int SUPPLEMENTARY_SERVICE_CF_CFNRC = 6; // 0x6
+    field public static final int SUPPLEMENTARY_SERVICE_CF_CFNRY = 5; // 0x5
+    field public static final int SUPPLEMENTARY_SERVICE_CF_CFU = 2; // 0x2
+    field public static final int SUPPLEMENTARY_SERVICE_CW = 0; // 0x0
+    field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP = 8; // 0x8
+    field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR = 10; // 0xa
+    field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP = 9; // 0x9
+    field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR = 11; // 0xb
+  }
+
+  public static final class CarrierConfigManager.ImsVoice {
+    field public static final int ALERTING_SRVCC_SUPPORT = 1; // 0x1
+    field public static final int BANDWIDTH_EFFICIENT = 0; // 0x0
+    field public static final int BASIC_SRVCC_SUPPORT = 0; // 0x0
+    field public static final int CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG = 0; // 0x0
+    field public static final int CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG = 1; // 0x1
+    field public static final int EVS_ENCODED_BW_TYPE_FB = 3; // 0x3
+    field public static final int EVS_ENCODED_BW_TYPE_NB = 0; // 0x0
+    field public static final int EVS_ENCODED_BW_TYPE_NB_WB = 4; // 0x4
+    field public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB = 5; // 0x5
+    field public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB = 6; // 0x6
+    field public static final int EVS_ENCODED_BW_TYPE_SWB = 2; // 0x2
+    field public static final int EVS_ENCODED_BW_TYPE_WB = 1; // 0x1
+    field public static final int EVS_ENCODED_BW_TYPE_WB_SWB = 7; // 0x7
+    field public static final int EVS_ENCODED_BW_TYPE_WB_SWB_FB = 8; // 0x8
+    field public static final int EVS_OPERATIONAL_MODE_AMRWB_IO = 1; // 0x1
+    field public static final int EVS_OPERATIONAL_MODE_PRIMARY = 0; // 0x0
+    field public static final int EVS_PRIMARY_MODE_BITRATE_128_0_KBPS = 11; // 0xb
+    field public static final int EVS_PRIMARY_MODE_BITRATE_13_2_KBPS = 4; // 0x4
+    field public static final int EVS_PRIMARY_MODE_BITRATE_16_4_KBPS = 5; // 0x5
+    field public static final int EVS_PRIMARY_MODE_BITRATE_24_4_KBPS = 6; // 0x6
+    field public static final int EVS_PRIMARY_MODE_BITRATE_32_0_KBPS = 7; // 0x7
+    field public static final int EVS_PRIMARY_MODE_BITRATE_48_0_KBPS = 8; // 0x8
+    field public static final int EVS_PRIMARY_MODE_BITRATE_5_9_KBPS = 0; // 0x0
+    field public static final int EVS_PRIMARY_MODE_BITRATE_64_0_KBPS = 9; // 0x9
+    field public static final int EVS_PRIMARY_MODE_BITRATE_7_2_KBPS = 1; // 0x1
+    field public static final int EVS_PRIMARY_MODE_BITRATE_8_0_KBPS = 2; // 0x2
+    field public static final int EVS_PRIMARY_MODE_BITRATE_96_0_KBPS = 10; // 0xa
+    field public static final int EVS_PRIMARY_MODE_BITRATE_9_6_KBPS = 3; // 0x3
+    field public static final String KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE = "imsvoice.amrnb_payload_description_bundle";
+    field public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.amrnb_payload_type_int_array";
+    field public static final String KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE = "imsvoice.amrwb_payload_description_bundle";
+    field public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.amrwb_payload_type_int_array";
+    field public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY = "imsvoice.amr_codec_attribute_modeset_int_array";
+    field public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT = "imsvoice.amr_codec_attribute_payload_format_int";
+    field public static final String KEY_AUDIO_AS_BANDWIDTH_KBPS_INT = "imsvoice.audio_as_bandwidth_kbps_int";
+    field public static final String KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = "imsvoice.audio_codec_capability_payload_types_bundle";
+    field public static final String KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY = "imsvoice.audio_inactivity_call_end_reasons_int_array";
+    field public static final String KEY_AUDIO_RR_BANDWIDTH_BPS_INT = "imsvoice.audio_rr_bandwidth_bps_int";
+    field public static final String KEY_AUDIO_RS_BANDWIDTH_BPS_INT = "imsvoice.audio_rs_bandwidth_bps_int";
+    field public static final String KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT = "imsvoice.audio_rtcp_inactivity_timer_millis_int";
+    field public static final String KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT = "imsvoice.audio_rtp_inactivity_timer_millis_int";
+    field public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL = "imsvoice.carrier_volte_roaming_available_bool";
+    field public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT = "imsvoice.codec_attribute_mode_change_capability_int";
+    field public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT = "imsvoice.codec_attribute_mode_change_neighbor_int";
+    field public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT = "imsvoice.codec_attribute_mode_change_period_int";
+    field public static final String KEY_CONFERENCE_FACTORY_URI_STRING = "imsvoice.conference_factory_uri_string";
+    field public static final String KEY_CONFERENCE_SUBSCRIBE_TYPE_INT = "imsvoice.conference_subscribe_type_int";
+    field public static final String KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT = "imsvoice.dedicated_bearer_wait_timer_millis_int";
+    field public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.dtmfnb_payload_type_int_array";
+    field public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.dtmfwb_payload_type_int_array";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT = "imsvoice.evs_codec_attribute_bandwidth_int";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY = "imsvoice.evs_codec_attribute_bitrate_int_array";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT = "imsvoice.evs_codec_attribute_channels_int";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT = "imsvoice.evs_codec_attribute_ch_aw_recv_int";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT = "imsvoice.codec_attribute_cmr_int";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL = "imsvoice.evs_codec_attribute_dtx_bool";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL = "imsvoice.evs_codec_attribute_dtx_recv_bool";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT = "imsvoice.evs_codec_attribute_hf_only_int";
+    field public static final String KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT = "imsvoice.evs_codec_attribute_mode_switch_int";
+    field public static final String KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE = "imsvoice.evs_payload_description_bundle";
+    field public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.evs_payload_type_int_array";
+    field public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL = "imsvoice.include_caller_id_service_codes_in_sip_invite_bool";
+    field public static final String KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT = "imsvoice.minimum_session_expires_timer_sec_int";
+    field public static final String KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT = "imsvoice.mo_call_request_timeout_millis_int";
+    field public static final String KEY_MULTIENDPOINT_SUPPORTED_BOOL = "imsvoice.multiendpoint_supported_bool";
+    field public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL = "imsvoice.oip_source_from_header_bool";
+    field public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL = "imsvoice.prack_supported_for_18x_bool";
+    field public static final String KEY_PREFIX = "imsvoice.";
+    field public static final String KEY_RINGBACK_TIMER_MILLIS_INT = "imsvoice.ringback_timer_millis_int";
+    field public static final String KEY_RINGING_TIMER_MILLIS_INT = "imsvoice.ringing_timer_millis_int";
+    field public static final String KEY_SESSION_EXPIRES_TIMER_SEC_INT = "imsvoice.session_expires_timer_sec_int";
+    field public static final String KEY_SESSION_REFRESHER_TYPE_INT = "imsvoice.session_refresher_type_int";
+    field public static final String KEY_SESSION_REFRESH_METHOD_INT = "imsvoice.session_refresh_method_int";
+    field public static final String KEY_SESSION_TIMER_SUPPORTED_BOOL = "imsvoice.session_timer_supported_bool";
+    field public static final String KEY_SRVCC_TYPE_INT_ARRAY = "imsvoice.srvcc_type_int_array";
+    field public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL = "imsvoice.voice_on_default_bearer_supported_bool";
+    field public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL = "imsvoice.voice_qos_precondition_supported_bool";
+    field public static final int MIDCALL_SRVCC_SUPPORT = 3; // 0x3
+    field public static final int OCTET_ALIGNED = 1; // 0x1
+    field public static final int PREALERTING_SRVCC_SUPPORT = 2; // 0x2
+    field public static final int SESSION_REFRESHER_TYPE_UAC = 1; // 0x1
+    field public static final int SESSION_REFRESHER_TYPE_UAS = 2; // 0x2
+    field public static final int SESSION_REFRESHER_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int SESSION_REFRESH_METHOD_INVITE = 0; // 0x0
+    field public static final int SESSION_REFRESH_METHOD_UPDATE_PREFERRED = 1; // 0x1
+  }
+
+  public static final class CarrierConfigManager.ImsVt {
+    field public static final String KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE = "imsvt.h264_payload_description_bundle";
+    field public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY = "imsvt.h264_payload_type_int_array";
+    field public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING = "imsvt.h264_video_codec_attribute_profile_level_id_string";
+    field public static final String KEY_PREFIX = "imsvt.";
+    field public static final String KEY_VIDEO_AS_BANDWIDTH_KBPS_INT = "imsvt.video_as_bandwidth_kbps_int";
+    field public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT = "imsvt.video_codec_attribute_frame_rate_int";
+    field public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT = "imsvt.video_codec_attribute_packetization_mode_int";
+    field public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY = "imsvt.video_codec_attribute_resolution_int_array";
+    field public static final String KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = "imsvt.video_codec_capability_payload_types_bundle";
+    field public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL = "imsvt.video_on_default_bearer_supported_bool";
+    field public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL = "imsvt.video_qos_precondition_supported_bool";
+    field public static final String KEY_VIDEO_RR_BANDWIDTH_BPS_INT = "imsvt.video_rr_bandwidth_bps_int";
+    field public static final String KEY_VIDEO_RS_BANDWIDTH_BPS_INT = "imsvt.video_rs_bandwidth_bps_int";
+    field public static final String KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT = "imsvt.video_rtcp_inactivity_timer_millis_int";
+    field public static final String KEY_VIDEO_RTP_DSCP_INT = "imsvt.video_rtp_dscp_int";
+    field public static final String KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT = "imsvt.video_rtp_inactivity_timer_millis_int";
+  }
+
+  public static final class CarrierConfigManager.ImsWfc {
+    field public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL = "imswfc.emergency_call_over_emergency_pdn_bool";
+    field public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY = "imswfc.pidf_short_code_string_array";
+    field public static final String KEY_PREFIX = "imswfc.";
+  }
+
   public static final class CarrierConfigManager.Iwlan {
     field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
     field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
@@ -41682,6 +42352,7 @@
     field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
     field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
     field public static final int EPDG_ADDRESS_STATIC = 0; // 0x0
+    field public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4; // 0x4
     field public static final int ID_TYPE_FQDN = 2; // 0x2
     field public static final int ID_TYPE_KEY_ID = 11; // 0xb
     field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
@@ -41713,6 +42384,7 @@
     field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
     field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
     field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
+    field public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.supports_eap_aka_fast_reauth_bool";
   }
 
   public abstract class CellIdentity implements android.os.Parcelable {
@@ -42942,6 +43614,7 @@
     field public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; // 0x7b
     field public static final int RESULT_RIL_CANCELLED = 119; // 0x77
     field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d
+    field public static final int RESULT_RIL_GENERIC_ERROR = 124; // 0x7c
     field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71
     field public static final int RESULT_RIL_INVALID_ARGUMENTS = 104; // 0x68
     field public static final int RESULT_RIL_INVALID_MODEM_STATE = 115; // 0x73
@@ -43750,6 +44423,7 @@
     field public static final int TYPE_DEFAULT = 17; // 0x11
     field public static final int TYPE_DUN = 8; // 0x8
     field public static final int TYPE_EMERGENCY = 512; // 0x200
+    field public static final int TYPE_ENTERPRISE = 16384; // 0x4000
     field public static final int TYPE_FOTA = 32; // 0x20
     field public static final int TYPE_HIPRI = 16; // 0x10
     field public static final int TYPE_IA = 256; // 0x100
@@ -43946,8 +44620,8 @@
     method public boolean isEnabled();
     method public boolean isSimPortAvailable(int);
     method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
-    method @Deprecated @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent);
-    method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccManager.ResultListener);
+    method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent);
+    method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, int, @NonNull android.app.PendingIntent);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void updateSubscriptionNickname(int, @Nullable String, @NonNull android.app.PendingIntent);
     field public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
     field public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
@@ -43966,6 +44640,7 @@
     field public static final int ERROR_INSTALL_PROFILE = 10009; // 0x2719
     field public static final int ERROR_INVALID_ACTIVATION_CODE = 10001; // 0x2711
     field public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002; // 0x2712
+    field public static final int ERROR_INVALID_PORT = 10017; // 0x2721
     field public static final int ERROR_INVALID_RESPONSE = 10015; // 0x271f
     field public static final int ERROR_NO_PROFILES_AVAILABLE = 10013; // 0x271d
     field public static final int ERROR_OPERATION_BUSY = 10016; // 0x2720
@@ -43993,10 +44668,6 @@
     field public static final int OPERATION_SYSTEM = 1; // 0x1
   }
 
-  public static interface EuiccManager.ResultListener {
-    method public void onComplete(int, @Nullable android.content.Intent);
-  }
-
 }
 
 package android.telephony.gsm {
@@ -44099,6 +44770,7 @@
   public class ImsManager {
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+    method @NonNull public android.telephony.ims.ProvisioningManager getProvisioningManager(int);
     field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
     field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
     field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
@@ -44349,6 +45021,23 @@
     field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1
   }
 
+  public class ProvisioningManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int, int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isProvisioningRequiredForCapability(int, int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isRcsProvisioningRequiredForCapability(int, int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerFeatureProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, int, boolean);
+    method public void unregisterFeatureProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback);
+  }
+
+  public static class ProvisioningManager.FeatureProvisioningCallback {
+    ctor public ProvisioningManager.FeatureProvisioningCallback();
+    method public void onFeatureProvisioningChanged(int, int, boolean);
+    method public void onRcsFeatureProvisioningChanged(int, int, boolean);
+  }
+
   public class RcsUceAdapter {
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
   }
@@ -44389,6 +45078,27 @@
     field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
   }
 
+  public class RcsFeature {
+  }
+
+  public static class RcsFeature.RcsImsCapabilities {
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+  }
+
+}
+
+package android.telephony.ims.stub {
+
+  public class ImsRegistrationImplBase {
+    field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
+    field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
+    field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
+    field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
+    field public static final int REGISTRATION_TECH_NR = 3; // 0x3
+  }
+
 }
 
 package android.telephony.mbms {
@@ -44647,6 +45357,7 @@
   public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback {
     ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
     ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+    ctor public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
     method public void ellipsized(int, int);
     method public int getBottomPadding();
     method public int getEllipsisCount(int);
@@ -44661,9 +45372,12 @@
     method public int getTopPadding();
     method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
     method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
+    method @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
     method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
     method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+    method @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
     method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
+    method @NonNull public android.text.BoringLayout replaceOrMake(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
     method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
   }
 
@@ -44872,6 +45586,7 @@
     method public abstract int getTopPadding();
     method public final int getWidth();
     method public final void increaseWidthTo(int);
+    method public boolean isFallbackLineSpacingEnabled();
     method public boolean isRtlCharAt(int);
     method protected final boolean isSpanned();
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -44959,6 +45674,7 @@
   public static final class PrecomputedText.Params {
     method public int getBreakStrategy();
     method public int getHyphenationFrequency();
+    method @Nullable public android.graphics.text.LineBreakConfig getLineBreakConfig();
     method @NonNull public android.text.TextDirectionHeuristic getTextDirection();
     method @NonNull public android.text.TextPaint getTextPaint();
   }
@@ -44969,6 +45685,7 @@
     method @NonNull public android.text.PrecomputedText.Params build();
     method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
     method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
+    method @NonNull public android.text.PrecomputedText.Params.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
     method public android.text.PrecomputedText.Params.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
   }
 
@@ -45129,6 +45846,7 @@
     method @NonNull public android.text.StaticLayout.Builder setIncludePad(boolean);
     method @NonNull public android.text.StaticLayout.Builder setIndents(@Nullable int[], @Nullable int[]);
     method @NonNull public android.text.StaticLayout.Builder setJustificationMode(int);
+    method @NonNull public android.text.StaticLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
     method @NonNull public android.text.StaticLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
     method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int);
     method public android.text.StaticLayout.Builder setText(CharSequence);
@@ -46842,6 +47560,15 @@
     field public float ydpi;
   }
 
+  public interface Dumpable {
+    method public void dump(@NonNull java.io.PrintWriter, @Nullable String[]);
+    method @NonNull public default String getDumpableName();
+  }
+
+  public interface DumpableContainer {
+    method public boolean addDumpable(@NonNull android.util.Dumpable);
+  }
+
   public class EventLog {
     method public static int getTagCode(String);
     method public static String getTagName(int);
@@ -47595,15 +48322,33 @@
 
   public final class Choreographer {
     method public static android.view.Choreographer getInstance();
+    method public void postExtendedFrameCallback(@NonNull android.view.Choreographer.ExtendedFrameCallback);
     method public void postFrameCallback(android.view.Choreographer.FrameCallback);
     method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long);
+    method public void removeExtendedFrameCallback(@Nullable android.view.Choreographer.ExtendedFrameCallback);
     method public void removeFrameCallback(android.view.Choreographer.FrameCallback);
   }
 
+  public static interface Choreographer.ExtendedFrameCallback {
+    method public void onVsync(@NonNull android.view.Choreographer.FrameData);
+  }
+
   public static interface Choreographer.FrameCallback {
     method public void doFrame(long);
   }
 
+  public static class Choreographer.FrameData {
+    method public long getFrameTimeNanos();
+    method @NonNull public android.view.Choreographer.FrameTimeline[] getFrameTimelines();
+    method @NonNull public android.view.Choreographer.FrameTimeline getPreferredFrameTimeline();
+  }
+
+  public static class Choreographer.FrameTimeline {
+    method public long getDeadlineNanos();
+    method public long getExpectedPresentTimeNanos();
+    method public long getVsyncId();
+  }
+
   public interface CollapsibleActionView {
     method public void onActionViewCollapsed();
     method public void onActionViewExpanded();
@@ -47749,6 +48494,18 @@
     method @NonNull public android.graphics.Insets getWaterfallInsets();
   }
 
+  public static final class DisplayCutout.Builder {
+    ctor public DisplayCutout.Builder();
+    method @NonNull public android.view.DisplayCutout build();
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectBottom(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectLeft(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectRight(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectTop(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setCutoutPath(@NonNull android.graphics.Path);
+    method @NonNull public android.view.DisplayCutout.Builder setSafeInsets(@NonNull android.graphics.Insets);
+    method @NonNull public android.view.DisplayCutout.Builder setWaterfallInsets(@NonNull android.graphics.Insets);
+  }
+
   public final class DragAndDropPermissions implements android.os.Parcelable {
     method public int describeContents();
     method public void release();
@@ -47903,7 +48660,7 @@
     field public static final int CLOCK_TICK = 4; // 0x4
     field public static final int CONFIRM = 16; // 0x10
     field public static final int CONTEXT_CLICK = 6; // 0x6
-    field public static final int FLAG_IGNORE_GLOBAL_SETTING = 2; // 0x2
+    field @Deprecated public static final int FLAG_IGNORE_GLOBAL_SETTING = 2; // 0x2
     field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
     field public static final int GESTURE_END = 13; // 0xd
     field public static final int GESTURE_START = 12; // 0xc
@@ -47933,6 +48690,7 @@
     method public static int[] getDeviceIds();
     method public int getId();
     method public android.view.KeyCharacterMap getKeyCharacterMap();
+    method public int getKeyCodeForKeyLocation(int);
     method public int getKeyboardType();
     method @NonNull public android.hardware.lights.LightsManager getLightsManager();
     method public android.view.InputDevice.MotionRange getMotionRange(int);
@@ -48841,6 +49599,7 @@
     field public static final int EDGE_LEFT = 4; // 0x4
     field public static final int EDGE_RIGHT = 8; // 0x8
     field public static final int EDGE_TOP = 1; // 0x1
+    field public static final int FLAG_CANCELED = 32; // 0x20
     field public static final int FLAG_WINDOW_IS_OBSCURED = 1; // 0x1
     field public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 2; // 0x2
     field public static final int INVALID_POINTER_ID = -1; // 0xffffffff
@@ -48878,6 +49637,22 @@
     field public int toolType;
   }
 
+  public interface OnBackInvokedCallback {
+    method public default void onBackInvoked();
+  }
+
+  public abstract class OnBackInvokedDispatcher {
+    ctor public OnBackInvokedDispatcher();
+    method public abstract void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, int);
+    method public abstract void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback);
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240
+  }
+
+  public interface OnBackInvokedDispatcherOwner {
+    method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+  }
+
   public interface OnReceiveContentListener {
     method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.View, @NonNull android.view.ContentInfo);
   }
@@ -49301,7 +50076,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedMotionEvent> CREATOR;
   }
 
-  @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+  @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
     ctor public View(android.content.Context);
     ctor public View(android.content.Context, @Nullable android.util.AttributeSet);
     ctor public View(android.content.Context, @Nullable android.util.AttributeSet, int);
@@ -49505,6 +50280,7 @@
     method @IdRes public int getNextFocusLeftId();
     method @IdRes public int getNextFocusRightId();
     method @IdRes public int getNextFocusUpId();
+    method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method @ColorInt public int getOutlineAmbientShadowColor();
     method public android.view.ViewOutlineProvider getOutlineProvider();
@@ -49522,6 +50298,7 @@
     method public float getPivotX();
     method public float getPivotY();
     method public android.view.PointerIcon getPointerIcon();
+    method @NonNull public final java.util.List<android.graphics.Rect> getPreferKeepClearRects();
     method @Nullable public String[] getReceiveContentMimeTypes();
     method public android.content.res.Resources getResources();
     method public final boolean getRevealOnFocusHint();
@@ -49639,6 +50416,7 @@
     method protected boolean isPaddingOffsetRequired();
     method public boolean isPaddingRelative();
     method public boolean isPivotSet();
+    method public final boolean isPreferKeepClear();
     method public boolean isPressed();
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
@@ -49881,6 +50659,8 @@
     method public void setPivotX(float);
     method public void setPivotY(float);
     method public void setPointerIcon(android.view.PointerIcon);
+    method public final void setPreferKeepClear(boolean);
+    method public final void setPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
     method public void setPressed(boolean);
     method public void setRenderEffect(@Nullable android.graphics.RenderEffect);
     method public final void setRevealOnFocusHint(boolean);
@@ -51933,6 +52713,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
     field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
@@ -51942,7 +52723,10 @@
     method public final float getFontScale();
     method @Nullable public final java.util.Locale getLocale();
     method @NonNull public android.view.accessibility.CaptioningManager.CaptionStyle getUserStyle();
+    method public boolean isCallCaptioningEnabled();
     method public final boolean isEnabled();
+    method public final boolean isSystemAudioCaptioningRequested();
+    method public final boolean isSystemAudioCaptioningUiRequested();
     method public void removeCaptioningChangeListener(@NonNull android.view.accessibility.CaptioningManager.CaptioningChangeListener);
   }
 
@@ -51971,6 +52755,8 @@
     method public void onEnabledChanged(boolean);
     method public void onFontScaleChanged(float);
     method public void onLocaleChanged(@Nullable java.util.Locale);
+    method public void onSystemAudioCaptioningChanged(boolean);
+    method public void onSystemAudioCaptioningUiChanged(boolean);
     method public void onUserStyleChanged(@NonNull android.view.accessibility.CaptioningManager.CaptionStyle);
   }
 
@@ -52013,6 +52799,7 @@
     method public int getRepeatCount();
     method public int getRepeatMode();
     method protected float getScaleFactor();
+    method public boolean getShowBackground();
     method public long getStartOffset();
     method public long getStartTime();
     method public boolean getTransformation(long, android.view.animation.Transformation);
@@ -52038,6 +52825,7 @@
     method public void setInterpolator(android.view.animation.Interpolator);
     method public void setRepeatCount(int);
     method public void setRepeatMode(int);
+    method public void setShowBackground(boolean);
     method public void setStartOffset(long);
     method public void setStartTime(long);
     method public void setZAdjustment(int);
@@ -52543,6 +53331,7 @@
     method public int getCharacterBoundsFlags(int);
     method public CharSequence getComposingText();
     method public int getComposingTextStart();
+    method @Nullable public android.view.inputmethod.EditorBoundsInfo getEditorBoundsInfo();
     method public float getInsertionMarkerBaseline();
     method public float getInsertionMarkerBottom();
     method public int getInsertionMarkerFlags();
@@ -52564,11 +53353,27 @@
     method public android.view.inputmethod.CursorAnchorInfo build();
     method public void reset();
     method public android.view.inputmethod.CursorAnchorInfo.Builder setComposingText(int, CharSequence);
+    method @NonNull public android.view.inputmethod.CursorAnchorInfo.Builder setEditorBoundsInfo(@Nullable android.view.inputmethod.EditorBoundsInfo);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float, int);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setMatrix(android.graphics.Matrix);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
   }
 
+  public final class EditorBoundsInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.graphics.RectF getEditorBounds();
+    method @Nullable public android.graphics.RectF getHandwritingBounds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorBoundsInfo> CREATOR;
+  }
+
+  public static final class EditorBoundsInfo.Builder {
+    ctor public EditorBoundsInfo.Builder();
+    method @NonNull public android.view.inputmethod.EditorBoundsInfo build();
+    method @NonNull public android.view.inputmethod.EditorBoundsInfo.Builder setEditorBounds(@Nullable android.graphics.RectF);
+    method @NonNull public android.view.inputmethod.EditorBoundsInfo.Builder setHandwritingBounds(@Nullable android.graphics.RectF);
+  }
+
   public class EditorInfo implements android.text.InputType android.os.Parcelable {
     ctor public EditorInfo();
     method public int describeContents();
@@ -52878,6 +53683,7 @@
     method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
     method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int);
     method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int);
+    method public void startStylusHandwriting(@NonNull android.view.View);
     method @Deprecated public boolean switchToLastInputMethod(android.os.IBinder);
     method @Deprecated public boolean switchToNextInputMethod(android.os.IBinder, boolean);
     method @Deprecated public void toggleSoftInput(int, int);
@@ -54694,6 +55500,7 @@
     method protected boolean isInFilterMode();
     method public boolean isItemChecked(int);
     method public boolean isScrollingCacheEnabled();
+    method public boolean isSelectedChildViewEnabled();
     method public boolean isSmoothScrollbarEnabled();
     method public boolean isStackFromBottom();
     method public boolean isTextFilterEnabled();
@@ -54729,6 +55536,7 @@
     method public void setRemoteViewsAdapter(android.content.Intent);
     method public void setScrollIndicators(android.view.View, android.view.View);
     method public void setScrollingCacheEnabled(boolean);
+    method public void setSelectedChildViewEnabled(boolean);
     method public void setSelectionFromTop(int, int);
     method public void setSelector(@DrawableRes int);
     method public void setSelector(android.graphics.drawable.Drawable);
@@ -57141,6 +57949,7 @@
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
     method public int getLineBounds(int, android.graphics.Rect);
+    method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
     method public int getLineCount();
     method public int getLineHeight();
     method public float getLineSpacingExtra();
@@ -57268,6 +58077,7 @@
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLastBaselineToBottomHeight(@IntRange(from=0) @Px int);
     method public void setLetterSpacing(float);
+    method public void setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
     method public void setLineHeight(@IntRange(from=0) @Px int);
     method public void setLineSpacing(float, float);
     method public void setLines(int);
@@ -57676,6 +58486,8 @@
     method public void clearOnExitAnimationListener();
     method public void setOnExitAnimationListener(@NonNull android.window.SplashScreen.OnExitAnimationListener);
     method public void setSplashScreenTheme(@StyleRes int);
+    field public static final int SPLASH_SCREEN_STYLE_EMPTY = 0; // 0x0
+    field public static final int SPLASH_SCREEN_STYLE_ICON = 1; // 0x1
   }
 
   public static interface SplashScreen.OnExitAnimationListener {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 04c1e9e..4134223 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -9,6 +9,10 @@
 
 package android.app {
 
+  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+    method public final boolean addDumpable(@NonNull android.util.Dumpable);
+  }
+
   public class ActivityManager {
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
@@ -67,7 +71,30 @@
 package android.app.usage {
 
   public class NetworkStatsManager {
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate();
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>);
+    method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long);
+    method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(@NonNull android.net.NetworkTemplate, long, long, int, int, int) throws java.lang.SecurityException;
+    method @NonNull @WorkerThread public android.app.usage.NetworkStats querySummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException;
+    method @NonNull @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(@NonNull android.net.NetworkTemplate, long, long);
+    method @NonNull @WorkerThread public android.app.usage.NetworkStats queryTaggedSummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException;
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setDefaultGlobalAlert(long);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setStatsProviderWarningAndLimitAsync(@NonNull String, long, long);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setUidForeground(int, boolean);
+  }
+
+}
+
+package android.bluetooth {
+
+  public class BluetoothFrameworkInitializer {
+    method public static void registerServiceWrappers();
+    method public static void setBluetoothServiceManager(@NonNull android.os.BluetoothServiceManager);
+  }
+
+  public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public android.net.TetheringManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheredInterfaceCallback);
   }
 
 }
@@ -88,6 +115,14 @@
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
+  public class Intent implements java.lang.Cloneable android.os.Parcelable {
+    field public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
+    field public static final String EXTRA_SETTING_NAME = "setting_name";
+    field public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
+    field public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
+    field public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
+  }
+
 }
 
 package android.content.pm {
@@ -117,10 +152,12 @@
     field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
     field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
     field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff
+    field public static final int USB_HAL_RETRY = -2; // 0xfffffffe
     field public static final int USB_HAL_V1_0 = 10; // 0xa
     field public static final int USB_HAL_V1_1 = 11; // 0xb
     field public static final int USB_HAL_V1_2 = 12; // 0xc
     field public static final int USB_HAL_V1_3 = 13; // 0xd
+    field public static final int USB_HAL_V2_0 = 20; // 0x14
   }
 
 }
@@ -152,6 +189,7 @@
 
   public final class BtProfileConnectionInfo implements android.os.Parcelable {
     method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
+    method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
     method public int describeContents();
     method public boolean getIsLeOutput();
     method public int getProfile();
@@ -233,11 +271,39 @@
     method public int getResourceId();
   }
 
+  public class NetworkIdentity {
+    method public int getOemManaged();
+    method public int getRatType();
+    method @Nullable public String getSubscriberId();
+    method public int getType();
+    method @Nullable public String getWifiNetworkKey();
+    method public boolean isDefaultNetwork();
+    method public boolean isMetered();
+    method public boolean isRoaming();
+  }
+
+  public static final class NetworkIdentity.Builder {
+    ctor public NetworkIdentity.Builder();
+    method @NonNull public android.net.NetworkIdentity build();
+    method @NonNull public android.net.NetworkIdentity.Builder clearRatType();
+    method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean);
+    method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean);
+    method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot);
+    method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int);
+    method @NonNull public android.net.NetworkIdentity.Builder setRatType(int);
+    method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean);
+    method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String);
+    method @NonNull public android.net.NetworkIdentity.Builder setType(int);
+    method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String);
+  }
+
   public class NetworkPolicyManager {
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network);
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int);
+    method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.telephony.SubscriptionPlan getSubscriptionPlan(@NonNull android.net.NetworkTemplate);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningOrLimitReached();
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
   }
@@ -258,6 +324,67 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
   }
 
+  public final class NetworkStatsHistory implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR;
+  }
+
+  public static final class NetworkStatsHistory.Builder {
+    ctor public NetworkStatsHistory.Builder(long, int);
+    method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry);
+    method @NonNull public android.net.NetworkStatsHistory build();
+  }
+
+  public static final class NetworkStatsHistory.Entry {
+    ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long);
+    method public long getActiveTime();
+    method public long getBucketStart();
+    method public long getOperations();
+    method public long getRxBytes();
+    method public long getRxPackets();
+    method public long getTxBytes();
+    method public long getTxPackets();
+  }
+
+  public final class NetworkTemplate implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDefaultNetworkStatus();
+    method public int getMatchRule();
+    method public int getMeteredness();
+    method public int getOemManaged();
+    method public int getRatType();
+    method public int getRoaming();
+    method @NonNull public java.util.Set<java.lang.String> getSubscriberIds();
+    method @NonNull public java.util.Set<java.lang.String> getWifiNetworkKeys();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkTemplate> CREATOR;
+    field public static final int MATCH_BLUETOOTH = 8; // 0x8
+    field public static final int MATCH_CARRIER = 10; // 0xa
+    field public static final int MATCH_ETHERNET = 5; // 0x5
+    field public static final int MATCH_MOBILE = 1; // 0x1
+    field public static final int MATCH_WIFI = 4; // 0x4
+    field public static final int NETWORK_TYPE_ALL = -1; // 0xffffffff
+    field public static final int OEM_MANAGED_ALL = -1; // 0xffffffff
+    field public static final int OEM_MANAGED_NO = 0; // 0x0
+    field public static final int OEM_MANAGED_PAID = 1; // 0x1
+    field public static final int OEM_MANAGED_PRIVATE = 2; // 0x2
+    field public static final int OEM_MANAGED_YES = -2; // 0xfffffffe
+  }
+
+  public static final class NetworkTemplate.Builder {
+    ctor public NetworkTemplate.Builder(int);
+    method @NonNull public android.net.NetworkTemplate build();
+    method @NonNull public android.net.NetworkTemplate.Builder setDefaultNetworkStatus(int);
+    method @NonNull public android.net.NetworkTemplate.Builder setMeteredness(int);
+    method @NonNull public android.net.NetworkTemplate.Builder setOemManaged(int);
+    method @NonNull public android.net.NetworkTemplate.Builder setRatType(int);
+    method @NonNull public android.net.NetworkTemplate.Builder setRoaming(int);
+    method @NonNull public android.net.NetworkTemplate.Builder setSubscriberIds(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public android.net.NetworkTemplate.Builder setWifiNetworkKeys(@NonNull java.util.Set<java.lang.String>);
+  }
+
   public class NetworkWatchlistManager {
     method @Nullable public byte[] getWatchlistConfigHash();
   }
@@ -276,6 +403,10 @@
     method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
   }
 
+  public class TrafficStats {
+    method public static void init(@NonNull android.content.Context);
+  }
+
   public final class UnderlyingNetworkInfo implements android.os.Parcelable {
     ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
@@ -306,6 +437,21 @@
     method public final void markVintfStability();
   }
 
+  public class BluetoothServiceManager {
+    method @NonNull public android.os.BluetoothServiceManager.ServiceRegisterer getBluetoothManagerServiceRegisterer();
+  }
+
+  public static class BluetoothServiceManager.ServiceNotFoundException extends java.lang.Exception {
+    ctor public BluetoothServiceManager.ServiceNotFoundException(@NonNull String);
+  }
+
+  public static final class BluetoothServiceManager.ServiceRegisterer {
+    method @Nullable public android.os.IBinder get();
+    method @NonNull public android.os.IBinder getOrThrow() throws android.os.BluetoothServiceManager.ServiceNotFoundException;
+    method public void register(@NonNull android.os.IBinder);
+    method @Nullable public android.os.IBinder tryGet();
+  }
+
   public class Build {
     method public static boolean isDebuggable();
   }
@@ -319,6 +465,10 @@
   }
 
   public class Process {
+    method public static final boolean isSupplemental(int);
+    method public static final int toAppUid(int);
+    method public static final int toSupplementalUid(int);
+    field public static final int NFC_UID = 1027; // 0x403
     field public static final int VPN_UID = 1016; // 0x3f8
   }
 
@@ -342,10 +492,24 @@
     method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
   }
 
+  public final class StrictMode {
+    method public static void noteUntaggedSocket();
+  }
+
   public class SystemConfigManager {
     method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
   }
 
+  public final class Trace {
+    method public static void asyncTraceBegin(long, @NonNull String, int);
+    method public static void asyncTraceEnd(long, @NonNull String, int);
+    method public static boolean isTagEnabled(long);
+    method public static void traceBegin(long, @NonNull String);
+    method public static void traceCounter(long, @NonNull String, int);
+    method public static void traceEnd(long);
+    field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L
+  }
+
 }
 
 package android.os.storage {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 07639fb..311b110 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -513,7 +513,7 @@
 
 package android.view {
 
-  @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+  @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
     method protected void initializeFadingEdge(android.content.res.TypedArray);
     method protected void initializeScrollbars(android.content.res.TypedArray);
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1f562ac..f9d802b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2,12 +2,14 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final String ACCESS_AMBIENT_CONTEXT_EVENT = "android.permission.ACCESS_AMBIENT_CONTEXT_EVENT";
     field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
     field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
     field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
     field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
     field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
     field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
+    field public static final String ACCESS_FPS_COUNTER = "android.permission.ACCESS_FPS_COUNTER";
     field public static final String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
     field public static final String ACCESS_LOCUS_ID_USAGE_STATS = "android.permission.ACCESS_LOCUS_ID_USAGE_STATS";
     field public static final String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
@@ -23,6 +25,7 @@
     field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
     field public static final String ACCESS_TV_SHARED_FILTER = "android.permission.ACCESS_TV_SHARED_FILTER";
     field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER";
+    field public static final String ACCESS_ULTRASOUND = "android.permission.ACCESS_ULTRASOUND";
     field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
@@ -36,6 +39,7 @@
     field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BACKUP = "android.permission.BACKUP";
     field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
+    field public static final String BIND_AMBIENT_CONTEXT_DETECTION_SERVICE = "android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE";
     field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
     field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
     field public static final String BIND_CALL_DIAGNOSTIC_SERVICE = "android.permission.BIND_CALL_DIAGNOSTIC_SERVICE";
@@ -138,6 +142,7 @@
     field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
     field public static final String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
     field public static final String KILL_UID = "android.permission.KILL_UID";
+    field public static final String LAUNCH_DEVICE_MANAGER_SETUP = "android.permission.LAUNCH_DEVICE_MANAGER_SETUP";
     field public static final String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
     field public static final String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
     field public static final String LOOP_RADIO = "android.permission.LOOP_RADIO";
@@ -149,6 +154,7 @@
     field public static final String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS";
     field public static final String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
     field public static final String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
+    field public static final String MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED = "android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED";
     field public static final String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
     field public static final String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
@@ -156,6 +162,7 @@
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+    field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
     field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
@@ -165,6 +172,7 @@
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
+    field public static final String MANAGE_SAFETY_CENTER = "android.permission.MANAGE_SAFETY_CENTER";
     field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI";
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
@@ -177,6 +185,7 @@
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
+    field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
     field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
     field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
     field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
@@ -226,7 +235,6 @@
     field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
     field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
-    field public static final String READ_COMMUNAL_STATE = "android.permission.READ_COMMUNAL_STATE";
     field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
     field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
     field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
@@ -291,13 +299,16 @@
     field public static final String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
     field public static final String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
     field public static final String SET_SCREEN_COMPATIBILITY = "android.permission.SET_SCREEN_COMPATIBILITY";
+    field public static final String SET_SYSTEM_AUDIO_CAPTION = "android.permission.SET_SYSTEM_AUDIO_CAPTION";
     field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
+    field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
     field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
     field public static final String SOUND_TRIGGER_RUN_IN_BATTERY_SAVER = "android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER";
     field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
+    field public static final String START_CROSS_PROFILE_ACTIVITIES = "android.permission.START_CROSS_PROFILE_ACTIVITIES";
     field public static final String START_REVIEW_PERMISSION_DECISIONS = "android.permission.START_REVIEW_PERMISSION_DECISIONS";
     field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
@@ -342,6 +353,7 @@
 
   public static final class R.array {
     field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
+    field public static final int config_optionalIpSecAlgorithms;
   }
 
   public static final class R.attr {
@@ -359,11 +371,11 @@
   }
 
   public static final class R.bool {
+    field public static final int config_enableQrCodeScannerOnLockScreen;
     field public static final int config_sendPackageName = 17891328; // 0x1110000
     field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
     field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
     field public static final int config_showDefaultHome = 17891331; // 0x1110003
-    field public static final int config_systemCaptionsServiceCallsEnabled;
   }
 
   public static final class R.color {
@@ -387,6 +399,7 @@
     field public static final int config_customMediaKeyDispatcher = 17039404; // 0x104002c
     field public static final int config_customMediaSessionPolicyProvider = 17039405; // 0x104002d
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
+    field public static final int config_defaultAutomotiveNavigation;
     field public static final int config_defaultBrowser = 17039394; // 0x1040022
     field public static final int config_defaultCallRedirection = 17039397; // 0x1040025
     field public static final int config_defaultCallScreening = 17039398; // 0x1040026
@@ -403,6 +416,7 @@
     field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033
     field public static final int config_systemAppProtectionService;
     field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034
+    field public static final int config_systemAutomotiveCalendarSyncManager;
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
     field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
@@ -438,7 +452,7 @@
 
 package android.app {
 
-  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     method public void convertFromTranslucent();
     method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions);
     method @Deprecated public boolean isBackgroundVisibleBehind();
@@ -745,7 +759,17 @@
   }
 
   public final class GameManager {
-    method @RequiresPermission("android.permission.MANAGE_GAME_MODE") public void setGameMode(@NonNull String, int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public android.app.GameModeInfo getGameModeInfo(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public void setGameMode(@NonNull String, int);
+  }
+
+  public final class GameModeInfo implements android.os.Parcelable {
+    ctor public GameModeInfo(int, @NonNull int[]);
+    method public int describeContents();
+    method public int getActiveGameMode();
+    method @NonNull public int[] getAvailableGameModes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.GameModeInfo> CREATOR;
   }
 
   public abstract class InstantAppResolverService extends android.app.Service {
@@ -767,20 +791,33 @@
   }
 
   public class KeyguardManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public long addWeakEscrowToken(@NonNull byte[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenActivatedListener);
     method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed();
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean isValidLockPasswordComplexity(int, @NonNull byte[], int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean isWeakEscrowTokenActive(long, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean isWeakEscrowTokenValid(long, @NonNull byte[], @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean registerWeakEscrowTokenRemovedListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean removeWeakEscrowToken(long, @NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean unregisterWeakEscrowTokenRemovedListener(@NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
     field public static final int PASSWORD = 0; // 0x0
     field public static final int PATTERN = 2; // 0x2
     field public static final int PIN = 1; // 0x1
   }
 
+  public static interface KeyguardManager.WeakEscrowTokenActivatedListener {
+    method public void onWeakEscrowTokenActivated(long, @NonNull android.os.UserHandle);
+  }
+
+  public static interface KeyguardManager.WeakEscrowTokenRemovedListener {
+    method public void onWeakEscrowTokenRemoved(long, @NonNull android.os.UserHandle);
+  }
+
   public class LocaleManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_APP_SPECIFIC_LOCALES) public android.os.LocaleList getApplicationLocales(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void setApplicationLocales(@NonNull String, @NonNull android.os.LocaleList);
   }
 
@@ -858,7 +895,11 @@
 
   public class StatusBarManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int);
+    field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1
+    field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0
   }
 
   public static final class StatusBarManager.DisableInfo {
@@ -897,15 +938,21 @@
     method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void addOnProjectionStateChangedListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.app.UiModeManager.OnProjectionStateChangedListener);
     method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int);
     method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public int getActiveProjectionTypes();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public int getNightModeCustomType();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public java.util.Set<java.lang.String> getProjectingPackages(int);
     method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean releaseProjection(int);
     method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void removeOnProjectionStateChangedListener(@NonNull android.app.UiModeManager.OnProjectionStateChangedListener);
     method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean requestProjection(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivatedForCustomMode(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public void setNightModeCustomType(int);
     field public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = "android.app.action.ENTER_CAR_MODE_PRIORITIZED";
     field public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = "android.app.action.EXIT_CAR_MODE_PRIORITIZED";
     field public static final int DEFAULT_PRIORITY = 0; // 0x0
     field public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE";
     field public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY";
+    field public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1; // 0x1
+    field public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0; // 0x0
+    field public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1; // 0xffffffff
     field public static final int PROJECTION_TYPE_ALL = -1; // 0xffffffff
     field public static final int PROJECTION_TYPE_AUTOMOTIVE = 1; // 0x1
     field public static final int PROJECTION_TYPE_NONE = 0; // 0x0
@@ -963,14 +1010,28 @@
 
   public class WallpaperManager {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
+    method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
     method public void setDisplayOffset(android.os.IBinder, int, int);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float);
   }
 
 }
 
 package android.app.admin {
 
+  public final class DevicePolicyDrawableResource implements android.os.Parcelable {
+    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, int, int, int, @DrawableRes int);
+    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, int, int, @DrawableRes int);
+    method public int describeContents();
+    method @DrawableRes public int getCallingPackageResourceId();
+    method public int getDrawableId();
+    method public int getDrawableSource();
+    method public int getDrawableStyle();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyDrawableResource> CREATOR;
+  }
+
   public class DevicePolicyKeyguardService extends android.app.Service {
     ctor public DevicePolicyKeyguardService();
     method @Nullable public void dismiss();
@@ -992,6 +1053,8 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
     method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
+    method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>);
+    method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
     method public boolean isDeviceManaged();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
@@ -1003,24 +1066,29 @@
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull int[]);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull String[]);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull java.util.Set<android.app.admin.DevicePolicyDrawableResource>);
     method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
     method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setStrings(@NonNull java.util.Set<android.app.admin.DevicePolicyStringResource>);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
+    field @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE) public static final String ACTION_ESTABLISH_NETWORK_CONNECTION = "android.app.action.ESTABLISH_NETWORK_CONNECTION";
     field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
     field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
     field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
     field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
-    field public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
-    field public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
-    field public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
+    field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
+    field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+    field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
     field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
-    field public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
+    field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
     field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
     field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -1036,16 +1104,19 @@
     field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
     field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
     field public static final int CODE_USER_SETUP_COMPLETED = 4; // 0x4
+    field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
     field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
     field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
+    field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT = "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
     field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
     field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES";
     field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
     field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
     field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
+    field public static final String EXTRA_ROLE_HOLDER_STATE = "android.app.extra.ROLE_HOLDER_STATE";
     field public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4
     field public static final int FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1
     field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
@@ -1061,6 +1132,7 @@
     field public static final int RESULT_DEVICE_OWNER_SET = 123; // 0x7b
     field public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR = 1; // 0x1
     field public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2; // 0x2
+    field public static final int RESULT_UPDATE_ROLE_HOLDER = 2; // 0x2
     field public static final int RESULT_WORK_PROFILE_CREATED = 122; // 0x7a
     field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
     field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
@@ -1070,6 +1142,48 @@
     field public static final int STATE_USER_UNMANAGED = 0; // 0x0
   }
 
+  public static final class DevicePolicyResources.Strings {
+    field public static final String UNDEFINED = "UNDEFINED";
+  }
+
+  public static final class DevicePolicyResources.Strings.DocumentsUi {
+    field public static final String CANT_SAVE_TO_PERSONAL_MESSAGE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE";
+    field public static final String CANT_SAVE_TO_PERSONAL_TITLE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE";
+    field public static final String CANT_SAVE_TO_WORK_MESSAGE = "DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE";
+    field public static final String CANT_SAVE_TO_WORK_TITLE = "DocumentsUi.CANT_SAVE_TO_WORK_TITLE";
+    field public static final String CANT_SELECT_PERSONAL_FILES_MESSAGE = "DocumentsUi.CANT_SELECT_PERSONAL_FILES_MESSAGE";
+    field public static final String CANT_SELECT_PERSONAL_FILES_TITLE = "DocumentsUi.CANT_SELECT_PERSONAL_FILES_TITLE";
+    field public static final String CANT_SELECT_WORK_FILES_MESSAGE = "DocumentsUi.CANT_SELECT_WORK_FILES_MESSAGE";
+    field public static final String CANT_SELECT_WORK_FILES_TITLE = "DocumentsUi.CANT_SELECT_WORK_FILES_TITLE";
+    field public static final String CROSS_PROFILE_NOT_ALLOWED_MESSAGE = "DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_MESSAGE";
+    field public static final String CROSS_PROFILE_NOT_ALLOWED_TITLE = "DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_TITLE";
+    field public static final String PERSONAL_TAB = "DocumentsUi.PERSONAL_TAB";
+    field public static final String PREVIEW_WORK_FILE_ACCESSIBILITY = "DocumentsUi.PREVIEW_WORK_FILE_ACCESSIBILITY";
+    field public static final String WORK_ACCESSIBILITY = "DocumentsUi.WORK_ACCESSIBILITY";
+    field public static final String WORK_PROFILE_OFF_ENABLE_BUTTON = "DocumentsUi.WORK_PROFILE_OFF_ENABLE_BUTTON";
+    field public static final String WORK_PROFILE_OFF_ERROR_TITLE = "DocumentsUi.WORK_PROFILE_OFF_ERROR_TITLE";
+    field public static final String WORK_TAB = "DocumentsUi.WORK_TAB";
+  }
+
+  public static final class DevicePolicyResources.Strings.MediaProvider {
+    field public static final String BLOCKED_BY_ADMIN_TITLE = "MediaProvider.BLOCKED_BY_ADMIN_TITLE";
+    field public static final String BLOCKED_FROM_PERSONAL_MESSAGE = "MediaProvider.BLOCKED_FROM_PERSONAL_MESSAGE";
+    field public static final String BLOCKED_FROM_WORK_MESSAGE = "MediaProvider.BLOCKED_FROM_WORK_MESSAGE";
+    field public static final String SWITCH_TO_PERSONAL_MESSAGE = "MediaProvider.SWITCH_TO_PERSONAL_MESSAGE";
+    field public static final String SWITCH_TO_WORK_MESSAGE = "MediaProvider.SWITCH_TO_WORK_MESSAGE";
+    field public static final String WORK_PROFILE_PAUSED_MESSAGE = "MediaProvider.WORK_PROFILE_PAUSED_MESSAGE";
+    field public static final String WORK_PROFILE_PAUSED_TITLE = "MediaProvider.WORK_PROFILE_PAUSED_TITLE";
+  }
+
+  public final class DevicePolicyStringResource implements android.os.Parcelable {
+    ctor public DevicePolicyStringResource(@NonNull android.content.Context, @NonNull String, @StringRes int);
+    method public int describeContents();
+    method public int getCallingPackageResourceId();
+    method @NonNull public String getStringId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyStringResource> CREATOR;
+  }
+
   public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
     method public boolean canDeviceOwnerGrantSensorsPermissions();
     method public int describeContents();
@@ -1141,6 +1255,87 @@
 
 }
 
+package android.app.ambientcontext {
+
+  public final class AmbientContextEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getConfidenceLevel();
+    method public int getDensityLevel();
+    method @NonNull public java.time.Instant getEndTime();
+    method public int getEventType();
+    method @NonNull public java.time.Instant getStartTime();
+    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_COUGH = 1; // 0x1
+    field public static final int EVENT_SNORE = 2; // 0x2
+    field public static final int EVENT_UNKNOWN = 0; // 0x0
+    field public static final int LEVEL_HIGH = 5; // 0x5
+    field public static final int LEVEL_LOW = 1; // 0x1
+    field public static final int LEVEL_MEDIUM = 3; // 0x3
+    field public static final int LEVEL_MEDIUM_HIGH = 4; // 0x4
+    field public static final int LEVEL_MEDIUM_LOW = 2; // 0x2
+    field public static final int LEVEL_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class AmbientContextEvent.Builder {
+    ctor public AmbientContextEvent.Builder();
+    method @NonNull public android.app.ambientcontext.AmbientContextEvent build();
+    method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setConfidenceLevel(int);
+    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 @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
+  }
+
+  public final class AmbientContextEventRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Set<java.lang.Integer> getEventTypes();
+    method @NonNull public android.os.PersistableBundle getOptions();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventRequest> CREATOR;
+  }
+
+  public static final class AmbientContextEventRequest.Builder {
+    ctor public AmbientContextEventRequest.Builder();
+    method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder addEventType(int);
+    method @NonNull public android.app.ambientcontext.AmbientContextEventRequest build();
+    method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder setOptions(@NonNull android.os.PersistableBundle);
+  }
+
+  public final class AmbientContextEventResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.app.PendingIntent getActionPendingIntent();
+    method @NonNull public java.util.List<android.app.ambientcontext.AmbientContextEvent> getEvents();
+    method @NonNull public String getPackageName();
+    method public int getStatusCode();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventResponse> CREATOR;
+    field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
+    field public static final int STATUS_MICROPHONE_DISABLED = 4; // 0x4
+    field public static final int STATUS_NOT_SUPPORTED = 2; // 0x2
+    field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 1; // 0x1
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class AmbientContextEventResponse.Builder {
+    ctor public AmbientContextEventResponse.Builder();
+    method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
+    method @NonNull public android.app.ambientcontext.AmbientContextEventResponse build();
+    method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setActionPendingIntent(@NonNull android.app.PendingIntent);
+    method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setPackageName(@NonNull String);
+    method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setStatusCode(int);
+  }
+
+  public final class AmbientContextManager {
+    method @Nullable public static android.app.ambientcontext.AmbientContextEventResponse getResponseFromIntent(@NonNull android.content.Intent);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void registerObserver(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull android.app.PendingIntent);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void unregisterObserver();
+    field public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE = "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+  }
+
+}
+
 package android.app.assist {
 
   public class ActivityId {
@@ -1383,20 +1578,6 @@
 
 }
 
-package android.app.communal {
-
-  public final class CommunalManager {
-    method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void addCommunalModeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.communal.CommunalManager.CommunalModeListener);
-    method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public boolean isCommunalMode();
-    method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void removeCommunalModeListener(@NonNull android.app.communal.CommunalManager.CommunalModeListener);
-  }
-
-  @java.lang.FunctionalInterface public static interface CommunalManager.CommunalModeListener {
-    method public void onCommunalModeChanged(boolean);
-  }
-
-}
-
 package android.app.compat {
 
   public final class CompatChanges {
@@ -2004,6 +2185,8 @@
   }
 
   public class NetworkStatsManager {
+    method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getMobileUidStats();
+    method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getWifiUidStats();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
   }
@@ -2180,6 +2363,7 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isEncrypted();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setLowLatencyAudioAllowed(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMessageAccessPermission(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setPhonebookAccessPermission(int);
@@ -2221,9 +2405,12 @@
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int connectAudio();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int disconnectAudio();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioState(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInbandRingingEnabled();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean startScoUsingVirtualVoiceCall();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall();
@@ -2247,6 +2434,10 @@
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
   }
 
+  public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
+  }
+
   public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
     method public void close();
     method protected void finalize();
@@ -2256,15 +2447,21 @@
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
   }
 
-  public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile {
+  public final class BluetoothMapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.SEND_SMS}) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn();
-    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
     field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
@@ -2285,6 +2482,15 @@
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
   }
 
+  public final class BluetoothPbapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
+  }
+
   public interface BluetoothProfile {
     field public static final int A2DP_SINK = 11; // 0xb
     field public static final int AVRCP_CONTROLLER = 12; // 0xc
@@ -2302,6 +2508,14 @@
 
   public final class BluetoothStatusCodes {
     field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
+    field public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; // 0x45c
+    field public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; // 0x45d
+    field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
+    field public static final int ERROR_CALL_ACTIVE = 1119; // 0x45f
+    field public static final int ERROR_NOT_ACTIVE_DEVICE = 12; // 0xc
+    field public static final int ERROR_NO_ACTIVE_DEVICES = 13; // 0xd
+    field public static final int ERROR_PROFILE_NOT_CONNECTED = 14; // 0xe
+    field public static final int ERROR_TIMEOUT = 15; // 0xf
   }
 
   public final class BluetoothUuid {
@@ -2318,6 +2532,7 @@
     field @NonNull public static final android.os.ParcelUuid COORDINATED_SET;
     field @NonNull public static final android.os.ParcelUuid DIP;
     field @NonNull public static final android.os.ParcelUuid GENERIC_MEDIA_CONTROL;
+    field @NonNull public static final android.os.ParcelUuid HAS;
     field @NonNull public static final android.os.ParcelUuid HEARING_AID;
     field @NonNull public static final android.os.ParcelUuid HFP;
     field @NonNull public static final android.os.ParcelUuid HFP_AG;
@@ -2431,6 +2646,25 @@
 
 package android.bluetooth.le {
 
+  public final class AdvertiseSettings implements android.os.Parcelable {
+    method public int getOwnAddressType();
+  }
+
+  public static final class AdvertiseSettings.Builder {
+    method @NonNull public android.bluetooth.le.AdvertiseSettings.Builder setOwnAddressType(int);
+  }
+
+  public final class AdvertisingSetParameters implements android.os.Parcelable {
+    method public int getOwnAddressType();
+    field public static final int ADDRESS_TYPE_DEFAULT = -1; // 0xffffffff
+    field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
+    field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
+  }
+
+  public static final class AdvertisingSetParameters.Builder {
+    method @NonNull public android.bluetooth.le.AdvertisingSetParameters.Builder setOwnAddressType(int);
+  }
+
   public final class BluetoothLeScanner {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
@@ -2515,10 +2749,32 @@
 package android.companion.virtual {
 
   public final class VirtualDeviceManager {
+    method @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
   }
 
   public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
     method public void close();
+    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(int, int, int, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+  }
+
+  public final class VirtualDeviceParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLockState();
+    method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
+    field public static final int LOCK_STATE_ALWAYS_LOCKED = 0; // 0x0
+    field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
+  }
+
+  public static final class VirtualDeviceParams.Builder {
+    ctor public VirtualDeviceParams.Builder();
+    method @NonNull public android.companion.virtual.VirtualDeviceParams build();
+    method @NonNull @RequiresPermission(value="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY", conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
+    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
   }
 
 }
@@ -2576,6 +2832,7 @@
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
     method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+    field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
     field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
     field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -2583,7 +2840,6 @@
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
     field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
     field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
-    field public static final String COMMUNAL_SERVICE = "communal";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
     field public static final String ETHERNET_SERVICE = "ethernet";
@@ -2592,6 +2848,7 @@
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
     field public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition";
+    field public static final String NEARBY_SERVICE = "nearby";
     field public static final String NETD_SERVICE = "netd";
     field @Deprecated public static final String NETWORK_SCORE_SERVICE = "network_score";
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
@@ -2812,7 +3069,7 @@
   }
 
   public class CrossProfileApps {
-    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) public void startActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES}) public void startActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
   }
 
   public class DataLoaderParams {
@@ -2916,6 +3173,7 @@
   }
 
   public static class LauncherApps.ShortcutQuery {
+    field public static final int FLAG_GET_PERSISTED_DATA = 4096; // 0x1000
     field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
   }
 
@@ -3024,6 +3282,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
     field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
+    field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER";
     field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
     field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
@@ -3570,6 +3829,7 @@
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
+    field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
   }
 
 }
@@ -3785,7 +4045,7 @@
   }
 
   public class HdmiDeviceInfo implements android.os.Parcelable {
-    ctor public HdmiDeviceInfo();
+    ctor @Deprecated public HdmiDeviceInfo();
     method public int describeContents();
     method public int getAdopterId();
     method public int getDeviceId();
@@ -3806,6 +4066,7 @@
     method public boolean isSourceType();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ADDR_INTERNAL = 0; // 0x0
+    field public static final int ADDR_INVALID = -1; // 0xffffffff
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.hdmi.HdmiDeviceInfo> CREATOR;
     field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
     field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
@@ -3819,6 +4080,7 @@
     field public static final int PATH_INTERNAL = 0; // 0x0
     field public static final int PATH_INVALID = 65535; // 0xffff
     field public static final int PORT_INVALID = -1; // 0xffffffff
+    field public static final int VENDOR_ID_UNKNOWN = 16777215; // 0xffffff
   }
 
   public final class HdmiHotplugEvent implements android.os.Parcelable {
@@ -4003,6 +4265,7 @@
 
   public class VirtualMouse implements java.io.Closeable {
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.graphics.PointF getCursorPosition();
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent);
@@ -4119,7 +4382,7 @@
   public class ContextHubClient implements java.io.Closeable {
     method public void close();
     method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub();
-    method public int getId();
+    method @IntRange(from=0, to=65535) public int getId();
     method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
   }
 
@@ -4962,8 +5225,27 @@
   }
 
   public final class UsbPort {
+    method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableLimitPowerTransfer(boolean);
+    method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbData(boolean);
+    method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbDataWhileDocked();
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USB) public android.hardware.usb.UsbPortStatus getStatus();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setRoles(int, int);
+    field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1; // 0x1
+    field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4; // 0x4
+    field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3; // 0x3
+    field public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0; // 0x0
+    field public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1; // 0x1
+    field public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; // 0x4
+    field public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; // 0x3
+    field public static final int ENABLE_USB_DATA_SUCCESS = 0; // 0x0
+    field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED = 4; // 0x4
+    field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL = 1; // 0x1
+    field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER = 5; // 0x5
+    field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH = 3; // 0x3
+    field public static final int ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS = 0; // 0x0
   }
 
   public final class UsbPortStatus implements android.os.Parcelable {
@@ -4971,8 +5253,11 @@
     method public int getCurrentDataRole();
     method public int getCurrentMode();
     method public int getCurrentPowerRole();
+    method public int getPowerBrickStatus();
     method public int getSupportedRoleCombinations();
+    method @Nullable public int[] getUsbDataStatus();
     method public boolean isConnected();
+    method public boolean isPowerTransferLimited();
     method public boolean isRoleCombinationSupported(int, int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
@@ -4984,9 +5269,19 @@
     field public static final int MODE_DFP = 2; // 0x2
     field public static final int MODE_NONE = 0; // 0x0
     field public static final int MODE_UFP = 1; // 0x1
+    field public static final int POWER_BRICK_STATUS_CONNECTED = 1; // 0x1
+    field public static final int POWER_BRICK_STATUS_DISCONNECTED = 2; // 0x2
+    field public static final int POWER_BRICK_STATUS_UNKNOWN = 0; // 0x0
     field public static final int POWER_ROLE_NONE = 0; // 0x0
     field public static final int POWER_ROLE_SINK = 2; // 0x2
     field public static final int POWER_ROLE_SOURCE = 1; // 0x1
+    field public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3; // 0x3
+    field public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6; // 0x6
+    field public static final int USB_DATA_STATUS_DISABLED_DOCK = 4; // 0x4
+    field public static final int USB_DATA_STATUS_DISABLED_FORCE = 5; // 0x5
+    field public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2; // 0x2
+    field public static final int USB_DATA_STATUS_ENABLED = 1; // 0x1
+    field public static final int USB_DATA_STATUS_UNKNOWN = 0; // 0x0
   }
 
 }
@@ -5603,6 +5898,7 @@
     method public int getCapturePreset();
     method public int getSystemUsage();
     method public static boolean isSystemUsage(int);
+    field @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public static final int CONTENT_TYPE_ULTRASOUND = 1997; // 0x7cd
     field public static final int FLAG_BEACON = 8; // 0x8
     field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 64; // 0x40
     field public static final int FLAG_BYPASS_MUTE = 128; // 0x80
@@ -5619,6 +5915,7 @@
     method public android.media.AudioAttributes.Builder setCapturePreset(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public android.media.AudioAttributes.Builder setHotwordModeEnabled(boolean);
     method public android.media.AudioAttributes.Builder setInternalCapturePreset(int);
+    method @NonNull public android.media.AudioAttributes.Builder setInternalContentType(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
   }
 
@@ -5834,6 +6131,7 @@
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
+    field @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public static final int ULTRASOUND = 2000; // 0x7d0
   }
 
   public final class MediaRouter2 {
@@ -6316,6 +6614,7 @@
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
     method @NonNull public java.util.List<java.lang.String> getAvailableExtensionInterfaceNames(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+    method public int getClientPriority(int, @Nullable String);
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
     method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
     method @Nullable public android.os.IBinder getExtensionInterface(@NonNull String, @NonNull String);
@@ -6333,6 +6632,7 @@
 
   public static final class TvInputManager.Hardware {
     method public void overrideAudioSink(int, String, int, int, int);
+    method public void overrideAudioSink(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int, int, int);
     method public void setStreamVolume(float);
     method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig);
   }
@@ -6435,7 +6735,9 @@
   }
 
   public class Lnb implements java.lang.AutoCloseable {
+    method public void addCallback(@NonNull android.media.tv.tuner.LnbCallback, @NonNull java.util.concurrent.Executor);
     method public void close();
+    method public boolean removeCallback(@NonNull android.media.tv.tuner.LnbCallback);
     method public int sendDiseqcMessage(@NonNull byte[]);
     method public int setSatellitePosition(int);
     method public int setTone(int);
@@ -6472,6 +6774,7 @@
     method public void clearOnTuneEventListener();
     method public void clearResourceLostListener();
     method public void close();
+    method public void closeFrontend();
     method public int connectCiCam(int);
     method public int connectFrontendToCiCam(int);
     method public int disconnectCiCam();
@@ -6479,7 +6782,7 @@
     method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
     method public long getAvSyncTime(int);
     method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getAvailableFrontendInfos();
-    method @Nullable public String getCurrentFrontendHardwardInfo();
+    method @Nullable public String getCurrentFrontendHardwareInfo();
     method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
     method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
     method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
@@ -6494,12 +6797,14 @@
     method @Nullable public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER) public static android.media.tv.tuner.filter.SharedFilter openSharedFilter(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.filter.SharedFilterCallback);
     method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
+    method public int removeOutputPid(@IntRange(from=0) int);
     method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
     method public int setLnaEnabled(boolean);
     method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
     method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
     method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
     method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
+    method public int transferOwner(@NonNull android.media.tv.tuner.Tuner);
     method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
     method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void updateResourcePriority(int, int);
     field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
@@ -6713,8 +7018,8 @@
     method @Nullable public String acquireSharedFilterToken();
     method public void close();
     method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
-    method public int delayCallbackUntilBufferFilled(int);
-    method public int delayCallbackUntilTimeMillis(long);
+    method public int delayCallbackUntilBytesAccumulated(int);
+    method public int delayCallbackUntilMillisElapsed(long);
     method public int flush();
     method public void freeSharedFilterToken(@NonNull String);
     method @Deprecated public int getId();
@@ -6933,12 +7238,14 @@
   }
 
   public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings {
+    method public int getLengthFieldBitWidth();
     method public boolean isCrcEnabled();
     method public boolean isRaw();
     method public boolean isRepeat();
   }
 
   public abstract static class SectionSettings.Builder<T extends android.media.tv.tuner.filter.SectionSettings.Builder<T>> {
+    method @NonNull public T setBitWidthOfLengthField(@IntRange(from=0) int);
     method @NonNull public T setCrcEnabled(boolean);
     method @NonNull public T setRaw(boolean);
     method @NonNull public T setRepeat(boolean);
@@ -7631,6 +7938,7 @@
 
   public class FrontendStatus {
     method public int getAgc();
+    method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpInfo[] getAllAtsc3PlpInfo();
     method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
     method public int getBandwidth();
     method public int getBer();
@@ -7673,6 +7981,7 @@
     method public boolean isRfLocked();
     method public boolean isShortFramesEnabled();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
+    field public static final int FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO = 41; // 0x29
     field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
     field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
@@ -7929,7 +8238,7 @@
     method public void onScanStopped();
     method public void onSignalTypeReported(int);
     method public void onSymbolRatesReported(@NonNull int[]);
-    method public default void onUnLocked();
+    method public default void onUnlocked();
   }
 
 }
@@ -8081,21 +8390,26 @@
     field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
   }
 
-  public final class NetworkStats implements android.os.Parcelable {
+  public final class NetworkStats implements java.lang.Iterable<android.net.NetworkStats.Entry> android.os.Parcelable {
     ctor public NetworkStats(long, int);
     method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
     method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
     method public int describeContents();
+    method @NonNull public java.util.Iterator<android.net.NetworkStats.Entry> iterator();
     method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
+    field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
     field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
     field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
     field public static final String IFACE_VT = "vt_data0";
+    field public static final int METERED_ALL = -1; // 0xffffffff
     field public static final int METERED_NO = 0; // 0x0
     field public static final int METERED_YES = 1; // 0x1
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
     field public static final int ROAMING_NO = 0; // 0x0
     field public static final int ROAMING_YES = 1; // 0x1
+    field public static final int SET_ALL = -1; // 0xffffffff
     field public static final int SET_DEFAULT = 0; // 0x0
     field public static final int SET_FOREGROUND = 1; // 0x1
     field public static final int TAG_NONE = 0; // 0x0
@@ -8105,6 +8419,17 @@
 
   public static class NetworkStats.Entry {
     ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
+    method public int getDefaultNetwork();
+    method public int getMetered();
+    method public long getOperations();
+    method public int getRoaming();
+    method public long getRxBytes();
+    method public long getRxPackets();
+    method public int getSet();
+    method public int getTag();
+    method public long getTxBytes();
+    method public long getTxPackets();
+    method public int getUid();
   }
 
   @Deprecated public class RssiCurve implements android.os.Parcelable {
@@ -8141,6 +8466,7 @@
   public class TrafficStats {
     method public static void setThreadStatsTagApp();
     method public static void setThreadStatsTagBackup();
+    method public static void setThreadStatsTagDownload();
     method public static void setThreadStatsTagRestore();
     field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f
     field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80
@@ -8547,6 +8873,7 @@
     method @Nullable public android.net.wifi.nl80211.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
     method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
+    method public boolean notifyCountryCodeChanged();
     method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
     method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
     method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
@@ -9093,6 +9420,7 @@
     field public static final int EVENT_UNSPECIFIED = 0; // 0x0
     field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
     field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+    field public static final int REASON_BLUETOOTH_BROADCAST = 203; // 0xcb
     field public static final int REASON_GEOFENCING = 100; // 0x64
     field public static final int REASON_LOCATION_PROVIDER = 312; // 0x138
     field public static final int REASON_OTHER = 1; // 0x1
@@ -9335,6 +9663,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
@@ -9342,6 +9671,10 @@
     field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
     field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
     field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
+    field public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; // 0x2
+    field public static final int REMOVE_RESULT_DEFERRED = 1; // 0x1
+    field public static final int REMOVE_RESULT_ERROR = 3; // 0x3
+    field public static final int REMOVE_RESULT_REMOVED = 0; // 0x0
     field public static final int RESTRICTION_NOT_SET = 0; // 0x0
     field public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 2; // 0x2
     field public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 4; // 0x4
@@ -9519,7 +9852,7 @@
 
   public final class StorageVolume implements android.os.Parcelable {
     method @NonNull public String getId();
-    method public boolean isStub();
+    method public boolean isExternallyManaged();
   }
 
 }
@@ -9538,11 +9871,17 @@
 
   public final class PermissionControllerManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void getHibernationEligibility(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
+    method public void getUnusedAppCount(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
     field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
     field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
+    field public static final int HIBERNATION_ELIGIBILITY_ELIGIBLE = 0; // 0x0
+    field public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM = 1; // 0x1
+    field public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER = 2; // 0x2
+    field public static final int HIBERNATION_ELIGIBILITY_UNKNOWN = -1; // 0xffffffff
     field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
     field public static final int REASON_MALWARE = 1; // 0x1
   }
@@ -9560,13 +9899,16 @@
     method @BinderThread public abstract void onCountPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull java.util.function.IntConsumer);
     method @BinderThread public abstract void onGetAppPermissions(@NonNull String, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionPresentationInfo>>);
     method @BinderThread public void onGetGroupOfPlatformPermission(@NonNull String, @NonNull java.util.function.Consumer<java.lang.String>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetHibernationEligibility(@NonNull String, @NonNull java.util.function.IntConsumer);
     method @BinderThread public abstract void onGetPermissionUsages(boolean, long, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionUsageInfo>>);
     method @BinderThread public void onGetPlatformPermissionsForGroup(@NonNull String, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
     method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
     method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
     method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
     method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
+    method @BinderThread public void onRevokeOwnPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
     method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -9776,6 +10118,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
     field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
     field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
+    field public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE = "ambient_context_manager_service";
     field public static final String NAMESPACE_APPSEARCH = "appsearch";
     field public static final String NAMESPACE_APP_COMPAT = "app_compat";
     field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
@@ -9818,6 +10161,7 @@
     field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
     field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
     field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+    field public static final String NAMESPACE_SUPPLEMENTAL_API = "supplemental_api";
     field public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT = "surface_flinger_native_boot";
     field public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
@@ -10011,7 +10355,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
     field public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
     field public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
-    field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+    field @Deprecated public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
     field public static final String CARRIER_APP_NAMES = "carrier_app_names";
     field public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
     field public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
@@ -10046,6 +10390,7 @@
     field public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
     field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
     field public static final String DOZE_ALWAYS_ON = "doze_always_on";
+    field public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
     field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
     field public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled";
     field public static final String LAST_SETUP_SHOWN = "last_setup_shown";
@@ -10346,6 +10691,18 @@
 
 }
 
+package android.service.ambientcontext {
+
+  public abstract class AmbientContextDetectionService extends android.app.Service {
+    ctor public AmbientContextDetectionService();
+    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.app.ambientcontext.AmbientContextEventResponse>);
+    method public abstract void onStopDetection(@NonNull String);
+    field public static final String SERVICE_INTERFACE = "android.service.ambientcontext.AmbientContextDetectionService";
+  }
+
+}
+
 package android.service.appprediction {
 
   public abstract class AppPredictionService extends android.app.Service {
@@ -10725,6 +11082,7 @@
     field public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
     field public static final String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
     field public static final String EXTRA_RESOLUTION_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_PORT_INDEX";
+    field public static final String EXTRA_RESOLUTION_USE_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX";
     field public static final String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS";
     field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1
     field public static final int RESOLVABLE_ERROR_POLICY_RULES = 2; // 0x2
@@ -10783,9 +11141,11 @@
 
   public class GameService extends android.app.Service {
     ctor public GameService();
-    method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method public final void createGameSession(@IntRange(from=0) int);
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
+    method public void onGameStarted(@NonNull android.service.games.GameStartedEvent);
     field public static final String ACTION_GAME_SERVICE = "android.service.games.action.GAME_SERVICE";
     field public static final String SERVICE_META_DATA = "android.game_service";
   }
@@ -10794,15 +11154,33 @@
     ctor public GameSession();
     method public void onCreate();
     method public void onDestroy();
+    method public void onGameTaskFocusChanged(boolean);
+    method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
+    method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
+  }
+
+  public static interface GameSession.ScreenshotCallback {
+    method public void onFailure(int);
+    method public void onSuccess(@NonNull android.graphics.Bitmap);
+    field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0
   }
 
   public abstract class GameSessionService extends android.app.Service {
     ctor public GameSessionService();
-    method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method @NonNull public abstract android.service.games.GameSession onNewSession(@NonNull android.service.games.CreateGameSessionRequest);
     field public static final String ACTION_GAME_SESSION_SERVICE = "android.service.games.action.GAME_SESSION_SERVICE";
   }
 
+  public final class GameStartedEvent implements android.os.Parcelable {
+    ctor public GameStartedEvent(@IntRange(from=0) int, @NonNull String);
+    method public int describeContents();
+    method @NonNull public String getPackageName();
+    method @IntRange(from=0) public int getTaskId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.games.GameStartedEvent> CREATOR;
+  }
+
 }
 
 package android.service.notification {
@@ -10925,6 +11303,7 @@
     method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
     method public long getMaximumDataBlockSize();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
+    method @NonNull public String getPersistentDataPackageName();
     method public byte[] read();
     method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
     method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
@@ -11851,11 +12230,18 @@
     method public int getCallDuration();
     method public int getCodecType();
     method public int getDownlinkCallQualityLevel();
+    method public long getMaxPlayoutDelayMillis();
     method public int getMaxRelativeJitter();
+    method public long getMinPlayoutDelayMillis();
+    method public int getNumDroppedRtpPackets();
+    method public int getNumNoDataFrames();
+    method public int getNumRtpDuplicatePackets();
     method public int getNumRtpPacketsNotReceived();
     method public int getNumRtpPacketsReceived();
     method public int getNumRtpPacketsTransmitted();
     method public int getNumRtpPacketsTransmittedLost();
+    method public int getNumRtpSidPacketsRx();
+    method public int getNumVoiceFrames();
     method public int getUplinkCallQualityLevel();
     method public boolean isIncomingSilenceDetectedAtCallSetup();
     method public boolean isOutgoingSilenceDetectedAtCallSetup();
@@ -11870,6 +12256,32 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
   }
 
+  public static final class CallQuality.Builder {
+    ctor public CallQuality.Builder();
+    method @NonNull public android.telephony.CallQuality build();
+    method @NonNull public android.telephony.CallQuality.Builder setAverageRelativeJitter(int);
+    method @NonNull public android.telephony.CallQuality.Builder setAverageRoundTripTime(int);
+    method @NonNull public android.telephony.CallQuality.Builder setCallDuration(int);
+    method @NonNull public android.telephony.CallQuality.Builder setCodecType(int);
+    method @NonNull public android.telephony.CallQuality.Builder setDownlinkCallQualityLevel(int);
+    method @NonNull public android.telephony.CallQuality.Builder setIncomingSilenceDetectedAtCallSetup(boolean);
+    method @NonNull public android.telephony.CallQuality.Builder setMaxPlayoutDelayMillis(long);
+    method @NonNull public android.telephony.CallQuality.Builder setMaxRelativeJitter(int);
+    method @NonNull public android.telephony.CallQuality.Builder setMinPlayoutDelayMillis(long);
+    method @NonNull public android.telephony.CallQuality.Builder setNumDroppedRtpPackets(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumNoDataFrames(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumRtpDuplicatePackets(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsNotReceived(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsReceived(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsTransmitted(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsTransmittedLost(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumRtpSidPacketsRx(int);
+    method @NonNull public android.telephony.CallQuality.Builder setNumVoiceFrames(int);
+    method @NonNull public android.telephony.CallQuality.Builder setOutgoingSilenceDetectedAtCallSetup(boolean);
+    method @NonNull public android.telephony.CallQuality.Builder setRtpInactivityDetected(boolean);
+    method @NonNull public android.telephony.CallQuality.Builder setUplinkCallQualityLevel(int);
+  }
+
   public class CarrierConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
     method @NonNull public static android.os.PersistableBundle getDefaultConfig();
@@ -12849,6 +13261,7 @@
     field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0
     field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
     field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
+    field public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5; // 0x5
     field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
     field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
     field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
@@ -13017,6 +13430,7 @@
   }
 
   public final class UiccSlotMapping implements android.os.Parcelable {
+    ctor public UiccSlotMapping(int, int, int);
     method public int describeContents();
     method @IntRange(from=0) public int getLogicalSlotIndex();
     method @IntRange(from=0) public int getPhysicalSlotIndex();
@@ -13074,6 +13488,7 @@
     field public static final String TYPE_DEFAULT_STRING = "default";
     field public static final String TYPE_DUN_STRING = "dun";
     field public static final String TYPE_EMERGENCY_STRING = "emergency";
+    field public static final String TYPE_ENTERPRISE_STRING = "enterprise";
     field public static final String TYPE_FOTA_STRING = "fota";
     field public static final String TYPE_HIPRI_STRING = "hipri";
     field public static final String TYPE_IA_STRING = "ia";
@@ -13771,6 +14186,7 @@
     field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
     field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
+    field public static final int OIR_PRESENTATION_UNAVAILABLE = 5; // 0x5
     field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
     field public static final int PRIORITY_NORMAL = 0; // 0x0
     field public static final int PRIORITY_URGENT = 1; // 0x1
@@ -13933,14 +14349,21 @@
 
   public class ImsService extends android.app.Service {
     ctor public ImsService();
-    method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
-    method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
-    method public void disableIms(int);
-    method public void enableIms(int);
-    method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method @Nullable public android.telephony.ims.feature.MmTelFeature createEmergencyOnlyMmTelFeature(int);
+    method @Deprecated public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
+    method @Nullable public android.telephony.ims.feature.MmTelFeature createMmTelFeatureForSubscription(int, int);
+    method @Deprecated public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
+    method @Nullable public android.telephony.ims.feature.RcsFeature createRcsFeatureForSubscription(int, int);
+    method @Deprecated public void disableIms(int);
+    method public void disableImsForSubscription(int, int);
+    method @Deprecated public void enableIms(int);
+    method public void enableImsForSubscription(int, int);
+    method @Deprecated public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method @NonNull public android.telephony.ims.stub.ImsConfigImplBase getConfigForSubscription(int, int);
     method @NonNull public java.util.concurrent.Executor getExecutor();
     method public long getImsServiceCapabilities();
-    method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @Deprecated public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @NonNull public android.telephony.ims.stub.ImsRegistrationImplBase getRegistrationForSubscription(int, int);
     method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
     method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
     method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
@@ -14164,18 +14587,16 @@
   public class ProvisioningManager {
     method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerRcsProvisioningCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerRcsReconfiguration();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void unregisterRcsProvisioningCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
@@ -14608,9 +15029,6 @@
     method public void addCapabilities(int);
     method public boolean isCapable(int);
     method public void removeCapabilities(int);
-    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
-    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
-    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
 }
@@ -14760,11 +15178,6 @@
     method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
     method public void triggerSipDelegateDeregistration();
     method public void updateSipDelegateRegistration();
-    field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
-    field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
-    field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
-    field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
-    field public static final int REGISTRATION_TECH_NR = 3; // 0x3
   }
 
   public class ImsSmsImplBase {
@@ -15042,6 +15455,8 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public android.graphics.Region getCurrentImeTouchRegion();
+    method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull android.window.TaskFpsCallback);
+    method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback);
   }
 
   public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
@@ -15067,6 +15482,11 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
   }
 
+  public class CaptioningManager {
+    method @RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION) public final void setSystemAudioCaptioningRequested(boolean);
+    method @RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION) public final void setSystemAudioCaptioningUiRequested(boolean);
+  }
+
 }
 
 package android.view.autofill {
@@ -15624,3 +16044,15 @@
 
 }
 
+package android.window {
+
+  public final class TaskFpsCallback {
+    ctor public TaskFpsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback.OnFpsCallbackListener);
+  }
+
+  public static interface TaskFpsCallback.OnFpsCallbackListener {
+    method public void onFpsReported(float);
+  }
+
+}
+
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 716f43e..3d756ba 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -91,6 +91,10 @@
 
 
 
+NoSettingsProvider: android.provider.Settings.Secure#FAST_PAIR_SCAN_ENABLED:
+    New setting keys are not allowed (Field: FAST_PAIR_SCAN_ENABLED); use getters/setters in relevant manager class
+
+
 OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent):
     Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent`
 
@@ -103,6 +107,16 @@
 
 
 
+RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount():
+    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimmingAmount():
+    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimAmount(float):
+    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(float):
+    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+
+
 SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
 
 SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8ae6e4c..ff26ae8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -36,13 +36,14 @@
     field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
+    field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
     field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
+    field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
     field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
     field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
-    field public static final String WRITE_COMMUNAL_STATE = "android.permission.WRITE_COMMUNAL_STATE";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
     field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -66,6 +67,7 @@
   public static final class R.string {
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
     field public static final int config_defaultDialer = 17039395; // 0x1040023
+    field public static final int config_systemAutomotiveCalendarSyncManager;
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
     field public static final int config_systemGallery = 17039399; // 0x1040027
@@ -100,7 +102,7 @@
 
 package android.app {
 
-  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     method public void onMovedToDisplay(int, android.content.res.Configuration);
   }
 
@@ -555,14 +557,6 @@
 
 }
 
-package android.app.communal {
-
-  public final class CommunalManager {
-    method @RequiresPermission(android.Manifest.permission.WRITE_COMMUNAL_STATE) public void setCommunalViewShowing(boolean);
-  }
-
-}
-
 package android.app.contentsuggestions {
 
   public final class ContentSuggestionsManager {
@@ -1145,15 +1139,15 @@
 
   public final class DisplayManager {
     method public boolean areUserDisabledHdrTypesAllowed();
-    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode();
+    method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
     method @NonNull public int[] getUserDisabledHdrTypes();
-    method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean isMinimalPostProcessingRequested(int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
     method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
     field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
     field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
@@ -1187,9 +1181,23 @@
 
 package android.hardware.input {
 
+  public final class InputDeviceIdentifier implements android.os.Parcelable {
+    ctor public InputDeviceIdentifier(@NonNull String, int, int);
+    method public int describeContents();
+    method @NonNull public String getDescriptor();
+    method public int getProductId();
+    method public int getVendorId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.InputDeviceIdentifier> CREATOR;
+  }
+
   public final class InputManager {
     method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
+    method @Nullable public String getCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier);
+    method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice);
+    method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
+    method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float);
     field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
   }
@@ -1317,7 +1325,7 @@
     method public void setAccumulatedDeltaRangeMeters(double);
     method public void setAccumulatedDeltaRangeState(int);
     method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
-    method public void setAutomaticGainControlLevelInDb(double);
+    method @Deprecated public void setAutomaticGainControlLevelInDb(double);
     method public void setBasebandCn0DbHz(double);
     method @Deprecated public void setCarrierCycles(long);
     method public void setCarrierFrequencyHz(float);
@@ -1344,10 +1352,6 @@
     field public static final int ADR_STATE_ALL = 31; // 0x1f
   }
 
-  public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    ctor public GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]);
-  }
-
   public final class GnssNavigationMessage implements android.os.Parcelable {
     ctor public GnssNavigationMessage();
     method public void reset();
@@ -1679,6 +1683,10 @@
     method public void removeSyncBarrier(int);
   }
 
+  public final class NewUserResponse {
+    ctor public NewUserResponse(@Nullable android.os.UserHandle, int);
+  }
+
   public final class PackageTagsList implements android.os.Parcelable {
     method public boolean contains(@NonNull String, @Nullable String);
     method public boolean contains(@NonNull android.os.PackageTagsList);
@@ -1818,13 +1826,21 @@
 
   public static final class VibrationEffect.WaveformBuilder {
     method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
-    method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=-1.0F, to=1.0f) float, @IntRange(from=0) int);
+    method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int);
     method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
-    method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=-1.0F, to=1.0f) float, @IntRange(from=0) int);
+    method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int);
     method @NonNull public android.os.VibrationEffect build();
     method @NonNull public android.os.VibrationEffect build(int);
   }
 
+  public abstract class Vibrator {
+    method public int getDefaultVibrationIntensity(int);
+    field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3
+    field public static final int VIBRATION_INTENSITY_LOW = 1; // 0x1
+    field public static final int VIBRATION_INTENSITY_MEDIUM = 2; // 0x2
+    field public static final int VIBRATION_INTENSITY_OFF = 0; // 0x0
+  }
+
   public class VintfObject {
     method public static String[] getHalNamesAndVersions();
     method @NonNull public static String getPlatformSepolicyVersion();
@@ -1927,6 +1943,9 @@
     method @NonNull public static String convert(@NonNull java.util.UUID);
     method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
     method public static boolean isUserKeyUnlocked(int);
+    field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
+    field public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
+    field public static final String STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
   }
 
   public final class StorageVolume implements android.os.Parcelable {
@@ -1978,9 +1997,9 @@
     method public int describeContents();
     method public long getDuration();
     method public float getEndAmplitude();
-    method public float getEndFrequency();
+    method public float getEndFrequencyHz();
     method public float getStartAmplitude();
-    method public float getStartFrequency();
+    method public float getStartFrequencyHz();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.RampSegment> CREATOR;
   }
@@ -1989,7 +2008,7 @@
     method public int describeContents();
     method public float getAmplitude();
     method public long getDuration();
-    method public float getFrequency();
+    method public float getFrequencyHz();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.StepSegment> CREATOR;
   }
@@ -2032,6 +2051,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
     method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
+    method public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int);
   }
 
 }
@@ -2305,6 +2325,7 @@
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
     method public final void requestExit();
+    method public final boolean shouldShowComplications();
   }
 
 }
@@ -2698,11 +2719,14 @@
   }
 
   public final class Display {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode();
     method @NonNull public android.view.Display.Mode getDefaultMode();
     method @NonNull public int[] getReportedHdrTypes();
     method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
     method public int getType();
+    method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean hasAccess(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
     field public static final int FLAG_TRUSTED = 128; // 0x80
     field public static final int TYPE_EXTERNAL = 2; // 0x2
     field public static final int TYPE_INTERNAL = 1; // 0x1
@@ -2717,6 +2741,13 @@
     method public boolean matches(int, int, float);
   }
 
+  public static final class Display.Mode.Builder {
+    ctor public Display.Mode.Builder();
+    method @NonNull public android.view.Display.Mode build();
+    method @NonNull public android.view.Display.Mode.Builder setRefreshRate(float);
+    method @NonNull public android.view.Display.Mode.Builder setResolution(int, int);
+  }
+
   public class FocusFinder {
     method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean);
   }
@@ -2724,6 +2755,7 @@
   public final class InputDevice implements android.os.Parcelable {
     method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void disable();
     method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void enable();
+    method @NonNull public android.hardware.input.InputDeviceIdentifier getIdentifier();
   }
 
   public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable {
@@ -2766,7 +2798,7 @@
     method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
   }
 
-  @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+  @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
     method public android.view.View getTooltipView();
     method public boolean isAutofilled();
     method public static boolean isDefaultFocusHighlightEnabled();
@@ -2877,6 +2909,7 @@
   }
 
   public final class AutofillManager {
+    field public static final String DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "compat_mode_allowed_packages";
     field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
     field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
     field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index e3690e5..01604e6 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -737,6 +737,8 @@
     
 MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
     
+MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int):
+    android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int)
 
 
 MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9a55867..479e6bf 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -588,7 +588,7 @@
         boolean onKeyEvent(KeyEvent event);
         /** Magnification changed callbacks for different displays */
         void onMagnificationChanged(int displayId, @NonNull Region region,
-                float scale, float centerX, float centerY);
+                MagnificationConfig config);
         /** Callbacks for receiving motion events. */
         void onMotionEvent(MotionEvent event);
         /** Callback for tuch state changes. */
@@ -1183,14 +1183,14 @@
         }
     }
 
-    private void onMagnificationChanged(int displayId, @NonNull Region region, float scale,
-            float centerX, float centerY) {
+    private void onMagnificationChanged(int displayId, @NonNull Region region,
+            MagnificationConfig config) {
         MagnificationController controller;
         synchronized (mLock) {
             controller = mMagnificationControllers.get(displayId);
         }
         if (controller != null) {
-            controller.dispatchMagnificationChanged(region, scale, centerX, centerY);
+            controller.dispatchMagnificationChanged(region, config);
         }
     }
 
@@ -1328,8 +1328,8 @@
          * Dispatches magnification changes to any registered listeners. This
          * should be called on the service's main thread.
          */
-        void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
-                final float centerX, final float centerY) {
+        void dispatchMagnificationChanged(final @NonNull Region region,
+                final MagnificationConfig config) {
             final ArrayMap<OnMagnificationChangedListener, Handler> entries;
             synchronized (mLock) {
                 if (mListeners == null || mListeners.isEmpty()) {
@@ -1348,16 +1348,13 @@
                 final OnMagnificationChangedListener listener = entries.keyAt(i);
                 final Handler handler = entries.valueAt(i);
                 if (handler != null) {
-                    handler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            listener.onMagnificationChanged(MagnificationController.this,
-                                    region, scale, centerX, centerY);
-                        }
+                    handler.post(() -> {
+                        listener.onMagnificationChanged(MagnificationController.this,
+                                region, config);
                     });
                 } else {
                     // We're already on the main thread, just run the listener.
-                    listener.onMagnificationChanged(this, region, scale, centerX, centerY);
+                    listener.onMagnificationChanged(this, region, config);
                 }
             }
         }
@@ -1503,6 +1500,12 @@
          * {@link AccessibilityService#onServiceConnected()} has not yet been
          * called) or the service has been disconnected, this method will
          * return an empty region.
+         * </p>
+         * <p>
+         * <strong>Note:</strong> This legacy API gets the magnification region of full-screen
+         * magnification. To get the magnification region of the current controlling magnifier,
+         * use {@link #getCurrentMagnificationRegion()} instead.
+         * </p>
          *
          * @return the region of the screen currently active for magnification, or an empty region
          * if magnification is not active.
@@ -1524,6 +1527,45 @@
         }
 
         /**
+         * Returns the region of the screen currently active for magnification if the
+         * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
+         * Returns the region of screen projected on the magnification window if the
+         * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
+         *
+         * <p>
+         * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+         * the returned region will be empty if the magnification is
+         * not active. And the magnification is active if magnification gestures are enabled
+         * or if a service is running that can control magnification.
+         * </p><p>
+         * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+         * the returned region will be empty if the magnification is not activated.
+         * </p><p>
+         * <strong>Note:</strong> If the service is not yet connected (e.g.
+         * {@link AccessibilityService#onServiceConnected()} has not yet been
+         * called) or the service has been disconnected, this method will
+         * return an empty region.
+         * </p>
+         *
+         * @return the magnification region of the currently controlling magnification
+         */
+        @NonNull
+        public Region getCurrentMagnificationRegion() {
+            final IAccessibilityServiceConnection connection =
+                    AccessibilityInteractionClient.getInstance(mService).getConnection(
+                            mService.mConnectionId);
+            if (connection != null) {
+                try {
+                    return connection.getCurrentMagnificationRegion(mDisplayId);
+                } catch (RemoteException re) {
+                    Log.w(LOG_TAG, "Failed to obtain the current magnified region", re);
+                    re.rethrowFromSystemServer();
+                }
+            }
+            return Region.obtain();
+        }
+
+        /**
          * Resets magnification scale and center to their default (e.g. no
          * magnification) values.
          * <p>
@@ -1531,6 +1573,11 @@
          * {@link AccessibilityService#onServiceConnected()} has not yet been
          * called) or the service has been disconnected, this method will have
          * no effect and return {@code false}.
+         * <p>
+         * <strong>Note:</strong> This legacy API reset full-screen magnification.
+         * To reset the current controlling magnifier, use
+         * {@link #resetCurrentMagnification(boolean)} ()} instead.
+         * </p>
          *
          * @param animate {@code true} to animate from the current scale and
          *                center or {@code false} to reset the scale and center
@@ -1553,6 +1600,36 @@
         }
 
         /**
+         * Resets magnification scale and center of the controlling magnification
+         * to their default (e.g. no magnification) values.
+         * <p>
+         * <strong>Note:</strong> If the service is not yet connected (e.g.
+         * {@link AccessibilityService#onServiceConnected()} has not yet been
+         * called) or the service has been disconnected, this method will have
+         * no effect and return {@code false}.
+         * </p>
+         *
+         * @param animate {@code true} to animate from the current scale and
+         *                center or {@code false} to reset the scale and center
+         *                immediately
+         * @return {@code true} on success, {@code false} on failure
+         */
+        public boolean resetCurrentMagnification(boolean animate) {
+            final IAccessibilityServiceConnection connection =
+                    AccessibilityInteractionClient.getInstance(mService).getConnection(
+                            mService.mConnectionId);
+            if (connection != null) {
+                try {
+                    return connection.resetCurrentMagnification(mDisplayId, animate);
+                } catch (RemoteException re) {
+                    Log.w(LOG_TAG, "Failed to reset", re);
+                    re.rethrowFromSystemServer();
+                }
+            }
+            return false;
+        }
+
+        /**
          * Sets the {@link MagnificationConfig}. The service controls the magnification by
          * setting the config.
          * <p>
@@ -1665,6 +1742,10 @@
         public interface OnMagnificationChangedListener {
             /**
              * Called when the magnified region, scale, or center changes.
+             * <p>
+             * <strong>Note:</strong> This legacy callback notifies only full-screen
+             * magnification change.
+             * </p>
              *
              * @param controller the magnification controller
              * @param region the magnification region
@@ -1676,6 +1757,38 @@
              */
             void onMagnificationChanged(@NonNull MagnificationController controller,
                     @NonNull Region region, float scale, float centerX, float centerY);
+
+            /**
+             * Called when the magnified region, mode, scale, or center changes of
+             * all magnification modes.
+             * <p>
+             * <strong>Note:</strong> This method can be overridden to listen to the
+             * magnification changes of all magnification modes then the legacy callback
+             * would not receive the notifications.
+             * Skipping calling super when overriding this method results in
+             * {@link #onMagnificationChanged(MagnificationController, Region, float, float, float)}
+             * not getting called.
+             * </p>
+             *
+             * @param controller the magnification controller
+             * @param region the magnification region
+             *               If the config mode is
+             *               {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+             *               it is the region of the screen currently active for magnification.
+             *               that is the same region as {@link #getMagnificationRegion()}.
+             *               If the config mode is
+             *               {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+             *               it is the region of screen projected on the magnification window.
+             * @param config The magnification config. That has the controlling magnification
+             *               mode, the new scale and the new screen-relative center position
+             */
+            default void onMagnificationChanged(@NonNull MagnificationController controller,
+                    @NonNull Region region, @NonNull MagnificationConfig config) {
+                if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) {
+                    onMagnificationChanged(controller, region,
+                            config.getScale(), config.getCenterX(), config.getCenterY());
+                }
+            }
         }
     }
 
@@ -2449,9 +2562,8 @@
 
             @Override
             public void onMagnificationChanged(int displayId, @NonNull Region region,
-                    float scale, float centerX, float centerY) {
-                AccessibilityService.this.onMagnificationChanged(displayId, region, scale,
-                        centerX, centerY);
+                    MagnificationConfig config) {
+                AccessibilityService.this.onMagnificationChanged(displayId, region, config);
             }
 
             @Override
@@ -2575,12 +2687,10 @@
 
         /** Magnification changed callbacks for different displays */
         public void onMagnificationChanged(int displayId, @NonNull Region region,
-                float scale, float centerX, float centerY) {
+                MagnificationConfig config) {
             final SomeArgs args = SomeArgs.obtain();
             args.arg1 = region;
-            args.arg2 = scale;
-            args.arg3 = centerX;
-            args.arg4 = centerY;
+            args.arg2 = config;
             args.argi1 = displayId;
 
             final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
@@ -2739,13 +2849,10 @@
                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
                         final SomeArgs args = (SomeArgs) message.obj;
                         final Region region = (Region) args.arg1;
-                        final float scale = (float) args.arg2;
-                        final float centerX = (float) args.arg3;
-                        final float centerY = (float) args.arg4;
+                        final MagnificationConfig config = (MagnificationConfig) args.arg2;
                         final int displayId = args.argi1;
                         args.recycle();
-                        mCallback.onMagnificationChanged(displayId, region, scale,
-                                centerX, centerY);
+                        mCallback.onMagnificationChanged(displayId, region, config);
                     }
                     return;
                 }
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index a821dad..857c541 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -525,7 +525,7 @@
         public GestureStep(Parcel parcel) {
             timeSinceGestureStart = parcel.readLong();
             Parcelable[] parcelables =
-                    parcel.readParcelableArray(TouchPoint.class.getClassLoader());
+                    parcel.readParcelableArray(TouchPoint.class.getClassLoader(), TouchPoint.class);
             numTouchPoints = (parcelables == null) ? 0 : parcelables.length;
             touchPoints = new TouchPoint[numTouchPoints];
             for (int i = 0; i < numTouchPoints; i++) {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 651c50f..375383d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -21,6 +21,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.MagnificationConfig;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -43,7 +44,7 @@
 
     void onKeyEvent(in KeyEvent event, int sequence);
 
-    void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
+    void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config);
 
     void onMotionEvent(in MotionEvent event);
 
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7d76bbf..2cc15b4 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -88,8 +88,12 @@
 
     Region getMagnificationRegion(int displayId);
 
+    Region getCurrentMagnificationRegion(int displayId);
+
     boolean resetMagnification(int displayId, boolean animate);
 
+    boolean resetCurrentMagnification(int displayId, boolean animate);
+
     boolean setMagnificationConfig(int displayId, in MagnificationConfig config, boolean animate);
 
     void setMagnificationCallbackEnabled(int displayId, boolean enabled);
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index a8ba1d3..bb2b8d4 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -24,6 +24,8 @@
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 
+import java.util.LinkedList;
+import java.util.Queue;
 import java.util.concurrent.Executor;
 
 /**
@@ -102,6 +104,11 @@
     private boolean mServiceDetectsGestures;
     /** Map of callbacks to executors. Lazily created when adding the first callback. */
     private ArrayMap<Callback, Executor> mCallbacks;
+    // A list of motion events that should be queued until a pending transition has taken place.
+    private Queue<MotionEvent> mQueuedMotionEvents = new LinkedList<>();
+    // Whether this controller is waiting for a state transition.
+    // Motion events will be queued and sent to listeners after the transition has taken place.
+    private boolean mStateChangeRequested = false;
 
     // The current state of the display.
     private int mState = STATE_CLEAR;
@@ -169,6 +176,14 @@
      * main thread.
      */
     void onMotionEvent(MotionEvent event) {
+        if (mStateChangeRequested) {
+            mQueuedMotionEvents.add(event);
+        } else {
+            sendEventToAllListeners(event);
+        }
+    }
+
+    private void sendEventToAllListeners(MotionEvent event) {
         final ArrayMap<Callback, Executor> entries;
         synchronized (mLock) {
             // callbacks may remove themselves. Perform a shallow copy to avoid concurrent
@@ -209,6 +224,10 @@
                 callback.onStateChanged(state);
             }
         }
+        mStateChangeRequested = false;
+        while (mQueuedMotionEvents.size() > 0) {
+            sendEventToAllListeners(mQueuedMotionEvents.poll());
+        }
     }
 
     /**
@@ -253,6 +272,7 @@
             } catch (RemoteException re) {
                 throw new RuntimeException(re);
             }
+            mStateChangeRequested = true;
         }
     }
 
@@ -281,6 +301,7 @@
             } catch (RemoteException re) {
                 throw new RuntimeException(re);
             }
+            mStateChangeRequested = true;
         }
     }
 
@@ -302,6 +323,7 @@
             } catch (RemoteException re) {
                 throw new RuntimeException(re);
             }
+            mStateChangeRequested = true;
         }
     }
 
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 2bbf280..fa9de6e 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -384,7 +384,7 @@
                 new PropertyInvalidatedCache<UserIdPackage, Account[]>(
                 CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
         @Override
-        protected Account[] recompute(UserIdPackage userAndPackage) {
+        public Account[] recompute(UserIdPackage userAndPackage) {
             try {
                 return mService.getAccountsAsUser(null, userAndPackage.userId, userAndPackage.packageName);
             } catch (RemoteException e) {
@@ -392,11 +392,11 @@
             }
         }
         @Override
-        protected boolean bypass(UserIdPackage query) {
+        public boolean bypass(UserIdPackage query) {
             return query.userId < 0;
         }
         @Override
-        protected boolean debugCompareQueryResults(Account[] l, Account[] r) {
+        public boolean resultEquals(Account[] l, Account[] r) {
             if (l == r) {
                 return true;
             } else if (l == null || r == null) {
@@ -455,7 +455,7 @@
             new PropertyInvalidatedCache<AccountKeyData, String>(CACHE_USER_DATA_SIZE,
                     CACHE_KEY_USER_DATA_PROPERTY) {
             @Override
-            protected String recompute(AccountKeyData accountKeyData) {
+            public String recompute(AccountKeyData accountKeyData) {
                 Account account = accountKeyData.account;
                 String key = accountKeyData.key;
 
diff --git a/core/java/android/accounts/CantAddAccountActivity.java b/core/java/android/accounts/CantAddAccountActivity.java
index f7f232e..107efc3 100644
--- a/core/java/android/accounts/CantAddAccountActivity.java
+++ b/core/java/android/accounts/CantAddAccountActivity.java
@@ -16,9 +16,13 @@
 package android.accounts;
 
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+
 import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
 import android.os.Bundle;
 import android.view.View;
+import android.widget.TextView;
 
 import com.android.internal.R;
 
@@ -33,6 +37,12 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.app_not_authorized);
+
+        TextView view = findViewById(R.id.description);
+        String text = getSystemService(DevicePolicyManager.class).getString(
+                CANT_ADD_ACCOUNT_MESSAGE,
+                () -> getString(R.string.error_message_change_not_allowed));
+        view.setText(text);
     }
 
     public void onCancelButtonClicked(View view) {
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 2e9f73c..0d82ac9 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -15,7 +15,10 @@
  */
 package android.accounts;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+
 import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -199,7 +202,14 @@
 
         if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) {
             requestWindowFeature(Window.FEATURE_NO_TITLE);
+
             setContentView(R.layout.app_not_authorized);
+            TextView view = findViewById(R.id.description);
+            String text = getSystemService(DevicePolicyManager.class).getString(
+                    CANT_ADD_ACCOUNT_MESSAGE,
+                    () -> getString(R.string.error_message_change_not_allowed));
+            view.setText(text);
+
             mDontShowPicker = true;
         }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cf2b7ac..a7b96a6 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -90,6 +90,7 @@
 import android.transition.TransitionManager;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.Dumpable;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -110,6 +111,8 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
+import android.view.OnBackInvokedDispatcherOwner;
 import android.view.RemoteAnimationDefinition;
 import android.view.SearchEvent;
 import android.view.View;
@@ -145,6 +148,7 @@
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
 import com.android.internal.policy.PhoneWindow;
+import com.android.internal.util.dump.DumpableContainerImpl;
 
 import dalvik.system.VMRuntime;
 
@@ -734,7 +738,8 @@
         Window.Callback, KeyEvent.Callback,
         OnCreateContextMenuListener, ComponentCallbacks2,
         Window.OnWindowDismissedCallback,
-        ContentCaptureManager.ContentCaptureClient {
+        ContentCaptureManager.ContentCaptureClient,
+        OnBackInvokedDispatcherOwner {
     private static final String TAG = "Activity";
     private static final boolean DEBUG_LIFECYCLE = false;
 
@@ -954,6 +959,9 @@
 
     private SplashScreen mSplashScreen;
 
+    @Nullable
+    private DumpableContainerImpl mDumpableContainer;
+
     private final WindowControllerCallback mWindowControllerCallback =
             new WindowControllerCallback() {
         /**
@@ -1516,7 +1524,10 @@
     }
 
     private void dispatchActivityConfigurationChanged() {
-        getApplication().dispatchActivityConfigurationChanged(this);
+        // In case the new config comes before mApplication is assigned.
+        if (getApplication() != null) {
+            getApplication().dispatchActivityConfigurationChanged(this);
+        }
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i = 0; i < callbacks.length; i++) {
@@ -7081,8 +7092,23 @@
         dumpInner(prefix, fd, writer, args);
     }
 
+    /**
+     * See {@link android.util.DumpableContainer#addDumpable(Dumpable)}.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public final boolean addDumpable(@NonNull Dumpable dumpable) {
+        if (mDumpableContainer == null) {
+            mDumpableContainer = new DumpableContainerImpl();
+        }
+        return mDumpableContainer.addDumpable(dumpable);
+    }
+
     void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
             @NonNull PrintWriter writer, @Nullable String[] args) {
+        String innerPrefix = prefix + "  ";
+
         if (args != null && args.length > 0) {
             // Handle special cases
             switch (args[0]) {
@@ -7095,12 +7121,33 @@
                 case "--translation":
                     dumpUiTranslation(prefix, writer);
                     return;
+                case "--list-dumpables":
+                    if (mDumpableContainer == null) {
+                        writer.print(prefix); writer.println("No dumpables");
+                        return;
+                    }
+                    mDumpableContainer.listDumpables(prefix, writer);
+                    return;
+                case "--dump-dumpable":
+                    if (args.length == 1) {
+                        writer.println("--dump-dumpable requires the dumpable name");
+                        return;
+                    }
+                    if (mDumpableContainer == null) {
+                        writer.println("no dumpables");
+                        return;
+                    }
+                    // Strips --dump-dumpable NAME
+                    String[] prunedArgs = new String[args.length - 2];
+                    System.arraycopy(args, 2, prunedArgs, 0, prunedArgs.length);
+                    mDumpableContainer.dumpOneDumpable(prefix, writer, args[1], prunedArgs);
+                    return;
             }
         }
+
         writer.print(prefix); writer.print("Local Activity ");
                 writer.print(Integer.toHexString(System.identityHashCode(this)));
                 writer.println(" State:");
-        String innerPrefix = prefix + "  ";
         writer.print(innerPrefix); writer.print("mResumed=");
                 writer.print(mResumed); writer.print(" mStopped=");
                 writer.print(mStopped); writer.print(" mFinished=");
@@ -7138,6 +7185,10 @@
         dumpUiTranslation(prefix, writer);
 
         ResourcesManager.getInstance().dump(prefix, writer);
+
+        if (mDumpableContainer != null) {
+            mDumpableContainer.dumpAllDumpables(prefix, writer, args);
+        }
     }
 
     void dumpContentCaptureManager(String prefix, PrintWriter writer) {
@@ -8627,4 +8678,22 @@
             return (w != null && w.peekDecorView() != null);
         }
     }
+
+    /**
+     * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
+     * activity is attached to.
+     *
+     * Returns null if the activity is not attached to a window with a decor.
+     */
+    @Nullable
+    @Override
+    public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+        if (mWindow != null) {
+            View decorView = mWindow.getDecorView();
+            if (decorView != null) {
+                return decorView.getOnBackInvokedDispatcher();
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 324e1ae..96487de 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -71,6 +71,9 @@
     }
 
     // Access modes for handleIncomingUser.
+    /**
+     * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
+     */
     public static final int ALLOW_NON_FULL = 0;
     /**
      * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
@@ -78,13 +81,18 @@
      * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
      */
     public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+    /**
+     * Allows access to a caller only if it has the full
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     */
     public static final int ALLOW_FULL_ONLY = 2;
     /**
      * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
-     * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
-     * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+     * if in the same profile group.
+     * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS} is required and suffices
+     * as in {@link #ALLOW_NON_FULL}.
      */
-    public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
+    public static final int ALLOW_PROFILES_OR_NON_FULL = 3;
 
     /**
      * Returns profile information in free form string in two separate strings.
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index f7d5e52..5e5649f 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -337,7 +337,7 @@
     private static final String KEY_LAUNCHED_FROM_BUBBLE =
             "android.activity.launchTypeBubble";
 
-    /** See {@link #setSplashscreenStyle(int)}. */
+    /** See {@link #setSplashScreenStyle(int)}. */
     private static final String KEY_SPLASH_SCREEN_STYLE =
             "android.activity.splashScreenStyle";
 
@@ -1393,20 +1393,27 @@
     }
 
     /**
-     * Sets the preferred splash screen style.
+     * Gets the style can be used for cold-launching an activity.
+     * @see #setSplashScreenStyle(int)
      * @hide
      */
-    public void setSplashscreenStyle(@SplashScreen.SplashScreenStyle int style) {
-        mSplashScreenStyle = style;
+    public @SplashScreen.SplashScreenStyle int getSplashScreenStyle() {
+        return mSplashScreenStyle;
     }
 
     /**
-     * Gets the preferred splash screen style from caller
-     * @hide
+     * Sets the preferred splash screen style of the opening activities. This only applies if the
+     * Activity or Process is not yet created.
+     * @param style Can be either {@link SplashScreen#SPLASH_SCREEN_STYLE_ICON} or
+     *              {@link SplashScreen#SPLASH_SCREEN_STYLE_EMPTY}
      */
-    @SplashScreen.SplashScreenStyle
-    public int getSplashScreenStyle() {
-        return mSplashScreenStyle;
+    @NonNull
+    public ActivityOptions setSplashScreenStyle(@SplashScreen.SplashScreenStyle int style) {
+        if (style == SplashScreen.SPLASH_SCREEN_STYLE_ICON
+                || style == SplashScreen.SPLASH_SCREEN_STYLE_EMPTY) {
+            mSplashScreenStyle = style;
+        }
+        return this;
     }
 
     /**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1778ea4..ea62714 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -61,6 +61,7 @@
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.TransactionExecutor;
 import android.app.servertransaction.TransactionExecutorHelper;
+import android.bluetooth.BluetoothFrameworkInitializer;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
@@ -105,9 +106,11 @@
 import android.media.MediaServiceManager;
 import android.net.ConnectivityManager;
 import android.net.Proxy;
+import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.BluetoothServiceManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -1646,7 +1649,7 @@
 
         @Override
         public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) {
-            PropertyInvalidatedCache.dumpCacheInfo(pfd.getFileDescriptor(), args);
+            PropertyInvalidatedCache.dumpCacheInfo(pfd, args);
             IoUtils.closeQuietly(pfd);
         }
 
@@ -4178,15 +4181,20 @@
         view.requestLayout();
 
         view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
+            private boolean mHandled = false;
             @Override
             public void onDraw() {
+                if (mHandled) {
+                    return;
+                }
+                mHandled = true;
                 // Transfer the splash screen view from shell to client.
                 // Call syncTransferSplashscreenViewTransaction at the first onDraw so we can ensure
                 // the client view is ready to show and we can use applyTransactionOnDraw to make
                 // all transitions happen at the same frame.
                 syncTransferSplashscreenViewTransaction(
                         view, r.token, decorView, startingWindowLeash);
-                view.postOnAnimation(() -> view.getViewTreeObserver().removeOnDrawListener(this));
+                view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this));
             }
         });
     }
@@ -4530,6 +4538,12 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
+        // To investigate "duplciate Application objects" bug (b/185177290)
+        if (UserHandle.myUserId() != UserHandle.getUserId(data.info.applicationInfo.uid)) {
+            Slog.wtf(TAG, "handleCreateService called with wrong appinfo UID: myUserId="
+                    + UserHandle.myUserId() + " appinfo.uid=" + data.info.applicationInfo.uid);
+        }
+
         LoadedApk packageInfo = getPackageInfoNoCheck(
                 data.info.applicationInfo, data.compatInfo);
         Service service = null;
@@ -4645,12 +4659,14 @@
     }
 
     private void handleDumpGfxInfo(DumpComponentInfo info) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         try {
             ThreadedRenderer.handleDumpGfxInfo(info.fd.getFileDescriptor(), info.args);
         } catch (Exception e) {
             Log.w(TAG, "Caught exception from dumpGfxInfo()", e);
         } finally {
             IoUtils.closeQuietly(info.fd);
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
@@ -6378,9 +6394,7 @@
         if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level);
 
         if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
-            for (PropertyInvalidatedCache pic : PropertyInvalidatedCache.getActiveCaches()) {
-                pic.clear();
-            }
+            PropertyInvalidatedCache.onTrimMemory();
         }
 
         final ArrayList<ComponentCallbacks2> callbacks =
@@ -6714,6 +6728,13 @@
         NetworkSecurityConfigProvider.install(appContext);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
+        // For backward compatibility, TrafficStats needs static access to the application context.
+        // But for isolated apps which cannot access network related services, service discovery
+        // is restricted. Hence, calling this would result in NPE.
+        if (!Process.isIsolated()) {
+            TrafficStats.init(appContext);
+        }
+
         // Continue loading instrumentation.
         if (ii != null) {
             initInstrumentation(ii, data, appContext);
@@ -7900,6 +7921,7 @@
         StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
         MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager());
         MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
+        BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
     }
 
     private void purgePendingResources() {
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 7f849ef..9eb3e8f 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -29,9 +29,13 @@
 import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Bundle;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
 import android.view.autofill.AutofillManager;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 
 /**
@@ -53,6 +57,10 @@
  */
 public class Application extends ContextWrapper implements ComponentCallbacks2 {
     private static final String TAG = "Application";
+
+    /** Whether to enable the check to detect "duplicate application instances". */
+    private static final boolean DEBUG_DUP_APP_INSTANCES = true;
+
     @UnsupportedAppUsage
     private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
             new ArrayList<ActivityLifecycleCallbacks>();
@@ -66,6 +74,13 @@
     @UnsupportedAppUsage
     public LoadedApk mLoadedApk;
 
+    @GuardedBy("sInstances")
+    private static final ArrayMap<Class<?>, Application> sInstances =
+            DEBUG_DUP_APP_INSTANCES ? new ArrayMap<>(1) : null;
+
+    // Only set when DEBUG_DUP_APP_INSTANCES is true.
+    private StackTrace mConstructorStackTrace;
+
     public interface ActivityLifecycleCallbacks {
 
         /**
@@ -231,6 +246,41 @@
 
     public Application() {
         super(null);
+        if (DEBUG_DUP_APP_INSTANCES) {
+            checkDuplicateInstances();
+        }
+    }
+
+    private void checkDuplicateInstances() {
+        final Class<?> myClass = this.getClass();
+
+        // We only activate this check for custom application classes.
+        // Otherwise, it'd misfire if multiple apps share the same process, if all of them use
+        // the same Application class (on the same classloader).
+        if (myClass == Application.class) {
+            return;
+        }
+        synchronized (sInstances) {
+            final Application firstInstance = sInstances.get(myClass);
+            if (firstInstance == null) {
+                this.mConstructorStackTrace = new StackTrace("First ctor was called here");
+                sInstances.put(myClass, this);
+                return;
+            }
+            final StackTrace currentStackTrace = new StackTrace("Current ctor was called here",
+                    firstInstance.mConstructorStackTrace);
+            this.mConstructorStackTrace = currentStackTrace;
+            Slog.wtf(TAG, "Application ctor called twice for " + myClass
+                    + " first LoadedApk=" + firstInstance.getLoadedApkInfo(),
+                    currentStackTrace);
+        }
+    }
+
+    private String getLoadedApkInfo() {
+        if (mLoadedApk == null) {
+            return "null";
+        }
+        return mLoadedApk + "/pkg=" + mLoadedApk.mPackageName;
     }
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 44fb5db..7b55b6c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,6 +62,7 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -73,12 +74,6 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.ArtManager;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.pm.pkg.FrameworkPackageUserState;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -802,7 +797,7 @@
             new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
                 256, "cache_key.has_system_feature") {
                 @Override
-                protected Boolean recompute(HasSystemFeatureQuery query) {
+                public Boolean recompute(HasSystemFeatureQuery query) {
                     try {
                         return ActivityThread.currentActivityThread().getPackageManager().
                             hasSystemFeature(query.name, query.version);
@@ -1098,7 +1093,7 @@
             new PropertyInvalidatedCache<Integer, GetPackagesForUidResult>(
                 32, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
                 @Override
-                protected GetPackagesForUidResult recompute(Integer uid) {
+                public GetPackagesForUidResult recompute(Integer uid) {
                     try {
                         return new GetPackagesForUidResult(
                             ActivityThread.currentActivityThread().
@@ -2335,37 +2330,6 @@
         return info.loadLabel(this);
     }
 
-    @Nullable
-    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
-        return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
-    }
-
-    @Nullable
-    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
-            PackageInfoFlags flags) {
-        long flagsBits = flags.getValue();
-        if ((flagsBits & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
-            // Caller expressed no opinion about what encryption
-            // aware/unaware components they want to see, so match both
-            flagsBits |= PackageManager.MATCH_DIRECT_BOOT_AWARE
-                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-        }
-
-        boolean collectCertificates = (flagsBits & PackageManager.GET_SIGNATURES) != 0
-                || (flagsBits & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
-
-        ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
-        ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
-                new File(archiveFilePath), 0, getPermissionManager().getSplitPermissions(),
-                collectCertificates);
-        if (result.isError()) {
-            return null;
-        }
-        return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flagsBits, 0, 0,
-                null, FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
-    }
-
     @Override
     public int installExistingPackage(String packageName) throws NameNotFoundException {
         return installExistingPackage(packageName, INSTALL_REASON_UNKNOWN);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 0bb6ffa..7812aba 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -430,8 +430,8 @@
      * permissions set by {@link #setRequireAllOfPermissions(String[])}, and none of the
      * permissions set by {@link #setRequireNoneOfPermissions(String[])} to get the broadcast.
      *
-     * @param requiredPermissions a list of Strings of permission the receiver must have, or null
-     *                            to clear any previously set value.
+     * @param requiredPermissions a list of Strings of permission the receiver must have. Set to
+     *                            null or an empty array to clear any previously set value.
      * @hide
      */
     @SystemApi
@@ -449,8 +449,8 @@
      * permissions set by {@link #setRequireAllOfPermissions(String[])}, and none of the
      * permissions set by {@link #setRequireNoneOfPermissions(String[])} to get the broadcast.
      *
-     * @param excludedPermissions a list of Strings of permission the receiver must not have,
-     *                            or null to clear any previously set value.
+     * @param excludedPermissions a list of Strings of permission the receiver must not have. Set to
+     *                            null or an empty array to clear any previously set value.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c895636..f3315a8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -113,6 +113,7 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -311,6 +312,14 @@
     @ContextType
     private int mContextType;
 
+    /**
+     * {@code true} to indicate that the {@link Context} owns the {@link #getWindowContextToken()}
+     * and is responsible for detaching the token when the Context is released.
+     *
+     * @see #finalize()
+     */
+    private boolean mOwnsToken = false;
+
     @GuardedBy("mSync")
     private File mDatabasesDir;
     @GuardedBy("mSync")
@@ -1796,7 +1805,6 @@
                     && ((flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
                 flags = flags | Context.RECEIVER_EXPORTED;
             }
-
             final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                     mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
                     AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
@@ -1811,9 +1819,6 @@
             return intent;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
-        } catch (WtfException e) {
-            Log.wtf(TAG, e.getMessage());
-            return null;
         }
     }
 
@@ -2174,6 +2179,11 @@
     }
 
     @Override
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+        getSystemService(PermissionManager.class).revokeOwnPermissionsOnKill(permissions);
+    }
+
+    @Override
     public int checkCallingPermission(String permission) {
         if (permission == null) {
             throw new IllegalArgumentException("permission is null");
@@ -3013,7 +3023,7 @@
         // WindowContainer. We should detach from WindowContainer when the Context is finalized
         // if this Context is not a WindowContext. WindowContext finalization is handled in
         // WindowContext class.
-        if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) {
+        if (mToken instanceof WindowTokenClient && mOwnsToken) {
             ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
         }
         super.finalize();
@@ -3044,6 +3054,7 @@
         token.attachContext(context);
         token.attachToDisplayContent(displayId);
         context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
+        context.mOwnsToken = true;
 
         return context;
     }
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 3060353..a7fb83b 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -52,6 +52,8 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
+import android.view.OnBackInvokedDispatcherOwner;
 import android.view.SearchEvent;
 import android.view.View;
 import android.view.View.OnCreateContextMenuListener;
@@ -94,7 +96,8 @@
  * </div>
  */
 public class Dialog implements DialogInterface, Window.Callback,
-        KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
+        KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback,
+        OnBackInvokedDispatcherOwner {
     private static final String TAG = "Dialog";
     @UnsupportedAppUsage
     private Activity mOwnerActivity;
@@ -1439,4 +1442,22 @@
             }
         }
     }
+
+    /**
+     * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
+     * dialog is attached to.
+     *
+     * Returns null if the dialog is not attached to a window with a decor.
+     */
+    @Nullable
+    @Override
+    public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+        if (mWindow != null) {
+            View decorView = mWindow.getDecorView();
+            if (decorView != null) {
+                return decorView.getOnBackInvokedDispatcher();
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 29e1b70..76471d3 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -119,6 +119,31 @@
     }
 
     /**
+     * Returns the {@link GameModeInfo} associated with the game associated with
+     * the given {@code packageName}. If the given package is not a game, {@code null} is
+     * always returned.
+     * <p>
+     * An application can use <code>android:isGame="true"</code> or
+     * <code>android:appCategory="game"</code> to indicate that the application is a game.
+     * If the manifest doesn't define a category, the category can also be
+     * provided by the installer via
+     * {@link android.content.pm.PackageManager#setApplicationCategoryHint(String, int)}.
+     * <p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public @Nullable GameModeInfo getGameModeInfo(@NonNull String packageName) {
+        try {
+            return mService.getGameModeInfo(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Sets the game mode for the given package.
      * <p>
      * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
diff --git a/core/java/android/app/GameModeInfo.aidl b/core/java/android/app/GameModeInfo.aidl
new file mode 100644
index 0000000..3b13201
--- /dev/null
+++ b/core/java/android/app/GameModeInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * @hide
+ */
+parcelable GameModeInfo;
\ No newline at end of file
diff --git a/core/java/android/app/GameModeInfo.java b/core/java/android/app/GameModeInfo.java
new file mode 100644
index 0000000..fe0ac35
--- /dev/null
+++ b/core/java/android/app/GameModeInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * GameModeInfo returned from {@link GameManager#getGameModeInfo(String)}.
+ * @hide
+ */
+@SystemApi
+public final class GameModeInfo implements Parcelable {
+
+    public static final @NonNull Creator<GameModeInfo> CREATOR = new Creator<GameModeInfo>() {
+        @Override
+        public GameModeInfo createFromParcel(Parcel in) {
+            return new GameModeInfo(in);
+        }
+
+        @Override
+        public GameModeInfo[] newArray(int size) {
+            return new GameModeInfo[size];
+        }
+    };
+
+    public GameModeInfo(@GameManager.GameMode int activeGameMode,
+            @NonNull @GameManager.GameMode int[] availableGameModes) {
+        mActiveGameMode = activeGameMode;
+        mAvailableGameModes = availableGameModes;
+    }
+
+    GameModeInfo(Parcel in) {
+        mActiveGameMode = in.readInt();
+        final int availableGameModesCount = in.readInt();
+        mAvailableGameModes = new int[availableGameModesCount];
+        in.readIntArray(mAvailableGameModes);
+    }
+
+    /**
+     * Returns the {@link GameManager.GameMode} the application is currently using.
+     * Developers can enable game modes by adding
+     * <code>
+     *     <meta-data android:name="android.game_mode_intervention"
+     *             android:resource="@xml/GAME_MODE_CONFIG_FILE" />
+     * </code>
+     * to the {@link <application> tag}, where the GAME_MODE_CONFIG_FILE is an XML file that
+     * specifies the game mode enablement and configuration:
+     * <code>
+     *     <game-mode-config xmlns:android="http://schemas.android.com/apk/res/android"
+     *         android:gameModePerformance="true"
+     *         android:gameModeBattery="false"
+     *     />
+     * </code>
+     */
+    public @GameManager.GameMode int getActiveGameMode() {
+        return mActiveGameMode;
+    }
+
+    /**
+     * The collection of {@link GameManager.GameMode GameModes} that can be applied to the game.
+     */
+    @NonNull
+    public @GameManager.GameMode int[] getAvailableGameModes() {
+        return mAvailableGameModes;
+    }
+
+    // Ideally there should be callback that the caller can register to know when the available
+    // GameMode and/or the active GameMode is changed, however, there's no concrete use case
+    // at the moment so there's no callback mechanism introduced    .
+    private final @GameManager.GameMode int[] mAvailableGameModes;
+    private final @GameManager.GameMode int mActiveGameMode;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mActiveGameMode);
+        dest.writeInt(mAvailableGameModes.length);
+        dest.writeIntArray(mAvailableGameModes);
+    }
+}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b052bc5..0801b24 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -72,6 +72,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
 import android.window.IWindowOrganizerController;
+import android.window.BackNavigationInfo;
 import android.window.SplashScreenView;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
@@ -178,17 +179,16 @@
     Point getAppTaskThumbnailSize();
     /**
      * Only callable from the system. This token grants a temporary permission to call
-     * #startActivityAsCallerWithToken. The token will time out after
-     * START_AS_CALLER_TOKEN_TIMEOUT if it is not used.
+     * #startActivityAsCaller. The token will time out after START_AS_CALLER_TOKEN_TIMEOUT
+     * if it is not used.
      *
-     * @param delegatorToken The Binder token referencing the system Activity that wants to delegate
-     *        the #startActivityAsCaller to another app. The "caller" will be the caller of this
-     *        activity's token, not the delegate's caller (which is probably the delegator itself).
+     * @param componentName The component name of the delegated component that is allowed to
+     *                      call #startActivityAsCaller with the returned token.
      *
      * @return Returns a token that can be given to a "delegate" app that may call
      *         #startActivityAsCaller
      */
-    IBinder requestStartActivityPermissionToken(in IBinder delegatorToken);
+    IBinder requestStartActivityPermissionToken(in ComponentName componentName);
 
     oneway void releaseSomeActivities(in IApplicationThread app);
     Bitmap getTaskDescriptionIcon(in String filename, int userId);
@@ -347,7 +347,8 @@
     void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
 
     /**
-     * Prepare the back preview in the server
+     * Prepare the back navigation in the server. This setups the leashed for sysui to animate
+     * the back gesture and returns the data needed for the animation.
      */
-    void startBackPreview(IRemoteAnimationRunner runner);
+    android.window.BackNavigationInfo startBackNavigation();
 }
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index d9aa586..57de8c7 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.GameModeInfo;
 import android.app.GameState;
 
 /**
@@ -27,4 +28,5 @@
     int[] getAvailableGameModes(String packageName);
     boolean getAngleEnabled(String packageName, int userId);
     void setGameState(String packageName, in GameState gameState, int userId);
-}
\ No newline at end of file
+    GameModeInfo getGameModeInfo(String packageName, int userId);
+}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index fdcf99d..a82ecce 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -63,6 +63,7 @@
     boolean isInInvalidMsgState(String pkg, int uid);
     boolean hasUserDemotedInvalidMsgApp(String pkg, int uid);
     void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted);
+    boolean hasSentValidBubble(String pkg, int uid);
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     /**
      * Updates the notification's enabled state. Additionally locks importance for all of the
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 440dd62..55afed2 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -48,20 +48,43 @@
     
     /**
      * Sets the night mode.
+     * <p>
      * The mode can be one of:
-     *   1 - notnight mode
-     *   2 - night mode
-     *   3 - automatic mode switching
+     * <ol>notnight mode</ol>
+     * <ol>night mode</ol>
+     * <ol>custom schedule mode switching</ol>
      */
     void setNightMode(int mode);
 
     /**
-     * Gets the currently configured night mode.  Return 1 for notnight,
-     * 2 for night, and 3 for automatic mode switching.
+     * Gets the currently configured night mode.
+     * <p>
+     * Returns
+     * <ol>notnight mode</ol>
+     * <ol>night mode</ol>
+     * <ol>custom schedule mode switching</ol>
      */
     int getNightMode();
 
     /**
+     * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type
+     * {@code nightModeCustomType}.
+     *
+     * @param nightModeCustomType
+     * @hide
+     */
+    void setNightModeCustomType(int nightModeCustomType);
+
+    /**
+     * Returns the custom night mode type.
+     * <p>
+     * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns
+     * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}.
+     * @hide
+     */
+    int getNightModeCustomType();
+
+    /**
      * Sets the dark mode for the given application. This setting is persisted and will override the
      * system configuration for this application.
      *   1 - notnight mode
@@ -81,8 +104,21 @@
     boolean isNightModeLocked();
 
     /**
-    * [De]Activates night mode
-    */
+     * [De]activating night mode for the current user if the current night mode is custom and the
+     * custom type matches {@code nightModeCustomType}.
+     *
+     * @param nightModeCustomType the specify type of custom mode
+     * @param active {@code true} to activate night mode. Otherwise, deactivate night mode
+     * @return {@code true} if night mode has successfully activated for the requested
+     *         {@code nightModeCustomType}.
+     * @hide
+     */
+    boolean setNightModeActivatedForCustomMode(int nightModeCustom, boolean active);
+
+    /**
+     * [De]Activates night mode.
+     * @hide
+     */
     boolean setNightModeActivated(boolean active);
 
     /**
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 4f7c684..28c273e 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -204,4 +204,27 @@
      * @hide
      */
     void notifyGoingToSleep(int x, int y, in Bundle extras);
+
+    /**
+     * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
+     * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
+     *
+     * @hide
+     */
+    oneway void setWallpaperDimAmount(float dimAmount);
+
+    /**
+     * Gets the current additional dim amount set on the wallpaper. 0f means no application has
+     * added any dimming on top of the system default dim amount.
+     *
+     * @hide
+     */
+    float getWallpaperDimAmount();
+
+    /**
+     * Whether the lock screen wallpaper is different from the system wallpaper.
+     *
+     * @hide
+     */
+    boolean lockScreenWallpaperExists();
 }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index dc71a32..14afd0f 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,8 +41,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.persistentdata.IPersistentDataBlockService;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.IOnKeyguardExitResult;
 import android.view.IWindowManager;
@@ -49,6 +52,9 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
 import com.android.internal.widget.LockscreenCredential;
@@ -57,6 +63,8 @@
 import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * Class that can be used to lock and unlock the keyguard. The
@@ -69,10 +77,13 @@
     private static final String TAG = "KeyguardManager";
 
     private final Context mContext;
+    private final LockPatternUtils mLockPatternUtils;
     private final IWindowManager mWM;
     private final IActivityManager mAm;
     private final ITrustManager mTrustManager;
     private final INotificationManager mNotificationManager;
+    private final ArrayMap<WeakEscrowTokenRemovedListener, IWeakEscrowTokenRemovedListener>
+            mListeners = new ArrayMap<>();
 
     /**
      * Intent used to prompt user for device credentials.
@@ -455,8 +466,42 @@
         public void onDismissCancelled() { }
     }
 
+    /**
+     * Callback passed to
+     * {@link KeyguardManager#addWeakEscrowToken}
+     * to notify caller of state change.
+     * @hide
+     */
+    @SystemApi
+    public interface WeakEscrowTokenActivatedListener {
+        /**
+         * The method to be called when the token is activated.
+         * @param handle 64 bit handle corresponding to the escrow token
+         * @param user user for whom the weak escrow token has been added
+         */
+        void onWeakEscrowTokenActivated(long handle, @NonNull UserHandle user);
+    }
+
+    /**
+     * Listener passed to
+     * {@link KeyguardManager#registerWeakEscrowTokenRemovedListener} and
+     * {@link KeyguardManager#unregisterWeakEscrowTokenRemovedListener}
+     * to notify caller of an weak escrow token has been removed.
+     * @hide
+     */
+    @SystemApi
+    public interface WeakEscrowTokenRemovedListener {
+        /**
+         * The method to be called when the token is removed.
+         * @param handle 64 bit handle corresponding to the escrow token
+         * @param user user for whom the escrow token has been added
+         */
+        void onWeakEscrowTokenRemoved(long handle, @NonNull UserHandle user);
+    }
+
     KeyguardManager(Context context) throws ServiceNotFoundException {
         mContext = context;
+        mLockPatternUtils = new LockPatternUtils(context);
         mWM = WindowManagerGlobal.getWindowManagerService();
         mAm = ActivityManager.getService();
         mTrustManager = ITrustManager.Stub.asInterface(
@@ -785,7 +830,6 @@
             return false;
         }
 
-        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
         int userId = mContext.getUserId();
         if (isDeviceSecure(userId)) {
             Log.e(TAG, "Password already set, rejecting call to setLock");
@@ -799,7 +843,7 @@
         try {
             LockscreenCredential credential = createLockscreenCredential(
                     lockType, password);
-            success = lockPatternUtils.setLockCredential(
+            success = mLockPatternUtils.setLockCredential(
                     credential,
                     /* savedPassword= */ LockscreenCredential.createNone(),
                     userId);
@@ -813,6 +857,150 @@
     }
 
     /**
+     * Create a weak escrow token for the current user, which can later be used to unlock FBE
+     * or change user password.
+     *
+     * After adding, if the user currently  has a secure lockscreen, they will need to perform a
+     * confirm credential operation in order to activate the token for future use. If the user
+     * has no secure lockscreen, then the token is activated immediately.
+     *
+     * If the user changes or removes the lockscreen password, any activated weak escrow token will
+     * be removed.
+     *
+     * @return a unique 64-bit token handle which is needed to refer to this token later.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public long addWeakEscrowToken(@NonNull byte[] token, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WeakEscrowTokenActivatedListener listener) {
+        Objects.requireNonNull(token, "Token cannot be null.");
+        Objects.requireNonNull(user, "User cannot be null.");
+        Objects.requireNonNull(executor, "Executor cannot be null.");
+        Objects.requireNonNull(listener, "Listener cannot be null.");
+        int userId = user.getIdentifier();
+        IWeakEscrowTokenActivatedListener internalListener =
+                new IWeakEscrowTokenActivatedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenActivated(long handle, int userId) {
+                UserHandle user = UserHandle.of(userId);
+                final long restoreToken = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onWeakEscrowTokenActivated(handle, user));
+                } finally {
+                    Binder.restoreCallingIdentity(restoreToken);
+                }
+                Log.i(TAG, "Weak escrow token activated.");
+            }
+        };
+        return mLockPatternUtils.addWeakEscrowToken(token, userId, internalListener);
+    }
+
+    /**
+     * Remove a weak escrow token.
+     *
+     * @return true if the given handle refers to a valid weak token previously returned from
+     * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean removeWeakEscrowToken(long handle, @NonNull UserHandle user) {
+        Objects.requireNonNull(user, "User cannot be null.");
+        return mLockPatternUtils.removeWeakEscrowToken(handle, user.getIdentifier());
+    }
+
+    /**
+     * Check if the given weak escrow token is active or not.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean isWeakEscrowTokenActive(long handle, @NonNull UserHandle user) {
+        Objects.requireNonNull(user, "User cannot be null.");
+        return mLockPatternUtils.isWeakEscrowTokenActive(handle, user.getIdentifier());
+    }
+
+    /**
+     * Check if the given weak escrow token is validate.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean isWeakEscrowTokenValid(long handle, @NonNull byte[] token,
+            @NonNull UserHandle user) {
+        Objects.requireNonNull(token, "Token cannot be null.");
+        Objects.requireNonNull(user, "User cannot be null.");
+        return mLockPatternUtils.isWeakEscrowTokenValid(handle, token, user.getIdentifier());
+    }
+
+    /**
+     * Register the given WeakEscrowTokenRemovedListener.
+     *
+     * @return true if the listener is registered successfully, return false otherwise.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean registerWeakEscrowTokenRemovedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WeakEscrowTokenRemovedListener listener) {
+        Objects.requireNonNull(listener, "Listener cannot be null.");
+        Objects.requireNonNull(executor, "Executor cannot be null.");
+        Preconditions.checkArgument(!mListeners.containsKey(listener),
+                "Listener already registered: %s", listener);
+        IWeakEscrowTokenRemovedListener internalListener =
+                new IWeakEscrowTokenRemovedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenRemoved(long handle, int userId) {
+                UserHandle user = UserHandle.of(userId);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onWeakEscrowTokenRemoved(handle, user));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        };
+        if (mLockPatternUtils.registerWeakEscrowTokenRemovedListener(internalListener)) {
+            mListeners.put(listener, internalListener);
+            return true;
+        } else {
+            Log.e(TAG, "Listener failed to register");
+            return false;
+        }
+    }
+
+    /**
+     * Unregister the given WeakEscrowTokenRemovedListener.
+     *
+     * @return true if the listener is unregistered successfully, return false otherwise.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            @NonNull WeakEscrowTokenRemovedListener listener) {
+        Objects.requireNonNull(listener, "Listener cannot be null.");
+        IWeakEscrowTokenRemovedListener internalListener = mListeners.get(listener);
+        Preconditions.checkArgument(internalListener != null, "Listener was not registered");
+        if (mLockPatternUtils.unregisterWeakEscrowTokenRemovedListener(internalListener)) {
+            mListeners.remove(listener);
+            return true;
+        } else {
+            Log.e(TAG, "Listener failed to unregister.");
+            return false;
+        }
+    }
+
+    /**
      * Set the lockscreen password to {@code newPassword} after validating the current password
      * against {@code currentPassword}.
      * <p>If no password is currently set, {@code currentPassword} should be set to {@code null}.
@@ -832,13 +1020,12 @@
     })
     public boolean setLock(@LockTypes int newLockType, @Nullable byte[] newPassword,
             @LockTypes int currentLockType, @Nullable byte[] currentPassword) {
-        final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
         final int userId = mContext.getUserId();
         LockscreenCredential currentCredential = createLockscreenCredential(
                 currentLockType, currentPassword);
         LockscreenCredential newCredential = createLockscreenCredential(
                 newLockType, newPassword);
-        return lockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
+        return mLockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
     }
 
     /**
@@ -857,10 +1044,9 @@
             Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
     })
     public boolean checkLock(@LockTypes int lockType, @Nullable byte[] password) {
-        final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
         final LockscreenCredential credential = createLockscreenCredential(
                 lockType, password);
-        final VerifyCredentialResponse response = lockPatternUtils.verifyCredential(
+        final VerifyCredentialResponse response = mLockPatternUtils.verifyCredential(
                 credential, mContext.getUserId(), /* flags= */ 0);
         if (response == null) {
             return false;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 77af474..4e32e9a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1341,15 +1341,43 @@
         return mResources;
     }
 
+    /**
+     * Used to investigate "duplicate app objects" bug (b/185177290).
+     * makeApplication() should only be called on the main thread, so no synchronization should
+     * be needed, but syncing anyway just in case.
+     */
+    @GuardedBy("sApplicationCache")
+    private static final ArrayMap<String, Application> sApplicationCache = new ArrayMap<>(4);
+
     @UnsupportedAppUsage
     public Application makeApplication(boolean forceDefaultAppClass,
             Instrumentation instrumentation) {
         if (mApplication != null) {
             return mApplication;
         }
-
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
 
+        // For b/185177290.
+        final boolean wrongUser =
+                UserHandle.myUserId() != UserHandle.getUserId(mApplicationInfo.uid);
+        if (wrongUser) {
+            Slog.wtf(TAG, "makeApplication called with wrong appinfo UID: myUserId="
+                    + UserHandle.myUserId() + " appinfo.uid=" + mApplicationInfo.uid);
+        }
+        synchronized (sApplicationCache) {
+            final Application cached = sApplicationCache.get(mPackageName);
+            if (cached != null) {
+                // Looks like this is always happening for the system server, because
+                // the LoadedApk created in systemMain() -> attach() isn't cached properly?
+                if (!"android".equals(mPackageName)) {
+                    Slog.wtf(TAG, "App instance already created for package=" + mPackageName
+                            + " instance=" + cached);
+                }
+                // TODO Return the cached one, unles it's for the wrong user?
+                // For now, we just add WTF checks.
+            }
+        }
+
         Application app = null;
 
         final String myProcessName = Process.myProcessName();
@@ -1397,6 +1425,9 @@
         }
         mActivityThread.mAllApplications.add(app);
         mApplication = app;
+        synchronized (sApplicationCache) {
+            sApplicationCache.put(mPackageName, app);
+        }
 
         if (instrumentation != null) {
             try {
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
new file mode 100644
index 0000000..436eac3
--- /dev/null
+++ b/core/java/android/app/LocaleConfig.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.LocaleList;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The LocaleConfig of an application.
+ * Defined in an XML resource file with an {@code <locale-config>} element and
+ * referenced in the manifest via {@code android:localeConfig} on
+ * {@code <application>}.
+ *
+ * For more information, see TODO(b/214154050): add link to guide
+ *
+ * @attr ref android.R.styleable#LocaleConfig_Locale_name
+ * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig
+ */
+public class LocaleConfig {
+
+    private static final String TAG = "LocaleConfig";
+    public static final String TAG_LOCALE_CONFIG = "locale-config";
+    public static final String TAG_LOCALE = "locale";
+    private LocaleList mLocales;
+    private int mStatus;
+
+    /**
+     * succeeded reading the LocaleConfig structure stored in an XML file.
+     */
+    public static final int STATUS_SUCCESS = 0;
+    /**
+     * No android:localeConfig tag on <application>.
+     */
+    public static final int STATUS_NOT_SPECIFIED = 1;
+    /**
+     * Malformed input in the XML file where the LocaleConfig was stored.
+     */
+    public static final int STATUS_PARSING_FAILED = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_SUCCESS,
+            STATUS_NOT_SPECIFIED,
+            STATUS_PARSING_FAILED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Status{}
+
+    /**
+     * Returns the LocaleConfig for the provided application context
+     *
+     * @param context the context of the application
+     *
+     * @see Context#createPackageContext(String, int).
+     */
+    public LocaleConfig(@NonNull Context context) {
+        int resId = 0;
+        Resources res = context.getResources();
+        try {
+            //Get the resource id
+            resId = new ApplicationInfo(context.getApplicationInfo()).getLocaleConfigRes();
+            //Get the parser to read XML data
+            XmlResourceParser parser = res.getXml(resId);
+            parseLocaleConfig(parser, res);
+        } catch (Resources.NotFoundException e) {
+            Slog.w(TAG, "The resource file pointed to by the given resource ID isn't found.", e);
+            mStatus = STATUS_NOT_SPECIFIED;
+        } catch (XmlPullParserException | IOException e) {
+            Slog.w(TAG, "Failed to parse XML configuration from "
+                    + res.getResourceEntryName(resId), e);
+            mStatus = STATUS_PARSING_FAILED;
+        }
+    }
+
+    /**
+     * Parse the XML content and get the locales supported by the application
+     */
+    private void parseLocaleConfig(XmlResourceParser parser, Resources res)
+            throws IOException, XmlPullParserException {
+        XmlUtils.beginDocument(parser, TAG_LOCALE_CONFIG);
+        int outerDepth = parser.getDepth();
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        Set<String> localeNames = new HashSet<String>();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (TAG_LOCALE.equals(parser.getName())) {
+                final TypedArray attributes = res.obtainAttributes(
+                        attrs, com.android.internal.R.styleable.LocaleConfig_Locale);
+                String nameAttr = attributes.getString(
+                        com.android.internal.R.styleable.LocaleConfig_Locale_name);
+                localeNames.add(nameAttr);
+                attributes.recycle();
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        mStatus = STATUS_SUCCESS;
+        mLocales = LocaleList.forLanguageTags(String.join(",", localeNames));
+    }
+
+    /**
+     * Returns the locales supported by the specified application.
+     *
+     * <p><b>Note:</b> The locale format should follow the
+     * <a href="https://www.rfc-editor.org/rfc/bcp/bcp47.txt">IETF BCP47 regular expression</a>
+     *
+     * @return the {@link LocaleList}
+     */
+    public @Nullable LocaleList getSupportedLocales() {
+        return mLocales;
+    }
+
+    /**
+     * Get the status of reading the resource file where the LocaleConfig was stored.
+     *
+     * <p>Distinguish "the application didn't provide the resource file" from "the application
+     * provided malformed input" if {@link #getSupportedLocales()} returns {@code null}.
+     *
+     * @return {@code STATUS_SUCCESS} if the LocaleConfig structure existed in an XML file was
+     * successfully read, or {@code STATUS_NOT_SPECIFIED} if no android:localeConfig tag on
+     * <application> pointing to an XML file that stores the LocaleConfig, or
+     * {@code STATUS_PARSING_FAILED} if the application provided malformed input for the
+     * LocaleConfig structure.
+     *
+     * @see #STATUS_SUCCESS
+     * @see #STATUS_NOT_SPECIFIED
+     * @see #STATUS_PARSING_FAILED
+     *
+     */
+    public @Status int getStatus() {
+        return mStatus;
+    }
+}
diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java
index 2aa7c8e..522dc84 100644
--- a/core/java/android/app/LocaleManager.java
+++ b/core/java/android/app/LocaleManager.java
@@ -55,6 +55,9 @@
      *
      * <p>Pass a {@link LocaleList#getEmptyLocaleList()} to reset to the system locale.
      *
+     * <p><b>Note:</b> The set locales are persisted; they are backed up if the user has enabled
+     * Backup & Restore.
+     *
      * @param locales the desired locales for the calling app.
      */
     @UserHandleAware
@@ -67,6 +70,9 @@
      *
      * <p>Pass a {@link LocaleList#getEmptyLocaleList()} to reset to the system locale.
      *
+     * <p><b>Note:</b> The set locales are persisted; they are backed up if the user has enabled
+     * Backup & Restore.
+     *
      * @param appPackageName the package name of the app for which to set the locales.
      * @param locales the desired locales for the specified app.
      * @hide
@@ -95,18 +101,20 @@
     }
 
     /**
-     * Returns the current UI locales for a specific app (described by package name).
+     * Returns the current UI locales for a specified app (described by package name).
      *
      * <p>Returns a {@link LocaleList#getEmptyLocaleList()} if no app-specific locales are set.
      *
-     * <b>Note:</b> Non-system apps should read Locale information via their in-process
-     * LocaleLists.
+     * <p>This API can be used by an app's installer
+     * (per {@link android.content.pm.InstallSourceInfo#getInstallingPackageName}) to retrieve
+     * the app's locales.
+     * All other cases require {@code android.Manifest.permission#READ_APP_SPECIFIC_LOCALES}.
+     * Apps should generally retrieve their own locales via their in-process LocaleLists,
+     * or by calling {@link #getApplicationLocales()}.
      *
      * @param appPackageName the package name of the app for which to retrieve the locales.
-     * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_APP_SPECIFIC_LOCALES)
+    @RequiresPermission(value = Manifest.permission.READ_APP_SPECIFIC_LOCALES, conditional = true)
     @UserHandleAware
     @NonNull
     public LocaleList getApplicationLocales(@NonNull String appPackageName) {
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index ef4d7b1..ec8d989 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.text.TextUtils;
@@ -29,7 +30,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastPrintWriter;
 
-import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -355,11 +355,12 @@
     private final int mMaxEntries;
 
     /**
-     * Make a new property invalidated cache.
+     * Make a new property invalidated cache.  This constructor names the cache after the
+     * property name.  New clients should prefer the constructor that takes an explicit
+     * cache name.
      *
      * @param maxEntries Maximum number of entries to cache; LRU discard
-     * @param propertyName Name of the system property holding the cache invalidation nonce
-     * Defaults the cache name to the property name.
+     * @param propertyName Name of the system property holding the cache invalidation nonce.
      */
     public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
         this(maxEntries, propertyName, propertyName);
@@ -418,7 +419,7 @@
      * Enable or disable testing.  The testing property map is cleared every time this
      * method is called.
      */
-    @VisibleForTesting
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static void setTestMode(boolean mode) {
         sTesting = mode;
         synchronized (sTestingPropertyMap) {
@@ -426,12 +427,12 @@
         }
     }
 
-  /**
-   * Enable testing the specific cache key.  Only keys in the map are subject to testing.
-   * There is no method to stop testing a property name.  Just disable the test mode.
-   */
-    @VisibleForTesting
-    public static void testPropertyName(String name) {
+    /**
+     * Enable testing the specific cache key.  Only keys in the map are subject to testing.
+     * There is no method to stop testing a property name.  Just disable the test mode.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static void testPropertyName(@NonNull String name) {
         synchronized (sTestingPropertyMap) {
             sTestingPropertyMap.put(name, (long) NONCE_UNSET);
         }
@@ -505,21 +506,23 @@
      * block. If this function returns null, the result of the cache query is null. There is no
      * "negative cache" in the query: we don't cache null results at all.
      */
-    protected abstract Result recompute(Query query);
+    public abstract @NonNull Result recompute(@NonNull Query query);
 
     /**
      * Return true if the query should bypass the cache.  The default behavior is to
      * always use the cache but the method can be overridden for a specific class.
      */
-    protected boolean bypass(Query query) {
+    public boolean bypass(@NonNull Query query) {
         return false;
     }
 
     /**
-     * Determines if a pair of responses are considered equal. Used to determine whether
-     * a cache is inadvertently returning stale results when VERIFY is set to true.
+     * Determines if a pair of responses are considered equal. Used to determine whether a
+     * cache is inadvertently returning stale results when VERIFY is set to true.  Some
+     * existing clients override this method, but it is now deprecated in favor of a valid
+     * equals() method on the Result class.
      */
-    protected boolean debugCompareQueryResults(Result cachedResult, Result fetchedResult) {
+    public boolean resultEquals(Result cachedResult, Result fetchedResult) {
         // If a service crashes and returns a null result, the cached value remains valid.
         if (fetchedResult != null) {
             return Objects.equals(cachedResult, fetchedResult);
@@ -544,8 +547,11 @@
     }
 
     /**
-     * Disable the use of this cache in this process.
+     * Disable the use of this cache in this process.  This method is using during
+     * testing.  To disable a cache in normal code, use disableLocal().
+     * @hide
      */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public final void disableInstance() {
         synchronized (mLock) {
             mDisabled = true;
@@ -558,7 +564,7 @@
      * using the key will be disabled now, and all future cache instances that use the key will be
      * disabled in their constructor.
      */
-    public static final void disableLocal(@NonNull String name) {
+    private static final void disableLocal(@NonNull String name) {
         synchronized (sCorkLock) {
             sDisabledKeys.add(name);
             for (PropertyInvalidatedCache cache : sCaches.keySet()) {
@@ -570,7 +576,21 @@
     }
 
     /**
+     * Stop disabling local caches with a particular name.  Any caches that are currently
+     * disabled remain disabled (the "disabled" setting is sticky).  However, new caches
+     * with this name will not be disabled.  It is not an error if the cache name is not
+     * found in the list of disabled caches.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public final void clearDisableLocal() {
+        synchronized (sCorkLock) {
+            sDisabledKeys.remove(mCacheName);
+        }
+    }
+
+    /**
      * Disable this cache in the current process, and all other caches that use the same
+     * name.  This does not affect caches that have a different name but use the same
      * property.
      */
     public final void disableLocal() {
@@ -580,6 +600,7 @@
     /**
      * Return whether the cache is disabled in this process.
      */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public final boolean isDisabledLocal() {
         return mDisabled || !sEnabled;
     }
@@ -587,7 +608,7 @@
     /**
      * Get a value from the cache or recompute it.
      */
-    public Result query(Query query) {
+    public @NonNull Result query(@NonNull Query query) {
         // Let access to mDisabled race: it's atomic anyway.
         long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
         if (bypass(query)) {
@@ -704,6 +725,7 @@
      * the cache objects to invalidate all of the cache objects becomes confusing and you should
      * just use the static version of this function.
      */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public final void disableSystemWide() {
         disableSystemWide(mPropertyName);
     }
@@ -714,7 +736,7 @@
      *
      * @param name Name of the cache-key property to invalidate
      */
-    public static void disableSystemWide(@NonNull String name) {
+    private static void disableSystemWide(@NonNull String name) {
         if (!sEnabled) {
             return;
         }
@@ -784,8 +806,8 @@
                     "invalidating cache [%s]: [%s] -> [%s]",
                     name, nonce, Long.toString(newValue)));
         }
-        // TODO(dancol): add an atomic compare and exchange property set operation to avoid a
-        // small race with concurrent disable here.
+        // There is a small race with concurrent disables here.  A compare-and-exchange
+        // property operation would be required to eliminate the race condition.
         setNonce(name, newValue);
         long invalidateCount = sInvalidates.getOrDefault(name, (long) 0);
         sInvalidates.put(name, ++invalidateCount);
@@ -990,11 +1012,15 @@
         }
     }
 
-    protected Result maybeCheckConsistency(Query query, Result proposedResult) {
+    /**
+     * Return the result generated by a given query to the cache, performing debugging checks when
+     * enabled.
+     */
+    private Result maybeCheckConsistency(Query query, Result proposedResult) {
         if (VERIFY) {
             Result resultToCompare = recompute(query);
             boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
-            if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
+            if (!nonceChanged && !resultEquals(proposedResult, resultToCompare)) {
                 Log.e(TAG, TextUtils.formatSimple(
                         "cache %s inconsistent for %s is %s should be %s",
                         cacheName(), queryToString(query),
@@ -1007,24 +1033,27 @@
     }
 
     /**
-     * Return the name of the cache, to be used in debug messages.  The
-     * method is public so clients can use it.
+     * Return the name of the cache, to be used in debug messages.
      */
-    public String cacheName() {
+    private final @NonNull String cacheName() {
         return mCacheName;
     }
 
     /**
-     * Return the query as a string, to be used in debug messages.  The
-     * method is public so clients can use it in external debug messages.
+     * Return the query as a string, to be used in debug messages.  New clients should not
+     * override this, but should instead add the necessary toString() method to the Query
+     * class.
      */
-    public String queryToString(Query query) {
+    protected @NonNull String queryToString(@NonNull Query query) {
         return Objects.toString(query);
     }
 
     /**
-     * Disable all caches in the local process.  Once disabled it is not
-     * possible to re-enable caching in the current process.
+     * Disable all caches in the local process.  This is primarily useful for testing when
+     * the test needs to bypass the cache or when the test is for a server, and the test
+     * process does not have privileges to write SystemProperties. Once disabled it is not
+     * possible to re-enable caching in the current process.  If a client wants to
+     * temporarily disable caching, use the corking mechanism.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static void disableForTestMode() {
@@ -1044,7 +1073,7 @@
     /**
      * Returns a list of caches alive at the current time.
      */
-    public static ArrayList<PropertyInvalidatedCache> getActiveCaches() {
+    private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() {
         synchronized (sCorkLock) {
             return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
         }
@@ -1053,7 +1082,7 @@
     /**
      * Returns a list of the active corks in a process.
      */
-    public static ArrayList<Map.Entry<String, Integer>> getActiveCorks() {
+    private static @NonNull ArrayList<Map.Entry<String, Integer>> getActiveCorks() {
         synchronized (sCorkLock) {
             return new ArrayList<Map.Entry<String, Integer>>(sCorks.entrySet());
         }
@@ -1104,14 +1133,14 @@
     }
 
     /**
-     * Dumps contents of every cache in the process to the provided FileDescriptor.
+     * Dumps the contents of every cache in the process to the provided ParcelFileDescriptor.
      */
-    public static void dumpCacheInfo(FileDescriptor fd, String[] args) {
+    public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
         ArrayList<PropertyInvalidatedCache> activeCaches;
         ArrayList<Map.Entry<String, Integer>> activeCorks;
 
         try  (
-            FileOutputStream fout = new FileOutputStream(fd);
+            FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
             PrintWriter pw = new FastPrintWriter(fout);
         ) {
             if (!sEnabled) {
@@ -1142,4 +1171,13 @@
             Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
         }
     }
+
+    /**
+     * Trim memory by clearing all the caches.
+     */
+    public static void onTrimMemory() {
+        for (PropertyInvalidatedCache pic : getActiveCaches()) {
+            pic.clear();
+        }
+    }
 }
diff --git a/core/java/android/app/ServiceStartNotAllowedException.java b/core/java/android/app/ServiceStartNotAllowedException.java
index 33285b2..b1f47ee 100644
--- a/core/java/android/app/ServiceStartNotAllowedException.java
+++ b/core/java/android/app/ServiceStartNotAllowedException.java
@@ -40,4 +40,11 @@
             return new BackgroundServiceStartNotAllowedException(message);
         }
     }
+
+    @Override
+    public synchronized Throwable getCause() {
+        // "Cause" is often used for clustering exceptions, and developers don't want to have it
+        // for this exception. b/210890426
+        return null;
+    }
 }
diff --git a/core/java/android/app/StackTrace.java b/core/java/android/app/StackTrace.java
index ec058f8..6a77abd 100644
--- a/core/java/android/app/StackTrace.java
+++ b/core/java/android/app/StackTrace.java
@@ -24,4 +24,8 @@
     public StackTrace(String message) {
         super(message);
     }
+
+    public StackTrace(String message, Throwable innerStackTrace) {
+        super(message, innerStackTrace);
+    }
 }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index ae578f5..64d3a9f 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -282,6 +282,33 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface RequestResult {}
 
+    /**
+     * Constant for {@link #setNavBarModeOverride(int)} indicating the default navbar mode.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0;
+
+    /**
+     * Constant for {@link #setNavBarModeOverride(int)} indicating kids navbar mode.
+     *
+     * <p>When used, back and home icons will change drawables and layout, recents will be hidden,
+     * and the navbar will remain visible when apps are in immersive mode.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1;
+
+    /** @hide */
+    @IntDef(prefix = {"NAV_BAR_MODE_OVERRIDE_"}, value = {
+            NAV_BAR_MODE_OVERRIDE_NONE,
+            NAV_BAR_MODE_OVERRIDE_KIDS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NavBarModeOverride {}
+
     @UnsupportedAppUsage
     private Context mContext;
     private IStatusBarService mService;
@@ -687,6 +714,52 @@
         }
     }
 
+    /**
+     * Sets or removes the navigation bar mode override.
+     *
+     * @param navBarModeOverride the mode of the navigation bar override to be set.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+    public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) {
+        if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE
+                && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) {
+            throw new UnsupportedOperationException(
+                    "Supplied navBarModeOverride not supported: " + navBarModeOverride);
+        }
+
+        try {
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                svc.setNavBarModeOverride(navBarModeOverride);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the navigation bar mode override. Returns default value if no override is set.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+    public @NavBarModeOverride int getNavBarModeOverride() {
+        int navBarModeOverride = NAV_BAR_MODE_OVERRIDE_NONE;
+        try {
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                navBarModeOverride = svc.getNavBarModeOverride();
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return navBarModeOverride;
+    }
+
     /** @hide */
     public static String windowStateToString(int state) {
         if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 85ddff9..7f8e46e 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -24,10 +24,10 @@
 import android.app.ContextImpl.ServiceInitializationState;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
+import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.IAmbientContextEventObserver;
 import android.app.appsearch.AppSearchManagerFrameworkInitializer;
 import android.app.blob.BlobStoreManagerFrameworkInitializer;
-import android.app.communal.CommunalManager;
-import android.app.communal.ICommunalManager;
 import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IContentSuggestionsManager;
 import android.app.job.JobSchedulerFrameworkInitializer;
@@ -51,7 +51,7 @@
 import android.app.usage.UsageStatsManager;
 import android.apphibernation.AppHibernationManager;
 import android.appwidget.AppWidgetManager;
-import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothFrameworkInitializer;
 import android.companion.CompanionDeviceManager;
 import android.companion.ICompanionDeviceManager;
 import android.companion.virtual.IVirtualDeviceManager;
@@ -126,10 +126,11 @@
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
 import android.media.tv.TvInputManager;
-import android.media.tv.interactive.ITvIAppManager;
-import android.media.tv.interactive.TvIAppManager;
+import android.media.tv.interactive.ITvInteractiveAppManager;
+import android.media.tv.interactive.TvInteractiveAppManager;
 import android.media.tv.tunerresourcemanager.ITunerResourceManager;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.nearby.NearbyFrameworkInitializer;
 import android.net.ConnectivityFrameworkInitializer;
 import android.net.ConnectivityFrameworkInitializerTiramisu;
 import android.net.EthernetManager;
@@ -347,13 +348,6 @@
                 return new MediaRouter(ctx);
             }});
 
-        registerService(Context.BLUETOOTH_SERVICE, BluetoothManager.class,
-                new CachedServiceFetcher<BluetoothManager>() {
-            @Override
-            public BluetoothManager createService(ContextImpl ctx) {
-                return new BluetoothManager(ctx);
-            }});
-
         registerService(Context.HDMI_CONTROL_SERVICE, HdmiControlManager.class,
                 new StaticServiceFetcher<HdmiControlManager>() {
             @Override
@@ -965,13 +959,16 @@
                     }
                 });
 
-        registerService(Context.TV_IAPP_SERVICE, TvIAppManager.class,
-                new CachedServiceFetcher<TvIAppManager>() {
+        registerService(Context.TV_INTERACTIVE_APP_SERVICE, TvInteractiveAppManager.class,
+                new CachedServiceFetcher<TvInteractiveAppManager>() {
             @Override
-            public TvIAppManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TV_IAPP_SERVICE);
-                ITvIAppManager service = ITvIAppManager.Stub.asInterface(iBinder);
-                return new TvIAppManager(service, ctx.getUserId());
+            public TvInteractiveAppManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                IBinder iBinder =
+                        ServiceManager.getServiceOrThrow(Context.TV_INTERACTIVE_APP_SERVICE);
+                ITvInteractiveAppManager service =
+                        ITvInteractiveAppManager.Stub.asInterface(iBinder);
+                return new TvInteractiveAppManager(service, ctx.getUserId());
             }});
 
         registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
@@ -1022,19 +1019,21 @@
             }});
 
         registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
-                new StaticServiceFetcher<PersistentDataBlockManager>() {
+                new CachedServiceFetcher<PersistentDataBlockManager>() {
             @Override
-            public PersistentDataBlockManager createService() throws ServiceNotFoundException {
+            public PersistentDataBlockManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                 IPersistentDataBlockService persistentDataBlockService =
                         IPersistentDataBlockService.Stub.asInterface(b);
                 if (persistentDataBlockService != null) {
-                    return new PersistentDataBlockManager(persistentDataBlockService);
+                    return new PersistentDataBlockManager(ctx, persistentDataBlockService);
                 } else {
                     // not supported
                     return null;
                 }
-            }});
+            }
+         });
 
         registerService(Context.OEM_LOCK_SERVICE, OemLockManager.class,
                 new StaticServiceFetcher<OemLockManager>() {
@@ -1519,20 +1518,17 @@
                     }
                 });
 
-        registerService(Context.COMMUNAL_SERVICE, CommunalManager.class,
-                new CachedServiceFetcher<CommunalManager>() {
+        registerService(Context.AMBIENT_CONTEXT_SERVICE, AmbientContextManager.class,
+                new CachedServiceFetcher<AmbientContextManager>() {
                     @Override
-                    public CommunalManager createService(ContextImpl ctx) {
-                        if (!ctx.getPackageManager().hasSystemFeature(
-                                PackageManager.FEATURE_COMMUNAL_MODE)) {
-                            return null;
-                        }
-                        IBinder iBinder =
-                                ServiceManager.getService(Context.COMMUNAL_SERVICE);
-                        return iBinder != null ? new CommunalManager(
-                                ICommunalManager.Stub.asInterface(iBinder)) : null;
-                    }
-                });
+                    public AmbientContextManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder iBinder = ServiceManager.getServiceOrThrow(
+                                Context.AMBIENT_CONTEXT_SERVICE);
+                        IAmbientContextEventObserver manager =
+                                IAmbientContextEventObserver.Stub.asInterface(iBinder);
+                        return new AmbientContextManager(ctx.getOuterContext(), manager);
+                    }});
 
         sInitializing = true;
         try {
@@ -1541,6 +1537,7 @@
             ConnectivityFrameworkInitializer.registerServiceWrappers();
             JobSchedulerFrameworkInitializer.registerServiceWrappers();
             BlobStoreManagerFrameworkInitializer.initialize();
+            BluetoothFrameworkInitializer.registerServiceWrappers();
             TelephonyFrameworkInitializer.registerServiceWrappers();
             AppSearchManagerFrameworkInitializer.initialize();
             WifiFrameworkInitializer.registerServiceWrappers();
@@ -1554,6 +1551,7 @@
             UwbFrameworkInitializer.registerServiceWrappers();
             SafetyCenterFrameworkInitializer.registerServiceWrappers();
             ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
+            NearbyFrameworkInitializer.registerServiceWrappers();
         } finally {
             // If any of the above code throws, we're in a pretty bad shape and the process
             // will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 58ded71..00903a8 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -22,6 +22,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.MagnificationConfig;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1581,7 +1582,7 @@
 
                 @Override
                 public void onMagnificationChanged(int displayId, @NonNull Region region,
-                        float scale, float centerX, float centerY) {
+                        MagnificationConfig config) {
                     /* do nothing */
                 }
 
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 973a8fb..73a9e5a 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -243,6 +243,45 @@
      */
     public static final int MODE_NIGHT_YES = 2;
 
+    /**
+     * Granular types for {@link MODE_NIGHT_CUSTOM_TYPE_BEDTIME}
+     * @hide
+     */
+    @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = {
+            MODE_NIGHT_CUSTOM_TYPE_UNKNOWN,
+            MODE_NIGHT_CUSTOM_TYPE_SCHEDULE,
+            MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NightModeCustomType {}
+
+    /**
+     * A granular type for {@link #MODE_NIGHT_CUSTOM} which is unknown.
+     * <p>
+     * This is the default value when the night mode is set to value other than
+     * {@link #MODE_NIGHT_CUSTOM}.
+     * @hide
+     */
+    @SystemApi
+    public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1;
+
+    /**
+     * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on a custom schedule.
+     * <p>
+     * This is the default value when night mode is set to {@link #MODE_NIGHT_CUSTOM} unless the
+     * the night mode custom type is specified by calling {@link #setNightModeCustomType(int)}.
+     * @hide
+     */
+    @SystemApi
+    public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0;
+
+    /**
+     * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on the bedtime schedule.
+     * @hide
+     */
+    @SystemApi
+    public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1;
+
     private IUiModeManager mService;
 
     /**
@@ -496,6 +535,45 @@
     }
 
     /**
+     * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type
+     * {@code nightModeCustomType}.
+     *
+     * @param nightModeCustomType
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+    public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) {
+        if (mService != null) {
+            try {
+                mService.setNightModeCustomType(nightModeCustomType);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the custom night mode type.
+     * <p>
+     * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns
+     * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+    public int getNightModeCustomType() {
+        if (mService != null) {
+            try {
+                return mService.getNightModeCustomType();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
+    }
+
+    /**
      * Sets and persist the night mode for this application.
      * <p>
      * The mode can be one of:
@@ -599,11 +677,36 @@
     }
 
     /**
+     * [De]activating night mode for the current user if the current night mode is custom and the
+     * custom type matches {@code nightModeCustomType}.
+     *
+     * @param nightModeCustomType the specify type of custom mode
+     * @param active {@code true} to activate night mode. Otherwise, deactivate night mode
+     * @return {@code true} if night mode has successfully activated for the requested
+     *         {@code nightModeCustomType}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+    public boolean setNightModeActivatedForCustomMode(@NightModeCustomType int nightModeCustomType,
+            boolean active) {
+        if (mService != null) {
+            try {
+                return mService.setNightModeActivatedForCustomMode(nightModeCustomType, active);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Activating night mode for the current user
      *
      * @return {@code true} if the change is successful
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
     public boolean setNightModeActivated(boolean active) {
         if (mService != null) {
             try {
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 7ef0a19..067a4c3 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +28,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Size;
 
 import com.android.internal.graphics.ColorUtils;
@@ -44,6 +46,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -173,6 +176,22 @@
         if (bitmap == null) {
             throw new IllegalArgumentException("Bitmap can't be null");
         }
+        return fromBitmap(bitmap, 0f /* dimAmount */);
+    }
+
+    /**
+     * Constructs {@link WallpaperColors} from a bitmap with dimming applied.
+     * <p>
+     * Main colors will be extracted from the bitmap with dimming taken into account when
+     * calculating dark hints.
+     *
+     * @param bitmap Source where to extract from.
+     * @param dimAmount Wallpaper dim amount
+     * @hide
+     */
+    public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap,
+            @FloatRange (from = 0f, to = 1f) float dimAmount) {
+        Objects.requireNonNull(bitmap, "Bitmap can't be null");
 
         final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
         boolean shouldRecycle = false;
@@ -211,7 +230,7 @@
 
         }
 
-        int hints = calculateDarkHints(bitmap);
+        int hints = calculateDarkHints(bitmap, dimAmount);
 
         if (shouldRecycle) {
             bitmap.recycle();
@@ -507,13 +526,15 @@
      * Checks if image is bright and clean enough to support light text.
      *
      * @param source What to read.
+     * @param dimAmount How much wallpaper dim amount was applied.
      * @return Whether image supports dark text or not.
      */
-    private static int calculateDarkHints(Bitmap source) {
+    private static int calculateDarkHints(Bitmap source, float dimAmount) {
         if (source == null) {
             return 0;
         }
 
+        dimAmount = MathUtils.saturate(dimAmount);
         int[] pixels = new int[source.getWidth() * source.getHeight()];
         double totalLuminance = 0;
         final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
@@ -521,24 +542,37 @@
         source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
                 source.getWidth(), source.getHeight());
 
+        // Create a new black layer with dimAmount as the alpha to be accounted for when computing
+        // the luminance.
+        int dimmingLayerAlpha = (int) (255 * dimAmount);
+        int blackTransparent = ColorUtils.setAlphaComponent(Color.BLACK, dimmingLayerAlpha);
+
         // This bitmap was already resized to fit the maximum allowed area.
         // Let's just loop through the pixels, no sweat!
         float[] tmpHsl = new float[3];
         for (int i = 0; i < pixels.length; i++) {
-            ColorUtils.colorToHSL(pixels[i], tmpHsl);
-            final float luminance = tmpHsl[2];
-            final int alpha = Color.alpha(pixels[i]);
+            int pixelColor = pixels[i];
+            ColorUtils.colorToHSL(pixelColor, tmpHsl);
+            final int alpha = Color.alpha(pixelColor);
+
+            // Apply composite colors where the foreground is a black layer with an alpha value of
+            // the dim amount and the background is the wallpaper pixel color.
+            int compositeColors = ColorUtils.compositeColors(blackTransparent, pixelColor);
+
+            // Calculate the adjusted luminance of the dimmed wallpaper pixel color.
+            double adjustedLuminance = ColorUtils.calculateLuminance(compositeColors);
+
             // Make sure we don't have a dark pixel mass that will
             // make text illegible.
             final boolean satisfiesTextContrast = ContrastColorUtil
-                    .calculateContrast(pixels[i], Color.BLACK) > DARK_PIXEL_CONTRAST;
+                    .calculateContrast(pixelColor, Color.BLACK) > DARK_PIXEL_CONTRAST;
             if (!satisfiesTextContrast && alpha != 0) {
                 darkPixels++;
                 if (DEBUG_DARK_PIXELS) {
                     pixels[i] = Color.RED;
                 }
             }
-            totalLuminance += luminance;
+            totalLuminance += adjustedLuminance;
         }
 
         int hints = 0;
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 772492d..0a18588 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -71,6 +72,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Pair;
 import android.view.Display;
 import android.view.WindowManagerGlobal;
@@ -1489,27 +1491,18 @@
                     mContext.getUserId());
             if (fd != null) {
                 FileOutputStream fos = null;
-                final Bitmap tmp = BitmapFactory.decodeStream(resources.openRawResource(resid));
+                boolean ok = false;
                 try {
-                    // If the stream can't be decoded, treat it as an invalid input.
-                    if (tmp != null) {
-                        fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
-                        tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
-                        // The 'close()' is the trigger for any server-side image manipulation,
-                        // so we must do that before waiting for completion.
-                        fos.close();
-                        completion.waitForCompletion();
-                    } else {
-                        throw new IllegalArgumentException(
-                                "Resource 0x" + Integer.toHexString(resid) + " is invalid");
-                    }
+                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+                    copyStreamToWallpaperFile(resources.openRawResource(resid), fos);
+                    // The 'close()' is the trigger for any server-side image manipulation,
+                    // so we must do that before waiting for completion.
+                    fos.close();
+                    completion.waitForCompletion();
                 } finally {
                     // Might be redundant but completion shouldn't wait unless the write
                     // succeeded; this is a fallback if it threw past the close+wait.
                     IoUtils.closeQuietly(fos);
-                    if (tmp != null) {
-                        tmp.recycle();
-                    }
                 }
             }
         } catch (RemoteException e) {
@@ -1751,22 +1744,13 @@
                     result, which, completion, mContext.getUserId());
             if (fd != null) {
                 FileOutputStream fos = null;
-                final Bitmap tmp = BitmapFactory.decodeStream(bitmapData);
                 try {
-                    // If the stream can't be decoded, treat it as an invalid input.
-                    if (tmp != null) {
-                        fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
-                        tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
-                        fos.close();
-                        completion.waitForCompletion();
-                    } else {
-                        throw new IllegalArgumentException("InputStream is invalid");
-                    }
+                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+                    copyStreamToWallpaperFile(bitmapData, fos);
+                    fos.close();
+                    completion.waitForCompletion();
                 } finally {
                     IoUtils.closeQuietly(fos);
-                    if (tmp != null) {
-                        tmp.recycle();
-                    }
                 }
             }
         } catch (RemoteException e) {
@@ -2012,6 +1996,63 @@
     }
 
     /**
+     * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
+     * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
+    public void setWallpaperDimAmount(@FloatRange (from = 0f, to = 1f) float dimAmount) {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            throw new RuntimeException(new DeadSystemException());
+        }
+        try {
+            sGlobals.mService.setWallpaperDimAmount(MathUtils.saturate(dimAmount));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the current additional dim amount set on the wallpaper. 0f means no application has
+     * added any dimming on top of the system default dim amount.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
+    public float getWallpaperDimAmount() {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            throw new RuntimeException(new DeadSystemException());
+        }
+        try {
+            return sGlobals.mService.getWallpaperDimAmount();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Whether the lock screen wallpaper is different from the system wallpaper.
+     *
+     * @hide
+     */
+    public boolean lockScreenWallpaperExists() {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            throw new RuntimeException(new DeadSystemException());
+        }
+        try {
+            return sGlobals.mService.lockScreenWallpaperExists();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Set the live wallpaper.
      *
      * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
diff --git a/core/java/android/app/WtfException.java b/core/java/android/app/WtfException.java
deleted file mode 100644
index ba8dbef..0000000
--- a/core/java/android/app/WtfException.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Exception meant to be thrown instead of calling Log.wtf() such that server side code can
- * throw this exception, and it will carry across the binder to do client side logging.
- * {@hide}
- */
-public final class WtfException extends RuntimeException implements Parcelable {
-    public static final @android.annotation.NonNull
-            Creator<WtfException> CREATOR = new Creator<WtfException>() {
-                @Override
-                public WtfException createFromParcel(Parcel source) {
-                    return new WtfException(source.readString8());
-                }
-
-                @Override
-                public WtfException[] newArray(int size) {
-                    return new WtfException[size];
-                }
-            };
-
-    public WtfException(@android.annotation.NonNull String message) {
-        super(message);
-    }
-
-    /** {@hide} */
-    public static Throwable readFromParcel(Parcel in) {
-        final String msg = in.readString8();
-        return new WtfException(msg);
-    }
-
-    /** {@hide} */
-    public static void writeToParcel(Parcel out, Throwable t) {
-        out.writeString8(t.getMessage());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
-        dest.writeString8(getMessage());
-    }
-}
-
diff --git a/core/java/android/app/admin/DevicePolicyDrawableResource.aidl b/core/java/android/app/admin/DevicePolicyDrawableResource.aidl
new file mode 100644
index 0000000..6b73d981
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyDrawableResource.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable DevicePolicyDrawableResource;
diff --git a/core/java/android/app/admin/DevicePolicyDrawableResource.java b/core/java/android/app/admin/DevicePolicyDrawableResource.java
new file mode 100644
index 0000000..d32ff84
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyDrawableResource.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Used to pass in the required information for updating an enterprise drawable resource using
+ * {@link DevicePolicyManager#setDrawables}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DevicePolicyDrawableResource implements Parcelable {
+    private final @DevicePolicyResources.UpdatableDrawableId int mDrawableId;
+    private final @DevicePolicyResources.UpdatableDrawableStyle int mDrawableStyle;
+    private final @DevicePolicyResources.UpdatableDrawableSource int mDrawableSource;
+    private final @DrawableRes int mCallingPackageResourceId;
+    @NonNull private ParcelableResource mResource;
+
+    /**
+     * Creates an object containing the required information for updating an enterprise drawable
+     * resource using {@link DevicePolicyManager#setDrawables}.
+     *
+     * <p>It will be used to update the drawable defined by {@code drawableId} with style
+     * {@code drawableStyle} located in source {@code drawableSource} to the drawable with ID
+     * {@code callingPackageResourceId} in the calling package</p>
+     *
+     * @param drawableId The ID of the drawable to update.
+     * @param drawableStyle The style of the drawable to update.
+     * @param drawableSource The source of the drawable to update.
+     * @param callingPackageResourceId The ID of the drawable resource in the calling package to
+     *        use as an updated resource.
+     *
+     * @throws IllegalStateException if the resource with ID
+     * {@code callingPackageResourceId} doesn't exist in the {@code context} package.
+     */
+    public DevicePolicyDrawableResource(
+            @NonNull Context context,
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @DrawableRes int callingPackageResourceId) {
+        this(drawableId, drawableStyle, drawableSource, callingPackageResourceId,
+                new ParcelableResource(context, callingPackageResourceId,
+                        ParcelableResource.RESOURCE_TYPE_DRAWABLE));
+    }
+
+    private DevicePolicyDrawableResource(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @DrawableRes int callingPackageResourceId,
+            @NonNull ParcelableResource resource) {
+        this.mDrawableId = drawableId;
+        this.mDrawableStyle = drawableStyle;
+        this.mDrawableSource = drawableSource;
+        this.mCallingPackageResourceId = callingPackageResourceId;
+        this.mResource = resource;
+    }
+
+    /**
+     * Creates an object containing the required information for updating an enterprise drawable
+     * resource using {@link DevicePolicyManager#setDrawables}.
+     * <p>It will be used to update the drawable defined by {@code drawableId} with style
+     * {@code drawableStyle} to the drawable with ID {@code callingPackageResourceId} in the
+     * calling package</p>
+     *
+     * @param drawableId The ID of the drawable to update.
+     * @param drawableStyle The style of the drawable to update.
+     * @param callingPackageResourceId The ID of the drawable resource in the calling package to
+     *        use as an updated resource.
+     *
+     * @throws IllegalStateException if the resource with ID
+     * {@code callingPackageResourceId} doesn't exist in the calling package.
+     */
+    public DevicePolicyDrawableResource(
+            @NonNull Context context,
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DrawableRes int callingPackageResourceId) {
+       this(context, drawableId, drawableStyle, DevicePolicyResources.Drawable.Source.UNDEFINED,
+               callingPackageResourceId);
+    }
+
+    /**
+     * Returns the ID of the drawable to update.
+     */
+    @DevicePolicyResources.UpdatableDrawableId
+    public int getDrawableId() {
+        return mDrawableId;
+    }
+
+    /**
+     * Returns the style of the drawable to update
+     */
+    @DevicePolicyResources.UpdatableDrawableStyle
+    public int getDrawableStyle() {
+        return mDrawableStyle;
+    }
+
+    /**
+     * Returns the source of the drawable to update.
+     */
+    @DevicePolicyResources.UpdatableDrawableSource
+    public int getDrawableSource() {
+        return mDrawableSource;
+    }
+
+    /**
+     * Returns the ID of the drawable resource in the calling package to use as an updated
+     * resource.
+     */
+    @DrawableRes
+    public int getCallingPackageResourceId() {
+        return mCallingPackageResourceId;
+    }
+
+    /**
+     * Returns the {@link ParcelableResource} of the drawable.
+     *
+     * @hide
+     */
+    @NonNull
+    public ParcelableResource getResource() {
+        return mResource;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DevicePolicyDrawableResource other = (DevicePolicyDrawableResource) o;
+        return mDrawableId == other.mDrawableId
+                && mDrawableStyle == other.mDrawableStyle
+                && mDrawableSource == other.mDrawableSource
+                && mCallingPackageResourceId == other.mCallingPackageResourceId
+                && mResource.equals(other.mResource);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mDrawableId, mDrawableStyle, mDrawableSource, mCallingPackageResourceId, mResource);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDrawableId);
+        dest.writeInt(mDrawableStyle);
+        dest.writeInt(mDrawableSource);
+        dest.writeInt(mCallingPackageResourceId);
+        dest.writeTypedObject(mResource, flags);
+    }
+
+    public static final @NonNull Creator<DevicePolicyDrawableResource> CREATOR =
+            new Creator<DevicePolicyDrawableResource>() {
+                @Override
+                public DevicePolicyDrawableResource createFromParcel(Parcel in) {
+                    int drawableId = in.readInt();
+                    int drawableStyle = in.readInt();
+                    int drawableSource = in.readInt();
+                    int callingPackageResourceId = in.readInt();
+                    ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR);
+
+                    return new DevicePolicyDrawableResource(
+                            drawableId, drawableStyle, drawableSource, callingPackageResourceId,
+                            resource);
+                }
+
+                @Override
+                public DevicePolicyDrawableResource[] newArray(int size) {
+                    return new DevicePolicyDrawableResource[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7969cda..fa0af2d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,9 @@
 
 package android.app.admin;
 
+import static android.app.admin.DevicePolicyResources.Drawable.INVALID_ID;
+import static android.app.admin.DevicePolicyResources.Drawable.Source.UNDEFINED;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.Manifest.permission;
@@ -52,7 +55,9 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
 import android.net.PrivateDnsConnectivityChecker;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -89,6 +94,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.DebugUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 
@@ -123,6 +129,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -480,6 +487,10 @@
      *
      * <p>Device management role holders are required to have a handler for this intent action.
      *
+     * <p>If {@link #EXTRA_ROLE_HOLDER_STATE} is supplied to this intent, it is the responsibility
+     * of the role holder to restore its state from this extra. This is the same {@link Bundle}
+     * which the role holder returns alongside {@link #RESULT_UPDATE_ROLE_HOLDER}.
+     *
      * <p>A result code of {@link Activity#RESULT_OK} implies that managed profile provisioning
      * finished successfully. If it did not, a result code of {@link Activity#RESULT_CANCELED}
      * is used instead.
@@ -488,8 +499,7 @@
      *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE =
@@ -528,6 +538,10 @@
      *
      * <p>Device management role holders are required to have a handler for this intent action.
      *
+     * <p>If {@link #EXTRA_ROLE_HOLDER_STATE} is supplied to this intent, it is the responsibility
+     * of the role holder to restore its state from this extra. This is the same {@link Bundle}
+     * which the role holder returns alongside {@link #RESULT_UPDATE_ROLE_HOLDER}.
+     *
      * <p>The result codes can be either {@link #RESULT_WORK_PROFILE_CREATED}, {@link
      * #RESULT_DEVICE_OWNER_SET} or {@link Activity#RESULT_CANCELED} if provisioning failed.
      *
@@ -535,8 +549,7 @@
      *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE =
@@ -561,14 +574,80 @@
      *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION =
             "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
 
     /**
+     * {@link Activity} result code which can be returned by {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} and {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} to signal that an update
+     * to the role holder is required.
+     *
+     * <p>This result code must be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_UPDATE_ROLE_HOLDER = 2;
+
+    /**
+     * A {@link Bundle} extra which describes the state of the role holder at the time when it
+     * returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
+     *
+     * <p>After the update completes, the role holder's {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent will be relaunched,
+     * which will contain this extra. It is the role holder's responsibility to restore its
+     * state from this extra.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ROLE_HOLDER_STATE = "android.app.extra.ROLE_HOLDER_STATE";
+
+    /**
+     * A {@code boolean} extra which determines whether to force a role holder update, regardless
+     * of any internal conditions {@link #ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER} might have.
+     *
+     * <p>This extra can be provided to intents with action {@link
+     * #ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER =
+            "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
+
+    /**
+     * A boolean extra indicating whether offline provisioning is allowed.
+     *
+     * <p>For the online provisioning flow, there will be an attempt to download and install
+     * the latest version of the device management role holder. The platform will then delegate
+     * provisioning to the device management role holder via role holder-specific provisioning
+     * actions.
+     *
+     * <p>For the offline provisioning flow, the provisioning flow will always be handled by
+     * the platform.
+     *
+     * <p>If this extra is set to {@code false}, the provisioning flow will enforce that an
+     * internet connection is established, which will start the online provisioning flow. If an
+     * internet connection cannot be established, provisioning will fail.
+     *
+     * <p>If this extra is set to {@code true}, the provisioning flow will still try to connect to
+     * the internet, but if it fails it will start the offline provisioning flow.
+     *
+     * <p>The default value is {@code false}.
+     *
+     * <p>This extra is respected when provided via the provisioning intent actions such as {@link
+     * #ACTION_PROVISION_MANAGED_PROFILE}.
+     */
+    public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE =
+            "android.app.extra.PROVISIONING_ALLOW_OFFLINE";
+
+    /**
      * Action: Bugreport sharing with device owner has been accepted by the user.
      *
      * @hide
@@ -1499,6 +1578,78 @@
     public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 1 << 2;
 
     /**
+     * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+     * {@link #setMinimumRequiredWifiSecurityLevel(int)}: no minimum security level.
+     *
+     * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+     * represents the current minimum security level required.
+     * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+     * minimum security level a Wi-Fi network must meet.
+     *
+     * @see #WIFI_SECURITY_PERSONAL
+     * @see #WIFI_SECURITY_ENTERPRISE_EAP
+     * @see #WIFI_SECURITY_ENTERPRISE_192
+     */
+    public static final int WIFI_SECURITY_OPEN = 0;
+
+    /**
+     * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+     * {@link #setMinimumRequiredWifiSecurityLevel(int)}: personal network such as WEP, WPA2-PSK.
+     *
+     * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+     * represents the current minimum security level required.
+     * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+     * minimum security level a Wi-Fi network must meet.
+     *
+     * @see #WIFI_SECURITY_OPEN
+     * @see #WIFI_SECURITY_ENTERPRISE_EAP
+     * @see #WIFI_SECURITY_ENTERPRISE_192
+     */
+    public static final int WIFI_SECURITY_PERSONAL = 1;
+
+    /**
+     * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+     * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise EAP network.
+     *
+     * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+     * represents the current minimum security level required.
+     * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+     * minimum security level a Wi-Fi network must meet.
+     *
+     * @see #WIFI_SECURITY_OPEN
+     * @see #WIFI_SECURITY_PERSONAL
+     * @see #WIFI_SECURITY_ENTERPRISE_192
+     */
+    public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2;
+
+    /**
+     * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+     * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise 192 bit network.
+     *
+     * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+     * represents the current minimum security level required.
+     * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+     * minimum security level a Wi-Fi network must meet.
+     *
+     * @see #WIFI_SECURITY_OPEN
+     * @see #WIFI_SECURITY_PERSONAL
+     * @see #WIFI_SECURITY_ENTERPRISE_EAP
+     */
+    public static final int WIFI_SECURITY_ENTERPRISE_192 = 3;
+
+    /**
+     * Possible Wi-Fi minimum security levels
+     *
+     * @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"WIFI_SECURITY_"}, value = {
+            WIFI_SECURITY_OPEN,
+            WIFI_SECURITY_PERSONAL,
+            WIFI_SECURITY_ENTERPRISE_EAP,
+            WIFI_SECURITY_ENTERPRISE_192})
+    public @interface WifiSecurity {}
+
+    /**
      * This MIME type is used for starting the device owner provisioning.
      *
      * <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -2832,6 +2983,20 @@
             "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
 
     /**
+     * A {@code boolean} flag that indicates whether the screen should be on throughout the
+     * provisioning flow.
+     *
+     * <p>The default value is {@code false}.
+     *
+     * <p>This extra can either be passed as an extra to the {@link
+     * #ACTION_PROVISION_MANAGED_PROFILE} intent, or it can be returned by the
+     * admin app when performing the admin-integrated provisioning flow as a result of the
+     * {@link #ACTION_GET_PROVISIONING_MODE} activity.
+     */
+    public static final String EXTRA_PROVISIONING_KEEP_SCREEN_ON =
+            "android.app.extra.PROVISIONING_KEEP_SCREEN_ON";
+
+    /**
      * Activity action: Starts the administrator to show policy compliance for the provisioning.
      * This action is used any time that the administrator has an opportunity to show policy
      * compliance before the end of setup wizard. This could happen as part of the admin-integrated
@@ -2857,10 +3022,12 @@
      * #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters a problem
      * that will not be solved by relaunching it again.
      *
+     * <p>If this activity has additional internal conditions which are not met, it should return
+     * {@link #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR}.
+     *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER =
@@ -2885,6 +3052,86 @@
     public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2;
 
     /**
+     * An {@link Intent} extra which resolves to a custom user consent screen.
+     *
+     * <p>If this extra is provided to the device management role holder via either {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}, the device management role holder must
+     * launch this intent which shows the custom user consent screen, replacing its own standard
+     * consent screen.
+     *
+     * <p>If this extra is provided, it is the responsibility of the intent handler to show the
+     * list of disclaimers which are normally shown by the standard consent screen:
+     * <ul>
+     *     <li>Disclaimers set by the IT admin via the {@link #EXTRA_PROVISIONING_DISCLAIMERS}
+     *     provisioning extra</li>
+     *     <li>For fully-managed device provisioning, disclaimers defined in system apps via the
+     *     {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER} and {@link
+     *     #EXTRA_PROVISIONING_DISCLAIMER_CONTENT} metadata in their manifests</li>
+     *     <li>General disclaimer relevant to the provisioning mode</li>
+     * </ul>
+     *
+     * <p>If the custom consent screens are granted by the user {@link Activity#RESULT_OK} is
+     * returned, otherwise {@link Activity#RESULT_CANCELED} is returned. The device management
+     * role holder should ensure that the provisioning flow terminates immediately if consent
+     * is not granted by the user.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT =
+            "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
+
+    /**
+     * Activity action: attempts to establish network connection
+     *
+     * <p>This intent can be accompanied by any of the relevant provisioning extras related to
+     * network connectivity, such as:
+     * <ul>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_SSID}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_HIDDEN}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PASSWORD}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_EAP_METHOD}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_PHASE2_AUTH}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_IDENTITY}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_DOMAIN}</li>
+     * </ul>
+     *
+     * <p>If there are provisioning extras related to network connectivity, this activity
+     * attempts to connect to the specified network. Otherwise it prompts the end-user to connect.
+     *
+     * <p>This activity is meant to be started by the provisioning initiator prior to starting
+     * {@link #ACTION_PROVISION_MANAGED_PROFILE} or {@link
+     * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
+     *
+     * <p>Note that network connectivity is still also handled when provisioning via {@link
+     * #ACTION_PROVISION_MANAGED_PROFILE} or {@link
+     * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. {@link
+     * #ACTION_ESTABLISH_NETWORK_CONNECTION} should only be used in cases when the provisioning
+     * initiator would like to do some additional logic after the network connectivity step and
+     * before the start of provisioning.
+     *
+     * If network connection is established, {@link Activity#RESULT_OK} will be returned. Otherwise
+     * the result will be {@link Activity#RESULT_CANCELED}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_ESTABLISH_NETWORK_CONNECTION =
+            "android.app.action.ESTABLISH_NETWORK_CONNECTION";
+
+    /**
      * Maximum supported password length. Kind-of arbitrary.
      * @hide
      */
@@ -3153,6 +3400,45 @@
      */
     public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1;
 
+    /**
+     * Broadcast action: notify system apps (e.g. settings, SysUI, etc) that the device management
+     * resources with IDs {@link #EXTRA_RESOURCE_ID} has been updated, the updated resources can be
+     * retrieved using {@link #getDrawable} and {@code #getString}.
+     *
+     * <p>This broadcast is sent to registered receivers only.
+     *
+     * <p> The following extras will be included to identify the type of resource being updated:
+     * <ul>
+     *     <li>{@link #EXTRA_RESOURCE_TYPE_DRAWABLE} for drawable resources</li>
+     *     <li>{@link #EXTRA_RESOURCE_TYPE_STRING} for string resources</li>
+     * </ul>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED =
+            "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED";
+
+    /**
+     * A boolean extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate that a
+     * resource of type {@link Drawable} is being updated.
+     */
+    public static final String EXTRA_RESOURCE_TYPE_DRAWABLE =
+            "android.app.extra.RESOURCE_TYPE_DRAWABLE";
+
+    /**
+     * A boolean extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate that a
+     * resource of type {@link String} is being updated.
+     */
+    public static final String EXTRA_RESOURCE_TYPE_STRING =
+            "android.app.extra.RESOURCE_TYPE_STRING";
+
+    /**
+     * An integer array extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate which
+     * resource IDs (see {@link DevicePolicyResources.UpdatableDrawableId} and
+     * {@link DevicePolicyResources.UpdatableStringId}) have been updated.
+     */
+    public static final String EXTRA_RESOURCE_ID =
+            "android.app.extra.RESOURCE_ID";
+
     /** @hide */
     @NonNull
     @TestApi
@@ -14344,4 +14630,509 @@
     public Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
         return ProvisioningIntentHelper.createProvisioningIntentFromNfcIntent(nfcIntent);
     }
+
+    /**
+     * Called by device owner or profile owner of an organization-owned managed profile to
+     * specify the minimum security level required for Wi-Fi networks.
+     * The device may not connect to networks that do not meet the minimum security level.
+     * If the current network does not meet the minimum security level set, it will be disconnected.
+     *
+     *
+     * @param level minimum security level
+     * @throws SecurityException if the caller is not a device owner or a profile owner on
+     *         an organization-owned managed profile.
+     */
+    public void setMinimumRequiredWifiSecurityLevel(@WifiSecurity int level) {
+        throwIfParentInstance("setMinimumRequiredWifiSecurityLevel");
+        if (mService != null) {
+            try {
+                mService.setMinimumRequiredWifiSecurityLevel(level);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the current Wi-Fi minimum security level.
+     *
+     * @see #setMinimumRequiredWifiSecurityLevel(int)
+     */
+    public @WifiSecurity int getMinimumRequiredWifiSecurityLevel() {
+        throwIfParentInstance("getMinimumRequiredWifiSecurityLevel");
+        if (mService == null) {
+            return WIFI_SECURITY_OPEN;
+        }
+        try {
+            return mService.getMinimumRequiredWifiSecurityLevel();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by device owner or profile owner of an organization-owned managed profile to
+     * specify the Wi-Fi SSID policy ({@link WifiSsidPolicy}).
+     * Wi-Fi SSID policy specifies the SSID restriction the network must satisfy
+     * in order to be eligible for a connection. Providing a null policy results in the
+     * deactivation of the SSID restriction
+     *
+     * @param policy Wi-Fi SSID policy
+     * @throws SecurityException if the caller is not a device owner or a profile owner on
+     *         an organization-owned managed profile.
+     */
+    public void setWifiSsidPolicy(@Nullable WifiSsidPolicy policy) {
+        throwIfParentInstance("setWifiSsidPolicy");
+        if (mService != null) {
+            try {
+                if (policy == null) {
+                    mService.setSsidAllowlist(new ArrayList<>());
+                } else {
+                    int policyType = policy.getPolicyType();
+                    if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
+                        mService.setSsidAllowlist(new ArrayList<>(policy.getSsids()));
+                    } else {
+                        mService.setSsidDenylist(new ArrayList<>(policy.getSsids()));
+                    }
+                }
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the current Wi-Fi SSID policy.
+     * If the policy has not been set, it will return NULL.
+     *
+     * @see #setWifiSsidPolicy(WifiSsidPolicy)
+     * @throws SecurityException if the caller is not a device owner or a profile owner on
+     * an organization-owned managed profile or a system app.
+     */
+    @Nullable
+    public WifiSsidPolicy getWifiSsidPolicy() {
+        throwIfParentInstance("getWifiSsidPolicy");
+        if (mService == null) {
+            return null;
+        }
+        try {
+            List<String> allowlist = mService.getSsidAllowlist();
+            if (!allowlist.isEmpty()) {
+                return WifiSsidPolicy.createAllowlistPolicy(new ArraySet<>(allowlist));
+            }
+            List<String> denylist = mService.getSsidDenylist();
+            if (!denylist.isEmpty()) {
+                return WifiSsidPolicy.createDenylistPolicy(new ArraySet<>(denylist));
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return null;
+    }
+
+    /**
+     * For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if
+     * {@link DevicePolicyDrawableResource#getDrawableSource()} is not set or is set to
+     * {@link DevicePolicyResources.Drawable.Source#UNDEFINED}, it updates the drawable resource for
+     * the combination of {@link DevicePolicyDrawableResource#getDrawableId()} and
+     * {@link DevicePolicyDrawableResource#getDrawableStyle()}, (see
+     * {@link DevicePolicyResources.Drawable} and {@link DevicePolicyResources.Drawable.Style}) to
+     * the drawable with ID {@link DevicePolicyDrawableResource#getCallingPackageResourceId()},
+     * meaning any system UI surface calling {@link #getDrawable}
+     * with {@code drawableId} and {@code drawableStyle} will get the new resource after this API
+     * is called.
+     *
+     * <p>Otherwise, if {@link DevicePolicyDrawableResource#getDrawableSource()} is set (see
+     * {@link DevicePolicyResources.Drawable.Source}, it overrides any drawables that was set for
+     * the same {@code drawableId} and {@code drawableStyle} for the provided source.
+     *
+     * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+     * registered receivers when a resource has been updated successfully.
+     *
+     * <p>Important notes to consider when using this API:
+     * <ul>
+     * <li>{@link #getDrawable} references the resource
+     * {@link DevicePolicyDrawableResource#getCallingPackageResourceId()} in the
+     * calling package each time it gets called. You have to ensure that the resource is always
+     * available in the calling package as long as it is used as an updated resource.
+     * <li>You still have to re-call {@code setDrawables} even if you only make changes to the
+     * content of the resource with ID
+     * {@link DevicePolicyDrawableResource#getCallingPackageResourceId()} as the content might be
+     * cached and would need updating.
+     * </ul>
+     *
+     * @param drawables The list of {@link DevicePolicyDrawableResource} to update.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+    public void setDrawables(@NonNull Set<DevicePolicyDrawableResource> drawables) {
+        if (mService != null) {
+            try {
+                mService.setDrawables(new ArrayList<>(drawables));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes all updated drawables for the list of {@code drawableIds} (see
+     * {@link DevicePolicyResources.Drawable} that was previously set by calling
+     * {@link #setDrawables}, meaning any subsequent calls to {@link #getDrawable} for the provided
+     * IDs with any {@link DevicePolicyResources.Drawable.Style} and any
+     * {@link DevicePolicyResources.Drawable.Source} will return the default drawable from
+     * {@code defaultDrawableLoader}.
+     *
+     * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+     * registered receivers when a resource has been reset successfully.
+     *
+     * @param drawableIds The list of IDs  to remove.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+    public void resetDrawables(@NonNull int[] drawableIds) {
+        if (mService != null) {
+            try {
+                mService.resetDrawables(drawableIds);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the appropriate updated drawable for the {@code drawableId}
+     * (see {@link DevicePolicyResources.Drawable}), with style {@code drawableStyle}
+     * (see {@link DevicePolicyResources.Drawable.Style}) if one was set using
+     * {@code setDrawables}, otherwise returns the drawable from {@code defaultDrawableLoader}.
+     *
+     * <p>Also returns the drawable from {@code defaultDrawableLoader} if
+     * {@link DevicePolicyResources.Drawable#INVALID_ID} was passed.
+     *
+     * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+     * {@link NullPointerException} is thrown.
+     *
+     * <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to
+     * set a different value use
+     * {@link #getDrawableForDensity(int, int, int, Callable)}.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * <p>Note that each call to this API loads the resource from the package that called
+     * {@code setDrawables} to set the updated resource.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @NonNull
+    public Drawable getDrawable(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        return getDrawable(drawableId, drawableStyle, UNDEFINED, defaultDrawableLoader);
+    }
+
+    /**
+     * Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
+     * a {@code drawableSource} (see {@link DevicePolicyResources.Drawable.Source}) which
+     * could result in returning a different drawable than {@link #getDrawable(int, int, Callable)}
+     * if an override was set for that specific source.
+     *
+     * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+     * {@link NullPointerException} is thrown.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param drawableSource The source for the caller.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @NonNull
+    public Drawable getDrawable(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
+        if (drawableId == INVALID_ID) {
+            return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+        }
+        if (mService != null) {
+            try {
+                ParcelableResource resource = mService.getDrawable(
+                        drawableId, drawableStyle, drawableSource);
+                if (resource == null) {
+                    return ParcelableResource.loadDefaultDrawable(
+                            defaultDrawableLoader);
+                }
+                return resource.getDrawable(
+                        mContext,
+                        /* density= */ 0,
+                        defaultDrawableLoader);
+
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Error getting the updated drawable from DevicePolicyManagerService.",
+                        e);
+                return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+            }
+        }
+        return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+    }
+
+    /**
+     * Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
+     * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
+     *
+     * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+     * {@link NullPointerException} is thrown.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param density The desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}. A value of 0 means to use the
+     *            density returned from {@link Resources#getConfiguration()}.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @NonNull
+    public Drawable getDrawableForDensity(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            int density,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        return getDrawableForDensity(
+                drawableId,
+                drawableStyle,
+                UNDEFINED,
+                density,
+                defaultDrawableLoader);
+    }
+
+     /**
+     * Similar to {@link #getDrawable(int, int, int, Callable)}, but also accepts
+     * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
+     *
+     * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+     * {@link NullPointerException} is thrown.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param drawableSource The source for the caller.
+     * @param density The desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}. A value of 0 means to use the
+     *            density returned from {@link Resources#getConfiguration()}.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @NonNull
+    public Drawable getDrawableForDensity(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            int density,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
+        if (drawableId == INVALID_ID) {
+            return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+        }
+        if (mService != null) {
+            try {
+                ParcelableResource resource = mService.getDrawable(
+                        drawableId, drawableStyle, drawableSource);
+                if (resource == null) {
+                    return ParcelableResource.loadDefaultDrawable(
+                            defaultDrawableLoader);
+                }
+                return resource.getDrawable(mContext, density, defaultDrawableLoader);
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Error getting the updated drawable from DevicePolicyManagerService.",
+                        e);
+                return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+            }
+        }
+        return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+    }
+
+    /**
+     * For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string
+     * resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID
+     * {@code callingPackageResourceId} (see {@link DevicePolicyResources.String}), meaning any
+     * system UI surface calling {@link #getString} with {@code stringId} will get
+     * the new resource after this API is called.
+     *
+     * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+     * registered receivers when a resource has been updated successfully.
+     *
+     * <p>Important notes to consider when using this API:
+     * <ul>
+     * <li> {@link #getString} references the resource
+     * {@code callingPackageResourceId} in the calling package each time it gets called. You have to
+     * ensure that the resource is always available in the calling package as long as it is used as
+     * an updated resource.
+     * <li> You still have to re-call {@code setStrings} even if you only make changes to the
+     * content of the resource with ID {@code callingPackageResourceId} as the content might be
+     * cached and would need updating.
+     * </ul>
+     *
+     * @param strings The list of {@link DevicePolicyStringResource} to update.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+    public void setStrings(@NonNull Set<DevicePolicyStringResource> strings) {
+        if (mService != null) {
+            try {
+                mService.setStrings(new ArrayList<>(strings));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes the updated strings for the list of {@code stringIds} (see
+     * {@link DevicePolicyResources.String}) that was previously set by calling {@link #setStrings},
+     * meaning any subsequent calls to {@link #getString} for the provided IDs will
+     * return the default string from {@code defaultStringLoader}.
+     *
+     * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+     * registered receivers when a resource has been reset successfully.
+     *
+     * @param stringIds The list of IDs to remove the updated resources for.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+    public void resetStrings(@NonNull String[] stringIds) {
+        if (mService != null) {
+            try {
+                mService.resetStrings(stringIds);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the appropriate updated string for the {@code stringId} (see
+     * {@link DevicePolicyResources.String}) if one was set using
+     * {@link #setStrings}, otherwise returns the string from {@code defaultStringLoader}.
+     *
+     * <p>Also returns the string from {@code defaultStringLoader} if
+     * {@link DevicePolicyResources.String#INVALID_ID} was passed.
+     *
+     * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
+     * {@link NullPointerException} is thrown.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * <p>Note that each call to this API loads the resource from the package that called
+     * {@link #setStrings} to set the updated resource.
+     *
+     * @param stringId The IDs to get the updated resource for.
+     * @param defaultStringLoader To get the default string if no updated string was set for
+     *         {@code stringId}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public String getString(
+            @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+            @NonNull Callable<String> defaultStringLoader) {
+
+        Objects.requireNonNull(stringId, "stringId can't be null");
+        Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
+
+        if (stringId.equals(DevicePolicyResources.Strings.UNDEFINED)) {
+            return ParcelableResource.loadDefaultString(defaultStringLoader);
+        }
+        if (mService != null) {
+            try {
+                ParcelableResource resource = mService.getString(stringId);
+                if (resource == null) {
+                    return ParcelableResource.loadDefaultString(defaultStringLoader);
+                }
+                return resource.getString(mContext, defaultStringLoader);
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Error getting the updated string from DevicePolicyManagerService.",
+                        e);
+                return ParcelableResource.loadDefaultString(defaultStringLoader);
+            }
+        }
+        return ParcelableResource.loadDefaultString(defaultStringLoader);
+    }
+
+    /**
+     * Similar to {@link #getString(String, Callable)} but accepts {@code formatArgs} and returns a
+     * localized formatted string, substituting the format arguments as defined in
+     * {@link java.util.Formatter} and {@link java.lang.String#format}, (see
+     * {@link Resources#getString(int, Object...)}).
+     *
+     * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
+     * {@link NullPointerException} is thrown.
+     *
+     * @param stringId The IDs to get the updated resource for.
+     * @param defaultStringLoader To get the default string if no updated string was set for
+     *         {@code stringId}.
+     * @param formatArgs The format arguments that will be used for substitution.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @SuppressLint("SamShouldBeLast")
+    public String getString(
+            @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+            @NonNull Callable<String> defaultStringLoader,
+            @NonNull Object... formatArgs) {
+
+        Objects.requireNonNull(stringId, "stringId can't be null");
+        Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
+
+        if (stringId.equals(DevicePolicyResources.Strings.UNDEFINED)) {
+            return ParcelableResource.loadDefaultString(defaultStringLoader);
+        }
+        if (mService != null) {
+            try {
+                ParcelableResource resource = mService.getString(stringId);
+                if (resource == null) {
+                    return ParcelableResource.loadDefaultString(defaultStringLoader);
+                }
+                return resource.getString(mContext, defaultStringLoader, formatArgs);
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Error getting the updated string from DevicePolicyManagerService.",
+                        e);
+                return ParcelableResource.loadDefaultString(defaultStringLoader);
+            }
+        }
+        return ParcelableResource.loadDefaultString(defaultStringLoader);
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
new file mode 100644
index 0000000..46e2cce
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -0,0 +1,1417 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_CHANNEL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_SOON_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_PERSONAL_FILES_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_PERSONAL_FILES_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_WORK_FILES_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_WORK_FILES_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.PREVIEW_WORK_FILE_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_PROFILE_OFF_ENABLE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_PROFILE_OFF_ERROR_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.DISABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_FOLDER_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU_ACCEPT;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_ENABLE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_BY_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_FROM_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_FROM_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.ONGOING_PRIVACY_DIALOG_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_CA_CERT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NETWORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_NAMED_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_VIEW_POLICIES;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_CA_CERT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_NETWORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_MULTIPLE_VPNS;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_WORK_PROFILE_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_PERSONAL_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_NETWORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.STATUS_BAR_WORK_ICON_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.WORK_LOCK_ACCESSIBILITY;
+
+import android.annotation.IntDef;
+import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class containing the required identifiers to update device management resources.
+ *
+ * <p>See {@link DevicePolicyManager#getDrawable} and
+ * {@code DevicePolicyManager#getString}.
+ */
+public final class DevicePolicyResources {
+
+    /**
+     * Resource identifiers used to update device management-related system drawable resources.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Drawable.INVALID_ID,
+            Drawable.WORK_PROFILE_ICON_BADGE,
+            Drawable.WORK_PROFILE_ICON,
+            Drawable.WORK_PROFILE_OFF_ICON,
+            Drawable.WORK_PROFILE_USER_ICON
+    })
+    public @interface UpdatableDrawableId {}
+
+    /**
+     * Identifiers to specify the desired style for the updatable device management system
+     * resource.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Drawable.Style.SOLID_COLORED,
+            Drawable.Style.SOLID_NOT_COLORED,
+            Drawable.Style.OUTLINE,
+    })
+    public @interface UpdatableDrawableStyle {}
+
+    /**
+     * Identifiers to specify the location if the updatable device management system resource.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Drawable.Source.UNDEFINED,
+            Drawable.Source.NOTIFICATION,
+            Drawable.Source.PROFILE_SWITCH_ANIMATION,
+            Drawable.Source.HOME_WIDGET,
+            Drawable.Source.LAUNCHER_OFF_BUTTON,
+            Drawable.Source.QUICK_SETTINGS,
+            Drawable.Source.STATUS_BAR
+    })
+    public @interface UpdatableDrawableSource {}
+
+    /**
+     * Resource identifiers used to update device management-related string resources.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+            // Launcher Strings
+            WORK_PROFILE_EDU, WORK_PROFILE_EDU_ACCEPT, Strings.Launcher.WORK_PROFILE_PAUSED_TITLE,
+            WORK_PROFILE_PAUSED_DESCRIPTION, WORK_PROFILE_PAUSE_BUTTON, WORK_PROFILE_ENABLE_BUTTON,
+            ALL_APPS_WORK_TAB, ALL_APPS_PERSONAL_TAB, ALL_APPS_WORK_TAB_ACCESSIBILITY,
+            ALL_APPS_PERSONAL_TAB_ACCESSIBILITY, WORK_FOLDER_NAME, WIDGETS_WORK_TAB,
+            WIDGETS_PERSONAL_TAB, DISABLED_BY_ADMIN_MESSAGE,
+
+            // SysUI Strings
+            QS_MSG_MANAGEMENT, QS_MSG_NAMED_MANAGEMENT, QS_MSG_MANAGEMENT_MONITORING,
+            QS_MSG_NAMED_MANAGEMENT_MONITORING, QS_MSG_MANAGEMENT_NAMED_VPN,
+            QS_MSG_NAMED_MANAGEMENT_NAMED_VPN, QS_MSG_MANAGEMENT_MULTIPLE_VPNS,
+            QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS, QS_MSG_WORK_PROFILE_MONITORING,
+            QS_MSG_NAMED_WORK_PROFILE_MONITORING, QS_MSG_WORK_PROFILE_NETWORK,
+            QS_MSG_WORK_PROFILE_NAMED_VPN, QS_MSG_PERSONAL_PROFILE_NAMED_VPN,
+            QS_DIALOG_MANAGEMENT_TITLE, QS_DIALOG_VIEW_POLICIES, QS_DIALOG_MANAGEMENT,
+            QS_DIALOG_NAMED_MANAGEMENT, QS_DIALOG_MANAGEMENT_CA_CERT,
+            QS_DIALOG_WORK_PROFILE_CA_CERT, QS_DIALOG_MANAGEMENT_NETWORK,
+            QS_DIALOG_WORK_PROFILE_NETWORK, QS_DIALOG_MANAGEMENT_NAMED_VPN,
+            QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN, QS_DIALOG_WORK_PROFILE_NAMED_VPN,
+            QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN, BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT,
+            BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT, BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT,
+            BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS, STATUS_BAR_WORK_ICON_ACCESSIBILITY,
+            ONGOING_PRIVACY_DIALOG_WORK, KEYGUARD_MANAGEMENT_DISCLOSURE,
+            KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE, WORK_LOCK_ACCESSIBILITY,
+
+            // Core Strings
+            WORK_PROFILE_DELETED_TITLE, WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+            WORK_PROFILE_DELETED_GENERIC_MESSAGE, WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+            PERSONAL_APP_SUSPENSION_TITLE, PERSONAL_APP_SUSPENSION_MESSAGE,
+            PERSONAL_APP_SUSPENSION_SOON_MESSAGE, PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+            PRINTING_DISABLED_NAMED_ADMIN, LOCATION_CHANGED_TITLE, LOCATION_CHANGED_MESSAGE,
+            NETWORK_LOGGING_TITLE,  NETWORK_LOGGING_MESSAGE,
+            NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION, NOTIFICATION_CHANNEL_DEVICE_ADMIN,
+            SWITCH_TO_WORK_LABEL, SWITCH_TO_PERSONAL_LABEL, FORWARD_INTENT_TO_WORK,
+            FORWARD_INTENT_TO_PERSONAL, RESOLVER_WORK_PROFILE_NOT_SUPPORTED, RESOLVER_PERSONAL_TAB,
+            RESOLVER_WORK_TAB, RESOLVER_PERSONAL_TAB_ACCESSIBILITY, RESOLVER_WORK_TAB_ACCESSIBILITY,
+            RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, RESOLVER_CANT_SHARE_WITH_PERSONAL,
+            RESOLVER_CANT_SHARE_WITH_WORK, RESOLVER_CANT_ACCESS_PERSONAL, RESOLVER_CANT_ACCESS_WORK,
+            RESOLVER_WORK_PAUSED_TITLE, RESOLVER_NO_WORK_APPS, RESOLVER_NO_PERSONAL_APPS,
+            CANT_ADD_ACCOUNT_MESSAGE, PACKAGE_INSTALLED_BY_DO, PACKAGE_UPDATED_BY_DO,
+            PACKAGE_DELETED_BY_DO, UNLAUNCHABLE_APP_WORK_PAUSED_TITLE,
+            UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE, PROFILE_ENCRYPTED_TITLE, PROFILE_ENCRYPTED_DETAIL,
+            PROFILE_ENCRYPTED_MESSAGE, WORK_PROFILE_BADGED_LABEL,
+
+            // DocsUi Strings
+            WORK_PROFILE_OFF_ERROR_TITLE, WORK_PROFILE_OFF_ENABLE_BUTTON,
+            CANT_SELECT_WORK_FILES_TITLE, CANT_SELECT_WORK_FILES_MESSAGE,
+            CANT_SELECT_PERSONAL_FILES_TITLE, CANT_SELECT_PERSONAL_FILES_MESSAGE,
+            CANT_SAVE_TO_WORK_TITLE, CANT_SAVE_TO_WORK_MESSAGE, CANT_SAVE_TO_PERSONAL_TITLE,
+            CANT_SAVE_TO_PERSONAL_MESSAGE, CROSS_PROFILE_NOT_ALLOWED_TITLE,
+            CROSS_PROFILE_NOT_ALLOWED_MESSAGE, PREVIEW_WORK_FILE_ACCESSIBILITY, PERSONAL_TAB,
+            WORK_TAB, WORK_ACCESSIBILITY,
+
+            // MediaProvider Strings
+            SWITCH_TO_WORK_MESSAGE, SWITCH_TO_PERSONAL_MESSAGE, BLOCKED_BY_ADMIN_TITLE,
+            BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_PERSONAL_MESSAGE,
+            BLOCKED_FROM_WORK_MESSAGE, Strings.MediaProvider.WORK_PROFILE_PAUSED_TITLE,
+            WORK_PROFILE_PAUSED_MESSAGE
+    })
+    public @interface UpdatableStringId {
+    }
+
+    /**
+     * Class containing the identifiers used to update device management-related system drawable.
+     */
+    public static final class Drawable {
+
+        private Drawable() {
+        }
+
+        /**
+         * An ID for any drawable that can't be updated.
+         */
+        public static final int INVALID_ID = -1;
+
+        /**
+         * Specifically used to badge work profile app icons.
+         */
+        public static final int WORK_PROFILE_ICON_BADGE = 0;
+
+        /**
+         * General purpose work profile icon (i.e. generic icon badging). For badging app icons
+         * specifically, see {@link #WORK_PROFILE_ICON_BADGE}.
+         */
+        public static final int WORK_PROFILE_ICON = 1;
+
+        /**
+         * General purpose icon representing the work profile off state.
+         */
+        public static final int WORK_PROFILE_OFF_ICON = 2;
+
+        /**
+         * General purpose icon for the work profile user avatar.
+         */
+        public static final int WORK_PROFILE_USER_ICON = 3;
+
+        /**
+         * @hide
+         */
+        public static final Set<Integer> UPDATABLE_DRAWABLE_IDS = buildDrawablesSet();
+
+        private static Set<Integer> buildDrawablesSet() {
+            Set<Integer> drawables = new HashSet<>();
+            drawables.add(WORK_PROFILE_ICON_BADGE);
+            drawables.add(WORK_PROFILE_ICON);
+            drawables.add(WORK_PROFILE_OFF_ICON);
+            drawables.add(WORK_PROFILE_USER_ICON);
+            return drawables;
+        }
+
+        /**
+         * Class containing the source identifiers used to update device management-related system
+         * drawable.
+         */
+        public static final class Source {
+
+            private Source() {
+            }
+
+            /**
+             * A source identifier indicating that the updatable resource is used in a generic
+             * undefined location.
+             */
+            public static final int UNDEFINED = -1;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in notifications.
+             */
+            public static final int NOTIFICATION = 0;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in a cross
+             * profile switching animation.
+             */
+            public static final int PROFILE_SWITCH_ANIMATION = 1;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in a work
+             * profile home screen widget.
+             */
+            public static final int HOME_WIDGET = 2;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in the launcher
+             * turn off work button.
+             */
+            public static final int LAUNCHER_OFF_BUTTON = 3;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in quick settings.
+             */
+            public static final int QUICK_SETTINGS = 4;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in the status bar.
+             */
+            public static final int STATUS_BAR = 5;
+
+            /**
+             * @hide
+             */
+            public static final Set<Integer> UPDATABLE_DRAWABLE_SOURCES = buildSourcesSet();
+
+            private static Set<Integer> buildSourcesSet() {
+                Set<Integer> sources = new HashSet<>();
+                sources.add(UNDEFINED);
+                sources.add(NOTIFICATION);
+                sources.add(PROFILE_SWITCH_ANIMATION);
+                sources.add(HOME_WIDGET);
+                sources.add(LAUNCHER_OFF_BUTTON);
+                sources.add(QUICK_SETTINGS);
+                sources.add(STATUS_BAR);
+                return sources;
+            }
+        }
+
+        /**
+         * Class containing the style identifiers used to update device management-related system
+         * drawable.
+         */
+        @SuppressLint("StaticUtils")
+        public static final class Style {
+
+            private Style() {
+            }
+
+            /**
+             * A style identifier indicating that the updatable drawable should use the default
+             * style.
+             */
+            public static final int DEFAULT = -1;
+
+            /**
+             * A style identifier indicating that the updatable drawable has a solid color fill.
+             */
+            public static final int SOLID_COLORED = 0;
+
+            /**
+             * A style identifier indicating that the updatable drawable has a solid non-colored
+             * fill.
+             */
+            public static final int SOLID_NOT_COLORED = 1;
+
+            /**
+             * A style identifier indicating that the updatable drawable is an outline.
+             */
+            public static final int OUTLINE = 2;
+
+            /**
+             * @hide
+             */
+            public static final Set<Integer> UPDATABLE_DRAWABLE_STYLES = buildStylesSet();
+
+            private static Set<Integer> buildStylesSet() {
+                Set<Integer> styles = new HashSet<>();
+                styles.add(DEFAULT);
+                styles.add(SOLID_COLORED);
+                styles.add(SOLID_NOT_COLORED);
+                styles.add(OUTLINE);
+                return styles;
+            }
+        }
+    }
+
+    /**
+     * Class containing the identifiers used to update device management-related system strings.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Strings {
+
+        private Strings() {}
+
+        /**
+         * An ID for any string that can't be updated.
+         */
+        public static final String UNDEFINED = "UNDEFINED";
+
+        /**
+         * @hide
+         */
+        public static final Set<String> UPDATABLE_STRING_IDS = buildStringsSet();
+
+        private static Set<String> buildStringsSet() {
+            Set<String> strings = new HashSet<>();
+            strings.addAll(Launcher.buildStringsSet());
+            strings.addAll(SystemUi.buildStringsSet());
+            strings.addAll(Core.buildStringsSet());
+            strings.addAll(DocumentsUi.buildStringsSet());
+            strings.addAll(MediaProvider.buildStringsSet());
+            return strings;
+        }
+
+        /**
+         * Class containing the identifiers used to update device management-related system strings
+         * in the Launcher package.
+         *
+         * @hide
+         */
+        public static final class Launcher {
+
+            private Launcher(){}
+
+            private static final String PREFIX = "Launcher.";
+
+            /**
+             * User on-boarding title for work profile apps.
+             */
+            public static final String WORK_PROFILE_EDU = PREFIX + "WORK_PROFILE_EDU";
+
+            /**
+             * Action label to finish work profile edu.
+             */
+            public static final String WORK_PROFILE_EDU_ACCEPT = PREFIX + "WORK_PROFILE_EDU_ACCEPT";
+
+            /**
+             * Title shown when user opens work apps tab while work profile is paused.
+             */
+            public static final String WORK_PROFILE_PAUSED_TITLE =
+                    PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+            /**
+             * Description shown when user opens work apps tab while work profile is paused.
+             */
+            public static final String WORK_PROFILE_PAUSED_DESCRIPTION =
+                    PREFIX + "WORK_PROFILE_PAUSED_DESCRIPTION";
+
+            /**
+             * Shown on the button to pause work profile.
+             */
+            public static final String WORK_PROFILE_PAUSE_BUTTON =
+                    PREFIX + "WORK_PROFILE_PAUSE_BUTTON";
+
+            /**
+             * Shown on the button to enable work profile.
+             */
+            public static final String WORK_PROFILE_ENABLE_BUTTON =
+                    PREFIX + "WORK_PROFILE_ENABLE_BUTTON";
+
+            /**
+             * Label on launcher tab to indicate work apps.
+             */
+            public static final String ALL_APPS_WORK_TAB = PREFIX + "ALL_APPS_WORK_TAB";
+
+            /**
+             * Label on launcher tab to indicate personal apps.
+             */
+            public static final String ALL_APPS_PERSONAL_TAB = PREFIX + "ALL_APPS_PERSONAL_TAB";
+
+            /**
+             * Accessibility description for launcher tab to indicate work apps.
+             */
+            public static final String ALL_APPS_WORK_TAB_ACCESSIBILITY =
+                    PREFIX + "ALL_APPS_WORK_TAB_ACCESSIBILITY";
+
+            /**
+             * Accessibility description for launcher tab to indicate personal apps.
+             */
+            public static final String ALL_APPS_PERSONAL_TAB_ACCESSIBILITY =
+                    PREFIX + "ALL_APPS_PERSONAL_TAB_ACCESSIBILITY";
+
+            /**
+             * Work folder name.
+             */
+            public static final String WORK_FOLDER_NAME = PREFIX + "WORK_FOLDER_NAME";
+
+            /**
+             * Label on widget tab to indicate work app widgets.
+             */
+            public static final String WIDGETS_WORK_TAB = PREFIX + "WIDGETS_WORK_TAB";
+
+            /**
+             * Label on widget tab to indicate personal app widgets.
+             */
+            public static final String WIDGETS_PERSONAL_TAB = PREFIX + "WIDGETS_PERSONAL_TAB";
+
+            /**
+             * Message shown when a feature is disabled by the admin (e.g. changing wallpaper).
+             */
+            public static final String DISABLED_BY_ADMIN_MESSAGE =
+                    PREFIX + "DISABLED_BY_ADMIN_MESSAGE";
+
+            /**
+             * @hide
+             */
+            static Set<String> buildStringsSet() {
+                Set<String> strings = new HashSet<>();
+                strings.add(WORK_PROFILE_EDU);
+                strings.add(WORK_PROFILE_EDU_ACCEPT);
+                strings.add(WORK_PROFILE_PAUSED_TITLE);
+                strings.add(WORK_PROFILE_PAUSED_DESCRIPTION);
+                strings.add(WORK_PROFILE_PAUSE_BUTTON);
+                strings.add(WORK_PROFILE_ENABLE_BUTTON);
+                strings.add(ALL_APPS_WORK_TAB);
+                strings.add(ALL_APPS_PERSONAL_TAB);
+                strings.add(ALL_APPS_PERSONAL_TAB_ACCESSIBILITY);
+                strings.add(ALL_APPS_WORK_TAB_ACCESSIBILITY);
+                strings.add(WORK_FOLDER_NAME);
+                strings.add(WIDGETS_WORK_TAB);
+                strings.add(WIDGETS_PERSONAL_TAB);
+                strings.add(DISABLED_BY_ADMIN_MESSAGE);
+                return strings;
+            }
+        }
+
+        /**
+         * Class containing the identifiers used to update device management-related system strings
+         * in the SystemUi package.
+         *
+         * @hide
+         */
+        public static final class SystemUi {
+
+            private SystemUi() {
+            }
+            private static final String PREFIX = "SystemUi.";
+
+            /**
+             * Label in quick settings for toggling work profile on/off.
+             */
+            public static final String QS_WORK_PROFILE_LABEL = PREFIX + "QS_WORK_PROFILE_LABEL";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate device management.
+             */
+            public static final String QS_MSG_MANAGEMENT = PREFIX + "QS_MSG_MANAGEMENT";
+
+            /**
+             * Similar to {@link #QS_MSG_MANAGEMENT} but accepts the organization name as a
+             * param.
+             */
+            public static final String QS_MSG_NAMED_MANAGEMENT = PREFIX + "QS_MSG_NAMED_MANAGEMENT";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate device management monitoring.
+             */
+            public static final String QS_MSG_MANAGEMENT_MONITORING =
+                    PREFIX + "QS_MSG_MANAGEMENT_MONITORING";
+
+            /**
+             * Similar to {@link #QS_MSG_MANAGEMENT_MONITORING} but accepts the
+             * organization name as a param.
+             */
+            public static final String QS_MSG_NAMED_MANAGEMENT_MONITORING =
+                    PREFIX + "QS_MSG_NAMED_MANAGEMENT_MONITORING";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate device management and the
+             * device is connected to a VPN, accepts VPN name as a param.
+             */
+            public static final String QS_MSG_MANAGEMENT_NAMED_VPN =
+                    PREFIX + "QS_MSG_MANAGEMENT_NAMED_VPN";
+
+            /**
+             * Similar to {@link #QS_MSG_MANAGEMENT_NAMED_VPN} but also accepts the
+             * organization name as a param.
+             */
+            public static final String QS_MSG_NAMED_MANAGEMENT_NAMED_VPN =
+                    PREFIX + "QS_MSG_NAMED_MANAGEMENT_NAMED_VPN";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate device management and the
+             * device is connected to multiple VPNs.
+             */
+            public static final String QS_MSG_MANAGEMENT_MULTIPLE_VPNS =
+                    PREFIX + "QS_MSG_MANAGEMENT_MULTIPLE_VPNS";
+
+            /**
+             * Similar to {@link #QS_MSG_MANAGEMENT_MULTIPLE_VPNS} but also accepts the
+             * organization name as a param.
+             */
+            public static final String QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS =
+                    PREFIX + "QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate work profile monitoring.
+             */
+            public static final String QS_MSG_WORK_PROFILE_MONITORING =
+                    PREFIX + "QS_MSG_WORK_PROFILE_MONITORING";
+
+            /**
+             * Similar to {@link #QS_MSG_WORK_PROFILE_MONITORING} but accepts the
+             * organization name as a param.
+             */
+            public static final String QS_MSG_NAMED_WORK_PROFILE_MONITORING =
+                    PREFIX + "QS_MSG_NAMED_WORK_PROFILE_MONITORING";
+
+            /**
+            * Disclosure at the bottom of Quick Settings to indicate network activity is visible to
+             * admin.
+            */
+            public static final String QS_MSG_WORK_PROFILE_NETWORK =
+                    PREFIX + "QS_MSG_WORK_PROFILE_NETWORK";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate work profile is connected to a
+             * VPN, accepts VPN name as a param.
+             */
+            public static final String QS_MSG_WORK_PROFILE_NAMED_VPN =
+                    PREFIX + "QS_MSG_WORK_PROFILE_NAMED_VPN";
+
+            /**
+             * Disclosure at the bottom of Quick Settings to indicate personal profile is connected
+             * to a VPN, accepts VPN name as a param.
+             */
+            public static final String QS_MSG_PERSONAL_PROFILE_NAMED_VPN =
+                    PREFIX + "QS_MSG_PERSONAL_PROFILE_NAMED_VPN";
+
+            /**
+             * Title for dialog to indicate device management.
+             */
+            public static final String QS_DIALOG_MANAGEMENT_TITLE =
+                    PREFIX + "QS_DIALOG_MANAGEMENT_TITLE";
+
+            /**
+             * Label for button in the device management dialog to open a page with more information
+             * on the admin's abilities.
+             */
+            public static final String QS_DIALOG_VIEW_POLICIES =
+                    PREFIX + "QS_DIALOG_VIEW_POLICIES";
+
+            /**
+             * Description for device management dialog to indicate admin abilities.
+             */
+            public static final String QS_DIALOG_MANAGEMENT = PREFIX + "QS_DIALOG_MANAGEMENT";
+
+            /**
+             * Similar to {@link #QS_DIALOG_MANAGEMENT} but accepts the organization name as a
+             * param.
+             */
+            public static final String QS_DIALOG_NAMED_MANAGEMENT =
+                    PREFIX + "QS_DIALOG_NAMED_MANAGEMENT";
+
+            /**
+             * Description for the managed device certificate authorities in the device management
+             * dialog.
+             */
+            public static final String QS_DIALOG_MANAGEMENT_CA_CERT =
+                    PREFIX + "QS_DIALOG_MANAGEMENT_CA_CERT";
+
+            /**
+             * Description for the work profile certificate authorities in the device management
+             * dialog.
+             */
+            public static final String QS_DIALOG_WORK_PROFILE_CA_CERT =
+                    PREFIX + "QS_DIALOG_WORK_PROFILE_CA_CERT";
+
+            /**
+             * Description for the managed device network logging in the device management dialog.
+             */
+            public static final String QS_DIALOG_MANAGEMENT_NETWORK =
+                    PREFIX + "QS_DIALOG_MANAGEMENT_NETWORK";
+
+            /**
+             * Description for the work profile network logging in the device management dialog.
+             */
+            public static final String QS_DIALOG_WORK_PROFILE_NETWORK =
+                    PREFIX + "QS_DIALOG_WORK_PROFILE_NETWORK";
+
+            /**
+             * Description for an active VPN in the device management dialog, accepts VPN name as a
+             * param.
+             */
+            public static final String QS_DIALOG_MANAGEMENT_NAMED_VPN =
+                    PREFIX + "QS_DIALOG_MANAGEMENT_NAMED_VPN";
+
+            /**
+             * Description for two active VPN in the device management dialog, accepts two VPN names
+             * as params.
+             */
+            public static final String QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN =
+                    PREFIX + "QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN";
+
+            /**
+             * Description for an active work profile VPN in the device management dialog, accepts
+             * VPN name as a param.
+             */
+            public static final String QS_DIALOG_WORK_PROFILE_NAMED_VPN =
+                    PREFIX + "QS_DIALOG_WORK_PROFILE_NAMED_VPN";
+
+            /**
+             * Description for an active personal profile VPN in the device management dialog,
+             * accepts VPN name as a param.
+             */
+            public static final String QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN =
+                    PREFIX + "QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN";
+
+            /**
+             * Content of a dialog shown when the user only has one attempt left to provide the
+             * correct pin before the work profile is removed.
+             */
+            public static final String BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT =
+                    PREFIX + "BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT";
+
+            /**
+             * Content of a dialog shown when the user only has one attempt left to provide the
+             * correct pattern before the work profile is removed.
+             */
+            public static final String BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT =
+                    PREFIX + "BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT";
+
+            /**
+             * Content of a dialog shown when the user only has one attempt left to provide the
+             * correct password before the work profile is removed.
+             */
+            public static final String BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT =
+                    PREFIX + "BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT";
+
+            /**
+             * Content of a dialog shown when the user has failed to provide the work lock too many
+             * times and the work profile is removed.
+             */
+            public static final String BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS =
+                    PREFIX + "BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS";
+
+            /**
+             * Accessibility label for managed profile icon in the status bar
+             */
+            public static final String STATUS_BAR_WORK_ICON_ACCESSIBILITY =
+                    PREFIX + "STATUS_BAR_WORK_ICON_ACCESSIBILITY";
+
+            /**
+             * Text appended to privacy dialog, indicating that the application is in the work
+             * profile.
+             */
+            public static final String ONGOING_PRIVACY_DIALOG_WORK =
+                    PREFIX + "ONGOING_PRIVACY_DIALOG_WORK";
+
+            /**
+             * Text on keyguard screen indicating device management.
+             */
+            public static final String KEYGUARD_MANAGEMENT_DISCLOSURE =
+                    PREFIX + "KEYGUARD_MANAGEMENT_DISCLOSURE";
+
+            /**
+             * Similar to {@link #KEYGUARD_MANAGEMENT_DISCLOSURE} but also accepts organization name
+             * as a param.
+             */
+            public static final String KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE =
+                    PREFIX + "KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE";
+
+            /**
+             * Content description for the work profile lock screen.
+             */
+            public static final String WORK_LOCK_ACCESSIBILITY = PREFIX + "WORK_LOCK_ACCESSIBILITY";
+
+            /**
+             * @hide
+             */
+            static Set<String> buildStringsSet() {
+                Set<String> strings = new HashSet<>();
+                strings.add(QS_WORK_PROFILE_LABEL);
+                strings.add(QS_MSG_MANAGEMENT);
+                strings.add(QS_MSG_NAMED_MANAGEMENT);
+                strings.add(QS_MSG_MANAGEMENT_MONITORING);
+                strings.add(QS_MSG_NAMED_MANAGEMENT_MONITORING);
+                strings.add(QS_MSG_MANAGEMENT_NAMED_VPN);
+                strings.add(QS_MSG_NAMED_MANAGEMENT_NAMED_VPN);
+                strings.add(QS_MSG_MANAGEMENT_MULTIPLE_VPNS);
+                strings.add(QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS);
+                strings.add(QS_MSG_WORK_PROFILE_MONITORING);
+                strings.add(QS_MSG_NAMED_WORK_PROFILE_MONITORING);
+                strings.add(QS_MSG_WORK_PROFILE_NETWORK);
+                strings.add(QS_MSG_WORK_PROFILE_NAMED_VPN);
+                strings.add(QS_MSG_PERSONAL_PROFILE_NAMED_VPN);
+                strings.add(QS_DIALOG_MANAGEMENT_TITLE);
+                strings.add(QS_DIALOG_VIEW_POLICIES);
+                strings.add(QS_DIALOG_MANAGEMENT);
+                strings.add(QS_DIALOG_NAMED_MANAGEMENT);
+                strings.add(QS_DIALOG_MANAGEMENT_CA_CERT);
+                strings.add(QS_DIALOG_WORK_PROFILE_CA_CERT);
+                strings.add(QS_DIALOG_MANAGEMENT_NETWORK);
+                strings.add(QS_DIALOG_WORK_PROFILE_NETWORK);
+                strings.add(QS_DIALOG_MANAGEMENT_NAMED_VPN);
+                strings.add(QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN);
+                strings.add(QS_DIALOG_WORK_PROFILE_NAMED_VPN);
+                strings.add(QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN);
+                strings.add(BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT);
+                strings.add(BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT);
+                strings.add(BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT);
+                strings.add(BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS);
+                strings.add(STATUS_BAR_WORK_ICON_ACCESSIBILITY);
+                strings.add(ONGOING_PRIVACY_DIALOG_WORK);
+                strings.add(KEYGUARD_MANAGEMENT_DISCLOSURE);
+                strings.add(KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE);
+                strings.add(WORK_LOCK_ACCESSIBILITY);
+                return strings;
+            }
+        }
+
+        /**
+         * Class containing the identifiers used to update device management-related system strings
+         * in the android core package.
+         *
+         * @hide
+         */
+        public static final class Core {
+
+            private Core() {
+            }
+
+            private static final String PREFIX = "Core.";
+            /**
+             * Notification title when the system deletes the work profile.
+             */
+            public static final String WORK_PROFILE_DELETED_TITLE =
+                    PREFIX + "WORK_PROFILE_DELETED_TITLE";
+
+            /**
+             * Content text for the "Work profile deleted" notification to indicates that a work
+             * profile has been deleted because the maximum failed password attempts as been
+             * reached.
+             */
+            public static final String WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE =
+                    PREFIX + "WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE";
+
+            /**
+             * Content text for the "Work profile deleted" notification to indicate that a work
+             * profile has been deleted.
+             */
+            public static final String WORK_PROFILE_DELETED_GENERIC_MESSAGE =
+                    PREFIX + "WORK_PROFILE_DELETED_GENERIC_MESSAGE";
+
+            /**
+             * Content text for the "Work profile deleted" notification to indicates that a work
+             * profile has been deleted because the admin of an organization-owned device has
+             * relinquishes it.
+             */
+            public static final String WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE =
+                    PREFIX + "WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE";
+
+            /**
+             * Notification title for when personal apps are either blocked or will be blocked
+             * soon due to a work policy from their admin.
+             */
+            public static final String PERSONAL_APP_SUSPENSION_TITLE =
+                    PREFIX + "PERSONAL_APP_SUSPENSION_TITLE";
+
+            /**
+             * Content text for the personal app suspension notification to indicate that personal
+             * apps are blocked due to a work policy from the admin.
+             */
+            public static final String PERSONAL_APP_SUSPENSION_MESSAGE =
+                    PREFIX + "PERSONAL_APP_SUSPENSION_MESSAGE";
+
+            /**
+             * Content text for the personal app suspension notification to indicate that personal
+             * apps will be blocked at a particular time due to a work policy from their admin.
+             * It also explains for how many days the profile is allowed to be off.
+             * <ul>Takes in the following as params:
+             * <li> The date that the personal apps will get suspended at</li>
+             * <li> The time that the personal apps will get suspended at</li>
+             * <li> The max allowed days for the work profile stay switched off</li>
+             * </ul>
+             */
+            public static final String PERSONAL_APP_SUSPENSION_SOON_MESSAGE =
+                    PREFIX + "PERSONAL_APP_SUSPENSION_SOON_MESSAGE";
+
+            /**
+             * Title for the button that turns work profile in the personal app suspension
+             * notification.
+             */
+            public static final String PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE =
+                    PREFIX + "PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE";
+
+            /**
+             * A toast message displayed when printing is attempted but disabled by policy, accepts
+             * admin name as a param.
+             */
+            public static final String PRINTING_DISABLED_NAMED_ADMIN =
+                    PREFIX + "PRINTING_DISABLED_NAMED_ADMIN";
+
+            /**
+             * Notification title to indicate that the device owner has changed the location
+             * settings.
+             */
+            public static final String LOCATION_CHANGED_TITLE = PREFIX + "LOCATION_CHANGED_TITLE";
+
+            /**
+             * Content text for the location changed notification to indicate that the device owner
+             * has changed the location settings.
+             */
+            public static final String LOCATION_CHANGED_MESSAGE =
+                    PREFIX + "LOCATION_CHANGED_MESSAGE";
+
+            /**
+             * Notification title to indicate that the device is managed and network logging was
+             * activated by a device owner.
+             */
+            public static final String NETWORK_LOGGING_TITLE = PREFIX + "NETWORK_LOGGING_TITLE";
+
+            /**
+             * Content text for the network logging notification to indicate that the device is
+             * managed and network logging was activated by a device owner.
+             */
+            public static final String NETWORK_LOGGING_MESSAGE = PREFIX + "NETWORK_LOGGING_MESSAGE";
+
+            /**
+             * Content description of the work profile icon in the notifications.
+             */
+            public static final String NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION =
+                    PREFIX + "NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION";
+
+            /**
+             * Notification channel name for high-priority alerts from the user's IT admin for key
+             * updates about the device.
+             */
+            public static final String NOTIFICATION_CHANNEL_DEVICE_ADMIN =
+                    PREFIX + "NOTIFICATION_CHANNEL_DEVICE_ADMIN";
+
+            /**
+             * Label returned from
+             * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)}
+             * that calling app can show to user for the semantic of switching to work profile.
+             */
+            public static final String SWITCH_TO_WORK_LABEL = PREFIX + "SWITCH_TO_WORK_LABEL";
+
+            /**
+             * Label returned from
+             * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)}
+             * that calling app can show to user for the semantic of switching to personal profile.
+             */
+            public static final String SWITCH_TO_PERSONAL_LABEL =
+                    PREFIX + "SWITCH_TO_PERSONAL_LABEL";
+
+            /**
+             * Message to show when an intent automatically switches users into the work profile.
+             */
+            public static final String FORWARD_INTENT_TO_WORK = PREFIX + "FORWARD_INTENT_TO_WORK";
+
+            /**
+             * Message to show when an intent automatically switches users into the personal
+             * profile.
+             */
+            public static final String FORWARD_INTENT_TO_PERSONAL =
+                    PREFIX + "FORWARD_INTENT_TO_PERSONAL";
+
+            /**
+             * Text for the toast that is shown when the user clicks on a launcher that doesn't
+             * support the work profile, takes in the launcher name as a param.
+             */
+            public static final String RESOLVER_WORK_PROFILE_NOT_SUPPORTED =
+                    PREFIX + "RESOLVER_WORK_PROFILE_NOT_SUPPORTED";
+
+            /**
+             * Label for the personal tab in the {@link com.android.internal.app.ResolverActivity).
+             */
+            public static final String RESOLVER_PERSONAL_TAB = PREFIX + "RESOLVER_PERSONAL_TAB";
+
+            /**
+             * Label for the work tab in the {@link com.android.internal.app.ResolverActivity).
+             */
+            public static final String RESOLVER_WORK_TAB = PREFIX + "RESOLVER_WORK_TAB";
+
+            /**
+             * Accessibility Label for the personal tab in the
+             * {@link com.android.internal.app.ResolverActivity).
+             */
+            public static final String RESOLVER_PERSONAL_TAB_ACCESSIBILITY =
+                    PREFIX + "RESOLVER_PERSONAL_TAB_ACCESSIBILITY";
+
+            /**
+             * Accessibility Label for the work tab in the
+             * {@link com.android.internal.app.ResolverActivity).
+             */
+            public static final String RESOLVER_WORK_TAB_ACCESSIBILITY =
+                    PREFIX + "RESOLVER_WORK_TAB_ACCESSIBILITY";
+
+            /**
+             * Title for resolver screen to let the user know that their IT admin doesn't allow
+             * them to share this content across profiles.
+             */
+            public static final String RESOLVER_CROSS_PROFILE_BLOCKED_TITLE =
+                    PREFIX + "RESOLVER_CROSS_PROFILE_BLOCKED_TITLE";
+
+            /**
+             * Description for resolver screen to let the user know that their IT admin doesn't
+             * allow them to share this content with apps in their personal profile.
+             */
+            public static final String RESOLVER_CANT_SHARE_WITH_PERSONAL =
+                    PREFIX + "RESOLVER_CANT_SHARE_WITH_PERSONAL";
+
+            /**
+             * Description for resolver screen to let the user know that their IT admin doesn't
+             * allow them to share this content with apps in their work profile.
+             */
+            public static final String RESOLVER_CANT_SHARE_WITH_WORK =
+                    PREFIX + "RESOLVER_CANT_SHARE_WITH_WORK";
+
+            /**
+             * Description for resolver screen to let the user know that their IT admin doesn't
+             * allow them to open this specific content with an app in their personal profile.
+             */
+            public static final String RESOLVER_CANT_ACCESS_PERSONAL =
+                    PREFIX + "RESOLVER_CANT_ACCESS_PERSONAL";
+
+            /**
+             * Description for resolver screen to let the user know that their IT admin doesn't
+             * allow them to open this specific content with an app in their work profile.
+             */
+            public static final String RESOLVER_CANT_ACCESS_WORK =
+                    PREFIX + "RESOLVER_CANT_ACCESS_WORK";
+
+            /**
+             * Title for resolver screen to let the user know that they need to turn on work apps
+             * in order to share or open content
+             */
+            public static final String RESOLVER_WORK_PAUSED_TITLE =
+                    PREFIX + "RESOLVER_WORK_PAUSED_TITLE";
+
+            /**
+             * Text on resolver screen to let the user know that their current work apps don't
+             * support the specific content.
+             */
+            public static final String RESOLVER_NO_WORK_APPS = PREFIX + "RESOLVER_NO_WORK_APPS";
+
+            /**
+             * Text on resolver screen to let the user know that their current personal apps don't
+             * support the specific content.
+             */
+            public static final String RESOLVER_NO_PERSONAL_APPS =
+                    PREFIX + "RESOLVER_NO_PERSONAL_APPS";
+
+            /**
+             * Message informing user that the adding the account is disallowed by an administrator.
+             */
+            public static final String CANT_ADD_ACCOUNT_MESSAGE =
+                    PREFIX + "CANT_ADD_ACCOUNT_MESSAGE";
+
+            /**
+             * Notification shown when device owner silently installs a package.
+             */
+            public static final String PACKAGE_INSTALLED_BY_DO = PREFIX + "PACKAGE_INSTALLED_BY_DO";
+
+            /**
+             * Notification shown when device owner silently updates a package.
+             */
+            public static final String PACKAGE_UPDATED_BY_DO = PREFIX + "PACKAGE_UPDATED_BY_DO";
+
+            /**
+             * Notification shown when device owner silently deleted a package.
+             */
+            public static final String PACKAGE_DELETED_BY_DO = PREFIX + "PACKAGE_DELETED_BY_DO";
+
+            /**
+             * Title for dialog shown when user tries to open a work app when the work profile is
+             * turned off, confirming that the user wants to turn on access to their
+             * work apps.
+             */
+            public static final String UNLAUNCHABLE_APP_WORK_PAUSED_TITLE =
+                    PREFIX + "UNLAUNCHABLE_APP_WORK_PAUSED_TITLE";
+
+            /**
+             * Text for dialog shown when user tries to open a work app when the work profile is
+             * turned off, confirming that the user wants to turn on access to their
+             * work apps.
+             */
+            public static final String UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE =
+                    PREFIX + "UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE";
+
+            /**
+             * Notification title shown when work profile is credential encrypted and requires
+             * the user to unlock before it's usable.
+             */
+            public static final String PROFILE_ENCRYPTED_TITLE = PREFIX + "PROFILE_ENCRYPTED_TITLE";
+
+            /**
+             * Notification detail shown when work profile is credential encrypted and requires
+             * the user to unlock before it's usable.
+             */
+            public static final String PROFILE_ENCRYPTED_DETAIL =
+                    PREFIX + "PROFILE_ENCRYPTED_DETAIL";
+
+            /**
+             * Notification message shown when work profile is credential encrypted and requires
+             * the user to unlock before it's usable.
+             */
+            public static final String PROFILE_ENCRYPTED_MESSAGE =
+                    PREFIX + "PROFILE_ENCRYPTED_MESSAGE";
+
+            /**
+             * Used to badge a string with "Work" for work profile content, e.g. "Work Email".
+             * Accepts the string to badge as an argument.
+             * <p>See {@link android.content.pm.PackageManager#getUserBadgedLabel}</p>
+             */
+            public static final String WORK_PROFILE_BADGED_LABEL =
+                    PREFIX + "WORK_PROFILE_BADGED_LABEL";
+
+            /**
+             * @hide
+             */
+            static Set<String> buildStringsSet() {
+                Set<String> strings = new HashSet<>();
+                strings.add(WORK_PROFILE_DELETED_TITLE);
+                strings.add(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE);
+                strings.add(WORK_PROFILE_DELETED_GENERIC_MESSAGE);
+                strings.add(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE);
+                strings.add(PERSONAL_APP_SUSPENSION_TITLE);
+                strings.add(PERSONAL_APP_SUSPENSION_MESSAGE);
+                strings.add(PERSONAL_APP_SUSPENSION_SOON_MESSAGE);
+                strings.add(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE);
+                strings.add(PRINTING_DISABLED_NAMED_ADMIN);
+                strings.add(LOCATION_CHANGED_TITLE);
+                strings.add(LOCATION_CHANGED_MESSAGE);
+                strings.add(NETWORK_LOGGING_TITLE);
+                strings.add(NETWORK_LOGGING_MESSAGE);
+                strings.add(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION);
+                strings.add(NOTIFICATION_CHANNEL_DEVICE_ADMIN);
+                strings.add(SWITCH_TO_WORK_LABEL);
+                strings.add(SWITCH_TO_PERSONAL_LABEL);
+                strings.add(FORWARD_INTENT_TO_WORK);
+                strings.add(FORWARD_INTENT_TO_PERSONAL);
+                strings.add(RESOLVER_WORK_PROFILE_NOT_SUPPORTED);
+                strings.add(RESOLVER_PERSONAL_TAB);
+                strings.add(RESOLVER_WORK_TAB);
+                strings.add(RESOLVER_PERSONAL_TAB_ACCESSIBILITY);
+                strings.add(RESOLVER_WORK_TAB_ACCESSIBILITY);
+                strings.add(RESOLVER_CROSS_PROFILE_BLOCKED_TITLE);
+                strings.add(RESOLVER_CANT_SHARE_WITH_PERSONAL);
+                strings.add(RESOLVER_CANT_SHARE_WITH_WORK);
+                strings.add(RESOLVER_CANT_ACCESS_PERSONAL);
+                strings.add(RESOLVER_CANT_ACCESS_WORK);
+                strings.add(RESOLVER_WORK_PAUSED_TITLE);
+                strings.add(RESOLVER_NO_WORK_APPS);
+                strings.add(RESOLVER_NO_PERSONAL_APPS);
+                strings.add(CANT_ADD_ACCOUNT_MESSAGE);
+                strings.add(PACKAGE_INSTALLED_BY_DO);
+                strings.add(PACKAGE_UPDATED_BY_DO);
+                strings.add(PACKAGE_DELETED_BY_DO);
+                strings.add(UNLAUNCHABLE_APP_WORK_PAUSED_TITLE);
+                strings.add(UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE);
+                strings.add(PROFILE_ENCRYPTED_TITLE);
+                strings.add(PROFILE_ENCRYPTED_DETAIL);
+                strings.add(PROFILE_ENCRYPTED_MESSAGE);
+                strings.add(WORK_PROFILE_BADGED_LABEL);
+                return strings;
+            }
+        }
+
+        /**
+         * Class containing the identifiers used to update device management-related system strings
+         * in the DocumentsUi package.
+         */
+        public static final class DocumentsUi {
+
+            private DocumentsUi() {
+            }
+
+            private static final String PREFIX = "DocumentsUi.";
+
+            /**
+             * Title for error message shown when work profile is turned off.
+             */
+            public static final String WORK_PROFILE_OFF_ERROR_TITLE =
+                    PREFIX + "WORK_PROFILE_OFF_ERROR_TITLE";
+
+            /**
+             * Button text shown when work profile is turned off.
+             */
+            public static final String WORK_PROFILE_OFF_ENABLE_BUTTON =
+                    PREFIX + "WORK_PROFILE_OFF_ENABLE_BUTTON";
+
+            /**
+             * Title for error message shown when a user's IT admin does not allow the user to
+             * select work files from a personal app.
+             */
+            public static final String CANT_SELECT_WORK_FILES_TITLE =
+                    PREFIX + "CANT_SELECT_WORK_FILES_TITLE";
+
+            /**
+             * Message shown when a user's IT admin does not allow the user to select work files
+             * from a personal app.
+             */
+            public static final String CANT_SELECT_WORK_FILES_MESSAGE =
+                    PREFIX + "CANT_SELECT_WORK_FILES_MESSAGE";
+
+            /**
+             * Title for error message shown when a user's IT admin does not allow the user to
+             * select personal files from a work app.
+             */
+            public static final String CANT_SELECT_PERSONAL_FILES_TITLE =
+                    PREFIX + "CANT_SELECT_PERSONAL_FILES_TITLE";
+
+            /**
+             * Message shown when a user's IT admin does not allow the user to select personal files
+             * from a work app.
+             */
+            public static final String CANT_SELECT_PERSONAL_FILES_MESSAGE =
+                    PREFIX + "CANT_SELECT_PERSONAL_FILES_MESSAGE";
+
+            /**
+             * Title for error message shown when a user's IT admin does not allow the user to save
+             * files from their personal profile to their work profile.
+             */
+            public static final String CANT_SAVE_TO_WORK_TITLE =
+                    PREFIX + "CANT_SAVE_TO_WORK_TITLE";
+
+            /**
+             * Message shown when a user's IT admin does not allow the user to save files from their
+             * personal profile to their work profile.
+             */
+            public static final String CANT_SAVE_TO_WORK_MESSAGE =
+                    PREFIX + "CANT_SAVE_TO_WORK_MESSAGE";
+
+            /**
+             * Title for error message shown when a user's IT admin does not allow the user to save
+             * files from their work profile to their personal profile.
+             */
+            public static final String CANT_SAVE_TO_PERSONAL_TITLE =
+                    PREFIX + "CANT_SAVE_TO_PERSONAL_TITLE";
+
+            /**
+             * Message shown when a user's IT admin does not allow the user to save files from their
+             * work profile to their personal profile.
+             */
+            public static final String CANT_SAVE_TO_PERSONAL_MESSAGE =
+                    PREFIX + "CANT_SAVE_TO_PERSONAL_MESSAGE";
+
+            /**
+             * Title for error message shown when a user tries to do something on their work
+             * device, but that action isn't allowed by their IT admin.
+             */
+            public static final String CROSS_PROFILE_NOT_ALLOWED_TITLE =
+                    PREFIX + "CROSS_PROFILE_NOT_ALLOWED_TITLE";
+
+            /**
+             * Message shown when a user tries to do something on their work device, but that action
+             * isn't allowed by their IT admin.
+             */
+            public static final String CROSS_PROFILE_NOT_ALLOWED_MESSAGE =
+                    PREFIX + "CROSS_PROFILE_NOT_ALLOWED_MESSAGE";
+
+            /**
+             * Content description text that's spoken by a screen reader for previewing a work file
+             * before opening it. Accepts file name as a param.
+             */
+            public static final String PREVIEW_WORK_FILE_ACCESSIBILITY =
+                    PREFIX + "PREVIEW_WORK_FILE_ACCESSIBILITY";
+
+            /**
+             * Label for tab and sidebar to indicate personal content.
+             */
+            public static final String PERSONAL_TAB = PREFIX + "PERSONAL_TAB";
+
+            /**
+             * Label for tab and sidebar tab to indicate work content
+             */
+            public static final String WORK_TAB = PREFIX + "WORK_TAB";
+
+            /**
+             * Accessibility label to indicate the subject(e.g. file/folder) is from work profile.
+             */
+            public static final String WORK_ACCESSIBILITY = PREFIX + "WORK_ACCESSIBILITY";
+
+            /**
+             * @hide
+             */
+            static Set<String> buildStringsSet() {
+                Set<String> strings = new HashSet<>();
+                strings.add(WORK_PROFILE_OFF_ERROR_TITLE);
+                strings.add(WORK_PROFILE_OFF_ENABLE_BUTTON);
+                strings.add(CANT_SELECT_WORK_FILES_TITLE);
+                strings.add(CANT_SELECT_WORK_FILES_MESSAGE);
+                strings.add(CANT_SELECT_PERSONAL_FILES_TITLE);
+                strings.add(CANT_SELECT_PERSONAL_FILES_MESSAGE);
+                strings.add(CANT_SAVE_TO_WORK_TITLE);
+                strings.add(CANT_SAVE_TO_WORK_MESSAGE);
+                strings.add(CANT_SAVE_TO_PERSONAL_TITLE);
+                strings.add(CANT_SAVE_TO_PERSONAL_MESSAGE);
+                strings.add(CROSS_PROFILE_NOT_ALLOWED_TITLE);
+                strings.add(CROSS_PROFILE_NOT_ALLOWED_MESSAGE);
+                strings.add(PREVIEW_WORK_FILE_ACCESSIBILITY);
+                strings.add(PERSONAL_TAB);
+                strings.add(WORK_TAB);
+                strings.add(WORK_ACCESSIBILITY);
+                return strings;
+            }
+        }
+
+        /**
+         * Class containing the identifiers used to update device management-related system strings
+         * in the MediaProvider module.
+         */
+        public static final class MediaProvider {
+
+            private MediaProvider() {
+            }
+
+            private static final String PREFIX = "MediaProvider.";
+
+            /**
+             * The text shown to switch to the work profile in PhotoPicker.
+             */
+            public static final String SWITCH_TO_WORK_MESSAGE =
+                    PREFIX + "SWITCH_TO_WORK_MESSAGE";
+
+            /**
+             * The text shown to switch to the personal profile in PhotoPicker.
+             */
+            public static final String SWITCH_TO_PERSONAL_MESSAGE =
+                    PREFIX + "SWITCH_TO_PERSONAL_MESSAGE";
+
+            /**
+             * The title for error dialog in PhotoPicker when the admin blocks cross user
+             * interaction for the intent.
+             */
+            public static final String BLOCKED_BY_ADMIN_TITLE =
+                    PREFIX + "BLOCKED_BY_ADMIN_TITLE";
+
+            /**
+             * The message for error dialog in PhotoPicker when the admin blocks cross user
+             * interaction from the personal profile.
+             */
+            public static final String BLOCKED_FROM_PERSONAL_MESSAGE =
+                    PREFIX + "BLOCKED_FROM_PERSONAL_MESSAGE";
+
+            /**
+             * The message for error dialog in PhotoPicker when the admin blocks cross user
+             * interaction from the work profile.
+             */
+            public static final String BLOCKED_FROM_WORK_MESSAGE =
+                    PREFIX + "BLOCKED_FROM_WORK_MESSAGE";
+
+            /**
+             * The title of the error dialog in PhotoPicker when the user tries to switch to work
+             * content, but work profile is off.
+             */
+            public static final String WORK_PROFILE_PAUSED_TITLE =
+                    PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+            /**
+             * The message of the error dialog in PhotoPicker when the user tries to switch to work
+             * content, but work profile is off.
+             */
+            public static final String WORK_PROFILE_PAUSED_MESSAGE =
+                    PREFIX + "WORK_PROFILE_PAUSED_MESSAGE";
+
+            /**
+             * @hide
+             */
+            static Set<String> buildStringsSet() {
+                Set<String> strings = new HashSet<>();
+                strings.add(SWITCH_TO_WORK_MESSAGE);
+                strings.add(SWITCH_TO_PERSONAL_MESSAGE);
+                strings.add(BLOCKED_BY_ADMIN_TITLE);
+                strings.add(BLOCKED_FROM_PERSONAL_MESSAGE);
+                strings.add(BLOCKED_FROM_WORK_MESSAGE);
+                strings.add(WORK_PROFILE_PAUSED_TITLE);
+                strings.add(WORK_PROFILE_PAUSED_MESSAGE);
+                return strings;
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/admin/DevicePolicyStringResource.aidl b/core/java/android/app/admin/DevicePolicyStringResource.aidl
new file mode 100644
index 0000000..13b0b95
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyStringResource.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable DevicePolicyStringResource;
diff --git a/core/java/android/app/admin/DevicePolicyStringResource.java b/core/java/android/app/admin/DevicePolicyStringResource.java
new file mode 100644
index 0000000..5f09bfd
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyStringResource.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Used to pass in the required information for updating an enterprise string resource using
+ * {@link DevicePolicyManager#setStrings}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DevicePolicyStringResource implements Parcelable {
+    @NonNull private final @DevicePolicyResources.UpdatableStringId String mStringId;
+    private final @StringRes int mCallingPackageResourceId;
+    @NonNull private ParcelableResource mResource;
+
+    /**
+     * Creates an object containing the required information for updating an enterprise string
+     * resource using {@link DevicePolicyManager#setStrings}.
+     *
+     * <p>It will be used to update the string defined by {@code stringId} to the string with ID
+     * {@code callingPackageResourceId} in the calling package</p>
+     *
+     * @param stringId The ID of the string to update.
+     * @param callingPackageResourceId The ID of the {@link StringRes} in the calling package to
+     * use as an updated resource.
+     *
+     * @throws IllegalStateException if the resource with ID {@code callingPackageResourceId}
+     * doesn't exist in the {@code context} package.
+     */
+    public DevicePolicyStringResource(
+            @NonNull Context context,
+            @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+            @StringRes int callingPackageResourceId) {
+        this(stringId, callingPackageResourceId, new ParcelableResource(
+                context, callingPackageResourceId, ParcelableResource.RESOURCE_TYPE_STRING));
+    }
+
+    private DevicePolicyStringResource(
+            @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+            @StringRes int callingPackageResourceId,
+            @NonNull ParcelableResource resource) {
+        Objects.requireNonNull(stringId, "stringId must be provided.");
+        Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+
+        this.mStringId = stringId;
+        this.mCallingPackageResourceId = callingPackageResourceId;
+        this.mResource = resource;
+    }
+
+    /**
+     * Returns the ID of the string to update.
+     */
+    @DevicePolicyResources.UpdatableStringId
+    @NonNull
+    public String getStringId() {
+        return mStringId;
+    }
+
+    /**
+     * Returns the ID of the {@link StringRes} in the calling package to use as an updated
+     * resource.
+     */
+    public int getCallingPackageResourceId() {
+        return mCallingPackageResourceId;
+    }
+
+    /**
+     * Returns the {@link ParcelableResource} of the string.
+     *
+     * @hide
+     */
+    @NonNull
+    public ParcelableResource getResource() {
+        return mResource;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DevicePolicyStringResource other = (DevicePolicyStringResource) o;
+        return mStringId == other.mStringId
+                && mCallingPackageResourceId == other.mCallingPackageResourceId
+                && mResource.equals(other.mResource);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStringId, mCallingPackageResourceId, mResource);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mStringId);
+        dest.writeInt(mCallingPackageResourceId);
+        dest.writeTypedObject(mResource, flags);
+    }
+
+    public static final @NonNull Creator<DevicePolicyStringResource> CREATOR =
+            new Creator<DevicePolicyStringResource>() {
+        @Override
+        public DevicePolicyStringResource createFromParcel(Parcel in) {
+            String stringId = in.readString();
+            int callingPackageResourceId = in.readInt();
+            ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR);
+
+            return new DevicePolicyStringResource(stringId, callingPackageResourceId, resource);
+        }
+
+        @Override
+        public DevicePolicyStringResource[] newArray(int size) {
+            return new DevicePolicyStringResource[size];
+        }
+    };
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b9fcdf5..f663c17 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -17,6 +17,9 @@
 
 package android.app.admin;
 
+import android.app.admin.DevicePolicyDrawableResource;
+import android.app.admin.DevicePolicyStringResource;
+import android.app.admin.ParcelableResource;
 import android.app.admin.NetworkEvent;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
@@ -530,5 +533,20 @@
     boolean isUsbDataSignalingEnabledForUser(int userId);
     boolean canUsbDataSignalingBeDisabled();
 
+    void setMinimumRequiredWifiSecurityLevel(int level);
+    int getMinimumRequiredWifiSecurityLevel();
+
+    void setSsidAllowlist(in List<String> ssids);
+    List<String> getSsidAllowlist();
+    void setSsidDenylist(in List<String> ssids);
+    List<String> getSsidDenylist();
+
     List<UserHandle> listForegroundAffiliatedUsers();
+    void setDrawables(in List<DevicePolicyDrawableResource> drawables);
+    void resetDrawables(in int[] drawableIds);
+    ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource);
+
+    void setStrings(in List<DevicePolicyStringResource> strings);
+    void resetStrings(in String[] stringIds);
+    ParcelableResource getString(String stringId);
 }
diff --git a/core/java/android/app/admin/ParcelableResource.aidl b/core/java/android/app/admin/ParcelableResource.aidl
new file mode 100644
index 0000000..dd2b975
--- /dev/null
+++ b/core/java/android/app/admin/ParcelableResource.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable ParcelableResource;
diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java
new file mode 100644
index 0000000..dba3628
--- /dev/null
+++ b/core/java/android/app/admin/ParcelableResource.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.AnyRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+
+/**
+ * Used to store the required information to load a resource that was updated using
+ * {@link DevicePolicyManager#setDrawables} and {@link DevicePolicyManager#setStrings}.
+ *
+ * @hide
+ */
+public final class ParcelableResource implements Parcelable {
+
+    private static String TAG = "DevicePolicyManager";
+
+    private static final String ATTR_RESOURCE_ID = "resource-id";
+    private static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_RESOURCE_NAME = "resource-name";
+    private static final String ATTR_RESOURCE_TYPE = "resource-type";
+
+    public static final int RESOURCE_TYPE_DRAWABLE = 1;
+    public static final int RESOURCE_TYPE_STRING = 2;
+
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "RESOURCE_TYPE_" }, value = {
+            RESOURCE_TYPE_DRAWABLE,
+            RESOURCE_TYPE_STRING
+    })
+    public @interface ResourceType {}
+
+    private final int mResourceId;
+    @NonNull private final String mPackageName;
+    @NonNull private final String mResourceName;
+    private final int mResourceType;
+
+    /**
+     *
+     * Creates a {@code ParcelableDevicePolicyResource} for the given {@code resourceId} and
+     * verifies that it exists in the package of the given {@code context}.
+     *
+     * @param context for the package containing the {@code resourceId} to use as the updated
+     *                resource
+     * @param resourceId of the resource to use as an updated resource
+     * @param resourceType see {@link ResourceType}
+     */
+    public ParcelableResource(
+            @NonNull Context context, @AnyRes int resourceId, @ResourceType int resourceType)
+            throws IllegalStateException, IllegalArgumentException {
+        Objects.requireNonNull(context, "context must be provided");
+        verifyResourceExistsInCallingPackage(context, resourceId, resourceType);
+
+        this.mResourceId = resourceId;
+        this.mPackageName = context.getResources().getResourcePackageName(resourceId);
+        this.mResourceName = context.getResources().getResourceName(resourceId);
+        this.mResourceType = resourceType;
+    }
+
+    /**
+     * Creates a {@code ParcelableDevicePolicyResource} with the given params, this DOES NOT make
+     * any verifications on whether the given {@code resourceId} actually exists.
+     */
+    private ParcelableResource(
+            @AnyRes int resourceId, @NonNull String packageName, @NonNull String resourceName,
+            @ResourceType int resourceType) {
+        this.mResourceId = resourceId;
+        this.mPackageName = requireNonNull(packageName);
+        this.mResourceName = requireNonNull(resourceName);
+        this.mResourceType = resourceType;
+    }
+
+    private static void verifyResourceExistsInCallingPackage(
+            Context context, @AnyRes int resourceId, @ResourceType int resourceType)
+            throws IllegalStateException, IllegalArgumentException {
+        switch (resourceType) {
+            case RESOURCE_TYPE_DRAWABLE:
+                if (!hasDrawableInCallingPackage(context, resourceId)) {
+                    throw new IllegalStateException(String.format(
+                            "Drawable with id %d doesn't exist in the calling package %s",
+                            resourceId,
+                            context.getPackageName()));
+                }
+                break;
+            case RESOURCE_TYPE_STRING:
+                if (!hasStringInCallingPackage(context, resourceId)) {
+                    throw new IllegalStateException(String.format(
+                            "String with id %d doesn't exist in the calling package %s",
+                            resourceId,
+                            context.getPackageName()));
+                }
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown ResourceType: " + resourceType);
+        }
+    }
+
+    private static boolean hasDrawableInCallingPackage(Context context, @AnyRes int resourceId) {
+        try {
+            return "drawable".equals(context.getResources().getResourceTypeName(resourceId));
+        } catch (Resources.NotFoundException e) {
+            return false;
+        }
+    }
+
+    private static boolean hasStringInCallingPackage(Context context, @AnyRes int resourceId) {
+        try {
+            return "string".equals(context.getResources().getResourceTypeName(resourceId));
+        } catch (Resources.NotFoundException e) {
+            return false;
+        }
+    }
+
+    public @AnyRes int getResourceId() {
+        return mResourceId;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @NonNull
+    public String getResourceName() {
+        return mResourceName;
+    }
+
+    public int getResourceType() {
+        return mResourceType;
+    }
+
+    /**
+     * Loads the drawable with id {@code mResourceId} from {@code mPackageName} using the provided
+     * {@code density} and {@link Resources.Theme} and {@link Resources#getConfiguration} of the
+     * provided {@code context}.
+     *
+     * <p>Returns the default drawable by calling the {@code defaultDrawableLoader} if the updated
+     * drawable was not found or could not be loaded.</p>
+     */
+    @NonNull
+    public Drawable getDrawable(
+            Context context,
+            int density,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        // TODO(b/203548565): properly handle edge case when the device manager role holder is
+        //  unavailable because it's being updated.
+        try {
+            Resources resources = getAppResourcesWithCallersConfiguration(context);
+            verifyResourceName(resources);
+            return resources.getDrawableForDensity(mResourceId, density, context.getTheme());
+        } catch (PackageManager.NameNotFoundException | RuntimeException e) {
+            Slog.e(TAG, "Unable to load drawable resource " + mResourceName, e);
+            return loadDefaultDrawable(defaultDrawableLoader);
+        }
+    }
+
+    /**
+     * Loads the string with id {@code mResourceId} from {@code mPackageName} using the
+     * configuration returned from {@link Resources#getConfiguration} of the provided
+     * {@code context}.
+     *
+     * <p>Returns the default string by calling  {@code defaultStringLoader} if the updated
+     * string was not found or could not be loaded.</p>
+     */
+    @NonNull
+    public String getString(
+            Context context,
+            @NonNull Callable<String> defaultStringLoader) {
+        // TODO(b/203548565): properly handle edge case when the device manager role holder is
+        //  unavailable because it's being updated.
+        try {
+            Resources resources = getAppResourcesWithCallersConfiguration(context);
+            verifyResourceName(resources);
+            return resources.getString(mResourceId);
+        } catch (PackageManager.NameNotFoundException | RuntimeException e) {
+            Slog.e(TAG, "Unable to load string resource " + mResourceName, e);
+            return loadDefaultString(defaultStringLoader);
+        }
+    }
+
+    /**
+     * Loads the string with id {@code mResourceId} from {@code mPackageName} using the
+     * configuration returned from {@link Resources#getConfiguration} of the provided
+     * {@code context}.
+     *
+     * <p>Returns the default string by calling  {@code defaultStringLoader} if the updated
+     * string was not found or could not be loaded.</p>
+     */
+    @Nullable
+    public String getString(
+            Context context,
+            @NonNull Callable<String> defaultStringLoader,
+            @NonNull Object... formatArgs) {
+        // TODO(b/203548565): properly handle edge case when the device manager role holder is
+        //  unavailable because it's being updated.
+        try {
+            Resources resources = getAppResourcesWithCallersConfiguration(context);
+            verifyResourceName(resources);
+            String rawString = resources.getString(mResourceId);
+            return String.format(
+                    context.getResources().getConfiguration().getLocales().get(0),
+                    rawString,
+                    formatArgs);
+        } catch (PackageManager.NameNotFoundException | RuntimeException e) {
+            Slog.e(TAG, "Unable to load string resource " + mResourceName, e);
+            return loadDefaultString(defaultStringLoader);
+        }
+    }
+
+    private Resources getAppResourcesWithCallersConfiguration(Context context)
+            throws PackageManager.NameNotFoundException {
+        PackageManager pm = context.getPackageManager();
+        ApplicationInfo ai = pm.getApplicationInfo(
+                mPackageName,
+                PackageManager.MATCH_UNINSTALLED_PACKAGES
+                        | PackageManager.GET_SHARED_LIBRARY_FILES);
+        return pm.getResourcesForApplication(ai, context.getResources().getConfiguration());
+    }
+
+    private void verifyResourceName(Resources resources) throws IllegalStateException {
+        String name = resources.getResourceName(mResourceId);
+        if (!mResourceName.equals(name)) {
+            throw new IllegalStateException(String.format("Current resource name %s for resource id"
+                            + " %d has changed from the previously stored resource name %s.",
+                    name, mResourceId, mResourceName));
+        }
+    }
+
+    /**
+     * returns the {@link Drawable} loaded from calling {@code defaultDrawableLoader}.
+     */
+    @NonNull
+    public static Drawable loadDefaultDrawable(@NonNull Callable<Drawable> defaultDrawableLoader) {
+        try {
+            Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
+
+            Drawable drawable = defaultDrawableLoader.call();
+            Objects.requireNonNull(drawable, "defaultDrawable can't be null");
+
+            return drawable;
+        } catch (NullPointerException rethrown) {
+            throw rethrown;
+        } catch (Exception e) {
+            throw new RuntimeException("Couldn't load default drawable: ", e);
+        }
+    }
+
+    /**
+     * returns the {@link String} loaded from calling {@code defaultStringLoader}.
+     */
+    @NonNull
+    public static String loadDefaultString(@NonNull Callable<String> defaultStringLoader) {
+        try {
+            Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
+
+            String string = defaultStringLoader.call();
+            Objects.requireNonNull(string, "defaultString can't be null");
+
+            return string;
+        } catch (NullPointerException rethrown) {
+            throw rethrown;
+        } catch (Exception e) {
+            throw new RuntimeException("Couldn't load default string: ", e);
+        }
+    }
+
+    /**
+     * Writes the content of the current {@code ParcelableDevicePolicyResource} to the xml file
+     * specified by {@code xmlSerializer}.
+     */
+    public void writeToXmlFile(TypedXmlSerializer xmlSerializer) throws IOException {
+        xmlSerializer.attributeInt(/* namespace= */ null, ATTR_RESOURCE_ID, mResourceId);
+        xmlSerializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
+        xmlSerializer.attribute(/* namespace= */ null, ATTR_RESOURCE_NAME, mResourceName);
+        xmlSerializer.attributeInt(/* namespace= */ null, ATTR_RESOURCE_TYPE, mResourceType);
+    }
+
+    /**
+     * Creates a new {@code ParcelableDevicePolicyResource} using the content of
+     * {@code xmlPullParser}.
+     */
+    public static ParcelableResource createFromXml(TypedXmlPullParser xmlPullParser)
+            throws XmlPullParserException, IOException {
+        int resourceId = xmlPullParser.getAttributeInt(/* namespace= */ null, ATTR_RESOURCE_ID);
+        String packageName = xmlPullParser.getAttributeValue(
+                /* namespace= */ null, ATTR_PACKAGE_NAME);
+        String resourceName = xmlPullParser.getAttributeValue(
+                /* namespace= */ null, ATTR_RESOURCE_NAME);
+        int resourceType = xmlPullParser.getAttributeInt(
+                /* namespace= */ null, ATTR_RESOURCE_TYPE);
+
+        return new ParcelableResource(
+                resourceId, packageName, resourceName, resourceType);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ParcelableResource other = (ParcelableResource) o;
+        return mResourceId == other.mResourceId
+                && mPackageName.equals(other.mPackageName)
+                && mResourceName.equals(other.mResourceName)
+                && mResourceType == other.mResourceType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mResourceId, mPackageName, mResourceName, mResourceType);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mResourceId);
+        dest.writeString(mPackageName);
+        dest.writeString(mResourceName);
+        dest.writeInt(mResourceType);
+    }
+
+    public static final @NonNull Creator<ParcelableResource> CREATOR =
+            new Creator<ParcelableResource>() {
+                @Override
+                public ParcelableResource createFromParcel(Parcel in) {
+                    int resourceId = in.readInt();
+                    String packageName = in.readString();
+                    String resourceName = in.readString();
+                    int resourceType = in.readInt();
+
+                    return new ParcelableResource(
+                            resourceId, packageName, resourceName, resourceType);
+                }
+
+                @Override
+                public ParcelableResource[] newArray(int size) {
+                    return new ParcelableResource[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
new file mode 100644
index 0000000..3715017
--- /dev/null
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Used to indicate the Wi-Fi SSID restriction policy the network must satisfy
+ * in order to be eligible for a connection.
+ *
+ * If the policy type is a denylist, the device may not connect to networks on the denylist.
+ * If the policy type is an allowlist, the device may only connect to networks on the allowlist.
+ * Admin configured networks are not exempt from this restriction.
+ * This policy only prohibits connecting to a restricted network and
+ * does not affect adding a restricted network.
+ * If the current network is present in the denylist or not present in the allowlist,
+ * it will be disconnected.
+ */
+public final class WifiSsidPolicy implements Parcelable {
+    /**
+     * SSID policy type indicator for {@link WifiSsidPolicy}.
+     *
+     * <p> When returned from {@link WifiSsidPolicy#getPolicyType()}, the constant
+     * indicates that the SSID policy type is an allowlist.
+     *
+     * @see #WIFI_SSID_POLICY_TYPE_DENYLIST
+     */
+    public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0;
+
+    /**
+     * SSID policy type indicator for {@link WifiSsidPolicy}.
+     *
+     * <p> When returned from {@link WifiSsidPolicy#getPolicyType()}, the constant
+     * indicates that the SSID policy type is a denylist.
+     *
+     * @see #WIFI_SSID_POLICY_TYPE_ALLOWLIST
+     */
+    public static final int WIFI_SSID_POLICY_TYPE_DENYLIST = 1;
+
+    /**
+     * Possible SSID policy types
+     *
+     * @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"WIFI_SSID_POLICY_TYPE_"}, value = {
+            WIFI_SSID_POLICY_TYPE_ALLOWLIST,
+            WIFI_SSID_POLICY_TYPE_DENYLIST})
+    public @interface WifiSsidPolicyType {}
+
+    private @WifiSsidPolicyType int mPolicyType;
+    private ArraySet<String> mSsids;
+
+    private WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<String> ssids) {
+        mPolicyType = policyType;
+        mSsids = new ArraySet<>(ssids);
+    }
+
+    private WifiSsidPolicy(Parcel in) {
+        mPolicyType = in.readInt();
+        mSsids = (ArraySet<String>) in.readArraySet(null);
+    }
+    /**
+     * Create the allowlist Wi-Fi SSID Policy.
+     *
+     * @param ssids allowlist of SSIDs in UTF-8 without double quotes format
+     * @throws IllegalArgumentException if the input ssids list is empty
+     */
+    @NonNull
+    public static WifiSsidPolicy createAllowlistPolicy(@NonNull Set<String> ssids) {
+        if (ssids.isEmpty()) {
+            throw new IllegalArgumentException("SSID list cannot be empty");
+        }
+        return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
+    }
+
+    /**
+     * Create the denylist Wi-Fi SSID Policy.
+     *
+     * @param ssids denylist of SSIDs in UTF-8 without double quotes format
+     * @throws IllegalArgumentException if the input ssids list is empty
+     */
+    @NonNull
+    public static WifiSsidPolicy createDenylistPolicy(@NonNull Set<String> ssids) {
+        if (ssids.isEmpty()) {
+            throw new IllegalArgumentException("SSID list cannot be empty");
+        }
+        return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
+    }
+
+    /**
+     * Returns the set of SSIDs in UTF-8 without double quotes format.
+     */
+    @NonNull
+    public Set<String> getSsids() {
+        return mSsids;
+    }
+
+    /**
+     * Returns the policy type.
+     */
+    public @WifiSsidPolicyType int getPolicyType() {
+        return mPolicyType;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<WifiSsidPolicy> CREATOR = new Creator<WifiSsidPolicy>() {
+        @Override
+        public WifiSsidPolicy createFromParcel(Parcel source) {
+            return new WifiSsidPolicy(source);
+        }
+
+        @Override
+        public WifiSsidPolicy[] newArray(int size) {
+            return new WifiSsidPolicy[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPolicyType);
+        dest.writeArraySet(mSsids);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.aidl b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
new file mode 100644
index 0000000..0965b1a
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+parcelable AmbientContextEvent;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
new file mode 100644
index 0000000..11e695ad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+
+
+/**
+ * Represents a detected ambient event. Each event has a type, start time, end time,
+ * plus some optional data.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(
+        genBuilder = true,
+        genConstructor = false,
+        genHiddenConstDefs = true,
+        genParcelable = true,
+        genToString = true
+)
+public final class AmbientContextEvent implements Parcelable {
+    /**
+     * The integer indicating an unknown event was detected.
+     */
+    public static final int EVENT_UNKNOWN = 0;
+
+    /**
+     * The integer indicating a cough event was detected.
+     */
+    public static final int EVENT_COUGH = 1;
+
+    /**
+     * The integer indicating a snore event was detected.
+     */
+    public static final int EVENT_SNORE = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "EVENT_" }, value = {
+            EVENT_UNKNOWN,
+            EVENT_COUGH,
+            EVENT_SNORE,
+    }) public @interface EventCode {}
+
+    /** The integer indicating an unknown level. */
+    public static final int LEVEL_UNKNOWN = 0;
+
+    /** The integer indicating a low level. */
+    public static final int LEVEL_LOW = 1;
+
+    /** The integer indicating a medium low level. */
+    public static final int LEVEL_MEDIUM_LOW = 2;
+
+    /** The integer indicating a medium Level. */
+    public static final int LEVEL_MEDIUM = 3;
+
+    /** The integer indicating a medium high level. */
+    public static final int LEVEL_MEDIUM_HIGH = 4;
+
+    /** The integer indicating a high level. */
+    public static final int LEVEL_HIGH = 5;
+
+    /** @hide */
+    @IntDef(prefix = {"LEVEL_"}, value = {
+            LEVEL_UNKNOWN,
+            LEVEL_LOW,
+            LEVEL_MEDIUM_LOW,
+            LEVEL_MEDIUM,
+            LEVEL_MEDIUM_HIGH,
+            LEVEL_HIGH
+    }) public @interface LevelValue {}
+
+    @EventCode private final int mEventType;
+    private static int defaultEventType() {
+        return EVENT_UNKNOWN;
+    }
+
+    /** Event start time */
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
+    @NonNull private final Instant mStartTime;
+    @NonNull private static Instant defaultStartTime() {
+        return Instant.MIN;
+    }
+
+    /** Event end time */
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
+    @NonNull private final Instant mEndTime;
+    @NonNull private static Instant defaultEndTime() {
+        return Instant.MAX;
+    }
+
+    /**
+     * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+     * Apps can add post-processing filter using this value if needed.
+     */
+    @LevelValue private final int mConfidenceLevel;
+    private static int defaultConfidenceLevel() {
+        return LEVEL_UNKNOWN;
+    }
+
+    /**
+     * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+     * Apps can add post-processing filter using this value if needed.
+     */
+    @LevelValue private final int mDensityLevel;
+    private static int defaultDensityLevel() {
+        return LEVEL_UNKNOWN;
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /** @hide */
+    @IntDef(prefix = "EVENT_", value = {
+        EVENT_UNKNOWN,
+        EVENT_COUGH,
+        EVENT_SNORE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Event {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String eventToString(@Event int value) {
+        switch (value) {
+            case EVENT_UNKNOWN:
+                    return "EVENT_UNKNOWN";
+            case EVENT_COUGH:
+                    return "EVENT_COUGH";
+            case EVENT_SNORE:
+                    return "EVENT_SNORE";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    /** @hide */
+    @IntDef(prefix = "LEVEL_", value = {
+        LEVEL_UNKNOWN,
+        LEVEL_LOW,
+        LEVEL_MEDIUM_LOW,
+        LEVEL_MEDIUM,
+        LEVEL_MEDIUM_HIGH,
+        LEVEL_HIGH
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Level {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String levelToString(@Level int value) {
+        switch (value) {
+            case LEVEL_UNKNOWN:
+                    return "LEVEL_UNKNOWN";
+            case LEVEL_LOW:
+                    return "LEVEL_LOW";
+            case LEVEL_MEDIUM_LOW:
+                    return "LEVEL_MEDIUM_LOW";
+            case LEVEL_MEDIUM:
+                    return "LEVEL_MEDIUM";
+            case LEVEL_MEDIUM_HIGH:
+                    return "LEVEL_MEDIUM_HIGH";
+            case LEVEL_HIGH:
+                    return "LEVEL_HIGH";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    @DataClass.Generated.Member
+    /* package-private */ AmbientContextEvent(
+            @EventCode int eventType,
+            @NonNull Instant startTime,
+            @NonNull Instant endTime,
+            @LevelValue int confidenceLevel,
+            @LevelValue int densityLevel) {
+        this.mEventType = eventType;
+        com.android.internal.util.AnnotationValidations.validate(
+                EventCode.class, null, mEventType);
+        this.mStartTime = startTime;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mStartTime);
+        this.mEndTime = endTime;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mEndTime);
+        this.mConfidenceLevel = confidenceLevel;
+        com.android.internal.util.AnnotationValidations.validate(
+                LevelValue.class, null, mConfidenceLevel);
+        this.mDensityLevel = densityLevel;
+        com.android.internal.util.AnnotationValidations.validate(
+                LevelValue.class, null, mDensityLevel);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @EventCode int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * Event start time
+     */
+    @DataClass.Generated.Member
+    public @NonNull Instant getStartTime() {
+        return mStartTime;
+    }
+
+    /**
+     * Event end time
+     */
+    @DataClass.Generated.Member
+    public @NonNull Instant getEndTime() {
+        return mEndTime;
+    }
+
+    /**
+     * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+     * Apps can add post-processing filter using this value if needed.
+     */
+    @DataClass.Generated.Member
+    public @LevelValue int getConfidenceLevel() {
+        return mConfidenceLevel;
+    }
+
+    /**
+     * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+     * Apps can add post-processing filter using this value if needed.
+     */
+    @DataClass.Generated.Member
+    public @LevelValue int getDensityLevel() {
+        return mDensityLevel;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "AmbientContextEvent { " +
+                "eventType = " + mEventType + ", " +
+                "startTime = " + mStartTime + ", " +
+                "endTime = " + mEndTime + ", " +
+                "confidenceLevel = " + mConfidenceLevel + ", " +
+                "densityLevel = " + mDensityLevel +
+        " }";
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<Instant> sParcellingForStartTime =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInstant.class);
+    static {
+        if (sParcellingForStartTime == null) {
+            sParcellingForStartTime = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInstant());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<Instant> sParcellingForEndTime =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInstant.class);
+    static {
+        if (sParcellingForEndTime == null) {
+            sParcellingForEndTime = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInstant());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeInt(mEventType);
+        sParcellingForStartTime.parcel(mStartTime, dest, flags);
+        sParcellingForEndTime.parcel(mEndTime, dest, flags);
+        dest.writeInt(mConfidenceLevel);
+        dest.writeInt(mDensityLevel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ AmbientContextEvent(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int eventType = in.readInt();
+        Instant startTime = sParcellingForStartTime.unparcel(in);
+        Instant endTime = sParcellingForEndTime.unparcel(in);
+        int confidenceLevel = in.readInt();
+        int densityLevel = in.readInt();
+
+        this.mEventType = eventType;
+        com.android.internal.util.AnnotationValidations.validate(
+                EventCode.class, null, mEventType);
+        this.mStartTime = startTime;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mStartTime);
+        this.mEndTime = endTime;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mEndTime);
+        this.mConfidenceLevel = confidenceLevel;
+        com.android.internal.util.AnnotationValidations.validate(
+                LevelValue.class, null, mConfidenceLevel);
+        this.mDensityLevel = densityLevel;
+        com.android.internal.util.AnnotationValidations.validate(
+                LevelValue.class, null, mDensityLevel);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<AmbientContextEvent> CREATOR
+            = new Parcelable.Creator<AmbientContextEvent>() {
+        @Override
+        public AmbientContextEvent[] newArray(int size) {
+            return new AmbientContextEvent[size];
+        }
+
+        @Override
+        public AmbientContextEvent createFromParcel(@NonNull android.os.Parcel in) {
+            return new AmbientContextEvent(in);
+        }
+    };
+
+    /**
+     * A builder for {@link AmbientContextEvent}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @EventCode int mEventType;
+        private @NonNull Instant mStartTime;
+        private @NonNull Instant mEndTime;
+        private @LevelValue int mConfidenceLevel;
+        private @LevelValue int mDensityLevel;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        @DataClass.Generated.Member
+        public @NonNull Builder setEventType(@EventCode int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mEventType = value;
+            return this;
+        }
+
+        /**
+         * Event start time
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setStartTime(@NonNull Instant value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mStartTime = value;
+            return this;
+        }
+
+        /**
+         * Event end time
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setEndTime(@NonNull Instant value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mEndTime = value;
+            return this;
+        }
+
+        /**
+         * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+         * Apps can add post-processing filter using this value if needed.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setConfidenceLevel(@LevelValue int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mConfidenceLevel = value;
+            return this;
+        }
+
+        /**
+         * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+         * Apps can add post-processing filter using this value if needed.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDensityLevel(@LevelValue int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mDensityLevel = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull AmbientContextEvent build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mEventType = defaultEventType();
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mStartTime = defaultStartTime();
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mEndTime = defaultEndTime();
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mConfidenceLevel = defaultConfidenceLevel();
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mDensityLevel = defaultDensityLevel();
+            }
+            AmbientContextEvent o = new AmbientContextEvent(
+                    mEventType,
+                    mStartTime,
+                    mEndTime,
+                    mConfidenceLevel,
+                    mDensityLevel);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x20) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1642040319323L,
+            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 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 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()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
new file mode 100644
index 0000000..e24c6ad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+parcelable AmbientContextEventRequest;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
new file mode 100644
index 0000000..82b16a2
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Represents the request for ambient event detection.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextEventRequest implements Parcelable {
+    @NonNull private final Set<Integer> mEventTypes;
+    @NonNull private final PersistableBundle mOptions;
+
+    AmbientContextEventRequest(
+            @NonNull Set<Integer> eventTypes,
+            @NonNull PersistableBundle options) {
+        this.mEventTypes = eventTypes;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mEventTypes);
+        this.mOptions = options;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mOptions);
+    }
+
+    /**
+     * The event types to detect.
+     */
+    public @NonNull Set<Integer> getEventTypes() {
+        return mEventTypes;
+    }
+
+    /**
+     * Optional detection options.
+     */
+    public @NonNull PersistableBundle getOptions() {
+        return mOptions;
+    }
+
+    @Override
+    public String toString() {
+        return "AmbientContextEventRequest { " + "eventTypes = " + mEventTypes + ", "
+                + "options = " + mOptions + " }";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeArraySet(new ArraySet<>(mEventTypes));
+        dest.writeTypedObject(mOptions, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    AmbientContextEventRequest(@NonNull Parcel in) {
+        Set<Integer> eventTypes = (Set<Integer>) in.readArraySet(Integer.class.getClassLoader());
+        PersistableBundle options = (PersistableBundle) in.readTypedObject(
+                PersistableBundle.CREATOR);
+
+        this.mEventTypes = eventTypes;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mEventTypes);
+        this.mOptions = options;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mOptions);
+    }
+
+    public static final @NonNull Parcelable.Creator<AmbientContextEventRequest> CREATOR =
+            new Parcelable.Creator<AmbientContextEventRequest>() {
+        @Override
+        public AmbientContextEventRequest[] newArray(int size) {
+            return new AmbientContextEventRequest[size];
+        }
+
+        @Override
+        public AmbientContextEventRequest createFromParcel(@NonNull Parcel in) {
+            return new AmbientContextEventRequest(in);
+        }
+    };
+
+    /**
+     * A builder for {@link AmbientContextEventRequest}
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static final class Builder {
+        private @NonNull Set<Integer> mEventTypes;
+        private @NonNull PersistableBundle mOptions;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Add an event type to detect.
+         */
+        public @NonNull Builder addEventType(@AmbientContextEvent.EventCode int value) {
+            checkNotUsed();
+            if (mEventTypes == null) {
+                mBuilderFieldsSet |= 0x1;
+                mEventTypes = new HashSet<>();
+            }
+            mEventTypes.add(value);
+            return this;
+        }
+
+        /**
+         * Optional detection options.
+         */
+        public @NonNull Builder setOptions(@NonNull PersistableBundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mOptions = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull AmbientContextEventRequest build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mEventTypes = new HashSet<>();
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mOptions = new PersistableBundle();
+            }
+            AmbientContextEventRequest o = new AmbientContextEventRequest(
+                    mEventTypes,
+                    mOptions);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
new file mode 100644
index 0000000..4dc6466
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+parcelable AmbientContextEventResponse;
\ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
new file mode 100644
index 0000000..472a78b
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a response from the {@code AmbientContextEvent} service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextEventResponse implements Parcelable {
+    /**
+     * An unknown status.
+     */
+    public static final int STATUS_UNKNOWN = 0;
+    /**
+     * The value of the status code that indicates success.
+     */
+    public static final int STATUS_SUCCESS = 1;
+    /**
+     * The value of the status code that indicates one or more of the
+     * requested events are not supported.
+     */
+    public static final int STATUS_NOT_SUPPORTED = 2;
+    /**
+     * The value of the status code that indicates service not available.
+     */
+    public static final int STATUS_SERVICE_UNAVAILABLE = 3;
+    /**
+     * The value of the status code that microphone is disabled.
+     */
+    public static final int STATUS_MICROPHONE_DISABLED = 4;
+    /**
+     * The value of the status code that the app is not granted access.
+     */
+    public static final int STATUS_ACCESS_DENIED = 5;
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN,
+            STATUS_SUCCESS,
+            STATUS_NOT_SUPPORTED,
+            STATUS_SERVICE_UNAVAILABLE,
+            STATUS_MICROPHONE_DISABLED,
+            STATUS_ACCESS_DENIED
+    }) public @interface StatusCode {}
+
+    @StatusCode private final int mStatusCode;
+    @NonNull private final List<AmbientContextEvent> mEvents;
+    @NonNull private final String mPackageName;
+    @Nullable private final PendingIntent mActionPendingIntent;
+
+    /** @hide */
+    public static String statusToString(@StatusCode int value) {
+        switch (value) {
+            case STATUS_UNKNOWN:
+                return "STATUS_UNKNOWN";
+            case STATUS_SUCCESS:
+                return "STATUS_SUCCESS";
+            case STATUS_NOT_SUPPORTED:
+                return "STATUS_NOT_SUPPORTED";
+            case STATUS_SERVICE_UNAVAILABLE:
+                return "STATUS_SERVICE_UNAVAILABLE";
+            case STATUS_MICROPHONE_DISABLED:
+                return "STATUS_MICROPHONE_DISABLED";
+            case STATUS_ACCESS_DENIED:
+                return "STATUS_ACCESS_DENIED";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    AmbientContextEventResponse(
+            @StatusCode int statusCode,
+            @NonNull List<AmbientContextEvent> events,
+            @NonNull String packageName,
+            @Nullable PendingIntent actionPendingIntent) {
+        this.mStatusCode = statusCode;
+        AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
+        this.mEvents = events;
+        AnnotationValidations.validate(NonNull.class, null, mEvents);
+        this.mPackageName = packageName;
+        AnnotationValidations.validate(NonNull.class, null, mPackageName);
+        this.mActionPendingIntent = actionPendingIntent;
+    }
+
+    /**
+     * The status of the response.
+     */
+    public @StatusCode int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /**
+     * The detected event.
+     */
+    public @NonNull List<AmbientContextEvent> getEvents() {
+        return mEvents;
+    }
+
+    /**
+     * The package to deliver the response to.
+     */
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * A {@link PendingIntent} that the client should call to allow further actions by user.
+     * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to the
+     * grant access activity.
+     */
+    public @Nullable PendingIntent getActionPendingIntent() {
+        return mActionPendingIntent;
+    }
+
+    @Override
+    public String toString() {
+        return "AmbientContextEventResponse { " + "statusCode = " + mStatusCode + ", "
+                + "events = " + mEvents + ", " + "packageName = " + mPackageName + ", "
+                + "callbackPendingIntent = " + mActionPendingIntent + " }";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        byte flg = 0;
+        if (mActionPendingIntent != null) flg |= 0x8;
+        dest.writeByte(flg);
+        dest.writeInt(mStatusCode);
+        dest.writeParcelableList(mEvents, flags);
+        dest.writeString(mPackageName);
+        if (mActionPendingIntent != null) dest.writeTypedObject(mActionPendingIntent, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    AmbientContextEventResponse(@NonNull android.os.Parcel in) {
+        byte flg = in.readByte();
+        int statusCode = in.readInt();
+        List<AmbientContextEvent> events = new ArrayList<>();
+        in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
+                AmbientContextEvent.class);
+        String packageName = in.readString();
+        PendingIntent callbackPendingIntent = (flg & 0x8) == 0 ? null
+                : (PendingIntent) in.readTypedObject(PendingIntent.CREATOR);
+
+        this.mStatusCode = statusCode;
+        AnnotationValidations.validate(
+                StatusCode.class, null, mStatusCode);
+        this.mEvents = events;
+        AnnotationValidations.validate(
+                NonNull.class, null, mEvents);
+        this.mPackageName = packageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mActionPendingIntent = callbackPendingIntent;
+    }
+
+    public static final @NonNull Parcelable.Creator<AmbientContextEventResponse> CREATOR =
+            new Parcelable.Creator<AmbientContextEventResponse>() {
+        @Override
+        public AmbientContextEventResponse[] newArray(int size) {
+            return new AmbientContextEventResponse[size];
+        }
+
+        @Override
+        public AmbientContextEventResponse createFromParcel(@NonNull android.os.Parcel in) {
+            return new AmbientContextEventResponse(in);
+        }
+    };
+
+    /**
+     * A builder for {@link AmbientContextEventResponse}
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static final class Builder {
+        private @StatusCode int mStatusCode;
+        private @NonNull List<AmbientContextEvent> mEvents;
+        private @NonNull String mPackageName;
+        private @Nullable PendingIntent mCallbackPendingIntent;
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The status of the response.
+         */
+        public @NonNull Builder setStatusCode(@StatusCode int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mStatusCode = value;
+            return this;
+        }
+
+        /**
+         * Adds an event to the builder.
+         */
+        public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
+            checkNotUsed();
+            if (mEvents == null) {
+                mBuilderFieldsSet |= 0x2;
+                mEvents = new ArrayList<>();
+            }
+            mEvents.add(value);
+            return this;
+        }
+
+        /**
+         * The package to deliver the response to.
+         */
+        public @NonNull Builder setPackageName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mPackageName = value;
+            return this;
+        }
+
+        /**
+         * A {@link PendingIntent} that the client should call to allow further actions by user.
+         * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to
+         * the grant access activity.
+         */
+        public @NonNull Builder setActionPendingIntent(@NonNull PendingIntent value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mCallbackPendingIntent = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull AmbientContextEventResponse build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mStatusCode = STATUS_UNKNOWN;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mEvents = new ArrayList<>();
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mPackageName = "";
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mCallbackPendingIntent = null;
+            }
+            AmbientContextEventResponse o = new AmbientContextEventResponse(
+                    mStatusCode,
+                    mEvents,
+                    mPackageName,
+                    mCallbackPendingIntent);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
new file mode 100644
index 0000000..6841d1b
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Allows granted apps to register for particular pre-defined {@link AmbientContextEvent}s.
+ * After successful registration, the app receives a callback on the provided {@link PendingIntent}
+ * when the requested event is detected.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ *     // Create request
+ *     AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ *         .addEventType(AmbientContextEvent.EVENT_COUGH)
+ *         .addEventTYpe(AmbientContextEvent.EVENT_SNORE)
+ *         .build();
+ *     // Create PendingIntent
+ *     Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
+ *         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ *     PendingIntent pendingIntent = PendingIntents.getBroadcastMutable(context, 0, intent, 0);
+ *     // Register for events
+ *     AmbientContextManager ambientContextManager =
+ *         context.getSystemService(AmbientContextManager.class);
+ *    ambientContextManager.registerObserver(request, pendingIntent);
+ *
+ *    // Handle the callback intent in your receiver
+ *    {@literal @}Override
+ *    protected void onReceive(Context context, Intent intent) {
+ *      AmbientContextEventResponse response =
+ *          AmbientContextManager.getResponseFromIntent(intent);
+ *      if (response != null) {
+ *        if (response.getStatusCode() == AmbientContextEventResponse.STATUS_SUCCESS) {
+ *          // Do something useful with response.getEvent()
+ *        } else if (response.getStatusCode() == AmbientContextEventResponse.STATUS_ACCESS_DENIED) {
+ *          // Redirect users to grant access
+ *          PendingIntent callbackPendingIntent = response.getCallbackPendingIntent();
+ *          if (callbackPendingIntent != null) {
+ *            callbackPendingIntent.send();
+ *          }
+ *        } else ...
+ *      }
+ *    }
+ * </code></pre>
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.AMBIENT_CONTEXT_SERVICE)
+public final class AmbientContextManager {
+
+    /**
+     * The key of an Intent extra indicating the response.
+     */
+    public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE =
+            "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+
+    /**
+     * Allows clients to retrieve the response from the intent.
+     * @param intent received from the PendingIntent callback
+     *
+     * @return the AmbientContextEventResponse, or null if not present
+     */
+    @Nullable
+    public static AmbientContextEventResponse getResponseFromIntent(
+            @NonNull Intent intent) {
+        if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE)) {
+            return intent.getParcelableExtra(EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE);
+        } else {
+            return null;
+        }
+    }
+
+    private final Context mContext;
+    private final IAmbientContextEventObserver mService;
+
+    /**
+     * {@hide}
+     */
+    public AmbientContextManager(Context context, IAmbientContextEventObserver service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Allows app to register as a {@link AmbientContextEvent} observer. The
+     * observer receives a callback on the provided {@link PendingIntent} when the requested
+     * event is detected. Registering another observer from the same package that has already been
+     * registered will override the previous observer.
+     *
+     * @param request The request with events to observe.
+     * @param pendingIntent A mutable {@link PendingIntent} that will be dispatched when any
+     *                     requested event is detected.
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+    public void registerObserver(
+            @NonNull AmbientContextEventRequest request,
+            @NonNull PendingIntent pendingIntent) {
+        Preconditions.checkArgument(!pendingIntent.isImmutable());
+        try {
+            mService.registerObserver(request, pendingIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters the requesting app as an {@code AmbientContextEvent} observer. Unregistering an
+     * observer that was already unregistered or never registered will have no effect.
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+    public void unregisterObserver() {
+        try {
+            mService.unregisterObserver(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
new file mode 100644
index 0000000..9032fe1
--- /dev/null
+++ b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+
+/**
+ * Interface for an AmbientContextEventManager that provides access to AmbientContextEvents.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextEventObserver {
+    void registerObserver(in AmbientContextEventRequest request, in PendingIntent pendingIntent);
+    void unregisterObserver(in String callingPackage);
+}
\ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/OWNERS b/core/java/android/app/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/core/java/android/app/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/app/communal/CommunalManager.java b/core/java/android/app/communal/CommunalManager.java
deleted file mode 100644
index 22f07693..0000000
--- a/core/java/android/app/communal/CommunalManager.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.communal;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresFeature;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.annotation.TestApi;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
-import android.compat.annotation.Overridable;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-
-import java.util.concurrent.Executor;
-
-/**
- * System private class for talking with the
- * {@link com.android.server.communal.CommunalManagerService} that handles communal mode state.
- *
- * @hide
- */
-@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
-@SystemService(Context.COMMUNAL_SERVICE)
-@RequiresFeature(PackageManager.FEATURE_COMMUNAL_MODE)
-public final class CommunalManager {
-    private final ICommunalManager mService;
-    private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
-
-    /**
-     * This change id is used to annotate packages which can run in communal mode by default,
-     * without requiring user opt-in.
-     *
-     * @hide
-     */
-    @ChangeId
-    @Overridable
-    @Disabled
-    public static final long ALLOW_COMMUNAL_MODE_BY_DEFAULT = 203673428L;
-
-    /**
-     * This change id is used to annotate packages which are allowed to run in communal mode.
-     *
-     * @hide
-     */
-    @ChangeId
-    @Overridable
-    @Disabled
-    public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
-
-    /** @hide */
-    public CommunalManager(ICommunalManager service) {
-        mService = service;
-        mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
-    }
-
-    /**
-     * Updates whether or not the communal view is currently showing over the lockscreen.
-     *
-     * @param isShowing Whether communal view is showing.
-     *
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
-    public void setCommunalViewShowing(boolean isShowing) {
-        try {
-            mService.setCommunalViewShowing(isShowing);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Checks whether or not the communal view is currently showing over the lockscreen.
-     */
-    @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
-    public boolean isCommunalMode() {
-        try {
-            return mService.isCommunalMode();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Listener for communal state changes.
-     */
-    @FunctionalInterface
-    public interface CommunalModeListener {
-        /**
-         * Callback function that executes when the communal state changes.
-         */
-        void onCommunalModeChanged(boolean isCommunalMode);
-    }
-
-    /**
-     * Registers a callback to execute when the communal state changes.
-     *
-     * @param listener The listener to add to receive communal state changes.
-     * @param executor {@link Executor} to dispatch to. To dispatch the callback to the main
-     *                 thread of your application, use
-     *                 {@link android.content.Context#getMainExecutor()}.
-     */
-    @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
-    public void addCommunalModeListener(@NonNull Executor executor,
-            @NonNull CommunalModeListener listener) {
-        synchronized (mCommunalModeListeners) {
-            try {
-                ICommunalModeListener iListener = new ICommunalModeListener.Stub() {
-                    @Override
-                    public void onCommunalModeChanged(boolean isCommunalMode) {
-                        executor.execute(() -> listener.onCommunalModeChanged(isCommunalMode));
-                    }
-                };
-                mService.addCommunalModeListener(iListener);
-                mCommunalModeListeners.put(listener, iListener);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
-     * Unregisters a callback that executes when communal state changes.
-     */
-    @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
-    public void removeCommunalModeListener(@NonNull CommunalModeListener listener) {
-        synchronized (mCommunalModeListeners) {
-            ICommunalModeListener iListener = mCommunalModeListeners.get(listener);
-            if (iListener != null) {
-                try {
-                    mService.removeCommunalModeListener(iListener);
-                    mCommunalModeListeners.remove(listener);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-        }
-    }
-}
diff --git a/core/java/android/app/communal/ICommunalManager.aidl b/core/java/android/app/communal/ICommunalManager.aidl
deleted file mode 100644
index 869891e..0000000
--- a/core/java/android/app/communal/ICommunalManager.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.communal;
-
-import android.app.communal.ICommunalModeListener;
-
-/**
- * System private API for talking with the communal manager service that handles communal mode
- * state.
- *
- * @hide
- */
-interface ICommunalManager {
-    oneway void setCommunalViewShowing(boolean isShowing);
-    boolean isCommunalMode();
-    void addCommunalModeListener(in ICommunalModeListener listener);
-    void removeCommunalModeListener(in ICommunalModeListener listener);
-}
\ No newline at end of file
diff --git a/core/java/android/app/communal/ICommunalModeListener.aidl b/core/java/android/app/communal/ICommunalModeListener.aidl
deleted file mode 100644
index 006e782..0000000
--- a/core/java/android/app/communal/ICommunalModeListener.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.communal;
-
-/**
- * System private API to be notified about communal mode changes.
- *
- * @hide
- */
-oneway interface ICommunalModeListener {
-    void onCommunalModeChanged(boolean isCommunalMode);
-}
\ No newline at end of file
diff --git a/core/java/android/app/communal/OWNERS b/core/java/android/app/communal/OWNERS
deleted file mode 100644
index b02883d..0000000
--- a/core/java/android/app/communal/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
[email protected]
[email protected]
[email protected]
[email protected]
\ No newline at end of file
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index 3d0bf91..acd404b 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -84,7 +84,7 @@
     }
 
     @Override
-    protected Boolean recompute(ChangeIdStateQuery query) {
+    public Boolean recompute(ChangeIdStateQuery query) {
         final long token = Binder.clearCallingIdentity();
         try {
             if (query.type == ChangeIdStateQuery.QUERY_BY_PACKAGE_NAME) {
diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl
index 65b0249..6b9d2c73 100644
--- a/core/java/android/app/trust/ITrustListener.aidl
+++ b/core/java/android/app/trust/ITrustListener.aidl
@@ -16,13 +16,16 @@
 */
 package android.app.trust;
 
+import java.util.List;
+
 /**
  * Private API to be notified about trust changes.
  *
  * {@hide}
  */
 oneway interface ITrustListener {
-    void onTrustChanged(boolean enabled, int userId, int flags);
+    void onTrustChanged(boolean enabled, int userId, int flags,
+        in List<String> trustGrantedMessages);
     void onTrustManagedChanged(boolean managed, int userId);
     void onTrustError(in CharSequence message);
 }
\ No newline at end of file
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 9985cc0..edabccf 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -26,6 +26,7 @@
  */
 interface ITrustManager {
     void reportUnlockAttempt(boolean successful, int userId);
+    void reportUserRequestedUnlock(int userId);
     void reportUnlockLockout(int timeoutMs, int userId);
     void reportEnabledTrustAgentsChanged(int userId);
     void registerTrustListener(in ITrustListener trustListener);
@@ -36,5 +37,5 @@
     boolean isDeviceSecure(int userId);
     boolean isTrustUsuallyManaged(int userId);
     void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
-    void clearAllBiometricRecognized(in BiometricSourceType target);
+    void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser);
 }
diff --git a/core/java/android/app/trust/OWNERS b/core/java/android/app/trust/OWNERS
new file mode 100644
index 0000000..e2c6ce1
--- /dev/null
+++ b/core/java/android/app/trust/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/trust/OWNERS
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 65b2775..70b7de0 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -29,6 +29,9 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * See {@link com.android.server.trust.TrustManagerService}
  * @hide
@@ -43,6 +46,7 @@
     private static final String TAG = "TrustManager";
     private static final String DATA_FLAGS = "initiatedByUser";
     private static final String DATA_MESSAGE = "message";
+    private static final String DATA_GRANTED_MESSAGES = "grantedMessages";
 
     private final ITrustManager mService;
     private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
@@ -85,6 +89,19 @@
     }
 
     /**
+     * Reports that the user {@code userId} is likely interested in unlocking the device.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     */
+    public void reportUserRequestedUnlock(int userId) {
+        try {
+            mService.reportUserRequestedUnlock(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Reports that user {@param userId} has entered a temporary device lockout.
      *
      * This generally occurs when  the user has unsuccessfully tried to unlock the device too many
@@ -139,12 +156,15 @@
         try {
             ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
                 @Override
-                public void onTrustChanged(boolean enabled, int userId, int flags) {
+                public void onTrustChanged(boolean enabled, int userId, int flags,
+                        List<String> trustGrantedMessages) {
                     Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId,
                             trustListener);
                     if (flags != 0) {
                         m.getData().putInt(DATA_FLAGS, flags);
                     }
+                    m.getData().putCharSequenceArrayList(
+                            DATA_GRANTED_MESSAGES, (ArrayList) trustGrantedMessages);
                     m.sendToTarget();
                 }
 
@@ -156,7 +176,7 @@
 
                 @Override
                 public void onTrustError(CharSequence message) {
-                    Message m = mHandler.obtainMessage(MSG_TRUST_ERROR);
+                    Message m = mHandler.obtainMessage(MSG_TRUST_ERROR, trustListener);
                     m.getData().putCharSequence(DATA_MESSAGE, message);
                     m.sendToTarget();
                 }
@@ -217,9 +237,9 @@
      * Clears authentication by the specified biometric type for all users.
      */
     @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-    public void clearAllBiometricRecognized(BiometricSourceType source) {
+    public void clearAllBiometricRecognized(BiometricSourceType source, int unlockedUser) {
         try {
-            mService.clearAllBiometricRecognized(source);
+            mService.clearAllBiometricRecognized(source, unlockedUser);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -231,14 +251,15 @@
             switch(msg.what) {
                 case MSG_TRUST_CHANGED:
                     int flags = msg.peekData() != null ? msg.peekData().getInt(DATA_FLAGS) : 0;
-                    ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags);
+                    ((TrustListener) msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags,
+                            msg.getData().getStringArrayList(DATA_GRANTED_MESSAGES));
                     break;
                 case MSG_TRUST_MANAGED_CHANGED:
                     ((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2);
                     break;
                 case MSG_TRUST_ERROR:
                     final CharSequence message = msg.peekData().getCharSequence(DATA_MESSAGE);
-                    ((TrustListener)msg.obj).onTrustError(message);
+                    ((TrustListener) msg.obj).onTrustError(message);
             }
         }
     };
@@ -252,8 +273,11 @@
          * @param flags Flags specified by the trust agent when granting trust. See
          *     {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)
          *                 TrustAgentService.grantTrust(CharSequence, long, int)}.
+         * @param trustGrantedMessages Messages to display to the user when trust has been granted
+         *        by one or more trust agents.
          */
-        void onTrustChanged(boolean enabled, int userId, int flags);
+        void onTrustChanged(boolean enabled, int userId, int flags,
+                List<String> trustGrantedMessages);
 
         /**
          * Reports that whether trust is managed has changed
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 630b8d2..d7e197e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1277,6 +1277,28 @@
         }
     }
 
+    /** @hide */
+    public static String standbyBucketToString(int standbyBucket) {
+        switch (standbyBucket) {
+            case STANDBY_BUCKET_EXEMPTED:
+                return "EXEMPTED";
+            case STANDBY_BUCKET_ACTIVE:
+                return "ACTIVE";
+            case STANDBY_BUCKET_WORKING_SET:
+                return "WORKING_SET";
+            case STANDBY_BUCKET_FREQUENT:
+                return "FREQUENT";
+            case STANDBY_BUCKET_RARE:
+                return "RARE";
+            case STANDBY_BUCKET_RESTRICTED:
+                return "RESTRICTED";
+            case STANDBY_BUCKET_NEVER:
+                return "NEVER";
+            default:
+                return String.valueOf(standbyBucket);
+        }
+    }
+
     /**
      * {@hide}
      * Temporarily allowlist the specified app for a short duration. This is to allow an app
diff --git a/core/java/android/app/wallpapereffectsgeneration/OWNERS b/core/java/android/app/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 0000000..2bc0154
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,5 @@
[email protected]
[email protected]
[email protected]
[email protected]
+
diff --git a/core/java/android/bluetooth/Attributable.java b/core/java/android/bluetooth/Attributable.java
deleted file mode 100644
index d9acbe3..0000000
--- a/core/java/android/bluetooth/Attributable.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.AttributionSource;
-
-import java.util.List;
-
-/**
- * Marker interface for a class which can have an {@link AttributionSource}
- * assigned to it; these are typically {@link android.os.Parcelable} classes
- * which need to be updated after crossing Binder transaction boundaries.
- *
- * @hide
- */
-public interface Attributable {
-    void setAttributionSource(@NonNull AttributionSource attributionSource);
-
-    static @Nullable <T extends Attributable> T setAttributionSource(
-            @Nullable T attributable,
-            @NonNull AttributionSource attributionSource) {
-        if (attributable != null) {
-            attributable.setAttributionSource(attributionSource);
-        }
-        return attributable;
-    }
-
-    static @Nullable <T extends Attributable> List<T> setAttributionSource(
-            @Nullable List<T> attributableList,
-            @NonNull AttributionSource attributionSource) {
-        if (attributableList != null) {
-            final int size = attributableList.size();
-            for (int i = 0; i < size; i++) {
-                setAttributionSource(attributableList.get(i), attributionSource);
-            }
-        }
-        return attributableList;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
deleted file mode 100644
index 8b9cec1..0000000
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ /dev/null
@@ -1,1149 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-
-/**
- * This class provides the public APIs to control the Bluetooth A2DP
- * profile.
- *
- * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothA2dp proxy object.
- *
- * <p> Android only supports one connected Bluetooth A2dp device at a time.
- * Each method is protected with its appropriate permission.
- */
-public final class BluetoothA2dp implements BluetoothProfile {
-    private static final String TAG = "BluetoothA2dp";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the A2DP
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Playing state of the A2DP
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PLAYING_STATE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Audio Codec state of the
-     * A2DP Source profile.
-     *
-     * <p>This intent will have 2 extras:
-     * <ul>
-     * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
-     * connected, otherwise it is not included.</li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(trackingBug = 181103983)
-    public static final String ACTION_CODEC_CONFIG_CHANGED =
-            "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
-
-    /**
-     * A2DP sink device is streaming music. This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
-     */
-    public static final int STATE_PLAYING = 10;
-
-    /**
-     * A2DP sink device is NOT streaming music. This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
-     */
-    public static final int STATE_NOT_PLAYING = 11;
-
-    /** @hide */
-    @IntDef(prefix = "OPTIONAL_CODECS_", value = {
-            OPTIONAL_CODECS_SUPPORT_UNKNOWN,
-            OPTIONAL_CODECS_NOT_SUPPORTED,
-            OPTIONAL_CODECS_SUPPORTED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface OptionalCodecsSupportStatus {}
-
-    /**
-     * We don't have a stored preference for whether or not the given A2DP sink device supports
-     * optional codecs.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
-
-    /**
-     * The given A2DP sink device does not support optional codecs.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
-
-    /**
-     * The given A2DP sink device does support optional codecs.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_SUPPORTED = 1;
-
-    /** @hide */
-    @IntDef(prefix = "OPTIONAL_CODECS_PREF_", value = {
-            OPTIONAL_CODECS_PREF_UNKNOWN,
-            OPTIONAL_CODECS_PREF_DISABLED,
-            OPTIONAL_CODECS_PREF_ENABLED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface OptionalCodecsPreferenceStatus {}
-
-    /**
-     * We don't have a stored preference for whether optional codecs should be enabled or
-     * disabled for the given A2DP device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
-
-    /**
-     * Optional codecs should be disabled for the given A2DP device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
-
-    /**
-     * Optional codecs should be enabled for the given A2DP device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
-
-    /** @hide */
-    @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = {
-            DYNAMIC_BUFFER_SUPPORT_NONE,
-            DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD,
-            DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Type {}
-
-    /**
-     * Indicates the supported type of Dynamic Audio Buffer is not supported.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0;
-
-    /**
-     * Indicates the supported type of Dynamic Audio Buffer is A2DP offload.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1;
-
-    /**
-     * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
-                    IBluetoothA2dp.class.getName()) {
-                @Override
-                public IBluetoothA2dp getServiceInterface(IBinder service) {
-                    return IBluetoothA2dp.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothA2dp proxy object for interacting with the local
-     * Bluetooth A2DP service.
-     */
-    /* package */ BluetoothA2dp(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    @UnsupportedAppUsage
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothA2dp getService() {
-        return mProfileConnector.getService();
-    }
-
-    @Override
-    public void finalize() {
-        // The empty finalize needs to be kept or the
-        // cts signature tests would fail.
-    }
-
-    /**
-     * Initiate connection to a profile of the remote Bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothA2dp service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothA2dp service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStatesWithAttribution(states,
-                        mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @BtProfileState int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, A2DP audio streaming
-     * is to the active A2DP Sink device. If a remote device is not
-     * connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     * @param device the remote Bluetooth device. Could be null to clear
-     * the active device and stop streaming audio to a Bluetooth device.
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) log("setActiveDevice(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected device that is active.
-     *
-     * @return the connected device that is active or null if no device
-     * is active
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getActiveDevice() {
-        if (VDBG) log("getActiveDevice()");
-        final IBluetoothA2dp service = getService();
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevice(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Checks if Avrcp device supports the absolute volume feature.
-     *
-     * @return true if device supports absolute volume
-     * @hide
-     */
-    @RequiresNoPermission
-    public boolean isAvrcpAbsoluteVolumeSupported() {
-        if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isAvrcpAbsoluteVolumeSupported(recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Tells remote device to set an absolute volume. Only if absolute volume is supported
-     *
-     * @param volume Absolute volume to be set on AVRCP side
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setAvrcpAbsoluteVolume(int volume) {
-        if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                service.setAvrcpAbsoluteVolume(volume, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Check if A2DP profile is streaming music.
-     *
-     * @param device BluetoothDevice device
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isA2dpPlaying(BluetoothDevice device) {
-        if (DBG) log("isA2dpPlaying(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isA2dpPlaying(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * This function checks if the remote device is an AVCRP
-     * target and thus whether we should send volume keys
-     * changes or not.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean shouldSendVolumeKeys(BluetoothDevice device) {
-        if (isEnabled() && isValidDevice(device)) {
-            ParcelUuid[] uuids = device.getUuids();
-            if (uuids == null) return false;
-
-            for (ParcelUuid uuid : uuids) {
-                if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Gets the current codec status (configuration and capability).
-     *
-     * @param device the remote Bluetooth device.
-     * @return the current codec status
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 181103983)
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
-        verifyDeviceNotNull(device, "getCodecStatus");
-        final IBluetoothA2dp service = getService();
-        final BluetoothCodecStatus defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<BluetoothCodecStatus> recv =
-                        new SynchronousResultReceiver();
-                service.getCodecStatus(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets the codec configuration preference.
-     *
-     * @param device the remote Bluetooth device.
-     * @param codecConfig the codec configuration preference
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 181103983)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setCodecConfigPreference(@NonNull BluetoothDevice device,
-                                         @NonNull BluetoothCodecConfig codecConfig) {
-        if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
-        verifyDeviceNotNull(device, "setCodecConfigPreference");
-        if (codecConfig == null) {
-            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
-            throw new IllegalArgumentException("codecConfig cannot be null");
-        }
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Enables the optional codecs.
-     *
-     * @param device the remote Bluetooth device.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
-        verifyDeviceNotNull(device, "enableOptionalCodecs");
-        enableDisableOptionalCodecs(device, true);
-    }
-
-    /**
-     * Disables the optional codecs.
-     *
-     * @param device the remote Bluetooth device.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
-        verifyDeviceNotNull(device, "disableOptionalCodecs");
-        enableDisableOptionalCodecs(device, false);
-    }
-
-    /**
-     * Enables or disables the optional codecs.
-     *
-     * @param device the remote Bluetooth device.
-     * @param enable if true, enable the optional codecs, other disable them
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                if (enable) {
-                    service.enableOptionalCodecs(device, mAttributionSource);
-                } else {
-                    service.disableOptionalCodecs(device, mAttributionSource);
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Returns whether this device supports optional codecs.
-     *
-     * @param device The device to check
-     * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
-     * OPTIONAL_CODECS_SUPPORTED.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @OptionalCodecsSupportStatus
-    public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
-        if (DBG) log("isOptionalCodecsSupported(" + device + ")");
-        verifyDeviceNotNull(device, "isOptionalCodecsSupported");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.supportsOptionalCodecs(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns whether this device should have optional codecs enabled.
-     *
-     * @param device The device in question.
-     * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
-     * OPTIONAL_CODECS_PREF_DISABLED.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @OptionalCodecsPreferenceStatus
-    public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
-        if (DBG) log("isOptionalCodecsEnabled(" + device + ")");
-        verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getOptionalCodecsEnabled(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets a persistent preference for whether a given device should have optional codecs enabled.
-     *
-     * @param device The device to set this preference for.
-     * @param value Whether the optional codecs should be enabled for this device.  This should be
-     * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
-     * OPTIONAL_CODECS_PREF_DISABLED.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
-            @OptionalCodecsPreferenceStatus int value) {
-        if (DBG) log("setOptionalCodecsEnabled(" + device + ")");
-        verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
-        if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
-                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
-                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
-            Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
-            return;
-        }
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                service.setOptionalCodecsEnabled(device, value, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Get the supported type of the Dynamic Audio Buffer.
-     * <p>Possible return values are
-     * {@link #DYNAMIC_BUFFER_SUPPORT_NONE},
-     * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
-     * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
-     *
-     * @return supported type of Dynamic Audio Buffer feature
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @Type int getDynamicBufferSupport() {
-        if (VDBG) log("getDynamicBufferSupport()");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getDynamicBufferSupport(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Return the record of {@link BufferConstraints} object that
-     * has the default/maximum/minimum audio buffer. This can be used to inform what the controller
-     * has support for the audio buffer.
-     *
-     * @return a record with {@link BufferConstraints} or null if report is unavailable
-     * or unsupported
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @Nullable BufferConstraints getBufferConstraints() {
-        if (VDBG) log("getBufferConstraints()");
-        final IBluetoothA2dp service = getService();
-        final BufferConstraints defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BufferConstraints> recv =
-                        new SynchronousResultReceiver();
-                service.getBufferConstraints(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set Dynamic Audio Buffer Size.
-     *
-     * @param codec audio codec
-     * @param value buffer millis
-     * @return true to indicate success, or false on immediate error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
-            int value) {
-        if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
-        if (value < 0) {
-            Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
-            return false;
-        }
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setBufferLengthMillis(codec, value, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            case STATE_PLAYING:
-                return "playing";
-            case STATE_NOT_PLAYING:
-                return "not playing";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
-        if (device == null) {
-            Log.e(TAG, methodName + ": device param is null");
-            throw new IllegalArgumentException("Device cannot be null");
-        }
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
deleted file mode 100755
index 5941681..0000000
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth A2DP Sink
- * profile.
- *
- * <p>BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothA2dpSink proxy object.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothA2dpSink implements BluetoothProfile {
-    private static final String TAG = "BluetoothA2dpSink";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the A2DP Sink
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @SystemApi
-    @SuppressLint("ActionValue")
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK,
-                    "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
-                @Override
-                public IBluetoothA2dpSink getServiceInterface(IBinder service) {
-                    return IBluetoothA2dpSink.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothA2dp proxy object for interacting with the local
-     * Bluetooth A2DP service.
-     */
-    /* package */ BluetoothA2dpSink(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothA2dpSink getService() {
-        return mProfileConnector.getService();
-    }
-
-    @Override
-    public void finalize() {
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> Currently, the system supports only 1 connection to the
-     * A2DP profile. The API will automatically disconnect connected
-     * devices before connecting.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothA2dpSink service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothA2dpSink service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the current audio configuration for the A2DP source device,
-     * or null if the device has no audio configuration
-     *
-     * @param device Remote bluetooth device.
-     * @return audio configuration for the device, or null
-     *
-     * {@see BluetoothAudioConfig}
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
-        if (VDBG) log("getAudioConfig(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final BluetoothAudioConfig defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<BluetoothAudioConfig> recv =
-                        new SynchronousResultReceiver();
-                service.getAudioConfig(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if audio is playing on the bluetooth device (A2DP profile is streaming music).
-     *
-     * @param device BluetoothDevice device
-     * @return true if audio is playing (A2dp is streaming music), false otherwise
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
-        if (VDBG) log("isAudioPlaying(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isA2dpPlaying(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            case BluetoothA2dp.STATE_PLAYING:
-                return "playing";
-            case BluetoothA2dp.STATE_NOT_PLAYING:
-                return "not playing";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
deleted file mode 100644
index c17a7b4..0000000
--- a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.ElapsedRealtimeLong;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Record of energy and activity information from controller and
- * underlying bt stack state.Timestamp the record with system
- * time.
- *
- * @hide
- */
-@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
-public final class BluetoothActivityEnergyInfo implements Parcelable {
-    private final long mTimestamp;
-    private int mBluetoothStackState;
-    private long mControllerTxTimeMs;
-    private long mControllerRxTimeMs;
-    private long mControllerIdleTimeMs;
-    private long mControllerEnergyUsed;
-    private List<UidTraffic> mUidTraffic;
-
-    /** @hide */
-    @IntDef(prefix = { "BT_STACK_STATE_" }, value = {
-            BT_STACK_STATE_INVALID,
-            BT_STACK_STATE_STATE_ACTIVE,
-            BT_STACK_STATE_STATE_SCANNING,
-            BT_STACK_STATE_STATE_IDLE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BluetoothStackState {}
-
-    public static final int BT_STACK_STATE_INVALID = 0;
-    public static final int BT_STACK_STATE_STATE_ACTIVE = 1;
-    public static final int BT_STACK_STATE_STATE_SCANNING = 2;
-    public static final int BT_STACK_STATE_STATE_IDLE = 3;
-
-    /** @hide */
-    public BluetoothActivityEnergyInfo(long timestamp, int stackState,
-            long txTime, long rxTime, long idleTime, long energyUsed) {
-        mTimestamp = timestamp;
-        mBluetoothStackState = stackState;
-        mControllerTxTimeMs = txTime;
-        mControllerRxTimeMs = rxTime;
-        mControllerIdleTimeMs = idleTime;
-        mControllerEnergyUsed = energyUsed;
-    }
-
-    /** @hide */
-    private BluetoothActivityEnergyInfo(Parcel in) {
-        mTimestamp = in.readLong();
-        mBluetoothStackState = in.readInt();
-        mControllerTxTimeMs = in.readLong();
-        mControllerRxTimeMs = in.readLong();
-        mControllerIdleTimeMs = in.readLong();
-        mControllerEnergyUsed = in.readLong();
-        mUidTraffic = in.createTypedArrayList(UidTraffic.CREATOR);
-    }
-
-    /** @hide */
-    @Override
-    public String toString() {
-        return "BluetoothActivityEnergyInfo{"
-                + " mTimestamp=" + mTimestamp
-                + " mBluetoothStackState=" + mBluetoothStackState
-                + " mControllerTxTimeMs=" + mControllerTxTimeMs
-                + " mControllerRxTimeMs=" + mControllerRxTimeMs
-                + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
-                + " mControllerEnergyUsed=" + mControllerEnergyUsed
-                + " mUidTraffic=" + mUidTraffic
-                + " }";
-    }
-
-    public static final @NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
-            new Parcelable.Creator<BluetoothActivityEnergyInfo>() {
-                public BluetoothActivityEnergyInfo createFromParcel(Parcel in) {
-                    return new BluetoothActivityEnergyInfo(in);
-                }
-
-                public BluetoothActivityEnergyInfo[] newArray(int size) {
-                    return new BluetoothActivityEnergyInfo[size];
-                }
-            };
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mTimestamp);
-        out.writeInt(mBluetoothStackState);
-        out.writeLong(mControllerTxTimeMs);
-        out.writeLong(mControllerRxTimeMs);
-        out.writeLong(mControllerIdleTimeMs);
-        out.writeLong(mControllerEnergyUsed);
-        out.writeTypedList(mUidTraffic);
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Get the Bluetooth stack state associated with the energy info.
-     *
-     * @return one of {@link #BluetoothStackState} states
-     */
-    @BluetoothStackState
-    public int getBluetoothStackState() {
-        return mBluetoothStackState;
-    }
-
-    /**
-     * @return tx time in ms
-     */
-    public long getControllerTxTimeMillis() {
-        return mControllerTxTimeMs;
-    }
-
-    /**
-     * @return rx time in ms
-     */
-    public long getControllerRxTimeMillis() {
-        return mControllerRxTimeMs;
-    }
-
-    /**
-     * @return idle time in ms
-     */
-    public long getControllerIdleTimeMillis() {
-        return mControllerIdleTimeMs;
-    }
-
-    /**
-     * Get the product of current (mA), voltage (V), and time (ms).
-     *
-     * @return energy used
-     */
-    public long getControllerEnergyUsed() {
-        return mControllerEnergyUsed;
-    }
-
-    /**
-     * @return timestamp (real time elapsed in milliseconds since boot) of record creation
-     */
-    public @ElapsedRealtimeLong long getTimestampMillis() {
-        return mTimestamp;
-    }
-
-    /**
-     * Get the {@link List} of each application {@link android.bluetooth.UidTraffic}.
-     *
-     * @return current {@link List} of {@link android.bluetooth.UidTraffic}
-     */
-    public @NonNull List<UidTraffic> getUidTraffic() {
-        if (mUidTraffic == null) {
-            return Collections.emptyList();
-        }
-        return mUidTraffic;
-    }
-
-    /** @hide */
-    public void setUidTraffic(List<UidTraffic> traffic) {
-        mUidTraffic = traffic;
-    }
-
-    /**
-     * @return true if the record Tx time, Rx time, and Idle time are more than 0.
-     */
-    public boolean isValid() {
-        return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0)
-                && (mControllerIdleTimeMs >= 0));
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
deleted file mode 100644
index 9a0f02e..0000000
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ /dev/null
@@ -1,4374 +0,0 @@
-/*
- * Copyright 2009-2016 The Android Open Source Project
- * Copyright 2015 Samsung LSI
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
-import android.bluetooth.BluetoothDevice.Transport;
-import android.bluetooth.BluetoothProfile.ConnectionPolicy;
-import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.bluetooth.le.BluetoothLeAdvertiser;
-import android.bluetooth.le.BluetoothLeScanner;
-import android.bluetooth.le.PeriodicAdvertisingManager;
-import android.bluetooth.le.ScanCallback;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanRecord;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.le.ScanSettings;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.sysprop.BluetoothProperties;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-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;
-import java.util.UUID;
-import java.util.WeakHashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
- * lets you perform fundamental Bluetooth tasks, such as initiate
- * device discovery, query a list of bonded (paired) devices,
- * instantiate a {@link BluetoothDevice} using a known MAC address, and create
- * a {@link BluetoothServerSocket} to listen for connection requests from other
- * devices, and start a scan for Bluetooth LE devices.
- *
- * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
- * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}.
- * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter}
- * method instead.
- * </p><p>
- * Fundamentally, this is your starting point for all
- * Bluetooth actions. Once you have the local adapter, you can get a set of
- * {@link BluetoothDevice} objects representing all paired devices with
- * {@link #getBondedDevices()}; start device discovery with
- * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
- * listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
- * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
- * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
- * </p>
- * <p>This class is thread safe.</p>
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about using Bluetooth, read the <a href=
- * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
- * guide.
- * </p>
- * </div>
- *
- * {@see BluetoothDevice}
- * {@see BluetoothServerSocket}
- */
-public final class BluetoothAdapter {
-    private static final String TAG = "BluetoothAdapter";
-    private static final String DESCRIPTOR = "android.bluetooth.BluetoothAdapter";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Default MAC address reported to a client that does not have the
-     * android.permission.LOCAL_MAC_ADDRESS permission.
-     *
-     * @hide
-     */
-    public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
-
-    /**
-     * Sentinel error value for this class. Guaranteed to not equal any other
-     * integer constant in this class. Provided as a convenience for functions
-     * that require a sentinel error value, for example:
-     * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-     * BluetoothAdapter.ERROR)</code>
-     */
-    public static final int ERROR = Integer.MIN_VALUE;
-
-    /**
-     * Broadcast Action: The state of the local Bluetooth adapter has been
-     * changed.
-     * <p>For example, Bluetooth has been turned on or off.
-     * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
-     * #EXTRA_PREVIOUS_STATE} containing the new and old states
-     * respectively.
-     */
-    @RequiresLegacyBluetoothPermission
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
-     * intents to request the current power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     */
-    public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE";
-    /**
-     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
-     * intents to request the previous power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF}
-     */
-    public static final String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.adapter.extra.PREVIOUS_STATE";
-
-    /** @hide */
-    @IntDef(prefix = { "STATE_" }, value = {
-            STATE_OFF,
-            STATE_TURNING_ON,
-            STATE_ON,
-            STATE_TURNING_OFF,
-            STATE_BLE_TURNING_ON,
-            STATE_BLE_ON,
-            STATE_BLE_TURNING_OFF
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AdapterState {}
-
-    /**
-     * Indicates the local Bluetooth adapter is off.
-     */
-    public static final int STATE_OFF = 10;
-    /**
-     * Indicates the local Bluetooth adapter is turning on. However local
-     * clients should wait for {@link #STATE_ON} before attempting to
-     * use the adapter.
-     */
-    public static final int STATE_TURNING_ON = 11;
-    /**
-     * Indicates the local Bluetooth adapter is on, and ready for use.
-     */
-    public static final int STATE_ON = 12;
-    /**
-     * Indicates the local Bluetooth adapter is turning off. Local clients
-     * should immediately attempt graceful disconnection of any remote links.
-     */
-    public static final int STATE_TURNING_OFF = 13;
-
-    /**
-     * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
-     *
-     * @hide
-     */
-    public static final int STATE_BLE_TURNING_ON = 14;
-
-    /**
-     * Indicates the local Bluetooth adapter is in LE only mode.
-     *
-     * @hide
-     */
-    public static final int STATE_BLE_ON = 15;
-
-    /**
-     * Indicates the local Bluetooth adapter is turning off LE only mode.
-     *
-     * @hide
-     */
-    public static final int STATE_BLE_TURNING_OFF = 16;
-
-    /**
-     * UUID of the GATT Read Characteristics for LE_PSM value.
-     *
-     * @hide
-     */
-    public static final UUID LE_PSM_CHARACTERISTIC_UUID =
-            UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
-
-    /**
-     * Human-readable string helper for AdapterState
-     *
-     * @hide
-     */
-    public static String nameForState(@AdapterState int state) {
-        switch (state) {
-            case STATE_OFF:
-                return "OFF";
-            case STATE_TURNING_ON:
-                return "TURNING_ON";
-            case STATE_ON:
-                return "ON";
-            case STATE_TURNING_OFF:
-                return "TURNING_OFF";
-            case STATE_BLE_TURNING_ON:
-                return "BLE_TURNING_ON";
-            case STATE_BLE_ON:
-                return "BLE_ON";
-            case STATE_BLE_TURNING_OFF:
-                return "BLE_TURNING_OFF";
-            default:
-                return "?!?!? (" + state + ")";
-        }
-    }
-
-    /**
-     * Activity Action: Show a system activity that requests discoverable mode.
-     * This activity will also request the user to turn on Bluetooth if it
-     * is not currently enabled.
-     * <p>Discoverable mode is equivalent to {@link
-     * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
-     * this Bluetooth adapter when they perform a discovery.
-     * <p>For privacy, Android is not discoverable by default.
-     * <p>The sender of this Intent can optionally use extra field {@link
-     * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
-     * discoverability. Currently the default duration is 120 seconds, and
-     * maximum duration is capped at 300 seconds for each request.
-     * <p>Notification of the result of this activity is posted using the
-     * {@link android.app.Activity#onActivityResult} callback. The
-     * <code>resultCode</code>
-     * will be the duration (in seconds) of discoverability or
-     * {@link android.app.Activity#RESULT_CANCELED} if the user rejected
-     * discoverability or an error has occurred.
-     * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
-     * for global notification whenever the scan mode changes. For example, an
-     * application can be notified when the device has ended discoverability.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
-            ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
-
-    /**
-     * Used as an optional int extra field in {@link
-     * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
-     * for discoverability in seconds. The current default is 120 seconds, and
-     * requests over 300 seconds will be capped. These values could change.
-     */
-    public static final String EXTRA_DISCOVERABLE_DURATION =
-            "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
-
-    /**
-     * Activity Action: Show a system activity that allows the user to turn on
-     * Bluetooth.
-     * <p>This system activity will return once Bluetooth has completed turning
-     * on, or the user has decided not to turn Bluetooth on.
-     * <p>Notification of the result of this activity is posted using the
-     * {@link android.app.Activity#onActivityResult} callback. The
-     * <code>resultCode</code>
-     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
-     * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user
-     * has rejected the request or an error has occurred.
-     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
-     * for global notification whenever Bluetooth is turned on or off.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
-            ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
-
-    /**
-     * Activity Action: Show a system activity that allows the user to turn off
-     * Bluetooth. This is used only if permission review is enabled which is for
-     * apps targeting API less than 23 require a permission review before any of
-     * the app's components can run.
-     * <p>This system activity will return once Bluetooth has completed turning
-     * off, or the user has decided not to turn Bluetooth off.
-     * <p>Notification of the result of this activity is posted using the
-     * {@link android.app.Activity#onActivityResult} callback. The
-     * <code>resultCode</code>
-     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
-     * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user
-     * has rejected the request or an error has occurred.
-     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
-     * for global notification whenever Bluetooth is turned on or off.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
-            ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
-
-    /**
-     * Activity Action: Show a system activity that allows user to enable BLE scans even when
-     * Bluetooth is turned off.<p>
-     *
-     * Notification of result of this activity is posted using
-     * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be
-     * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or
-     * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an
-     * error occurred.
-     *
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE =
-            "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
-
-    /**
-     * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
-     * has changed.
-     * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
-     * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
-     * respectively.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
-     * intents to request the current scan mode. Possible values are:
-     * {@link #SCAN_MODE_NONE},
-     * {@link #SCAN_MODE_CONNECTABLE},
-     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
-     */
-    public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
-    /**
-     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
-     * intents to request the previous scan mode. Possible values are:
-     * {@link #SCAN_MODE_NONE},
-     * {@link #SCAN_MODE_CONNECTABLE},
-     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
-     */
-    public static final String EXTRA_PREVIOUS_SCAN_MODE =
-            "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
-
-    /** @hide */
-    @IntDef(prefix = { "SCAN_" }, value = {
-            SCAN_MODE_NONE,
-            SCAN_MODE_CONNECTABLE,
-            SCAN_MODE_CONNECTABLE_DISCOVERABLE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanMode {}
-
-    /** @hide */
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanModeStatusCode {}
-
-    /**
-     * Indicates that both inquiry scan and page scan are disabled on the local
-     * Bluetooth adapter. Therefore this device is neither discoverable
-     * nor connectable from remote Bluetooth devices.
-     */
-    public static final int SCAN_MODE_NONE = 20;
-    /**
-     * Indicates that inquiry scan is disabled, but page scan is enabled on the
-     * local Bluetooth adapter. Therefore this device is not discoverable from
-     * remote Bluetooth devices, but is connectable from remote devices that
-     * have previously discovered this device.
-     */
-    public static final int SCAN_MODE_CONNECTABLE = 21;
-    /**
-     * Indicates that both inquiry scan and page scan are enabled on the local
-     * Bluetooth adapter. Therefore this device is both discoverable and
-     * connectable from remote Bluetooth devices.
-     */
-    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
-
-    /**
-     * Device only has a display.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_OUT = 0;
-
-    /**
-     * Device has a display and the ability to input Yes/No.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_IO = 1;
-
-    /**
-     * Device only has a keyboard for entry but no display.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_IN = 2;
-
-    /**
-     * Device has no Input or Output capability.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_NONE = 3;
-
-    /**
-     * Device has a display and a full keyboard.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_KBDISP = 4;
-
-    /**
-     * Maximum range value for Input/Output capabilities.
-     *
-     * <p>This should be updated when adding a new Input/Output capability. Other code
-     * like validation depends on this being accurate.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_MAX = 5;
-
-    /**
-     * The Input/Output capability of the device is unknown.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_UNKNOWN = 255;
-
-    /** @hide */
-    @IntDef({IO_CAPABILITY_OUT, IO_CAPABILITY_IO, IO_CAPABILITY_IN, IO_CAPABILITY_NONE,
-            IO_CAPABILITY_KBDISP})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IoCapability {}
-
-    /** @hide */
-    @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO,
-            ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ActiveDeviceUse {}
-
-    /**
-     * Use the specified device for audio (a2dp and hearing aid profile)
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACTIVE_DEVICE_AUDIO = 0;
-
-    /**
-     * Use the specified device for phone calls (headset profile and hearing
-     * aid profile)
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACTIVE_DEVICE_PHONE_CALL = 1;
-
-    /**
-     * Use the specified device for a2dp, hearing aid profile, and headset profile
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACTIVE_DEVICE_ALL = 2;
-
-    /** @hide */
-    @IntDef({BluetoothProfile.HEADSET, BluetoothProfile.A2DP,
-            BluetoothProfile.HEARING_AID})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ActiveDeviceProfile {}
-
-    /**
-     * Broadcast Action: The local Bluetooth adapter has started the remote
-     * device discovery process.
-     * <p>This usually involves an inquiry scan of about 12 seconds, followed
-     * by a page scan of each new device to retrieve its Bluetooth name.
-     * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
-     * remote Bluetooth devices are found.
-     * <p>Device discovery is a heavyweight procedure. New connections to
-     * remote Bluetooth devices should not be attempted while discovery is in
-     * progress, and existing connections will experience limited bandwidth
-     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
-     * discovery.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
-    /**
-     * Broadcast Action: The local Bluetooth adapter has finished the device
-     * discovery process.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
-
-    /**
-     * Broadcast Action: The local Bluetooth adapter has changed its friendly
-     * Bluetooth name.
-     * <p>This name is visible to remote Bluetooth devices.
-     * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
-     * the name.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
-    /**
-     * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
-     * intents to request the local Bluetooth name.
-     */
-    public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
-
-    /**
-     * Intent used to broadcast the change in connection state of the local
-     * Bluetooth adapter to a profile of the remote device. When the adapter is
-     * not connected to any profiles of any remote devices and it attempts a
-     * connection to a profile this intent will be sent. Once connected, this intent
-     * will not be sent for any more connection attempts to any profiles of any
-     * remote device. When the adapter disconnects from the last profile its
-     * connected to of any remote device, this intent will be sent.
-     *
-     * <p> This intent is useful for applications that are only concerned about
-     * whether the local adapter is connected to any profile of any device and
-     * are not really concerned about which profile. For example, an application
-     * which displays an icon to display whether Bluetooth is connected or not
-     * can use this intent.
-     *
-     * <p>This intent will have 3 extras:
-     * {@link #EXTRA_CONNECTION_STATE} - The current connection state.
-     * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state.
-     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
-     *
-     * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE}
-     * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * This extra represents the current connection state.
-     */
-    public static final String EXTRA_CONNECTION_STATE =
-            "android.bluetooth.adapter.extra.CONNECTION_STATE";
-
-    /**
-     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * This extra represents the previous connection state.
-     */
-    public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
-            "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
-
-    /**
-     * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi public static final String ACTION_BLE_STATE_CHANGED =
-            "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Bluetooth address
-     * of the local Bluetooth adapter.
-     * <p>Always contains the extra field {@link
-     * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address.
-     *
-     * Note: only system level processes are allowed to send this
-     * defined broadcast.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED =
-            "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED";
-
-    /**
-     * Used as a String extra field in {@link
-     * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local
-     * Bluetooth address.
-     *
-     * @hide
-     */
-    public static final String EXTRA_BLUETOOTH_ADDRESS =
-            "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS";
-
-    /**
-     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
-     * by BLE Always on enabled application to know the ACL_CONNECTED event
-     * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
-     * as Bluetooth LE is the only feature available in STATE_BLE_ON
-     *
-     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
-     * works in Bluetooth state STATE_ON
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLE_ACL_CONNECTED =
-            "android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
-
-    /**
-     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
-     * by BLE Always on enabled application to know the ACL_DISCONNECTED event
-     * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
-     * LE is the only feature available in STATE_BLE_ON
-     *
-     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
-     * works in Bluetooth state STATE_ON
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLE_ACL_DISCONNECTED =
-            "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
-
-    /** The profile is in disconnected state */
-    public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
-    /** The profile is in connecting state */
-    public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
-    /** The profile is in connected state */
-    public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
-    /** The profile is in disconnecting state */
-    public static final int STATE_DISCONNECTING =
-            BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
-
-    /** @hide */
-    public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
-    private final IBinder mToken;
-
-
-    /**
-     * When creating a ServerSocket using listenUsingRfcommOn() or
-     * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create
-     * a ServerSocket that auto assigns a channel number to the first
-     * bluetooth socket.
-     * The channel number assigned to this first Bluetooth Socket will
-     * be stored in the ServerSocket, and reused for subsequent Bluetooth
-     * sockets.
-     *
-     * @hide
-     */
-    public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2;
-
-
-    private static final int ADDRESS_LENGTH = 17;
-
-    /**
-     * Lazily initialized singleton. Guaranteed final after first object
-     * constructed.
-     */
-    private static BluetoothAdapter sAdapter;
-
-    private BluetoothLeScanner mBluetoothLeScanner;
-    private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
-    private PeriodicAdvertisingManager mPeriodicAdvertisingManager;
-
-    private final IBluetoothManager mManagerService;
-    private final AttributionSource mAttributionSource;
-
-    // Yeah, keeping both mService and sService isn't pretty, but it's too late
-    // in the current release for a major refactoring, so we leave them both
-    // intact until this can be cleaned up in a future release
-
-    @UnsupportedAppUsage
-    @GuardedBy("mServiceLock")
-    private IBluetooth mService;
-    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
-
-    @GuardedBy("sServiceLock")
-    private static boolean sServiceRegistered;
-    @GuardedBy("sServiceLock")
-    private static IBluetooth sService;
-    private static final Object sServiceLock = new Object();
-
-    private final Object mLock = new Object();
-    private final Map<LeScanCallback, ScanCallback> mLeScanClients;
-    private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
-                mMetadataListeners = new HashMap<>();
-    private final Map<BluetoothConnectionCallback, Executor>
-            mBluetoothConnectionCallbackExecutorMap = new HashMap<>();
-
-    /**
-     * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
-     * implementation.
-     */
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothMetadataListener mBluetoothMetadataListener =
-            new IBluetoothMetadataListener.Stub() {
-        @Override
-        public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            synchronized (mMetadataListeners) {
-                if (mMetadataListeners.containsKey(device)) {
-                    List<Pair<OnMetadataChangedListener, Executor>> list =
-                            mMetadataListeners.get(device);
-                    for (Pair<OnMetadataChangedListener, Executor> pair : list) {
-                        OnMetadataChangedListener listener = pair.first;
-                        Executor executor = pair.second;
-                        executor.execute(() -> {
-                            listener.onMetadataChanged(device, key, value);
-                        });
-                    }
-                }
-            }
-            return;
-        }
-    };
-
-    /**
-     * Get a handle to the default local Bluetooth adapter.
-     * <p>
-     * Currently Android only supports one Bluetooth adapter, but the API could
-     * be extended to support more. This will always return the default adapter.
-     * </p>
-     *
-     * @return the default local adapter, or null if Bluetooth is not supported
-     *         on this hardware platform
-     * @deprecated this method will continue to work, but developers are
-     *             strongly encouraged to migrate to using
-     *             {@link BluetoothManager#getAdapter()}, since that approach
-     *             enables support for {@link Context#createAttributionContext}.
-     */
-    @Deprecated
-    @RequiresNoPermission
-    public static synchronized BluetoothAdapter getDefaultAdapter() {
-        if (sAdapter == null) {
-            sAdapter = createAdapter(AttributionSource.myAttributionSource());
-        }
-        return sAdapter;
-    }
-
-    /** {@hide} */
-    public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
-        IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
-        if (binder != null) {
-            return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder),
-                    attributionSource);
-        } else {
-            Log.e(TAG, "Bluetooth binder is null");
-            return null;
-        }
-    }
-
-    /**
-     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
-     */
-    BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
-        mManagerService = Objects.requireNonNull(managerService);
-        mAttributionSource = Objects.requireNonNull(attributionSource);
-        synchronized (mServiceLock.writeLock()) {
-            mService = getBluetoothService(mManagerCallback);
-        }
-        mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
-        mToken = new Binder(DESCRIPTOR);
-    }
-
-    /**
-     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
-     * address.
-     * <p>Valid Bluetooth hardware addresses must be upper case, in a format
-     * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
-     * available to validate a Bluetooth address.
-     * <p>A {@link BluetoothDevice} will always be returned for a valid
-     * hardware address, even if this adapter has never seen that device.
-     *
-     * @param address valid Bluetooth MAC address
-     * @throws IllegalArgumentException if address is invalid
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getRemoteDevice(String address) {
-        final BluetoothDevice res = new BluetoothDevice(address);
-        res.setAttributionSource(mAttributionSource);
-        return res;
-    }
-
-    /**
-     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
-     * address.
-     * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method
-     * expects the address in network byte order (MSB first).
-     * <p>A {@link BluetoothDevice} will always be returned for a valid
-     * hardware address, even if this adapter has never seen that device.
-     *
-     * @param address Bluetooth MAC address (6 bytes)
-     * @throws IllegalArgumentException if address is invalid
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getRemoteDevice(byte[] address) {
-        if (address == null || address.length != 6) {
-            throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
-        }
-        final BluetoothDevice res = new BluetoothDevice(
-                String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
-                        address[2], address[3], address[4], address[5]));
-        res.setAttributionSource(mAttributionSource);
-        return res;
-    }
-
-    /**
-     * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
-     * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not
-     * supported on this device.
-     * <p>
-     * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
-     * on this device before calling this method.
-     */
-    @RequiresNoPermission
-    public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
-        if (!getLeAccess()) {
-            return null;
-        }
-        synchronized (mLock) {
-            if (mBluetoothLeAdvertiser == null) {
-                mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this);
-            }
-            return mBluetoothLeAdvertiser;
-        }
-    }
-
-    /**
-     * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising
-     * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic
-     * Advertising is not supported on this device.
-     * <p>
-     * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is
-     * supported on this device before calling this method.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
-        if (!getLeAccess()) {
-            return null;
-        }
-
-        if (!isLePeriodicAdvertisingSupported()) {
-            return null;
-        }
-
-        synchronized (mLock) {
-            if (mPeriodicAdvertisingManager == null) {
-                mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this);
-            }
-            return mPeriodicAdvertisingManager;
-        }
-    }
-
-    /**
-     * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
-     */
-    @RequiresNoPermission
-    public BluetoothLeScanner getBluetoothLeScanner() {
-        if (!getLeAccess()) {
-            return null;
-        }
-        synchronized (mLock) {
-            if (mBluetoothLeScanner == null) {
-                mBluetoothLeScanner = new BluetoothLeScanner(this);
-            }
-            return mBluetoothLeScanner;
-        }
-    }
-
-    /**
-     * Return true if Bluetooth is currently enabled and ready for use.
-     * <p>Equivalent to:
-     * <code>getBluetoothState() == STATE_ON</code>
-     *
-     * @return true if the local adapter is turned on
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isEnabled() {
-        return getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    /**
-     * Return true if Bluetooth LE(Always BLE On feature) is currently
-     * enabled and ready for use
-     * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
-     *
-     * @return true if the local Bluetooth LE adapter is turned on
-     * @hide
-     */
-    @SystemApi
-    @RequiresNoPermission
-    public boolean isLeEnabled() {
-        final int state = getLeState();
-        if (DBG) {
-            Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
-        }
-        return (state == BluetoothAdapter.STATE_ON
-                || state == BluetoothAdapter.STATE_BLE_ON
-                || state == BluetoothAdapter.STATE_TURNING_ON
-                || state == BluetoothAdapter.STATE_TURNING_OFF);
-    }
-
-    /**
-     * Turns off Bluetooth LE which was earlier turned on by calling enableBLE().
-     *
-     * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
-     * to STATE_OFF and completely shut-down Bluetooth
-     *
-     * <p> If the Adapter state is STATE_ON, This would unregister the existance of
-     * special Bluetooth LE application and hence the further turning off of Bluetooth
-     * from UI would ensure the complete turn-off of Bluetooth rather than staying back
-     * BLE only state
-     *
-     * <p>This is an asynchronous call: it will return immediately, and
-     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
-     * later transition to either {@link #STATE_BLE_ON} or {@link
-     * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
-     * If this call returns false then there was an
-     * immediate problem that will prevent the QAdapter from being turned off -
-     * such as the QAadapter already being turned off.
-     *
-     * @return true to indicate success, or false on immediate error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disableBLE() {
-        if (!isBleScanAlwaysAvailable()) {
-            return false;
-        }
-        try {
-            return mManagerService.disableBle(mAttributionSource, mToken);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE.
-     *
-     * enableBLE registers the existence of an app using only LE functions.
-     *
-     * enableBLE may enable Bluetooth to an LE only mode so that an app can use
-     * LE related features (BluetoothGatt or BluetoothGattServer classes)
-     *
-     * If the user disables Bluetooth while an app is registered to use LE only features,
-     * Bluetooth will remain on in LE only mode for the app.
-     *
-     * When Bluetooth is in LE only mode, it is not shown as ON to the UI.
-     *
-     * <p>This is an asynchronous call: it returns immediately, and
-     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
-     * to be notified of adapter state changes.
-     *
-     * If this call returns * true, then the adapter state is either in a mode where
-     * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON},
-     * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}.
-     *
-     * If this call returns false then there was an immediate problem that prevents the
-     * adapter from being turned on - such as Airplane mode.
-     *
-     * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various
-     * states, It includes all the classic Bluetooth Adapter states along with
-     * internal BLE only states
-     *
-     * @return true to indicate Bluetooth LE will be available, or false on immediate error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enableBLE() {
-        if (!isBleScanAlwaysAvailable()) {
-            return false;
-        }
-        try {
-            return mManagerService.enableBle(mAttributionSource, mToken);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-
-        return false;
-    }
-
-    private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state";
-
-    private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache =
-            new PropertyInvalidatedCache<Void, Integer>(
-                8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                protected Integer recompute(Void query) {
-                    try {
-                        return mService.getState();
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
-                    }
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableBluetoothGetStateCache() {
-        mBluetoothGetStateCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateBluetoothGetStateCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY);
-    }
-
-    /**
-     * Fetch the current bluetooth state.  If the service is down, return
-     * OFF.
-     */
-    @AdapterState
-    private int getStateInternal() {
-        int state = BluetoothAdapter.STATE_OFF;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                state = mBluetoothGetStateCache.query(null);
-            }
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "", e.getCause());
-            } else {
-                throw e;
-            }
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return state;
-    }
-
-    /**
-     * Get the current state of the local Bluetooth adapter.
-     * <p>Possible return values are
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF}.
-     *
-     * @return current state of Bluetooth adapter
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    @AdapterState
-    public int getState() {
-        int state = getStateInternal();
-
-        // Consider all internal states as OFF
-        if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
-                || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            if (VDBG) {
-                Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
-            }
-            state = BluetoothAdapter.STATE_OFF;
-        }
-        if (VDBG) {
-            Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
-                    state));
-        }
-        return state;
-    }
-
-    /**
-     * Get the current state of the local Bluetooth adapter
-     * <p>This returns current internal state of Adapter including LE ON/OFF
-     *
-     * <p>Possible return values are
-     * {@link #STATE_OFF},
-     * {@link #STATE_BLE_TURNING_ON},
-     * {@link #STATE_BLE_ON},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     * {@link #STATE_BLE_TURNING_OFF}.
-     *
-     * @return current state of Bluetooth adapter
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    @AdapterState
-    @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
-            + "whether you can use BLE & BT classic.")
-    public int getLeState() {
-        int state = getStateInternal();
-
-        if (VDBG) {
-            Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
-        }
-        return state;
-    }
-
-    boolean getLeAccess() {
-        if (getLeState() == STATE_ON) {
-            return true;
-        } else if (getLeState() == STATE_BLE_ON) {
-            return true; // TODO: FILTER SYSTEM APPS HERE <--
-        }
-
-        return false;
-    }
-
-    /**
-     * Turn on the local Bluetooth adapter&mdash;do not use without explicit
-     * user action to turn on Bluetooth.
-     * <p>This powers on the underlying Bluetooth hardware, and starts all
-     * Bluetooth system services.
-     * <p class="caution"><strong>Bluetooth should never be enabled without
-     * direct user consent</strong>. If you want to turn on Bluetooth in order
-     * to create a wireless connection, you should use the {@link
-     * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests
-     * user permission to turn on Bluetooth. The {@link #enable()} method is
-     * provided only for applications that include a user interface for changing
-     * system settings, such as a "power manager" app.</p>
-     * <p>This is an asynchronous call: it will return immediately, and
-     * clients should listen for {@link #ACTION_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes. If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
-     * later transition to either {@link #STATE_OFF} or {@link
-     * #STATE_ON}. If this call returns false then there was an
-     * immediate problem that will prevent the adapter from being turned on -
-     * such as Airplane mode, or the adapter is already turned on.
-     *
-     * @return true to indicate adapter startup has begun, or false on immediate error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enable() {
-        if (isEnabled()) {
-            if (DBG) {
-                Log.d(TAG, "enable(): BT already enabled!");
-            }
-            return true;
-        }
-        try {
-            return mManagerService.enable(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Turn off the local Bluetooth adapter&mdash;do not use without explicit
-     * user action to turn off Bluetooth.
-     * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
-     * system services, and powers down the underlying Bluetooth hardware.
-     * <p class="caution"><strong>Bluetooth should never be disabled without
-     * direct user consent</strong>. The {@link #disable()} method is
-     * provided only for applications that include a user interface for changing
-     * system settings, such as a "power manager" app.</p>
-     * <p>This is an asynchronous call: it will return immediately, and
-     * clients should listen for {@link #ACTION_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes. If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
-     * later transition to either {@link #STATE_OFF} or {@link
-     * #STATE_ON}. If this call returns false then there was an
-     * immediate problem that will prevent the adapter from being turned off -
-     * such as the adapter already being turned off.
-     *
-     * @return true to indicate adapter shutdown has begun, or false on immediate error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disable() {
-        try {
-            return mManagerService.disable(mAttributionSource, true);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Turn off the local Bluetooth adapter and don't persist the setting.
-     *
-     * @param persist Indicate whether the off state should be persisted following the next reboot
-     * @return true to indicate adapter shutdown has begun, or false on immediate error
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disable(boolean persist) {
-
-        try {
-            return mManagerService.disable(mAttributionSource, persist);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Returns the hardware address of the local Bluetooth adapter.
-     * <p>For example, "00:11:22:AA:BB:CC".
-     *
-     * @return Bluetooth hardware address as string
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.LOCAL_MAC_ADDRESS,
-    })
-    public String getAddress() {
-        try {
-            return mManagerService.getAddress(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Get the friendly Bluetooth name of the local Bluetooth adapter.
-     * <p>This name is visible to remote Bluetooth devices.
-     *
-     * @return the Bluetooth name, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getName() {
-        try {
-            return mManagerService.getName(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /** {@hide} */
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public int getNameLengthForAdvertise() {
-        try {
-            return mService.getNameLengthForAdvertise(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return -1;
-    }
-
-    /**
-     * Factory reset bluetooth settings.
-     *
-     * @return true to indicate that the config file was successfully cleared
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean factoryReset() {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null && mService.factoryReset(mAttributionSource)
-                    && mManagerService != null
-                    && mManagerService.onFactoryReset(mAttributionSource)) {
-                return true;
-            }
-            Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
-            BluetoothProperties.factory_reset(true);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Get the UUIDs supported by the local Bluetooth adapter.
-     *
-     * @return the UUIDs supported by the local Bluetooth Adapter.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @Nullable ParcelUuid[] getUuids() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getUuids(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Set the friendly Bluetooth name of the local Bluetooth adapter.
-     * <p>This name is visible to remote Bluetooth devices.
-     * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8
-     * encoding, although many remote devices can only display the first
-     * 40 characters, and some may be limited to just 20.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @param name a valid Bluetooth name
-     * @return true if the name was set, false otherwise
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setName(String name) {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setName(name, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
-     * adapter.
-     *
-     * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothClass getBluetoothClass() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getBluetoothClass(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
-     * adapter.
-     *
-     * <p>Note: This value persists across system reboot.
-     *
-     * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to.
-     * @return true if successful, false if unsuccessful.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setBluetoothClass(bluetoothClass, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the Input/Output capability of the device for classic Bluetooth.
-     *
-     * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *         {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
-     *         {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @IoCapability
-    public int getIoCapability() {
-        if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.getIoCapability(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-    }
-
-    /**
-     * Sets the Input/Output capability of the device for classic Bluetooth.
-     *
-     * <p>Changing the Input/Output capability of a device only takes effect on restarting the
-     * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
-     * and {@link BluetoothAdapter#enable()} to see the changes.
-     *
-     * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *                   {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
-     *                   {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setIoCapability(@IoCapability int capability) {
-        if (getState() != STATE_ON) return false;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.setIoCapability(capability, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the Input/Output capability of the device for BLE operations.
-     *
-     * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *         {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
-     *         {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @IoCapability
-    public int getLeIoCapability() {
-        if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.getLeIoCapability(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-    }
-
-    /**
-     * Sets the Input/Output capability of the device for BLE operations.
-     *
-     * <p>Changing the Input/Output capability of a device only takes effect on restarting the
-     * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
-     * and {@link BluetoothAdapter#enable()} to see the changes.
-     *
-     * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *                   {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
-     *                   {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setLeIoCapability(@IoCapability int capability) {
-        if (getState() != STATE_ON) return false;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Get the current Bluetooth scan mode of the local Bluetooth adapter.
-     * <p>The Bluetooth scan mode determines if the local adapter is
-     * connectable and/or discoverable from remote Bluetooth devices.
-     * <p>Possible values are:
-     * {@link #SCAN_MODE_NONE},
-     * {@link #SCAN_MODE_CONNECTABLE},
-     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return scan mode
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @ScanMode
-    public int getScanMode() {
-        if (getState() != STATE_ON) {
-            return SCAN_MODE_NONE;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getScanMode(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return SCAN_MODE_NONE;
-    }
-
-    /**
-     * Set the local Bluetooth adapter connectablility and discoverability.
-     * <p>If the scan mode is set to {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
-     * it will change to {@link #SCAN_MODE_CONNECTABLE} after the discoverable timeout.
-     * The discoverable timeout can be set with {@link #setDiscoverableTimeout} and
-     * checked with {@link #getDiscoverableTimeout}. By default, the timeout is usually
-     * 120 seconds on phones which is enough for a remote device to initiate and complete
-     * its discovery process.
-     * <p>Applications cannot set the scan mode. They should use
-     * {@link #ACTION_REQUEST_DISCOVERABLE} instead.
-     *
-     * @param mode represents the desired state of the local device scan mode
-     *
-     * @return status code indicating whether the scan mode was successfully set
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    @ScanModeStatusCode
-    public int setScanMode(@ScanMode int mode) {
-        if (getState() != STATE_ON) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setScanMode(mode, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Get the timeout duration of the {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
-     *
-     * @return the duration of the discoverable timeout or null if an error has occurred
-     */
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public @Nullable Duration getDiscoverableTimeout() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                long timeout = mService.getDiscoverableTimeout(mAttributionSource);
-                return (timeout == -1) ? null : Duration.ofSeconds(timeout);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Set the total time the Bluetooth local adapter will stay discoverable when
-     * {@link #setScanMode} is called with {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} mode.
-     * After this timeout, the scan mode will fallback to {@link #SCAN_MODE_CONNECTABLE}.
-     * <p>If <code>timeout</code> is set to 0, no timeout will occur and the scan mode will
-     * be persisted until a subsequent call to {@link #setScanMode}.
-     *
-     * @param timeout represents the total duration the local Bluetooth adapter will remain
-     *                discoverable, or no timeout if set to 0
-     * @return whether the timeout was successfully set
-     * @throws IllegalArgumentException if <code>timeout</code> duration in seconds is more
-     *         than {@link Integer#MAX_VALUE}
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    @ScanModeStatusCode
-    public int setDiscoverableTimeout(@NonNull Duration timeout) {
-        if (getState() != STATE_ON) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        if (timeout.toSeconds() > Integer.MAX_VALUE) {
-            throw new IllegalArgumentException("Timeout in seconds must be less or equal to "
-                    + Integer.MAX_VALUE);
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setDiscoverableTimeout(timeout.toSeconds(), mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Get the end time of the latest remote device discovery process.
-     *
-     * @return the latest time that the bluetooth adapter was/will be in discovery mode, in
-     * milliseconds since the epoch. This time can be in the future if {@link #startDiscovery()} has
-     * been called recently.
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public long getDiscoveryEndMillis() {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getDiscoveryEndMillis(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return -1;
-    }
-
-    /**
-     * Start the remote device discovery process.
-     * <p>The discovery process usually involves an inquiry scan of about 12
-     * seconds, followed by a page scan of each new device to retrieve its
-     * Bluetooth name.
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_DISCOVERY_STARTED} and {@link
-     * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
-     * discovery starts and completes. Register for {@link
-     * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
-     * are found.
-     * <p>Device discovery is a heavyweight procedure. New connections to
-     * remote Bluetooth devices should not be attempted while discovery is in
-     * progress, and existing connections will experience limited bandwidth
-     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
-     * discovery. Discovery is not managed by the Activity,
-     * but is run as a system service, so an application should always call
-     * {@link BluetoothAdapter#cancelDiscovery()} even if it
-     * did not directly request a discovery, just to be sure.
-     * <p>Device discovery will only find remote devices that are currently
-     * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
-     * not discoverable by default, and need to be entered into a special mode.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED}
-     * with {@link #STATE_ON} to get the updated value.
-     * <p>If a device is currently bonding, this request will be queued and executed once that
-     * device has finished bonding. If a request is already queued, this request will be ignored.
-     *
-     * @return true on success, false on error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean startDiscovery() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.startDiscovery(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Cancel the current device discovery process.
-     * <p>Because discovery is a heavyweight procedure for the Bluetooth
-     * adapter, this method should always be called before attempting to connect
-     * to a remote device with {@link
-     * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
-     * the  Activity, but is run as a system service, so an application should
-     * always call cancel discovery even if it did not directly request a
-     * discovery, just to be sure.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return true on success, false on error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean cancelDiscovery() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.cancelDiscovery(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if the local Bluetooth adapter is currently in the device
-     * discovery process.
-     * <p>Device discovery is a heavyweight procedure. New connections to
-     * remote Bluetooth devices should not be attempted while discovery is in
-     * progress, and existing connections will experience limited bandwidth
-     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
-     * discovery.
-     * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
-     * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
-     * starts or completes.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return true if discovering
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean isDiscovering() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isDiscovering(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Removes the active device for the grouping of @ActiveDeviceUse specified
-     *
-     * @param profiles represents the purpose for which we are setting this as the active device.
-     *                 Possible values are:
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
-     * @return false on immediate error, true otherwise
-     * @throws IllegalArgumentException if device is null or profiles is not one of
-     * {@link ActiveDeviceUse}
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
-        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
-                && profiles != ACTIVE_DEVICE_ALL) {
-            Log.e(TAG, "Invalid profiles param value in removeActiveDevice");
-            throw new IllegalArgumentException("Profiles must be one of "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
-                return mService.removeActiveDevice(profiles, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * Sets device as the active devices for the profiles passed into the function
-     *
-     * @param device is the remote bluetooth device
-     * @param profiles represents the purpose for which we are setting this as the active device.
-     *                 Possible values are:
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
-     * @return false on immediate error, true otherwise
-     * @throws IllegalArgumentException if device is null or profiles is not one of
-     * {@link ActiveDeviceUse}
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean setActiveDevice(@NonNull BluetoothDevice device,
-            @ActiveDeviceUse int profiles) {
-        if (device == null) {
-            Log.e(TAG, "setActiveDevice: Null device passed as parameter");
-            throw new IllegalArgumentException("device cannot be null");
-        }
-        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
-                && profiles != ACTIVE_DEVICE_ALL) {
-            Log.e(TAG, "Invalid profiles param value in setActiveDevice");
-            throw new IllegalArgumentException("Profiles must be one of "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                if (DBG) {
-                    Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
-                }
-                return mService.setActiveDevice(device, profiles, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * Get the active devices for the BluetoothProfile specified
-     *
-     * @param profile is the profile from which we want the active devices.
-     *                Possible values are:
-     *                {@link BluetoothProfile#HEADSET},
-     *                {@link BluetoothProfile#A2DP},
-     *                {@link BluetoothProfile#HEARING_AID}
-     *                {@link BluetoothProfile#LE_AUDIO}
-     * @return A list of active bluetooth devices
-     * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile}
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) {
-        if (profile != BluetoothProfile.HEADSET
-                && profile != BluetoothProfile.A2DP
-                && profile != BluetoothProfile.HEARING_AID
-                && profile != BluetoothProfile.LE_AUDIO) {
-            Log.e(TAG, "Invalid profile param value in getActiveDevices");
-            throw new IllegalArgumentException("Profiles must be one of "
-                    + "BluetoothProfile.A2DP, "
-                    + "BluetoothProfile.HEARING_AID, or"
-                    + "BluetoothProfile.HEARING_AID"
-                    + "BluetoothProfile.LE_AUDIO");
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                if (DBG) {
-                    Log.d(TAG, "getActiveDevices(profile= "
-                            + BluetoothProfile.getProfileName(profile) + ")");
-                }
-                return mService.getActiveDevices(profile, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return new ArrayList<>();
-    }
-
-    /**
-     * Return true if the multi advertisement is supported by the chipset
-     *
-     * @return true if Multiple Advertisement feature is supported
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isMultipleAdvertisementSupported() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isMultiAdvertisementSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p>
-     *
-     * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and
-     * fetch scan results even when Bluetooth is turned off.<p>
-     *
-     * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresNoPermission
-    public boolean isBleScanAlwaysAvailable() {
-        try {
-            return mManagerService.isBleScanAlwaysAvailable();
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception when calling isBleScanAlwaysAvailable", e);
-            return false;
-        }
-    }
-
-    private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
-            "cache_key.bluetooth.is_offloaded_filtering_supported";
-    private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
-            new PropertyInvalidatedCache<Void, Boolean>(
-                8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                protected Boolean recompute(Void query) {
-                    try {
-                        mServiceLock.readLock().lock();
-                        if (mService != null) {
-                            return mService.isOffloadedFilteringSupported();
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
-                    } finally {
-                        mServiceLock.readLock().unlock();
-                    }
-                    return false;
-
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableIsOffloadedFilteringSupportedCache() {
-        mBluetoothFilteringCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateIsOffloadedFilteringSupportedCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
-    }
-
-    /**
-     * Return true if offloaded filters are supported
-     *
-     * @return true if chipset supports on-chip filtering
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isOffloadedFilteringSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        return mBluetoothFilteringCache.query(null);
-    }
-
-    /**
-     * Return true if offloaded scan batching is supported
-     *
-     * @return true if chipset supports on-chip scan batching
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isOffloadedScanBatchingSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isOffloadedScanBatchingSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE 2M PHY feature is supported.
-     *
-     * @return true if chipset supports LE 2M PHY feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLe2MPhySupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLe2MPhySupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE Coded PHY feature is supported.
-     *
-     * @return true if chipset supports LE Coded PHY feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLeCodedPhySupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLeCodedPhySupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE Extended Advertising feature is supported.
-     *
-     * @return true if chipset supports LE Extended Advertising feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLeExtendedAdvertisingSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLeExtendedAdvertisingSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE Periodic Advertising feature is supported.
-     *
-     * @return true if chipset supports LE Periodic Advertising feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLePeriodicAdvertisingSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLePeriodicAdvertisingSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED,
-    })
-    public @interface LeFeatureReturnValues {}
-
-    /**
-     * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is
-     * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
-     * the feature is not supported or an error code.
-     *
-     * @return whether the LE audio is supported
-     */
-    @RequiresNoPermission
-    public @LeFeatureReturnValues int isLeAudioSupported() {
-        if (!getLeAccess()) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLeAudioSupported();
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender
-     * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
-     * feature is not supported or an error code
-     *
-     * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature
-     */
-    @RequiresNoPermission
-    public @LeFeatureReturnValues int isLePeriodicAdvertisingSyncTransferSenderSupported() {
-        if (!getLeAccess()) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLePeriodicAdvertisingSyncTransferSenderSupported();
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Return the maximum LE advertising data length in bytes,
-     * if LE Extended Advertising feature is supported, 0 otherwise.
-     *
-     * @return the maximum LE advertising data length.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public int getLeMaximumAdvertisingDataLength() {
-        if (!getLeAccess()) {
-            return 0;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getLeMaximumAdvertisingDataLength();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return 0;
-    }
-
-    /**
-     * Return true if Hearing Aid Profile is supported.
-     *
-     * @return true if phone supports Hearing Aid Profile
-     */
-    @RequiresNoPermission
-    private boolean isHearingAidProfileSupported() {
-        try {
-            return mManagerService.isHearingAidProfileSupported();
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception when calling isHearingAidProfileSupported", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get the maximum number of connected audio devices.
-     *
-     * @return the maximum number of connected audio devices
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getMaxConnectedAudioDevices() {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getMaxConnectedAudioDevices(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return 1;
-    }
-
-    /**
-     * Return true if hardware has entries available for matching beacons
-     *
-     * @return true if there are hw entries available for matching beacons
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isHardwareTrackingFiltersAvailable() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported
-                return false;
-            }
-            return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Request the record of {@link BluetoothActivityEnergyInfo} object that
-     * has the activity and energy info. This can be used to ascertain what
-     * the controller has been up to, since the last sample.
-     *
-     * A null value for the activity info object may be sent if the bluetooth service is
-     * unreachable or the device does not support reporting such information.
-     *
-     * @param result The callback to which to send the activity info.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void requestControllerActivityEnergyInfo(ResultReceiver result) {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                mService.requestActivityInfo(result, mAttributionSource);
-                result = null;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
-        } finally {
-            mServiceLock.readLock().unlock();
-            if (result != null) {
-                // Only send an immediate result if we failed.
-                result.send(0, null);
-            }
-        }
-    }
-
-    /**
-     * Fetches a list of the most recently connected bluetooth devices ordered by how recently they
-     * were connected with most recently first and least recently last
-     *
-     * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were
-     * connected
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
-        if (getState() != STATE_ON) {
-            return new ArrayList<>();
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return Attributable.setAttributionSource(
-                        mService.getMostRecentlyConnectedDevices(mAttributionSource),
-                        mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return new ArrayList<>();
-    }
-
-    /**
-     * Return the set of {@link BluetoothDevice} objects that are bonded
-     * (paired) to the local adapter.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return an empty set. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return unmodifiable set of {@link BluetoothDevice}, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public Set<BluetoothDevice> getBondedDevices() {
-        if (getState() != STATE_ON) {
-            return toDeviceSet(Arrays.asList());
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return toDeviceSet(Attributable.setAttributionSource(
-                        Arrays.asList(mService.getBondedDevices(mAttributionSource)),
-                        mAttributionSource));
-            }
-            return toDeviceSet(Arrays.asList());
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Gets the currently supported profiles by the adapter.
-     *
-     * <p> This can be used to check whether a profile is supported before attempting
-     * to connect to its respective proxy.
-     *
-     * @return a list of integers indicating the ids of supported profiles as defined in {@link
-     * BluetoothProfile}.
-     * @hide
-     */
-    @RequiresNoPermission
-    public @NonNull List<Integer> getSupportedProfiles() {
-        final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
-
-        try {
-            synchronized (mManagerCallback) {
-                if (mService != null) {
-                    final long supportedProfilesBitMask = mService.getSupportedProfiles();
-
-                    for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
-                        if ((supportedProfilesBitMask & (1 << i)) != 0) {
-                            supportedProfiles.add(i);
-                        }
-                    }
-                } else {
-                    // Bluetooth is disabled. Just fill in known supported Profiles
-                    if (isHearingAidProfileSupported()) {
-                        supportedProfiles.add(BluetoothProfile.HEARING_AID);
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "getSupportedProfiles:", e);
-        }
-        return supportedProfiles;
-    }
-
-    private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY =
-            "cache_key.bluetooth.get_adapter_connection_state";
-    private final PropertyInvalidatedCache<Void, Integer>
-            mBluetoothGetAdapterConnectionStateCache =
-            new PropertyInvalidatedCache<Void, Integer> (
-                8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) {
-                /**
-                 * This method must not be called when mService is null.
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                protected Integer recompute(Void query) {
-                    try {
-                        return mService.getAdapterConnectionState();
-                    } catch (RemoteException e) {
-                        throw e.rethrowAsRuntimeException();
-                    }
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableGetAdapterConnectionStateCache() {
-        mBluetoothGetAdapterConnectionStateCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateGetAdapterConnectionStateCache() {
-        PropertyInvalidatedCache.invalidateCache(
-            BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY);
-    }
-
-    /**
-     * Get the current connection state of the local Bluetooth adapter.
-     * This can be used to check whether the local Bluetooth adapter is connected
-     * to any profile of any other remote Bluetooth Device.
-     *
-     * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED}
-     * intent to get the connection state of the adapter.
-     *
-     * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, {@link
-     * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public int getConnectionState() {
-        if (getState() != STATE_ON) {
-            return BluetoothAdapter.STATE_DISCONNECTED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mBluetoothGetAdapterConnectionStateCache.query(null);
-            }
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "getConnectionState:", e.getCause());
-            } else {
-                throw e;
-            }
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothAdapter.STATE_DISCONNECTED;
-    }
-
-    private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
-            "cache_key.bluetooth.get_profile_connection_state";
-    private final PropertyInvalidatedCache<Integer, Integer>
-            mGetProfileConnectionStateCache =
-            new PropertyInvalidatedCache<Integer, Integer>(
-                8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                protected Integer recompute(Integer query) {
-                    try {
-                        mServiceLock.readLock().lock();
-                        if (mService != null) {
-                            return mService.getProfileConnectionState(query);
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "getProfileConnectionState:", e);
-                    } finally {
-                        mServiceLock.readLock().unlock();
-                    }
-                    return BluetoothProfile.STATE_DISCONNECTED;
-                }
-                @Override
-                public String queryToString(Integer query) {
-                    return String.format("getProfileConnectionState(profile=\"%d\")",
-                                         query);
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableGetProfileConnectionStateCache() {
-        mGetProfileConnectionStateCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateGetProfileConnectionStateCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
-    }
-
-    /**
-     * Get the current connection state of a profile.
-     * This function can be used to check whether the local Bluetooth adapter
-     * is connected to any remote device for a specific profile.
-     * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
-     *
-     * <p> Return value can be one of
-     * {@link BluetoothProfile#STATE_DISCONNECTED},
-     * {@link BluetoothProfile#STATE_CONNECTING},
-     * {@link BluetoothProfile#STATE_CONNECTED},
-     * {@link BluetoothProfile#STATE_DISCONNECTING}
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public int getProfileConnectionState(int profile) {
-        if (getState() != STATE_ON) {
-            return BluetoothProfile.STATE_DISCONNECTED;
-        }
-        return mGetProfileConnectionStateCache.query(new Integer(profile));
-    }
-
-    /**
-     * Create a listening, secure RFCOMM Bluetooth socket.
-     * <p>A remote device connecting to this socket will be authenticated and
-     * communication on this socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>Valid RFCOMM channels are in range 1 to 30.
-     *
-     * @param channel RFCOMM channel to listen on
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
-        return listenUsingRfcommOn(channel, false, false);
-    }
-
-    /**
-     * Create a listening, secure RFCOMM Bluetooth socket.
-     * <p>A remote device connecting to this socket will be authenticated and
-     * communication on this socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>Valid RFCOMM channels are in range 1 to 30.
-     * <p>To auto assign a channel without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
-     *
-     * @param channel RFCOMM channel to listen on
-     * @param mitm enforce person-in-the-middle protection for authentication.
-     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
-     * connections.
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
-            boolean min16DigitPin) throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm,
-                        min16DigitPin);
-        int errno = socket.mSocket.bindListen();
-        if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
-     * <p>A remote device connecting to this socket will be authenticated and
-     * communication on this socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>The system will assign an unused RFCOMM channel to listen on.
-     * <p>The system will also register a Service Discovery
-     * Protocol (SDP) record with the local SDP server containing the specified
-     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
-     * can use the same UUID to query our SDP server and discover which channel
-     * to connect to. This SDP record will be removed when this socket is
-     * closed, or if this application closes unexpectedly.
-     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
-     * connect to this socket from another device using the same {@link UUID}.
-     *
-     * @param name service name for SDP record
-     * @param uuid uuid for SDP record
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
-            throws IOException {
-        return createNewRfcommSocketAndRecord(name, uuid, true, true);
-    }
-
-    /**
-     * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
-     * <p>The link key is not required to be authenticated, i.e the communication may be
-     * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices,
-     * the link will be encrypted, as encryption is mandatory.
-     * For legacy devices (pre Bluetooth 2.1 devices) the link will not
-     * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
-     * encrypted and authenticated communication channel is desired.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>The system will assign an unused RFCOMM channel to listen on.
-     * <p>The system will also register a Service Discovery
-     * Protocol (SDP) record with the local SDP server containing the specified
-     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
-     * can use the same UUID to query our SDP server and discover which channel
-     * to connect to. This SDP record will be removed when this socket is
-     * closed, or if this application closes unexpectedly.
-     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
-     * connect to this socket from another device using the same {@link UUID}.
-     *
-     * @param name service name for SDP record
-     * @param uuid uuid for SDP record
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
-            throws IOException {
-        return createNewRfcommSocketAndRecord(name, uuid, false, false);
-    }
-
-    /**
-     * Create a listening, encrypted,
-     * RFCOMM Bluetooth socket with Service Record.
-     * <p>The link will be encrypted, but the link key is not required to be authenticated
-     * i.e the communication is vulnerable to Person In the Middle attacks. Use
-     * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
-     * <p> Use this socket if authentication of link key is not possible.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not have
-     * an input and output capability or just has the ability to display a numeric key,
-     * a secure socket connection is not possible and this socket can be used.
-     * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
-     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>The system will assign an unused RFCOMM channel to listen on.
-     * <p>The system will also register a Service Discovery
-     * Protocol (SDP) record with the local SDP server containing the specified
-     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
-     * can use the same UUID to query our SDP server and discover which channel
-     * to connect to. This SDP record will be removed when this socket is
-     * closed, or if this application closes unexpectedly.
-     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
-     * connect to this socket from another device using the same {@link UUID}.
-     *
-     * @param name service name for SDP record
-     * @param uuid uuid for SDP record
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
-            throws IOException {
-        return createNewRfcommSocketAndRecord(name, uuid, false, true);
-    }
-
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
-            boolean auth, boolean encrypt) throws IOException {
-        BluetoothServerSocket socket;
-        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt,
-                new ParcelUuid(uuid));
-        socket.setServiceName(name);
-        int errno = socket.mSocket.bindListen();
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
-     * Call #accept to retrieve connections to this socket.
-     *
-     * @return An RFCOMM BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct an encrypted, authenticated, L2CAP server socket.
-     * Call #accept to retrieve connections to this socket.
-     * <p>To auto assign a port without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
-     *
-     * @param port the PSM to listen on
-     * @param mitm enforce person-in-the-middle protection for authentication.
-     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
-     * connections.
-     * @return An L2CAP BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
-            throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm,
-                        min16DigitPin);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            int assignedChannel = socket.mSocket.getPort();
-            if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
-            socket.setChannel(assignedChannel);
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct an encrypted, authenticated, L2CAP server socket.
-     * Call #accept to retrieve connections to this socket.
-     * <p>To auto assign a port without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
-     *
-     * @param port the PSM to listen on
-     * @return An L2CAP BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
-        return listenUsingL2capOn(port, false, false);
-    }
-
-    /**
-     * Construct an insecure L2CAP server socket.
-     * Call #accept to retrieve connections to this socket.
-     * <p>To auto assign a port without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
-     *
-     * @param port the PSM to listen on
-     * @return An L2CAP BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
-        Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
-                                          false);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            int assignedChannel = socket.mSocket.getPort();
-            if (DBG) {
-                Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
-                        + assignedChannel);
-            }
-            socket.setChannel(assignedChannel);
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-
-    }
-
-    /**
-     * Read the local Out of Band Pairing Data
-     *
-     * @return Pair<byte[], byte[]> of Hash and Randomizer
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public Pair<byte[], byte[]> readOutOfBandData() {
-        return null;
-    }
-
-    /**
-     * Get the profile proxy object associated with the profile.
-     *
-     * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link
-     * BluetoothProfile#GATT_SERVER}. Clients must implement {@link
-     * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the
-     * proxy object.
-     *
-     * @param context Context of the application
-     * @param listener The service Listener for connection callbacks.
-     * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
-     * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link
-     * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}.
-     * @return true on success, false on error
-     */
-    @SuppressLint({
-        "AndroidFrameworkRequiresPermission",
-        "AndroidFrameworkBluetoothPermission"
-    })
-    public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
-            int profile) {
-        if (context == null || listener == null) {
-            return false;
-        }
-
-        if (profile == BluetoothProfile.HEADSET) {
-            BluetoothHeadset headset = new BluetoothHeadset(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.A2DP) {
-            BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.A2DP_SINK) {
-            BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
-            BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HID_HOST) {
-            BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.PAN) {
-            BluetoothPan pan = new BluetoothPan(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.PBAP) {
-            BluetoothPbap pbap = new BluetoothPbap(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HEALTH) {
-            Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
-            return false;
-        } else if (profile == BluetoothProfile.MAP) {
-            BluetoothMap map = new BluetoothMap(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
-            BluetoothHeadsetClient headsetClient =
-                    new BluetoothHeadsetClient(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.SAP) {
-            BluetoothSap sap = new BluetoothSap(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.PBAP_CLIENT) {
-            BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.MAP_CLIENT) {
-            BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HID_DEVICE) {
-            BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HEARING_AID) {
-            if (isHearingAidProfileSupported()) {
-                BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this);
-                return true;
-            }
-            return false;
-        } else if (profile == BluetoothProfile.LE_AUDIO) {
-            BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.VOLUME_CONTROL) {
-            BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) {
-            BluetoothCsipSetCoordinator csipSetCoordinator =
-                    new BluetoothCsipSetCoordinator(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
-            BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Close the connection of the profile proxy to the Service.
-     *
-     * <p> Clients should call this when they are no longer using
-     * the proxy obtained from {@link #getProfileProxy}.
-     * Profile can be one of  {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}
-     *
-     * @param profile
-     * @param proxy Profile proxy object
-     */
-    @SuppressLint({
-            "AndroidFrameworkRequiresPermission",
-            "AndroidFrameworkBluetoothPermission"
-    })
-    public void closeProfileProxy(int profile, BluetoothProfile proxy) {
-        if (proxy == null) {
-            return;
-        }
-
-        switch (profile) {
-            case BluetoothProfile.HEADSET:
-                BluetoothHeadset headset = (BluetoothHeadset) proxy;
-                headset.close();
-                break;
-            case BluetoothProfile.A2DP:
-                BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
-                a2dp.close();
-                break;
-            case BluetoothProfile.A2DP_SINK:
-                BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink) proxy;
-                a2dpSink.close();
-                break;
-            case BluetoothProfile.AVRCP_CONTROLLER:
-                BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy;
-                avrcp.close();
-                break;
-            case BluetoothProfile.HID_HOST:
-                BluetoothHidHost iDev = (BluetoothHidHost) proxy;
-                iDev.close();
-                break;
-            case BluetoothProfile.PAN:
-                BluetoothPan pan = (BluetoothPan) proxy;
-                pan.close();
-                break;
-            case BluetoothProfile.PBAP:
-                BluetoothPbap pbap = (BluetoothPbap) proxy;
-                pbap.close();
-                break;
-            case BluetoothProfile.GATT:
-                BluetoothGatt gatt = (BluetoothGatt) proxy;
-                gatt.close();
-                break;
-            case BluetoothProfile.GATT_SERVER:
-                BluetoothGattServer gattServer = (BluetoothGattServer) proxy;
-                gattServer.close();
-                break;
-            case BluetoothProfile.MAP:
-                BluetoothMap map = (BluetoothMap) proxy;
-                map.close();
-                break;
-            case BluetoothProfile.HEADSET_CLIENT:
-                BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient) proxy;
-                headsetClient.close();
-                break;
-            case BluetoothProfile.SAP:
-                BluetoothSap sap = (BluetoothSap) proxy;
-                sap.close();
-                break;
-            case BluetoothProfile.PBAP_CLIENT:
-                BluetoothPbapClient pbapClient = (BluetoothPbapClient) proxy;
-                pbapClient.close();
-                break;
-            case BluetoothProfile.MAP_CLIENT:
-                BluetoothMapClient mapClient = (BluetoothMapClient) proxy;
-                mapClient.close();
-                break;
-            case BluetoothProfile.HID_DEVICE:
-                BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
-                hidDevice.close();
-                break;
-            case BluetoothProfile.HEARING_AID:
-                BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
-                hearingAid.close();
-                break;
-            case BluetoothProfile.LE_AUDIO:
-                BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
-                leAudio.close();
-                break;
-            case BluetoothProfile.VOLUME_CONTROL:
-                BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
-                vcs.close();
-                break;
-            case BluetoothProfile.CSIP_SET_COORDINATOR:
-                BluetoothCsipSetCoordinator csipSetCoordinator =
-                        (BluetoothCsipSetCoordinator) proxy;
-                csipSetCoordinator.close();
-                break;
-            case BluetoothProfile.LE_CALL_CONTROL:
-                BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
-                tbs.close();
-                break;
-        }
-    }
-
-    private static final IBluetoothManagerCallback sManagerCallback =
-            new IBluetoothManagerCallback.Stub() {
-                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
-                    }
-
-                    synchronized (sServiceLock) {
-                        sService = bluetoothService;
-                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceUp(bluetoothService);
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceUp: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
-                    }
-                }
-
-                public void onBluetoothServiceDown() {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothServiceDown");
-                    }
-
-                    synchronized (sServiceLock) {
-                        sService = null;
-                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceDown();
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
-                    }
-                }
-
-                public void onBrEdrDown() {
-                    if (VDBG) {
-                        Log.i(TAG, "onBrEdrDown");
-                    }
-
-                    synchronized (sServiceLock) {
-                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBrEdrDown();
-                                } else {
-                                    Log.d(TAG, "onBrEdrDown: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
-                    }
-                }
-            };
-
-    private final IBluetoothManagerCallback mManagerCallback =
-            new IBluetoothManagerCallback.Stub() {
-                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    synchronized (mServiceLock.writeLock()) {
-                        mService = bluetoothService;
-                    }
-                    synchronized (mMetadataListeners) {
-                        mMetadataListeners.forEach((device, pair) -> {
-                            try {
-                                mService.registerMetadataListener(mBluetoothMetadataListener,
-                                        device, mAttributionSource);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Failed to register metadata listener", e);
-                            }
-                        });
-                    }
-                    synchronized (mBluetoothConnectionCallbackExecutorMap) {
-                        if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-                            try {
-                                mService.registerBluetoothConnectionCallback(mConnectionCallback,
-                                        mAttributionSource);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
-                                        + "connection callback", e);
-                            }
-                        }
-                    }
-                }
-
-                public void onBluetoothServiceDown() {
-                    synchronized (mServiceLock.writeLock()) {
-                        mService = null;
-                        if (mLeScanClients != null) {
-                            mLeScanClients.clear();
-                        }
-                        if (mBluetoothLeAdvertiser != null) {
-                            mBluetoothLeAdvertiser.cleanup();
-                        }
-                        if (mBluetoothLeScanner != null) {
-                            mBluetoothLeScanner.cleanup();
-                        }
-                    }
-                }
-
-                public void onBrEdrDown() {
-                }
-            };
-
-    /**
-     * Enable the Bluetooth Adapter, but don't auto-connect devices
-     * and don't persist state. Only for use by system applications.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enableNoAutoConnect() {
-        if (isEnabled()) {
-            if (DBG) {
-                Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
-            }
-            return true;
-        }
-        try {
-            return mManagerService.enableNoAutoConnect(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST,
-    })
-    public @interface OobError {}
-
-    /**
-     * Provides callback methods for receiving {@link OobData} from the host stack, as well as an
-     * error interface in order to allow the caller to determine next steps based on the {@code
-     * ErrorCode}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface OobDataCallback {
-        /**
-         * Handles the {@link OobData} received from the host stack.
-         *
-         * @param transport - whether the {@link OobData} is generated for LE or Classic.
-         * @param oobData - data generated in the host stack(LE) or controller (Classic)
-         */
-        void onOobData(@Transport int transport, @NonNull OobData oobData);
-
-        /**
-         * Provides feedback when things don't go as expected.
-         *
-         * @param errorCode - the code describing the type of error that occurred.
-         */
-        void onError(@OobError int errorCode);
-    }
-
-    /**
-     * Wraps an AIDL interface around an {@link OobDataCallback} interface.
-     *
-     * @see {@link IBluetoothOobDataCallback} for interface definition.
-     *
-     * @hide
-     */
-    public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub {
-        private final OobDataCallback mCallback;
-        private final Executor mExecutor;
-
-        /**
-         * @param callback - object to receive {@link OobData} must be a non null argument
-         *
-         * @throws NullPointerException if the callback is null.
-         */
-        WrappedOobDataCallback(@NonNull OobDataCallback callback,
-                @NonNull @CallbackExecutor Executor executor) {
-            requireNonNull(callback);
-            requireNonNull(executor);
-            mCallback = callback;
-            mExecutor = executor;
-        }
-        /**
-         * Wrapper function to relay to the {@link OobDataCallback#onOobData}
-         *
-         * @param transport - whether the {@link OobData} is generated for LE or Classic.
-         * @param oobData - data generated in the host stack(LE) or controller (Classic)
-         *
-         * @hide
-         */
-        public void onOobData(@Transport int transport, @NonNull OobData oobData) {
-            mExecutor.execute(new Runnable() {
-                public void run() {
-                    mCallback.onOobData(transport, oobData);
-                }
-            });
-        }
-        /**
-         * Wrapper function to relay to the {@link OobDataCallback#onError}
-         *
-         * @param errorCode - the code descibing the type of error that occurred.
-         *
-         * @hide
-         */
-        public void onError(@OobError int errorCode) {
-            mExecutor.execute(new Runnable() {
-                public void run() {
-                    mCallback.onError(errorCode);
-                }
-            });
-        }
-    }
-
-    /**
-     * Fetches a secret data value that can be used for a secure and simple pairing experience.
-     *
-     * <p>This is the Local Out of Band data the comes from the
-     *
-     * <p>This secret is the local Out of Band data.  This data is used to securely and quickly
-     * pair two devices with minimal user interaction.
-     *
-     * <p>For example, this secret can be transferred to a remote device out of band (meaning any
-     * other way besides using bluetooth).  Once the remote device finds this device using the
-     * information given in the data, such as the PUBLIC ADDRESS, the remote device could then
-     * connect to this device using this secret when the pairing sequenece asks for the secret.
-     * This device will respond by automatically accepting the pairing due to the secret being so
-     * trustworthy.
-     *
-     * @param transport - provide type of transport (e.g. LE or Classic).
-     * @param callback - target object to receive the {@link OobData} value.
-     *
-     * @throws NullPointerException if callback is null.
-     * @throws IllegalArgumentException if the transport is not valid.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void generateLocalOobData(@Transport int transport,
-            @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
-        if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
-                != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Invalid transport '" + transport + "'!");
-        }
-        requireNonNull(callback);
-        if (!isEnabled()) {
-            Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
-            callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED);
-        } else {
-            try {
-                mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
-                        executor), mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-    }
-
-    /**
-     * Enable control of the Bluetooth Adapter for a single application.
-     *
-     * <p>Some applications need to use Bluetooth for short periods of time to
-     * transfer data but don't want all the associated implications like
-     * automatic connection to headsets etc.
-     *
-     * <p> Multiple applications can call this. This is reference counted and
-     * Bluetooth disabled only when no one else is using it. There will be no UI
-     * shown to the user while bluetooth is being enabled. Any user action will
-     * override this call. For example, if user wants Bluetooth on and the last
-     * user of this API wanted to disable Bluetooth, Bluetooth will not be
-     * turned off.
-     *
-     * <p> This API is only meant to be used by internal applications. Third
-     * party applications but use {@link #enable} and {@link #disable} APIs.
-     *
-     * <p> If this API returns true, it means the callback will be called.
-     * The callback will be called with the current state of Bluetooth.
-     * If the state is not what was requested, an internal error would be the
-     * reason. If Bluetooth is already on and if this function is called to turn
-     * it on, the api will return true and a callback will be called.
-     *
-     * @param on True for on, false for off.
-     * @param callback The callback to notify changes to the state.
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean changeApplicationBluetoothState(boolean on,
-            BluetoothStateChangeCallback callback) {
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    public interface BluetoothStateChangeCallback {
-        /**
-         * @hide
-         */
-        void onBluetoothStateChange(boolean on);
-    }
-
-    /**
-     * @hide
-     */
-    public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
-        private BluetoothStateChangeCallback mCallback;
-
-        StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onBluetoothStateChange(boolean on) {
-            mCallback.onBluetoothStateChange(on);
-        }
-    }
-
-    private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) {
-        Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices);
-        return Collections.unmodifiableSet(deviceSet);
-    }
-
-    protected void finalize() throws Throwable {
-        try {
-            removeServiceStateCallback(mManagerCallback);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"
-     * <p>Alphabetic characters must be uppercase to be valid.
-     *
-     * @param address Bluetooth address as string
-     * @return true if the address is valid, false otherwise
-     */
-    public static boolean checkBluetoothAddress(String address) {
-        if (address == null || address.length() != ADDRESS_LENGTH) {
-            return false;
-        }
-        for (int i = 0; i < ADDRESS_LENGTH; i++) {
-            char c = address.charAt(i);
-            switch (i % 3) {
-                case 0:
-                case 1:
-                    if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
-                        // hex character, OK
-                        break;
-                    }
-                    return false;
-                case 2:
-                    if (c == ':') {
-                        break;  // OK
-                    }
-                    return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00"
-     * is a RANDOM STATIC address.
-     *
-     * RANDOM STATIC: (addr & 0xC0) == 0xC0
-     * RANDOM RESOLVABLE: (addr &  0xC0) == 0x40
-     * RANDOM non-RESOLVABLE: (addr &  0xC0) == 0x00
-     *
-     * @param address Bluetooth address as string
-     * @return true if the 2 Most Significant Bits of the address equals 0xC0.
-     *
-     * @hide
-     */
-    public static boolean isAddressRandomStatic(@NonNull String address) {
-        requireNonNull(address);
-        return checkBluetoothAddress(address)
-                && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0;
-    }
-
-    /** {@hide} */
-    @UnsupportedAppUsage
-    @RequiresNoPermission
-    public IBluetoothManager getBluetoothManager() {
-        return mManagerService;
-    }
-
-    /** {@hide} */
-    @RequiresNoPermission
-    public AttributionSource getAttributionSource() {
-        return mAttributionSource;
-    }
-
-    @GuardedBy("sServiceLock")
-    private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks =
-            new WeakHashMap<>();
-
-    /*package*/ IBluetooth getBluetoothService() {
-        synchronized (sServiceLock) {
-            if (sProxyServiceStateCallbacks.isEmpty()) {
-                throw new IllegalStateException(
-                        "Anonymous service access requires at least one lifecycle in process");
-            }
-            return sService;
-        }
-    }
-
-    @UnsupportedAppUsage
-    /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
-        Objects.requireNonNull(cb);
-        synchronized (sServiceLock) {
-            sProxyServiceStateCallbacks.put(cb, null);
-            registerOrUnregisterAdapterLocked();
-            return sService;
-        }
-    }
-
-    /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) {
-        Objects.requireNonNull(cb);
-        synchronized (sServiceLock) {
-            sProxyServiceStateCallbacks.remove(cb);
-            registerOrUnregisterAdapterLocked();
-        }
-    }
-
-    /**
-     * Handle registering (or unregistering) a single process-wide
-     * {@link IBluetoothManagerCallback} based on the presence of local
-     * {@link #sProxyServiceStateCallbacks} clients.
-     */
-    @GuardedBy("sServiceLock")
-    private void registerOrUnregisterAdapterLocked() {
-        final boolean isRegistered = sServiceRegistered;
-        final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty();
-
-        if (isRegistered != wantRegistered) {
-            if (wantRegistered) {
-                try {
-                    sService = mManagerService.registerAdapter(sManagerCallback);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            } else {
-                try {
-                    mManagerService.unregisterAdapter(sManagerCallback);
-                    sService = null;
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-            sServiceRegistered = wantRegistered;
-        }
-    }
-
-    /**
-     * Callback interface used to deliver LE scan results.
-     *
-     * @see #startLeScan(LeScanCallback)
-     * @see #startLeScan(UUID[], LeScanCallback)
-     */
-    public interface LeScanCallback {
-        /**
-         * Callback reporting an LE device found during a device scan initiated
-         * by the {@link BluetoothAdapter#startLeScan} function.
-         *
-         * @param device Identifies the remote device
-         * @param rssi The RSSI value for the remote device as reported by the Bluetooth hardware. 0
-         * if no RSSI value is available.
-         * @param scanRecord The content of the advertisement record offered by the remote device.
-         */
-        void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
-    }
-
-    /**
-     * Register a callback to receive events whenever the bluetooth stack goes down and back up,
-     * e.g. in the event the bluetooth is turned off/on via settings.
-     *
-     * If the bluetooth stack is currently up, there will not be an initial callback call.
-     * You can use the return value as an indication of this being the case.
-     *
-     * Callbacks will be delivered on a binder thread.
-     *
-     * @return whether bluetooth is already up currently
-     *
-     * @hide
-     */
-    public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
-        return getBluetoothService(callback.mRemote) != null;
-    }
-
-    /**
-     * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
-     *
-     * @hide
-     */
-    public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
-        removeServiceStateCallback(callback.mRemote);
-    }
-
-    /**
-     * A callback for {@link #registerServiceLifecycleCallback}
-     *
-     * @hide
-     */
-    public abstract static class ServiceLifecycleCallback {
-
-        /** Called when the bluetooth stack is up */
-        public abstract void onBluetoothServiceUp();
-
-        /** Called when the bluetooth stack is down */
-        public abstract void onBluetoothServiceDown();
-
-        IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
-            @Override
-            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                ServiceLifecycleCallback.this.onBluetoothServiceUp();
-            }
-
-            @Override
-            public void onBluetoothServiceDown() {
-                ServiceLifecycleCallback.this.onBluetoothServiceDown();
-            }
-
-            @Override
-            public void onBrEdrDown() {}
-        };
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices.
-     *
-     * <p>Results of the scan are reported using the
-     * {@link LeScanCallback#onLeScan} callback.
-     *
-     * @param callback the callback LE scan results are delivered
-     * @return true, if the scan was started successfully
-     * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
-     * instead.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean startLeScan(LeScanCallback callback) {
-        return startLeScan(null, callback);
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices, looking for devices that
-     * advertise given services.
-     *
-     * <p>Devices which advertise all specified services are reported using the
-     * {@link LeScanCallback#onLeScan} callback.
-     *
-     * @param serviceUuids Array of services to look for
-     * @param callback the callback LE scan results are delivered
-     * @return true, if the scan was started successfully
-     * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
-     * instead.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
-        if (DBG) {
-            Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
-        }
-        if (callback == null) {
-            if (DBG) {
-                Log.e(TAG, "startLeScan: null callback");
-            }
-            return false;
-        }
-        BluetoothLeScanner scanner = getBluetoothLeScanner();
-        if (scanner == null) {
-            if (DBG) {
-                Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
-            }
-            return false;
-        }
-
-        synchronized (mLeScanClients) {
-            if (mLeScanClients.containsKey(callback)) {
-                if (DBG) {
-                    Log.e(TAG, "LE Scan has already started");
-                }
-                return false;
-            }
-
-            try {
-                IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-                if (iGatt == null) {
-                    // BLE is not supported
-                    return false;
-                }
-
-                @SuppressLint("AndroidFrameworkBluetoothPermission")
-                ScanCallback scanCallback = new ScanCallback() {
-                    @Override
-                    public void onScanResult(int callbackType, ScanResult result) {
-                        if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
-                            // Should not happen.
-                            Log.e(TAG, "LE Scan has already started");
-                            return;
-                        }
-                        ScanRecord scanRecord = result.getScanRecord();
-                        if (scanRecord == null) {
-                            return;
-                        }
-                        if (serviceUuids != null) {
-                            List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
-                            for (UUID uuid : serviceUuids) {
-                                uuids.add(new ParcelUuid(uuid));
-                            }
-                            List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
-                            if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
-                                if (DBG) {
-                                    Log.d(TAG, "uuids does not match");
-                                }
-                                return;
-                            }
-                        }
-                        callback.onLeScan(result.getDevice(), result.getRssi(),
-                                scanRecord.getBytes());
-                    }
-                };
-                ScanSettings settings = new ScanSettings.Builder().setCallbackType(
-                        ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
-                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
-                        .build();
-
-                List<ScanFilter> filters = new ArrayList<ScanFilter>();
-                if (serviceUuids != null && serviceUuids.length > 0) {
-                    // Note scan filter does not support matching an UUID array so we put one
-                    // UUID to hardware and match the whole array in callback.
-                    ScanFilter filter =
-                            new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0]))
-                                    .build();
-                    filters.add(filter);
-                }
-                scanner.startScan(filters, settings, scanCallback);
-
-                mLeScanClients.put(callback, scanCallback);
-                return true;
-
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE device scan.
-     *
-     * @param callback used to identify which scan to stop must be the same handle used to start the
-     * scan
-     * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void stopLeScan(LeScanCallback callback) {
-        if (DBG) {
-            Log.d(TAG, "stopLeScan()");
-        }
-        BluetoothLeScanner scanner = getBluetoothLeScanner();
-        if (scanner == null) {
-            return;
-        }
-        synchronized (mLeScanClients) {
-            ScanCallback scanCallback = mLeScanClients.remove(callback);
-            if (scanCallback == null) {
-                if (DBG) {
-                    Log.d(TAG, "scan not started yet");
-                }
-                return;
-            }
-            scanner.stopScan(scanCallback);
-        }
-    }
-
-    /**
-     * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
-     * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
-     * for incoming connections. The supported Bluetooth transport is LE only.
-     * <p>A remote device connecting to this socket will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
-     * {@link BluetoothServerSocket}.
-     * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link
-     * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
-     * closed, Bluetooth is turned off, or the application exits unexpectedly.
-     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
-     * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
-     * socket from another Android device that is given the PSM value.
-     *
-     * @return an L2CAP CoC BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or unable to start this CoC
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull BluetoothServerSocket listenUsingL2capChannel()
-            throws IOException {
-        BluetoothServerSocket socket =
-                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
-                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
-        int errno = socket.mSocket.bindListen();
-        if (errno != 0) {
-            throw new IOException("Error: " + errno);
-        }
-
-        int assignedPsm = socket.mSocket.getPort();
-        if (assignedPsm == 0) {
-            throw new IOException("Error: Unable to assign PSM value");
-        }
-        if (DBG) {
-            Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
-                    + assignedPsm);
-        }
-        socket.setChannel(assignedPsm);
-
-        return socket;
-    }
-
-    /**
-     * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
-     * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
-     * supported Bluetooth transport is LE only.
-     * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
-     * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
-     * authenticated communication channel is desired.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
-     * {@link BluetoothServerSocket}.
-     * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
-     * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released
-     * when this server socket is closed, Bluetooth is turned off, or the application exits
-     * unexpectedly.
-     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
-     * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
-     * socket from another Android device that is given the PSM value.
-     *
-     * @return an L2CAP CoC BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or unable to start this CoC
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel()
-            throws IOException {
-        BluetoothServerSocket socket =
-                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
-                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
-        int errno = socket.mSocket.bindListen();
-        if (errno != 0) {
-            throw new IOException("Error: " + errno);
-        }
-
-        int assignedPsm = socket.mSocket.getPort();
-        if (assignedPsm == 0) {
-            throw new IOException("Error: Unable to assign PSM value");
-        }
-        if (DBG) {
-            Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
-                    + assignedPsm);
-        }
-        socket.setChannel(assignedPsm);
-
-        return socket;
-    }
-
-    /**
-     * Register a {@link #OnMetadataChangedListener} to receive update about metadata
-     * changes for this {@link BluetoothDevice}.
-     * Registration must be done when Bluetooth is ON and will last until
-     * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth
-     * restarted in the middle.
-     * All input parameters should not be null or {@link NullPointerException} will be triggered.
-     * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be
-     * registered once, double registration would cause {@link IllegalArgumentException}.
-     *
-     * @param device {@link BluetoothDevice} that will be registered
-     * @param executor the executor for listener callback
-     * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks
-     * @return true on success, false on error
-     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor}
-     * is null.
-     * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and
-     * {@link BluetoothDevice} are registered twice.
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
-            @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
-        if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");
-
-        final IBluetooth service = mService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener");
-            return false;
-        }
-        if (listener == null) {
-            throw new NullPointerException("listener is null");
-        }
-        if (device == null) {
-            throw new NullPointerException("device is null");
-        }
-        if (executor == null) {
-            throw new NullPointerException("executor is null");
-        }
-
-        synchronized (mMetadataListeners) {
-            List<Pair<OnMetadataChangedListener, Executor>> listenerList =
-                    mMetadataListeners.get(device);
-            if (listenerList == null) {
-                // Create new listener/executor list for registeration
-                listenerList = new ArrayList<>();
-                mMetadataListeners.put(device, listenerList);
-            } else {
-                // Check whether this device was already registed by the lisenter
-                if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) {
-                    throw new IllegalArgumentException("listener was already regestered"
-                            + " for the device");
-                }
-            }
-
-            Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor);
-            listenerList.add(listenerPair);
-
-            boolean ret = false;
-            try {
-                ret = service.registerMetadataListener(mBluetoothMetadataListener, device,
-                        mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, "registerMetadataListener fail", e);
-            } finally {
-                if (!ret) {
-                    // Remove listener registered earlier when fail.
-                    listenerList.remove(listenerPair);
-                    if (listenerList.isEmpty()) {
-                        // Remove the device if its listener list is empty
-                        mMetadataListeners.remove(device);
-                    }
-                }
-            }
-            return ret;
-        }
-    }
-
-    /**
-     * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}.
-     * Unregistration can be done when Bluetooth is either ON or OFF.
-     * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)}
-     * must be called before unregisteration.
-     *
-     * @param device {@link BluetoothDevice} that will be unregistered. It
-     * should not be null or {@link NullPointerException} will be triggered.
-     * @param listener {@link OnMetadataChangedListener} that will be unregistered. It
-     * should not be null or {@link NullPointerException} will be triggered.
-     * @return true on success, false on error
-     * @throws NullPointerException If {@code listener} or {@code device} is null.
-     * @throws IllegalArgumentException If {@code device} has not been registered before.
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
-            @NonNull OnMetadataChangedListener listener) {
-        if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
-        if (device == null) {
-            throw new NullPointerException("device is null");
-        }
-        if (listener == null) {
-            throw new NullPointerException("listener is null");
-        }
-
-        synchronized (mMetadataListeners) {
-            if (!mMetadataListeners.containsKey(device)) {
-                throw new IllegalArgumentException("device was not registered");
-            }
-            // Remove issued listener from the registered device
-            mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));
-
-            if (mMetadataListeners.get(device).isEmpty()) {
-                // Unregister to Bluetooth service if all listeners are removed from
-                // the registered device
-                mMetadataListeners.remove(device);
-                final IBluetooth service = mService;
-                if (service == null) {
-                    // Bluetooth is OFF, do nothing to Bluetooth service.
-                    return true;
-                }
-                try {
-                    return service.unregisterMetadataListener(device, mAttributionSource);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "unregisterMetadataListener fail", e);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * This interface is used to implement {@link BluetoothAdapter} metadata listener.
-     * @hide
-     */
-    @SystemApi
-    public interface OnMetadataChangedListener {
-        /**
-         * Callback triggered if the metadata of {@link BluetoothDevice} registered in
-         * {@link #addOnMetadataChangedListener}.
-         *
-         * @param device changed {@link BluetoothDevice}.
-         * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
-         * @param value the new value of metadata as byte array.
-         */
-        void onMetadataChanged(@NonNull BluetoothDevice device, int key,
-                @Nullable byte[] value);
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothConnectionCallback mConnectionCallback =
-            new IBluetoothConnectionCallback.Stub() {
-        @Override
-        public void onDeviceConnected(BluetoothDevice device) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
-                    mBluetoothConnectionCallbackExecutorMap.entrySet()) {
-                BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
-                Executor executor = callbackExecutorEntry.getValue();
-                executor.execute(() -> callback.onDeviceConnected(device));
-            }
-        }
-
-        @Override
-        public void onDeviceDisconnected(BluetoothDevice device, int hciReason) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
-                    mBluetoothConnectionCallbackExecutorMap.entrySet()) {
-                BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
-                Executor executor = callbackExecutorEntry.getValue();
-                executor.execute(() -> callback.onDeviceDisconnected(device, hciReason));
-            }
-        }
-    };
-
-    /**
-     * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device
-     * (classic or low energy) is connected or disconnected.
-     *
-     * @param executor is the callback executor
-     * @param callback is the connection callback you wish to register
-     * @return true if the callback was registered successfully, false otherwise
-     * @throws IllegalArgumentException if the callback is already registered
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull BluetoothConnectionCallback callback) {
-        if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()");
-        if (callback == null) {
-            return false;
-        }
-
-        synchronized (mBluetoothConnectionCallbackExecutorMap) {
-            // If the callback map is empty, we register the service-to-app callback
-            if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-                try {
-                    mServiceLock.readLock().lock();
-                    if (mService != null) {
-                        if (!mService.registerBluetoothConnectionCallback(mConnectionCallback,
-                                mAttributionSource)) {
-                            return false;
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "", e);
-                    mBluetoothConnectionCallbackExecutorMap.remove(callback);
-                } finally {
-                    mServiceLock.readLock().unlock();
-                }
-            }
-
-            // Adds the passed in callback to our map of callbacks to executors
-            if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
-                throw new IllegalArgumentException("This callback has already been registered");
-            }
-            mBluetoothConnectionCallbackExecutorMap.put(callback, executor);
-        }
-
-        return true;
-    }
-
-    /**
-     * Unregisters the BluetoothConnectionCallback that was previously registered by the application
-     *
-     * @param callback is the connection callback you wish to unregister
-     * @return true if the callback was unregistered successfully, false otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean unregisterBluetoothConnectionCallback(
-            @NonNull BluetoothConnectionCallback callback) {
-        if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()");
-        if (callback == null) {
-            return false;
-        }
-
-        synchronized (mBluetoothConnectionCallbackExecutorMap) {
-            if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) {
-                return false;
-            }
-        }
-
-        if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-            return true;
-        }
-
-        // If the callback map is empty, we unregister the service-to-app callback
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.unregisterBluetoothConnectionCallback(mConnectionCallback,
-                        mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth
-     * Low Energy (BLE) device is either connected or disconnected.
-     *
-     * @hide
-     */
-    public abstract static class BluetoothConnectionCallback {
-        /**
-         * Callback triggered when a bluetooth device (classic or BLE) is connected
-         * @param device is the connected bluetooth device
-         */
-        public void onDeviceConnected(BluetoothDevice device) {}
-
-        /**
-         * Callback triggered when a bluetooth device (classic or BLE) is disconnected
-         * @param device is the disconnected bluetooth device
-         * @param reason is the disconnect reason
-         */
-        public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {}
-
-        /**
-         * @hide
-         */
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef(prefix = { "REASON_" }, value = {
-                BluetoothStatusCodes.ERROR_UNKNOWN,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS})
-        public @interface DisconnectReason {}
-
-        /**
-         * Returns human-readable strings corresponding to {@link DisconnectReason}.
-         */
-        public static String disconnectReasonText(@DisconnectReason int reason) {
-            switch (reason) {
-                case BluetoothStatusCodes.ERROR_UNKNOWN:
-                    return "Reason unknown";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST:
-                    return "Local request";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST:
-                    return "Remote request";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL:
-                    return "Local error";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE:
-                    return "Remote error";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT:
-                    return "Timeout";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY:
-                    return "Security";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY:
-                    return "System policy";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED:
-                    return "Resource constrained";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS:
-                    return "Connection already exists";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS:
-                    return "Bad parameters";
-                default:
-                    return "Unrecognized disconnect reason: " + reason;
-            }
-        }
-    }
-
-    /**
-     * Converts old constant of priority to the new for connection policy
-     *
-     * @param priority is the priority to convert to connection policy
-     * @return the equivalent connection policy constant to the priority
-     *
-     * @hide
-     */
-    public static @ConnectionPolicy int priorityToConnectionPolicy(int priority) {
-        switch(priority) {
-            case BluetoothProfile.PRIORITY_AUTO_CONNECT:
-                return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-            case BluetoothProfile.PRIORITY_ON:
-                return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-            case BluetoothProfile.PRIORITY_OFF:
-                return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-            case BluetoothProfile.PRIORITY_UNDEFINED:
-                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
-            default:
-                Log.e(TAG, "setPriority: Invalid priority: " + priority);
-                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
-        }
-    }
-
-    /**
-     * Converts new constant of connection policy to the old for priority
-     *
-     * @param connectionPolicy is the connection policy to convert to priority
-     * @return the equivalent priority constant to the connectionPolicy
-     *
-     * @hide
-     */
-    public static int connectionPolicyToPriority(@ConnectionPolicy int connectionPolicy) {
-        switch(connectionPolicy) {
-            case BluetoothProfile.CONNECTION_POLICY_ALLOWED:
-                return BluetoothProfile.PRIORITY_ON;
-            case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN:
-                return BluetoothProfile.PRIORITY_OFF;
-            case BluetoothProfile.CONNECTION_POLICY_UNKNOWN:
-                return BluetoothProfile.PRIORITY_UNDEFINED;
-        }
-        return BluetoothProfile.PRIORITY_UNDEFINED;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
deleted file mode 100644
index 41a34e0..0000000
--- a/core/java/android/bluetooth/BluetoothAssignedNumbers.java
+++ /dev/null
@@ -1,1171 +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.
- */
-
-package android.bluetooth;
-
-/**
- * Bluetooth Assigned Numbers.
- * <p>
- * For now we only include Company ID values.
- *
- * @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm"> The Official
- * Bluetooth SIG Member Website | Company Identifiers</a>
- */
-public class BluetoothAssignedNumbers {
-
-    // Bluetooth SIG Company ID values
-    /*
-     * Ericsson Technology Licensing.
-     */
-    public static final int ERICSSON_TECHNOLOGY = 0x0000;
-
-    /*
-     * Nokia Mobile Phones.
-     */
-    public static final int NOKIA_MOBILE_PHONES = 0x0001;
-
-    /*
-     * Intel Corp.
-     */
-    public static final int INTEL = 0x0002;
-
-    /*
-     * IBM Corp.
-     */
-    public static final int IBM = 0x0003;
-
-    /*
-     * Toshiba Corp.
-     */
-    public static final int TOSHIBA = 0x0004;
-
-    /*
-     * 3Com.
-     */
-    public static final int THREECOM = 0x0005;
-
-    /*
-     * Microsoft.
-     */
-    public static final int MICROSOFT = 0x0006;
-
-    /*
-     * Lucent.
-     */
-    public static final int LUCENT = 0x0007;
-
-    /*
-     * Motorola.
-     */
-    public static final int MOTOROLA = 0x0008;
-
-    /*
-     * Infineon Technologies AG.
-     */
-    public static final int INFINEON_TECHNOLOGIES = 0x0009;
-
-    /*
-     * Cambridge Silicon Radio.
-     */
-    public static final int CAMBRIDGE_SILICON_RADIO = 0x000A;
-
-    /*
-     * Silicon Wave.
-     */
-    public static final int SILICON_WAVE = 0x000B;
-
-    /*
-     * Digianswer A/S.
-     */
-    public static final int DIGIANSWER = 0x000C;
-
-    /*
-     * Texas Instruments Inc.
-     */
-    public static final int TEXAS_INSTRUMENTS = 0x000D;
-
-    /*
-     * Parthus Technologies Inc.
-     */
-    public static final int PARTHUS_TECHNOLOGIES = 0x000E;
-
-    /*
-     * Broadcom Corporation.
-     */
-    public static final int BROADCOM = 0x000F;
-
-    /*
-     * Mitel Semiconductor.
-     */
-    public static final int MITEL_SEMICONDUCTOR = 0x0010;
-
-    /*
-     * Widcomm, Inc.
-     */
-    public static final int WIDCOMM = 0x0011;
-
-    /*
-     * Zeevo, Inc.
-     */
-    public static final int ZEEVO = 0x0012;
-
-    /*
-     * Atmel Corporation.
-     */
-    public static final int ATMEL = 0x0013;
-
-    /*
-     * Mitsubishi Electric Corporation.
-     */
-    public static final int MITSUBISHI_ELECTRIC = 0x0014;
-
-    /*
-     * RTX Telecom A/S.
-     */
-    public static final int RTX_TELECOM = 0x0015;
-
-    /*
-     * KC Technology Inc.
-     */
-    public static final int KC_TECHNOLOGY = 0x0016;
-
-    /*
-     * Newlogic.
-     */
-    public static final int NEWLOGIC = 0x0017;
-
-    /*
-     * Transilica, Inc.
-     */
-    public static final int TRANSILICA = 0x0018;
-
-    /*
-     * Rohde & Schwarz GmbH & Co. KG.
-     */
-    public static final int ROHDE_AND_SCHWARZ = 0x0019;
-
-    /*
-     * TTPCom Limited.
-     */
-    public static final int TTPCOM = 0x001A;
-
-    /*
-     * Signia Technologies, Inc.
-     */
-    public static final int SIGNIA_TECHNOLOGIES = 0x001B;
-
-    /*
-     * Conexant Systems Inc.
-     */
-    public static final int CONEXANT_SYSTEMS = 0x001C;
-
-    /*
-     * Qualcomm.
-     */
-    public static final int QUALCOMM = 0x001D;
-
-    /*
-     * Inventel.
-     */
-    public static final int INVENTEL = 0x001E;
-
-    /*
-     * AVM Berlin.
-     */
-    public static final int AVM_BERLIN = 0x001F;
-
-    /*
-     * BandSpeed, Inc.
-     */
-    public static final int BANDSPEED = 0x0020;
-
-    /*
-     * Mansella Ltd.
-     */
-    public static final int MANSELLA = 0x0021;
-
-    /*
-     * NEC Corporation.
-     */
-    public static final int NEC = 0x0022;
-
-    /*
-     * WavePlus Technology Co., Ltd.
-     */
-    public static final int WAVEPLUS_TECHNOLOGY = 0x0023;
-
-    /*
-     * Alcatel.
-     */
-    public static final int ALCATEL = 0x0024;
-
-    /*
-     * Philips Semiconductors.
-     */
-    public static final int PHILIPS_SEMICONDUCTORS = 0x0025;
-
-    /*
-     * C Technologies.
-     */
-    public static final int C_TECHNOLOGIES = 0x0026;
-
-    /*
-     * Open Interface.
-     */
-    public static final int OPEN_INTERFACE = 0x0027;
-
-    /*
-     * R F Micro Devices.
-     */
-    public static final int RF_MICRO_DEVICES = 0x0028;
-
-    /*
-     * Hitachi Ltd.
-     */
-    public static final int HITACHI = 0x0029;
-
-    /*
-     * Symbol Technologies, Inc.
-     */
-    public static final int SYMBOL_TECHNOLOGIES = 0x002A;
-
-    /*
-     * Tenovis.
-     */
-    public static final int TENOVIS = 0x002B;
-
-    /*
-     * Macronix International Co. Ltd.
-     */
-    public static final int MACRONIX = 0x002C;
-
-    /*
-     * GCT Semiconductor.
-     */
-    public static final int GCT_SEMICONDUCTOR = 0x002D;
-
-    /*
-     * Norwood Systems.
-     */
-    public static final int NORWOOD_SYSTEMS = 0x002E;
-
-    /*
-     * MewTel Technology Inc.
-     */
-    public static final int MEWTEL_TECHNOLOGY = 0x002F;
-
-    /*
-     * ST Microelectronics.
-     */
-    public static final int ST_MICROELECTRONICS = 0x0030;
-
-    /*
-     * Synopsys.
-     */
-    public static final int SYNOPSYS = 0x0031;
-
-    /*
-     * Red-M (Communications) Ltd.
-     */
-    public static final int RED_M = 0x0032;
-
-    /*
-     * Commil Ltd.
-     */
-    public static final int COMMIL = 0x0033;
-
-    /*
-     * Computer Access Technology Corporation (CATC).
-     */
-    public static final int CATC = 0x0034;
-
-    /*
-     * Eclipse (HQ Espana) S.L.
-     */
-    public static final int ECLIPSE = 0x0035;
-
-    /*
-     * Renesas Technology Corp.
-     */
-    public static final int RENESAS_TECHNOLOGY = 0x0036;
-
-    /*
-     * Mobilian Corporation.
-     */
-    public static final int MOBILIAN_CORPORATION = 0x0037;
-
-    /*
-     * Terax.
-     */
-    public static final int TERAX = 0x0038;
-
-    /*
-     * Integrated System Solution Corp.
-     */
-    public static final int INTEGRATED_SYSTEM_SOLUTION = 0x0039;
-
-    /*
-     * Matsushita Electric Industrial Co., Ltd.
-     */
-    public static final int MATSUSHITA_ELECTRIC = 0x003A;
-
-    /*
-     * Gennum Corporation.
-     */
-    public static final int GENNUM = 0x003B;
-
-    /*
-     * Research In Motion.
-     */
-    public static final int RESEARCH_IN_MOTION = 0x003C;
-
-    /*
-     * IPextreme, Inc.
-     */
-    public static final int IPEXTREME = 0x003D;
-
-    /*
-     * Systems and Chips, Inc.
-     */
-    public static final int SYSTEMS_AND_CHIPS = 0x003E;
-
-    /*
-     * Bluetooth SIG, Inc.
-     */
-    public static final int BLUETOOTH_SIG = 0x003F;
-
-    /*
-     * Seiko Epson Corporation.
-     */
-    public static final int SEIKO_EPSON = 0x0040;
-
-    /*
-     * Integrated Silicon Solution Taiwan, Inc.
-     */
-    public static final int INTEGRATED_SILICON_SOLUTION = 0x0041;
-
-    /*
-     * CONWISE Technology Corporation Ltd.
-     */
-    public static final int CONWISE_TECHNOLOGY = 0x0042;
-
-    /*
-     * PARROT SA.
-     */
-    public static final int PARROT = 0x0043;
-
-    /*
-     * Socket Mobile.
-     */
-    public static final int SOCKET_MOBILE = 0x0044;
-
-    /*
-     * Atheros Communications, Inc.
-     */
-    public static final int ATHEROS_COMMUNICATIONS = 0x0045;
-
-    /*
-     * MediaTek, Inc.
-     */
-    public static final int MEDIATEK = 0x0046;
-
-    /*
-     * Bluegiga.
-     */
-    public static final int BLUEGIGA = 0x0047;
-
-    /*
-     * Marvell Technology Group Ltd.
-     */
-    public static final int MARVELL = 0x0048;
-
-    /*
-     * 3DSP Corporation.
-     */
-    public static final int THREE_DSP = 0x0049;
-
-    /*
-     * Accel Semiconductor Ltd.
-     */
-    public static final int ACCEL_SEMICONDUCTOR = 0x004A;
-
-    /*
-     * Continental Automotive Systems.
-     */
-    public static final int CONTINENTAL_AUTOMOTIVE = 0x004B;
-
-    /*
-     * Apple, Inc.
-     */
-    public static final int APPLE = 0x004C;
-
-    /*
-     * Staccato Communications, Inc.
-     */
-    public static final int STACCATO_COMMUNICATIONS = 0x004D;
-
-    /*
-     * Avago Technologies.
-     */
-    public static final int AVAGO = 0x004E;
-
-    /*
-     * APT Licensing Ltd.
-     */
-    public static final int APT_LICENSING = 0x004F;
-
-    /*
-     * SiRF Technology, Inc.
-     */
-    public static final int SIRF_TECHNOLOGY = 0x0050;
-
-    /*
-     * Tzero Technologies, Inc.
-     */
-    public static final int TZERO_TECHNOLOGIES = 0x0051;
-
-    /*
-     * J&M Corporation.
-     */
-    public static final int J_AND_M = 0x0052;
-
-    /*
-     * Free2move AB.
-     */
-    public static final int FREE2MOVE = 0x0053;
-
-    /*
-     * 3DiJoy Corporation.
-     */
-    public static final int THREE_DIJOY = 0x0054;
-
-    /*
-     * Plantronics, Inc.
-     */
-    public static final int PLANTRONICS = 0x0055;
-
-    /*
-     * Sony Ericsson Mobile Communications.
-     */
-    public static final int SONY_ERICSSON = 0x0056;
-
-    /*
-     * Harman International Industries, Inc.
-     */
-    public static final int HARMAN_INTERNATIONAL = 0x0057;
-
-    /*
-     * Vizio, Inc.
-     */
-    public static final int VIZIO = 0x0058;
-
-    /*
-     * Nordic Semiconductor ASA.
-     */
-    public static final int NORDIC_SEMICONDUCTOR = 0x0059;
-
-    /*
-     * EM Microelectronic-Marin SA.
-     */
-    public static final int EM_MICROELECTRONIC_MARIN = 0x005A;
-
-    /*
-     * Ralink Technology Corporation.
-     */
-    public static final int RALINK_TECHNOLOGY = 0x005B;
-
-    /*
-     * Belkin International, Inc.
-     */
-    public static final int BELKIN_INTERNATIONAL = 0x005C;
-
-    /*
-     * Realtek Semiconductor Corporation.
-     */
-    public static final int REALTEK_SEMICONDUCTOR = 0x005D;
-
-    /*
-     * Stonestreet One, LLC.
-     */
-    public static final int STONESTREET_ONE = 0x005E;
-
-    /*
-     * Wicentric, Inc.
-     */
-    public static final int WICENTRIC = 0x005F;
-
-    /*
-     * RivieraWaves S.A.S.
-     */
-    public static final int RIVIERAWAVES = 0x0060;
-
-    /*
-     * RDA Microelectronics.
-     */
-    public static final int RDA_MICROELECTRONICS = 0x0061;
-
-    /*
-     * Gibson Guitars.
-     */
-    public static final int GIBSON_GUITARS = 0x0062;
-
-    /*
-     * MiCommand Inc.
-     */
-    public static final int MICOMMAND = 0x0063;
-
-    /*
-     * Band XI International, LLC.
-     */
-    public static final int BAND_XI_INTERNATIONAL = 0x0064;
-
-    /*
-     * Hewlett-Packard Company.
-     */
-    public static final int HEWLETT_PACKARD = 0x0065;
-
-    /*
-     * 9Solutions Oy.
-     */
-    public static final int NINE_SOLUTIONS = 0x0066;
-
-    /*
-     * GN Netcom A/S.
-     */
-    public static final int GN_NETCOM = 0x0067;
-
-    /*
-     * General Motors.
-     */
-    public static final int GENERAL_MOTORS = 0x0068;
-
-    /*
-     * A&D Engineering, Inc.
-     */
-    public static final int A_AND_D_ENGINEERING = 0x0069;
-
-    /*
-     * MindTree Ltd.
-     */
-    public static final int MINDTREE = 0x006A;
-
-    /*
-     * Polar Electro OY.
-     */
-    public static final int POLAR_ELECTRO = 0x006B;
-
-    /*
-     * Beautiful Enterprise Co., Ltd.
-     */
-    public static final int BEAUTIFUL_ENTERPRISE = 0x006C;
-
-    /*
-     * BriarTek, Inc.
-     */
-    public static final int BRIARTEK = 0x006D;
-
-    /*
-     * Summit Data Communications, Inc.
-     */
-    public static final int SUMMIT_DATA_COMMUNICATIONS = 0x006E;
-
-    /*
-     * Sound ID.
-     */
-    public static final int SOUND_ID = 0x006F;
-
-    /*
-     * Monster, LLC.
-     */
-    public static final int MONSTER = 0x0070;
-
-    /*
-     * connectBlue AB.
-     */
-    public static final int CONNECTBLUE = 0x0071;
-
-    /*
-     * ShangHai Super Smart Electronics Co. Ltd.
-     */
-    public static final int SHANGHAI_SUPER_SMART_ELECTRONICS = 0x0072;
-
-    /*
-     * Group Sense Ltd.
-     */
-    public static final int GROUP_SENSE = 0x0073;
-
-    /*
-     * Zomm, LLC.
-     */
-    public static final int ZOMM = 0x0074;
-
-    /*
-     * Samsung Electronics Co. Ltd.
-     */
-    public static final int SAMSUNG_ELECTRONICS = 0x0075;
-
-    /*
-     * Creative Technology Ltd.
-     */
-    public static final int CREATIVE_TECHNOLOGY = 0x0076;
-
-    /*
-     * Laird Technologies.
-     */
-    public static final int LAIRD_TECHNOLOGIES = 0x0077;
-
-    /*
-     * Nike, Inc.
-     */
-    public static final int NIKE = 0x0078;
-
-    /*
-     * lesswire AG.
-     */
-    public static final int LESSWIRE = 0x0079;
-
-    /*
-     * MStar Semiconductor, Inc.
-     */
-    public static final int MSTAR_SEMICONDUCTOR = 0x007A;
-
-    /*
-     * Hanlynn Technologies.
-     */
-    public static final int HANLYNN_TECHNOLOGIES = 0x007B;
-
-    /*
-     * A & R Cambridge.
-     */
-    public static final int A_AND_R_CAMBRIDGE = 0x007C;
-
-    /*
-     * Seers Technology Co. Ltd.
-     */
-    public static final int SEERS_TECHNOLOGY = 0x007D;
-
-    /*
-     * Sports Tracking Technologies Ltd.
-     */
-    public static final int SPORTS_TRACKING_TECHNOLOGIES = 0x007E;
-
-    /*
-     * Autonet Mobile.
-     */
-    public static final int AUTONET_MOBILE = 0x007F;
-
-    /*
-     * DeLorme Publishing Company, Inc.
-     */
-    public static final int DELORME_PUBLISHING_COMPANY = 0x0080;
-
-    /*
-     * WuXi Vimicro.
-     */
-    public static final int WUXI_VIMICRO = 0x0081;
-
-    /*
-     * Sennheiser Communications A/S.
-     */
-    public static final int SENNHEISER_COMMUNICATIONS = 0x0082;
-
-    /*
-     * TimeKeeping Systems, Inc.
-     */
-    public static final int TIMEKEEPING_SYSTEMS = 0x0083;
-
-    /*
-     * Ludus Helsinki Ltd.
-     */
-    public static final int LUDUS_HELSINKI = 0x0084;
-
-    /*
-     * BlueRadios, Inc.
-     */
-    public static final int BLUERADIOS = 0x0085;
-
-    /*
-     * equinox AG.
-     */
-    public static final int EQUINOX_AG = 0x0086;
-
-    /*
-     * Garmin International, Inc.
-     */
-    public static final int GARMIN_INTERNATIONAL = 0x0087;
-
-    /*
-     * Ecotest.
-     */
-    public static final int ECOTEST = 0x0088;
-
-    /*
-     * GN ReSound A/S.
-     */
-    public static final int GN_RESOUND = 0x0089;
-
-    /*
-     * Jawbone.
-     */
-    public static final int JAWBONE = 0x008A;
-
-    /*
-     * Topcorn Positioning Systems, LLC.
-     */
-    public static final int TOPCORN_POSITIONING_SYSTEMS = 0x008B;
-
-    /*
-     * Qualcomm Labs, Inc.
-     */
-    public static final int QUALCOMM_LABS = 0x008C;
-
-    /*
-     * Zscan Software.
-     */
-    public static final int ZSCAN_SOFTWARE = 0x008D;
-
-    /*
-     * Quintic Corp.
-     */
-    public static final int QUINTIC = 0x008E;
-
-    /*
-     * Stollman E+V GmbH.
-     */
-    public static final int STOLLMAN_E_PLUS_V = 0x008F;
-
-    /*
-     * Funai Electric Co., Ltd.
-     */
-    public static final int FUNAI_ELECTRIC = 0x0090;
-
-    /*
-     * Advanced PANMOBIL Systems GmbH & Co. KG.
-     */
-    public static final int ADVANCED_PANMOBIL_SYSTEMS = 0x0091;
-
-    /*
-     * ThinkOptics, Inc.
-     */
-    public static final int THINKOPTICS = 0x0092;
-
-    /*
-     * Universal Electronics, Inc.
-     */
-    public static final int UNIVERSAL_ELECTRONICS = 0x0093;
-
-    /*
-     * Airoha Technology Corp.
-     */
-    public static final int AIROHA_TECHNOLOGY = 0x0094;
-
-    /*
-     * NEC Lighting, Ltd.
-     */
-    public static final int NEC_LIGHTING = 0x0095;
-
-    /*
-     * ODM Technology, Inc.
-     */
-    public static final int ODM_TECHNOLOGY = 0x0096;
-
-    /*
-     * Bluetrek Technologies Limited.
-     */
-    public static final int BLUETREK_TECHNOLOGIES = 0x0097;
-
-    /*
-     * zer01.tv GmbH.
-     */
-    public static final int ZER01_TV = 0x0098;
-
-    /*
-     * i.Tech Dynamic Global Distribution Ltd.
-     */
-    public static final int I_TECH_DYNAMIC_GLOBAL_DISTRIBUTION = 0x0099;
-
-    /*
-     * Alpwise.
-     */
-    public static final int ALPWISE = 0x009A;
-
-    /*
-     * Jiangsu Toppower Automotive Electronics Co., Ltd.
-     */
-    public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS = 0x009B;
-
-    /*
-     * Colorfy, Inc.
-     */
-    public static final int COLORFY = 0x009C;
-
-    /*
-     * Geoforce Inc.
-     */
-    public static final int GEOFORCE = 0x009D;
-
-    /*
-     * Bose Corporation.
-     */
-    public static final int BOSE = 0x009E;
-
-    /*
-     * Suunto Oy.
-     */
-    public static final int SUUNTO = 0x009F;
-
-    /*
-     * Kensington Computer Products Group.
-     */
-    public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0;
-
-    /*
-     * SR-Medizinelektronik.
-     */
-    public static final int SR_MEDIZINELEKTRONIK = 0x00A1;
-
-    /*
-     * Vertu Corporation Limited.
-     */
-    public static final int VERTU = 0x00A2;
-
-    /*
-     * Meta Watch Ltd.
-     */
-    public static final int META_WATCH = 0x00A3;
-
-    /*
-     * LINAK A/S.
-     */
-    public static final int LINAK = 0x00A4;
-
-    /*
-     * OTL Dynamics LLC.
-     */
-    public static final int OTL_DYNAMICS = 0x00A5;
-
-    /*
-     * Panda Ocean Inc.
-     */
-    public static final int PANDA_OCEAN = 0x00A6;
-
-    /*
-     * Visteon Corporation.
-     */
-    public static final int VISTEON = 0x00A7;
-
-    /*
-     * ARP Devices Limited.
-     */
-    public static final int ARP_DEVICES = 0x00A8;
-
-    /*
-     * Magneti Marelli S.p.A.
-     */
-    public static final int MAGNETI_MARELLI = 0x00A9;
-
-    /*
-     * CAEN RFID srl.
-     */
-    public static final int CAEN_RFID = 0x00AA;
-
-    /*
-     * Ingenieur-Systemgruppe Zahn GmbH.
-     */
-    public static final int INGENIEUR_SYSTEMGRUPPE_ZAHN = 0x00AB;
-
-    /*
-     * Green Throttle Games.
-     */
-    public static final int GREEN_THROTTLE_GAMES = 0x00AC;
-
-    /*
-     * Peter Systemtechnik GmbH.
-     */
-    public static final int PETER_SYSTEMTECHNIK = 0x00AD;
-
-    /*
-     * Omegawave Oy.
-     */
-    public static final int OMEGAWAVE = 0x00AE;
-
-    /*
-     * Cinetix.
-     */
-    public static final int CINETIX = 0x00AF;
-
-    /*
-     * Passif Semiconductor Corp.
-     */
-    public static final int PASSIF_SEMICONDUCTOR = 0x00B0;
-
-    /*
-     * Saris Cycling Group, Inc.
-     */
-    public static final int SARIS_CYCLING_GROUP = 0x00B1;
-
-    /*
-     * Bekey A/S.
-     */
-    public static final int BEKEY = 0x00B2;
-
-    /*
-     * Clarinox Technologies Pty. Ltd.
-     */
-    public static final int CLARINOX_TECHNOLOGIES = 0x00B3;
-
-    /*
-     * BDE Technology Co., Ltd.
-     */
-    public static final int BDE_TECHNOLOGY = 0x00B4;
-
-    /*
-     * Swirl Networks.
-     */
-    public static final int SWIRL_NETWORKS = 0x00B5;
-
-    /*
-     * Meso international.
-     */
-    public static final int MESO_INTERNATIONAL = 0x00B6;
-
-    /*
-     * TreLab Ltd.
-     */
-    public static final int TRELAB = 0x00B7;
-
-    /*
-     * Qualcomm Innovation Center, Inc. (QuIC).
-     */
-    public static final int QUALCOMM_INNOVATION_CENTER = 0x00B8;
-
-    /*
-     * Johnson Controls, Inc.
-     */
-    public static final int JOHNSON_CONTROLS = 0x00B9;
-
-    /*
-     * Starkey Laboratories Inc.
-     */
-    public static final int STARKEY_LABORATORIES = 0x00BA;
-
-    /*
-     * S-Power Electronics Limited.
-     */
-    public static final int S_POWER_ELECTRONICS = 0x00BB;
-
-    /*
-     * Ace Sensor Inc.
-     */
-    public static final int ACE_SENSOR = 0x00BC;
-
-    /*
-     * Aplix Corporation.
-     */
-    public static final int APLIX = 0x00BD;
-
-    /*
-     * AAMP of America.
-     */
-    public static final int AAMP_OF_AMERICA = 0x00BE;
-
-    /*
-     * Stalmart Technology Limited.
-     */
-    public static final int STALMART_TECHNOLOGY = 0x00BF;
-
-    /*
-     * AMICCOM Electronics Corporation.
-     */
-    public static final int AMICCOM_ELECTRONICS = 0x00C0;
-
-    /*
-     * Shenzhen Excelsecu Data Technology Co.,Ltd.
-     */
-    public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY = 0x00C1;
-
-    /*
-     * Geneq Inc.
-     */
-    public static final int GENEQ = 0x00C2;
-
-    /*
-     * adidas AG.
-     */
-    public static final int ADIDAS = 0x00C3;
-
-    /*
-     * LG Electronics.
-     */
-    public static final int LG_ELECTRONICS = 0x00C4;
-
-    /*
-     * Onset Computer Corporation.
-     */
-    public static final int ONSET_COMPUTER = 0x00C5;
-
-    /*
-     * Selfly BV.
-     */
-    public static final int SELFLY = 0x00C6;
-
-    /*
-     * Quuppa Oy.
-     */
-    public static final int QUUPPA = 0x00C7;
-
-    /*
-     * GeLo Inc.
-     */
-    public static final int GELO = 0x00C8;
-
-    /*
-     * Evluma.
-     */
-    public static final int EVLUMA = 0x00C9;
-
-    /*
-     * MC10.
-     */
-    public static final int MC10 = 0x00CA;
-
-    /*
-     * Binauric SE.
-     */
-    public static final int BINAURIC = 0x00CB;
-
-    /*
-     * Beats Electronics.
-     */
-    public static final int BEATS_ELECTRONICS = 0x00CC;
-
-    /*
-     * Microchip Technology Inc.
-     */
-    public static final int MICROCHIP_TECHNOLOGY = 0x00CD;
-
-    /*
-     * Elgato Systems GmbH.
-     */
-    public static final int ELGATO_SYSTEMS = 0x00CE;
-
-    /*
-     * ARCHOS SA.
-     */
-    public static final int ARCHOS = 0x00CF;
-
-    /*
-     * Dexcom, Inc.
-     */
-    public static final int DEXCOM = 0x00D0;
-
-    /*
-     * Polar Electro Europe B.V.
-     */
-    public static final int POLAR_ELECTRO_EUROPE = 0x00D1;
-
-    /*
-     * Dialog Semiconductor B.V.
-     */
-    public static final int DIALOG_SEMICONDUCTOR = 0x00D2;
-
-    /*
-     * Taixingbang Technology (HK) Co,. LTD.
-     */
-    public static final int TAIXINGBANG_TECHNOLOGY = 0x00D3;
-
-    /*
-     * Kawantech.
-     */
-    public static final int KAWANTECH = 0x00D4;
-
-    /*
-     * Austco Communication Systems.
-     */
-    public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5;
-
-    /*
-     * Timex Group USA, Inc.
-     */
-    public static final int TIMEX_GROUP_USA = 0x00D6;
-
-    /*
-     * Qualcomm Technologies, Inc.
-     */
-    public static final int QUALCOMM_TECHNOLOGIES = 0x00D7;
-
-    /*
-     * Qualcomm Connected Experiences, Inc.
-     */
-    public static final int QUALCOMM_CONNECTED_EXPERIENCES = 0x00D8;
-
-    /*
-     * Voyetra Turtle Beach.
-     */
-    public static final int VOYETRA_TURTLE_BEACH = 0x00D9;
-
-    /*
-     * txtr GmbH.
-     */
-    public static final int TXTR = 0x00DA;
-
-    /*
-     * Biosentronics.
-     */
-    public static final int BIOSENTRONICS = 0x00DB;
-
-    /*
-     * Procter & Gamble.
-     */
-    public static final int PROCTER_AND_GAMBLE = 0x00DC;
-
-    /*
-     * Hosiden Corporation.
-     */
-    public static final int HOSIDEN = 0x00DD;
-
-    /*
-     * Muzik LLC.
-     */
-    public static final int MUZIK = 0x00DE;
-
-    /*
-     * Misfit Wearables Corp.
-     */
-    public static final int MISFIT_WEARABLES = 0x00DF;
-
-    /*
-     * Google.
-     */
-    public static final int GOOGLE = 0x00E0;
-
-    /*
-     * Danlers Ltd.
-     */
-    public static final int DANLERS = 0x00E1;
-
-    /*
-     * Semilink Inc.
-     */
-    public static final int SEMILINK = 0x00E2;
-
-    /*
-     * You can't instantiate one of these.
-     */
-    private BluetoothAssignedNumbers() {
-    }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java
deleted file mode 100644
index 4c8b8c1..0000000
--- a/core/java/android/bluetooth/BluetoothAudioConfig.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents the audio configuration for a Bluetooth A2DP source device.
- *
- * {@see BluetoothA2dpSink}
- *
- * {@hide}
- */
-public final class BluetoothAudioConfig implements Parcelable {
-
-    private final int mSampleRate;
-    private final int mChannelConfig;
-    private final int mAudioFormat;
-
-    public BluetoothAudioConfig(int sampleRate, int channelConfig, int audioFormat) {
-        mSampleRate = sampleRate;
-        mChannelConfig = channelConfig;
-        mAudioFormat = audioFormat;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothAudioConfig) {
-            BluetoothAudioConfig bac = (BluetoothAudioConfig) o;
-            return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig
-                    && bac.mAudioFormat == mAudioFormat);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mSampleRate | (mChannelConfig << 24) | (mAudioFormat << 28);
-    }
-
-    @Override
-    public String toString() {
-        return "{mSampleRate:" + mSampleRate + ",mChannelConfig:" + mChannelConfig
-                + ",mAudioFormat:" + mAudioFormat + "}";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothAudioConfig> CREATOR =
-            new Parcelable.Creator<BluetoothAudioConfig>() {
-                public BluetoothAudioConfig createFromParcel(Parcel in) {
-                    int sampleRate = in.readInt();
-                    int channelConfig = in.readInt();
-                    int audioFormat = in.readInt();
-                    return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat);
-                }
-
-                public BluetoothAudioConfig[] newArray(int size) {
-                    return new BluetoothAudioConfig[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSampleRate);
-        out.writeInt(mChannelConfig);
-        out.writeInt(mAudioFormat);
-    }
-
-    /**
-     * Returns the sample rate in samples per second
-     *
-     * @return sample rate
-     */
-    public int getSampleRate() {
-        return mSampleRate;
-    }
-
-    /**
-     * Returns the channel configuration (either {@link android.media.AudioFormat#CHANNEL_IN_MONO}
-     * or {@link android.media.AudioFormat#CHANNEL_IN_STEREO})
-     *
-     * @return channel configuration
-     */
-    public int getChannelConfig() {
-        return mChannelConfig;
-    }
-
-    /**
-     * Returns the channel audio format (either {@link android.media.AudioFormat#ENCODING_PCM_16BIT}
-     * or {@link android.media.AudioFormat#ENCODING_PCM_8BIT}
-     *
-     * @return audio format
-     */
-    public int getAudioFormat() {
-        return mAudioFormat;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAvrcp.java b/core/java/android/bluetooth/BluetoothAvrcp.java
deleted file mode 100644
index 1a4c759..0000000
--- a/core/java/android/bluetooth/BluetoothAvrcp.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * This class contains constants for Bluetooth AVRCP profile.
- *
- * {@hide}
- */
-public final class BluetoothAvrcp {
-
-    /*
-     * State flags for Passthrough commands
-    */
-    public static final int PASSTHROUGH_STATE_PRESS = 0;
-    public static final int PASSTHROUGH_STATE_RELEASE = 1;
-
-    /*
-     * Operation IDs for Passthrough commands
-    */
-    public static final int PASSTHROUGH_ID_SELECT = 0x00;    /* select */
-    public static final int PASSTHROUGH_ID_UP = 0x01;    /* up */
-    public static final int PASSTHROUGH_ID_DOWN = 0x02;    /* down */
-    public static final int PASSTHROUGH_ID_LEFT = 0x03;    /* left */
-    public static final int PASSTHROUGH_ID_RIGHT = 0x04;    /* right */
-    public static final int PASSTHROUGH_ID_RIGHT_UP = 0x05;    /* right-up */
-    public static final int PASSTHROUGH_ID_RIGHT_DOWN = 0x06;    /* right-down */
-    public static final int PASSTHROUGH_ID_LEFT_UP = 0x07;    /* left-up */
-    public static final int PASSTHROUGH_ID_LEFT_DOWN = 0x08;    /* left-down */
-    public static final int PASSTHROUGH_ID_ROOT_MENU = 0x09;    /* root menu */
-    public static final int PASSTHROUGH_ID_SETUP_MENU = 0x0A;    /* setup menu */
-    public static final int PASSTHROUGH_ID_CONT_MENU = 0x0B;    /* contents menu */
-    public static final int PASSTHROUGH_ID_FAV_MENU = 0x0C;    /* favorite menu */
-    public static final int PASSTHROUGH_ID_EXIT = 0x0D;    /* exit */
-    public static final int PASSTHROUGH_ID_0 = 0x20;    /* 0 */
-    public static final int PASSTHROUGH_ID_1 = 0x21;    /* 1 */
-    public static final int PASSTHROUGH_ID_2 = 0x22;    /* 2 */
-    public static final int PASSTHROUGH_ID_3 = 0x23;    /* 3 */
-    public static final int PASSTHROUGH_ID_4 = 0x24;    /* 4 */
-    public static final int PASSTHROUGH_ID_5 = 0x25;    /* 5 */
-    public static final int PASSTHROUGH_ID_6 = 0x26;    /* 6 */
-    public static final int PASSTHROUGH_ID_7 = 0x27;    /* 7 */
-    public static final int PASSTHROUGH_ID_8 = 0x28;    /* 8 */
-    public static final int PASSTHROUGH_ID_9 = 0x29;    /* 9 */
-    public static final int PASSTHROUGH_ID_DOT = 0x2A;    /* dot */
-    public static final int PASSTHROUGH_ID_ENTER = 0x2B;    /* enter */
-    public static final int PASSTHROUGH_ID_CLEAR = 0x2C;    /* clear */
-    public static final int PASSTHROUGH_ID_CHAN_UP = 0x30;    /* channel up */
-    public static final int PASSTHROUGH_ID_CHAN_DOWN = 0x31;    /* channel down */
-    public static final int PASSTHROUGH_ID_PREV_CHAN = 0x32;    /* previous channel */
-    public static final int PASSTHROUGH_ID_SOUND_SEL = 0x33;    /* sound select */
-    public static final int PASSTHROUGH_ID_INPUT_SEL = 0x34;    /* input select */
-    public static final int PASSTHROUGH_ID_DISP_INFO = 0x35;    /* display information */
-    public static final int PASSTHROUGH_ID_HELP = 0x36;    /* help */
-    public static final int PASSTHROUGH_ID_PAGE_UP = 0x37;    /* page up */
-    public static final int PASSTHROUGH_ID_PAGE_DOWN = 0x38;    /* page down */
-    public static final int PASSTHROUGH_ID_POWER = 0x40;    /* power */
-    public static final int PASSTHROUGH_ID_VOL_UP = 0x41;    /* volume up */
-    public static final int PASSTHROUGH_ID_VOL_DOWN = 0x42;    /* volume down */
-    public static final int PASSTHROUGH_ID_MUTE = 0x43;    /* mute */
-    public static final int PASSTHROUGH_ID_PLAY = 0x44;    /* play */
-    public static final int PASSTHROUGH_ID_STOP = 0x45;    /* stop */
-    public static final int PASSTHROUGH_ID_PAUSE = 0x46;    /* pause */
-    public static final int PASSTHROUGH_ID_RECORD = 0x47;    /* record */
-    public static final int PASSTHROUGH_ID_REWIND = 0x48;    /* rewind */
-    public static final int PASSTHROUGH_ID_FAST_FOR = 0x49;    /* fast forward */
-    public static final int PASSTHROUGH_ID_EJECT = 0x4A;    /* eject */
-    public static final int PASSTHROUGH_ID_FORWARD = 0x4B;    /* forward */
-    public static final int PASSTHROUGH_ID_BACKWARD = 0x4C;    /* backward */
-    public static final int PASSTHROUGH_ID_ANGLE = 0x50;    /* angle */
-    public static final int PASSTHROUGH_ID_SUBPICT = 0x51;    /* subpicture */
-    public static final int PASSTHROUGH_ID_F1 = 0x71;    /* F1 */
-    public static final int PASSTHROUGH_ID_F2 = 0x72;    /* F2 */
-    public static final int PASSTHROUGH_ID_F3 = 0x73;    /* F3 */
-    public static final int PASSTHROUGH_ID_F4 = 0x74;    /* F4 */
-    public static final int PASSTHROUGH_ID_F5 = 0x75;    /* F5 */
-    public static final int PASSTHROUGH_ID_VENDOR = 0x7E;    /* vendor unique */
-    public static final int PASSTHROUGH_KEYPRESSED_RELEASE = 0x80;
-}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
deleted file mode 100644
index 81fc3e1..0000000
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
- * supports player information, playback support and track metadata.
- *
- * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothAvrcpController proxy object.
- *
- * {@hide}
- */
-public final class BluetoothAvrcpController implements BluetoothProfile {
-    private static final String TAG = "BluetoothAvrcpController";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the AVRCP Controller
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in player application setting state on AVRCP AG.
-     *
-     * <p>This intent will have the following extras:
-     * <ul>
-     * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
-     * most recent player setting. </li>
-     * </ul>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PLAYER_SETTING =
-            "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
-
-    public static final String EXTRA_PLAYER_SETTING =
-            "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
-                    "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
-                @Override
-                public IBluetoothAvrcpController getServiceInterface(IBinder service) {
-                    return IBluetoothAvrcpController.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothAvrcpController proxy object for interacting with the local
-     * Bluetooth AVRCP service.
-     */
-    /* package */ BluetoothAvrcpController(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothAvrcpController getService() {
-        return mProfileConnector.getService();
-    }
-
-    @Override
-    public void finalize() {
-        close();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothAvrcpController service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothAvrcpController service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothAvrcpController service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Gets the player application settings.
-     *
-     * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getPlayerSettings");
-        BluetoothAvrcpPlayerSettings settings = null;
-        final IBluetoothAvrcpController service = getService();
-        final BluetoothAvrcpPlayerSettings defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv =
-                        new SynchronousResultReceiver();
-                service.getPlayerSettings(device, mAttributionSource, recv);
-                settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets the player app setting for current player.
-     * returns true in case setting is supported by remote, false otherwise
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
-        if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
-        final IBluetoothAvrcpController service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Group Navigation Command to Remote.
-     * possible keycode values: next_grp, previous_grp defined above
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
-        Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
-                + keyState);
-        final IBluetoothAvrcpController service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-                return;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
deleted file mode 100644
index 30aea1a..0000000
--- a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Class used to identify settings associated with the player on AG.
- *
- * {@hide}
- */
-public final class BluetoothAvrcpPlayerSettings implements Parcelable {
-    public static final String TAG = "BluetoothAvrcpPlayerSettings";
-
-    /**
-     * Equalizer setting.
-     */
-    public static final int SETTING_EQUALIZER = 0x01;
-
-    /**
-     * Repeat setting.
-     */
-    public static final int SETTING_REPEAT = 0x02;
-
-    /**
-     * Shuffle setting.
-     */
-    public static final int SETTING_SHUFFLE = 0x04;
-
-    /**
-     * Scan mode setting.
-     */
-    public static final int SETTING_SCAN = 0x08;
-
-    /**
-     * Invalid state.
-     *
-     * Used for returning error codes.
-     */
-    public static final int STATE_INVALID = -1;
-
-    /**
-     * OFF state.
-     *
-     * Denotes a general OFF state. Applies to all settings.
-     */
-    public static final int STATE_OFF = 0x00;
-
-    /**
-     * ON state.
-     *
-     * Applies to {@link SETTING_EQUALIZER}.
-     */
-    public static final int STATE_ON = 0x01;
-
-    /**
-     * Single track repeat.
-     *
-     * Applies only to {@link SETTING_REPEAT}.
-     */
-    public static final int STATE_SINGLE_TRACK = 0x02;
-
-    /**
-     * All track repeat/shuffle.
-     *
-     * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}.
-     */
-    public static final int STATE_ALL_TRACK = 0x03;
-
-    /**
-     * Group repeat/shuffle.
-     *
-     * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}.
-     */
-    public static final int STATE_GROUP = 0x04;
-
-    /**
-     * List of supported settings ORed.
-     */
-    private int mSettings;
-
-    /**
-     * Hash map of current capability values.
-     */
-    private Map<Integer, Integer> mSettingsValue = new HashMap<Integer, Integer>();
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSettings);
-        out.writeInt(mSettingsValue.size());
-        for (int k : mSettingsValue.keySet()) {
-            out.writeInt(k);
-            out.writeInt(mSettingsValue.get(k));
-        }
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothAvrcpPlayerSettings> CREATOR =
-            new Parcelable.Creator<BluetoothAvrcpPlayerSettings>() {
-        public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) {
-            return new BluetoothAvrcpPlayerSettings(in);
-        }
-
-        public BluetoothAvrcpPlayerSettings[] newArray(int size) {
-            return new BluetoothAvrcpPlayerSettings[size];
-        }
-    };
-
-    private BluetoothAvrcpPlayerSettings(Parcel in) {
-        mSettings = in.readInt();
-        int numSettings = in.readInt();
-        for (int i = 0; i < numSettings; i++) {
-            mSettingsValue.put(in.readInt(), in.readInt());
-        }
-    }
-
-    /**
-     * Create a new player settings object.
-     *
-     * @param settings a ORed value of SETTINGS_* defined above.
-     */
-    public BluetoothAvrcpPlayerSettings(int settings) {
-        mSettings = settings;
-    }
-
-    /**
-     * Get the supported settings.
-     *
-     * @return int ORed value of supported settings.
-     */
-    public int getSettings() {
-        return mSettings;
-    }
-
-    /**
-     * Add a setting value.
-     *
-     * The setting must be part of possible settings in {@link getSettings()}.
-     *
-     * @param setting setting config.
-     * @param value value for the setting.
-     * @throws IllegalStateException if the setting is not supported.
-     */
-    public void addSettingValue(int setting, int value) {
-        if ((setting & mSettings) == 0) {
-            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
-            throw new IllegalStateException("Setting not supported: " + setting);
-        }
-        mSettingsValue.put(setting, value);
-    }
-
-    /**
-     * Get a setting value.
-     *
-     * The setting must be part of possible settings in {@link getSettings()}.
-     *
-     * @param setting setting config.
-     * @return value value for the setting.
-     * @throws IllegalStateException if the setting is not supported.
-     */
-    public int getSettingValue(int setting) {
-        if ((setting & mSettings) == 0) {
-            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
-            throw new IllegalStateException("Setting not supported: " + setting);
-        }
-        Integer i = mSettingsValue.get(setting);
-        if (i == null) return -1;
-        return i;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
deleted file mode 100755
index 69525b5..0000000
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-
-/**
- * Represents a Bluetooth class, which describes general characteristics
- * and capabilities of a device. For example, a Bluetooth class will
- * specify the general device type such as a phone, a computer, or
- * headset, and whether it's capable of services such as audio or telephony.
- *
- * <p>Every Bluetooth class is composed of zero or more service classes, and
- * exactly one device class. The device class is further broken down into major
- * and minor device class components.
- *
- * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device
- * (for example to show an icon in the UI), but does not reliably describe which
- * Bluetooth profiles or services are actually supported by a device. Accurate
- * service discovery is done through SDP requests, which are automatically
- * performed when creating an RFCOMM socket with {@link
- * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link
- * BluetoothAdapter#listenUsingRfcommWithServiceRecord}</p>
- *
- * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
- * a remote device.
- *
- * <!--
- * The Bluetooth class is a 32 bit field. The format of these bits is defined at
- * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
- * (login required). This class contains that 32 bit field, and provides
- * constants and methods to determine which Service Class(es) and Device Class
- * are encoded in that field.
- * -->
- */
-public final class BluetoothClass implements Parcelable {
-    /**
-     * Legacy error value. Applications should use null instead.
-     *
-     * @hide
-     */
-    public static final int ERROR = 0xFF000000;
-
-    private final int mClass;
-
-    /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public BluetoothClass(int classInt) {
-        mClass = classInt;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothClass) {
-            return mClass == ((BluetoothClass) o).mClass;
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mClass;
-    }
-
-    @Override
-    public String toString() {
-        return Integer.toHexString(mClass);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothClass> CREATOR =
-            new Parcelable.Creator<BluetoothClass>() {
-                public BluetoothClass createFromParcel(Parcel in) {
-                    return new BluetoothClass(in.readInt());
-                }
-
-                public BluetoothClass[] newArray(int size) {
-                    return new BluetoothClass[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mClass);
-    }
-
-    /**
-     * Defines all service class constants.
-     * <p>Each {@link BluetoothClass} encodes zero or more service classes.
-     */
-    public static final class Service {
-        private static final int BITMASK = 0xFFE000;
-
-        public static final int LIMITED_DISCOVERABILITY = 0x002000;
-        public static final int POSITIONING = 0x010000;
-        public static final int NETWORKING = 0x020000;
-        public static final int RENDER = 0x040000;
-        public static final int CAPTURE = 0x080000;
-        public static final int OBJECT_TRANSFER = 0x100000;
-        public static final int AUDIO = 0x200000;
-        public static final int TELEPHONY = 0x400000;
-        public static final int INFORMATION = 0x800000;
-    }
-
-    /**
-     * Return true if the specified service class is supported by this
-     * {@link BluetoothClass}.
-     * <p>Valid service classes are the public constants in
-     * {@link BluetoothClass.Service}. For example, {@link
-     * BluetoothClass.Service#AUDIO}.
-     *
-     * @param service valid service class
-     * @return true if the service class is supported
-     */
-    public boolean hasService(int service) {
-        return ((mClass & Service.BITMASK & service) != 0);
-    }
-
-    /**
-     * Defines all device class constants.
-     * <p>Each {@link BluetoothClass} encodes exactly one device class, with
-     * major and minor components.
-     * <p>The constants in {@link
-     * BluetoothClass.Device} represent a combination of major and minor
-     * device components (the complete device class). The constants in {@link
-     * BluetoothClass.Device.Major} represent only major device classes.
-     * <p>See {@link BluetoothClass.Service} for service class constants.
-     */
-    public static class Device {
-        private static final int BITMASK = 0x1FFC;
-
-        /**
-         * Defines all major device class constants.
-         * <p>See {@link BluetoothClass.Device} for minor classes.
-         */
-        public static class Major {
-            private static final int BITMASK = 0x1F00;
-
-            public static final int MISC = 0x0000;
-            public static final int COMPUTER = 0x0100;
-            public static final int PHONE = 0x0200;
-            public static final int NETWORKING = 0x0300;
-            public static final int AUDIO_VIDEO = 0x0400;
-            public static final int PERIPHERAL = 0x0500;
-            public static final int IMAGING = 0x0600;
-            public static final int WEARABLE = 0x0700;
-            public static final int TOY = 0x0800;
-            public static final int HEALTH = 0x0900;
-            public static final int UNCATEGORIZED = 0x1F00;
-        }
-
-        // Devices in the COMPUTER major class
-        public static final int COMPUTER_UNCATEGORIZED = 0x0100;
-        public static final int COMPUTER_DESKTOP = 0x0104;
-        public static final int COMPUTER_SERVER = 0x0108;
-        public static final int COMPUTER_LAPTOP = 0x010C;
-        public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110;
-        public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114;
-        public static final int COMPUTER_WEARABLE = 0x0118;
-
-        // Devices in the PHONE major class
-        public static final int PHONE_UNCATEGORIZED = 0x0200;
-        public static final int PHONE_CELLULAR = 0x0204;
-        public static final int PHONE_CORDLESS = 0x0208;
-        public static final int PHONE_SMART = 0x020C;
-        public static final int PHONE_MODEM_OR_GATEWAY = 0x0210;
-        public static final int PHONE_ISDN = 0x0214;
-
-        // Minor classes for the AUDIO_VIDEO major class
-        public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400;
-        public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404;
-        public static final int AUDIO_VIDEO_HANDSFREE = 0x0408;
-        //public static final int AUDIO_VIDEO_RESERVED              = 0x040C;
-        public static final int AUDIO_VIDEO_MICROPHONE = 0x0410;
-        public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414;
-        public static final int AUDIO_VIDEO_HEADPHONES = 0x0418;
-        public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C;
-        public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420;
-        public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424;
-        public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428;
-        public static final int AUDIO_VIDEO_VCR = 0x042C;
-        public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430;
-        public static final int AUDIO_VIDEO_CAMCORDER = 0x0434;
-        public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438;
-        public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C;
-        public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440;
-        //public static final int AUDIO_VIDEO_RESERVED              = 0x0444;
-        public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448;
-
-        // Devices in the WEARABLE major class
-        public static final int WEARABLE_UNCATEGORIZED = 0x0700;
-        public static final int WEARABLE_WRIST_WATCH = 0x0704;
-        public static final int WEARABLE_PAGER = 0x0708;
-        public static final int WEARABLE_JACKET = 0x070C;
-        public static final int WEARABLE_HELMET = 0x0710;
-        public static final int WEARABLE_GLASSES = 0x0714;
-
-        // Devices in the TOY major class
-        public static final int TOY_UNCATEGORIZED = 0x0800;
-        public static final int TOY_ROBOT = 0x0804;
-        public static final int TOY_VEHICLE = 0x0808;
-        public static final int TOY_DOLL_ACTION_FIGURE = 0x080C;
-        public static final int TOY_CONTROLLER = 0x0810;
-        public static final int TOY_GAME = 0x0814;
-
-        // Devices in the HEALTH major class
-        public static final int HEALTH_UNCATEGORIZED = 0x0900;
-        public static final int HEALTH_BLOOD_PRESSURE = 0x0904;
-        public static final int HEALTH_THERMOMETER = 0x0908;
-        public static final int HEALTH_WEIGHING = 0x090C;
-        public static final int HEALTH_GLUCOSE = 0x0910;
-        public static final int HEALTH_PULSE_OXIMETER = 0x0914;
-        public static final int HEALTH_PULSE_RATE = 0x0918;
-        public static final int HEALTH_DATA_DISPLAY = 0x091C;
-
-        // Devices in PERIPHERAL major class
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_NON_KEYBOARD_NON_POINTING = 0x0500;
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_KEYBOARD = 0x0540;
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_POINTING = 0x0580;
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0;
-    }
-
-    /**
-     * Return the major device class component of this {@link BluetoothClass}.
-     * <p>Values returned from this function can be compared with the
-     * public constants in {@link BluetoothClass.Device.Major} to determine
-     * which major class is encoded in this Bluetooth class.
-     *
-     * @return major device class component
-     */
-    public int getMajorDeviceClass() {
-        return (mClass & Device.Major.BITMASK);
-    }
-
-    /**
-     * Return the (major and minor) device class component of this
-     * {@link BluetoothClass}.
-     * <p>Values returned from this function can be compared with the
-     * public constants in {@link BluetoothClass.Device} to determine which
-     * device class is encoded in this Bluetooth class.
-     *
-     * @return device class component
-     */
-    public int getDeviceClass() {
-        return (mClass & Device.BITMASK);
-    }
-
-    /**
-     * Return the Bluetooth Class of Device (CoD) value including the
-     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
-     * minor device fields.
-     *
-     * <p>This value is an integer representation of Bluetooth CoD as in
-     * Bluetooth specification.
-     *
-     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
-     *
-     * @hide
-     */
-    @TestApi
-    public int getClassOfDevice() {
-        return mClass;
-    }
-
-    /**
-     * Return the Bluetooth Class of Device (CoD) value including the
-     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
-     * minor device fields.
-     *
-     * <p>This value is a byte array representation of Bluetooth CoD as in
-     * Bluetooth specification.
-     *
-     * <p>Bluetooth COD information is 3 bytes, but stored as an int. Hence the
-     * MSB is useless and needs to be thrown away. The lower 3 bytes are
-     * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN.
-     *
-     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
-     *
-     * @hide
-     */
-    public byte[] getClassOfDeviceBytes() {
-        byte[] bytes = ByteBuffer.allocate(4)
-                .order(ByteOrder.BIG_ENDIAN)
-                .putInt(mClass)
-                .array();
-
-        // Discard the top byte
-        return Arrays.copyOfRange(bytes, 1, bytes.length);
-    }
-
-    public static final int PROFILE_HEADSET = 0;
-
-    public static final int PROFILE_A2DP = 1;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_OPP = 2;
-
-    public static final int PROFILE_HID = 3;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_PANU = 4;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_NAP = 5;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_A2DP_SINK = 6;
-
-    /**
-     * Check class bits for possible bluetooth profile support.
-     * This is a simple heuristic that tries to guess if a device with the
-     * given class bits might support specified profile. It is not accurate for all
-     * devices. It tries to err on the side of false positives.
-     *
-     * @param profile the profile to be checked
-     * @return whether this device supports specified profile
-     */
-    public boolean doesClassMatch(int profile) {
-        if (profile == PROFILE_A2DP) {
-            if (hasService(Service.RENDER)) {
-                return true;
-            }
-            // By the A2DP spec, sinks must indicate the RENDER service.
-            // However we found some that do not (Chordette). So lets also
-            // match on some other class bits.
-            switch (getDeviceClass()) {
-                case Device.AUDIO_VIDEO_HIFI_AUDIO:
-                case Device.AUDIO_VIDEO_HEADPHONES:
-                case Device.AUDIO_VIDEO_LOUDSPEAKER:
-                case Device.AUDIO_VIDEO_CAR_AUDIO:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_A2DP_SINK) {
-            if (hasService(Service.CAPTURE)) {
-                return true;
-            }
-            // By the A2DP spec, srcs must indicate the CAPTURE service.
-            // However if some device that do not, we try to
-            // match on some other class bits.
-            switch (getDeviceClass()) {
-                case Device.AUDIO_VIDEO_HIFI_AUDIO:
-                case Device.AUDIO_VIDEO_SET_TOP_BOX:
-                case Device.AUDIO_VIDEO_VCR:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_HEADSET) {
-            // The render service class is required by the spec for HFP, so is a
-            // pretty good signal
-            if (hasService(Service.RENDER)) {
-                return true;
-            }
-            // Just in case they forgot the render service class
-            switch (getDeviceClass()) {
-                case Device.AUDIO_VIDEO_HANDSFREE:
-                case Device.AUDIO_VIDEO_WEARABLE_HEADSET:
-                case Device.AUDIO_VIDEO_CAR_AUDIO:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_OPP) {
-            if (hasService(Service.OBJECT_TRANSFER)) {
-                return true;
-            }
-
-            switch (getDeviceClass()) {
-                case Device.COMPUTER_UNCATEGORIZED:
-                case Device.COMPUTER_DESKTOP:
-                case Device.COMPUTER_SERVER:
-                case Device.COMPUTER_LAPTOP:
-                case Device.COMPUTER_HANDHELD_PC_PDA:
-                case Device.COMPUTER_PALM_SIZE_PC_PDA:
-                case Device.COMPUTER_WEARABLE:
-                case Device.PHONE_UNCATEGORIZED:
-                case Device.PHONE_CELLULAR:
-                case Device.PHONE_CORDLESS:
-                case Device.PHONE_SMART:
-                case Device.PHONE_MODEM_OR_GATEWAY:
-                case Device.PHONE_ISDN:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_HID) {
-            return getMajorDeviceClass() == Device.Major.PERIPHERAL;
-        } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) {
-            // No good way to distinguish between the two, based on class bits.
-            if (hasService(Service.NETWORKING)) {
-                return true;
-            }
-            return getMajorDeviceClass() == Device.Major.NETWORKING;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
deleted file mode 100644
index 9a4151a..0000000
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ /dev/null
@@ -1,807 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Represents the codec configuration for a Bluetooth A2DP source device.
- * <p>Contains the source codec type, the codec priority, the codec sample
- * rate, the codec bits per sample, and the codec channel mode.
- * <p>The source codec type values are the same as those supported by the
- * device hardware.
- *
- * {@see BluetoothA2dp}
- */
-public final class BluetoothCodecConfig implements Parcelable {
-    /** @hide */
-    @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
-            SOURCE_CODEC_TYPE_SBC,
-            SOURCE_CODEC_TYPE_AAC,
-            SOURCE_CODEC_TYPE_APTX,
-            SOURCE_CODEC_TYPE_APTX_HD,
-            SOURCE_CODEC_TYPE_LDAC,
-            SOURCE_CODEC_TYPE_INVALID
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SourceCodecType {}
-
-    /**
-     * Source codec type SBC. This is the mandatory source codec
-     * type.
-     */
-    public static final int SOURCE_CODEC_TYPE_SBC = 0;
-
-    /**
-     * Source codec type AAC.
-     */
-    public static final int SOURCE_CODEC_TYPE_AAC = 1;
-
-    /**
-     * Source codec type APTX.
-     */
-    public static final int SOURCE_CODEC_TYPE_APTX = 2;
-
-    /**
-     * Source codec type APTX HD.
-     */
-    public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
-
-    /**
-     * Source codec type LDAC.
-     */
-    public static final int SOURCE_CODEC_TYPE_LDAC = 4;
-
-    /**
-     * Source codec type invalid. This is the default value used for codec
-     * type.
-     */
-    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
-
-    /**
-     * Represents the count of valid source codec types. Can be accessed via
-     * {@link #getMaxCodecType}.
-     */
-    private static final int SOURCE_CODEC_TYPE_MAX = 5;
-
-    /** @hide */
-    @IntDef(prefix = "CODEC_PRIORITY_", value = {
-            CODEC_PRIORITY_DISABLED,
-            CODEC_PRIORITY_DEFAULT,
-            CODEC_PRIORITY_HIGHEST
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CodecPriority {}
-
-    /**
-     * Codec priority disabled.
-     * Used to indicate that this codec is disabled and should not be used.
-     */
-    public static final int CODEC_PRIORITY_DISABLED = -1;
-
-    /**
-     * Codec priority default.
-     * Default value used for codec priority.
-     */
-    public static final int CODEC_PRIORITY_DEFAULT = 0;
-
-    /**
-     * Codec priority highest.
-     * Used to indicate the highest priority a codec can have.
-     */
-    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
-
-    /** @hide */
-    @IntDef(prefix = "SAMPLE_RATE_", value = {
-            SAMPLE_RATE_NONE,
-            SAMPLE_RATE_44100,
-            SAMPLE_RATE_48000,
-            SAMPLE_RATE_88200,
-            SAMPLE_RATE_96000,
-            SAMPLE_RATE_176400,
-            SAMPLE_RATE_192000
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SampleRate {}
-
-    /**
-     * Codec sample rate 0 Hz. Default value used for
-     * codec sample rate.
-     */
-    public static final int SAMPLE_RATE_NONE = 0;
-
-    /**
-     * Codec sample rate 44100 Hz.
-     */
-    public static final int SAMPLE_RATE_44100 = 0x1 << 0;
-
-    /**
-     * Codec sample rate 48000 Hz.
-     */
-    public static final int SAMPLE_RATE_48000 = 0x1 << 1;
-
-    /**
-     * Codec sample rate 88200 Hz.
-     */
-    public static final int SAMPLE_RATE_88200 = 0x1 << 2;
-
-    /**
-     * Codec sample rate 96000 Hz.
-     */
-    public static final int SAMPLE_RATE_96000 = 0x1 << 3;
-
-    /**
-     * Codec sample rate 176400 Hz.
-     */
-    public static final int SAMPLE_RATE_176400 = 0x1 << 4;
-
-    /**
-     * Codec sample rate 192000 Hz.
-     */
-    public static final int SAMPLE_RATE_192000 = 0x1 << 5;
-
-    /** @hide */
-    @IntDef(prefix = "BITS_PER_SAMPLE_", value = {
-            BITS_PER_SAMPLE_NONE,
-            BITS_PER_SAMPLE_16,
-            BITS_PER_SAMPLE_24,
-            BITS_PER_SAMPLE_32
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BitsPerSample {}
-
-    /**
-     * Codec bits per sample 0. Default value of the codec
-     * bits per sample.
-     */
-    public static final int BITS_PER_SAMPLE_NONE = 0;
-
-    /**
-     * Codec bits per sample 16.
-     */
-    public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
-
-    /**
-     * Codec bits per sample 24.
-     */
-    public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
-
-    /**
-     * Codec bits per sample 32.
-     */
-    public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
-
-    /** @hide */
-    @IntDef(prefix = "CHANNEL_MODE_", value = {
-            CHANNEL_MODE_NONE,
-            CHANNEL_MODE_MONO,
-            CHANNEL_MODE_STEREO
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ChannelMode {}
-
-    /**
-     * Codec channel mode NONE. Default value of the
-     * codec channel mode.
-     */
-    public static final int CHANNEL_MODE_NONE = 0;
-
-    /**
-     * Codec channel mode MONO.
-     */
-    public static final int CHANNEL_MODE_MONO = 0x1 << 0;
-
-    /**
-     * Codec channel mode STEREO.
-     */
-    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
-
-    private final @SourceCodecType int mCodecType;
-    private @CodecPriority int mCodecPriority;
-    private final @SampleRate int mSampleRate;
-    private final @BitsPerSample int mBitsPerSample;
-    private final @ChannelMode int mChannelMode;
-    private final long mCodecSpecific1;
-    private final long mCodecSpecific2;
-    private final long mCodecSpecific3;
-    private final long mCodecSpecific4;
-
-    /**
-     * Creates a new BluetoothCodecConfig.
-     *
-     * @param codecType the source codec type
-     * @param codecPriority the priority of this codec
-     * @param sampleRate the codec sample rate
-     * @param bitsPerSample the bits per sample of this codec
-     * @param channelMode the channel mode of this codec
-     * @param codecSpecific1 the specific value 1
-     * @param codecSpecific2 the specific value 2
-     * @param codecSpecific3 the specific value 3
-     * @param codecSpecific4 the specific value 4
-     * values to 0.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority,
-            @SampleRate int sampleRate, @BitsPerSample int bitsPerSample,
-            @ChannelMode int channelMode, long codecSpecific1,
-            long codecSpecific2, long codecSpecific3,
-            long codecSpecific4) {
-        mCodecType = codecType;
-        mCodecPriority = codecPriority;
-        mSampleRate = sampleRate;
-        mBitsPerSample = bitsPerSample;
-        mChannelMode = channelMode;
-        mCodecSpecific1 = codecSpecific1;
-        mCodecSpecific2 = codecSpecific2;
-        mCodecSpecific3 = codecSpecific3;
-        mCodecSpecific4 = codecSpecific4;
-    }
-
-    /**
-     * Creates a new BluetoothCodecConfig.
-     * <p> By default, the codec priority will be set
-     * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to
-     * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to
-     * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to
-     * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific
-     * values to 0.
-     *
-     * @param codecType the source codec type
-     */
-    public BluetoothCodecConfig(@SourceCodecType int codecType) {
-        this(codecType, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                BluetoothCodecConfig.SAMPLE_RATE_NONE,
-                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
-                BluetoothCodecConfig.CHANNEL_MODE_NONE, 0, 0, 0, 0);
-    }
-
-    private BluetoothCodecConfig(Parcel in) {
-        mCodecType = in.readInt();
-        mCodecPriority = in.readInt();
-        mSampleRate = in.readInt();
-        mBitsPerSample = in.readInt();
-        mChannelMode = in.readInt();
-        mCodecSpecific1 = in.readLong();
-        mCodecSpecific2 = in.readLong();
-        mCodecSpecific3 = in.readLong();
-        mCodecSpecific4 = in.readLong();
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothCodecConfig) {
-            BluetoothCodecConfig other = (BluetoothCodecConfig) o;
-            return (other.mCodecType == mCodecType
-                    && other.mCodecPriority == mCodecPriority
-                    && other.mSampleRate == mSampleRate
-                    && other.mBitsPerSample == mBitsPerSample
-                    && other.mChannelMode == mChannelMode
-                    && other.mCodecSpecific1 == mCodecSpecific1
-                    && other.mCodecSpecific2 == mCodecSpecific2
-                    && other.mCodecSpecific3 == mCodecSpecific3
-                    && other.mCodecSpecific4 == mCodecSpecific4);
-        }
-        return false;
-    }
-
-    /**
-     * Returns a hash representation of this BluetoothCodecConfig
-     * based on all the config values.
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
-                mBitsPerSample, mChannelMode, mCodecSpecific1,
-                mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
-    }
-
-    /**
-     * Adds capability string to an existing string.
-     *
-     * @param prevStr the previous string with the capabilities. Can be a {@code null} pointer
-     * @param capStr the capability string to append to prevStr argument
-     * @return the result string in the form "prevStr|capStr"
-     */
-    private static String appendCapabilityToString(@Nullable String prevStr,
-            @NonNull String capStr) {
-        if (prevStr == null) {
-            return capStr;
-        }
-        return prevStr + "|" + capStr;
-    }
-
-    /**
-     * Returns a {@link String} that describes each BluetoothCodecConfig parameter
-     * current value.
-     */
-    @Override
-    public String toString() {
-        String sampleRateStr = null;
-        if (mSampleRate == SAMPLE_RATE_NONE) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE");
-        }
-        if ((mSampleRate & SAMPLE_RATE_44100) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "44100");
-        }
-        if ((mSampleRate & SAMPLE_RATE_48000) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "48000");
-        }
-        if ((mSampleRate & SAMPLE_RATE_88200) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "88200");
-        }
-        if ((mSampleRate & SAMPLE_RATE_96000) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "96000");
-        }
-        if ((mSampleRate & SAMPLE_RATE_176400) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "176400");
-        }
-        if ((mSampleRate & SAMPLE_RATE_192000) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "192000");
-        }
-
-        String bitsPerSampleStr = null;
-        if (mBitsPerSample == BITS_PER_SAMPLE_NONE) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE");
-        }
-        if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16");
-        }
-        if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24");
-        }
-        if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32");
-        }
-
-        String channelModeStr = null;
-        if (mChannelMode == CHANNEL_MODE_NONE) {
-            channelModeStr = appendCapabilityToString(channelModeStr, "NONE");
-        }
-        if ((mChannelMode & CHANNEL_MODE_MONO) != 0) {
-            channelModeStr = appendCapabilityToString(channelModeStr, "MONO");
-        }
-        if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) {
-            channelModeStr = appendCapabilityToString(channelModeStr, "STEREO");
-        }
-
-        return "{codecName:" + getCodecName()
-                + ",mCodecType:" + mCodecType
-                + ",mCodecPriority:" + mCodecPriority
-                + ",mSampleRate:" + String.format("0x%x", mSampleRate)
-                + "(" + sampleRateStr + ")"
-                + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample)
-                + "(" + bitsPerSampleStr + ")"
-                + ",mChannelMode:" + String.format("0x%x", mChannelMode)
-                + "(" + channelModeStr + ")"
-                + ",mCodecSpecific1:" + mCodecSpecific1
-                + ",mCodecSpecific2:" + mCodecSpecific2
-                + ",mCodecSpecific3:" + mCodecSpecific3
-                + ",mCodecSpecific4:" + mCodecSpecific4 + "}";
-    }
-
-    /**
-     * @return 0
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecConfig> CREATOR =
-            new Parcelable.Creator<BluetoothCodecConfig>() {
-                public BluetoothCodecConfig createFromParcel(Parcel in) {
-                    return new BluetoothCodecConfig(in);
-                }
-
-                public BluetoothCodecConfig[] newArray(int size) {
-                    return new BluetoothCodecConfig[size];
-                }
-            };
-
-    /**
-     * Flattens the object to a parcel
-     *
-     * @param out The Parcel in which the object should be written
-     * @param flags Additional flags about how the object should be written
-     *
-     * @hide
-     */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mCodecType);
-        out.writeInt(mCodecPriority);
-        out.writeInt(mSampleRate);
-        out.writeInt(mBitsPerSample);
-        out.writeInt(mChannelMode);
-        out.writeLong(mCodecSpecific1);
-        out.writeLong(mCodecSpecific2);
-        out.writeLong(mCodecSpecific3);
-        out.writeLong(mCodecSpecific4);
-    }
-
-    /**
-     * Returns the codec name converted to {@link String}.
-     * @hide
-     */
-    public @NonNull String getCodecName() {
-        switch (mCodecType) {
-            case SOURCE_CODEC_TYPE_SBC:
-                return "SBC";
-            case SOURCE_CODEC_TYPE_AAC:
-                return "AAC";
-            case SOURCE_CODEC_TYPE_APTX:
-                return "aptX";
-            case SOURCE_CODEC_TYPE_APTX_HD:
-                return "aptX HD";
-            case SOURCE_CODEC_TYPE_LDAC:
-                return "LDAC";
-            case SOURCE_CODEC_TYPE_INVALID:
-                return "INVALID CODEC";
-            default:
-                break;
-        }
-        return "UNKNOWN CODEC(" + mCodecType + ")";
-    }
-
-    /**
-     * Returns the source codec type of this config.
-     */
-    public @SourceCodecType int getCodecType() {
-        return mCodecType;
-    }
-
-    /**
-     * Returns the valid codec types count.
-     */
-    public static int getMaxCodecType() {
-        return SOURCE_CODEC_TYPE_MAX;
-    }
-
-    /**
-     * Checks whether the codec is mandatory.
-     * <p> The actual mandatory codec type for Android Bluetooth audio is SBC.
-     * See {@link #SOURCE_CODEC_TYPE_SBC}.
-     *
-     * @return {@code true} if the codec is mandatory, {@code false} otherwise
-     * @hide
-     */
-    public boolean isMandatoryCodec() {
-        return mCodecType == SOURCE_CODEC_TYPE_SBC;
-    }
-
-    /**
-     * Returns the codec selection priority.
-     * <p>The codec selection priority is relative to other codecs: larger value
-     * means higher priority.
-     */
-    public @CodecPriority int getCodecPriority() {
-        return mCodecPriority;
-    }
-
-    /**
-     * Sets the codec selection priority.
-     * <p>The codec selection priority is relative to other codecs: larger value
-     * means higher priority.
-     *
-     * @param codecPriority the priority this codec should have
-     * @hide
-     */
-    public void setCodecPriority(@CodecPriority int codecPriority) {
-        mCodecPriority = codecPriority;
-    }
-
-    /**
-     * Returns the codec sample rate. The value can be a bitmask with all
-     * supported sample rates.
-     */
-    public @SampleRate int getSampleRate() {
-        return mSampleRate;
-    }
-
-    /**
-     * Returns the codec bits per sample. The value can be a bitmask with all
-     * bits per sample supported.
-     */
-    public @BitsPerSample int getBitsPerSample() {
-        return mBitsPerSample;
-    }
-
-    /**
-     * Returns the codec channel mode. The value can be a bitmask with all
-     * supported channel modes.
-     */
-    public @ChannelMode int getChannelMode() {
-        return mChannelMode;
-    }
-
-    /**
-     * Returns the codec specific value1.
-     */
-    public long getCodecSpecific1() {
-        return mCodecSpecific1;
-    }
-
-    /**
-     * Returns the codec specific value2.
-     */
-    public long getCodecSpecific2() {
-        return mCodecSpecific2;
-    }
-
-    /**
-     * Returns the codec specific value3.
-     */
-    public long getCodecSpecific3() {
-        return mCodecSpecific3;
-    }
-
-    /**
-     * Returns the codec specific value4.
-     */
-    public long getCodecSpecific4() {
-        return mCodecSpecific4;
-    }
-
-    /**
-     * Checks whether a value set presented by a bitmask has zero or single bit
-     *
-     * @param valueSet the value set presented by a bitmask
-     * @return {@code true} if the valueSet contains zero or single bit, {@code false} otherwise
-     * @hide
-     */
-    private static boolean hasSingleBit(int valueSet) {
-        return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
-    }
-
-    /**
-     * Returns whether the object contains none or single sample rate.
-     * @hide
-     */
-    public boolean hasSingleSampleRate() {
-        return hasSingleBit(mSampleRate);
-    }
-
-    /**
-     * Returns whether the object contains none or single bits per sample.
-     * @hide
-     */
-    public boolean hasSingleBitsPerSample() {
-        return hasSingleBit(mBitsPerSample);
-    }
-
-    /**
-     * Returns whether the object contains none or single channel mode.
-     * @hide
-     */
-    public boolean hasSingleChannelMode() {
-        return hasSingleBit(mChannelMode);
-    }
-
-    /**
-     * Checks whether the audio feeding parameters are the same.
-     *
-     * @param other the codec config to compare against
-     * @return {@code true} if the audio feeding parameters are same, {@code false} otherwise
-     * @hide
-     */
-    public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
-        return (other != null && other.mSampleRate == mSampleRate
-                && other.mBitsPerSample == mBitsPerSample
-                && other.mChannelMode == mChannelMode);
-    }
-
-    /**
-     * Checks whether another codec config has the similar feeding parameters.
-     * Any parameters with NONE value will be considered to be a wildcard matching.
-     *
-     * @param other the codec config to compare against
-     * @return {@code true} if the audio feeding parameters are similar, {@code false} otherwise
-     * @hide
-     */
-    public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
-        if (other == null || mCodecType != other.mCodecType) {
-            return false;
-        }
-        int sampleRate = other.mSampleRate;
-        if (mSampleRate == SAMPLE_RATE_NONE
-                || sampleRate == SAMPLE_RATE_NONE) {
-            sampleRate = mSampleRate;
-        }
-        int bitsPerSample = other.mBitsPerSample;
-        if (mBitsPerSample == BITS_PER_SAMPLE_NONE
-                || bitsPerSample == BITS_PER_SAMPLE_NONE) {
-            bitsPerSample = mBitsPerSample;
-        }
-        int channelMode = other.mChannelMode;
-        if (mChannelMode == CHANNEL_MODE_NONE
-                || channelMode == CHANNEL_MODE_NONE) {
-            channelMode = mChannelMode;
-        }
-        return sameAudioFeedingParameters(new BluetoothCodecConfig(
-                mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode,
-                /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0,
-                /* specific4 */ 0));
-    }
-
-    /**
-     * Checks whether the codec specific parameters are the same.
-     * <p> Currently, only AAC VBR and LDAC Playback Quality on CodecSpecific1
-     * are compared.
-     *
-     * @param other the codec config to compare against
-     * @return {@code true} if the codec specific parameters are the same, {@code false} otherwise
-     * @hide
-     */
-    public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
-        if (other == null && mCodecType != other.mCodecType) {
-            return false;
-        }
-        switch (mCodecType) {
-            case SOURCE_CODEC_TYPE_AAC:
-            case SOURCE_CODEC_TYPE_LDAC:
-                if (mCodecSpecific1 != other.mCodecSpecific1) {
-                    return false;
-                }
-            default:
-                return true;
-        }
-    }
-
-    /**
-     * Builder for {@link BluetoothCodecConfig}.
-     * <p> By default, the codec type will be set to
-     * {@link BluetoothCodecConfig#SOURCE_CODEC_TYPE_INVALID}, the codec priority
-     * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to
-     * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to
-     * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to
-     * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific
-     * values to 0.
-     */
-    public static final class Builder {
-        private int mCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
-        private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
-        private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE;
-        private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
-        private int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE;
-        private long mCodecSpecific1 = 0;
-        private long mCodecSpecific2 = 0;
-        private long mCodecSpecific3 = 0;
-        private long mCodecSpecific4 = 0;
-
-        /**
-         * Set codec type for Bluetooth codec config.
-         *
-         * @param codecType of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
-            mCodecType = codecType;
-            return this;
-        }
-
-        /**
-         * Set codec priority for Bluetooth codec config.
-         *
-         * @param codecPriority of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) {
-            mCodecPriority = codecPriority;
-            return this;
-        }
-
-        /**
-         * Set sample rate for Bluetooth codec config.
-         *
-         * @param sampleRate of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setSampleRate(@SampleRate int sampleRate) {
-            mSampleRate = sampleRate;
-            return this;
-        }
-
-        /**
-         * Set the bits per sample for Bluetooth codec config.
-         *
-         * @param bitsPerSample of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) {
-            mBitsPerSample = bitsPerSample;
-            return this;
-        }
-
-        /**
-         * Set the channel mode for Bluetooth codec config.
-         *
-         * @param channelMode of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setChannelMode(@ChannelMode int channelMode) {
-            mChannelMode = channelMode;
-            return this;
-        }
-
-        /**
-         * Set the first codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific1 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific1(long codecSpecific1) {
-            mCodecSpecific1 = codecSpecific1;
-            return this;
-        }
-
-        /**
-         * Set the second codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific2 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific2(long codecSpecific2) {
-            mCodecSpecific2 = codecSpecific2;
-            return this;
-        }
-
-        /**
-         * Set the third codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific3 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific3(long codecSpecific3) {
-            mCodecSpecific3 = codecSpecific3;
-            return this;
-        }
-
-        /**
-         * Set the fourth codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific4 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific4(long codecSpecific4) {
-            mCodecSpecific4 = codecSpecific4;
-            return this;
-        }
-
-        /**
-         * Build {@link BluetoothCodecConfig}.
-         * @return new BluetoothCodecConfig built
-         */
-        public @NonNull BluetoothCodecConfig build() {
-            return new BluetoothCodecConfig(mCodecType, mCodecPriority,
-                    mSampleRate, mBitsPerSample,
-                    mChannelMode, mCodecSpecific1,
-                    mCodecSpecific2, mCodecSpecific3,
-                    mCodecSpecific4);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
deleted file mode 100644
index 02606fe..0000000
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ /dev/null
@@ -1,208 +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 android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Represents the codec status (configuration and capability) for a Bluetooth
- * A2DP source device.
- *
- * {@see BluetoothA2dp}
- */
-public final class BluetoothCodecStatus implements Parcelable {
-    /**
-     * Extra for the codec configuration intents of the individual profiles.
-     *
-     * This extra represents the current codec status of the A2DP
-     * profile.
-     */
-    public static final String EXTRA_CODEC_STATUS =
-            "android.bluetooth.extra.CODEC_STATUS";
-
-    private final @Nullable BluetoothCodecConfig mCodecConfig;
-    private final @Nullable List<BluetoothCodecConfig> mCodecsLocalCapabilities;
-    private final @Nullable List<BluetoothCodecConfig> mCodecsSelectableCapabilities;
-
-    public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig,
-            @Nullable List<BluetoothCodecConfig> codecsLocalCapabilities,
-            @Nullable List<BluetoothCodecConfig> codecsSelectableCapabilities) {
-        mCodecConfig = codecConfig;
-        mCodecsLocalCapabilities = codecsLocalCapabilities;
-        mCodecsSelectableCapabilities = codecsSelectableCapabilities;
-    }
-
-    private BluetoothCodecStatus(Parcel in) {
-        mCodecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR);
-        mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR);
-        mCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothCodecStatus) {
-            BluetoothCodecStatus other = (BluetoothCodecStatus) o;
-            return (Objects.equals(other.mCodecConfig, mCodecConfig)
-                    && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
-                    && sameCapabilities(other.mCodecsSelectableCapabilities,
-                    mCodecsSelectableCapabilities));
-        }
-        return false;
-    }
-
-    /**
-     * Checks whether two lists of capabilities contain same capabilities.
-     * The order of the capabilities in each list is ignored.
-     *
-     * @param c1 the first list of capabilities to compare
-     * @param c2 the second list of capabilities to compare
-     * @return {@code true} if both lists contain same capabilities
-     */
-    private static boolean sameCapabilities(@Nullable List<BluetoothCodecConfig> c1,
-                                           @Nullable List<BluetoothCodecConfig> c2) {
-        if (c1 == null) {
-            return (c2 == null);
-        }
-        if (c2 == null) {
-            return false;
-        }
-        if (c1.size() != c2.size()) {
-            return false;
-        }
-        return c1.containsAll(c2);
-    }
-
-    /**
-     * Checks whether the codec config matches the selectable capabilities.
-     * Any parameters of the codec config with NONE value will be considered a wildcard matching.
-     *
-     * @param codecConfig the codec config to compare against
-     * @return {@code true} if the codec config matches, {@code false} otherwise
-     */
-    public boolean isCodecConfigSelectable(@Nullable BluetoothCodecConfig codecConfig) {
-        if (codecConfig == null || !codecConfig.hasSingleSampleRate()
-                || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) {
-            return false;
-        }
-        for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
-            if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
-                continue;
-            }
-            int sampleRate = codecConfig.getSampleRate();
-            if ((sampleRate & selectableConfig.getSampleRate()) == 0
-                    && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
-                continue;
-            }
-            int bitsPerSample = codecConfig.getBitsPerSample();
-            if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
-                    && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
-                continue;
-            }
-            int channelMode = codecConfig.getChannelMode();
-            if ((channelMode & selectableConfig.getChannelMode()) == 0
-                    && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
-                continue;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns a hash based on the codec config and local capabilities.
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
-                mCodecsLocalCapabilities);
-    }
-
-    /**
-     * Returns a {@link String} that describes each BluetoothCodecStatus parameter
-     * current value.
-     */
-    @Override
-    public String toString() {
-        return "{mCodecConfig:" + mCodecConfig
-                + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities
-                + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities
-                + "}";
-    }
-
-    /**
-     * @return 0
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecStatus> CREATOR =
-            new Parcelable.Creator<BluetoothCodecStatus>() {
-                public BluetoothCodecStatus createFromParcel(Parcel in) {
-                    return new BluetoothCodecStatus(in);
-                }
-
-                public BluetoothCodecStatus[] newArray(int size) {
-                    return new BluetoothCodecStatus[size];
-                }
-            };
-
-    /**
-     * Flattens the object to a parcel.
-     *
-     * @param out The Parcel in which the object should be written
-     * @param flags Additional flags about how the object should be written
-     */
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeTypedObject(mCodecConfig, 0);
-        out.writeTypedList(mCodecsLocalCapabilities);
-        out.writeTypedList(mCodecsSelectableCapabilities);
-    }
-
-    /**
-     * Returns the current codec configuration.
-     */
-    public @Nullable BluetoothCodecConfig getCodecConfig() {
-        return mCodecConfig;
-    }
-
-    /**
-     * Returns the codecs local capabilities.
-     */
-    public @NonNull List<BluetoothCodecConfig> getCodecsLocalCapabilities() {
-        return (mCodecsLocalCapabilities == null)
-                ? Collections.emptyList() : mCodecsLocalCapabilities;
-    }
-
-    /**
-     * Returns the codecs selectable capabilities.
-     */
-    public @NonNull List<BluetoothCodecConfig> getCodecsSelectableCapabilities() {
-        return (mCodecsSelectableCapabilities == null)
-                ? Collections.emptyList() : mCodecsSelectableCapabilities;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
deleted file mode 100644
index ba57ec4..0000000
--- a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth CSIP set coordinator.
- *
- * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothCsipSetCoordinator proxy object.
- *
- */
-public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable {
-    private static final String TAG = "BluetoothCsipSetCoordinator";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /**
-     * @hide
-     */
-    @SystemApi
-    public interface ClientLockCallback {
-        /**
-         * @hide
-         */
-        @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked);
-    }
-
-    private static class BluetoothCsipSetCoordinatorLockCallbackDelegate
-            extends IBluetoothCsipSetCoordinatorLockCallback.Stub {
-        private final ClientLockCallback mCallback;
-        private final Executor mExecutor;
-
-        BluetoothCsipSetCoordinatorLockCallbackDelegate(
-                Executor executor, ClientLockCallback callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) {
-            mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked));
-        }
-    };
-
-    /**
-     * Intent used to broadcast the change in connection state of the CSIS
-     * Client.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to expose broadcast receiving device.
-     *
-     * <p>This intent will have 2 extras:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device. </li>
-     * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
-     * <li> {@link #EXTRA_CSIS_GROUP_SIZE} - Group size. </li>
-     * <li> {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CSIS_DEVICE_AVAILABLE =
-            "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
-     * Contains the group id.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID";
-
-    /**
-     * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE";
-
-    /**
-     * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CSIS_GROUP_TYPE_UUID =
-            "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID";
-
-    /**
-     * Intent used to broadcast information about identified set member
-     * ready to connect.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * <li>  {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE =
-            "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
-
-    /**
-     * This represents an invalid group ID.
-     *
-     * @hide
-     */
-    public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID;
-
-    /**
-     * Indicating that group was locked with success.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_SUCCESS = 0;
-
-    /**
-     * Indicating that group locked failed due to invalid group ID.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1;
-
-    /**
-     * Indicating that group locked failed due to empty group.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2;
-
-    /**
-     * Indicating that group locked failed due to group members being disconnected.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3;
-
-    /**
-     * Indicating that group locked failed due to group member being already locked.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4;
-
-    /**
-     * Indicating that group locked failed due to other reason.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5;
-
-    /**
-     * Indicating that group member in locked state was lost.
-     *
-     * @hide
-     */
-    public static final int LOCKED_GROUP_MEMBER_LOST = 6;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG,
-                    IBluetoothCsipSetCoordinator.class.getName()) {
-                @Override
-                public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
-                    return IBluetoothCsipSetCoordinator.Stub.asInterface(service);
-                }
-            };
-
-    /**
-     * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local
-     * Bluetooth CSIS service.
-     */
-    /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    /**
-     * @hide
-     */
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    /**
-     * @hide
-     */
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothCsipSetCoordinator getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Lock the set.
-     * @param groupId group ID to lock,
-     * @param executor callback executor,
-     * @param cb callback to report lock and unlock events - stays valid until the app unlocks
-     *           using the returned lock identifier or the lock timeouts on the remote side,
-     *           as per CSIS specification,
-     * @return unique lock identifier used for unlocking or null if lock has failed.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public
-    @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
-            @Nullable ClientLockCallback cb) {
-        if (VDBG) log("groupLockSet()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final UUID defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            IBluetoothCsipSetCoordinatorLockCallback delegate = null;
-            if ((executor != null) && (cb != null)) {
-                delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
-            }
-            try {
-                final SynchronousResultReceiver<ParcelUuid> recv = new SynchronousResultReceiver();
-                service.groupLock(groupId, delegate, mAttributionSource, recv);
-                final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-                return ret == null ? defaultValue : ret.getUuid();
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Unlock the set.
-     * @param lockUuid unique lock identifier
-     * @return true if unlocked, false on error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean groupUnlock(@NonNull UUID lockUuid) {
-        if (VDBG) log("groupLockSet()");
-        if (lockUuid == null) {
-            return false;
-        }
-        final IBluetoothCsipSetCoordinator service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-                return true;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get device's groups.
-     * @param device the active device
-     * @return Map of groups ids and related UUIDs
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getGroupUuidMapByDevice()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final Map defaultValue = new HashMap<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Map> recv = new SynchronousResultReceiver();
-                service.getGroupUuidMapByDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get group id for the given UUID
-     * @param uuid
-     * @return list of group IDs
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
-        if (VDBG) log("getAllGroupIds()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final List<Integer> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<Integer>> recv =
-                        new SynchronousResultReceiver();
-                service.getAllGroupIds(uuid, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public
-    @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) {
-        if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public
-    @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean setConnectionPolicy(
-            @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
deleted file mode 100644
index 93f0268..0000000
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ /dev/null
@@ -1,2830 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.companion.AssociationRequest;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.UUID;
-
-/**
- * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
- * create a connection with the respective device or query information about
- * it, such as the name, address, class, and bonding state.
- *
- * <p>This class is really just a thin wrapper for a Bluetooth hardware
- * address. Objects of this class are immutable. Operations on this class
- * are performed on the remote Bluetooth hardware address, using the
- * {@link BluetoothAdapter} that was used to create this {@link
- * BluetoothDevice}.
- *
- * <p>To get a {@link BluetoothDevice}, use
- * {@link BluetoothAdapter#getRemoteDevice(String)
- * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device
- * of a known MAC address (which you can get through device discovery with
- * {@link BluetoothAdapter}) or get one from the set of bonded devices
- * returned by {@link BluetoothAdapter#getBondedDevices()
- * BluetoothAdapter.getBondedDevices()}. You can then open a
- * {@link BluetoothSocket} for communication with the remote device, using
- * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
- * {@link #createL2capChannel(int)} over Bluetooth LE.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about using Bluetooth, read the <a href=
- * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
- * guide.
- * </p>
- * </div>
- *
- * {@see BluetoothAdapter}
- * {@see BluetoothSocket}
- */
-public final class BluetoothDevice implements Parcelable, Attributable {
-    private static final String TAG = "BluetoothDevice";
-    private static final boolean DBG = false;
-
-    /**
-     * Connection state bitmask as returned by getConnectionState.
-     */
-    private static final int CONNECTION_STATE_DISCONNECTED = 0;
-    private static final int CONNECTION_STATE_CONNECTED = 1;
-    private static final int CONNECTION_STATE_ENCRYPTED_BREDR = 2;
-    private static final int CONNECTION_STATE_ENCRYPTED_LE = 4;
-
-    /**
-     * Sentinel error value for this class. Guaranteed to not equal any other
-     * integer constant in this class. Provided as a convenience for functions
-     * that require a sentinel error value, for example:
-     * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
-     * BluetoothDevice.ERROR)</code>
-     */
-    public static final int ERROR = Integer.MIN_VALUE;
-
-    /**
-     * Broadcast Action: Remote device discovered.
-     * <p>Sent when a remote device is found during discovery.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
-     * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available.
-     */
-    // TODO: Change API to not broadcast RSSI if not available (incoming connection)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_FOUND =
-            "android.bluetooth.device.action.FOUND";
-
-    /**
-     * Broadcast Action: Bluetooth class of a remote device has changed.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_CLASS}.
-     * {@see BluetoothClass}
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CLASS_CHANGED =
-            "android.bluetooth.device.action.CLASS_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates a low level (ACL) connection has been
-     * established with a remote device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     * <p>ACL connections are managed automatically by the Android Bluetooth
-     * stack.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACL_CONNECTED =
-            "android.bluetooth.device.action.ACL_CONNECTED";
-
-    /**
-     * Broadcast Action: Indicates that a low level (ACL) disconnection has
-     * been requested for a remote device, and it will soon be disconnected.
-     * <p>This is useful for graceful disconnection. Applications should use
-     * this intent as a hint to immediately terminate higher level connections
-     * (RFCOMM, L2CAP, or profile connections) to the remote device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACL_DISCONNECT_REQUESTED =
-            "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
-
-    /**
-     * Broadcast Action: Indicates a low level (ACL) disconnection from a
-     * remote device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     * <p>ACL connections are managed automatically by the Android Bluetooth
-     * stack.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACL_DISCONNECTED =
-            "android.bluetooth.device.action.ACL_DISCONNECTED";
-
-    /**
-     * Broadcast Action: Indicates the friendly name of a remote device has
-     * been retrieved for the first time, or changed since the last retrieval.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_NAME}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NAME_CHANGED =
-            "android.bluetooth.device.action.NAME_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates the alias of a remote device has been
-     * changed.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     */
-    @SuppressLint("ActionValue")
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ALIAS_CHANGED =
-            "android.bluetooth.device.action.ALIAS_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates a change in the bond state of a remote
-     * device. For example, if a device is bonded (paired).
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
-     * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
-     */
-    // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
-    // contain a hidden extra field EXTRA_REASON with the result code.
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BOND_STATE_CHANGED =
-            "android.bluetooth.device.action.BOND_STATE_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates the battery level of a remote device has
-     * been retrieved for the first time, or changed since the last retrieval
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_BATTERY_LEVEL}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BATTERY_LEVEL_CHANGED =
-            "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED";
-
-    /**
-     * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED}
-     * intent. It contains the most recently retrieved battery level information
-     * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN}
-     * when the valid is unknown or there is an error
-     *
-     * @hide
-     */
-    public static final String EXTRA_BATTERY_LEVEL =
-            "android.bluetooth.device.extra.BATTERY_LEVEL";
-
-    /**
-     * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()}
-     *
-     * @hide
-     */
-    public static final int BATTERY_LEVEL_UNKNOWN = -1;
-
-    /**
-     * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off
-     *
-     * @hide
-     */
-    public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100;
-
-    /**
-     * Used as a Parcelable {@link BluetoothDevice} extra field in every intent
-     * broadcast by this class. It contains the {@link BluetoothDevice} that
-     * the intent applies to.
-     */
-    public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
-
-    /**
-     * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link
-     * #ACTION_FOUND} intents. It contains the friendly Bluetooth name.
-     */
-    public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
-
-    /**
-     * Used as an optional short extra field in {@link #ACTION_FOUND} intents.
-     * Contains the RSSI value of the remote device as reported by the
-     * Bluetooth hardware.
-     */
-    public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
-
-    /**
-    * Used as an bool extra field in {@link #ACTION_FOUND} intents.
-    * It contains the information if device is discovered as member of a coordinated set or not.
-    * Pairing with device that belongs to a set would trigger pairing with the rest of set members.
-    * See Bluetooth CSIP specification for more details.
-    */
-    public static final String EXTRA_IS_COORDINATED_SET_MEMBER =
-            "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER";
-
-    /**
-     * Used as a Parcelable {@link BluetoothClass} extra field in {@link
-     * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
-     */
-    public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
-     * Contains the bond state of the remote device.
-     * <p>Possible values are:
-     * {@link #BOND_NONE},
-     * {@link #BOND_BONDING},
-     * {@link #BOND_BONDED}.
-     */
-    public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
-    /**
-     * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
-     * Contains the previous bond state of the remote device.
-     * <p>Possible values are:
-     * {@link #BOND_NONE},
-     * {@link #BOND_BONDING},
-     * {@link #BOND_BONDED}.
-     */
-    public static final String EXTRA_PREVIOUS_BOND_STATE =
-            "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
-    /**
-     * Indicates the remote device is not bonded (paired).
-     * <p>There is no shared link key with the remote device, so communication
-     * (if it is allowed at all) will be unauthenticated and unencrypted.
-     */
-    public static final int BOND_NONE = 10;
-    /**
-     * Indicates bonding (pairing) is in progress with the remote device.
-     */
-    public static final int BOND_BONDING = 11;
-    /**
-     * Indicates the remote device is bonded (paired).
-     * <p>A shared link keys exists locally for the remote device, so
-     * communication can be authenticated and encrypted.
-     * <p><i>Being bonded (paired) with a remote device does not necessarily
-     * mean the device is currently connected. It just means that the pending
-     * procedure was completed at some earlier time, and the link key is still
-     * stored locally, ready to use on the next connection.
-     * </i>
-     */
-    public static final int BOND_BONDED = 12;
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents for unbond reason.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents to indicate pairing method used. Possible values are:
-     * {@link #PAIRING_VARIANT_PIN},
-     * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION},
-     */
-    public static final String EXTRA_PAIRING_VARIANT =
-            "android.bluetooth.device.extra.PAIRING_VARIANT";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents as the value of passkey.
-     */
-    public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents as the value of passkey.
-     * @hide
-     */
-    public static final String EXTRA_PAIRING_INITIATOR =
-            "android.bluetooth.device.extra.PAIRING_INITIATOR";
-
-    /**
-     * Bluetooth pairing initiator, Foreground App
-     * @hide
-     */
-    public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1;
-
-    /**
-     * Bluetooth pairing initiator, Background
-     * @hide
-     */
-    public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2;
-
-    /**
-     * Bluetooth device type, Unknown
-     */
-    public static final int DEVICE_TYPE_UNKNOWN = 0;
-
-    /**
-     * Bluetooth device type, Classic - BR/EDR devices
-     */
-    public static final int DEVICE_TYPE_CLASSIC = 1;
-
-    /**
-     * Bluetooth device type, Low Energy - LE-only
-     */
-    public static final int DEVICE_TYPE_LE = 2;
-
-    /**
-     * Bluetooth device type, Dual Mode - BR/EDR/LE
-     */
-    public static final int DEVICE_TYPE_DUAL = 3;
-
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final String ACTION_SDP_RECORD =
-            "android.bluetooth.device.action.SDP_RECORD";
-
-    /** @hide */
-    @IntDef(prefix = "METADATA_", value = {
-            METADATA_MANUFACTURER_NAME,
-            METADATA_MODEL_NAME,
-            METADATA_SOFTWARE_VERSION,
-            METADATA_HARDWARE_VERSION,
-            METADATA_COMPANION_APP,
-            METADATA_MAIN_ICON,
-            METADATA_IS_UNTETHERED_HEADSET,
-            METADATA_UNTETHERED_LEFT_ICON,
-            METADATA_UNTETHERED_RIGHT_ICON,
-            METADATA_UNTETHERED_CASE_ICON,
-            METADATA_UNTETHERED_LEFT_BATTERY,
-            METADATA_UNTETHERED_RIGHT_BATTERY,
-            METADATA_UNTETHERED_CASE_BATTERY,
-            METADATA_UNTETHERED_LEFT_CHARGING,
-            METADATA_UNTETHERED_RIGHT_CHARGING,
-            METADATA_UNTETHERED_CASE_CHARGING,
-            METADATA_ENHANCED_SETTINGS_UI_URI,
-            METADATA_DEVICE_TYPE,
-            METADATA_MAIN_BATTERY,
-            METADATA_MAIN_CHARGING,
-            METADATA_MAIN_LOW_BATTERY_THRESHOLD,
-            METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
-            METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
-            METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MetadataKey{}
-
-    /**
-     * Maximum length of a metadata entry, this is to avoid exploding Bluetooth
-     * disk usage
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAX_LENGTH = 2048;
-
-    /**
-     * Manufacturer name of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MANUFACTURER_NAME = 0;
-
-    /**
-     * Model name of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MODEL_NAME = 1;
-
-    /**
-     * Software version of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_SOFTWARE_VERSION = 2;
-
-    /**
-     * Hardware version of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_HARDWARE_VERSION = 3;
-
-    /**
-     * Package name of the companion app, if any
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_COMPANION_APP = 4;
-
-    /**
-     * URI to the main icon shown on the settings UI
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_ICON = 5;
-
-    /**
-     * Whether this device is an untethered headset with left, right and case
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_IS_UNTETHERED_HEADSET = 6;
-
-    /**
-     * URI to icon of the left headset
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_ICON = 7;
-
-    /**
-     * URI to icon of the right headset
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_ICON = 8;
-
-    /**
-     * URI to icon of the headset charging case
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_ICON = 9;
-
-    /**
-     * Battery level of left headset
-     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
-     * as invalid.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10;
-
-    /**
-     * Battery level of rigth headset
-     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
-     * as invalid.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11;
-
-    /**
-     * Battery level of the headset charging case
-     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
-     * as invalid.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_BATTERY = 12;
-
-    /**
-     * Whether the left headset is charging
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13;
-
-    /**
-     * Whether the right headset is charging
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14;
-
-    /**
-     * Whether the headset charging case is charging
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_CHARGING = 15;
-
-    /**
-     * URI to the enhanced settings UI slice
-     * Data type should be {@String} as {@link Byte} array, null means
-     * the UI does not exist.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16;
-
-    /**
-     * Type of the Bluetooth device, must be within the list of
-     * BluetoothDevice.DEVICE_TYPE_*
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_DEVICE_TYPE = 17;
-
-    /**
-     * Battery level of the Bluetooth device, use when the Bluetooth device
-     * does not support HFP battery indicator.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_BATTERY = 18;
-
-    /**
-     * Whether the device is charging.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_CHARGING = 19;
-
-    /**
-     * The battery threshold of the Bluetooth device to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20;
-
-    /**
-     * The battery threshold of the left headset to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21;
-
-    /**
-     * The battery threshold of the right headset to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22;
-
-    /**
-     * The battery threshold of the case to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23;
-
-    /**
-     * Device type which is used in METADATA_DEVICE_TYPE
-     * Indicates this Bluetooth device is a standard Bluetooth accessory or
-     * not listed in METADATA_DEVICE_TYPE_*.
-     * @hide
-     */
-    @SystemApi
-    public static final String DEVICE_TYPE_DEFAULT = "Default";
-
-    /**
-     * Device type which is used in METADATA_DEVICE_TYPE
-     * Indicates this Bluetooth device is a watch.
-     * @hide
-     */
-    @SystemApi
-    public static final String DEVICE_TYPE_WATCH = "Watch";
-
-    /**
-     * Device type which is used in METADATA_DEVICE_TYPE
-     * Indicates this Bluetooth device is an untethered headset.
-     * @hide
-     */
-    @SystemApi
-    public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset";
-
-    /**
-     * Broadcast Action: This intent is used to broadcast the {@link UUID}
-     * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
-     * has been fetched. This intent is sent only when the UUIDs of the remote
-     * device are requested to be fetched using Service Discovery Protocol
-     * <p> Always contains the extra field {@link #EXTRA_DEVICE}
-     * <p> Always contains the extra field {@link #EXTRA_UUID}
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_UUID =
-            "android.bluetooth.device.action.UUID";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MAS_INSTANCE =
-            "android.bluetooth.device.action.MAS_INSTANCE";
-
-    /**
-     * Broadcast Action: Indicates a failure to retrieve the name of a remote
-     * device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     *
-     * @hide
-     */
-    //TODO: is this actually useful?
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NAME_FAILED =
-            "android.bluetooth.device.action.NAME_FAILED";
-
-    /**
-     * Broadcast Action: This intent is used to broadcast PAIRING REQUEST
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PAIRING_REQUEST =
-            "android.bluetooth.device.action.PAIRING_REQUEST";
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage
-    public static final String ACTION_PAIRING_CANCEL =
-            "android.bluetooth.device.action.PAIRING_CANCEL";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_ACCESS_REQUEST =
-            "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_ACCESS_REPLY =
-            "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_ACCESS_CANCEL =
-            "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
-
-    /**
-     * Intent to broadcast silence mode changed.
-     * Alway contains the extra field {@link #EXTRA_DEVICE}
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi
-    public static final String ACTION_SILENCE_MODE_CHANGED =
-            "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_ACCESS_REQUEST_TYPE =
-            "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE";
-
-    /** @hide */
-    public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1;
-
-    /** @hide */
-    public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2;
-
-    /** @hide */
-    public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3;
-
-    /** @hide */
-    public static final int REQUEST_TYPE_SIM_ACCESS = 4;
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
-     * Contains package name to return reply intent to.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
-     * Contains class name to return reply intent to.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CONNECTION_ACCESS_RESULT =
-            "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT";
-
-    /** @hide */
-    public static final int CONNECTION_ACCESS_YES = 1;
-
-    /** @hide */
-    public static final int CONNECTION_ACCESS_NO = 2;
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents,
-     * Contains boolean to indicate if the allowed response is once-for-all so that
-     * next request will be granted without asking user again.
-     *
-     * @hide
-     */
-    public static final String EXTRA_ALWAYS_ALLOWED =
-            "android.bluetooth.device.extra.ALWAYS_ALLOWED";
-
-    /**
-     * A bond attempt succeeded
-     *
-     * @hide
-     */
-    public static final int BOND_SUCCESS = 0;
-
-    /**
-     * A bond attempt failed because pins did not match, or remote device did
-     * not respond to pin request in time
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_AUTH_FAILED = 1;
-
-    /**
-     * A bond attempt failed because the other side explicitly rejected
-     * bonding
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_AUTH_REJECTED = 2;
-
-    /**
-     * A bond attempt failed because we canceled the bonding process
-     *
-     * @hide
-     */
-    public static final int UNBOND_REASON_AUTH_CANCELED = 3;
-
-    /**
-     * A bond attempt failed because we could not contact the remote device
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
-
-    /**
-     * A bond attempt failed because a discovery is in progress
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
-
-    /**
-     * A bond attempt failed because of authentication timeout
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_AUTH_TIMEOUT = 6;
-
-    /**
-     * A bond attempt failed because of repeated attempts
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7;
-
-    /**
-     * A bond attempt failed because we received an Authentication Cancel
-     * by remote end
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
-
-    /**
-     * An existing bond was explicitly revoked
-     *
-     * @hide
-     */
-    public static final int UNBOND_REASON_REMOVED = 9;
-
-    /**
-     * The user will be prompted to enter a pin or
-     * an app will enter a pin for user.
-     */
-    public static final int PAIRING_VARIANT_PIN = 0;
-
-    /**
-     * The user will be prompted to enter a passkey
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_PASSKEY = 1;
-
-    /**
-     * The user will be prompted to confirm the passkey displayed on the screen or
-     * an app will confirm the passkey for the user.
-     */
-    public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2;
-
-    /**
-     * The user will be prompted to accept or deny the incoming pairing request
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_CONSENT = 3;
-
-    /**
-     * The user will be prompted to enter the passkey displayed on remote device
-     * This is used for Bluetooth 2.1 pairing.
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
-
-    /**
-     * The user will be prompted to enter the PIN displayed on remote device.
-     * This is used for Bluetooth 2.0 pairing.
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_DISPLAY_PIN = 5;
-
-    /**
-     * The user will be prompted to accept or deny the OOB pairing request
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_OOB_CONSENT = 6;
-
-    /**
-     * The user will be prompted to enter a 16 digit pin or
-     * an app will enter a 16 digit pin for user.
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7;
-
-    /**
-     * Used as an extra field in {@link #ACTION_UUID} intents,
-     * Contains the {@link android.os.ParcelUuid}s of the remote device which
-     * is a parcelable version of {@link UUID}.
-     */
-    public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
-
-    /** @hide */
-    public static final String EXTRA_SDP_RECORD =
-            "android.bluetooth.device.extra.SDP_RECORD";
-
-    /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final String EXTRA_SDP_SEARCH_STATUS =
-            "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
-
-    /** @hide */
-    @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN,
-            ACCESS_ALLOWED, ACCESS_REJECTED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AccessPermission{}
-
-    /**
-     * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
-     * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACCESS_UNKNOWN = 0;
-
-    /**
-     * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
-     * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACCESS_ALLOWED = 1;
-
-    /**
-     * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
-     * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACCESS_REJECTED = 2;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "TRANSPORT_" },
-        value = {
-            /** Allow host to automatically select a transport (dual-mode only) */
-            TRANSPORT_AUTO,
-            /** Use Classic or BR/EDR transport.*/
-            TRANSPORT_BREDR,
-            /** Use Low Energy transport.*/
-            TRANSPORT_LE,
-        }
-    )
-    public @interface Transport {}
-
-    /**
-     * No preference of physical transport for GATT connections to remote dual-mode devices
-     */
-    public static final int TRANSPORT_AUTO = 0;
-
-    /**
-     * Prefer BR/EDR transport for GATT connections to remote dual-mode devices
-     */
-    public static final int TRANSPORT_BREDR = 1;
-
-    /**
-     * Prefer LE transport for GATT connections to remote dual-mode devices
-     */
-    public static final int TRANSPORT_LE = 2;
-
-    /**
-     * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or
-     * connection.
-     */
-    public static final int PHY_LE_1M = 1;
-
-    /**
-     * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or
-     * connection.
-     */
-    public static final int PHY_LE_2M = 2;
-
-    /**
-     * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning
-     * or connection.
-     */
-    public static final int PHY_LE_CODED = 3;
-
-    /**
-     * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available
-     * options in a bitmask.
-     */
-    public static final int PHY_LE_1M_MASK = 1;
-
-    /**
-     * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available
-     * options in a bitmask.
-     */
-    public static final int PHY_LE_2M_MASK = 2;
-
-    /**
-     * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many
-     * available options in a bitmask.
-     */
-    public static final int PHY_LE_CODED_MASK = 4;
-
-    /**
-     * No preferred coding when transmitting on the LE Coded PHY.
-     */
-    public static final int PHY_OPTION_NO_PREFERRED = 0;
-
-    /**
-     * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY.
-     */
-    public static final int PHY_OPTION_S2 = 1;
-
-    /**
-     * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY.
-     */
-    public static final int PHY_OPTION_S8 = 2;
-
-
-    /** @hide */
-    public static final String EXTRA_MAS_INSTANCE =
-            "android.bluetooth.device.extra.MAS_INSTANCE";
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "ADDRESS_TYPE_" },
-        value = {
-            /** Hardware MAC Address */
-            ADDRESS_TYPE_PUBLIC,
-            /** Address is either resolvable, non-resolvable or static.*/
-            ADDRESS_TYPE_RANDOM,
-        }
-    )
-    public @interface AddressType {}
-
-    /** Hardware MAC Address of the device */
-    public static final int ADDRESS_TYPE_PUBLIC = 0;
-    /** Address is either resolvable, non-resolvable or static. */
-    public static final int ADDRESS_TYPE_RANDOM = 1;
-
-    private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00";
-
-    /**
-     * Lazy initialization. Guaranteed final after first object constructed, or
-     * getService() called.
-     * TODO: Unify implementation of sService amongst BluetoothFoo API's
-     */
-    private static volatile IBluetooth sService;
-
-    private final String mAddress;
-    @AddressType private final int mAddressType;
-
-    private AttributionSource mAttributionSource;
-
-    /*package*/
-    @UnsupportedAppUsage
-    static IBluetooth getService() {
-        synchronized (BluetoothDevice.class) {
-            if (sService == null) {
-                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-                sService = adapter.getBluetoothService(sStateChangeCallback);
-            }
-        }
-        return sService;
-    }
-
-    static IBluetoothManagerCallback sStateChangeCallback = new IBluetoothManagerCallback.Stub() {
-
-        public void onBluetoothServiceUp(IBluetooth bluetoothService)
-                throws RemoteException {
-            synchronized (BluetoothDevice.class) {
-                if (sService == null) {
-                    sService = bluetoothService;
-                }
-            }
-        }
-
-        public void onBluetoothServiceDown()
-                throws RemoteException {
-            synchronized (BluetoothDevice.class) {
-                sService = null;
-            }
-        }
-
-        public void onBrEdrDown() {
-            if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
-        }
-
-        public void onOobData(@Transport int transport, OobData oobData) {
-            if (DBG) Log.d(TAG, "onOobData: got data");
-        }
-    };
-
-    /**
-     * Create a new BluetoothDevice
-     * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
-     * and is validated in this constructor.
-     *
-     * @param address valid Bluetooth MAC address
-     * @param attributionSource attribution for permission-protected calls
-     * @throws RuntimeException Bluetooth is not available on this platform
-     * @throws IllegalArgumentException address is invalid
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /*package*/ BluetoothDevice(String address) {
-        getService();  // ensures sService is initialized
-        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
-            throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
-        }
-
-        mAddress = address;
-        mAddressType = ADDRESS_TYPE_PUBLIC;
-        mAttributionSource = AttributionSource.myAttributionSource();
-    }
-
-    /** {@hide} */
-    public void setAttributionSource(@NonNull AttributionSource attributionSource) {
-        mAttributionSource = attributionSource;
-    }
-
-    /** {@hide} */
-    public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) {
-        setAttributionSource(attributionSource);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothDevice) {
-            return mAddress.equals(((BluetoothDevice) o).getAddress());
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mAddress.hashCode();
-    }
-
-    /**
-     * Returns a string representation of this BluetoothDevice.
-     * <p>Currently this is the Bluetooth hardware address, for example
-     * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
-     * if you explicitly require the Bluetooth hardware address in case the
-     * {@link #toString} representation changes in the future.
-     *
-     * @return string representation of this BluetoothDevice
-     */
-    @Override
-    public String toString() {
-        return mAddress;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothDevice> CREATOR =
-            new Parcelable.Creator<BluetoothDevice>() {
-                public BluetoothDevice createFromParcel(Parcel in) {
-                    return new BluetoothDevice(in.readString());
-                }
-
-                public BluetoothDevice[] newArray(int size) {
-                    return new BluetoothDevice[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mAddress);
-    }
-
-    /**
-     * Returns the hardware address of this BluetoothDevice.
-     * <p> For example, "00:11:22:AA:BB:CC".
-     *
-     * @return Bluetooth hardware address as string
-     */
-    public String getAddress() {
-        if (DBG) Log.d(TAG, "mAddress: " + mAddress);
-        return mAddress;
-    }
-
-    /**
-     * Returns the anonymized hardware address of this BluetoothDevice. The first three octets
-     * will be suppressed for anonymization.
-     * <p> For example, "XX:XX:XX:AA:BB:CC".
-     *
-     * @return Anonymized bluetooth hardware address as string
-     * @hide
-     */
-    public String getAnonymizedAddress() {
-        return "XX:XX:XX" + getAddress().substring(8);
-    }
-
-    /**
-     * Get the friendly Bluetooth name of the remote device.
-     *
-     * <p>The local adapter will automatically retrieve remote names when
-     * performing a device scan, and will cache them. This method just returns
-     * the name for this device from the cache.
-     *
-     * @return the Bluetooth name, or null if there was a problem.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getName() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Remote Device name");
-            return null;
-        }
-        try {
-            String name = service.getRemoteName(this, mAttributionSource);
-            if (name != null) {
-                // remove whitespace characters from the name
-                return name
-                        .replace('\t', ' ')
-                        .replace('\n', ' ')
-                        .replace('\r', ' ');
-            }
-            return null;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Get the Bluetooth device type of the remote device.
-     *
-     * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link
-     * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getType() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Remote Device type");
-            return DEVICE_TYPE_UNKNOWN;
-        }
-        try {
-            return service.getRemoteType(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return DEVICE_TYPE_UNKNOWN;
-    }
-
-    /**
-     * Get the locally modifiable name (alias) of the remote Bluetooth device.
-     *
-     * @return the Bluetooth alias, the friendly device name if no alias, or
-     * null if there was a problem
-     */
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getAlias() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias");
-            return null;
-        }
-        try {
-            String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource);
-            if (alias == null) {
-                return getName();
-            }
-            return alias
-                    .replace('\t', ' ')
-                    .replace('\n', ' ')
-                    .replace('\r', ' ');
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
-    })
-    public @interface SetAliasReturnValues{}
-
-    /**
-     * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method
-     * overwrites the previously stored alias. The new alias is saved in local
-     * storage so that the change is preserved over power cycles.
-     *
-     * <p>This method requires the calling app to be associated with Companion Device Manager (see
-     * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest,
-     * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the
-     * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the
-     * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can
-     * bypass the Companion Device Manager association requirement as well as other permission
-     * requirements.
-     *
-     * @param alias is the new locally modifiable name for the remote Bluetooth device which must
-     *              be the empty string. If null, we clear the alias.
-     * @return whether the alias was successfully changed
-     * @throws IllegalArgumentException if the alias is the empty string
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @SetAliasReturnValues int setAlias(@Nullable String alias) {
-        if (alias != null && alias.isEmpty()) {
-            throw new IllegalArgumentException("alias cannot be the empty string");
-        }
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            return service.setRemoteAlias(this, alias, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the most recent identified battery level of this Bluetooth device
-     *
-     * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if
-     * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does
-     * not have any battery reporting service, or return value is invalid
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getBatteryLevel() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level");
-            return BATTERY_LEVEL_BLUETOOTH_OFF;
-        }
-        try {
-            return service.getBatteryLevel(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return BATTERY_LEVEL_UNKNOWN;
-    }
-
-    /**
-     * Start the bonding (pairing) process with the remote device.
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
-     * the bonding process completes, and its result.
-     * <p>Android system services will handle the necessary user interactions
-     * to confirm and complete the bonding process.
-     *
-     * @return false on immediate error, true if bonding will begin
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean createBond() {
-        return createBond(TRANSPORT_AUTO);
-    }
-
-    /**
-     * Start the bonding (pairing) process with the remote device using the
-     * specified transport.
-     *
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
-     * the bonding process completes, and its result.
-     * <p>Android system services will handle the necessary user interactions
-     * to confirm and complete the bonding process.
-     *
-     * @param transport The transport to use for the pairing procedure.
-     * @return false on immediate error, true if bonding will begin
-     * @throws IllegalArgumentException if an invalid transport was specified
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean createBond(int transport) {
-        return createBondInternal(transport, null, null);
-    }
-
-    /**
-     * Start the bonding (pairing) process with the remote device using the
-     * Out Of Band mechanism.
-     *
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
-     * the bonding process completes, and its result.
-     *
-     * <p>Android system services will handle the necessary user interactions
-     * to confirm and complete the bonding process.
-     *
-     * <p>There are two possible versions of OOB Data.  This data can come in as
-     * P192 or P256.  This is a reference to the cryptography used to generate the key.
-     * The caller may pass one or both.  If both types of data are passed, then the
-     * P256 data will be preferred, and thus used.
-     *
-     * @param transport - Transport to use
-     * @param remoteP192Data - Out Of Band data (P192) or null
-     * @param remoteP256Data - Out Of Band data (P256) or null
-     * @return false on immediate error, true if bonding will begin
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
-            @Nullable OobData remoteP256Data) {
-        if (remoteP192Data == null && remoteP256Data == null) {
-            throw new IllegalArgumentException(
-                "One or both arguments for the OOB data types are required to not be null."
-                + "  Please use createBond() instead if you do not have OOB data to pass.");
-        }
-        return createBondInternal(transport, remoteP192Data, remoteP256Data);
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
-            @Nullable OobData remoteP256Data) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
-            return false;
-        }
-        if (NULL_MAC_ADDRESS.equals(mAddress)) {
-            Log.e(TAG, "Unable to create bond, invalid address " + mAddress);
-            return false;
-        }
-        try {
-            return service.createBond(
-                    this, transport, remoteP192Data, remoteP256Data, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Gets whether bonding was initiated locally
-     *
-     * @return true if bonding is initiated locally, false otherwise
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isBondingInitiatedLocally() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed");
-            return false;
-        }
-        try {
-            return service.isBondingInitiatedLocally(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Cancel an in-progress bonding request started with {@link #createBond}.
-     *
-     * @return true on success, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean cancelBondProcess() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond");
-            return false;
-        }
-        try {
-            Log.i(TAG, "cancelBondProcess() for device " + getAddress()
-                    + " called by pid: " + Process.myPid()
-                    + " tid: " + Process.myTid());
-            return service.cancelBondProcess(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Remove bond (pairing) with the remote device.
-     * <p>Delete the link key associated with the remote device, and
-     * immediately terminate connections to that device that require
-     * authentication and encryption.
-     *
-     * @return true on success, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean removeBond() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond");
-            return false;
-        }
-        try {
-            Log.i(TAG, "removeBond() for device " + getAddress()
-                    + " called by pid: " + Process.myPid()
-                    + " tid: " + Process.myTid());
-            return service.removeBond(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
-            "cache_key.bluetooth.get_bond_state";
-    private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
-            new PropertyInvalidatedCache<BluetoothDevice, Integer>(
-                8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                protected Integer recompute(BluetoothDevice query) {
-                    try {
-                        return sService.getBondState(query, mAttributionSource);
-                    } catch (RemoteException e) {
-                        throw e.rethrowAsRuntimeException();
-                    }
-                }
-            };
-
-    /** @hide */
-    public void disableBluetoothGetBondStateCache() {
-        mBluetoothBondCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateBluetoothGetBondStateCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
-    }
-
-    /**
-     * Get the bond state of the remote device.
-     * <p>Possible values for the bond state are:
-     * {@link #BOND_NONE},
-     * {@link #BOND_BONDING},
-     * {@link #BOND_BONDED}.
-     *
-     * @return the bond state
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public int getBondState() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get bond state");
-            return BOND_NONE;
-        }
-        try {
-            return mBluetoothBondCache.query(this);
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "", e);
-            } else {
-                throw e;
-            }
-        }
-        return BOND_NONE;
-    }
-
-    /**
-     * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip
-     * the bluetooth pairing dialog because it has been already consented by the CDM prompt.
-     *
-     * @return true if we can bond without the dialog, false otherwise
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean canBondWithoutDialog() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog");
-            return false;
-        }
-        try {
-            if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this);
-            return service.canBondWithoutDialog(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
-    })
-    public @interface ConnectionReturnValues{}
-
-    /**
-     * Connects all user enabled and supported bluetooth profiles between the local and remote
-     * device. If no profiles are user enabled (e.g. first connection), we connect all supported
-     * profiles. If the device is not already connected, this will page the device before initiating
-     * profile connections. Connection is asynchronous and you should listen to each profile's
-     * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful.
-     * For example, to verify a2dp is connected, you would listen for
-     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * @return whether the messages were successfully sent to try to connect all profiles
-     * @throws IllegalArgumentException if the device address is invalid
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public @ConnectionReturnValues int connect() {
-        if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
-            throw new IllegalArgumentException("device cannot have an invalid address");
-        }
-
-        try {
-            if (sService == null) {
-                Log.e(TAG, "BT not enabled. Cannot connect to remote device.");
-                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-            }
-            return sService.connectAllEnabledProfiles(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Disconnects all connected bluetooth profiles between the local and remote device.
-     * Disconnection is asynchronous and you should listen to each profile's broadcast intent
-     * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
-     * to verify a2dp is disconnected, you would listen for
-     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * @return whether the messages were successfully sent to try to disconnect all profiles
-     * @throws IllegalArgumentException if the device address is invalid
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionReturnValues int disconnect() {
-        if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
-            throw new IllegalArgumentException("device cannot have an invalid address");
-        }
-
-        try {
-            if (sService == null) {
-                Log.e(TAG, "BT not enabled. Cannot disconnect from remote device.");
-                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-            }
-            return sService.disconnectAllEnabledProfiles(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Returns whether there is an open connection to this device.
-     *
-     * @return True if there is at least one open connection to this device.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            // BT is not enabled, we cannot be connected.
-            return false;
-        }
-        try {
-            return service.getConnectionStateWithAttribution(this, mAttributionSource)
-                    != CONNECTION_STATE_DISCONNECTED;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Returns whether there is an open connection to this device
-     * that has been encrypted.
-     *
-     * @return True if there is at least one encrypted connection to this device.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isEncrypted() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            // BT is not enabled, we cannot be connected.
-            return false;
-        }
-        try {
-            return service.getConnectionStateWithAttribution(this, mAttributionSource)
-                    > CONNECTION_STATE_CONNECTED;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get the Bluetooth class of the remote device.
-     *
-     * @return Bluetooth class object, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothClass getBluetoothClass() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class");
-            return null;
-        }
-        try {
-            int classInt = service.getRemoteClass(this, mAttributionSource);
-            if (classInt == BluetoothClass.ERROR) return null;
-            return new BluetoothClass(classInt);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Returns the supported features (UUIDs) of the remote device.
-     *
-     * <p>This method does not start a service discovery procedure to retrieve the UUIDs
-     * from the remote device. Instead, the local cached copy of the service
-     * UUIDs are returned.
-     * <p>Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired.
-     *
-     * @return the supported features (UUIDs) of the remote device, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public ParcelUuid[] getUuids() {
-        final IBluetooth service = sService;
-        if (service == null || !isBluetoothEnabled()) {
-            Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
-            return null;
-        }
-        try {
-            return service.getRemoteUuids(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Perform a service discovery on the remote device to get the UUIDs supported.
-     *
-     * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
-     * with the UUIDs supported by the remote end. If there is an error
-     * in getting the SDP records or if the process takes a long time, or the device is bonding and
-     * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is
-     * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs
-     * if service discovery is not to be performed. If there is an ongoing bonding process,
-     * service discovery or device inquiry, the request will be queued.
-     *
-     * @return False if the check fails, True if the process of initiating an ACL connection
-     * to the remote device was started or cached UUIDs will be broadcast.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean fetchUuidsWithSdp() {
-        return fetchUuidsWithSdp(TRANSPORT_AUTO);
-    }
-
-    /**
-     * Perform a service discovery on the remote device to get the UUIDs supported with the
-     * specific transport.
-     *
-     * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
-     * with the UUIDs supported by the remote end. If there is an error
-     * in getting the SDP or GATT records or if the process takes a long time, or the device
-     * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the
-     * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids}
-     * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding
-     * process, service discovery or device inquiry, the request will be queued.
-     *
-     * @param transport - provide type of transport (e.g. LE or Classic).
-     * @return False if the check fails, True if the process of initiating an ACL connection
-     * to the remote device was started or cached UUIDs will be broadcast with the specific
-     * transport.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean fetchUuidsWithSdp(@Transport int transport) {
-        final IBluetooth service = sService;
-        if (service == null || !isBluetoothEnabled()) {
-            Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
-            return false;
-        }
-        try {
-            return service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Perform a service discovery on the remote device to get the SDP records associated
-     * with the specified UUID.
-     *
-     * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent,
-     * with the SDP records found on the remote end. If there is an error
-     * in getting the SDP records or if the process takes a long time,
-     * {@link #ACTION_SDP_RECORD} intent is sent with an status value in
-     * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
-     * Detailed status error codes can be found by members of the Bluetooth package in
-     * the AbstractionLayer class.
-     * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
-     * The object type will match one of the SdpXxxRecord types, depending on the UUID searched
-     * for.
-     *
-     * @return False if the check fails, True if the process
-     *               of initiating an ACL connection to the remote device
-     *               was started.
-     */
-    /** @hide */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sdpSearch(ParcelUuid uuid) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
-            return false;
-        }
-        try {
-            return service.sdpSearch(this, uuid, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
-     *
-     * @return true pin has been set false for error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPin(byte[] pin) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot set Remote Device pin");
-            return false;
-        }
-        try {
-            return service.setPin(this, true, pin.length, pin, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
-     *
-     * @return true pin has been set false for error
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPin(@NonNull String pin) {
-        byte[] pinBytes = convertPinToBytes(pin);
-        if (pinBytes == null) {
-            return false;
-        }
-        return setPin(pinBytes);
-    }
-
-    /**
-     * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing.
-     *
-     * @return true confirmation has been sent out false for error
-     */
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPairingConfirmation(boolean confirm) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot set pairing confirmation");
-            return false;
-        }
-        try {
-            return service.setPairingConfirmation(this, confirm, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Cancels pairing to this device
-     *
-     * @return true if pairing cancelled successfully, false otherwise
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean cancelPairing() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot cancel pairing");
-            return false;
-        }
-        try {
-            return service.cancelBondProcess(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    boolean isBluetoothEnabled() {
-        boolean ret = false;
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null && adapter.isEnabled()) {
-            ret = true;
-        }
-        return ret;
-    }
-
-    /**
-     * Gets whether the phonebook access is allowed for this bluetooth device
-     *
-     * @return Whether the phonebook access is allowed to this device. Can be {@link
-     * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @AccessPermission int getPhonebookAccessPermission() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return ACCESS_UNKNOWN;
-        }
-        try {
-            return service.getPhonebookAccessPermission(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return ACCESS_UNKNOWN;
-    }
-
-    /**
-     * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not
-     * be routed to the {@link BluetoothDevice} if set to {@code true}.
-     *
-     * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice}
-     * is an active device (for A2DP or HFP), the active device for that profile
-     * will be set to null.
-     * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP
-     * active device is null, the {@link BluetoothDevice} will be set as the
-     * active device for that profile.
-     * If the {@link BluetoothDevice} is disconnected, it exits silence mode.
-     * If the {@link BluetoothDevice} is set as the active device for A2DP or
-     * HFP, while silence mode is enabled, then the device will exit silence mode.
-     * If the {@link BluetoothDevice} is in silence mode, AVRCP position change
-     * event and HFP AG indicators will be disabled.
-     * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
-     * enter silence mode.
-     *
-     * @param silence true to enter silence mode, false to exit
-     * @return true on success, false on error.
-     * @throws IllegalStateException if Bluetooth is not turned ON.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setSilenceMode(boolean silence) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            throw new IllegalStateException("Bluetooth is not turned ON");
-        }
-        try {
-            return service.setSilenceMode(this, silence, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "setSilenceMode fail", e);
-            return false;
-        }
-    }
-
-    /**
-     * Check whether the {@link BluetoothDevice} is in silence mode
-     *
-     * @return true on device in silence mode, otherwise false.
-     * @throws IllegalStateException if Bluetooth is not turned ON.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean isInSilenceMode() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            throw new IllegalStateException("Bluetooth is not turned ON");
-        }
-        try {
-            return service.getSilenceMode(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "isInSilenceMode fail", e);
-            return false;
-        }
-    }
-
-    /**
-     * Sets whether the phonebook access is allowed to this device.
-     *
-     * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
-     * #ACCESS_REJECTED}.
-     * @return Whether the value has been successfully set.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPhonebookAccessPermission(@AccessPermission int value) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.setPhonebookAccessPermission(this, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Gets whether message access is allowed to this bluetooth device
-     *
-     * @return Whether the message access is allowed to this device.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @AccessPermission int getMessageAccessPermission() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return ACCESS_UNKNOWN;
-        }
-        try {
-            return service.getMessageAccessPermission(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return ACCESS_UNKNOWN;
-    }
-
-    /**
-     * Sets whether the message access is allowed to this device.
-     *
-     * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded,
-     * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if
-     * the permission is not being granted.
-     * @return Whether the value has been successfully set.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setMessageAccessPermission(@AccessPermission int value) {
-        // Validates param value is one of the accepted constants
-        if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
-            throw new IllegalArgumentException(value + "is not a valid AccessPermission value");
-        }
-        final IBluetooth service = sService;
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.setMessageAccessPermission(this, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Gets whether sim access is allowed for this bluetooth device
-     *
-     * @return Whether the Sim access is allowed to this device.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @AccessPermission int getSimAccessPermission() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return ACCESS_UNKNOWN;
-        }
-        try {
-            return service.getSimAccessPermission(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return ACCESS_UNKNOWN;
-    }
-
-    /**
-     * Sets whether the Sim access is allowed to this device.
-     *
-     * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded,
-     * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if
-     * the permission is not being granted.
-     * @return Whether the value has been successfully set.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setSimAccessPermission(int value) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.setSimAccessPermission(this, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
-     * outgoing connection to this remote device on given channel.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p> Use this socket only if an authenticated socket link is possible.
-     * Authentication refers to the authentication of the link key to
-     * prevent person-in-the-middle type of attacks.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not
-     * have an input and output capability or just has the ability to
-     * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {@link createInsecureRfcommSocket}.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection.
-     * <p>Valid RFCOMM channels are in range 1 to 30.
-     *
-     * @param channel RFCOMM channel to connect to
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createRfcommSocket(int channel) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
-                null);
-    }
-
-    /**
-     * Create an L2cap {@link BluetoothSocket} ready to start a secure
-     * outgoing connection to this remote device on given channel.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p> Use this socket only if an authenticated socket link is possible.
-     * Authentication refers to the authentication of the link key to
-     * prevent person-in-the-middle type of attacks.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not
-     * have an input and output capability or just has the ability to
-     * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {@link createInsecureRfcommSocket}.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection.
-     * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
-     *
-     * @param channel L2cap PSM/channel to connect to
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createL2capSocket(int channel) throws IOException {
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
-                null);
-    }
-
-    /**
-     * Create an L2cap {@link BluetoothSocket} ready to start an insecure
-     * outgoing connection to this remote device on given channel.
-     * <p>The remote device will be not authenticated and communication on this
-     * socket will not be encrypted.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection.
-     * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
-     *
-     * @param channel L2cap PSM/channel to connect to
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException {
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel,
-                null);
-    }
-
-    /**
-     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
-     * outgoing connection to this remote device using SDP lookup of uuid.
-     * <p>This is designed to be used with {@link
-     * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
-     * Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection. This will also perform an SDP lookup of the given uuid to
-     * determine which channel to connect to.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p> Use this socket only if an authenticated socket link is possible.
-     * Authentication refers to the authentication of the link key to
-     * prevent person-in-the-middle type of attacks.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not
-     * have an input and output capability or just has the ability to
-     * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Hint: If you are connecting to a Bluetooth serial board then try
-     * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
-     * However if you are connecting to an Android peer then please generate
-     * your own unique UUID.
-     *
-     * @param uuid service record uuid to lookup RFCOMM channel
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
-                new ParcelUuid(uuid));
-    }
-
-    /**
-     * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure
-     * outgoing connection to this remote device using SDP lookup of uuid.
-     * <p> The communication channel will not have an authenticated link key
-     * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1
-     * devices, the link key will be encrypted, as encryption is mandatory.
-     * For legacy devices (pre Bluetooth 2.1 devices) the link key will
-     * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an
-     * encrypted and authenticated communication channel is desired.
-     * <p>This is designed to be used with {@link
-     * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer
-     * Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection. This will also perform an SDP lookup of the given uuid to
-     * determine which channel to connect to.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p>Hint: If you are connecting to a Bluetooth serial board then try
-     * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
-     * However if you are connecting to an Android peer then please generate
-     * your own unique UUID.
-     *
-     * @param uuid service record uuid to lookup RFCOMM channel
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
-                new ParcelUuid(uuid));
-    }
-
-    /**
-     * Construct an insecure RFCOMM socket ready to start an outgoing
-     * connection.
-     * Call #connect on the returned #BluetoothSocket to begin the connection.
-     * The remote device will not be authenticated and communication on this
-     * socket will not be encrypted.
-     *
-     * @param port remote port
-     * @return An RFCOMM BluetoothSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @UnsupportedAppUsage(publicAlternatives = "Use "
-            + "{@link #createInsecureRfcommSocketToServiceRecord} instead.")
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
-                null);
-    }
-
-    /**
-     * Construct a SCO socket ready to start an outgoing connection.
-     * Call #connect on the returned #BluetoothSocket to begin the connection.
-     *
-     * @return a SCO BluetoothSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createScoSocket() throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
-    }
-
-    /**
-     * Check that a pin is valid and convert to byte array.
-     *
-     * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters.
-     *
-     * @param pin pin as java String
-     * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static byte[] convertPinToBytes(String pin) {
-        if (pin == null) {
-            return null;
-        }
-        byte[] pinBytes;
-        try {
-            pinBytes = pin.getBytes("UTF-8");
-        } catch (UnsupportedEncodingException uee) {
-            Log.e(TAG, "UTF-8 not supported?!?");  // this should not happen
-            return null;
-        }
-        if (pinBytes.length <= 0 || pinBytes.length > 16) {
-            return null;
-        }
-        return pinBytes;
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @throws IllegalArgumentException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback) {
-        return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO));
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @throws IllegalArgumentException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport) {
-        return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK));
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
-     * is set to true.
-     * @throws NullPointerException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport, int phy) {
-        return connectGatt(context, autoConnect, callback, transport, phy, null);
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
-     * is set to true.
-     * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
-     * an un-specified background thread.
-     * @throws NullPointerException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport, int phy,
-            Handler handler) {
-        return connectGatt(context, autoConnect, callback, transport, false, phy, handler);
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client
-     * does not hold a GATT connection. It automatically disconnects when no other GATT connections
-     * are active for the remote device.
-     * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
-     * is set to true.
-     * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
-     * an un-specified background thread.
-     * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client
-     * operations.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport,
-            boolean opportunistic, int phy, Handler handler) {
-        if (callback == null) {
-            throw new NullPointerException("callback is null");
-        }
-
-        // TODO(Bluetooth) check whether platform support BLE
-        //     Do the check here or in GattServer?
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager managerService = adapter.getBluetoothManager();
-        try {
-            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported
-                return null;
-            }
-            BluetoothGatt gatt = new BluetoothGatt(
-                    iGatt, this, transport, opportunistic, phy, mAttributionSource);
-            gatt.connect(autoConnect, callback, handler);
-            return gatt;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
-     * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
-     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
-     * peer-peer Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
-     * <p>Application using this API is responsible for obtaining PSM value from remote device.
-     * <p>The remote device will be authenticated and communication on this socket will be
-     * encrypted.
-     * <p> Use this socket if an authenticated socket link is possible. Authentication refers
-     * to the authentication of the link key to prevent person-in-the-middle type of attacks.
-     *
-     * @param psm dynamic PSM value from remote device
-     * @return a CoC #BluetoothSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
-            throw new IOException();
-        }
-        if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
-                null);
-    }
-
-    /**
-     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
-     * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
-     * <p>This is designed to be used with {@link
-     * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
-     * <p>Application using this API is responsible for obtaining PSM value from remote device.
-     * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
-     * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
-     * authenticated communication channel is possible.
-     *
-     * @param psm dynamic PSM value from remote device
-     * @return a CoC #BluetoothSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
-            throw new IOException();
-        }
-        if (DBG) {
-            Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
-                null);
-    }
-
-    /**
-     * Set a keyed metadata of this {@link BluetoothDevice} to a
-     * {@link String} value.
-     * Only bonded devices's metadata will be persisted across Bluetooth
-     * restart.
-     * Metadata will be removed when the device's bond state is moved to
-     * {@link #BOND_NONE}.
-     *
-     * @param key must be within the list of BluetoothDevice.METADATA_*
-     * @param value a byte array data to set for key. Must be less than
-     * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
-     * @return true on success, false on error
-     * @hide
-    */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
-            return false;
-        }
-        if (value.length > METADATA_MAX_LENGTH) {
-            throw new IllegalArgumentException("value length is " + value.length
-                    + ", should not over " + METADATA_MAX_LENGTH);
-        }
-        try {
-            return service.setMetadata(this, key, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "setMetadata fail", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
-     *
-     * @param key must be within the list of BluetoothDevice.METADATA_*
-     * @return Metadata of the key as byte array, null on error or not found
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public byte[] getMetadata(@MetadataKey int key) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
-            return null;
-        }
-        try {
-            return service.getMetadata(this, key, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "getMetadata fail", e);
-            return null;
-        }
-    }
-
-    /**
-     * Get the maxinum metadata key ID.
-     *
-     * @return the last supported metadata key
-     * @hide
-     */
-    public static @MetadataKey int getMaxMetadataKey() {
-        return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
deleted file mode 100644
index 26e4657..0000000
--- a/core/java/android/bluetooth/BluetoothDevicePicker.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-
-/**
- * A helper to show a system "Device Picker" activity to the user.
- *
- * @hide
- */
-public interface BluetoothDevicePicker {
-    public static final String EXTRA_NEED_AUTH =
-            "android.bluetooth.devicepicker.extra.NEED_AUTH";
-    public static final String EXTRA_FILTER_TYPE =
-            "android.bluetooth.devicepicker.extra.FILTER_TYPE";
-    public static final String EXTRA_LAUNCH_PACKAGE =
-            "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE";
-    public static final String EXTRA_LAUNCH_CLASS =
-            "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS";
-
-    /**
-     * Broadcast when one BT device is selected from BT device picker screen.
-     * Selected {@link BluetoothDevice} is returned in extra data named
-     * {@link BluetoothDevice#EXTRA_DEVICE}.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DEVICE_SELECTED =
-            "android.bluetooth.devicepicker.action.DEVICE_SELECTED";
-
-    /**
-     * Broadcast when someone want to select one BT device from devices list.
-     * This intent contains below extra data:
-     * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication
-     * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be
-     * listed
-     * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this
-     * intent come from
-     * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent
-     * come from
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LAUNCH =
-            "android.bluetooth.devicepicker.action.LAUNCH";
-
-    /** Ask device picker to show all kinds of BT devices */
-    public static final int FILTER_TYPE_ALL = 0;
-    /** Ask device picker to show BT devices that support AUDIO profiles */
-    public static final int FILTER_TYPE_AUDIO = 1;
-    /** Ask device picker to show BT devices that support Object Transfer */
-    public static final int FILTER_TYPE_TRANSFER = 2;
-    /**
-     * Ask device picker to show BT devices that support
-     * Personal Area Networking User (PANU) profile
-     */
-    public static final int FILTER_TYPE_PANU = 3;
-    /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */
-    public static final int FILTER_TYPE_NAP = 4;
-}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
deleted file mode 100644
index fe8d1ba..0000000
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ /dev/null
@@ -1,1848 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.os.Build;
-import android.os.Handler;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Public API for the Bluetooth GATT Profile.
- *
- * <p>This class provides Bluetooth GATT functionality to enable communication
- * with Bluetooth Smart or Smart Ready devices.
- *
- * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
- * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
- * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
- * scan process.
- */
-public final class BluetoothGatt implements BluetoothProfile {
-    private static final String TAG = "BluetoothGatt";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    @UnsupportedAppUsage
-    private IBluetoothGatt mService;
-    @UnsupportedAppUsage
-    private volatile BluetoothGattCallback mCallback;
-    private Handler mHandler;
-    @UnsupportedAppUsage
-    private int mClientIf;
-    private BluetoothDevice mDevice;
-    @UnsupportedAppUsage
-    private boolean mAutoConnect;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private int mAuthRetryState;
-    private int mConnState;
-    private final Object mStateLock = new Object();
-    private final Object mDeviceBusyLock = new Object();
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private Boolean mDeviceBusy = false;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private int mTransport;
-    private int mPhy;
-    private boolean mOpportunistic;
-    private final AttributionSource mAttributionSource;
-
-    private static final int AUTH_RETRY_STATE_IDLE = 0;
-    private static final int AUTH_RETRY_STATE_NO_MITM = 1;
-    private static final int AUTH_RETRY_STATE_MITM = 2;
-
-    private static final int CONN_STATE_IDLE = 0;
-    private static final int CONN_STATE_CONNECTING = 1;
-    private static final int CONN_STATE_CONNECTED = 2;
-    private static final int CONN_STATE_DISCONNECTING = 3;
-    private static final int CONN_STATE_CLOSED = 4;
-
-    private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
-    private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds
-
-    private List<BluetoothGattService> mServices;
-
-    /** A GATT operation completed successfully */
-    public static final int GATT_SUCCESS = 0;
-
-    /** GATT read operation is not permitted */
-    public static final int GATT_READ_NOT_PERMITTED = 0x2;
-
-    /** GATT write operation is not permitted */
-    public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
-
-    /** Insufficient authentication for a given operation */
-    public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
-
-    /** The given request is not supported */
-    public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
-
-    /** Insufficient encryption for a given operation */
-    public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
-
-    /** A read or write operation was requested with an invalid offset */
-    public static final int GATT_INVALID_OFFSET = 0x7;
-
-    /** Insufficient authorization for a given operation */
-    public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8;
-
-    /** A write operation exceeds the maximum length of the attribute */
-    public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
-
-    /** A remote device connection is congested. */
-    public static final int GATT_CONNECTION_CONGESTED = 0x8f;
-
-    /** A GATT operation failed, errors other than the above */
-    public static final int GATT_FAILURE = 0x101;
-
-    /**
-     * Connection parameter update - Use the connection parameters recommended by the
-     * Bluetooth SIG. This is the default value if no connection parameter update
-     * is requested.
-     */
-    public static final int CONNECTION_PRIORITY_BALANCED = 0;
-
-    /**
-     * Connection parameter update - Request a high priority, low latency connection.
-     * An application should only request high priority connection parameters to transfer large
-     * amounts of data over LE quickly. Once the transfer is complete, the application should
-     * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce
-     * energy use.
-     */
-    public static final int CONNECTION_PRIORITY_HIGH = 1;
-
-    /** Connection parameter update - Request low power, reduced data rate connection parameters. */
-    public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
-
-    /**
-     * No authentication required.
-     *
-     * @hide
-     */
-    /*package*/ static final int AUTHENTICATION_NONE = 0;
-
-    /**
-     * Authentication requested; no person-in-the-middle protection required.
-     *
-     * @hide
-     */
-    /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
-
-    /**
-     * Authentication with person-in-the-middle protection requested.
-     *
-     * @hide
-     */
-    /*package*/ static final int AUTHENTICATION_MITM = 2;
-
-    /**
-     * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
-     */
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothGattCallback mBluetoothGattCallback =
-            new IBluetoothGattCallback.Stub() {
-                /**
-                 * Application interface registered - app is ready to go
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onClientRegistered(int status, int clientIf) {
-                    if (DBG) {
-                        Log.d(TAG, "onClientRegistered() - status=" + status
-                                + " clientIf=" + clientIf);
-                    }
-                    if (VDBG) {
-                        synchronized (mStateLock) {
-                            if (mConnState != CONN_STATE_CONNECTING) {
-                                Log.e(TAG, "Bad connection state: " + mConnState);
-                            }
-                        }
-                    }
-                    mClientIf = clientIf;
-                    if (status != GATT_SUCCESS) {
-                        runOrQueueCallback(new Runnable() {
-                            @Override
-                            public void run() {
-                                final BluetoothGattCallback callback = mCallback;
-                                if (callback != null) {
-                                    callback.onConnectionStateChange(BluetoothGatt.this,
-                                            GATT_FAILURE,
-                                            BluetoothProfile.STATE_DISCONNECTED);
-                                }
-                            }
-                        });
-
-                        synchronized (mStateLock) {
-                            mConnState = CONN_STATE_IDLE;
-                        }
-                        return;
-                    }
-                    try {
-                        mService.clientConnect(mClientIf, mDevice.getAddress(),
-                                !mAutoConnect, mTransport, mOpportunistic,
-                                mPhy, mAttributionSource); // autoConnect is inverse of "isDirect"
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "", e);
-                    }
-                }
-
-                /**
-                 * Phy update callback
-                 * @hide
-                 */
-                @Override
-                public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onPhyUpdate() - status=" + status
-                                + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Phy read callback
-                 * @hide
-                 */
-                @Override
-                public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onPhyRead() - status=" + status
-                                + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Client connection state changed
-                 * @hide
-                 */
-                @Override
-                public void onClientConnectionState(int status, int clientIf,
-                        boolean connected, String address) {
-                    if (DBG) {
-                        Log.d(TAG, "onClientConnectionState() - status=" + status
-                                + " clientIf=" + clientIf + " device=" + address);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-                    int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
-                            BluetoothProfile.STATE_DISCONNECTED;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onConnectionStateChange(BluetoothGatt.this, status,
-                                        profileState);
-                            }
-                        }
-                    });
-
-                    synchronized (mStateLock) {
-                        if (connected) {
-                            mConnState = CONN_STATE_CONNECTED;
-                        } else {
-                            mConnState = CONN_STATE_IDLE;
-                        }
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-                }
-
-                /**
-                 * Remote search has been completed.
-                 * The internal object structure should now reflect the state
-                 * of the remote device database. Let the application know that
-                 * we are done at this point.
-                 * @hide
-                 */
-                @Override
-                public void onSearchComplete(String address, List<BluetoothGattService> services,
-                        int status) {
-                    if (DBG) {
-                        Log.d(TAG,
-                                "onSearchComplete() = Device=" + address + " Status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    for (BluetoothGattService s : services) {
-                        //services we receive don't have device set properly.
-                        s.setDevice(mDevice);
-                    }
-
-                    mServices.addAll(services);
-
-                    // Fix references to included services, as they doesn't point to right objects.
-                    for (BluetoothGattService fixedService : mServices) {
-                        ArrayList<BluetoothGattService> includedServices =
-                                new ArrayList(fixedService.getIncludedServices());
-                        fixedService.getIncludedServices().clear();
-
-                        for (BluetoothGattService brokenRef : includedServices) {
-                            BluetoothGattService includedService = getService(mDevice,
-                                    brokenRef.getUuid(), brokenRef.getInstanceId());
-                            if (includedService != null) {
-                                fixedService.addIncludedService(includedService);
-                            } else {
-                                Log.e(TAG, "Broken GATT database: can't find included service.");
-                            }
-                        }
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onServicesDiscovered(BluetoothGatt.this, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Remote characteristic has been read.
-                 * Updates the internal value.
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onCharacteristicRead(String address, int status, int handle,
-                        byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG, "onCharacteristicRead() - Device=" + address
-                                + " handle=" + handle + " Status=" + status);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.readCharacteristic(
-                                    mClientIf, address, handle, authReq, mAttributionSource);
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
-                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
-                            handle);
-                    if (characteristic == null) {
-                        Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                if (status == 0) characteristic.setValue(value);
-                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
-                                        value, status);
-                                // Keep calling deprecated callback to maintain app compatibility
-                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
-                                        status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Characteristic has been written to the remote device.
-                 * Let the app know how we did...
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onCharacteristicWrite(String address, int status, int handle,
-                        byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG, "onCharacteristicWrite() - Device=" + address
-                                + " handle=" + handle + " Status=" + status);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
-                            handle);
-                    if (characteristic == null) return;
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
-                            for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
-                                requestStatus =  mService.writeCharacteristic(mClientIf, address,
-                                                  handle, characteristic.getWriteType(), authReq,
-                                                  value, mAttributionSource);
-                                if (requestStatus
-                                        != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
-                                    break;
-                                }
-                                try {
-                                    Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
-                                } catch (InterruptedException e) {
-                                }
-                            }
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
-                                        status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Remote characteristic has been updated.
-                 * Updates the internal value.
-                 * @hide
-                 */
-                @Override
-                public void onNotify(String address, int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
-                            handle);
-                    if (characteristic == null) return;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                characteristic.setValue(value);
-                                callback.onCharacteristicChanged(BluetoothGatt.this,
-                                        characteristic, value);
-                                // Keep calling deprecated callback to maintain app compatibility
-                                callback.onCharacteristicChanged(BluetoothGatt.this,
-                                        characteristic);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Descriptor has been read.
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onDescriptorRead(String address, int status, int handle, byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG,
-                                "onDescriptorRead() - Device=" + address + " handle=" + handle);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
-                    if (descriptor == null) return;
-
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.readDescriptor(
-                                    mClientIf, address, handle, authReq, mAttributionSource);
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                if (status == 0) descriptor.setValue(value);
-                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status,
-                                        value);
-                                // Keep calling deprecated callback to maintain app compatibility
-                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Descriptor write operation complete.
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onDescriptorWrite(String address, int status, int handle,
-                        byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG,
-                                "onDescriptorWrite() - Device=" + address + " handle=" + handle);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
-                    if (descriptor == null) return;
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.writeDescriptor(mClientIf, address, handle,
-                                    authReq, value, mAttributionSource);
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Prepared write transaction completed (or aborted)
-                 * @hide
-                 */
-                @Override
-                public void onExecuteWrite(String address, int status) {
-                    if (VDBG) {
-                        Log.d(TAG, "onExecuteWrite() - Device=" + address
-                                + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onReliableWriteCompleted(BluetoothGatt.this, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Remote device RSSI has been read
-                 * @hide
-                 */
-                @Override
-                public void onReadRemoteRssi(String address, int rssi, int status) {
-                    if (VDBG) {
-                        Log.d(TAG, "onReadRemoteRssi() - Device=" + address
-                                + " rssi=" + rssi + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Callback invoked when the MTU for a given connection changes
-                 * @hide
-                 */
-                @Override
-                public void onConfigureMTU(String address, int mtu, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onConfigureMTU() - Device=" + address
-                                + " mtu=" + mtu + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onMtuChanged(BluetoothGatt.this, mtu, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Callback invoked when the given connection is updated
-                 * @hide
-                 */
-                @Override
-                public void onConnectionUpdated(String address, int interval, int latency,
-                        int timeout, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onConnectionUpdated() - Device=" + address
-                                + " interval=" + interval + " latency=" + latency
-                                + " timeout=" + timeout + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
-                                        timeout, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Callback invoked when service changed event is received
-                 * @hide
-                 */
-                @Override
-                public void onServiceChanged(String address) {
-                    if (DBG) {
-                        Log.d(TAG, "onServiceChanged() - Device=" + address);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onServiceChanged(BluetoothGatt.this);
-                            }
-                        }
-                    });
-                }
-            };
-
-    /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport,
-            boolean opportunistic, int phy, AttributionSource attributionSource) {
-        mService = iGatt;
-        mDevice = device;
-        mTransport = transport;
-        mPhy = phy;
-        mOpportunistic = opportunistic;
-        mAttributionSource = attributionSource;
-        mServices = new ArrayList<BluetoothGattService>();
-
-        mConnState = CONN_STATE_IDLE;
-        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-    }
-
-    /**
-     * Close this Bluetooth GATT client.
-     *
-     * Application should call this method as early as possible after it is done with
-     * this GATT client.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void close() {
-        if (DBG) Log.d(TAG, "close()");
-
-        unregisterApp();
-        mConnState = CONN_STATE_CLOSED;
-        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-    }
-
-    /**
-     * Returns a service by UUID, instance and type.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
-            int instanceId) {
-        for (BluetoothGattService svc : mServices) {
-            if (svc.getDevice().equals(device)
-                    && svc.getInstanceId() == instanceId
-                    && svc.getUuid().equals(uuid)) {
-                return svc;
-            }
-        }
-        return null;
-    }
-
-
-    /**
-     * Returns a characteristic with id equal to instanceId.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
-            int instanceId) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                if (charac.getInstanceId() == instanceId) {
-                    return charac;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns a descriptor with id equal to instanceId.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
-                    if (desc.getInstanceId() == instanceId) {
-                        return desc;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
-     * immediately if no Handler was provided.
-     */
-    private void runOrQueueCallback(final Runnable cb) {
-        if (mHandler == null) {
-            try {
-                cb.run();
-            } catch (Exception ex) {
-                Log.w(TAG, "Unhandled exception in callback", ex);
-            }
-        } else {
-            mHandler.post(cb);
-        }
-    }
-
-    /**
-     * Register an application callback to start using GATT.
-     *
-     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
-     * is used to notify success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @return If true, the callback will be called to notify success or failure, false on immediate
-     * error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
-        return registerApp(callback, handler, false);
-    }
-
-    /**
-     * Register an application callback to start using GATT.
-     *
-     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
-     * is used to notify success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param eatt_support indicate to allow for eatt support
-     * @return If true, the callback will be called to notify success or failure, false on immediate
-     * error
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean registerApp(BluetoothGattCallback callback, Handler handler,
-                                boolean eatt_support) {
-        if (DBG) Log.d(TAG, "registerApp()");
-        if (mService == null) return false;
-
-        mCallback = callback;
-        mHandler = handler;
-        UUID uuid = UUID.randomUUID();
-        if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
-
-        try {
-            mService.registerClient(
-                    new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Unregister the current application and callbacks.
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void unregisterApp() {
-        if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mCallback = null;
-            mService.unregisterClient(mClientIf, mAttributionSource);
-            mClientIf = 0;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Initiate a connection to a Bluetooth GATT capable device.
-     *
-     * <p>The connection may not be established right away, but will be
-     * completed when the remote device is available. A
-     * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
-     * invoked when the connection state changes as a result of this function.
-     *
-     * <p>The autoConnect parameter determines whether to actively connect to
-     * the remote device, or rather passively scan and finalize the connection
-     * when the remote device is in range/available. Generally, the first ever
-     * connection to a device should be direct (autoConnect set to false) and
-     * subsequent connections to known devices should be invoked with the
-     * autoConnect parameter set to true.
-     *
-     * @param device Remote device to connect to
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @return true, if the connection attempt was initiated successfully
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
-            Handler handler) {
-        if (DBG) {
-            Log.d(TAG,
-                    "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
-        }
-        synchronized (mStateLock) {
-            if (mConnState != CONN_STATE_IDLE) {
-                throw new IllegalStateException("Not idle");
-            }
-            mConnState = CONN_STATE_CONNECTING;
-        }
-
-        mAutoConnect = autoConnect;
-
-        if (!registerApp(callback, handler)) {
-            synchronized (mStateLock) {
-                mConnState = CONN_STATE_IDLE;
-            }
-            Log.e(TAG, "Failed to register callback");
-            return false;
-        }
-
-        // The connection will continue in the onClientRegistered callback
-        return true;
-    }
-
-    /**
-     * Disconnects an established connection, or cancels a connection attempt
-     * currently in progress.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void disconnect() {
-        if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Connect back to remote device.
-     *
-     * <p>This method is used to re-connect to a remote device after the
-     * connection has been dropped. If the device is not in range, the
-     * re-connection will be triggered once the device is back in range.
-     *
-     * @return true, if the connection attempt was initiated successfully
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect() {
-        try {
-            // autoConnect is inverse of "isDirect"
-            mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
-                    mOpportunistic, mPhy, mAttributionSource);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Set the preferred connection PHY for this app. Please note that this is just a
-     * recommendation, whether the PHY change will happen depends on other applications preferences,
-     * local and remote controller capabilities. Controller can override these settings.
-     * <p>
-     * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
-     * if no PHY change happens. It is also triggered when remote device updates the PHY.
-     *
-     * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
-     * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
-     * {@link BluetoothDevice#PHY_OPTION_S8}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
-        try {
-            mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
-                    phyOptions, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
-     * in {@link BluetoothGattCallback#onPhyRead}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void readPhy() {
-        try {
-            mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Return the remote bluetooth device this GATT client targets to
-     *
-     * @return remote bluetooth device
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Discovers services offered by a remote device as well as their
-     * characteristics and descriptors.
-     *
-     * <p>This is an asynchronous operation. Once service discovery is completed,
-     * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
-     * triggered. If the discovery was successful, the remote services can be
-     * retrieved using the {@link #getServices} function.
-     *
-     * @return true, if the remote service discovery has been started
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean discoverServices() {
-        if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        mServices.clear();
-
-        try {
-            mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Discovers a service by UUID. This is exposed only for passing PTS tests.
-     * It should never be used by real applications. The service is not searched
-     * for characteristics and descriptors, or returned in any callback.
-     *
-     * @return true, if the remote service discovery has been started
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean discoverServiceByUuid(UUID uuid) {
-        if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        mServices.clear();
-
-        try {
-            mService.discoverServiceByUuid(
-                    mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns a list of GATT services offered by the remote device.
-     *
-     * <p>This function requires that service discovery has been completed
-     * for the given device.
-     *
-     * @return List of services on the remote device. Returns an empty list if service discovery has
-     * not yet been performed.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public List<BluetoothGattService> getServices() {
-        List<BluetoothGattService> result =
-                new ArrayList<BluetoothGattService>();
-
-        for (BluetoothGattService service : mServices) {
-            if (service.getDevice().equals(mDevice)) {
-                result.add(service);
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Returns a {@link BluetoothGattService}, if the requested UUID is
-     * supported by the remote device.
-     *
-     * <p>This function requires that service discovery has been completed
-     * for the given device.
-     *
-     * <p>If multiple instances of the same service (as identified by UUID)
-     * exist, the first instance of the service is returned.
-     *
-     * @param uuid UUID of the requested service
-     * @return BluetoothGattService if supported, or null if the requested service is not offered by
-     * the remote device.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public BluetoothGattService getService(UUID uuid) {
-        for (BluetoothGattService service : mServices) {
-            if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
-                return service;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Reads the requested characteristic from the associated remote device.
-     *
-     * <p>This is an asynchronous operation. The result of the read operation
-     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[], int)} callback.
-     *
-     * @param characteristic Characteristic to read from the remote device
-     * @return true, if the read operation was initiated successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
-        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
-            return false;
-        }
-
-        if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
-        if (mService == null || mClientIf == 0) return false;
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.readCharacteristic(mClientIf, device.getAddress(),
-                    characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Reads the characteristic using its UUID from the associated remote device.
-     *
-     * <p>This is an asynchronous operation. The result of the read operation
-     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[], int)} callback.
-     *
-     * @param uuid UUID of characteristic to read from the remote device
-     * @return true, if the read operation was initiated successfully
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
-        if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
-        if (mService == null || mClientIf == 0) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
-                    new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE,
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-
-    /**
-     * Writes a given characteristic and its values to the associated remote device.
-     *
-     * <p>Once the write operation has been completed, the
-     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
-     * reporting the result of the operation.
-     *
-     * @param characteristic Characteristic to write on the remote device
-     * @return true, if the write operation was initiated successfully
-     * @throws IllegalArgumentException if characteristic or its value are null
-     *
-     * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
-     * int)} as this is not memory safe.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
-        try {
-            return writeCharacteristic(characteristic, characteristic.getValue(),
-                    characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
-            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
-            BluetoothStatusCodes.ERROR_UNKNOWN
-    })
-    public @interface WriteOperationReturnValues{}
-
-    /**
-     * Writes a given characteristic and its values to the associated remote device.
-     *
-     * <p>Once the write operation has been completed, the
-     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
-     * reporting the result of the operation.
-     *
-     * @param characteristic Characteristic to write on the remote device
-     * @return whether the characteristic was successfully written to
-     * @throws IllegalArgumentException if characteristic or value are null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @WriteOperationReturnValues
-    public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic,
-            @NonNull byte[] value, int writeType) {
-        if (characteristic == null) {
-            throw new IllegalArgumentException("characteristic must not be null");
-        }
-        if (value == null) {
-            throw new IllegalArgumentException("value must not be null");
-        }
-        if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
-        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
-                && (characteristic.getProperties()
-                & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
-            return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
-        }
-        if (mService == null || mClientIf == 0) {
-            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
-        }
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) {
-            throw new IllegalArgumentException("Characteristic must have a non-null service");
-        }
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) {
-            throw new IllegalArgumentException("Service must have a non-null device");
-        }
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) {
-                return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
-            }
-            mDeviceBusy = true;
-        }
-
-        int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
-        try {
-            for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
-                requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
-                        characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value,
-                        mAttributionSource);
-                if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
-                    break;
-                }
-                try {
-                    Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
-                } catch (InterruptedException e) {
-                }
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            throw e.rethrowFromSystemServer();
-        }
-
-        return requestStatus;
-    }
-
-    /**
-     * Reads the value for a given descriptor from the associated remote device.
-     *
-     * <p>Once the read operation has been completed, the
-     * {@link BluetoothGattCallback#onDescriptorRead} callback is
-     * triggered, signaling the result of the operation.
-     *
-     * @param descriptor Descriptor value to read from the remote device
-     * @return true, if the read operation was initiated successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
-        if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
-        if (mService == null || mClientIf == 0) return false;
-
-        BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
-        if (characteristic == null) return false;
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.readDescriptor(mClientIf, device.getAddress(),
-                    descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Write the value of a given descriptor to the associated remote device.
-     *
-     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
-     * result of the write operation.
-     *
-     * @param descriptor Descriptor to write to the associated remote device
-     * @return true, if the write operation was initiated successfully
-     * @throws IllegalArgumentException if descriptor or its value are null
-     *
-     * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
-     * this is not memory safe.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
-        try {
-            return writeDescriptor(descriptor, descriptor.getValue())
-                    == BluetoothStatusCodes.SUCCESS;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /**
-     * Write the value of a given descriptor to the associated remote device.
-     *
-     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
-     * result of the write operation.
-     *
-     * @param descriptor Descriptor to write to the associated remote device
-     * @return true, if the write operation was initiated successfully
-     * @throws IllegalArgumentException if descriptor or value are null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @WriteOperationReturnValues
-    public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor,
-            @NonNull byte[] value) {
-        if (descriptor == null) {
-            throw new IllegalArgumentException("descriptor must not be null");
-        }
-        if (value == null) {
-            throw new IllegalArgumentException("value must not be null");
-        }
-        if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
-        if (mService == null || mClientIf == 0) {
-            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
-        }
-
-        BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
-        if (characteristic == null) {
-            throw new IllegalArgumentException("Descriptor must have a non-null characteristic");
-        }
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) {
-            throw new IllegalArgumentException("Characteristic must have a non-null service");
-        }
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) {
-            throw new IllegalArgumentException("Service must have a non-null device");
-        }
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
-            mDeviceBusy = true;
-        }
-
-        try {
-            return mService.writeDescriptor(mClientIf, device.getAddress(),
-                    descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            e.rethrowFromSystemServer();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Initiates a reliable write transaction for a given remote device.
-     *
-     * <p>Once a reliable write transaction has been initiated, all calls
-     * to {@link #writeCharacteristic} are sent to the remote device for
-     * verification and queued up for atomic execution. The application will
-     * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every
-     * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is
-     * responsible for verifying if the value has been transmitted accurately.
-     *
-     * <p>After all characteristics have been queued up and verified,
-     * {@link #executeReliableWrite} will execute all writes. If a characteristic
-     * was not written correctly, calling {@link #abortReliableWrite} will
-     * cancel the current transaction without committing any values on the
-     * remote device.
-     *
-     * @return true, if the reliable write transaction has been initiated
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean beginReliableWrite() {
-        if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Executes a reliable write transaction for a given remote device.
-     *
-     * <p>This function will commit all queued up characteristic write
-     * operations for a given remote device.
-     *
-     * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
-     * invoked to indicate whether the transaction has been executed correctly.
-     *
-     * @return true, if the request to execute the transaction has been sent
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean executeReliableWrite() {
-        if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Cancels a reliable write transaction for a given device.
-     *
-     * <p>Calling this function will discard all queued characteristic write
-     * operations for a given remote device.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void abortReliableWrite() {
-        if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * @deprecated Use {@link #abortReliableWrite()}
-     */
-    @Deprecated
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void abortReliableWrite(BluetoothDevice mDevice) {
-        abortReliableWrite();
-    }
-
-    /**
-     * Enable or disable notifications/indications for a given characteristic.
-     *
-     * <p>Once notifications are enabled for a characteristic, a
-     * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device
-     * indicates that the given characteristic has changed.
-     *
-     * @param characteristic The characteristic for which to enable notifications
-     * @param enable Set to true to enable notifications/indications
-     * @return true, if the requested notification status was set successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
-            boolean enable) {
-        if (DBG) {
-            Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
-                    + " enable: " + enable);
-        }
-        if (mService == null || mClientIf == 0) return false;
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
-
-        try {
-            mService.registerForNotification(mClientIf, device.getAddress(),
-                    characteristic.getInstanceId(), enable, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Clears the internal cache and forces a refresh of the services from the
-     * remote device.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean refresh() {
-        if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Read the RSSI for a connected remote device.
-     *
-     * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
-     * invoked when the RSSI value has been read.
-     *
-     * @return true, if the RSSI value has been requested successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readRemoteRssi() {
-        if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Request an MTU size used for a given connection.
-     *
-     * <p>When performing a write request operation (write without response),
-     * the data sent is truncated to the MTU size. This function may be used
-     * to request a larger MTU size to be able to send more data at once.
-     *
-     * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
-     * whether this operation was successful.
-     *
-     * @return true, if the new MTU value has been requested successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean requestMtu(int mtu) {
-        if (DBG) {
-            Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
-                    + " mtu: " + mtu);
-        }
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Request a connection parameter update.
-     *
-     * <p>This function will send a connection parameter update request to the
-     * remote device.
-     *
-     * @param connectionPriority Request a specific connection priority. Must be one of {@link
-     * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
-     * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
-     * @throws IllegalArgumentException If the parameters are outside of their specified range.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean requestConnectionPriority(int connectionPriority) {
-        if (connectionPriority < CONNECTION_PRIORITY_BALANCED
-                || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
-            throw new IllegalArgumentException("connectionPriority not within valid range");
-        }
-
-        if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.connectionParameterUpdate(
-                    mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Request an LE connection parameter update.
-     *
-     * <p>This function will send an LE connection parameters update request to the remote device.
-     *
-     * @return true, if the request is send to the Bluetooth stack.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
-                                             int slaveLatency, int supervisionTimeout,
-                                             int minConnectionEventLen, int maxConnectionEventLen) {
-        if (DBG) {
-            Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
-                        + ")" + (1.25 * minConnectionInterval)
-                        + "msec, max=(" + maxConnectionInterval + ")"
-                        + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
-                        + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
-                        + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
-        }
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
-                    minConnectionInterval, maxConnectionInterval,
-                    slaveLatency, supervisionTimeout,
-                    minConnectionEventLen, maxConnectionEventLen,
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    @Deprecated
-    public int getConnectionState(BluetoothDevice device) {
-        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
-    }
-
-    /**
-     * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    @Deprecated
-    public List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getConnectedDevices instead.");
-    }
-
-    /**
-     * @deprecated Not supported - please use
-     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
-     * with {@link BluetoothProfile#GATT} as first argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    @Deprecated
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
deleted file mode 100644
index d0a5a1e..0000000
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ /dev/null
@@ -1,267 +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 android.bluetooth;
-
-import android.annotation.NonNull;
-
-/**
- * This abstract class is used to implement {@link BluetoothGatt} callbacks.
- */
-public abstract class BluetoothGattCallback {
-
-    /**
-     * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of
-     * remote device changing the PHY.
-     *
-     * @param gatt GATT client
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback triggered as result of {@link BluetoothGatt#readPhy}
-     *
-     * @param gatt GATT client
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback indicating when GATT client has connected/disconnected to/from a remote
-     * GATT server.
-     *
-     * @param gatt GATT client
-     * @param status Status of the connect or disconnect operation. {@link
-     * BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
-     * @param newState Returns the new connection state. Can be one of {@link
-     * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED}
-     */
-    public void onConnectionStateChange(BluetoothGatt gatt, int status,
-            int newState) {
-    }
-
-    /**
-     * Callback invoked when the list of remote services, characteristics and descriptors
-     * for the remote device have been updated, ie new services have been discovered.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device has been explored
-     * successfully.
-     */
-    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
-    }
-
-    /**
-     * Callback reporting the result of a characteristic read operation.
-     *
-     * @param gatt           GATT client invoked
-     *                       {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
-     * @param characteristic Characteristic that was read from the associated remote device.
-     * @param status         {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                       successfully.
-     * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[], int)} as it is memory safe
-     */
-    @Deprecated
-    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
-            int status) {
-    }
-
-    /**
-     * Callback reporting the result of a characteristic read operation.
-     *
-     * @param gatt           GATT client invoked
-     *                       {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
-     * @param characteristic Characteristic that was read from the associated remote device.
-     * @param value          the value of the characteristic
-     * @param status         {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                       successfully.
-     */
-    public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull
-            BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
-    }
-
-    /**
-     * Callback indicating the result of a characteristic write operation.
-     *
-     * <p>If this callback is invoked while a reliable write transaction is
-     * in progress, the value of the characteristic represents the value
-     * reported by the remote device. An application should compare this
-     * value to the desired value to be written. If the values don't match,
-     * the application must abort the reliable write transaction.
-     *
-     * @param gatt           GATT client that invoked
-     *                       {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic,
-     *                       byte[], int)}
-     * @param characteristic Characteristic that was written to the associated remote device.
-     * @param status         The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if
-     *                       the
-     *                       operation succeeds.
-     */
-    public void onCharacteristicWrite(BluetoothGatt gatt,
-            BluetoothGattCharacteristic characteristic, int status) {
-    }
-
-    /**
-     * Callback triggered as a result of a remote characteristic notification.
-     *
-     * @param gatt           GATT client the characteristic is associated with
-     * @param characteristic Characteristic that has been updated as a result of a remote
-     *                       notification event.
-     * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic
-     * value at the time of notification.
-     */
-    @Deprecated
-    public void onCharacteristicChanged(BluetoothGatt gatt,
-            BluetoothGattCharacteristic characteristic) {
-    }
-
-    /**
-     * Callback triggered as a result of a remote characteristic notification. Note that the value
-     * within the characteristic object may have changed since receiving the remote characteristic
-     * notification, so check the parameter value for the value at the time of notification.
-     *
-     * @param gatt           GATT client the characteristic is associated with
-     * @param characteristic Characteristic that has been updated as a result of a remote
-     *                       notification event.
-     * @param value          notified characteristic value
-     */
-    public void onCharacteristicChanged(@NonNull BluetoothGatt gatt,
-            @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
-    }
-
-    /**
-     * Callback reporting the result of a descriptor read operation.
-     *
-     * @param gatt       GATT client invoked {@link BluetoothGatt#readDescriptor}
-     * @param descriptor Descriptor that was read from the associated remote device.
-     * @param status     {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                   successfully
-     * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt,
-     * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor
-     * value at the time it was read.
-     */
-    @Deprecated
-    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
-            int status) {
-    }
-
-    /**
-     * Callback reporting the result of a descriptor read operation.
-     *
-     * @param gatt       GATT client invoked {@link BluetoothGatt#readDescriptor}
-     * @param descriptor Descriptor that was read from the associated remote device.
-     * @param status     {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                   successfully
-     * @param value      the descriptor value at the time of the read operation
-     */
-    public void onDescriptorRead(@NonNull BluetoothGatt gatt,
-            @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
-    }
-
-    /**
-     * Callback indicating the result of a descriptor write operation.
-     *
-     * @param gatt       GATT client invoked {@link BluetoothGatt#writeDescriptor}
-     * @param descriptor Descriptor that was writte to the associated remote device.
-     * @param status     The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
-     *                   operation succeeds.
-     */
-    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
-            int status) {
-    }
-
-    /**
-     * Callback invoked when a reliable write transaction has been completed.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write transaction was
-     * executed successfully
-     */
-    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
-    }
-
-    /**
-     * Callback reporting the RSSI for a remote device connection.
-     *
-     * This callback is triggered in response to the
-     * {@link BluetoothGatt#readRemoteRssi} function.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
-     * @param rssi The RSSI value for the remote device
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
-     */
-    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
-    }
-
-    /**
-     * Callback indicating the MTU for a given device connection has changed.
-     *
-     * This callback is triggered in response to the
-     * {@link BluetoothGatt#requestMtu} function, or in response to a connection
-     * event.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu}
-     * @param mtu The new MTU size
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully
-     */
-    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
-    }
-
-    /**
-     * Callback indicating the connection parameters were updated.
-     *
-     * @param gatt GATT client involved
-     * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
-     * 6 (7.5ms) to 3200 (4000ms).
-     * @param latency Worker latency for the connection in number of connection events. Valid range
-     * is from 0 to 499
-     * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
-     * (0.1s) to 3200 (32s)
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated
-     * successfully
-     * @hide
-     */
-    public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout,
-            int status) {
-    }
-
-    /**
-     * Callback indicating service changed event is received
-     *
-     * <p>Receiving this event means that the GATT database is out of sync with
-     * the remote device. {@link BluetoothGatt#discoverServices} should be
-     * called to re-discover the services.
-     *
-     * @param gatt GATT client involved
-     */
-    public void onServiceChanged(@NonNull BluetoothGatt gatt) {
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
deleted file mode 100644
index 053e0db..0000000
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Characteristic
- *
- * <p>A GATT characteristic is a basic data element used to construct a GATT service,
- * {@link BluetoothGattService}. The characteristic contains a value as well as
- * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}.
- */
-public class BluetoothGattCharacteristic implements Parcelable {
-
-    /**
-     * Characteristic proprty: Characteristic is broadcastable.
-     */
-    public static final int PROPERTY_BROADCAST = 0x01;
-
-    /**
-     * Characteristic property: Characteristic is readable.
-     */
-    public static final int PROPERTY_READ = 0x02;
-
-    /**
-     * Characteristic property: Characteristic can be written without response.
-     */
-    public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04;
-
-    /**
-     * Characteristic property: Characteristic can be written.
-     */
-    public static final int PROPERTY_WRITE = 0x08;
-
-    /**
-     * Characteristic property: Characteristic supports notification
-     */
-    public static final int PROPERTY_NOTIFY = 0x10;
-
-    /**
-     * Characteristic property: Characteristic supports indication
-     */
-    public static final int PROPERTY_INDICATE = 0x20;
-
-    /**
-     * Characteristic property: Characteristic supports write with signature
-     */
-    public static final int PROPERTY_SIGNED_WRITE = 0x40;
-
-    /**
-     * Characteristic property: Characteristic has extended properties
-     */
-    public static final int PROPERTY_EXTENDED_PROPS = 0x80;
-
-    /**
-     * Characteristic read permission
-     */
-    public static final int PERMISSION_READ = 0x01;
-
-    /**
-     * Characteristic permission: Allow encrypted read operations
-     */
-    public static final int PERMISSION_READ_ENCRYPTED = 0x02;
-
-    /**
-     * Characteristic permission: Allow reading with person-in-the-middle protection
-     */
-    public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
-
-    /**
-     * Characteristic write permission
-     */
-    public static final int PERMISSION_WRITE = 0x10;
-
-    /**
-     * Characteristic permission: Allow encrypted writes
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
-
-    /**
-     * Characteristic permission: Allow encrypted writes with person-in-the-middle
-     * protection
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
-
-    /**
-     * Characteristic permission: Allow signed write operations
-     */
-    public static final int PERMISSION_WRITE_SIGNED = 0x80;
-
-    /**
-     * Characteristic permission: Allow signed write operations with
-     * person-in-the-middle protection
-     */
-    public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
-
-    /**
-     * Write characteristic, requesting acknoledgement by the remote device
-     */
-    public static final int WRITE_TYPE_DEFAULT = 0x02;
-
-    /**
-     * Write characteristic without requiring a response by the remote device
-     */
-    public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
-
-    /**
-     * Write characteristic including authentication signature
-     */
-    public static final int WRITE_TYPE_SIGNED = 0x04;
-
-    /**
-     * Characteristic value format type uint8
-     */
-    public static final int FORMAT_UINT8 = 0x11;
-
-    /**
-     * Characteristic value format type uint16
-     */
-    public static final int FORMAT_UINT16 = 0x12;
-
-    /**
-     * Characteristic value format type uint32
-     */
-    public static final int FORMAT_UINT32 = 0x14;
-
-    /**
-     * Characteristic value format type sint8
-     */
-    public static final int FORMAT_SINT8 = 0x21;
-
-    /**
-     * Characteristic value format type sint16
-     */
-    public static final int FORMAT_SINT16 = 0x22;
-
-    /**
-     * Characteristic value format type sint32
-     */
-    public static final int FORMAT_SINT32 = 0x24;
-
-    /**
-     * Characteristic value format type sfloat (16-bit float)
-     */
-    public static final int FORMAT_SFLOAT = 0x32;
-
-    /**
-     * Characteristic value format type float (32-bit float)
-     */
-    public static final int FORMAT_FLOAT = 0x34;
-
-
-    /**
-     * The UUID of this characteristic.
-     *
-     * @hide
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this characteristic.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected int mInstance;
-
-    /**
-     * Characteristic properties.
-     *
-     * @hide
-     */
-    protected int mProperties;
-
-    /**
-     * Characteristic permissions.
-     *
-     * @hide
-     */
-    protected int mPermissions;
-
-    /**
-     * Key size (default = 16).
-     *
-     * @hide
-     */
-    protected int mKeySize = 16;
-
-    /**
-     * Write type for this characteristic.
-     * See WRITE_TYPE_* constants.
-     *
-     * @hide
-     */
-    protected int mWriteType;
-
-    /**
-     * Back-reference to the service this characteristic belongs to.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected BluetoothGattService mService;
-
-    /**
-     * The cached value of this characteristic.
-     *
-     * @hide
-     */
-    protected byte[] mValue;
-
-    /**
-     * List of descriptors included in this characteristic.
-     */
-    protected List<BluetoothGattDescriptor> mDescriptors;
-
-    /**
-     * Create a new BluetoothGattCharacteristic.
-     *
-     * @param uuid The UUID for this characteristic
-     * @param properties Properties of this characteristic
-     * @param permissions Permissions for this characteristic
-     */
-    public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
-        initCharacteristic(null, uuid, 0, properties, permissions);
-    }
-
-    /**
-     * Create a new BluetoothGattCharacteristic
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic(BluetoothGattService service,
-            UUID uuid, int instanceId,
-            int properties, int permissions) {
-        initCharacteristic(service, uuid, instanceId, properties, permissions);
-    }
-
-    /**
-     * Create a new BluetoothGattCharacteristic
-     *
-     * @hide
-     */
-    public BluetoothGattCharacteristic(UUID uuid, int instanceId,
-            int properties, int permissions) {
-        initCharacteristic(null, uuid, instanceId, properties, permissions);
-    }
-
-    private void initCharacteristic(BluetoothGattService service,
-            UUID uuid, int instanceId,
-            int properties, int permissions) {
-        mUuid = uuid;
-        mInstance = instanceId;
-        mProperties = properties;
-        mPermissions = permissions;
-        mService = service;
-        mValue = null;
-        mDescriptors = new ArrayList<BluetoothGattDescriptor>();
-
-        if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) {
-            mWriteType = WRITE_TYPE_NO_RESPONSE;
-        } else {
-            mWriteType = WRITE_TYPE_DEFAULT;
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstance);
-        out.writeInt(mProperties);
-        out.writeInt(mPermissions);
-        out.writeInt(mKeySize);
-        out.writeInt(mWriteType);
-        out.writeTypedList(mDescriptors);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattCharacteristic> CREATOR =
-            new Parcelable.Creator<BluetoothGattCharacteristic>() {
-        public BluetoothGattCharacteristic createFromParcel(Parcel in) {
-            return new BluetoothGattCharacteristic(in);
-        }
-
-        public BluetoothGattCharacteristic[] newArray(int size) {
-            return new BluetoothGattCharacteristic[size];
-        }
-    };
-
-    private BluetoothGattCharacteristic(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
-        mInstance = in.readInt();
-        mProperties = in.readInt();
-        mPermissions = in.readInt();
-        mKeySize = in.readInt();
-        mWriteType = in.readInt();
-
-        mDescriptors = new ArrayList<BluetoothGattDescriptor>();
-
-        ArrayList<BluetoothGattDescriptor> descs =
-                in.createTypedArrayList(BluetoothGattDescriptor.CREATOR);
-        if (descs != null) {
-            for (BluetoothGattDescriptor desc : descs) {
-                desc.setCharacteristic(this);
-                mDescriptors.add(desc);
-            }
-        }
-    }
-
-    /**
-     * Returns the desired key size.
-     *
-     * @hide
-     */
-    public int getKeySize() {
-        return mKeySize;
-    }
-
-    /**
-     * Adds a descriptor to this characteristic.
-     *
-     * @param descriptor Descriptor to be added to this characteristic.
-     * @return true, if the descriptor was added to the characteristic
-     */
-    public boolean addDescriptor(BluetoothGattDescriptor descriptor) {
-        mDescriptors.add(descriptor);
-        descriptor.setCharacteristic(this);
-        return true;
-    }
-
-    /**
-     * Get a descriptor by UUID and isntance id.
-     *
-     * @hide
-     */
-    /*package*/  BluetoothGattDescriptor getDescriptor(UUID uuid, int instanceId) {
-        for (BluetoothGattDescriptor descriptor : mDescriptors) {
-            if (descriptor.getUuid().equals(uuid)
-                    && descriptor.getInstanceId() == instanceId) {
-                return descriptor;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the service this characteristic belongs to.
-     *
-     * @return The asscociated service
-     */
-    public BluetoothGattService getService() {
-        return mService;
-    }
-
-    /**
-     * Sets the service associated with this device.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /*package*/ void setService(BluetoothGattService service) {
-        mService = service;
-    }
-
-    /**
-     * Returns the UUID of this characteristic
-     *
-     * @return UUID of this characteristic
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this characteristic.
-     *
-     * <p>If a remote device offers multiple characteristics with the same UUID,
-     * the instance ID is used to distuinguish between characteristics.
-     *
-     * @return Instance ID of this characteristic
-     */
-    public int getInstanceId() {
-        return mInstance;
-    }
-
-    /**
-     * Force the instance ID.
-     *
-     * @hide
-     */
-    public void setInstanceId(int instanceId) {
-        mInstance = instanceId;
-    }
-
-    /**
-     * Returns the properties of this characteristic.
-     *
-     * <p>The properties contain a bit mask of property flags indicating
-     * the features of this characteristic.
-     *
-     * @return Properties of this characteristic
-     */
-    public int getProperties() {
-        return mProperties;
-    }
-
-    /**
-     * Returns the permissions for this characteristic.
-     *
-     * @return Permissions of this characteristic
-     */
-    public int getPermissions() {
-        return mPermissions;
-    }
-
-    /**
-     * Gets the write type for this characteristic.
-     *
-     * @return Write type for this characteristic
-     */
-    public int getWriteType() {
-        return mWriteType;
-    }
-
-    /**
-     * Set the write type for this characteristic
-     *
-     * <p>Setting the write type of a characteristic determines how the
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function
-     * write this characteristic.
-     *
-     * @param writeType The write type to for this characteristic. Can be one of: {@link
-     * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}.
-     */
-    public void setWriteType(int writeType) {
-        mWriteType = writeType;
-    }
-
-    /**
-     * Set the desired key size.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void setKeySize(int keySize) {
-        mKeySize = keySize;
-    }
-
-    /**
-     * Returns a list of descriptors for this characteristic.
-     *
-     * @return Descriptors for this characteristic
-     */
-    public List<BluetoothGattDescriptor> getDescriptors() {
-        return mDescriptors;
-    }
-
-    /**
-     * Returns a descriptor with a given UUID out of the list of
-     * descriptors for this characteristic.
-     *
-     * @return GATT descriptor object or null if no descriptor with the given UUID was found.
-     */
-    public BluetoothGattDescriptor getDescriptor(UUID uuid) {
-        for (BluetoothGattDescriptor descriptor : mDescriptors) {
-            if (descriptor.getUuid().equals(uuid)) {
-                return descriptor;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Get the stored value for this characteristic.
-     *
-     * <p>This function returns the stored value for this characteristic as
-     * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached
-     * value of the characteristic is updated as a result of a read characteristic
-     * operation or if a characteristic update notification has been received.
-     *
-     * @return Cached value of the characteristic
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead
-     */
-    @Deprecated
-    public byte[] getValue() {
-        return mValue;
-    }
-
-    /**
-     * Return the stored value of this characteristic.
-     *
-     * <p>The formatType parameter determines how the characteristic value
-     * is to be interpreted. For example, settting formatType to
-     * {@link #FORMAT_UINT16} specifies that the first two bytes of the
-     * characteristic value at the given offset are interpreted to generate the
-     * return value.
-     *
-     * @param formatType The format type used to interpret the characteristic value.
-     * @param offset Offset at which the integer value can be found.
-     * @return Cached value of the characteristic or null of offset exceeds value size.
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
-     * the characteristic value
-     */
-    @Deprecated
-    public Integer getIntValue(int formatType, int offset) {
-        if ((offset + getTypeLen(formatType)) > mValue.length) return null;
-
-        switch (formatType) {
-            case FORMAT_UINT8:
-                return unsignedByteToInt(mValue[offset]);
-
-            case FORMAT_UINT16:
-                return unsignedBytesToInt(mValue[offset], mValue[offset + 1]);
-
-            case FORMAT_UINT32:
-                return unsignedBytesToInt(mValue[offset], mValue[offset + 1],
-                        mValue[offset + 2], mValue[offset + 3]);
-            case FORMAT_SINT8:
-                return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8);
-
-            case FORMAT_SINT16:
-                return unsignedToSigned(unsignedBytesToInt(mValue[offset],
-                        mValue[offset + 1]), 16);
-
-            case FORMAT_SINT32:
-                return unsignedToSigned(unsignedBytesToInt(mValue[offset],
-                        mValue[offset + 1], mValue[offset + 2], mValue[offset + 3]), 32);
-        }
-
-        return null;
-    }
-
-    /**
-     * Return the stored value of this characteristic.
-     * <p>See {@link #getValue} for details.
-     *
-     * @param formatType The format type used to interpret the characteristic value.
-     * @param offset Offset at which the float value can be found.
-     * @return Cached value of the characteristic at a given offset or null if the requested offset
-     * exceeds the value size.
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
-     * the characteristic value
-     */
-    @Deprecated
-    public Float getFloatValue(int formatType, int offset) {
-        if ((offset + getTypeLen(formatType)) > mValue.length) return null;
-
-        switch (formatType) {
-            case FORMAT_SFLOAT:
-                return bytesToFloat(mValue[offset], mValue[offset + 1]);
-
-            case FORMAT_FLOAT:
-                return bytesToFloat(mValue[offset], mValue[offset + 1],
-                        mValue[offset + 2], mValue[offset + 3]);
-        }
-
-        return null;
-    }
-
-    /**
-     * Return the stored value of this characteristic.
-     * <p>See {@link #getValue} for details.
-     *
-     * @param offset Offset at which the string value can be found.
-     * @return Cached value of the characteristic
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
-     * the characteristic value
-     */
-    @Deprecated
-    public String getStringValue(int offset) {
-        if (mValue == null || offset > mValue.length) return null;
-        byte[] strBytes = new byte[mValue.length - offset];
-        for (int i = 0; i != (mValue.length - offset); ++i) strBytes[i] = mValue[offset + i];
-        return new String(strBytes);
-    }
-
-    /**
-     * Updates the locally stored value of this characteristic.
-     *
-     * <p>This function modifies the locally stored cached value of this
-     * characteristic. To send the value to the remote device, call
-     * {@link BluetoothGatt#writeCharacteristic} to send the value to the
-     * remote device.
-     *
-     * @param value New value for this characteristic
-     * @return true if the locally stored value has been set, false if the requested value could not
-     * be stored locally.
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(byte[] value) {
-        mValue = value;
-        return true;
-    }
-
-    /**
-     * Set the locally stored value of this characteristic.
-     * <p>See {@link #setValue(byte[])} for details.
-     *
-     * @param value New value for this characteristic
-     * @param formatType Integer format type used to transform the value parameter
-     * @param offset Offset at which the value should be placed
-     * @return true if the locally stored value has been set
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(int value, int formatType, int offset) {
-        int len = offset + getTypeLen(formatType);
-        if (mValue == null) mValue = new byte[len];
-        if (len > mValue.length) return false;
-
-        switch (formatType) {
-            case FORMAT_SINT8:
-                value = intToSignedBits(value, 8);
-                // Fall-through intended
-            case FORMAT_UINT8:
-                mValue[offset] = (byte) (value & 0xFF);
-                break;
-
-            case FORMAT_SINT16:
-                value = intToSignedBits(value, 16);
-                // Fall-through intended
-            case FORMAT_UINT16:
-                mValue[offset++] = (byte) (value & 0xFF);
-                mValue[offset] = (byte) ((value >> 8) & 0xFF);
-                break;
-
-            case FORMAT_SINT32:
-                value = intToSignedBits(value, 32);
-                // Fall-through intended
-            case FORMAT_UINT32:
-                mValue[offset++] = (byte) (value & 0xFF);
-                mValue[offset++] = (byte) ((value >> 8) & 0xFF);
-                mValue[offset++] = (byte) ((value >> 16) & 0xFF);
-                mValue[offset] = (byte) ((value >> 24) & 0xFF);
-                break;
-
-            default:
-                return false;
-        }
-        return true;
-    }
-
-    /**
-     * Set the locally stored value of this characteristic.
-     * <p>See {@link #setValue(byte[])} for details.
-     *
-     * @param mantissa Mantissa for this characteristic
-     * @param exponent exponent value for this characteristic
-     * @param formatType Float format type used to transform the value parameter
-     * @param offset Offset at which the value should be placed
-     * @return true if the locally stored value has been set
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(int mantissa, int exponent, int formatType, int offset) {
-        int len = offset + getTypeLen(formatType);
-        if (mValue == null) mValue = new byte[len];
-        if (len > mValue.length) return false;
-
-        switch (formatType) {
-            case FORMAT_SFLOAT:
-                mantissa = intToSignedBits(mantissa, 12);
-                exponent = intToSignedBits(exponent, 4);
-                mValue[offset++] = (byte) (mantissa & 0xFF);
-                mValue[offset] = (byte) ((mantissa >> 8) & 0x0F);
-                mValue[offset] += (byte) ((exponent & 0x0F) << 4);
-                break;
-
-            case FORMAT_FLOAT:
-                mantissa = intToSignedBits(mantissa, 24);
-                exponent = intToSignedBits(exponent, 8);
-                mValue[offset++] = (byte) (mantissa & 0xFF);
-                mValue[offset++] = (byte) ((mantissa >> 8) & 0xFF);
-                mValue[offset++] = (byte) ((mantissa >> 16) & 0xFF);
-                mValue[offset] += (byte) (exponent & 0xFF);
-                break;
-
-            default:
-                return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Set the locally stored value of this characteristic.
-     * <p>See {@link #setValue(byte[])} for details.
-     *
-     * @param value New value for this characteristic
-     * @return true if the locally stored value has been set
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(String value) {
-        mValue = value.getBytes();
-        return true;
-    }
-
-    /**
-     * Returns the size of a give value type.
-     */
-    private int getTypeLen(int formatType) {
-        return formatType & 0xF;
-    }
-
-    /**
-     * Convert a signed byte to an unsigned int.
-     */
-    private int unsignedByteToInt(byte b) {
-        return b & 0xFF;
-    }
-
-    /**
-     * Convert signed bytes to a 16-bit unsigned int.
-     */
-    private int unsignedBytesToInt(byte b0, byte b1) {
-        return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8));
-    }
-
-    /**
-     * Convert signed bytes to a 32-bit unsigned int.
-     */
-    private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) {
-        return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8))
-                + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24);
-    }
-
-    /**
-     * Convert signed bytes to a 16-bit short float value.
-     */
-    private float bytesToFloat(byte b0, byte b1) {
-        int mantissa = unsignedToSigned(unsignedByteToInt(b0)
-                + ((unsignedByteToInt(b1) & 0x0F) << 8), 12);
-        int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4);
-        return (float) (mantissa * Math.pow(10, exponent));
-    }
-
-    /**
-     * Convert signed bytes to a 32-bit short float value.
-     */
-    private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) {
-        int mantissa = unsignedToSigned(unsignedByteToInt(b0)
-                + (unsignedByteToInt(b1) << 8)
-                + (unsignedByteToInt(b2) << 16), 24);
-        return (float) (mantissa * Math.pow(10, b3));
-    }
-
-    /**
-     * Convert an unsigned integer value to a two's-complement encoded
-     * signed value.
-     */
-    private int unsignedToSigned(int unsigned, int size) {
-        if ((unsigned & (1 << size - 1)) != 0) {
-            unsigned = -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
-        }
-        return unsigned;
-    }
-
-    /**
-     * Convert an integer into the signed bits of a given length.
-     */
-    private int intToSignedBits(int i, int size) {
-        if (i < 0) {
-            i = (1 << size - 1) + (i & ((1 << size - 1) - 1));
-        }
-        return i;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
deleted file mode 100644
index 6ed4706..0000000
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Descriptor
- *
- * <p> GATT Descriptors contain additional information and attributes of a GATT
- * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe
- * the characteristic's features or to control certain behaviours of the characteristic.
- */
-public class BluetoothGattDescriptor implements Parcelable {
-
-    /**
-     * Value used to enable notification for a client configuration descriptor
-     */
-    public static final byte[] ENABLE_NOTIFICATION_VALUE = {0x01, 0x00};
-
-    /**
-     * Value used to enable indication for a client configuration descriptor
-     */
-    public static final byte[] ENABLE_INDICATION_VALUE = {0x02, 0x00};
-
-    /**
-     * Value used to disable notifications or indicatinos
-     */
-    public static final byte[] DISABLE_NOTIFICATION_VALUE = {0x00, 0x00};
-
-    /**
-     * Descriptor read permission
-     */
-    public static final int PERMISSION_READ = 0x01;
-
-    /**
-     * Descriptor permission: Allow encrypted read operations
-     */
-    public static final int PERMISSION_READ_ENCRYPTED = 0x02;
-
-    /**
-     * Descriptor permission: Allow reading with person-in-the-middle protection
-     */
-    public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
-
-    /**
-     * Descriptor write permission
-     */
-    public static final int PERMISSION_WRITE = 0x10;
-
-    /**
-     * Descriptor permission: Allow encrypted writes
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
-
-    /**
-     * Descriptor permission: Allow encrypted writes with person-in-the-middle
-     * protection
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
-
-    /**
-     * Descriptor permission: Allow signed write operations
-     */
-    public static final int PERMISSION_WRITE_SIGNED = 0x80;
-
-    /**
-     * Descriptor permission: Allow signed write operations with
-     * person-in-the-middle protection
-     */
-    public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
-
-    /**
-     * The UUID of this descriptor.
-     *
-     * @hide
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this descriptor.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected int mInstance;
-
-    /**
-     * Permissions for this descriptor
-     *
-     * @hide
-     */
-    protected int mPermissions;
-
-    /**
-     * Back-reference to the characteristic this descriptor belongs to.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected BluetoothGattCharacteristic mCharacteristic;
-
-    /**
-     * The value for this descriptor.
-     *
-     * @hide
-     */
-    protected byte[] mValue;
-
-    /**
-     * Create a new BluetoothGattDescriptor.
-     *
-     * @param uuid The UUID for this descriptor
-     * @param permissions Permissions for this descriptor
-     */
-    public BluetoothGattDescriptor(UUID uuid, int permissions) {
-        initDescriptor(null, uuid, 0, permissions);
-    }
-
-    /**
-     * Create a new BluetoothGattDescriptor.
-     *
-     * @param characteristic The characteristic this descriptor belongs to
-     * @param uuid The UUID for this descriptor
-     * @param permissions Permissions for this descriptor
-     */
-    /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
-            int instance, int permissions) {
-        initDescriptor(characteristic, uuid, instance, permissions);
-    }
-
-    /**
-     * @hide
-     */
-    public BluetoothGattDescriptor(UUID uuid, int instance, int permissions) {
-        initDescriptor(null, uuid, instance, permissions);
-    }
-
-    private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
-            int instance, int permissions) {
-        mCharacteristic = characteristic;
-        mUuid = uuid;
-        mInstance = instance;
-        mPermissions = permissions;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstance);
-        out.writeInt(mPermissions);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattDescriptor> CREATOR =
-            new Parcelable.Creator<BluetoothGattDescriptor>() {
-        public BluetoothGattDescriptor createFromParcel(Parcel in) {
-            return new BluetoothGattDescriptor(in);
-        }
-
-        public BluetoothGattDescriptor[] newArray(int size) {
-            return new BluetoothGattDescriptor[size];
-        }
-    };
-
-    private BluetoothGattDescriptor(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
-        mInstance = in.readInt();
-        mPermissions = in.readInt();
-    }
-
-    /**
-     * Returns the characteristic this descriptor belongs to.
-     *
-     * @return The characteristic.
-     */
-    public BluetoothGattCharacteristic getCharacteristic() {
-        return mCharacteristic;
-    }
-
-    /**
-     * Set the back-reference to the associated characteristic
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
-        mCharacteristic = characteristic;
-    }
-
-    /**
-     * Returns the UUID of this descriptor.
-     *
-     * @return UUID of this descriptor
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this descriptor.
-     *
-     * <p>If a remote device offers multiple descriptors with the same UUID,
-     * the instance ID is used to distuinguish between descriptors.
-     *
-     * @return Instance ID of this descriptor
-     * @hide
-     */
-    public int getInstanceId() {
-        return mInstance;
-    }
-
-    /**
-     * Force the instance ID.
-     *
-     * @hide
-     */
-    public void setInstanceId(int instanceId) {
-        mInstance = instanceId;
-    }
-
-    /**
-     * Returns the permissions for this descriptor.
-     *
-     * @return Permissions of this descriptor
-     */
-    public int getPermissions() {
-        return mPermissions;
-    }
-
-    /**
-     * Returns the stored value for this descriptor
-     *
-     * <p>This function returns the stored value for this descriptor as
-     * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached
-     * value of the descriptor is updated as a result of a descriptor read
-     * operation.
-     *
-     * @return Cached value of the descriptor
-     *
-     * @deprecated  Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead
-     */
-    @Deprecated
-    public byte[] getValue() {
-        return mValue;
-    }
-
-    /**
-     * Updates the locally stored value of this descriptor.
-     *
-     * <p>This function modifies the locally stored cached value of this
-     * descriptor. To send the value to the remote device, call
-     * {@link BluetoothGatt#writeDescriptor} to send the value to the
-     * remote device.
-     *
-     * @param value New value for this descriptor
-     * @return true if the locally stored value has been set, false if the requested value could not
-     * be stored locally.
-     *
-     * @deprecated Pass the descriptor value directly into
-     * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}
-     */
-    @Deprecated
-    public boolean setValue(byte[] value) {
-        mValue = value;
-        return true;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.java b/core/java/android/bluetooth/BluetoothGattIncludedService.java
deleted file mode 100644
index 1ae2ca0..0000000
--- a/core/java/android/bluetooth/BluetoothGattIncludedService.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.bluetooth;
-
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Included Service
- *
- * @hide
- */
-public class BluetoothGattIncludedService implements Parcelable {
-
-    /**
-     * The UUID of this service.
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this service.
-     */
-    protected int mInstanceId;
-
-    /**
-     * Service type (Primary/Secondary).
-     */
-    protected int mServiceType;
-
-    /**
-     * Create a new BluetoothGattIncludedService
-     */
-    public BluetoothGattIncludedService(UUID uuid, int instanceId, int serviceType) {
-        mUuid = uuid;
-        mInstanceId = instanceId;
-        mServiceType = serviceType;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstanceId);
-        out.writeInt(mServiceType);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattIncludedService> CREATOR =
-            new Parcelable.Creator<BluetoothGattIncludedService>() {
-        public BluetoothGattIncludedService createFromParcel(Parcel in) {
-            return new BluetoothGattIncludedService(in);
-        }
-
-        public BluetoothGattIncludedService[] newArray(int size) {
-            return new BluetoothGattIncludedService[size];
-        }
-    };
-
-    private BluetoothGattIncludedService(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
-        mInstanceId = in.readInt();
-        mServiceType = in.readInt();
-    }
-
-    /**
-     * Returns the UUID of this service
-     *
-     * @return UUID of this service
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this service
-     *
-     * <p>If a remote device offers multiple services with the same UUID
-     * (ex. multiple battery services for different batteries), the instance
-     * ID is used to distuinguish services.
-     *
-     * @return Instance ID of this service
-     */
-    public int getInstanceId() {
-        return mInstanceId;
-    }
-
-    /**
-     * Get the type of this service (primary/secondary)
-     */
-    public int getType() {
-        return mServiceType;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
deleted file mode 100644
index 08e0178..0000000
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ /dev/null
@@ -1,954 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Public API for the Bluetooth GATT Profile server role.
- *
- * <p>This class provides Bluetooth GATT server role functionality,
- * allowing applications to create Bluetooth Smart services and
- * characteristics.
- *
- * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
- * via IPC.  Use {@link BluetoothManager#openGattServer} to get an instance
- * of this class.
- */
-public final class BluetoothGattServer implements BluetoothProfile {
-    private static final String TAG = "BluetoothGattServer";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private final IBluetoothGatt mService;
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-
-    private BluetoothGattServerCallback mCallback;
-
-    private Object mServerIfLock = new Object();
-    private int mServerIf;
-    private int mTransport;
-    private BluetoothGattService mPendingService;
-    private List<BluetoothGattService> mServices;
-
-    private static final int CALLBACK_REG_TIMEOUT = 10000;
-
-    /**
-     * Bluetooth GATT interface callbacks
-     */
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
-            new IBluetoothGattServerCallback.Stub() {
-                /**
-                 * Application interface registered - app is ready to go
-                 * @hide
-                 */
-                @Override
-                public void onServerRegistered(int status, int serverIf) {
-                    if (DBG) {
-                        Log.d(TAG, "onServerRegistered() - status=" + status
-                                + " serverIf=" + serverIf);
-                    }
-                    synchronized (mServerIfLock) {
-                        if (mCallback != null) {
-                            mServerIf = serverIf;
-                            mServerIfLock.notify();
-                        } else {
-                            // registration timeout
-                            Log.e(TAG, "onServerRegistered: mCallback is null");
-                        }
-                    }
-                }
-
-                /**
-                 * Server connection state changed
-                 * @hide
-                 */
-                @Override
-                public void onServerConnectionState(int status, int serverIf,
-                        boolean connected, String address) {
-                    if (DBG) {
-                        Log.d(TAG, "onServerConnectionState() - status=" + status
-                                + " serverIf=" + serverIf + " device=" + address);
-                    }
-                    try {
-                        mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
-                                connected ? BluetoothProfile.STATE_CONNECTED :
-                                        BluetoothProfile.STATE_DISCONNECTED);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Service has been added
-                 * @hide
-                 */
-                @Override
-                public void onServiceAdded(int status, BluetoothGattService service) {
-                    if (DBG) {
-                        Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId()
-                                + " uuid=" + service.getUuid() + " status=" + status);
-                    }
-
-                    if (mPendingService == null) {
-                        return;
-                    }
-
-                    BluetoothGattService tmp = mPendingService;
-                    mPendingService = null;
-
-                    // Rewrite newly assigned handles to existing service.
-                    tmp.setInstanceId(service.getInstanceId());
-                    List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics();
-                    List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics();
-                    for (int i = 0; i < svc_chars.size(); i++) {
-                        BluetoothGattCharacteristic temp_char = temp_chars.get(i);
-                        BluetoothGattCharacteristic svc_char = svc_chars.get(i);
-
-                        temp_char.setInstanceId(svc_char.getInstanceId());
-
-                        List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors();
-                        List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors();
-                        for (int j = 0; j < svc_descs.size(); j++) {
-                            temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId());
-                        }
-                    }
-
-                    mServices.add(tmp);
-
-                    try {
-                        mCallback.onServiceAdded((int) status, tmp);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Remote client characteristic read request.
-                 * @hide
-                 */
-                @Override
-                public void onCharacteristicReadRequest(String address, int transId,
-                        int offset, boolean isLong, int handle) {
-                    if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
-                    if (characteristic == null) {
-                        Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onCharacteristicReadRequest(device, transId, offset,
-                                characteristic);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Remote client descriptor read request.
-                 * @hide
-                 */
-                @Override
-                public void onDescriptorReadRequest(String address, int transId,
-                        int offset, boolean isLong, int handle) {
-                    if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
-                    if (descriptor == null) {
-                        Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Remote client characteristic write request.
-                 * @hide
-                 */
-                @Override
-                public void onCharacteristicWriteRequest(String address, int transId,
-                        int offset, int length, boolean isPrep, boolean needRsp,
-                        int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
-                    if (characteristic == null) {
-                        Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
-                                isPrep, needRsp, offset, value);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-
-                }
-
-                /**
-                 * Remote client descriptor write request.
-                 * @hide
-                 */
-                @Override
-                public void onDescriptorWriteRequest(String address, int transId, int offset,
-                        int length, boolean isPrep, boolean needRsp, int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
-                    if (descriptor == null) {
-                        Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onDescriptorWriteRequest(device, transId, descriptor,
-                                isPrep, needRsp, offset, value);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Execute pending writes.
-                 * @hide
-                 */
-                @Override
-                public void onExecuteWrite(String address, int transId,
-                        boolean execWrite) {
-                    if (DBG) {
-                        Log.d(TAG, "onExecuteWrite() - "
-                                + "device=" + address + ", transId=" + transId
-                                + "execWrite=" + execWrite);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onExecuteWrite(device, transId, execWrite);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * A notification/indication has been sent.
-                 * @hide
-                 */
-                @Override
-                public void onNotificationSent(String address, int status) {
-                    if (VDBG) {
-                        Log.d(TAG, "onNotificationSent() - "
-                                + "device=" + address + ", status=" + status);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onNotificationSent(device, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * The MTU for a connection has changed
-                 * @hide
-                 */
-                @Override
-                public void onMtuChanged(String address, int mtu) {
-                    if (DBG) {
-                        Log.d(TAG, "onMtuChanged() - "
-                                + "device=" + address + ", mtu=" + mtu);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onMtuChanged(device, mtu);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * The PHY for a connection was updated
-                 * @hide
-                 */
-                @Override
-                public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG,
-                                "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
-                                        + ", rxPHy=" + rxPhy);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * The PHY for a connection was read
-                 * @hide
-                 */
-                @Override
-                public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG,
-                                "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
-                                        + ", rxPHy=" + rxPhy);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onPhyRead(device, txPhy, rxPhy, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * Callback invoked when the given connection is updated
-                 * @hide
-                 */
-                @Override
-                public void onConnectionUpdated(String address, int interval, int latency,
-                        int timeout, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onConnectionUpdated() - Device=" + address
-                                + " interval=" + interval + " latency=" + latency
-                                + " timeout=" + timeout + " status=" + status);
-                    }
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onConnectionUpdated(device, interval, latency,
-                                timeout, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-            };
-
-    /**
-     * Create a BluetoothGattServer proxy object.
-     */
-    /* package */ BluetoothGattServer(IBluetoothGatt iGatt, int transport,
-            BluetoothAdapter adapter) {
-        mService = iGatt;
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mCallback = null;
-        mServerIf = 0;
-        mTransport = transport;
-        mServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * Returns a characteristic with given handle.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                if (charac.getInstanceId() == handle) {
-                    return charac;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns a descriptor with given handle.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
-                    if (desc.getInstanceId() == handle) {
-                        return desc;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Close this GATT server instance.
-     *
-     * Application should call this method as early as possible after it is done with
-     * this GATT server.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void close() {
-        if (DBG) Log.d(TAG, "close()");
-        unregisterCallback();
-    }
-
-    /**
-     * Register an application callback to start using GattServer.
-     *
-     * <p>This is an asynchronous call. The callback is used to notify
-     * success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @return true, the callback will be called to notify success or failure, false on immediate
-     * error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
-        return registerCallback(callback, false);
-    }
-
-    /**
-     * Register an application callback to start using GattServer.
-     *
-     * <p>This is an asynchronous call. The callback is used to notify
-     * success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param eatt_support indicates if server can use eatt
-     * @return true, the callback will be called to notify success or failure, false on immediate
-     * error
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback,
-                                         boolean eatt_support) {
-        if (DBG) Log.d(TAG, "registerCallback()");
-        if (mService == null) {
-            Log.e(TAG, "GATT service not available");
-            return false;
-        }
-        UUID uuid = UUID.randomUUID();
-        if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
-
-        synchronized (mServerIfLock) {
-            if (mCallback != null) {
-                Log.e(TAG, "App can register callback only once");
-                return false;
-            }
-
-            mCallback = callback;
-            try {
-                mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback,
-                        eatt_support, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-                mCallback = null;
-                return false;
-            }
-
-            try {
-                mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
-            } catch (InterruptedException e) {
-                Log.e(TAG, "" + e);
-                mCallback = null;
-            }
-
-            if (mServerIf == 0) {
-                mCallback = null;
-                return false;
-            } else {
-                return true;
-            }
-        }
-    }
-
-    /**
-     * Unregister the current application and callbacks.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void unregisterCallback() {
-        if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mCallback = null;
-            mService.unregisterServer(mServerIf, mAttributionSource);
-            mServerIf = 0;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Returns a service by UUID, instance and type.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
-        for (BluetoothGattService svc : mServices) {
-            if (svc.getType() == type
-                    && svc.getInstanceId() == instanceId
-                    && svc.getUuid().equals(uuid)) {
-                return svc;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Initiate a connection to a Bluetooth GATT capable device.
-     *
-     * <p>The connection may not be established right away, but will be
-     * completed when the remote device is available. A
-     * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
-     * invoked when the connection state changes as a result of this function.
-     *
-     * <p>The autoConnect parameter determines whether to actively connect to
-     * the remote device, or rather passively scan and finalize the connection
-     * when the remote device is in range/available. Generally, the first ever
-     * connection to a device should be direct (autoConnect set to false) and
-     * subsequent connections to known devices should be invoked with the
-     * autoConnect parameter set to true.
-     *
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @return true, if the connection attempt was initiated successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(BluetoothDevice device, boolean autoConnect) {
-        if (DBG) {
-            Log.d(TAG,
-                    "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
-        }
-        if (mService == null || mServerIf == 0) return false;
-
-        try {
-            // autoConnect is inverse of "isDirect"
-            mService.serverConnect(
-                    mServerIf, device.getAddress(), !autoConnect, mTransport, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Disconnects an established connection, or cancels a connection attempt
-     * currently in progress.
-     *
-     * @param device Remote device
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void cancelConnection(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Set the preferred connection PHY for this app. Please note that this is just a
-     * recommendation, whether the PHY change will happen depends on other applications peferences,
-     * local and remote controller capabilities. Controller can override these settings. <p> {@link
-     * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if
-     * no PHY change happens. It is also triggered when remote device updates the PHY.
-     *
-     * @param device The remote device to send this response to
-     * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
-     * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
-     * {@link BluetoothDevice#PHY_OPTION_S8}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
-        try {
-            mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
-                    phyOptions, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
-     * in {@link BluetoothGattServerCallback#onPhyRead}
-     *
-     * @param device The remote device to send this response to
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void readPhy(BluetoothDevice device) {
-        try {
-            mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Send a response to a read or write request to a remote device.
-     *
-     * <p>This function must be invoked in when a remote read/write request
-     * is received by one of these callback methods:
-     *
-     * <ul>
-     * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
-     * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
-     * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
-     * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
-     * </ul>
-     *
-     * @param device The remote device to send this response to
-     * @param requestId The ID of the request that was received with the callback
-     * @param status The status of the request to be sent to the remote devices
-     * @param offset Value offset for partial read/write response
-     * @param value The value of the attribute that was read/written (optional)
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendResponse(BluetoothDevice device, int requestId,
-            int status, int offset, byte[] value) {
-        if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
-        if (mService == null || mServerIf == 0) return false;
-
-        try {
-            mService.sendResponse(mServerIf, device.getAddress(), requestId,
-                    status, offset, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Send a notification or indication that a local characteristic has been
-     * updated.
-     *
-     * <p>A notification or indication is sent to the remote device to signal
-     * that the characteristic has been updated. This function should be invoked
-     * for every client that requests notifications/indications by writing
-     * to the "Client Configuration" descriptor for the given characteristic.
-     *
-     * @param device The remote device to receive the notification/indication
-     * @param characteristic The local characteristic that has been updated
-     * @param confirm true to request confirmation from the client (indication), false to send a
-     * notification
-     * @return true, if the notification has been triggered successfully
-     * @throws IllegalArgumentException
-     *
-     * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice,
-     * BluetoothGattCharacteristic, boolean, byte[])}  as this is not memory safe.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean notifyCharacteristicChanged(BluetoothDevice device,
-            BluetoothGattCharacteristic characteristic, boolean confirm) {
-        return notifyCharacteristicChanged(device, characteristic, confirm,
-                characteristic.getValue()) == BluetoothStatusCodes.SUCCESS;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
-            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
-            BluetoothStatusCodes.ERROR_UNKNOWN
-    })
-    public @interface NotifyCharacteristicReturnValues{}
-
-    /**
-     * Send a notification or indication that a local characteristic has been
-     * updated.
-     *
-     * <p>A notification or indication is sent to the remote device to signal
-     * that the characteristic has been updated. This function should be invoked
-     * for every client that requests notifications/indications by writing
-     * to the "Client Configuration" descriptor for the given characteristic.
-     *
-     * @param device the remote device to receive the notification/indication
-     * @param characteristic the local characteristic that has been updated
-     * @param confirm {@code true} to request confirmation from the client (indication) or
-     * {@code false} to send a notification
-     * @param value the characteristic value
-     * @return whether the notification has been triggered successfully
-     * @throws IllegalArgumentException if the characteristic value or service is null
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @NotifyCharacteristicReturnValues
-    public int notifyCharacteristicChanged(@NonNull BluetoothDevice device,
-            @NonNull BluetoothGattCharacteristic characteristic, boolean confirm,
-            @NonNull byte[] value) {
-        if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
-        if (mService == null || mServerIf == 0) {
-            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
-        }
-
-        if (characteristic == null) {
-            throw new IllegalArgumentException("characteristic must not be null");
-        }
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) {
-            throw new IllegalArgumentException("Characteristic must have a non-null service");
-        }
-        if (value == null) {
-            throw new IllegalArgumentException("Characteristic value must not be null");
-        }
-
-        try {
-            return mService.sendNotification(mServerIf, device.getAddress(),
-                    characteristic.getInstanceId(), confirm,
-                    value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Add a service to the list of services to be hosted.
-     *
-     * <p>Once a service has been addded to the list, the service and its
-     * included characteristics will be provided by the local device.
-     *
-     * <p>If the local device has already exposed services when this function
-     * is called, a service update notification will be sent to all clients.
-     *
-     * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
-     * whether this service has been added successfully. Do not add another service
-     * before this callback.
-     *
-     * @param service Service to be added to the list of services provided by this device.
-     * @return true, if the request to add service has been initiated
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean addService(BluetoothGattService service) {
-        if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
-        if (mService == null || mServerIf == 0) return false;
-
-        mPendingService = service;
-
-        try {
-            mService.addService(mServerIf, service, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Removes a service from the list of services to be provided.
-     *
-     * @param service Service to be removed.
-     * @return true, if the service has been removed
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean removeService(BluetoothGattService service) {
-        if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
-        if (mService == null || mServerIf == 0) return false;
-
-        BluetoothGattService intService = getService(service.getUuid(),
-                service.getInstanceId(), service.getType());
-        if (intService == null) return false;
-
-        try {
-            mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource);
-            mServices.remove(intService);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Remove all services from the list of provided services.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void clearServices() {
-        if (DBG) Log.d(TAG, "clearServices()");
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mService.clearServices(mServerIf, mAttributionSource);
-            mServices.clear();
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Returns a list of GATT services offered by this device.
-     *
-     * <p>An application must call {@link #addService} to add a serice to the
-     * list of services offered by this device.
-     *
-     * @return List of services. Returns an empty list if no services have been added yet.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public List<BluetoothGattService> getServices() {
-        return mServices;
-    }
-
-    /**
-     * Returns a {@link BluetoothGattService} from the list of services offered
-     * by this device.
-     *
-     * <p>If multiple instances of the same service (as identified by UUID)
-     * exist, the first instance of the service is returned.
-     *
-     * @param uuid UUID of the requested service
-     * @return BluetoothGattService if supported, or null if the requested service is not offered by
-     * this device.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public BluetoothGattService getService(UUID uuid) {
-        for (BluetoothGattService service : mServices) {
-            if (service.getUuid().equals(uuid)) {
-                return service;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    public int getConnectionState(BluetoothDevice device) {
-        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
-    }
-
-    /**
-     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    public List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getConnectedDevices instead.");
-    }
-
-    /**
-     * Not supported - please use
-     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
-     * with {@link BluetoothProfile#GATT} as first argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
deleted file mode 100644
index 0ead5f5..0000000
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ /dev/null
@@ -1,202 +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 android.bluetooth;
-
-/**
- * This abstract class is used to implement {@link BluetoothGattServer} callbacks.
- */
-public abstract class BluetoothGattServerCallback {
-
-    /**
-     * Callback indicating when a remote device has been connected or disconnected.
-     *
-     * @param device Remote device that has been connected or disconnected.
-     * @param status Status of the connect or disconnect operation.
-     * @param newState Returns the new connection state. Can be one of {@link
-     * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED}
-     */
-    public void onConnectionStateChange(BluetoothDevice device, int status,
-            int newState) {
-    }
-
-    /**
-     * Indicates whether a local service has been added successfully.
-     *
-     * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service was added
-     * successfully.
-     * @param service The service that has been added
-     */
-    public void onServiceAdded(int status, BluetoothGattService service) {
-    }
-
-    /**
-     * A remote client has requested to read a local characteristic.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the read operation
-     * @param requestId The Id of the request
-     * @param offset Offset into the value of the characteristic
-     * @param characteristic Characteristic to be read
-     */
-    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
-            int offset, BluetoothGattCharacteristic characteristic) {
-    }
-
-    /**
-     * A remote client has requested to write to a local characteristic.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the write operation
-     * @param requestId The Id of the request
-     * @param characteristic Characteristic to be written to.
-     * @param preparedWrite true, if this write operation should be queued for later execution.
-     * @param responseNeeded true, if the remote device requires a response
-     * @param offset The offset given for the value
-     * @param value The value the client wants to assign to the characteristic
-     */
-    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
-            BluetoothGattCharacteristic characteristic,
-            boolean preparedWrite, boolean responseNeeded,
-            int offset, byte[] value) {
-    }
-
-    /**
-     * A remote client has requested to read a local descriptor.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the read operation
-     * @param requestId The Id of the request
-     * @param offset Offset into the value of the characteristic
-     * @param descriptor Descriptor to be read
-     */
-    public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
-            int offset, BluetoothGattDescriptor descriptor) {
-    }
-
-    /**
-     * A remote client has requested to write to a local descriptor.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the write operation
-     * @param requestId The Id of the request
-     * @param descriptor Descriptor to be written to.
-     * @param preparedWrite true, if this write operation should be queued for later execution.
-     * @param responseNeeded true, if the remote device requires a response
-     * @param offset The offset given for the value
-     * @param value The value the client wants to assign to the descriptor
-     */
-    public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
-            BluetoothGattDescriptor descriptor,
-            boolean preparedWrite, boolean responseNeeded,
-            int offset, byte[] value) {
-    }
-
-    /**
-     * Execute all pending write operations for this device.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the write operations
-     * @param requestId The Id of the request
-     * @param execute Whether the pending writes should be executed (true) or cancelled (false)
-     */
-    public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
-    }
-
-    /**
-     * Callback invoked when a notification or indication has been sent to
-     * a remote device.
-     *
-     * <p>When multiple notifications are to be sent, an application must
-     * wait for this callback to be received before sending additional
-     * notifications.
-     *
-     * @param device The remote device the notification has been sent to
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful
-     */
-    public void onNotificationSent(BluetoothDevice device, int status) {
-    }
-
-    /**
-     * Callback indicating the MTU for a given device connection has changed.
-     *
-     * <p>This callback will be invoked if a remote client has requested to change
-     * the MTU for a given connection.
-     *
-     * @param device The remote device that requested the MTU change
-     * @param mtu The new MTU size
-     */
-    public void onMtuChanged(BluetoothDevice device, int mtu) {
-    }
-
-    /**
-     * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result
-     * of remote device changing the PHY.
-     *
-     * @param device The remote device
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback triggered as result of {@link BluetoothGattServer#readPhy}
-     *
-     * @param device The remote device that requested the PHY read
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback indicating the connection parameters were updated.
-     *
-     * @param device The remote device involved
-     * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
-     * 6 (7.5ms) to 3200 (4000ms).
-     * @param latency Worker latency for the connection in number of connection events. Valid range
-     * is from 0 to 499
-     * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
-     * (0.1s) to 3200 (32s)
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated
-     * successfully
-     * @hide
-     */
-    public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout,
-            int status) {
-    }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
deleted file mode 100644
index 36bc477..0000000
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Service
- *
- * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic},
- * as well as referenced services.
- */
-public class BluetoothGattService implements Parcelable {
-
-    /**
-     * Primary service
-     */
-    public static final int SERVICE_TYPE_PRIMARY = 0;
-
-    /**
-     * Secondary service (included by primary services)
-     */
-    public static final int SERVICE_TYPE_SECONDARY = 1;
-
-
-    /**
-     * The remote device this service is associated with.
-     * This applies to client applications only.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected BluetoothDevice mDevice;
-
-    /**
-     * The UUID of this service.
-     *
-     * @hide
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this service.
-     *
-     * @hide
-     */
-    protected int mInstanceId;
-
-    /**
-     * Handle counter override (for conformance testing).
-     *
-     * @hide
-     */
-    protected int mHandles = 0;
-
-    /**
-     * Service type (Primary/Secondary).
-     *
-     * @hide
-     */
-    protected int mServiceType;
-
-    /**
-     * List of characteristics included in this service.
-     */
-    protected List<BluetoothGattCharacteristic> mCharacteristics;
-
-    /**
-     * List of included services for this service.
-     */
-    protected List<BluetoothGattService> mIncludedServices;
-
-    /**
-     * Whether the service uuid should be advertised.
-     */
-    private boolean mAdvertisePreferred;
-
-    /**
-     * Create a new BluetoothGattService.
-     *
-     * @param uuid The UUID for this service
-     * @param serviceType The type of this service,
-     * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY}
-     * or {@link BluetoothGattService#SERVICE_TYPE_SECONDARY}
-     */
-    public BluetoothGattService(UUID uuid, int serviceType) {
-        mDevice = null;
-        mUuid = uuid;
-        mInstanceId = 0;
-        mServiceType = serviceType;
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * Create a new BluetoothGattService
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid,
-            int instanceId, int serviceType) {
-        mDevice = device;
-        mUuid = uuid;
-        mInstanceId = instanceId;
-        mServiceType = serviceType;
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * Create a new BluetoothGattService
-     *
-     * @hide
-     */
-    public BluetoothGattService(UUID uuid, int instanceId, int serviceType) {
-        mDevice = null;
-        mUuid = uuid;
-        mInstanceId = instanceId;
-        mServiceType = serviceType;
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * @hide
-     */
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstanceId);
-        out.writeInt(mServiceType);
-        out.writeTypedList(mCharacteristics);
-
-        ArrayList<BluetoothGattIncludedService> includedServices =
-                new ArrayList<BluetoothGattIncludedService>(mIncludedServices.size());
-        for (BluetoothGattService s : mIncludedServices) {
-            includedServices.add(new BluetoothGattIncludedService(s.getUuid(),
-                    s.getInstanceId(), s.getType()));
-        }
-        out.writeTypedList(includedServices);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattService> CREATOR =
-            new Parcelable.Creator<BluetoothGattService>() {
-        public BluetoothGattService createFromParcel(Parcel in) {
-            return new BluetoothGattService(in);
-        }
-
-        public BluetoothGattService[] newArray(int size) {
-            return new BluetoothGattService[size];
-        }
-    };
-
-    private BluetoothGattService(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
-        mInstanceId = in.readInt();
-        mServiceType = in.readInt();
-
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-
-        ArrayList<BluetoothGattCharacteristic> chrcs =
-                in.createTypedArrayList(BluetoothGattCharacteristic.CREATOR);
-        if (chrcs != null) {
-            for (BluetoothGattCharacteristic chrc : chrcs) {
-                chrc.setService(this);
-                mCharacteristics.add(chrc);
-            }
-        }
-
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-
-        ArrayList<BluetoothGattIncludedService> inclSvcs =
-                in.createTypedArrayList(BluetoothGattIncludedService.CREATOR);
-        if (chrcs != null) {
-            for (BluetoothGattIncludedService isvc : inclSvcs) {
-                mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(),
-                        isvc.getInstanceId(), isvc.getType()));
-            }
-        }
-    }
-
-    /**
-     * Returns the device associated with this service.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Returns the device associated with this service.
-     *
-     * @hide
-     */
-    /*package*/ void setDevice(BluetoothDevice device) {
-        mDevice = device;
-    }
-
-    /**
-     * Add an included service to this service.
-     *
-     * @param service The service to be added
-     * @return true, if the included service was added to the service
-     */
-    @RequiresLegacyBluetoothPermission
-    public boolean addService(BluetoothGattService service) {
-        mIncludedServices.add(service);
-        return true;
-    }
-
-    /**
-     * Add a characteristic to this service.
-     *
-     * @param characteristic The characteristics to be added
-     * @return true, if the characteristic was added to the service
-     */
-    @RequiresLegacyBluetoothPermission
-    public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) {
-        mCharacteristics.add(characteristic);
-        characteristic.setService(this);
-        return true;
-    }
-
-    /**
-     * Get characteristic by UUID and instanceId.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) {
-        for (BluetoothGattCharacteristic characteristic : mCharacteristics) {
-            if (uuid.equals(characteristic.getUuid())
-                    && characteristic.getInstanceId() == instanceId) {
-                return characteristic;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Force the instance ID.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void setInstanceId(int instanceId) {
-        mInstanceId = instanceId;
-    }
-
-    /**
-     * Get the handle count override (conformance testing.
-     *
-     * @hide
-     */
-    /*package*/ int getHandles() {
-        return mHandles;
-    }
-
-    /**
-     * Force the number of handles to reserve for this service.
-     * This is needed for conformance testing only.
-     *
-     * @hide
-     */
-    public void setHandles(int handles) {
-        mHandles = handles;
-    }
-
-    /**
-     * Add an included service to the internal map.
-     *
-     * @hide
-     */
-    public void addIncludedService(BluetoothGattService includedService) {
-        mIncludedServices.add(includedService);
-    }
-
-    /**
-     * Returns the UUID of this service
-     *
-     * @return UUID of this service
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this service
-     *
-     * <p>If a remote device offers multiple services with the same UUID
-     * (ex. multiple battery services for different batteries), the instance
-     * ID is used to distuinguish services.
-     *
-     * @return Instance ID of this service
-     */
-    public int getInstanceId() {
-        return mInstanceId;
-    }
-
-    /**
-     * Get the type of this service (primary/secondary)
-     */
-    public int getType() {
-        return mServiceType;
-    }
-
-    /**
-     * Get the list of included GATT services for this service.
-     *
-     * @return List of included services or empty list if no included services were discovered.
-     */
-    public List<BluetoothGattService> getIncludedServices() {
-        return mIncludedServices;
-    }
-
-    /**
-     * Returns a list of characteristics included in this service.
-     *
-     * @return Characteristics included in this service
-     */
-    public List<BluetoothGattCharacteristic> getCharacteristics() {
-        return mCharacteristics;
-    }
-
-    /**
-     * Returns a characteristic with a given UUID out of the list of
-     * characteristics offered by this service.
-     *
-     * <p>This is a convenience function to allow access to a given characteristic
-     * without enumerating over the list returned by {@link #getCharacteristics}
-     * manually.
-     *
-     * <p>If a remote service offers multiple characteristics with the same
-     * UUID, the first instance of a characteristic with the given UUID
-     * is returned.
-     *
-     * @return GATT characteristic object or null if no characteristic with the given UUID was
-     * found.
-     */
-    public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
-        for (BluetoothGattCharacteristic characteristic : mCharacteristics) {
-            if (uuid.equals(characteristic.getUuid())) {
-                return characteristic;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns whether the uuid of the service should be advertised.
-     *
-     * @hide
-     */
-    public boolean isAdvertisePreferred() {
-        return mAdvertisePreferred;
-    }
-
-    /**
-     * Set whether the service uuid should be advertised.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void setAdvertisePreferred(boolean advertisePreferred) {
-        mAdvertisePreferred = advertisePreferred;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
deleted file mode 100644
index f2a6276..0000000
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ /dev/null
@@ -1,1478 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Public API for controlling the Bluetooth Headset Service. This includes both
- * Bluetooth Headset and Handsfree (v1.5) profiles.
- *
- * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
- * Service via IPC.
- *
- * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHeadset proxy object. Use
- * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
- *
- * <p> Android only supports one connected Bluetooth Headset at a time.
- * Each method is protected with its appropriate permission.
- */
-public final class BluetoothHeadset implements BluetoothProfile {
-    private static final String TAG = "BluetoothHeadset";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Headset
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Audio Connection state of the
-     * HFP profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * Intent used to broadcast that the headset has posted a
-     * vendor-specific event.
-     *
-     * <p>This intent will have 4 extras and 1 category.
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
-     * </li>
-     * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
-     * specific command </li>
-     * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
-     * command type which can be one of  {@link #AT_CMD_TYPE_READ},
-     * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
-     * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
-     * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
-     * arguments. </li>
-     * </ul>
-     *
-     * <p> The category is the Company ID of the vendor defining the
-     * vendor-specific command. {@link BluetoothAssignedNumbers}
-     *
-     * For example, for Plantronics specific events
-     * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
-     *
-     * <p> For example, an AT+XEVENT=foo,3 will get translated into
-     * <ul>
-     * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
-     * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
-     * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
-     * </ul>
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
-            "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
-
-    /**
-     * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * intents that contains the name of the vendor-specific command.
-     */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
-
-    /**
-     * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * intents that contains the AT command type of the vendor-specific command.
-     */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
-
-    /**
-     * AT command type READ used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+VGM?. There are no arguments for this command type.
-     */
-    public static final int AT_CMD_TYPE_READ = 0;
-
-    /**
-     * AT command type TEST used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+VGM=?. There are no arguments for this command type.
-     */
-    public static final int AT_CMD_TYPE_TEST = 1;
-
-    /**
-     * AT command type SET used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+VGM=<args>.
-     */
-    public static final int AT_CMD_TYPE_SET = 2;
-
-    /**
-     * AT command type BASIC used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, ATD. Single character commands and everything following the
-     * character are arguments.
-     */
-    public static final int AT_CMD_TYPE_BASIC = 3;
-
-    /**
-     * AT command type ACTION used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+CHUP. There are no arguments for action commands.
-     */
-    public static final int AT_CMD_TYPE_ACTION = 4;
-
-    /**
-     * A Parcelable String array extra field in
-     * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
-     * the arguments to the vendor-specific command.
-     */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
-
-    /**
-     * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * for the companyId
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
-            "android.bluetooth.headset.intent.category.companyid";
-
-    /**
-     * A vendor-specific command for unsolicited result code.
-     */
-    public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
-
-    /**
-     * A vendor-specific AT command
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
-
-    /**
-     * A vendor-specific AT command
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
-
-    /**
-     * Battery level indicator associated with
-     * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
-     *
-     * @hide
-     */
-    public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
-
-    /**
-     * A vendor-specific AT command
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
-
-    /**
-     * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
-
-    /**
-     * Headset state when SCO audio is not connected.
-     * This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
-     */
-    public static final int STATE_AUDIO_DISCONNECTED = 10;
-
-    /**
-     * Headset state when SCO audio is connecting.
-     * This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
-     */
-    public static final int STATE_AUDIO_CONNECTING = 11;
-
-    /**
-     * Headset state when SCO audio is connected.
-     * This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
-     */
-    public static final int STATE_AUDIO_CONNECTED = 12;
-
-    /**
-     * Intent used to broadcast the headset's indicator status
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
-     * is supported by the headset ( as indicated by AT+BIND command in the SLC
-     * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
-     * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
-     * </ul>
-     * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
-     * are given an assigned number. Below shows the assigned number of Indicator added so far
-     * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
-     * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
-            "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
-
-    /**
-     * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
-     * intents that contains the assigned number of the headset indicator as defined by
-     * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
-     *
-     * @hide
-     */
-    public static final String EXTRA_HF_INDICATORS_IND_ID =
-            "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
-
-    /**
-     * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
-     * intents that contains the value of the Headset indicator that is being sent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_HF_INDICATORS_IND_VALUE =
-            "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
-
-    private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
-    private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
-
-    private final CloseGuard mCloseGuard = new CloseGuard();
-
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothHeadset mService;
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        doUnbind();
-                    } else {
-                        doBind();
-                    }
-                }
-            };
-
-    /**
-     * Create a BluetoothHeadset proxy object.
-     */
-    /* package */ BluetoothHeadset(Context context, ServiceListener l, BluetoothAdapter adapter) {
-        mContext = context;
-        mServiceListener = l;
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-
-        // Preserve legacy compatibility where apps were depending on
-        // registerStateChangeCallback() performing a permissions check which
-        // has been relaxed in modern platform versions
-        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
-                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Need BLUETOOTH permission");
-        }
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-        mCloseGuard.open("close");
-    }
-
-    private boolean doBind() {
-        synchronized (mConnection) {
-            if (mService == null) {
-                if (VDBG) Log.d(TAG, "Binding service...");
-                try {
-                    return mAdapter.getBluetoothManager().bindBluetoothProfileService(
-                            BluetoothProfile.HEADSET, mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to bind HeadsetService", e);
-                }
-            }
-        }
-        return false;
-    }
-
-    private void doUnbind() {
-        synchronized (mConnection) {
-            if (mService != null) {
-                if (VDBG) Log.d(TAG, "Unbinding service...");
-                try {
-                    mAdapter.getBluetoothManager().unbindBluetoothProfileService(
-                            BluetoothProfile.HEADSET, mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to unbind HeadsetService", e);
-                } finally {
-                    mService = null;
-                }
-            }
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothHeadset will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     */
-    @UnsupportedAppUsage
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "", re);
-            }
-        }
-        mServiceListener = null;
-        doUnbind();
-        mCloseGuard.close();
-    }
-
-    /** {@hide} */
-    @Override
-    protected void finalize() throws Throwable {
-        mCloseGuard.warnIfOpen();
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> Currently, the system supports only 1 connection to the
-     * headset/handsfree profile. The API will automatically disconnect connected
-     * devices before connecting.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHeadset service = mService;
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHeadset service = mService;
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
-     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Checks whether the headset supports some form of noise reduction
-     *
-     * @param device Bluetooth device
-     * @return true if echo cancellation and/or noise reduction is supported, false otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
-        if (DBG) log("isNoiseReductionSupported()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isNoiseReductionSupported(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Checks whether the headset supports voice recognition
-     *
-     * @param device Bluetooth device
-     * @return true if voice recognition is supported, false otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
-        if (DBG) log("isVoiceRecognitionSupported()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isVoiceRecognitionSupported(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Start Bluetooth voice recognition. This methods sends the voice
-     * recognition AT command to the headset and establishes the
-     * audio connection.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
-     *
-     * <p> {@link #EXTRA_STATE} will transition from
-     * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
-     * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
-     * in case of failure to establish the audio connection.
-     *
-     * @param device Bluetooth headset
-     * @return false if there is no headset connected, or the connected headset doesn't support
-     * voice recognition, or voice recognition is already started, or audio channel is occupied,
-     * or on error, true otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean startVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("startVoiceRecognition()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.startVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Stop Bluetooth Voice Recognition mode, and shut down the
-     * Bluetooth audio path.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
-     *
-     * @param device Bluetooth headset
-     * @return false if there is no headset connected, or voice recognition has not started,
-     * or voice recognition has ended on this headset, or on error, true otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean stopVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("stopVoiceRecognition()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.stopVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if Bluetooth SCO audio is connected.
-     *
-     * @param device Bluetooth headset
-     * @return true if SCO is connected, false otherwise or on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isAudioConnected(BluetoothDevice device) {
-        if (VDBG) log("isAudioConnected()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isAudioConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Indicates if current platform supports voice dialing over bluetooth SCO.
-     *
-     * @return true if voice dialing over bluetooth is supported, false otherwise.
-     * @hide
-     */
-    public static boolean isBluetoothVoiceDialingEnabled(Context context) {
-        return context.getResources().getBoolean(
-                com.android.internal.R.bool.config_bluetooth_sco_off_call);
-    }
-
-    /**
-     * Get the current audio state of the Headset.
-     * Note: This is an internal function and shouldn't be exposed
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getAudioState(BluetoothDevice device) {
-        if (VDBG) log("getAudioState");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (!isDisabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getAudioState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
-     * audio to the HF unless explicitly told to.
-     * This method should be used in cases where the SCO channel is shared between multiple profiles
-     * and must be delegated by a source knowledgeable
-     * Note: This is an internal function and shouldn't be exposed
-     *
-     * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setAudioRouteAllowed(boolean allowed) {
-        if (VDBG) log("setAudioRouteAllowed");
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setAudioRouteAllowed(allowed, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
-     * Note: This is an internal function and shouldn't be exposed
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getAudioRouteAllowed() {
-        if (VDBG) log("getAudioRouteAllowed");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getAudioRouteAllowed(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Force SCO audio to be opened regardless any other restrictions
-     *
-     * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
-     * False to use SCO audio in normal manner
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setForceScoAudio(boolean forced) {
-        if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setForceScoAudio(forced, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Check if at least one headset's SCO audio is connected or connecting
-     *
-     * @return true if at least one device's SCO audio is connected or connecting, false otherwise
-     * or on error
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isAudioOn() {
-        if (VDBG) log("isAudioOn()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isAudioOn(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates a connection of headset audio to the current active device
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
-     *
-     * <p> {@link #EXTRA_STATE} will transition from
-     * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
-     * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
-     * in case of failure to establish the audio connection.
-     *
-     * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
-     * before calling this method
-     *
-     * @return false if there was some error such as there is no active headset
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connectAudio() {
-        if (VDBG) log("connectAudio()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectAudio(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates a disconnection of HFP SCO audio.
-     * Tear down voice recognition or virtual voice call if any.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
-     *
-     * @return false if audio is not connected, or on error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnectAudio() {
-        if (VDBG) log("disconnectAudio()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectAudio(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates a SCO channel connection as a virtual voice call to the current active device
-     * Active handsfree device will be notified of incoming call and connected call.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
-     *
-     * <p> {@link #EXTRA_STATE} will transition from
-     * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
-     * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
-     * in case of failure to establish the audio connection.
-     *
-     * @return true if successful, false if one of the following case applies
-     *  - SCO audio is not idle (connecting or connected)
-     *  - virtual call has already started
-     *  - there is no active device
-     *  - a Telecom managed call is going on
-     *  - binder is dead or Bluetooth is disabled or other error
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean startScoUsingVirtualVoiceCall() {
-        if (DBG) log("startScoUsingVirtualVoiceCall()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.startScoUsingVirtualVoiceCall(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Terminates an ongoing SCO connection and the associated virtual call.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
-     *
-     * @return true if successful, false if one of the following case applies
-     *  - virtual voice call is not started or has ended
-     *  - binder is dead or Bluetooth is disabled or other error
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean stopScoUsingVirtualVoiceCall() {
-        if (DBG) log("stopScoUsingVirtualVoiceCall()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Notify Headset of phone state change.
-     * This is a backdoor for phone app to call BluetoothHeadset since
-     * there is currently not a good way to get precise call state change outside
-     * of phone app.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
-            int type, String name) {
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                service.phoneStateChanged(numActive, numHeld, callState, number, type, name,
-                        mAttributionSource);
-            } catch (RemoteException  e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Send Headset of CLCC response
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
-            String number, int type) {
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.clccResponse(index, direction, status, mode, mpty, number, type,
-                        mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Sends a vendor-specific unsolicited result code to the headset.
-     *
-     * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
-     * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
-     * string <code>"+ANDROID: 0"</code> will be sent.
-     *
-     * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
-     *
-     * @param device Bluetooth headset.
-     * @param command A vendor-specific command.
-     * @param arg The argument that will be attached to the command.
-     * @return {@code false} if there is no headset connected, or if the command is not an allowed
-     * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
-     * @throws IllegalArgumentException if {@code command} is {@code null}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
-            String arg) {
-        if (DBG) {
-            log("sendVendorSpecificResultCode()");
-        }
-        if (command == null) {
-            throw new IllegalArgumentException("command is null");
-        }
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendVendorSpecificResultCode(device, command, arg,
-                        mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, in HFP and HSP profiles,
-     * it is the device used for phone call audio. If a remote device is not
-     * connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     * @param device Remote Bluetooth Device, could be null if phone call audio should not be
-     * streamed to a headset
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) {
-            Log.d(TAG, "setActiveDevice: " + device);
-        }
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && (device == null || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected device that is active.
-     *
-     * @return the connected device that is active or null if no device
-     * is active.
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getActiveDevice() {
-        if (VDBG) Log.d(TAG, "getActiveDevice");
-        final IBluetoothHeadset service = mService;
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevice(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
-     * active connection.
-     *
-     * @return true if in-band ringing is enabled, false if in-band ringing is disabled
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean isInbandRingingEnabled() {
-        if (DBG) log("isInbandRingingEnabled()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isInbandRingingEnabled(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if in-band ringing is supported for this platform.
-     *
-     * @return true if in-band ringing is supported, false if in-band ringing is not supported
-     * @hide
-     */
-    public static boolean isInbandRingingSupported(Context context) {
-        return context.getResources().getBoolean(
-                com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothProfileServiceConnection mConnection =
-            new IBluetoothProfileServiceConnection.Stub() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothHeadset.Stub.asInterface(service);
-            mHandler.sendMessage(mHandler.obtainMessage(
-                    MESSAGE_HEADSET_SERVICE_CONNECTED));
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            doUnbind();
-            mHandler.sendMessage(mHandler.obtainMessage(
-                    MESSAGE_HEADSET_SERVICE_DISCONNECTED));
-        }
-    };
-
-    @UnsupportedAppUsage
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private boolean isDisabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_HEADSET_SERVICE_CONNECTED: {
-                    if (mServiceListener != null) {
-                        mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
-                                BluetoothHeadset.this);
-                    }
-                    break;
-                }
-                case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
-                    if (mServiceListener != null) {
-                        mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
-                    }
-                    break;
-                }
-            }
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
deleted file mode 100644
index 7d7a7f7..0000000
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ /dev/null
@@ -1,1356 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Public API to control Hands Free Profile (HFP role only).
- * <p>
- * This class defines methods that shall be used by application to manage profile
- * connection, calls states and calls actions.
- * <p>
- *
- * @hide
- */
-public final class BluetoothHeadsetClient implements BluetoothProfile {
-    private static final String TAG = "BluetoothHeadsetClient";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent sent whenever connection to remote changes.
-     *
-     * <p>It includes two extras:
-     * <code>BluetoothProfile.EXTRA_PREVIOUS_STATE</code>
-     * and <code>BluetoothProfile.EXTRA_STATE</code>, which
-     * are mandatory.
-     * <p>There are also non mandatory feature extras:
-     * {@link #EXTRA_AG_FEATURE_3WAY_CALLING},
-     * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION},
-     * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT},
-     * {@link #EXTRA_AG_FEATURE_REJECT_CALL},
-     * {@link #EXTRA_AG_FEATURE_ECC},
-     * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD},
-     * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL},
-     * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL},
-     * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT},
-     * {@link #EXTRA_AG_FEATURE_MERGE},
-     * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH},
-     * sent as boolean values only when <code>EXTRA_STATE</code>
-     * is set to <code>STATE_CONNECTED</code>.</p>
-     *
-     * <p>Note that features supported by AG are being sent as
-     * booleans with value <code>true</code>,
-     * and not supported ones are <strong>not</strong> being sent at all.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent sent whenever audio state changes.
-     *
-     * <p>It includes two mandatory extras:
-     * {@link BluetoothProfile#EXTRA_STATE},
-     * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE},
-     * with possible values:
-     * {@link #STATE_AUDIO_CONNECTING},
-     * {@link #STATE_AUDIO_CONNECTED},
-     * {@link #STATE_AUDIO_DISCONNECTED}</p>
-     * <p>When <code>EXTRA_STATE</code> is set
-     * to </code>STATE_AUDIO_CONNECTED</code>,
-     * it also includes {@link #EXTRA_AUDIO_WBS}
-     * indicating wide band speech support.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED";
-
-    /**
-     * Intent sending updates of the Audio Gateway state.
-     * Each extra is being sent only when value it
-     * represents has been changed recently on AG.
-     * <p>It can contain one or more of the following extras:
-     * {@link #EXTRA_NETWORK_STATUS},
-     * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH},
-     * {@link #EXTRA_NETWORK_ROAMING},
-     * {@link #EXTRA_BATTERY_LEVEL},
-     * {@link #EXTRA_OPERATOR_NAME},
-     * {@link #EXTRA_VOICE_RECOGNITION},
-     * {@link #EXTRA_IN_BAND_RING}</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AG_EVENT =
-            "android.bluetooth.headsetclient.profile.action.AG_EVENT";
-
-    /**
-     * Intent sent whenever state of a call changes.
-     *
-     * <p>It includes:
-     * {@link #EXTRA_CALL},
-     * with value of {@link BluetoothHeadsetClientCall} instance,
-     * representing actual call state.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CALL_CHANGED =
-            "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";
-
-    /**
-     * Intent that notifies about the result of the last issued action.
-     * Please note that not every action results in explicit action result code being sent.
-     * Instead other notifications about new Audio Gateway state might be sent,
-     * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value
-     * when for example user started voice recognition from HF unit.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_RESULT =
-            "android.bluetooth.headsetclient.profile.action.RESULT";
-
-    /**
-     * Intent that notifies about vendor specific event arrival. Events not defined in
-     * HFP spec will be matched with supported vendor event list and this intent will
-     * be broadcasted upon a match. Supported vendor events are of format of
-     * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx".
-     * Vendor event can be a response to an vendor specific command or unsolicited.
-     *
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT =
-            "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT";
-
-    /**
-     * Intent that notifies about the number attached to the last voice tag
-     * recorded on AG.
-     *
-     * <p>It contains:
-     * {@link #EXTRA_NUMBER},
-     * with a <code>String</code> value representing phone number.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LAST_VTAG =
-            "android.bluetooth.headsetclient.profile.action.LAST_VTAG";
-
-    public static final int STATE_AUDIO_DISCONNECTED = 0;
-    public static final int STATE_AUDIO_CONNECTING = 1;
-    public static final int STATE_AUDIO_CONNECTED = 2;
-
-    /**
-     * Extra with information if connected audio is WBS.
-     * <p>Possible values: <code>true</code>,
-     * <code>false</code>.</p>
-     */
-    public static final String EXTRA_AUDIO_WBS =
-            "android.bluetooth.headsetclient.extra.AUDIO_WBS";
-
-    /**
-     * Extra for AG_EVENT indicates network status.
-     * <p>Value: 0 - network unavailable,
-     * 1 - network available </p>
-     */
-    public static final String EXTRA_NETWORK_STATUS =
-            "android.bluetooth.headsetclient.extra.NETWORK_STATUS";
-    /**
-     * Extra for AG_EVENT intent indicates network signal strength.
-     * <p>Value: <code>Integer</code> representing signal strength.</p>
-     */
-    public static final String EXTRA_NETWORK_SIGNAL_STRENGTH =
-            "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH";
-    /**
-     * Extra for AG_EVENT intent indicates roaming state.
-     * <p>Value: 0 - no roaming
-     * 1 - active roaming</p>
-     */
-    public static final String EXTRA_NETWORK_ROAMING =
-            "android.bluetooth.headsetclient.extra.NETWORK_ROAMING";
-    /**
-     * Extra for AG_EVENT intent indicates the battery level.
-     * <p>Value: <code>Integer</code> representing signal strength.</p>
-     */
-    public static final String EXTRA_BATTERY_LEVEL =
-            "android.bluetooth.headsetclient.extra.BATTERY_LEVEL";
-    /**
-     * Extra for AG_EVENT intent indicates operator name.
-     * <p>Value: <code>String</code> representing operator name.</p>
-     */
-    public static final String EXTRA_OPERATOR_NAME =
-            "android.bluetooth.headsetclient.extra.OPERATOR_NAME";
-    /**
-     * Extra for AG_EVENT intent indicates voice recognition state.
-     * <p>Value:
-     * 0 - voice recognition stopped,
-     * 1 - voice recognition started.</p>
-     */
-    public static final String EXTRA_VOICE_RECOGNITION =
-            "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION";
-    /**
-     * Extra for AG_EVENT intent indicates in band ring state.
-     * <p>Value:
-     * 0 - in band ring tone not supported, or
-     * 1 - in band ring tone supported.</p>
-     */
-    public static final String EXTRA_IN_BAND_RING =
-            "android.bluetooth.headsetclient.extra.IN_BAND_RING";
-
-    /**
-     * Extra for AG_EVENT intent indicates subscriber info.
-     * <p>Value: <code>String</code> containing subscriber information.</p>
-     */
-    public static final String EXTRA_SUBSCRIBER_INFO =
-            "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO";
-
-    /**
-     * Extra for AG_CALL_CHANGED intent indicates the
-     * {@link BluetoothHeadsetClientCall} object that has changed.
-     */
-    public static final String EXTRA_CALL =
-            "android.bluetooth.headsetclient.extra.CALL";
-
-    /**
-     * Extra for ACTION_LAST_VTAG intent.
-     * <p>Value: <code>String</code> representing phone number
-     * corresponding to last voice tag recorded on AG</p>
-     */
-    public static final String EXTRA_NUMBER =
-            "android.bluetooth.headsetclient.extra.NUMBER";
-
-    /**
-     * Extra for ACTION_RESULT intent that shows the result code of
-     * last issued action.
-     * <p>Possible results:
-     * {@link #ACTION_RESULT_OK},
-     * {@link #ACTION_RESULT_ERROR},
-     * {@link #ACTION_RESULT_ERROR_NO_CARRIER},
-     * {@link #ACTION_RESULT_ERROR_BUSY},
-     * {@link #ACTION_RESULT_ERROR_NO_ANSWER},
-     * {@link #ACTION_RESULT_ERROR_DELAYED},
-     * {@link #ACTION_RESULT_ERROR_BLACKLISTED},
-     * {@link #ACTION_RESULT_ERROR_CME}</p>
-     */
-    public static final String EXTRA_RESULT_CODE =
-            "android.bluetooth.headsetclient.extra.RESULT_CODE";
-
-    /**
-     * Extra for ACTION_RESULT intent that shows the extended result code of
-     * last issued action.
-     * <p>Value: <code>Integer</code> - error code.</p>
-     */
-    public static final String EXTRA_CME_CODE =
-            "android.bluetooth.headsetclient.extra.CME_CODE";
-
-    /**
-     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
-     * indicates vendor ID.
-     */
-    public static final String EXTRA_VENDOR_ID =
-            "android.bluetooth.headsetclient.extra.VENDOR_ID";
-
-     /**
-     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
-     * indicates vendor event code.
-     */
-    public static final String EXTRA_VENDOR_EVENT_CODE =
-            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE";
-
-     /**
-     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
-     * contains full vendor event including event code and full arguments.
-     */
-    public static final String EXTRA_VENDOR_EVENT_FULL_ARGS =
-            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS";
-
-
-    /* Extras for AG_FEATURES, extras type is boolean */
-    // TODO verify if all of those are actually useful
-    /**
-     * AG feature: three way calling.
-     */
-    public static final String EXTRA_AG_FEATURE_3WAY_CALLING =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING";
-    /**
-     * AG feature: voice recognition.
-     */
-    public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION";
-    /**
-     * AG feature: fetching phone number for voice tagging procedure.
-     */
-    public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT";
-    /**
-     * AG feature: ability to reject incoming call.
-     */
-    public static final String EXTRA_AG_FEATURE_REJECT_CALL =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL";
-    /**
-     * AG feature: enhanced call handling (terminate specific call, private consultation).
-     */
-    public static final String EXTRA_AG_FEATURE_ECC =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC";
-    /**
-     * AG feature: response and hold.
-     */
-    public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD";
-    /**
-     * AG call handling feature: accept held or waiting call in three way calling scenarios.
-     */
-    public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL";
-    /**
-     * AG call handling feature: release held or waiting call in three way calling scenarios.
-     */
-    public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL";
-    /**
-     * AG call handling feature: release active call and accept held or waiting call in three way
-     * calling scenarios.
-     */
-    public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT";
-    /**
-     * AG call handling feature: merge two calls, held and active - multi party conference mode.
-     */
-    public static final String EXTRA_AG_FEATURE_MERGE =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE";
-    /**
-     * AG call handling feature: merge calls and disconnect from multi party
-     * conversation leaving peers connected to each other.
-     * Note that this feature needs to be supported by mobile network operator
-     * as it requires connection and billing transfer.
-     */
-    public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH";
-
-    /* Action result codes */
-    public static final int ACTION_RESULT_OK = 0;
-    public static final int ACTION_RESULT_ERROR = 1;
-    public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2;
-    public static final int ACTION_RESULT_ERROR_BUSY = 3;
-    public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4;
-    public static final int ACTION_RESULT_ERROR_DELAYED = 5;
-    public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6;
-    public static final int ACTION_RESULT_ERROR_CME = 7;
-
-    /* Detailed CME error codes */
-    public static final int CME_PHONE_FAILURE = 0;
-    public static final int CME_NO_CONNECTION_TO_PHONE = 1;
-    public static final int CME_OPERATION_NOT_ALLOWED = 3;
-    public static final int CME_OPERATION_NOT_SUPPORTED = 4;
-    public static final int CME_PHSIM_PIN_REQUIRED = 5;
-    public static final int CME_PHFSIM_PIN_REQUIRED = 6;
-    public static final int CME_PHFSIM_PUK_REQUIRED = 7;
-    public static final int CME_SIM_NOT_INSERTED = 10;
-    public static final int CME_SIM_PIN_REQUIRED = 11;
-    public static final int CME_SIM_PUK_REQUIRED = 12;
-    public static final int CME_SIM_FAILURE = 13;
-    public static final int CME_SIM_BUSY = 14;
-    public static final int CME_SIM_WRONG = 15;
-    public static final int CME_INCORRECT_PASSWORD = 16;
-    public static final int CME_SIM_PIN2_REQUIRED = 17;
-    public static final int CME_SIM_PUK2_REQUIRED = 18;
-    public static final int CME_MEMORY_FULL = 20;
-    public static final int CME_INVALID_INDEX = 21;
-    public static final int CME_NOT_FOUND = 22;
-    public static final int CME_MEMORY_FAILURE = 23;
-    public static final int CME_TEXT_STRING_TOO_LONG = 24;
-    public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25;
-    public static final int CME_DIAL_STRING_TOO_LONG = 26;
-    public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27;
-    public static final int CME_NO_NETWORK_SERVICE = 30;
-    public static final int CME_NETWORK_TIMEOUT = 31;
-    public static final int CME_EMERGENCY_SERVICE_ONLY = 32;
-    public static final int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33;
-    public static final int CME_NOT_SUPPORTED_FOR_VOIP = 34;
-    public static final int CME_SIP_RESPONSE_CODE = 35;
-    public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40;
-    public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41;
-    public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42;
-    public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43;
-    public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44;
-    public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45;
-    public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46;
-    public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47;
-    public static final int CME_HIDDEN_KEY_REQUIRED = 48;
-    public static final int CME_EAP_NOT_SUPPORTED = 49;
-    public static final int CME_INCORRECT_PARAMETERS = 50;
-
-    /* Action policy for other calls when accepting call */
-    public static final int CALL_ACCEPT_NONE = 0;
-    public static final int CALL_ACCEPT_HOLD = 1;
-    public static final int CALL_ACCEPT_TERMINATE = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT,
-                    "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
-                @Override
-                public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
-                    return IBluetoothHeadsetClient.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothHeadsetClient proxy object.
-     */
-    /* package */ BluetoothHeadsetClient(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothHeadsetClient will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     */
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHeadsetClient getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Connects to remote device.
-     *
-     * Currently, the system supports only 1 connection. So, in case of the
-     * second connection, this implementation will disconnect already connected
-     * device automatically and will process the new one.
-     *
-     * @param device a remote device we want connect to
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Disconnects remote device
-     *
-     * @param device a remote device we want disconnect
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Return the list of connected remote devices
-     *
-     * @return list of connected devices; empty list if nothing is connected.
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHeadsetClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns list of remote devices in a particular state
-     *
-     * @param states collection of states
-     * @return list of devices that state matches the states listed in <code>states</code>; empty
-     * list if nothing matches the <code>states</code>
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHeadsetClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns state of the <code>device</code>
-     *
-     * @param device a remote device
-     * @return the state of connection of the device
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Starts voice recognition.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
-     * is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean startVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("startVoiceRecognition()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.startVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send vendor specific AT command.
-     *
-     * @param device remote device
-     * @param vendorId vendor number by Bluetooth SIG
-     * @param atCommand command to be sent. It start with + prefix and only one command at one time.
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
-        if (DBG) log("sendVendorSpecificCommand()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Stops voice recognition.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
-     * is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean stopVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("stopVoiceRecognition()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.stopVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns list of all calls in any state.
-     *
-     * @param device remote device
-     * @return list of calls; empty list if none call exists
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
-        if (DBG) log("getCurrentCalls()");
-        final IBluetoothHeadsetClient service = getService();
-        final List<BluetoothHeadsetClientCall> defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothHeadsetClientCall>> recv =
-                        new SynchronousResultReceiver();
-                service.getCurrentCalls(device, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns list of current values of AG indicators.
-     *
-     * @param device remote device
-     * @return bundle of AG  indicators; null if device is not in CONNECTED state
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public Bundle getCurrentAgEvents(BluetoothDevice device) {
-        if (DBG) log("getCurrentAgEvents()");
-        final IBluetoothHeadsetClient service = getService();
-        final Bundle defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
-                service.getCurrentAgEvents(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Accepts a call
-     *
-     * @param device remote device
-     * @param flag action policy while accepting a call. Possible values {@link #CALL_ACCEPT_NONE},
-     * {@link #CALL_ACCEPT_HOLD}, {@link #CALL_ACCEPT_TERMINATE}
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean acceptCall(BluetoothDevice device, int flag) {
-        if (DBG) log("acceptCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.acceptCall(device, flag, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Holds a call.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean holdCall(BluetoothDevice device) {
-        if (DBG) log("holdCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.holdCall(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Rejects a call.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not
-     * supported.</p>
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean rejectCall(BluetoothDevice device) {
-        if (DBG) log("rejectCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.rejectCall(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Terminates a specified call.
-     *
-     * Works only when Extended Call Control is supported by Audio Gateway.
-     *
-     * @param device remote device
-     * @param call Handle of call obtained in {@link #dial(BluetoothDevice, String)} or obtained via
-     * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active
-     * calls.
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
-     * supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
-        if (DBG) log("terminateCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.terminateCall(device, call, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Enters private mode with a specified call.
-     *
-     * Works only when Extended Call Control is supported by Audio Gateway.
-     *
-     * @param device remote device
-     * @param index index of the call to connect in private mode
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
-     * supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enterPrivateMode(BluetoothDevice device, int index) {
-        if (DBG) log("enterPrivateMode()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.enterPrivateMode(device, index, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Performs explicit call transfer.
-     *
-     * That means connect other calls and disconnect.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature
-     * is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean explicitCallTransfer(BluetoothDevice device) {
-        if (DBG) log("explicitCallTransfer()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.explicitCallTransfer(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Places a call with specified number.
-     *
-     * @param device remote device
-     * @param number valid phone number
-     * @return <code>{@link BluetoothHeadsetClientCall} call</code> if command has been issued
-     * successfully; <code>{@link null}</code> otherwise; upon completion HFP sends {@link
-     * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
-        if (DBG) log("dial()");
-        final IBluetoothHeadsetClient service = getService();
-        final BluetoothHeadsetClientCall defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<BluetoothHeadsetClientCall> recv =
-                        new SynchronousResultReceiver();
-                service.dial(device, number, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends DTMF code.
-     *
-     * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,#
-     *
-     * @param device remote device
-     * @param code ASCII code
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendDTMF(BluetoothDevice device, byte code) {
-        if (DBG) log("sendDTMF()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendDTMF(device, code, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get a number corresponding to last voice tag recorded on AG.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_LAST_VTAG} or {@link #ACTION_RESULT}
-     * intent;
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when
-     * feature is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
-        if (DBG) log("getLastVoiceTagNumber()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getLastVoiceTagNumber(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns current audio state of Audio Gateway.
-     *
-     * Note: This is an internal function and shouldn't be exposed
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getAudioState(BluetoothDevice device) {
-        if (VDBG) log("getAudioState");
-        final IBluetoothHeadsetClient service = getService();
-        final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getAudioState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        } else {
-            return defaultValue;
-        }
-        return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
-    }
-
-    /**
-     * Sets whether audio routing is allowed.
-     *
-     * @param device remote device
-     * @param allowed if routing is allowed to the device Note: This is an internal function and
-     * shouldn't be exposed
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
-        if (VDBG) log("setAudioRouteAllowed");
-        final IBluetoothHeadsetClient service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Returns whether audio routing is allowed.
-     *
-     * @param device remote device
-     * @return whether the command succeeded Note: This is an internal function and shouldn't be
-     * exposed
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getAudioRouteAllowed(BluetoothDevice device) {
-        if (VDBG) log("getAudioRouteAllowed");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getAudioRouteAllowed(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates a connection of audio channel.
-     *
-     * It setup SCO channel with remote connected Handsfree AG device.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connectAudio(BluetoothDevice device) {
-        if (VDBG) log("connectAudio");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectAudio(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Disconnects audio channel.
-     *
-     * It tears down the SCO channel from remote AG device.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnectAudio(BluetoothDevice device) {
-        if (VDBG) log("disconnectAudio");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectAudio(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get Audio Gateway features
-     *
-     * @param device remote device
-     * @return bundle of AG features; null if no service or AG not connected
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
-        if (VDBG) log("getCurrentAgFeatures");
-        final IBluetoothHeadsetClient service = getService();
-        final Bundle defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
-                service.getCurrentAgFeatures(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
deleted file mode 100644
index e9dd761..0000000
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-
-import java.util.UUID;
-
-/**
- * This class represents a single call, its state and properties.
- * It implements {@link Parcelable} for inter-process message passing.
- *
- * @hide
- */
-public final class BluetoothHeadsetClientCall implements Parcelable, Attributable {
-
-    /* Call state */
-    /**
-     * Call is active.
-     */
-    public static final int CALL_STATE_ACTIVE = 0;
-    /**
-     * Call is in held state.
-     */
-    public static final int CALL_STATE_HELD = 1;
-    /**
-     * Outgoing call that is being dialed right now.
-     */
-    public static final int CALL_STATE_DIALING = 2;
-    /**
-     * Outgoing call that remote party has already been alerted about.
-     */
-    public static final int CALL_STATE_ALERTING = 3;
-    /**
-     * Incoming call that can be accepted or rejected.
-     */
-    public static final int CALL_STATE_INCOMING = 4;
-    /**
-     * Waiting call state when there is already an active call.
-     */
-    public static final int CALL_STATE_WAITING = 5;
-    /**
-     * Call that has been held by response and hold
-     * (see Bluetooth specification for further references).
-     */
-    public static final int CALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6;
-    /**
-     * Call that has been already terminated and should not be referenced as a valid call.
-     */
-    public static final int CALL_STATE_TERMINATED = 7;
-
-    private final BluetoothDevice mDevice;
-    private final int mId;
-    private int mState;
-    private String mNumber;
-    private boolean mMultiParty;
-    private final boolean mOutgoing;
-    private final UUID mUUID;
-    private final long mCreationElapsedMilli;
-    private final boolean mInBandRing;
-
-    /**
-     * Creates BluetoothHeadsetClientCall instance.
-     */
-    public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number,
-            boolean multiParty, boolean outgoing, boolean inBandRing) {
-        this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing);
-    }
-
-    public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state,
-            String number, boolean multiParty, boolean outgoing, boolean inBandRing) {
-        mDevice = device;
-        mId = id;
-        mUUID = uuid;
-        mState = state;
-        mNumber = number != null ? number : "";
-        mMultiParty = multiParty;
-        mOutgoing = outgoing;
-        mInBandRing = inBandRing;
-        mCreationElapsedMilli = SystemClock.elapsedRealtime();
-    }
-
-    /** {@hide} */
-    public void setAttributionSource(@NonNull AttributionSource attributionSource) {
-        Attributable.setAttributionSource(mDevice, attributionSource);
-    }
-
-    /**
-     * Sets call's state.
-     *
-     * <p>Note: This is an internal function and shouldn't be exposed</p>
-     *
-     * @param state new call state.
-     */
-    public void setState(int state) {
-        mState = state;
-    }
-
-    /**
-     * Sets call's number.
-     *
-     * <p>Note: This is an internal function and shouldn't be exposed</p>
-     *
-     * @param number String representing phone number.
-     */
-    public void setNumber(String number) {
-        mNumber = number;
-    }
-
-    /**
-     * Sets this call as multi party call.
-     *
-     * <p>Note: This is an internal function and shouldn't be exposed</p>
-     *
-     * @param multiParty if <code>true</code> sets this call as a part of multi party conference.
-     */
-    public void setMultiParty(boolean multiParty) {
-        mMultiParty = multiParty;
-    }
-
-    /**
-     * Gets call's device.
-     *
-     * @return call device.
-     */
-    public BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Gets call's Id.
-     *
-     * @return call id.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int getId() {
-        return mId;
-    }
-
-    /**
-     * Gets call's UUID.
-     *
-     * @return call uuid
-     * @hide
-     */
-    public UUID getUUID() {
-        return mUUID;
-    }
-
-    /**
-     * Gets call's current state.
-     *
-     * @return state of this particular phone call.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int getState() {
-        return mState;
-    }
-
-    /**
-     * Gets call's number.
-     *
-     * @return string representing phone number.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public String getNumber() {
-        return mNumber;
-    }
-
-    /**
-     * Gets call's creation time in millis since epoch.
-     *
-     * @return long representing the creation time.
-     */
-    public long getCreationElapsedMilli() {
-        return mCreationElapsedMilli;
-    }
-
-    /**
-     * Checks if call is an active call in a conference mode (aka multi party).
-     *
-     * @return <code>true</code> if call is a multi party call, <code>false</code> otherwise.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean isMultiParty() {
-        return mMultiParty;
-    }
-
-    /**
-     * Checks if this call is an outgoing call.
-     *
-     * @return <code>true</code> if its outgoing call, <code>false</code> otherwise.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean isOutgoing() {
-        return mOutgoing;
-    }
-
-    /**
-     * Checks if the ringtone will be generated by the connected phone
-     *
-     * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise.
-     */
-    public boolean isInBandRing() {
-        return mInBandRing;
-    }
-
-
-    @Override
-    public String toString() {
-        return toString(false);
-    }
-
-    /**
-     * Generate a log string for this call
-     * @param loggable whether device address should be logged
-     * @return log string
-     */
-    public String toString(boolean loggable) {
-        StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: ");
-        builder.append(loggable ? mDevice : mDevice.hashCode());
-        builder.append(", mId: ");
-        builder.append(mId);
-        builder.append(", mUUID: ");
-        builder.append(mUUID);
-        builder.append(", mState: ");
-        switch (mState) {
-            case CALL_STATE_ACTIVE:
-                builder.append("ACTIVE");
-                break;
-            case CALL_STATE_HELD:
-                builder.append("HELD");
-                break;
-            case CALL_STATE_DIALING:
-                builder.append("DIALING");
-                break;
-            case CALL_STATE_ALERTING:
-                builder.append("ALERTING");
-                break;
-            case CALL_STATE_INCOMING:
-                builder.append("INCOMING");
-                break;
-            case CALL_STATE_WAITING:
-                builder.append("WAITING");
-                break;
-            case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
-                builder.append("HELD_BY_RESPONSE_AND_HOLD");
-                break;
-            case CALL_STATE_TERMINATED:
-                builder.append("TERMINATED");
-                break;
-            default:
-                builder.append(mState);
-                break;
-        }
-        builder.append(", mNumber: ");
-        builder.append(loggable ? mNumber : mNumber.hashCode());
-        builder.append(", mMultiParty: ");
-        builder.append(mMultiParty);
-        builder.append(", mOutgoing: ");
-        builder.append(mOutgoing);
-        builder.append(", mInBandRing: ");
-        builder.append(mInBandRing);
-        builder.append("}");
-        return builder.toString();
-    }
-
-    /**
-     * {@link Parcelable.Creator} interface implementation.
-     */
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHeadsetClientCall> CREATOR =
-            new Parcelable.Creator<BluetoothHeadsetClientCall>() {
-                @Override
-                public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
-                    return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null, android.bluetooth.BluetoothDevice.class),
-                            in.readInt(), UUID.fromString(in.readString()), in.readInt(),
-                            in.readString(), in.readInt() == 1, in.readInt() == 1,
-                            in.readInt() == 1);
-                }
-
-                @Override
-                public BluetoothHeadsetClientCall[] newArray(int size) {
-                    return new BluetoothHeadsetClientCall[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mDevice, 0);
-        out.writeInt(mId);
-        out.writeString(mUUID.toString());
-        out.writeInt(mState);
-        out.writeString(mNumber);
-        out.writeInt(mMultiParty ? 1 : 0);
-        out.writeInt(mOutgoing ? 1 : 0);
-        out.writeInt(mInBandRing ? 1 : 0);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
deleted file mode 100644
index 65f68a9..0000000
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Public API for Bluetooth Health Profile.
- *
- * <p>BluetoothHealth is a proxy object for controlling the Bluetooth
- * Service via IPC.
- *
- * <p> How to connect to a health device which is acting in the source role.
- * <li> Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHealth proxy object. </li>
- * <li> Create an {@link BluetoothHealth} callback and call
- * {@link #registerSinkAppConfiguration} to register an application
- * configuration </li>
- * <li> Pair with the remote device. This currently needs to be done manually
- * from Bluetooth Settings </li>
- * <li> Connect to a health device using {@link #connectChannelToSource}. Some
- * devices will connect the channel automatically. The {@link BluetoothHealth}
- * callback will inform the application of channel state change. </li>
- * <li> Use the file descriptor provided with a connected channel to read and
- * write data to the health channel. </li>
- * <li> The received data needs to be interpreted using a health manager which
- * implements the IEEE 11073-xxxxx specifications.
- * <li> When done, close the health channel by calling {@link #disconnectChannel}
- * and unregister the application configuration calling
- * {@link #unregisterAppConfiguration}
- *
- * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps
- * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
- * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
- * {@link BluetoothDevice#createL2capChannel(int)}
- */
-@Deprecated
-public final class BluetoothHealth implements BluetoothProfile {
-    private static final String TAG = "BluetoothHealth";
-    /**
-     * Health Profile Source Role - the health device.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int SOURCE_ROLE = 1 << 0;
-
-    /**
-     * Health Profile Sink Role the device talking to the health device.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int SINK_ROLE = 1 << 1;
-
-    /**
-     * Health Profile - Channel Type used - Reliable
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int CHANNEL_TYPE_RELIABLE = 10;
-
-    /**
-     * Health Profile - Channel Type used - Streaming
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int CHANNEL_TYPE_STREAMING = 11;
-
-    /**
-     * Hide auto-created default constructor
-     * @hide
-     */
-    BluetoothHealth() {}
-
-    /**
-     * Register an application configuration that acts as a Health SINK.
-     * This is the configuration that will be used to communicate with health devices
-     * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
-     * the callback is used to notify success or failure if the function returns true.
-     *
-     * @param name The friendly name associated with the application or configuration.
-     * @param dataType The dataType of the Source role of Health Profile to which the sink wants to
-     * connect to.
-     * @param callback A callback to indicate success or failure of the registration and all
-     * operations done on this application configuration.
-     * @return If true, callback will be called.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean registerSinkAppConfiguration(String name, int dataType,
-            BluetoothHealthCallback callback) {
-        Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Unregister an application configuration that has been registered using
-     * {@link #registerSinkAppConfiguration}
-     *
-     * @param config The health app configuration
-     * @return Success or failure.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
-        Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Connect to a health device which has the {@link #SOURCE_ROLE}.
-     * This is an asynchronous call. If this function returns true, the callback
-     * associated with the application configuration will be called.
-     *
-     * @param device The remote Bluetooth device.
-     * @param config The application configuration which has been registered using {@link
-     * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
-     * @return If true, the callback associated with the application config will be called.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean connectChannelToSource(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config) {
-        Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Disconnect a connected health channel.
-     * This is an asynchronous call. If this function returns true, the callback
-     * associated with the application configuration will be called.
-     *
-     * @param device The remote Bluetooth device.
-     * @param config The application configuration which has been registered using {@link
-     * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
-     * @param channelId The channel id associated with the channel
-     * @return If true, the callback associated with the application config will be called.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean disconnectChannel(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config, int channelId) {
-        Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Get the file descriptor of the main channel associated with the remote device
-     * and application configuration.
-     *
-     * <p> Its the responsibility of the caller to close the ParcelFileDescriptor
-     * when done.
-     *
-     * @param device The remote Bluetooth health device
-     * @param config The application configuration
-     * @return null on failure, ParcelFileDescriptor on success.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config) {
-        Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
-        return null;
-    }
-
-    /**
-     * Get the current connection state of the profile.
-     *
-     * This is not specific to any application configuration but represents the connection
-     * state of the local Bluetooth adapter with the remote device. This can be used
-     * by applications like status bar which would just like to know the state of the
-     * local adapter.
-     *
-     * @param device Remote bluetooth device.
-     * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public int getConnectionState(BluetoothDevice device) {
-        Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
-        return STATE_DISCONNECTED;
-    }
-
-    /**
-     * Get connected devices for the health profile.
-     *
-     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
-     *
-     * This is not specific to any application configuration but represents the connection
-     * state of the local Bluetooth adapter for this profile. This can be used
-     * by applications like status bar which would just like to know the state of the
-     * local adapter.
-     *
-     * @return List of devices. The list will be empty on error.
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public List<BluetoothDevice> getConnectedDevices() {
-        Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
-        return new ArrayList<>();
-    }
-
-    /**
-     * Get a list of devices that match any of the given connection
-     * states.
-     *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * <p>This is not specific to any application configuration but represents the connection
-     * state of the local Bluetooth adapter for this profile. This can be used
-     * by applications like status bar which would just like to know the state of the
-     * local adapter.
-     *
-     * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
-        return new ArrayList<>();
-    }
-
-    /** Health Channel Connection State - Disconnected
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_DISCONNECTED = 0;
-    /** Health Channel Connection State - Connecting
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_CONNECTING = 1;
-    /** Health Channel Connection State - Connected
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_CONNECTED = 2;
-    /** Health Channel Connection State - Disconnecting
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_DISCONNECTING = 3;
-
-    /** Health App Configuration registration success
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
-    /** Health App Configuration registration failure
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
-    /** Health App Configuration un-registration success
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
-    /** Health App Configuration un-registration failure
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
-}
diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
deleted file mode 100644
index 2f66df2..0000000
--- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The Bluetooth Health Application Configuration that is used in conjunction with
- * the {@link BluetoothHealth} class. This class represents an application configuration
- * that the Bluetooth Health third party application will register to communicate with the
- * remote Bluetooth health device.
- *
- * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
- * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
- * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
- * {@link BluetoothDevice#createL2capChannel(int)}
- */
-@Deprecated
-public final class BluetoothHealthAppConfiguration implements Parcelable {
-
-    /**
-     * Hide auto-created default constructor
-     * @hide
-     */
-    BluetoothHealthAppConfiguration() {}
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Return the data type associated with this application configuration.
-     *
-     * @return dataType
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public int getDataType() {
-        return 0;
-    }
-
-    /**
-     * Return the name of the application configuration.
-     *
-     * @return String name
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public String getName() {
-        return null;
-    }
-
-    /**
-     * Return the role associated with this application configuration.
-     *
-     * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public int getRole() {
-        return 0;
-    }
-
-    /**
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR =
-            new Parcelable.Creator<BluetoothHealthAppConfiguration>() {
-                @Override
-                public BluetoothHealthAppConfiguration createFromParcel(Parcel in) {
-                    return new BluetoothHealthAppConfiguration();
-                }
-
-                @Override
-                public BluetoothHealthAppConfiguration[] newArray(int size) {
-                    return new BluetoothHealthAppConfiguration[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {}
-}
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
deleted file mode 100644
index 4769212..0000000
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.BinderThread;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-/**
- * This abstract class is used to implement {@link BluetoothHealth} callbacks.
- *
- * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
- * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
- * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
- * {@link BluetoothDevice#createL2capChannel(int)}
- */
-@Deprecated
-public abstract class BluetoothHealthCallback {
-    private static final String TAG = "BluetoothHealthCallback";
-
-    /**
-     * Callback to inform change in registration state of the health
-     * application.
-     * <p> This callback is called on the binder thread (not on the UI thread)
-     *
-     * @param config Bluetooth Health app configuration
-     * @param status Success or failure of the registration or unregistration calls. Can be one of
-     * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or {@link
-     * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or
-     * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS}
-     * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @BinderThread
-    @Deprecated
-    public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
-            int status) {
-        Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
-    }
-
-    /**
-     * Callback to inform change in channel state.
-     * <p> Its the responsibility of the implementor of this callback to close the
-     * parcel file descriptor when done. This callback is called on the Binder
-     * thread (not the UI thread)
-     *
-     * @param config The Health app configutation
-     * @param device The Bluetooth Device
-     * @param prevState The previous state of the channel
-     * @param newState The new state of the channel.
-     * @param fd The Parcel File Descriptor when the channel state is connected.
-     * @param channelId The id associated with the channel. This id will be used in future calls
-     * like when disconnecting the channel.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @BinderThread
-    @Deprecated
-    public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
-            BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
-            int channelId) {
-        Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device
-                + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd
-                + "ChannelId:" + channelId);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
deleted file mode 100644
index 339a75f..0000000
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ /dev/null
@@ -1,691 +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 android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Hearing Aid profile.
- *
- * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHearingAid proxy object.
- *
- * <p> Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each
- * method is protected with its appropriate permission.
- */
-public final class BluetoothHearingAid implements BluetoothProfile {
-    private static final String TAG = "BluetoothHearingAid";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Hearing Aid
-     * profile. Please note that in the binaural case, there will be two different LE devices for
-     * the left and right side and each device will have their own connection state changes.S
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * This device represents Left Hearing Aid.
-     *
-     * @hide
-     */
-    public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
-
-    /**
-     * This device represents Right Hearing Aid.
-     *
-     * @hide
-     */
-    public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
-
-    /**
-     * This device is Monaural.
-     *
-     * @hide
-     */
-    public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
-
-    /**
-     * This device is Binaural (should receive only left or right audio).
-     *
-     * @hide
-     */
-    public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
-
-    /**
-     * Indicates the HiSyncID could not be read and is unavailable.
-     *
-     * @hide
-     */
-    public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
-                    "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
-                @Override
-                public IBluetoothHearingAid getServiceInterface(IBinder service) {
-                    return IBluetoothHearingAid.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothHearingAid proxy object for interacting with the local
-     * Bluetooth Hearing Aid service.
-     */
-    /* package */ BluetoothHearingAid(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHearingAid getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHearingAid service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
-    @NonNull int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHearingAid service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @BluetoothProfile.BtProfileState int getConnectionState(
-    @NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, Hearing Aid audio
-     * streaming is to the active Hearing Aid device. If a remote device
-     * is not connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     * @param device the remote Bluetooth device. Could be null to clear
-     * the active device and stop streaming audio to a Bluetooth device.
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) log("setActiveDevice(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected physical Hearing Aid devices that are active
-     *
-     * @return the list of active devices. The first element is the left active
-     * device; the second element is the right active device. If either or both side
-     * is not active, it will be null on that position. Returns empty list on error.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getActiveDevices() {
-        if (VDBG) log("getActiveDevices()");
-        final IBluetoothHearingAid service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        verifyDeviceNotNull(device, "setConnectionPolicy");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        verifyDeviceNotNull(device, "getConnectionPolicy");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    /**
-     * Tells remote device to set an absolute volume.
-     *
-     * @param volume Absolute volume to be set on remote
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setVolume(int volume) {
-        if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
-        final IBluetoothHearingAid service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setVolume(volume, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Get the HiSyncId (unique hearing aid device identifier) of the device.
-     *
-     * <a href=https://source.android.com/devices/bluetooth/asha#hisyncid>HiSyncId documentation
-     * can be found here</a>
-     *
-     * @param device Bluetooth device
-     * @return the HiSyncId of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public long getHiSyncId(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getHiSyncId(" + device + ")");
-        verifyDeviceNotNull(device, "getConnectionPolicy");
-        final IBluetoothHearingAid service = getService();
-        final long defaultValue = HI_SYNC_ID_INVALID;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver();
-                service.getHiSyncId(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the side of the device.
-     *
-     * @param device Bluetooth device.
-     * @return SIDE_LEFT or SIDE_RIGHT
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getDeviceSide(BluetoothDevice device) {
-        if (VDBG) log("getDeviceSide(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = SIDE_LEFT;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getDeviceSide(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the mode of the device.
-     *
-     * @param device Bluetooth device
-     * @return MODE_MONAURAL or MODE_BINAURAL
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getDeviceMode(BluetoothDevice device) {
-        if (VDBG) log("getDeviceMode(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = MODE_MONAURAL;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getDeviceMode(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
-        if (device == null) {
-            Log.e(TAG, methodName + ": device param is null");
-            throw new IllegalArgumentException("Device cannot be null");
-        }
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
deleted file mode 100644
index 44a355b..0000000
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Provides the public APIs to control the Bluetooth HID Device profile.
- *
- * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC.
- * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
- */
-public final class BluetoothHidDevice implements BluetoothProfile {
-    private static final String TAG = BluetoothHidDevice.class.getSimpleName();
-    private static final boolean DBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Input Host profile.
-     *
-     * <p>This intent will have 3 extras:
-     *
-     * <ul>
-     *   <li>{@link #EXTRA_STATE} - The current state of the profile.
-     *   <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
-     *   <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link
-     * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link
-     * #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Constant representing unspecified HID device subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_NONE = (byte) 0x00;
-    /**
-     * Constant representing keyboard subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
-    /**
-     * Constant representing mouse subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_MOUSE = (byte) 0x80;
-    /**
-     * Constant representing combo keyboard and mouse subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_COMBO = (byte) 0xC0;
-
-    /**
-     * Constant representing uncategorized HID device subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00;
-    /**
-     * Constant representing joystick subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01;
-    /**
-     * Constant representing gamepad subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
-    /**
-     * Constant representing remote control subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
-    /**
-     * Constant representing sensing device subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
-    /**
-     * Constant representing digitizer tablet subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05;
-    /**
-     * Constant representing card reader subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
-
-    /**
-     * Constant representing HID Input Report type.
-     *
-     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
-     */
-    public static final byte REPORT_TYPE_INPUT = (byte) 1;
-    /**
-     * Constant representing HID Output Report type.
-     *
-     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
-     */
-    public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
-    /**
-     * Constant representing HID Feature Report type.
-     *
-     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
-     */
-    public static final byte REPORT_TYPE_FEATURE = (byte) 3;
-
-    /**
-     * Constant representing success response for Set Report.
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_SUCCESS = (byte) 0;
-    /**
-     * Constant representing error response for Set Report due to "not ready".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_NOT_READY = (byte) 1;
-    /**
-     * Constant representing error response for Set Report due to "invalid report ID".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2;
-    /**
-     * Constant representing error response for Set Report due to "unsupported request".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3;
-    /**
-     * Constant representing error response for Set Report due to "invalid parameter".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4;
-    /**
-     * Constant representing error response for Set Report with unknown reason.
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
-
-    /**
-     * Constant representing boot protocol mode used set by host. Default is always {@link
-     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
-     *
-     * @see Callback#onSetProtocol(BluetoothDevice, byte)
-     */
-    public static final byte PROTOCOL_BOOT_MODE = (byte) 0;
-    /**
-     * Constant representing report protocol mode used set by host. Default is always {@link
-     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
-     *
-     * @see Callback#onSetProtocol(BluetoothDevice, byte)
-     */
-    public static final byte PROTOCOL_REPORT_MODE = (byte) 1;
-
-    /**
-     * The template class that applications use to call callback functions on events from the HID
-     * host. Callback functions are wrapped in this class and registered to the Android system
-     * during app registration.
-     */
-    public abstract static class Callback {
-
-        private static final String TAG = "BluetoothHidDevCallback";
-
-        /**
-         * Callback called when application registration state changes. Usually it's called due to
-         * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
-         * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
-         * unsolicited in case e.g. Bluetooth was turned off in which case application is
-         * unregistered automatically.
-         *
-         * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently
-         *     has Virtual Cable established with device. Only valid when application is registered,
-         *     can be <code>null</code>.
-         * @param registered <code>true</code> if application is registered, <code>false</code>
-         *     otherwise.
-         */
-        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            Log.d(
-                    TAG,
-                    "onAppStatusChanged: pluggedDevice="
-                            + pluggedDevice
-                            + " registered="
-                            + registered);
-        }
-
-        /**
-         * Callback called when connection state with remote host was changed. Application can
-         * assume than Virtual Cable is established when called with {@link
-         * BluetoothProfile#STATE_CONNECTED} <code>state</code>.
-         *
-         * @param device {@link BluetoothDevice} object representing host device which connection
-         *     state was changed.
-         * @param state Connection state as defined in {@link BluetoothProfile}.
-         */
-        public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
-        }
-
-        /**
-         * Callback called when GET_REPORT is received from remote host. Should be replied by
-         * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
-         * byte[])}.
-         *
-         * @param type Requested Report Type.
-         * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
-         * @param bufferSize Requested buffer size, application shall respond with at least given
-         *     number of bytes.
-         */
-        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            Log.d(
-                    TAG,
-                    "onGetReport: device="
-                            + device
-                            + " type="
-                            + type
-                            + " id="
-                            + id
-                            + " bufferSize="
-                            + bufferSize);
-        }
-
-        /**
-         * Callback called when SET_REPORT is received from remote host. In case received data are
-         * invalid, application shall respond with {@link
-         * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
-         *
-         * @param type Report Type.
-         * @param id Report Id.
-         * @param data Report data.
-         */
-        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
-        }
-
-        /**
-         * Callback called when SET_PROTOCOL is received from remote host. Application shall use
-         * this information to send only reports valid for given protocol mode. By default, {@link
-         * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
-         *
-         * @param protocol Protocol Mode.
-         */
-        public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
-        }
-
-        /**
-         * Callback called when report data is received over interrupt channel. Report Type is
-         * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
-         *
-         * @param reportId Report Id.
-         * @param data Report data.
-         */
-        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
-        }
-
-        /**
-         * Callback called when Virtual Cable is removed. After this callback is received connection
-         * will be disconnected automatically.
-         */
-        public void onVirtualCableUnplug(BluetoothDevice device) {
-            Log.d(TAG, "onVirtualCableUnplug: device=" + device);
-        }
-    }
-
-    private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
-
-        private final Executor mExecutor;
-        private final Callback mCallback;
-        private final AttributionSource mAttributionSource;
-
-        CallbackWrapper(Executor executor, Callback callback, AttributionSource attributionSource) {
-            mExecutor = executor;
-            mCallback = callback;
-            mAttributionSource = attributionSource;
-        }
-
-        @Override
-        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            Attributable.setAttributionSource(pluggedDevice, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onVirtualCableUnplug(BluetoothDevice device) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-    }
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE,
-                    "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
-                @Override
-                public IBluetoothHidDevice getServiceInterface(IBinder service) {
-                    return IBluetoothHidDevice.Stub.asInterface(service);
-                }
-    };
-
-    BluetoothHidDevice(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHidDevice getService() {
-        return mProfileConnector.getService();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        final IBluetoothHidDevice service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        final IBluetoothHidDevice service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        final IBluetoothHidDevice service = getService();
-        final int defaultValue = STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Registers application to be used for HID device. Connections to HID Device are only possible
-     * when application is registered. Only one application can be registered at one time. When an
-     * application is registered, the HID Host service will be disabled until it is unregistered.
-     * When no longer used, application should be unregistered using {@link #unregisterApp()}. The
-     * app will be automatically unregistered if it is not foreground. The registration status
-     * should be tracked by the application by handling callback from Callback#onAppStatusChanged.
-     * The app registration status is not related to the return value of this method.
-     *
-     * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID
-     *     Device SDP record is required.
-     * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The
-     *     Incoming QoS Settings is not required. Use null or default
-     *     BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The
-     *     Outgoing QoS Settings is not required. Use null or default
-     *     BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param executor {@link Executor} object on which callback will be executed. The Executor
-     *     object is required.
-     * @param callback {@link Callback} object to which callback messages will be sent. The Callback
-     *     object is required.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean registerApp(
-            BluetoothHidDeviceAppSdpSettings sdp,
-            BluetoothHidDeviceAppQosSettings inQos,
-            BluetoothHidDeviceAppQosSettings outQos,
-            Executor executor,
-            Callback callback) {
-        boolean result = false;
-
-        if (sdp == null) {
-            throw new IllegalArgumentException("sdp parameter cannot be null");
-        }
-
-        if (executor == null) {
-            throw new IllegalArgumentException("executor parameter cannot be null");
-        }
-
-        if (callback == null) {
-            throw new IllegalArgumentException("callback parameter cannot be null");
-        }
-
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = result;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
-                service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv);
-                result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Unregisters application. Active connection will be disconnected and no new connections will
-     * be allowed until registered again using {@link #registerApp
-     * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be
-     * tracked by the application by handling callback from Callback#onAppStatusChanged. The app
-     * registration status is not related to the return value of this method.
-     *
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean unregisterApp() {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.unregisterApp(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends report to remote host using interrupt channel.
-     *
-     * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in
-     *     descriptor.
-     * @param data Report data, not including Report Id.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendReport(device, id, data, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends report to remote host as reply for GET_REPORT request from {@link
-     * Callback#onGetReport(BluetoothDevice, byte, byte, int)}.
-     *
-     * @param type Report Type, as in request.
-     * @param id Report Id, as in request.
-     * @param data Report data, not including Report Id.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.replyReport(device, type, id, data, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends error handshake message as reply for invalid SET_REPORT request from {@link
-     * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
-     *
-     * @param error Error to be sent for SET_REPORT via HANDSHAKE.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean reportError(BluetoothDevice device, byte error) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.reportError(device, error, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Gets the application name of the current HidDeviceService user.
-     *
-     * @return the current user name, or empty string if cannot get the name
-     * {@hide}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getUserAppName() {
-        final IBluetoothHidDevice service = getService();
-        final String defaultValue = "";
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver();
-                service.getUserAppName(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates connection to host which is currently paired with this device. If the application
-     * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
-     * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
-     * connection state is not related to the return value of this method.
-     *
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(BluetoothDevice device) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Disconnects from currently connected host. The connection state should be tracked by the
-     * application by handling callback from Callback#onConnectionStateChanged. The connection state
-     * is not related to the return value of this method.
-     *
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}
-     * and disconnects Hid device if connectionPolicy is
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}.
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of:
-     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
-     * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy determines whether hid device should be connected or disconnected
-     * @return true if hid device is connected or disconnected, false otherwise
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        if (DBG) {
-            Log.d(TAG, msg);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
deleted file mode 100644
index b21ebe5..0000000
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device application.
- *
- * <p>The BluetoothHidDevice framework will update the L2CAP QoS settings for the app during
- * registration.
- *
- * <p>{@see BluetoothHidDevice}
- */
-public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
-
-    private final int mServiceType;
-    private final int mTokenRate;
-    private final int mTokenBucketSize;
-    private final int mPeakBandwidth;
-    private final int mLatency;
-    private final int mDelayVariation;
-
-    public static final int SERVICE_NO_TRAFFIC = 0x00;
-    public static final int SERVICE_BEST_EFFORT = 0x01;
-    public static final int SERVICE_GUARANTEED = 0x02;
-
-    public static final int MAX = (int) 0xffffffff;
-
-    /**
-     * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
-     * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and
-     * Appendix D for parameters.
-     *
-     * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT
-     * @param tokenRate L2CAP token rate, default = 0
-     * @param tokenBucketSize L2CAP token bucket size, default = 0
-     * @param peakBandwidth L2CAP peak bandwidth, default = 0
-     * @param latency L2CAP latency, default = MAX
-     * @param delayVariation L2CAP delay variation, default = MAX
-     */
-    public BluetoothHidDeviceAppQosSettings(
-            int serviceType,
-            int tokenRate,
-            int tokenBucketSize,
-            int peakBandwidth,
-            int latency,
-            int delayVariation) {
-        mServiceType = serviceType;
-        mTokenRate = tokenRate;
-        mTokenBucketSize = tokenBucketSize;
-        mPeakBandwidth = peakBandwidth;
-        mLatency = latency;
-        mDelayVariation = delayVariation;
-    }
-
-    public int getServiceType() {
-        return mServiceType;
-    }
-
-    public int getTokenRate() {
-        return mTokenRate;
-    }
-
-    public int getTokenBucketSize() {
-        return mTokenBucketSize;
-    }
-
-    public int getPeakBandwidth() {
-        return mPeakBandwidth;
-    }
-
-    public int getLatency() {
-        return mLatency;
-    }
-
-    public int getDelayVariation() {
-        return mDelayVariation;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHidDeviceAppQosSettings> CREATOR =
-            new Parcelable.Creator<BluetoothHidDeviceAppQosSettings>() {
-
-                @Override
-                public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) {
-
-                    return new BluetoothHidDeviceAppQosSettings(
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt());
-                }
-
-                @Override
-                public BluetoothHidDeviceAppQosSettings[] newArray(int size) {
-                    return new BluetoothHidDeviceAppQosSettings[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mServiceType);
-        out.writeInt(mTokenRate);
-        out.writeInt(mTokenBucketSize);
-        out.writeInt(mPeakBandwidth);
-        out.writeInt(mLatency);
-        out.writeInt(mDelayVariation);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
deleted file mode 100644
index 4e1a2aa..0000000
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.EventLog;
-
-
-/**
- * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application.
- *
- * <p>The BluetoothHidDevice framework adds the SDP record during app registration, so that the
- * Android device can be discovered as a Bluetooth HID Device.
- *
- * <p>{@see BluetoothHidDevice}
- */
-public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
-
-    private static final int MAX_DESCRIPTOR_SIZE = 2048;
-
-    private final String mName;
-    private final String mDescription;
-    private final String mProvider;
-    private final byte mSubclass;
-    private final byte[] mDescriptors;
-
-    /**
-     * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
-     *
-     * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param subclass Subclass of this Bluetooth HID device. See <a
-     *     href="www.usb.org/developers/hidpage/HID1_11.pdf">
-     *     www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a>
-     * @param descriptors Descriptors of this Bluetooth HID device. See <a
-     *     href="www.usb.org/developers/hidpage/HID1_11.pdf">
-     *     www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes.
-     */
-    public BluetoothHidDeviceAppSdpSettings(
-            String name, String description, String provider, byte subclass, byte[] descriptors) {
-        mName = name;
-        mDescription = description;
-        mProvider = provider;
-        mSubclass = subclass;
-
-        if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) {
-            EventLog.writeEvent(0x534e4554, "119819889", -1, "");
-            throw new IllegalArgumentException("descriptors must be not null and shorter than "
-                    + MAX_DESCRIPTOR_SIZE);
-        }
-        mDescriptors = descriptors.clone();
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public String getDescription() {
-        return mDescription;
-    }
-
-    public String getProvider() {
-        return mProvider;
-    }
-
-    public byte getSubclass() {
-        return mSubclass;
-    }
-
-    public byte[] getDescriptors() {
-        return mDescriptors;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHidDeviceAppSdpSettings> CREATOR =
-            new Parcelable.Creator<BluetoothHidDeviceAppSdpSettings>() {
-
-                @Override
-                public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) {
-
-                    return new BluetoothHidDeviceAppSdpSettings(
-                            in.readString(),
-                            in.readString(),
-                            in.readString(),
-                            in.readByte(),
-                            in.createByteArray());
-                }
-
-                @Override
-                public BluetoothHidDeviceAppSdpSettings[] newArray(int size) {
-                    return new BluetoothHidDeviceAppSdpSettings[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mName);
-        out.writeString(mDescription);
-        out.writeString(mProvider);
-        out.writeByte(mSubclass);
-        out.writeByteArray(mDescriptors);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
deleted file mode 100644
index ecbeddf..0000000
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ /dev/null
@@ -1,831 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-
-/**
- * This class provides the public APIs to control the Bluetooth Input
- * Device Profile.
- *
- * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHidHost proxy object.
- *
- * <p>Each method is protected with its appropriate permission.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothHidHost implements BluetoothProfile {
-    private static final String TAG = "BluetoothHidHost";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Input
-     * Device profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @SuppressLint("ActionValue")
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PROTOCOL_MODE_CHANGED =
-            "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_HANDSHAKE =
-            "android.bluetooth.input.profile.action.HANDSHAKE";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_REPORT =
-            "android.bluetooth.input.profile.action.REPORT";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_VIRTUAL_UNPLUG_STATUS =
-            "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_IDLE_TIME_CHANGED =
-            "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED";
-
-    /**
-     * Return codes for the connect and disconnect Bluez / Dbus calls.
-     *
-     * @hide
-     */
-    public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_OPERATION_SUCCESS = 5004;
-
-    /**
-     * @hide
-     */
-    public static final int PROTOCOL_REPORT_MODE = 0;
-
-    /**
-     * @hide
-     */
-    public static final int PROTOCOL_BOOT_MODE = 1;
-
-    /**
-     * @hide
-     */
-    public static final int PROTOCOL_UNSUPPORTED_MODE = 255;
-
-    /*  int reportType, int reportType, int bufferSize */
-    /**
-     * @hide
-     */
-    public static final byte REPORT_TYPE_INPUT = 1;
-
-    /**
-     * @hide
-     */
-    public static final byte REPORT_TYPE_OUTPUT = 2;
-
-    /**
-     * @hide
-     */
-    public static final byte REPORT_TYPE_FEATURE = 3;
-
-    /**
-     * @hide
-     */
-    public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0;
-
-    /**
-     * @hide
-     */
-    public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1;
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_PROTOCOL_MODE =
-            "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT_TYPE =
-            "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT_ID =
-            "android.bluetooth.BluetoothHidHost.extra.REPORT_ID";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT_BUFFER_SIZE =
-            "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_VIRTUAL_UNPLUG_STATUS =
-            "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_IDLE_TIME =
-            "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST,
-                    "BluetoothHidHost", IBluetoothHidHost.class.getName()) {
-                @Override
-                public IBluetoothHidHost getServiceInterface(IBinder service) {
-                    return IBluetoothHidHost.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothHidHost proxy object for interacting with the local
-     * Bluetooth Service which handles the InputDevice profile
-     */
-    /* package */ BluetoothHidHost(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHidHost getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> The system supports connection to multiple input devices.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHidHost service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHidHost service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        final IBluetoothHidHost service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        final IBluetoothHidHost service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    /**
-     * Initiate virtual unplug for a HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean virtualUnplug(BluetoothDevice device) {
-        if (DBG) log("virtualUnplug(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.virtualUnplug(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Get_Protocol_Mode command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getProtocolMode(BluetoothDevice device) {
-        if (VDBG) log("getProtocolMode(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getProtocolMode(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Set_Protocol_Mode command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
-        if (DBG) log("setProtocolMode(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setProtocolMode(device, protocolMode, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Get_Report command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param reportType Report type
-     * @param reportId Report ID
-     * @param bufferSize Report receiving buffer size
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
-            int bufferSize) {
-        if (VDBG) {
-            log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
-                    + "bufferSize=" + bufferSize);
-        }
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getReport(device, reportType, reportId, bufferSize, mAttributionSource,
-                        recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Set_Report command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param reportType Report type
-     * @param report Report receiving buffer size
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setReport(BluetoothDevice device, byte reportType, String report) {
-        if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setReport(device, reportType, report, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Send_Data command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param report Report to send
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendData(BluetoothDevice device, String report) {
-        if (DBG) log("sendData(" + device + "), report=" + report);
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendData(device, report, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Get_Idle_Time command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getIdleTime(BluetoothDevice device) {
-        if (DBG) log("getIdletime(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getIdleTime(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Set_Idle_Time command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param idleTime Idle time to be set on HID Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
-        if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setIdleTime(device, idleTime, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
deleted file mode 100644
index 95f9229..0000000
--- a/core/java/android/bluetooth/BluetoothInputStream.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.SuppressLint;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * BluetoothInputStream.
- *
- * Used to write to a Bluetooth socket.
- *
- * @hide
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-/*package*/ final class BluetoothInputStream extends InputStream {
-    private BluetoothSocket mSocket;
-
-    /*package*/ BluetoothInputStream(BluetoothSocket s) {
-        mSocket = s;
-    }
-
-    /**
-     * Return number of bytes available before this stream will block.
-     */
-    public int available() throws IOException {
-        return mSocket.available();
-    }
-
-    public void close() throws IOException {
-        mSocket.close();
-    }
-
-    /**
-     * Reads a single byte from this stream and returns it as an integer in the
-     * range from 0 to 255. Returns -1 if the end of the stream has been
-     * reached. Blocks until one byte has been read, the end of the source
-     * stream is detected or an exception is thrown.
-     *
-     * @return the byte read or -1 if the end of stream has been reached.
-     * @throws IOException if the stream is closed or another IOException occurs.
-     * @since Android 1.5
-     */
-    public int read() throws IOException {
-        byte[] b = new byte[1];
-        int ret = mSocket.read(b, 0, 1);
-        if (ret == 1) {
-            return (int) b[0] & 0xff;
-        } else {
-            return -1;
-        }
-    }
-
-    /**
-     * Reads at most {@code length} bytes from this stream and stores them in
-     * the byte array {@code b} starting at {@code offset}.
-     *
-     * @param b the byte array in which to store the bytes read.
-     * @param offset the initial position in {@code buffer} to store the bytes read from this
-     * stream.
-     * @param length the maximum number of bytes to store in {@code b}.
-     * @return the number of bytes actually read or -1 if the end of the stream has been reached.
-     * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code length < 0}, or if {@code
-     * offset + length} is greater than the length of {@code b}.
-     * @throws IOException if the stream is closed or another IOException occurs.
-     * @since Android 1.5
-     */
-    public int read(byte[] b, int offset, int length) throws IOException {
-        if (b == null) {
-            throw new NullPointerException("byte array is null");
-        }
-        if ((offset | length) < 0 || length > b.length - offset) {
-            throw new ArrayIndexOutOfBoundsException("invalid offset or length");
-        }
-        return mSocket.read(b, offset, length);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
deleted file mode 100644
index 15db686..0000000
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ /dev/null
@@ -1,829 +0,0 @@
-/*
- * Copyright 2020 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the LeAudio profile.
- *
- * <p>BluetoothLeAudio is a proxy object for controlling the Bluetooth LE Audio
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothLeAudio proxy object.
- *
- * <p> Android only supports one set of connected Bluetooth LeAudio device at a time. Each
- * method is protected with its appropriate permission.
- */
-public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
-    private static final String TAG = "BluetoothLeAudio";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /**
-     * Intent used to broadcast the change in connection state of the LeAudio
-     * profile. Please note that in the binaural case, there will be two different LE devices for
-     * the left and right side and each device will have their own connection state changes.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * Intent used to broadcast group node status information.
-     *
-     * <p>This intent will have 3 extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED";
-
-
-    /**
-     * Intent used to broadcast group status information.
-     *
-     * <p>This intent will have 4 extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED";
-
-    /**
-     * Intent used to broadcast group audio configuration changed information.
-     *
-     * <p>This intent will have 5 extra:
-     * <ul>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned
-     * Numbers </li>
-     * <li> {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned
-     * Numbers </li>
-     * <li> {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per
-     * Bluetooth Assigned Numbers </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_CONF_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_CONF_CHANGED";
-
-    /**
-     * Indicates unspecified audio content.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_UNSPECIFIED = 0x0001;
-
-    /**
-     * Indicates conversation between humans as, for example, in telephony or video calls.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002;
-
-    /**
-     * Indicates media as, for example, in music, public radio, podcast or video soundtrack.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_MEDIA = 0x0004;
-
-    /**
-     * Indicates instructional audio as, for example, in navigation, traffic announcements
-     * or user guidance.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_INSTRUCTIONAL = 0x0008;
-
-    /**
-     * Indicates attention seeking audio as, for example, in beeps signalling arrival of a message
-     * or keyboard clicks.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_ATTENTION_SEEKING = 0x0010;
-
-    /**
-     * Indicates immediate alerts as, for example, in a low battery alarm, timer expiry or alarm
-     * clock.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_IMMEDIATE_ALERT = 0x0020;
-
-    /**
-     * Indicates man machine communication as, for example, with voice recognition or virtual
-     * assistant.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_MAN_MACHINE = 0x0040;
-
-    /**
-     * Indicates emergency alerts as, for example, with fire alarms or other urgent alerts.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_EMERGENCY_ALERT = 0x0080;
-
-    /**
-     * Indicates ringtone as in a call alert.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_RINGTONE = 0x0100;
-
-    /**
-     * Indicates audio associated with a television program and/or with metadata conforming to the
-     * Bluetooth Broadcast TV profile.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_TV = 0x0200;
-
-    /**
-     * Indicates audio associated with a low latency live audio stream.
-     *
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_LIVE = 0x0400;
-
-    /**
-     * Indicates audio associated with a video game stream.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_GAME = 0x0800;
-
-    /**
-     * This represents an invalid group ID.
-     *
-     * @hide
-     */
-    public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
-
-    /**
-     * Contains group id.
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_GROUP_ID =
-            "android.bluetooth.extra.LE_AUDIO_GROUP_ID";
-
-    /**
-     * Contains group node status, can be any of
-     * <p>
-     * <ul>
-     * <li> {@link #GROUP_NODE_ADDED} </li>
-     * <li> {@link #GROUP_NODE_REMOVED} </li>
-     * </ul>
-     * <p>
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS =
-            "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS";
-
-    /**
-     * Contains group status, can be any of
-     *
-     * <p>
-     * <ul>
-     * <li> {@link #GROUP_STATUS_ACTIVE} </li>
-     * <li> {@link #GROUP_STATUS_INACTIVE} </li>
-     * </ul>
-     * <p>
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_GROUP_STATUS =
-            "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS";
-
-    /**
-     * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source.
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_DIRECTION =
-            "android.bluetooth.extra.LE_AUDIO_DIRECTION";
-
-    /**
-     * Contains source location as per Bluetooth Assigned Numbers
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION =
-            "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION";
-
-    /**
-     * Contains sink location as per Bluetooth Assigned Numbers
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_SINK_LOCATION =
-            "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION";
-
-    /**
-     * Contains available context types for group as per Bluetooth Assigned Numbers
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS =
-            "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    /**
-     * Indicating that group is Active ( Audio device is available )
-     * @hide
-     */
-    public static final int GROUP_STATUS_ACTIVE = IBluetoothLeAudio.GROUP_STATUS_ACTIVE;
-
-    /**
-     * Indicating that group is Inactive ( Audio device is not available )
-     * @hide
-     */
-    public static final int GROUP_STATUS_INACTIVE = IBluetoothLeAudio.GROUP_STATUS_INACTIVE;
-
-    /**
-     * Indicating that node has been added to the group.
-     * @hide
-     */
-    public static final int GROUP_NODE_ADDED = IBluetoothLeAudio.GROUP_NODE_ADDED;
-
-    /**
-     * Indicating that node has been removed from the group.
-     * @hide
-     */
-    public static final int GROUP_NODE_REMOVED = IBluetoothLeAudio.GROUP_NODE_REMOVED;
-
-    private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio",
-                    IBluetoothLeAudio.class.getName()) {
-                @Override
-                public IBluetoothLeAudio getServiceInterface(IBinder service) {
-                    return IBluetoothLeAudio.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothLeAudio proxy object for interacting with the local
-     * Bluetooth LeAudio service.
-     */
-    /* package */ BluetoothLeAudio(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    /**
-     * @hide
-     */
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothLeAudio getService() {
-        return mProfileConnector.getService();
-    }
-
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(@Nullable BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(@Nullable BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothLeAudio service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
-            @NonNull int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothLeAudio service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, LeAudio audio
-     * streaming is to the active LeAudio device. If a remote device
-     * is not connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     *
-     * @param device the remote Bluetooth device. Could be null to clear
-     * the active device and stop streaming audio to a Bluetooth device.
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) log("setActiveDevice(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected LeAudio devices that are active
-     *
-     * @return the list of active devices. Returns empty list on error.
-     * @hide
-     */
-    @NonNull
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getActiveDevices() {
-        if (VDBG) log("getActiveDevice()");
-        final IBluetoothLeAudio service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get device group id. Devices with same group id belong to same group (i.e left and right
-     * earbud)
-     * @param device LE Audio capable device
-     * @return group id that this device currently belongs to
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getGroupId(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getGroupId()");
-        final IBluetoothLeAudio service = getService();
-        final int defaultValue = GROUP_ID_INVALID;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getGroupId(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set volume for the streaming devices
-     *
-     * @param volume volume to set
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED})
-    public void setVolume(int volume) {
-        if (VDBG) log("setVolume(vol: " + volume + " )");
-        final IBluetoothLeAudio service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setVolume(volume, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Add device to the given group.
-     * @param group_id group ID the device is being added to
-     * @param device the active device
-     * @return true on success, otherwise false
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED
-    })
-    public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) {
-        if (VDBG) log("groupAddNode()");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.groupAddNode(group_id, device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Remove device from a given group.
-     * @param group_id group ID the device is being removed from
-     * @param device the active device
-     * @return true on success, otherwise false
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED
-    })
-    public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) {
-        if (VDBG) log("groupRemoveNode()");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.groupRemoveNode(group_id, device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    private boolean isValidDevice(@Nullable BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
deleted file mode 100644
index dcaf4b6..0000000
--- a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Represents the codec configuration for a Bluetooth LE Audio source device.
- * <p>Contains the source codec type.
- * <p>The source codec type values are the same as those supported by the
- * device hardware.
- *
- * {@see BluetoothLeAudioCodecConfig}
- */
-public final class BluetoothLeAudioCodecConfig {
-    // Add an entry for each source codec here.
-
-    /** @hide */
-    @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
-            SOURCE_CODEC_TYPE_LC3,
-            SOURCE_CODEC_TYPE_INVALID
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SourceCodecType {};
-
-    public static final int SOURCE_CODEC_TYPE_LC3 = 0;
-    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
-
-    /**
-     * Represents the count of valid source codec types. Can be accessed via
-     * {@link #getMaxCodecType}.
-     */
-    private static final int SOURCE_CODEC_TYPE_MAX = 1;
-
-    private final @SourceCodecType int mCodecType;
-
-    /**
-     * Creates a new BluetoothLeAudioCodecConfig.
-     *
-     * @param codecType the source codec type
-     */
-    private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType) {
-        mCodecType = codecType;
-    }
-
-    @Override
-    public String toString() {
-        return "{codecName:" + getCodecName() + "}";
-    }
-
-    /**
-     * Gets the codec type.
-     *
-     * @return the codec type
-     */
-    public @SourceCodecType int getCodecType() {
-        return mCodecType;
-    }
-
-    /**
-     * Returns the valid codec types count.
-     */
-    public static int getMaxCodecType() {
-        return SOURCE_CODEC_TYPE_MAX;
-    }
-
-    /**
-     * Gets the codec name.
-     *
-     * @return the codec name
-     */
-    public @NonNull String getCodecName() {
-        switch (mCodecType) {
-            case SOURCE_CODEC_TYPE_LC3:
-                return "LC3";
-            case SOURCE_CODEC_TYPE_INVALID:
-                return "INVALID CODEC";
-            default:
-                break;
-        }
-        return "UNKNOWN CODEC(" + mCodecType + ")";
-    }
-
-    /**
-     * Builder for {@link BluetoothLeAudioCodecConfig}.
-     * <p> By default, the codec type will be set to
-     * {@link BluetoothLeAudioCodecConfig#SOURCE_CODEC_TYPE_INVALID}
-     */
-    public static final class Builder {
-        private int mCodecType = BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID;
-
-        /**
-         * Set codec type for Bluetooth codec config.
-         *
-         * @param codecType of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
-            mCodecType = codecType;
-            return this;
-        }
-
-        /**
-         * Build {@link BluetoothLeAudioCodecConfig}.
-         * @return new BluetoothLeAudioCodecConfig built
-         */
-        public @NonNull BluetoothLeAudioCodecConfig build() {
-            return new BluetoothLeAudioCodecConfig(mCodecType);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcast.java b/core/java/android/bluetooth/BluetoothLeBroadcast.java
deleted file mode 100644
index fed9f91..0000000
--- a/core/java/android/bluetooth/BluetoothLeBroadcast.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * This class provides the public APIs to control the Bluetooth LE Broadcast Source profile.
- *
- * <p>BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast
- * Source Service via IPC. Use {@link BluetoothAdapter#getProfileProxy}
- * to get the BluetoothLeBroadcast proxy object.
- *
- * @hide
- */
-public final class BluetoothLeBroadcast implements BluetoothProfile {
-    private static final String TAG = "BluetoothLeBroadcast";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Constants used by the LE Audio Broadcast profile for the Broadcast state
-     *
-     * @hide
-     */
-    @IntDef(prefix = {"LE_AUDIO_BROADCAST_STATE_"}, value = {
-      LE_AUDIO_BROADCAST_STATE_DISABLED,
-      LE_AUDIO_BROADCAST_STATE_ENABLING,
-      LE_AUDIO_BROADCAST_STATE_ENABLED,
-      LE_AUDIO_BROADCAST_STATE_DISABLING,
-      LE_AUDIO_BROADCAST_STATE_PLAYING,
-      LE_AUDIO_BROADCAST_STATE_NOT_PLAYING
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioBroadcastState {}
-
-    /**
-     * Indicates that LE Audio Broadcast mode is currently disabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10;
-
-    /**
-     * Indicates that LE Audio Broadcast mode is being enabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11;
-
-    /**
-     * Indicates that LE Audio Broadcast mode is currently enabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_ENABLED = 12;
-    /**
-     * Indicates that LE Audio Broadcast mode is being disabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_DISABLING = 13;
-
-    /**
-     * Indicates that an LE Audio Broadcast mode is currently playing
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14;
-
-    /**
-     * Indicates that LE Audio Broadcast is currently not playing
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15;
-
-    /**
-     * Constants used by the LE Audio Broadcast profile for encryption key length
-     *
-     * @hide
-     */
-    @IntDef(prefix = {"LE_AUDIO_BROADCAST_ENCRYPTION_KEY_"}, value = {
-      LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT,
-      LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioEncryptionKeyLength {}
-
-    /**
-     * Indicates that the LE Audio Broadcast encryption key size is 32 bits.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16;
-
-    /**
-     * Indicates that the LE Audio Broadcast encryption key size is 128 bits.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17;
-
-    /**
-     * Interface for receiving events related to broadcasts
-     */
-    public interface Callback {
-        /**
-         * Called when broadcast state has changed
-         *
-         * @param prevState broadcast state before the change
-         * @param newState broadcast state after the change
-         */
-        @LeAudioBroadcastState
-        void onBroadcastStateChange(int prevState, int newState);
-        /**
-         * Called when encryption key has been updated
-         *
-         * @param success true if the key was updated successfully, false otherwise
-         */
-        void onEncryptionKeySet(boolean success);
-    }
-
-    /**
-     * Create a BluetoothLeBroadcast proxy object for interacting with the local
-     * LE Audio Broadcast Source service.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothLeBroadcast(Context context,
-                                     BluetoothProfile.ServiceListener listener) {
-    }
-
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection
-     *
-     * @throws UnsupportedOperationException
-     *
-     * @hide
-     */
-    @Override
-    public int getConnectionState(BluetoothDevice device) {
-        throw new UnsupportedOperationException(
-                   "LE Audio Broadcasts are not connection-oriented.");
-    }
-
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection
-     *
-     * @throws UnsupportedOperationException
-     *
-     * @hide
-     */
-    @Override
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        throw new UnsupportedOperationException(
-                   "LE Audio Broadcasts are not connection-oriented.");
-    }
-
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection
-     *
-     * @throws UnsupportedOperationException
-     *
-     * @hide
-     */
-    @Override
-    public List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException(
-                   "LE Audio Broadcasts are not connection-oriented.");
-    }
-
-    /**
-     * Enable LE Audio Broadcast mode.
-     *
-     * Generates a new broadcast ID and enables sending of encrypted or unencrypted
-     * isochronous PDUs
-     *
-     * @hide
-     */
-    public int enableBroadcastMode() {
-        if (DBG) log("enableBroadcastMode");
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
-    }
-
-    /**
-     * Disable LE Audio Broadcast mode.
-     *
-     * @hide
-     */
-    public int disableBroadcastMode() {
-        if (DBG) log("disableBroadcastMode");
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
-    }
-
-    /**
-     * Get the current LE Audio broadcast state
-     *
-     * @hide
-     */
-    @LeAudioBroadcastState
-    public int getBroadcastState() {
-        if (DBG) log("getBroadcastState");
-        return LE_AUDIO_BROADCAST_STATE_DISABLED;
-    }
-
-    /**
-     * Enable LE Audio broadcast encryption
-     *
-     * @param keyLength if useExisting is true, this specifies the length of the key that should
-     *                  be generated
-     * @param useExisting true, if an existing key should be used
-     *                    false, if a new key should be generated
-     *
-     * @hide
-     */
-    @LeAudioEncryptionKeyLength
-    public int enableEncryption(boolean useExisting, int keyLength) {
-        if (DBG) log("enableEncryption useExisting=" + useExisting + " keyLength=" + keyLength);
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED;
-    }
-
-    /**
-     * Disable LE Audio broadcast encryption
-     *
-     * @param removeExisting true, if the existing key should be removed
-     *                       false, otherwise
-     *
-     * @hide
-     */
-    public int disableEncryption(boolean removeExisting) {
-        if (DBG) log("disableEncryption removeExisting=" + removeExisting);
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED;
-    }
-
-    /**
-     * Enable or disable LE Audio broadcast encryption
-     *
-     * @param key use the provided key if non-null, generate a new key if null
-     * @param keyLength 0 if encryption is disabled, 4 bytes (low security),
-     *                  16 bytes (high security)
-     *
-     * @hide
-     */
-    @LeAudioEncryptionKeyLength
-    public int setEncryptionKey(byte[] key, int keyLength) {
-        if (DBG) log("setEncryptionKey key=" + key + " keyLength=" + keyLength);
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED;
-    }
-
-
-    /**
-     * Get the encryption key that was set before
-     *
-     * @return encryption key as a byte array or null if no encryption key was set
-     *
-     * @hide
-     */
-    public byte[] getEncryptionKey() {
-        if (DBG) log("getEncryptionKey");
-        return null;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
deleted file mode 100644
index fb7789d..0000000
--- a/core/java/android/bluetooth/BluetoothLeCall.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.ParcelUuid;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Representation of Call
- *
- * @hide
- */
-public final class BluetoothLeCall implements Parcelable {
-
-    /** @hide */
-    @IntDef(prefix = "STATE_", value = {
-            STATE_INCOMING,
-            STATE_DIALING,
-            STATE_ALERTING,
-            STATE_ACTIVE,
-            STATE_LOCALLY_HELD,
-            STATE_REMOTELY_HELD,
-            STATE_LOCALLY_AND_REMOTELY_HELD
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface State {
-    }
-
-    /**
-     * A remote party is calling (incoming call).
-     *
-     * @hide
-     */
-    public static final int STATE_INCOMING = 0x00;
-
-    /**
-     * The process to call the remote party has started but the remote party is not
-     * being alerted (outgoing call).
-     *
-     * @hide
-     */
-    public static final int STATE_DIALING = 0x01;
-
-    /**
-     * A remote party is being alerted (outgoing call).
-     *
-     * @hide
-     */
-    public static final int STATE_ALERTING = 0x02;
-
-    /**
-     * The call is in an active conversation.
-     *
-     * @hide
-     */
-    public static final int STATE_ACTIVE = 0x03;
-
-    /**
-     * The call is connected but held locally. “Locally Held” implies that either
-     * the server or the client can affect the state.
-     *
-     * @hide
-     */
-    public static final int STATE_LOCALLY_HELD = 0x04;
-
-    /**
-     * The call is connected but held remotely. “Remotely Held” means that the state
-     * is controlled by the remote party of a call.
-     *
-     * @hide
-     */
-    public static final int STATE_REMOTELY_HELD = 0x05;
-
-    /**
-     * The call is connected but held both locally and remotely.
-     *
-     * @hide
-     */
-    public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
-
-    /**
-     * Whether the call direction is outgoing.
-     *
-     * @hide
-     */
-    public static final int FLAG_OUTGOING_CALL = 0x00000001;
-
-    /**
-     * Whether the call URI and Friendly Name are withheld by server.
-     *
-     * @hide
-     */
-    public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
-
-    /**
-     * Whether the call URI and Friendly Name are withheld by network.
-     *
-     * @hide
-     */
-    public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
-
-    /** Unique UUID that identifies this call */
-    private UUID mUuid;
-
-    /** Remote Caller URI */
-    private String mUri;
-
-    /** Caller friendly name */
-    private String mFriendlyName;
-
-    /** Call state */
-    private @State int mState;
-
-    /** Call flags */
-    private int mCallFlags;
-
-    /** @hide */
-    public BluetoothLeCall(@NonNull BluetoothLeCall that) {
-        mUuid = new UUID(that.getUuid().getMostSignificantBits(),
-                that.getUuid().getLeastSignificantBits());
-        mUri = that.mUri;
-        mFriendlyName = that.mFriendlyName;
-        mState = that.mState;
-        mCallFlags = that.mCallFlags;
-    }
-
-    /** @hide */
-    public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
-            @State int state, int callFlags) {
-        mUuid = uuid;
-        mUri = uri;
-        mFriendlyName = friendlyName;
-        mState = state;
-        mCallFlags = callFlags;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-        BluetoothLeCall that = (BluetoothLeCall) o;
-        return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
-                && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
-                && mCallFlags == that.mCallFlags;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
-    }
-
-    /**
-     * Returns a string representation of this BluetoothLeCall.
-     *
-     * <p>
-     * Currently this is the UUID.
-     *
-     * @return string representation of this BluetoothLeCall
-     */
-    @Override
-    public String toString() {
-        return mUuid.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeString(mUri);
-        out.writeString(mFriendlyName);
-        out.writeInt(mState);
-        out.writeInt(mCallFlags);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
-    						    new Parcelable.Creator<BluetoothLeCall>() {
-        public BluetoothLeCall createFromParcel(Parcel in) {
-            return new BluetoothLeCall(in);
-        }
-
-        public BluetoothLeCall[] newArray(int size) {
-            return new BluetoothLeCall[size];
-        }
-    };
-
-    private BluetoothLeCall(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
-        mUri = in.readString();
-        mFriendlyName = in.readString();
-        mState = in.readInt();
-        mCallFlags = in.readInt();
-    }
-
-    /**
-     * Returns an UUID of this BluetoothLeCall.
-     *
-     * <p>
-     * An UUID is unique identifier of a BluetoothLeCall.
-     *
-     * @return UUID of this BluetoothLeCall
-     * @hide
-     */
-    public @NonNull UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns a URI of the remote party of this BluetoothLeCall.
-     *
-     * @return string representation of this BluetoothLeCall
-     * @hide
-     */
-    public @NonNull String getUri() {
-        return mUri;
-    }
-
-    /**
-     * Returns a friendly name of the call.
-     *
-     * @return friendly name representation of this BluetoothLeCall
-     * @hide
-     */
-    public @NonNull String getFriendlyName() {
-        return mFriendlyName;
-    }
-
-    /**
-     * Returns the call state.
-     *
-     * @return the state of this BluetoothLeCall
-     * @hide
-     */
-    public @State int getState() {
-        return mState;
-    }
-
-    /**
-     * Returns the call flags.
-     *
-     * @return call flags
-     * @hide
-     */
-    public int getCallFlags() {
-        return mCallFlags;
-    }
-
-    /**
-     * Whether the call direction is incoming.
-     *
-     * @return true if incoming call, false otherwise
-     * @hide
-     */
-    public boolean isIncomingCall() {
-        return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
deleted file mode 100644
index 5283e08..0000000
--- a/core/java/android/bluetooth/BluetoothLeCallControl.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Copyright 2019 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-import android.annotation.SuppressLint;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.Executor;
-
-/**
- * This class provides the APIs to control the Call Control profile.
- *
- * <p>
- * This class provides Bluetooth Telephone Bearer Service functionality,
- * allowing applications to expose a GATT Service based interface to control the
- * state of the calls by remote devices such as LE audio devices.
- *
- * <p>
- * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothLeCallControl proxy object.
- *
- * @hide
- */
-public final class BluetoothLeCallControl implements BluetoothProfile {
-    private static final String TAG = "BluetoothLeCallControl";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /** @hide */
-    @IntDef(prefix = "RESULT_", value = {
-            RESULT_SUCCESS,
-            RESULT_ERROR_UNKNOWN_CALL_ID,
-            RESULT_ERROR_INVALID_URI,
-            RESULT_ERROR_APPLICATION
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Result {
-    }
-
-    /**
-     * Opcode write was successful.
-     *
-     * @hide
-     */
-    public static final int RESULT_SUCCESS = 0;
-
-    /**
-     * Unknown call Id has been used in the operation.
-     *
-     * @hide
-     */
-    public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
-
-    /**
-     * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
-     *
-     * @hide
-     */
-    public static final int RESULT_ERROR_INVALID_URI = 2;
-
-    /**
-     * Application internal error.
-     *
-     * @hide
-     */
-    public static final int RESULT_ERROR_APPLICATION = 3;
-
-    /** @hide */
-    @IntDef(prefix = "TERMINATION_REASON_", value = {
-            TERMINATION_REASON_INVALID_URI,
-            TERMINATION_REASON_FAIL,
-            TERMINATION_REASON_REMOTE_HANGUP,
-            TERMINATION_REASON_SERVER_HANGUP,
-            TERMINATION_REASON_LINE_BUSY,
-            TERMINATION_REASON_NETWORK_CONGESTION,
-            TERMINATION_REASON_CLIENT_HANGUP,
-            TERMINATION_REASON_NO_SERVICE,
-            TERMINATION_REASON_NO_ANSWER
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TerminationReason {
-    }
-
-    /**
-     * Remote Caller ID value used to place a call was formed improperly.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_INVALID_URI = 0x00;
-
-    /**
-     * Call fail.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_FAIL = 0x01;
-
-    /**
-     * Remote party ended call.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
-
-    /**
-     * Call ended from the server.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
-
-    /**
-     * Line busy.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
-
-    /**
-     * Network congestion.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
-
-    /**
-     * Client terminated.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
-
-    /**
-     * No service.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
-
-    /**
-     * No answer.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
-
-    /*
-     * Flag indicating support for hold/unhold call feature.
-     *
-     * @hide
-     */
-    public static final int CAPABILITY_HOLD_CALL = 0x00000001;
-
-    /**
-     * Flag indicating support for joining calls feature.
-     *
-     * @hide
-     */
-    public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
-
-    private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
-    private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
-
-    private static final int REG_TIMEOUT = 10000;
-
-    /**
-     * The template class is used to call callback functions on events from the TBS
-     * server. Callback functions are wrapped in this class and registered to the
-     * Android system during app registration.
-     *
-     * @hide
-     */
-    public abstract static class Callback {
-
-        private static final String TAG = "BluetoothLeCallControl.Callback";
-
-        /**
-         * Called when a remote client requested to accept the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to be accepted
-         * @hide
-         */
-        public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
-
-        /**
-         * A remote client has requested to terminate the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to terminate
-         * @hide
-         */
-        public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
-
-        /**
-         * A remote client has requested to hold the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to be put on hold
-         * @hide
-         */
-        public void onHoldCall(int requestId, @NonNull UUID callId) {
-            Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
-        }
-
-        /**
-         * A remote client has requested to unhold the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to unhold
-         * @hide
-         */
-        public void onUnholdCall(int requestId, @NonNull UUID callId) {
-            Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
-        }
-
-        /**
-         * A remote client has requested to place a call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The Id to be assigned for the new call
-         * @param uri       The caller URI requested
-         * @hide
-         */
-        public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
-
-        /**
-         * A remote client has requested to join the calls.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callIds   The call Id list requested to join
-         * @hide
-         */
-        public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
-            Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
-        }
-    }
-
-    private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
-
-        private final Executor mExecutor;
-        private final Callback mCallback;
-
-        CallbackWrapper(Executor executor, Callback callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onBearerRegistered(int ccid) {
-            synchronized (mServerIfLock) {
-                if (mCallback != null) {
-                    mCcid = ccid;
-                    mServerIfLock.notifyAll();
-                } else {
-                    // registration timeout
-                    Log.e(TAG, "onBearerRegistered: mCallback is null");
-                }
-            }
-        }
-
-        @Override
-        public void onAcceptCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onTerminateCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onHoldCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onUnholdCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
-            List<UUID> uuids = new ArrayList<>();
-            for (ParcelUuid parcelUuid : parcelUuids) {
-                uuids.add(parcelUuid.getUuid());
-            }
-
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-    };
-
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothLeCallControl mService;
-    private BluetoothAdapter mAdapter;
-    private int mCcid = 0;
-    private String mToken;
-    private Callback mCallback = null;
-    private Object mServerIfLock = new Object();
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-        new IBluetoothStateChangeCallback.Stub() {
-        public void onBluetoothStateChange(boolean up) {
-            if (DBG)
-                Log.d(TAG, "onBluetoothStateChange: up=" + up);
-            if (!up) {
-                doUnbind();
-            } else {
-                doBind();
-            }
-        }
-    };
-
-    /**
-     * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
-     * telephone bearer service.
-     */
-    /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
-        mContext = context;
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mServiceListener = listener;
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    private boolean doBind() {
-        synchronized (mConnection) {
-            if (mService == null) {
-                if (VDBG)
-                    Log.d(TAG, "Binding service...");
-                try {
-                    return mAdapter.getBluetoothManager().
-                            bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
-                            mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to bind TelephoneBearerService", e);
-                }
-            }
-        }
-        return false;
-    }
-
-    private void doUnbind() {
-        synchronized (mConnection) {
-            if (mService != null) {
-                if (VDBG)
-                    Log.d(TAG, "Unbinding service...");
-                try {
-                    mAdapter.getBluetoothManager().
-                        unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
-                        mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
-                } finally {
-                    mService = null;
-                }
-            }
-        }
-    }
-
-    /* package */ void close() {
-        if (VDBG)
-            log("close()");
-        unregisterBearer();
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "", re);
-            }
-        }
-        mServiceListener = null;
-        doUnbind();
-    }
-
-    private IBluetoothLeCallControl getService() {
-        return mService;
-    }
-
-    /**
-     * Not supported
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    public int getConnectionState(@Nullable BluetoothDevice device) {
-        throw new UnsupportedOperationException("not supported");
-    }
-
-    /**
-     * Not supported
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException("not supported");
-    }
-
-    /**
-     * Not supported
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
-        @NonNull int[] states) {
-        throw new UnsupportedOperationException("not supported");
-    }
-
-    /**
-     * Register Telephone Bearer exposing the interface that allows remote devices
-     * to track and control the call states.
-     *
-     * <p>
-     * This is an asynchronous call. The callback is used to notify success or
-     * failure if the function returns true.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * <!-- The UCI is a String identifier of the telephone bearer as defined at
-     * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
-     * (login required). -->
-     *
-     * <!-- The examples of common URI schemes can be found in
-     * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
-     *
-     * <!-- The Technology is an integer value. The possible values are defined at
-     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
-     * -->
-     *
-     * @param uci          Bearer Unique Client Identifier
-     * @param uriSchemes   URI Schemes supported list
-     * @param capabilities bearer capabilities
-     * @param provider     Network provider name
-     * @param technology   Network technology
-     * @param executor     {@link Executor} object on which callback will be
-     *                     executed. The Executor object is required.
-     * @param callback     {@link Callback} object to which callback messages will
-     *                     be sent. The Callback object is required.
-     * @return true on success, false otherwise
-     * @hide
-     */
-    @SuppressLint("ExecutorRegistration")
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean registerBearer(@Nullable String uci,
-                    @NonNull List<String> uriSchemes, int capabilities,
-                    @NonNull String provider, int technology,
-                    @NonNull Executor executor, @NonNull Callback callback) {
-        if (DBG) {
-            Log.d(TAG, "registerBearer");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("null parameter: " + callback);
-        }
-        if (mCcid != 0) {
-            return false;
-        }
-
-        mToken = uci;
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            synchronized (mServerIfLock) {
-                if (mCallback != null) {
-                    Log.e(TAG, "Bearer can be opened only once");
-                    return false;
-                }
-
-                mCallback = callback;
-                try {
-                    CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
-                    service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
-                                            provider, technology);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "", e);
-                    mCallback = null;
-                    return false;
-                }
-
-                try {
-                    mServerIfLock.wait(REG_TIMEOUT);
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "" + e);
-                    mCallback = null;
-                }
-
-                if (mCcid == 0) {
-                    mCallback = null;
-                    return false;
-                }
-
-                return true;
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-
-        return false;
-    }
-
-    /**
-     * Unregister Telephone Bearer Service and destroy all the associated data.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void unregisterBearer() {
-        if (DBG) {
-            Log.d(TAG, "unregisterBearer");
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        int ccid = mCcid;
-        mCcid = 0;
-        mCallback = null;
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.unregisterBearer(mToken);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Get the Content Control ID (CCID) value.
-     *
-     * @return ccid Content Control ID value
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public int getContentControlId() {
-        return mCcid;
-    }
-
-    /**
-     * Notify about the newly added call.
-     *
-     * <p>
-     * This shall be called as early as possible after the call has been added.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param call Newly added call
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onCallAdded(@NonNull BluetoothLeCall call) {
-        if (DBG) {
-            Log.d(TAG, "onCallAdded: call=" + call);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.callAdded(mCcid, call);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Notify about the removed call.
-     *
-     * <p>
-     * This shall be called as early as possible after the call has been removed.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param callId The Id of a call that has been removed
-     * @param reason Call termination reason
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
-        if (DBG) {
-            Log.d(TAG, "callRemoved: callId=" + callId);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.callRemoved(mCcid, new ParcelUuid(callId), reason);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Notify the call state change
-     *
-     * <p>
-     * This shall be called as early as possible after the state of the call has
-     * changed.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param callId The call Id that state has been changed
-     * @param state  Call state
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
-        if (DBG) {
-            Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.callStateChanged(mCcid, new ParcelUuid(callId), state);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Provide the current calls list
-     *
-     * <p>
-     * This function must be invoked after registration if application has any
-     * calls.
-     *
-     * @param calls current calls list
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-     public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.currentCallsList(mCcid, calls);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-    }
-
-    /**
-     * Provide the network current status
-     *
-     * <p>
-     * This function must be invoked on change of network state.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * <!-- The Technology is an integer value. The possible values are defined at
-     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
-     * -->
-     *
-     * @param provider   Network provider name
-     * @param technology Network technology
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void networkStateChanged(@NonNull String provider, int technology) {
-        if (DBG) {
-            Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.networkStateChanged(mCcid, provider, technology);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Send a response to a call control request to a remote device.
-     *
-     * <p>
-     * This function must be invoked in when a request is received by one of these
-     * callback methods:
-     *
-     * <ul>
-     * <li>{@link Callback#onAcceptCall}
-     * <li>{@link Callback#onTerminateCall}
-     * <li>{@link Callback#onHoldCall}
-     * <li>{@link Callback#onUnholdCall}
-     * <li>{@link Callback#onPlaceCall}
-     * <li>{@link Callback#onJoinCalls}
-     * </ul>
-     *
-     * @param requestId The ID of the request that was received with the callback
-     * @param result    The result of the request to be sent to the remote devices
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void requestResult(int requestId, @Result int result) {
-        if (DBG) {
-            Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.requestResult(mCcid, requestId, result);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private final IBluetoothProfileServiceConnection mConnection =
-                                    new IBluetoothProfileServiceConnection.Stub() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) {
-                Log.d(TAG, "Proxy object connected");
-            }
-            mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
-            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) {
-                Log.d(TAG, "Proxy object disconnected");
-            }
-            doUnbind();
-            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
-        }
-    };
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-            case MESSAGE_TBS_SERVICE_CONNECTED: {
-                if (mServiceListener != null) {
-                    mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
-                        BluetoothLeCallControl.this);
-                }
-                break;
-            }
-            case MESSAGE_TBS_SERVICE_DISCONNECTED: {
-                if (mServiceListener != null) {
-                    mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
-                }
-                break;
-            }
-            }
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
deleted file mode 100644
index fef6f22..0000000
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.RequiresFeature;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemService;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * High level manager used to obtain an instance of an {@link BluetoothAdapter}
- * and to conduct overall Bluetooth Management.
- * <p>
- * Use {@link android.content.Context#getSystemService(java.lang.String)}
- * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
- * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
- * </p>
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about using BLUETOOTH, read the <a href=
- * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
- * guide.
- * </p>
- * </div>
- *
- * @see Context#getSystemService
- * @see BluetoothAdapter#getDefaultAdapter()
- */
-@SystemService(Context.BLUETOOTH_SERVICE)
-@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
-public final class BluetoothManager {
-    private static final String TAG = "BluetoothManager";
-    private static final boolean DBG = false;
-
-    private final AttributionSource mAttributionSource;
-    private final BluetoothAdapter mAdapter;
-
-    /**
-     * @hide
-     */
-    public BluetoothManager(Context context) {
-        mAttributionSource = (context != null) ? context.getAttributionSource() :
-                AttributionSource.myAttributionSource();
-        mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
-    }
-
-    /**
-     * Get the BLUETOOTH Adapter for this device.
-     *
-     * @return the BLUETOOTH Adapter
-     */
-    @RequiresNoPermission
-    public BluetoothAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Get the current connection state of the profile to the remote device.
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for certain profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of Bluetooth.
-     *
-     * @param device Remote bluetooth device.
-     * @param profile GATT or GATT_SERVER
-     * @return State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED},
-     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
-     * {@link BluetoothProfile#STATE_DISCONNECTING}
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device, int profile) {
-        if (DBG) Log.d(TAG, "getConnectionState()");
-
-        List<BluetoothDevice> connectedDevices = getConnectedDevices(profile);
-        for (BluetoothDevice connectedDevice : connectedDevices) {
-            if (device.equals(connectedDevice)) {
-                return BluetoothProfile.STATE_CONNECTED;
-            }
-        }
-
-        return BluetoothProfile.STATE_DISCONNECTED;
-    }
-
-    /**
-     * Get connected devices for the specified profile.
-     *
-     * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED}
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of Bluetooth for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of Bluetooth.
-     *
-     * @param profile GATT or GATT_SERVER
-     * @return List of devices. The list will be empty on error.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices(int profile) {
-        if (DBG) Log.d(TAG, "getConnectedDevices");
-        return getDevicesMatchingConnectionStates(profile, new int[] {
-                BluetoothProfile.STATE_CONNECTED
-        });
-    }
-
-    /**
-     * Get a list of devices that match any of the given connection
-     * states.
-     *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * @param profile GATT or GATT_SERVER
-     * @param states Array of states. States can be one of {@link BluetoothProfile#STATE_CONNECTED},
-     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
-     * {@link BluetoothProfile#STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
-        if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates");
-
-        if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
-            throw new IllegalArgumentException("Profile not supported: " + profile);
-        }
-
-        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-
-        try {
-            IBluetoothManager managerService = mAdapter.getBluetoothManager();
-            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
-            if (iGatt == null) return devices;
-            devices = Attributable.setAttributionSource(
-                    iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource),
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-
-        return devices;
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @return BluetoothGattServer instance
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback) {
-
-        return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO));
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @param eatt_support idicates if server should use eatt channel for notifications.
-     * @return BluetoothGattServer instance
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback, boolean eatt_support) {
-        return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support));
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @return BluetoothGattServer instance
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback, int transport) {
-        return (openGattServer(context, callback, transport, false));
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param eatt_support idicates if server should use eatt channel for notifications.
-     * @return BluetoothGattServer instance
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback, int transport, boolean eatt_support) {
-        if (context == null || callback == null) {
-            throw new IllegalArgumentException("null parameter: " + context + " " + callback);
-        }
-
-        // TODO(Bluetooth) check whether platform support BLE
-        //     Do the check here or in GattServer?
-
-        try {
-            IBluetoothManager managerService = mAdapter.getBluetoothManager();
-            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
-            if (iGatt == null) {
-                Log.e(TAG, "Fail to get GATT Server connection");
-                return null;
-            }
-            BluetoothGattServer mGattServer =
-                    new BluetoothGattServer(iGatt, transport, mAdapter);
-            Boolean regStatus = mGattServer.registerCallback(callback, eatt_support);
-            return regStatus ? mGattServer : null;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return null;
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
deleted file mode 100644
index 56e4972..0000000
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth MAP
- * Profile.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
-
-    private static final String TAG = "BluetoothMap";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /** @hide */
-    @SuppressLint("ActionValue")
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * There was an error trying to obtain the state
-     *
-     * @hide
-     */
-    public static final int STATE_ERROR = -1;
-
-    /** @hide */
-    public static final int RESULT_FAILURE = 0;
-    /** @hide */
-    public static final int RESULT_SUCCESS = 1;
-    /**
-     * Connection canceled before completion.
-     *
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.MAP,
-                    "BluetoothMap", IBluetoothMap.class.getName()) {
-                @Override
-                public IBluetoothMap getServiceInterface(IBinder service) {
-                    return IBluetoothMap.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothMap proxy object.
-     */
-    /* package */ BluetoothMap(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothMap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     *
-     * @hide
-     */
-    @SystemApi
-    public void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothMap getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Get the current state of the BluetoothMap service.
-     *
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
-     * connected to the Map service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getState() {
-        if (VDBG) log("getState()");
-        final IBluetoothMap service = getService();
-        final int defaultValue = BluetoothMap.STATE_ERROR;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getState(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the currently connected remote Bluetooth device (PCE).
-     *
-     * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
-     * this proxy object is not connected to the Map service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getClient() {
-        if (VDBG) log("getClient()");
-        final IBluetoothMap service = getService();
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getClient(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns true if the specified Bluetooth device is connected.
-     * Returns false if not connected, or if this proxy object is not
-     * currently connected to the Map service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected(BluetoothDevice device) {
-        if (VDBG) log("isConnected(" + device + ")");
-        final IBluetoothMap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate connection. Initiation of outgoing connections is not
-     * supported for MAP server.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
-        return false;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothMap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check class bits for possible Map support.
-     * This is a simple heuristic that tries to guess if a device with the
-     * given class bits might support Map. It is not accurate for all
-     * devices. It tries to err on the side of false positives.
-     *
-     * @return True if this device might support Map.
-     *
-     * @hide
-     */
-    public static boolean doesClassMatchSink(BluetoothClass btClass) {
-        // TODO optimize the rule
-        switch (btClass.getDeviceClass()) {
-            case BluetoothClass.Device.COMPUTER_DESKTOP:
-            case BluetoothClass.Device.COMPUTER_LAPTOP:
-            case BluetoothClass.Device.COMPUTER_SERVER:
-            case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
-        final IBluetoothMap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothMap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothMap service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothMap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothMap service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
deleted file mode 100644
index 03536f9a..0000000
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth MAP MCE Profile.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothMapClient implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothMapClient";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
-    /** @hide */
-    @RequiresPermission(android.Manifest.permission.RECEIVE_SMS)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_RECEIVED =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
-    /* Actions to be used for pending intents */
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
-
-    /**
-     * Action to notify read status changed
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
-
-    /**
-     * Action to notify deleted status changed
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
-
-    /**
-     * Extras used in ACTION_MESSAGE_RECEIVED intent.
-     * NOTE: HANDLE is only valid for a single session with the device.
-     */
-    /** @hide */
-    public static final String EXTRA_MESSAGE_HANDLE =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE";
-    /** @hide */
-    public static final String EXTRA_MESSAGE_TIMESTAMP =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP";
-    /** @hide */
-    public static final String EXTRA_MESSAGE_READ_STATUS =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS";
-    /** @hide */
-    public static final String EXTRA_SENDER_CONTACT_URI =
-            "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
-    /** @hide */
-    public static final String EXTRA_SENDER_CONTACT_NAME =
-            "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
-
-    /**
-     * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED
-     * Contains the MAP message deleted status
-     * Possible values are:
-     * true: deleted
-     * false: undeleted
-     *
-     * @hide
-     */
-    public static final String EXTRA_MESSAGE_DELETED_STATUS =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS";
-
-    /**
-     * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED
-     * Possible values are:
-     * 0: failure
-     * 1: success
-     *
-     * @hide
-     */
-    public static final String EXTRA_RESULT_CODE =
-            "android.bluetooth.device.extra.RESULT_CODE";
-
-    /**
-     * There was an error trying to obtain the state
-     * @hide
-     */
-    public static final int STATE_ERROR = -1;
-
-    /** @hide */
-    public static final int RESULT_FAILURE = 0;
-    /** @hide */
-    public static final int RESULT_SUCCESS = 1;
-    /**
-     * Connection canceled before completion.
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-    /** @hide */
-    private static final int UPLOADING_FEATURE_BITMASK = 0x08;
-
-    /*
-     * UNREAD, READ, UNDELETED, DELETED are passed as parameters
-     * to setMessageStatus to indicate the messages new state.
-     */
-
-    /** @hide */
-    public static final int UNREAD = 0;
-    /** @hide */
-    public static final int READ = 1;
-    /** @hide */
-    public static final int UNDELETED = 2;
-    /** @hide */
-    public static final int DELETED = 3;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
-                    "BluetoothMapClient", IBluetoothMapClient.class.getName()) {
-                @Override
-                public IBluetoothMapClient getServiceInterface(IBinder service) {
-                    return IBluetoothMapClient.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothMapClient proxy object.
-     */
-    /* package */ BluetoothMapClient(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothMap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     * @hide
-     */
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothMapClient getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Returns true if the specified Bluetooth device is connected.
-     * Returns false if not connected, or if this proxy object is not
-     * currently connected to the Map service.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected(BluetoothDevice device) {
-        if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate connection. Initiation of outgoing connections is not
-     * supported for MAP server.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "disconnect(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) Log.d(TAG, "getConnectedDevices()");
-        final IBluetoothMapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
-        final IBluetoothMapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final int defaultValue =  BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send a message.
-     *
-     * Send an SMS message to either the contacts primary number or the telephone number specified.
-     *
-     * @param device Bluetooth device
-     * @param contacts Uri Collection of the contacts
-     * @param message Message to be sent
-     * @param sentIntent intent issued when message is sent
-     * @param deliveredIntent intent issued when message is delivered
-     * @return true if the message is enqueued, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.SEND_SMS,
-    })
-    public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
-            @NonNull String message, @Nullable PendingIntent sentIntent,
-            @Nullable PendingIntent deliveredIntent) {
-        return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent,
-                deliveredIntent);
-    }
-
-     /**
-     * Send a message.
-     *
-     * Send an SMS message to either the contacts primary number or the telephone number specified.
-     *
-     * @param device Bluetooth device
-     * @param contacts Uri[] of the contacts
-     * @param message Message to be sent
-     * @param sentIntent intent issued when message is sent
-     * @param deliveredIntent intent issued when message is delivered
-     * @return true if the message is enqueued, false on error
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.SEND_SMS,
-    })
-    public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
-            PendingIntent sentIntent, PendingIntent deliveredIntent) {
-        if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
-                        mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get unread messages.  Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}.
-     *
-     * @param device Bluetooth device
-     * @return true if the message is enqueued, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.READ_SMS,
-    })
-    public boolean getUnreadMessages(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getUnreadMessages(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns the "Uploading" feature bit value from the SDP record's
-     * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114).
-     * @param device The Bluetooth device to get this value for.
-     * @return Returns true if the Uploading bit value in SDP record's
-     *         MapSupportedFeatures field is set. False is returned otherwise.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isUploadingSupported(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final int defaultValue = 0;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getSupportedFeatures(device, mAttributionSource, recv);
-                return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
-                        & UPLOADING_FEATURE_BITMASK) > 0;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Set message status of message on MSE
-     * <p>
-     * When read status changed, the result will be published via
-     * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED}
-     * When deleted status changed, the result will be published via
-     * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED}
-     *
-     * @param device Bluetooth device
-     * @param handle message handle
-     * @param status <code>UNREAD</code> for "unread", <code>READ</code> for
-     *            "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for
-     *            "deleted", otherwise return error
-     * @return <code>true</code> if request has been sent, <code>false</code> on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.READ_SMS,
-    })
-    public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
-        if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ
-                    || status == UNREAD || status == UNDELETED  || status == DELETED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setMessageStatus(device, handle, status, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java
deleted file mode 100644
index eeaf085..0000000
--- a/core/java/android/bluetooth/BluetoothMasInstance.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public final class BluetoothMasInstance implements Parcelable {
-    private final int mId;
-    private final String mName;
-    private final int mChannel;
-    private final int mMsgTypes;
-
-    public BluetoothMasInstance(int id, String name, int channel, int msgTypes) {
-        mId = id;
-        mName = name;
-        mChannel = channel;
-        mMsgTypes = msgTypes;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothMasInstance) {
-            return mId == ((BluetoothMasInstance) o).mId;
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mId + (mChannel << 8) + (mMsgTypes << 16);
-    }
-
-    @Override
-    public String toString() {
-        return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":"
-                + Integer.toHexString(mMsgTypes);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothMasInstance> CREATOR =
-            new Parcelable.Creator<BluetoothMasInstance>() {
-                public BluetoothMasInstance createFromParcel(Parcel in) {
-                    return new BluetoothMasInstance(in.readInt(), in.readString(),
-                            in.readInt(), in.readInt());
-                }
-
-                public BluetoothMasInstance[] newArray(int size) {
-                    return new BluetoothMasInstance[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mId);
-        out.writeString(mName);
-        out.writeInt(mChannel);
-        out.writeInt(mMsgTypes);
-    }
-
-    public static final class MessageType {
-        public static final int EMAIL = 0x01;
-        public static final int SMS_GSM = 0x02;
-        public static final int SMS_CDMA = 0x04;
-        public static final int MMS = 0x08;
-    }
-
-    public int getId() {
-        return mId;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public int getChannel() {
-        return mChannel;
-    }
-
-    public int getMsgTypes() {
-        return mMsgTypes;
-    }
-
-    public boolean msgSupported(int msg) {
-        return (mMsgTypes & msg) != 0;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
deleted file mode 100644
index ac2b3ed..0000000
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.SuppressLint;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * BluetoothOutputStream.
- *
- * Used to read from a Bluetooth socket.
- *
- * @hide
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-/*package*/ final class BluetoothOutputStream extends OutputStream {
-    private BluetoothSocket mSocket;
-
-    /*package*/ BluetoothOutputStream(BluetoothSocket s) {
-        mSocket = s;
-    }
-
-    /**
-     * Close this output stream and the socket associated with it.
-     */
-    public void close() throws IOException {
-        mSocket.close();
-    }
-
-    /**
-     * Writes a single byte to this stream. Only the least significant byte of
-     * the integer {@code oneByte} is written to the stream.
-     *
-     * @param oneByte the byte to be written.
-     * @throws IOException if an error occurs while writing to this stream.
-     * @since Android 1.0
-     */
-    public void write(int oneByte) throws IOException {
-        byte[] b = new byte[1];
-        b[0] = (byte) oneByte;
-        mSocket.write(b, 0, 1);
-    }
-
-    /**
-     * Writes {@code count} bytes from the byte array {@code buffer} starting
-     * at position {@code offset} to this stream.
-     *
-     * @param b the buffer to be written.
-     * @param offset the start position in {@code buffer} from where to get bytes.
-     * @param count the number of bytes from {@code buffer} to write to this stream.
-     * @throws IOException if an error occurs while writing to this stream.
-     * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code count < 0}, or if {@code
-     * offset + count} is bigger than the length of {@code buffer}.
-     * @since Android 1.0
-     */
-    public void write(byte[] b, int offset, int count) throws IOException {
-        if (b == null) {
-            throw new NullPointerException("buffer is null");
-        }
-        if ((offset | count) < 0 || count > b.length - offset) {
-            throw new IndexOutOfBoundsException("invalid offset or length");
-        }
-        mSocket.write(b, offset, count);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
deleted file mode 100644
index d4ad4ef4..0000000
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth Pan
- * Profile.
- *
- * <p>BluetoothPan is a proxy object for controlling the Bluetooth
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothPan proxy object.
- *
- * <p>Each method is protected with its appropriate permission.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothPan implements BluetoothProfile {
-    private static final String TAG = "BluetoothPan";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Pan
-     * profile.
-     *
-     * <p>This intent will have 4 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is
-     * bound to. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
-     * {@link #LOCAL_PANU_ROLE}
-     */
-    @SuppressLint("ActionValue")
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent
-     * The local role of the PAN profile that the remote device is bound to.
-     * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}.
-     */
-    @SuppressLint("ActionValue")
-    public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
-
-    /**
-     * Intent used to broadcast the change in tethering state of the Pan
-     * Profile
-     *
-     * <p>This intent will have 1 extra:
-     * <ul>
-     * <li> {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth
-     * tethering. </li>
-     * </ul>
-     *
-     * <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or
-     * {@link #TETHERING_STATE_ON}
-     */
-    @RequiresLegacyBluetoothPermission
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_TETHERING_STATE_CHANGED =
-            "android.bluetooth.action.TETHERING_STATE_CHANGED";
-
-    /**
-     * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent
-     * The tethering state of the PAN profile.
-     * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}.
-     */
-    public static final String EXTRA_TETHERING_STATE =
-            "android.bluetooth.extra.TETHERING_STATE";
-
-    /** @hide */
-    @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LocalPanRole {}
-
-    public static final int PAN_ROLE_NONE = 0;
-    /**
-     * The local device is acting as a Network Access Point.
-     */
-    public static final int LOCAL_NAP_ROLE = 1;
-
-    /**
-     * The local device is acting as a PAN User.
-     */
-    public static final int LOCAL_PANU_ROLE = 2;
-
-    /** @hide */
-    @IntDef({PAN_ROLE_NONE, REMOTE_NAP_ROLE, REMOTE_PANU_ROLE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RemotePanRole {}
-
-    public static final int REMOTE_NAP_ROLE = 1;
-
-    public static final int REMOTE_PANU_ROLE = 2;
-
-    /** @hide **/
-    @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TetheringState{}
-
-    public static final int TETHERING_STATE_OFF = 1;
-
-    public static final int TETHERING_STATE_ON = 2;
-    /**
-     * Return codes for the connect and disconnect Bluez / Dbus calls.
-     *
-     * @hide
-     */
-    public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_OPERATION_GENERIC_FAILURE = 1003;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_OPERATION_SUCCESS = 1004;
-
-    private final Context mContext;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.PAN,
-                    "BluetoothPan", IBluetoothPan.class.getName()) {
-                @Override
-                public IBluetoothPan getServiceInterface(IBinder service) {
-                    return IBluetoothPan.Stub.asInterface(service);
-                }
-    };
-
-
-    /**
-     * Create a BluetoothPan proxy object for interacting with the local
-     * Bluetooth Service which handles the Pan profile
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /* package */ BluetoothPan(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mContext = context;
-        mProfileConnector.connect(context, listener);
-    }
-
-    /**
-     * Closes the connection to the service and unregisters callbacks
-     */
-    @UnsupportedAppUsage
-    void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothPan getService() {
-        return mProfileConnector.getService();
-    }
-
-    /** @hide */
-    protected void finalize() {
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothPan service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothPan service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getConnectionState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothPan service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Turns on/off bluetooth tethering
-     *
-     * @param value is whether to enable or disable bluetooth tethering
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.TETHER_PRIVILEGED,
-    })
-    public void setBluetoothTethering(boolean value) {
-        String pkgName = mContext.getOpPackageName();
-        if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
-        final IBluetoothPan service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setBluetoothTethering(value, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Determines whether tethering is enabled
-     *
-     * @return true if tethering is on, false if not or some error occurred
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isTetheringOn() {
-        if (VDBG) log("isTetheringOn()");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isTetheringOn(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    @UnsupportedAppUsage
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    @UnsupportedAppUsage
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    @UnsupportedAppUsage
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
deleted file mode 100644
index de2db9c..0000000
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Public API for controlling the Bluetooth Pbap Service. This includes
- * Bluetooth Phone book Access profile.
- * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
- * Service via IPC.
- *
- * Creating a BluetoothPbap object will create a binding with the
- * BluetoothPbap service. Users of this object should call close() when they
- * are finished with the BluetoothPbap, so that this proxy object can unbind
- * from the service.
- *
- * This BluetoothPbap object is not immediately bound to the
- * BluetoothPbap service. Use the ServiceListener interface to obtain a
- * notification when it is bound, this is especially important if you wish to
- * immediately call methods on BluetoothPbap after construction.
- *
- * To get an instance of the BluetoothPbap class, you can call
- * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param
- * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of
- * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}.
- *
- * Android only supports one connected Bluetooth Pce at a time.
- *
- * @hide
- */
-@SystemApi
-public class BluetoothPbap implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothPbap";
-    private static final boolean DBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the PBAP
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
-     *  can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
-     *  {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
-     *  {@link BluetoothProfile#STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @SuppressLint("ActionValue")
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
-
-    private final AttributionSource mAttributionSource;
-
-    /** @hide */
-    public static final int RESULT_FAILURE = 0;
-    /** @hide */
-    public static final int RESULT_SUCCESS = 1;
-    /**
-     * Connection canceled before completion.
-     *
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-
-    private BluetoothAdapter mAdapter;
-    private final BluetoothProfileConnector<IBluetoothPbap> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.PBAP, "BluetoothPbap",
-                    IBluetoothPbap.class.getName()) {
-                @Override
-                public IBluetoothPbap getServiceInterface(IBinder service) {
-                    return IBluetoothPbap.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothPbap proxy object.
-     *
-     * @hide
-     */
-    public BluetoothPbap(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /** @hide */
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothPbap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     *
-     * @hide
-     */
-    public synchronized void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothPbap getService() {
-        return (IBluetoothPbap) mProfileConnector.getService();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        log("getConnectedDevices()");
-        final IBluetoothPbap service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return new ArrayList<BluetoothDevice>();
-        }
-        try {
-            return Attributable.setAttributionSource(
-                    service.getConnectedDevices(mAttributionSource), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return new ArrayList<BluetoothDevice>();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
-        log("getConnectionState: device=" + device);
-        try {
-            final IBluetoothPbap service = getService();
-            if (service != null && isEnabled() && isValidDevice(device)) {
-                return service.getConnectionState(device, mAttributionSource);
-            }
-            if (service == null) {
-                Log.w(TAG, "Proxy not attached to service");
-            }
-            return BluetoothProfile.STATE_DISCONNECTED;
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return BluetoothProfile.STATE_DISCONNECTED;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
-        final IBluetoothPbap service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return new ArrayList<BluetoothDevice>();
-        }
-        try {
-            return Attributable.setAttributionSource(
-                    service.getDevicesMatchingConnectionStates(states, mAttributionSource),
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return new ArrayList<BluetoothDevice>();
-    }
-
-    /**
-     * Set connection policy of the profile and tries to disconnect it if connectionPolicy is
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of:
-     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
-     * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        try {
-            final IBluetoothPbap service = getService();
-            if (service != null && isEnabled()
-                    && isValidDevice(device)) {
-                if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
-                    return false;
-                }
-                return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
-            }
-            if (service == null) Log.w(TAG, "Proxy not attached to service");
-            return false;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-            return false;
-        }
-    }
-
-    /**
-     * Disconnects the current Pbap client (PCE). Currently this call blocks,
-     * it may soon be made asynchronous. Returns false if this proxy object is
-     * not currently connected to the Pbap service.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        log("disconnect()");
-        final IBluetoothPbap service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return false;
-        }
-        try {
-            service.disconnect(device, mAttributionSource);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return false;
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        if (DBG) {
-            Log.d(TAG, msg);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
deleted file mode 100644
index e096de8..0000000
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth PBAP Client Profile.
- *
- * @hide
- */
-public final class BluetoothPbapClient implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothPbapClient";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
-
-    /** There was an error trying to obtain the state */
-    public static final int STATE_ERROR = -1;
-
-    public static final int RESULT_FAILURE = 0;
-    public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completion. */
-    public static final int RESULT_CANCELED = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT,
-                    "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
-                @Override
-                public IBluetoothPbapClient getServiceInterface(IBinder service) {
-                    return IBluetoothPbapClient.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothPbapClient proxy object.
-     */
-    BluetoothPbapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        if (DBG) {
-            Log.d(TAG, "Create BluetoothPbapClient proxy object");
-        }
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothPbapClient will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     */
-    public synchronized void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothPbapClient getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Initiate connection.
-     * Upon successful connection to remote PBAP server the Client will
-     * attempt to automatically download the users phonebook and call log.
-     *
-     * @param device a remote device we want connect to
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise;
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) {
-            log("connect(" + device + ") for PBAP Client.");
-        }
-        final IBluetoothPbapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) {
-            log("disconnect(" + device + ")" + new Exception());
-        }
-        final IBluetoothPbapClient service = getService();
-        final boolean defaultValue = true;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-                return true;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of connected devices.
-     * Currently at most one.
-     *
-     * @return list of connected devices
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) {
-            log("getConnectedDevices()");
-        }
-        final IBluetoothPbapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) {
-            log("getDevicesMatchingStates()");
-        }
-        final IBluetoothPbapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) {
-            log("getConnectionState(" + device + ")");
-        }
-        final IBluetoothPbapClient service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) {
-            log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        }
-        final IBluetoothPbapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) {
-            log("getConnectionPolicy(" + device + ")");
-        }
-        final IBluetoothPbapClient service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
deleted file mode 100644
index d0f74e9..0000000
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (C) 2010-2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.RequiresNoPermission;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * Public APIs for the Bluetooth Profiles.
- *
- * <p> Clients should call {@link BluetoothAdapter#getProfileProxy},
- * to get the Profile Proxy. Each public profile implements this
- * interface.
- */
-public interface BluetoothProfile {
-
-    /**
-     * Extra for the connection state intents of the individual profiles.
-     *
-     * This extra represents the current connection state of the profile of the
-     * Bluetooth device.
-     */
-    @SuppressLint("ActionValue")
-    String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
-
-    /**
-     * Extra for the connection state intents of the individual profiles.
-     *
-     * This extra represents the previous connection state of the profile of the
-     * Bluetooth device.
-     */
-    @SuppressLint("ActionValue")
-    String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.profile.extra.PREVIOUS_STATE";
-
-    /** The profile is in disconnected state */
-    int STATE_DISCONNECTED = 0;
-    /** The profile is in connecting state */
-    int STATE_CONNECTING = 1;
-    /** The profile is in connected state */
-    int STATE_CONNECTED = 2;
-    /** The profile is in disconnecting state */
-    int STATE_DISCONNECTING = 3;
-
-    /** @hide */
-    @IntDef({
-            STATE_DISCONNECTED,
-            STATE_CONNECTING,
-            STATE_CONNECTED,
-            STATE_DISCONNECTING,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BtProfileState {}
-
-    /**
-     * Headset and Handsfree profile
-     */
-    int HEADSET = 1;
-
-    /**
-     * A2DP profile.
-     */
-    int A2DP = 2;
-
-    /**
-     * Health Profile
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    int HEALTH = 3;
-
-    /**
-     * HID Host
-     *
-     * @hide
-     */
-    int HID_HOST = 4;
-
-    /**
-     * PAN Profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int PAN = 5;
-
-    /**
-     * PBAP
-     *
-     * @hide
-     */
-    int PBAP = 6;
-
-    /**
-     * GATT
-     */
-    int GATT = 7;
-
-    /**
-     * GATT_SERVER
-     */
-    int GATT_SERVER = 8;
-
-    /**
-     * MAP Profile
-     *
-     * @hide
-     */
-    int MAP = 9;
-
-    /*
-     * SAP Profile
-     * @hide
-     */
-    int SAP = 10;
-
-    /**
-     * A2DP Sink Profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int A2DP_SINK = 11;
-
-    /**
-     * AVRCP Controller Profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int AVRCP_CONTROLLER = 12;
-
-    /**
-     * AVRCP Target Profile
-     *
-     * @hide
-     */
-    int AVRCP = 13;
-
-    /**
-     * Headset Client - HFP HF Role
-     *
-     * @hide
-     */
-    @SystemApi
-    int HEADSET_CLIENT = 16;
-
-    /**
-     * PBAP Client
-     *
-     * @hide
-     */
-    @SystemApi
-    int PBAP_CLIENT = 17;
-
-    /**
-     * MAP Messaging Client Equipment (MCE)
-     *
-     * @hide
-     */
-    @SystemApi
-    int MAP_CLIENT = 18;
-
-    /**
-     * HID Device
-     */
-    int HID_DEVICE = 19;
-
-    /**
-     * Object Push Profile (OPP)
-     *
-     * @hide
-     */
-    int OPP = 20;
-
-    /**
-     * Hearing Aid Device
-     *
-     */
-    int HEARING_AID = 21;
-
-    /**
-     * LE Audio Device
-     *
-     */
-    int LE_AUDIO = 22;
-
-    /**
-     * Volume Control profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int VOLUME_CONTROL = 23;
-
-    /**
-     * @hide
-     * Media Control Profile server
-     *
-     */
-    int MCP_SERVER = 24;
-
-    /**
-     * Coordinated Set Identification Profile set coordinator
-     *
-     */
-    int CSIP_SET_COORDINATOR = 25;
-
-    /**
-     * LE Audio Broadcast Source
-     *
-     * @hide
-     */
-    int LE_AUDIO_BROADCAST = 26;
-
-    /**
-     * @hide
-     * Telephone Bearer Service from Call Control Profile
-     *
-     */
-    int LE_CALL_CONTROL = 27;
-
-    /**
-     * Max profile ID. This value should be updated whenever a new profile is added to match
-     * the largest value assigned to a profile.
-     *
-     * @hide
-     */
-    int MAX_PROFILE_ID = 27;
-
-    /**
-     * Default priority for devices that we try to auto-connect to and
-     * and allow incoming connections for the profile
-     *
-     * @hide
-     **/
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    int PRIORITY_AUTO_CONNECT = 1000;
-
-    /**
-     * Default priority for devices that allow incoming
-     * and outgoing connections for the profile
-     *
-     * @hide
-     * @deprecated Replaced with {@link #CONNECTION_POLICY_ALLOWED}
-     **/
-    @Deprecated
-    @SystemApi
-    int PRIORITY_ON = 100;
-
-    /**
-     * Default priority for devices that does not allow incoming
-     * connections and outgoing connections for the profile.
-     *
-     * @hide
-     * @deprecated Replaced with {@link #CONNECTION_POLICY_FORBIDDEN}
-     **/
-    @Deprecated
-    @SystemApi
-    int PRIORITY_OFF = 0;
-
-    /**
-     * Default priority when not set or when the device is unpaired
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    int PRIORITY_UNDEFINED = -1;
-
-    /** @hide */
-    @IntDef(prefix = "CONNECTION_POLICY_", value = {CONNECTION_POLICY_ALLOWED,
-            CONNECTION_POLICY_FORBIDDEN, CONNECTION_POLICY_UNKNOWN})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ConnectionPolicy{}
-
-    /**
-     * Default connection policy for devices that allow incoming and outgoing connections
-     * for the profile
-     *
-     * @hide
-     **/
-    @SystemApi
-    int CONNECTION_POLICY_ALLOWED = 100;
-
-    /**
-     * Default connection policy for devices that do not allow incoming or outgoing connections
-     * for the profile.
-     *
-     * @hide
-     **/
-    @SystemApi
-    int CONNECTION_POLICY_FORBIDDEN = 0;
-
-    /**
-     * Default connection policy when not set or when the device is unpaired
-     *
-     * @hide
-     */
-    @SystemApi
-    int CONNECTION_POLICY_UNKNOWN = -1;
-
-    /**
-     * Get connected devices for this specific profile.
-     *
-     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
-     *
-     * @return List of devices. The list will be empty on error.
-     */
-    public List<BluetoothDevice> getConnectedDevices();
-
-    /**
-     * Get a list of devices that match any of the given connection
-     * states.
-     *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
-     */
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
-
-    /**
-     * Get the current connection state of the profile
-     *
-     * @param device Remote bluetooth device.
-     * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
-     */
-    @BtProfileState int getConnectionState(BluetoothDevice device);
-
-    /**
-     * An interface for notifying BluetoothProfile IPC clients when they have
-     * been connected or disconnected to the service.
-     */
-    public interface ServiceListener {
-        /**
-         * Called to notify the client when the proxy object has been
-         * connected to the service.
-         *
-         * @param profile - One of {@link #HEADSET} or {@link #A2DP}
-         * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
-         */
-        @RequiresNoPermission
-        public void onServiceConnected(int profile, BluetoothProfile proxy);
-
-        /**
-         * Called to notify the client that this proxy object has been
-         * disconnected from the service.
-         *
-         * @param profile - One of {@link #HEADSET} or {@link #A2DP}
-         */
-        @RequiresNoPermission
-        public void onServiceDisconnected(int profile);
-    }
-
-    /**
-     * Convert an integer value of connection state into human readable string
-     *
-     * @param connectionState - One of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, or {@link #STATE_DISCONNECTED}
-     * @return a string representation of the connection state, STATE_UNKNOWN if the state
-     * is not defined
-     * @hide
-     */
-    static String getConnectionStateName(int connectionState) {
-        switch (connectionState) {
-            case STATE_DISCONNECTED:
-                return "STATE_DISCONNECTED";
-            case STATE_CONNECTING:
-                return "STATE_CONNECTING";
-            case STATE_CONNECTED:
-                return "STATE_CONNECTED";
-            case STATE_DISCONNECTING:
-                return "STATE_DISCONNECTING";
-            default:
-                return "STATE_UNKNOWN";
-        }
-    }
-
-    /**
-     * Convert an integer value of profile ID into human readable string
-     *
-     * @param profile profile ID
-     * @return profile name as String, UNKOWN_PROFILE if the profile ID is not defined.
-     * @hide
-     */
-    static String getProfileName(int profile) {
-        switch(profile) {
-            case HEADSET:
-                return "HEADSET";
-            case A2DP:
-                return "A2DP";
-            case HID_HOST:
-                return "HID_HOST";
-            case PAN:
-                return "PAN";
-            case PBAP:
-                return "PBAP";
-            case GATT:
-                return "GATT";
-            case GATT_SERVER:
-                return "GATT_SERVER";
-            case MAP:
-                return "MAP";
-            case SAP:
-                return "SAP";
-            case A2DP_SINK:
-                return "A2DP_SINK";
-            case AVRCP_CONTROLLER:
-                return "AVRCP_CONTROLLER";
-            case AVRCP:
-                return "AVRCP";
-            case HEADSET_CLIENT:
-                return "HEADSET_CLIENT";
-            case PBAP_CLIENT:
-                return "PBAP_CLIENT";
-            case MAP_CLIENT:
-                return "MAP_CLIENT";
-            case HID_DEVICE:
-                return "HID_DEVICE";
-            case OPP:
-                return "OPP";
-            case HEARING_AID:
-                return "HEARING_AID";
-            case LE_AUDIO:
-                return "LE_AUDIO";
-            default:
-                return "UNKNOWN_PROFILE";
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
deleted file mode 100644
index a457679..0000000
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ /dev/null
@@ -1,220 +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 android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import java.util.List;
-/**
- * Connector for Bluetooth profile proxies to bind manager service and
- * profile services
- * @param <T> The Bluetooth profile interface for this connection.
- * @hide
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public abstract class BluetoothProfileConnector<T> {
-    private final CloseGuard mCloseGuard = new CloseGuard();
-    private final int mProfileId;
-    private BluetoothProfile.ServiceListener mServiceListener;
-    private final BluetoothProfile mProfileProxy;
-    private Context mContext;
-    private final String mProfileName;
-    private final String mServiceName;
-    private volatile T mService;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-        public void onBluetoothStateChange(boolean up) {
-            if (up) {
-                doBind();
-            } else {
-                doUnbind();
-            }
-        }
-    };
-
-    private @Nullable ComponentName resolveSystemService(@NonNull Intent intent,
-            @NonNull PackageManager pm) {
-        List<ResolveInfo> results = pm.queryIntentServices(intent,
-                PackageManager.ResolveInfoFlags.of(0));
-        if (results == null) {
-            return null;
-        }
-        ComponentName comp = null;
-        for (int i = 0; i < results.size(); i++) {
-            ResolveInfo ri = results.get(i);
-            if ((ri.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                continue;
-            }
-            ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,
-                    ri.serviceInfo.name);
-            if (comp != null) {
-                throw new IllegalStateException("Multiple system services handle " + intent
-                        + ": " + comp + ", " + foundComp);
-            }
-            comp = foundComp;
-        }
-        return comp;
-    }
-
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            logDebug("Proxy object connected");
-            mService = getServiceInterface(service);
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(mProfileId, mProfileProxy);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            logDebug("Proxy object disconnected");
-            doUnbind();
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(mProfileId);
-            }
-        }
-    };
-
-    BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
-            String serviceName) {
-        mProfileId = profileId;
-        mProfileProxy = profile;
-        mProfileName = profileName;
-        mServiceName = serviceName;
-    }
-
-    /** {@hide} */
-    @Override
-    public void finalize() {
-        mCloseGuard.warnIfOpen();
-        doUnbind();
-    }
-
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private boolean doBind() {
-        synchronized (mConnection) {
-            if (mService == null) {
-                logDebug("Binding service...");
-                mCloseGuard.open("doUnbind");
-                try {
-                    Intent intent = new Intent(mServiceName);
-                    ComponentName comp = resolveSystemService(intent, mContext.getPackageManager());
-                    intent.setComponent(comp);
-                    if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                            UserHandle.CURRENT)) {
-                        logError("Could not bind to Bluetooth Service with " + intent);
-                        return false;
-                    }
-                } catch (SecurityException se) {
-                    logError("Failed to bind service. " + se);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    private void doUnbind() {
-        synchronized (mConnection) {
-            if (mService != null) {
-                logDebug("Unbinding service...");
-                mCloseGuard.close();
-                try {
-                    mContext.unbindService(mConnection);
-                } catch (IllegalArgumentException ie) {
-                    logError("Unable to unbind service: " + ie);
-                } finally {
-                    mService = null;
-                }
-            }
-        }
-    }
-
-    void connect(Context context, BluetoothProfile.ServiceListener listener) {
-        mContext = context;
-        mServiceListener = listener;
-        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
-
-        // Preserve legacy compatibility where apps were depending on
-        // registerStateChangeCallback() performing a permissions check which
-        // has been relaxed in modern platform versions
-        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
-                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Need BLUETOOTH permission");
-        }
-
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                logError("Failed to register state change callback. " + re);
-            }
-        }
-        doBind();
-    }
-
-    void disconnect() {
-        mServiceListener = null;
-        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                logError("Failed to unregister state change callback" + re);
-            }
-        }
-        doUnbind();
-    }
-
-    T getService() {
-        return mService;
-    }
-
-    /**
-     * This abstract function is used to implement method to get the
-     * connected Bluetooth service interface.
-     * @param service the connected binder service.
-     * @return T the binder interface of {@code service}.
-     * @hide
-     */
-    public abstract T getServiceInterface(IBinder service);
-
-    private void logDebug(String log) {
-        Log.d(mProfileName, log);
-    }
-
-    private void logError(String log) {
-        Log.e(mProfileName, log);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
deleted file mode 100644
index 808fa39..0000000
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth SIM
- * Access Profile (SAP).
- *
- * <p>BluetoothSap is a proxy object for controlling the Bluetooth
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothSap proxy object.
- *
- * <p>Each method is protected with its appropriate permission.
- *
- * @hide
- */
-public final class BluetoothSap implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothSap";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the profile.
-     *
-     * <p>This intent will have 4 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * There was an error trying to obtain the state.
-     *
-     * @hide
-     */
-    public static final int STATE_ERROR = -1;
-
-    /**
-     * Connection state change succceeded.
-     *
-     * @hide
-     */
-    public static final int RESULT_SUCCESS = 1;
-
-    /**
-     * Connection canceled before completion.
-     *
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.SAP,
-                    "BluetoothSap", IBluetoothSap.class.getName()) {
-                @Override
-                public IBluetoothSap getServiceInterface(IBinder service) {
-                    return IBluetoothSap.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothSap proxy object.
-     */
-    /* package */ BluetoothSap(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothSap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     *
-     * @hide
-     */
-    public synchronized void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothSap getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Get the current state of the BluetoothSap service.
-     *
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
-     * connected to the Sap service.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getState() {
-        if (VDBG) log("getState()");
-        final IBluetoothSap service = getService();
-        final int defaultValue = BluetoothSap.STATE_ERROR;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getState(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the currently connected remote Bluetooth device (PCE).
-     *
-     * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
-     * this proxy object is not connected to the Sap service.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getClient() {
-        if (VDBG) log("getClient()");
-        final IBluetoothSap service = getService();
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getClient(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns true if the specified Bluetooth device is connected.
-     * Returns false if not connected, or if this proxy object is not
-     * currently connected to the Sap service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected(BluetoothDevice device) {
-        if (VDBG) log("isConnected(" + device + ")");
-        final IBluetoothSap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate connection. Initiation of outgoing connections is not
-     * supported for SAP server.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
-        return false;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothSap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
-        final IBluetoothSap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothSap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothSap service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothSap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothSap service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
deleted file mode 100644
index bb4e354..0000000
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.SuppressLint;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Handler;
-import android.os.ParcelUuid;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-/**
- * A listening Bluetooth socket.
- *
- * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
- * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
- * side, use a {@link BluetoothServerSocket} to create a listening server
- * socket. When a connection is accepted by the {@link BluetoothServerSocket},
- * it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both initiate
- * an outgoing connection and to manage the connection.
- *
- * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by
- * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It
- * is also known as the Serial Port Profile (SPP). To create a listening
- * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link
- * BluetoothAdapter#listenUsingRfcommWithServiceRecord
- * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}.
- *
- * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a
- * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control.
- * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel
- * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket}
- * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()}
- * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your
- * socket.
- *
- * <p> After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to
- * listen for incoming connection requests. This call will block until a connection is established,
- * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the
- * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link
- * BluetoothServerSocket} when it's no longer needed for accepting
- * connections. Closing the {@link BluetoothServerSocket} will <em>not</em> close the returned
- * {@link BluetoothSocket}.
- *
- * <p>{@link BluetoothServerSocket} is thread
- * safe. In particular, {@link #close} will always immediately abort ongoing
- * operations and close the server socket.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using Bluetooth, read the
- * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
- * </div>
- *
- * {@see BluetoothSocket}
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class BluetoothServerSocket implements Closeable {
-
-    private static final String TAG = "BluetoothServerSocket";
-    private static final boolean DBG = false;
-    @UnsupportedAppUsage(publicAlternatives = "Use public {@link BluetoothServerSocket} API "
-            + "instead.")
-    /*package*/ final BluetoothSocket mSocket;
-    private Handler mHandler;
-    private int mMessage;
-    private int mChannel;
-
-    /**
-     * Construct a socket for incoming connections.
-     *
-     * @param type type of socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param port remote port
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
-            throws IOException {
-        mChannel = port;
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
-        if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            mSocket.setExcludeSdp(true);
-        }
-    }
-
-    /**
-     * Construct a socket for incoming connections.
-     *
-     * @param type type of socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param port remote port
-     * @param mitm enforce person-in-the-middle protection for authentication.
-     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port,
-            boolean mitm, boolean min16DigitPin)
-            throws IOException {
-        mChannel = port;
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm,
-                min16DigitPin);
-        if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            mSocket.setExcludeSdp(true);
-        }
-    }
-
-    /**
-     * Construct a socket for incoming connections.
-     *
-     * @param type type of socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param uuid uuid
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)
-            throws IOException {
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid);
-        // TODO: This is the same as mChannel = -1 - is this intentional?
-        mChannel = mSocket.getPort();
-    }
-
-
-    /**
-     * Block until a connection is established.
-     * <p>Returns a connected {@link BluetoothSocket} on successful connection.
-     * <p>Once this call returns, it can be called again to accept subsequent
-     * incoming connections.
-     * <p>{@link #close} can be used to abort this call from another thread.
-     *
-     * @return a connected {@link BluetoothSocket}
-     * @throws IOException on error, for example this call was aborted, or timeout
-     */
-    public BluetoothSocket accept() throws IOException {
-        return accept(-1);
-    }
-
-    /**
-     * Block until a connection is established, with timeout.
-     * <p>Returns a connected {@link BluetoothSocket} on successful connection.
-     * <p>Once this call returns, it can be called again to accept subsequent
-     * incoming connections.
-     * <p>{@link #close} can be used to abort this call from another thread.
-     *
-     * @return a connected {@link BluetoothSocket}
-     * @throws IOException on error, for example this call was aborted, or timeout
-     */
-    public BluetoothSocket accept(int timeout) throws IOException {
-        return mSocket.accept(timeout);
-    }
-
-    /**
-     * Immediately close this socket, and release all associated resources.
-     * <p>Causes blocked calls on this socket in other threads to immediately
-     * throw an IOException.
-     * <p>Closing the {@link BluetoothServerSocket} will <em>not</em>
-     * close any {@link BluetoothSocket} received from {@link #accept()}.
-     */
-    public void close() throws IOException {
-        if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
-        synchronized (this) {
-            if (mHandler != null) {
-                mHandler.obtainMessage(mMessage).sendToTarget();
-            }
-        }
-        mSocket.close();
-    }
-
-    /*package*/
-    synchronized void setCloseHandler(Handler handler, int message) {
-        mHandler = handler;
-        mMessage = message;
-    }
-
-    /*package*/ void setServiceName(String serviceName) {
-        mSocket.setServiceName(serviceName);
-    }
-
-    /**
-     * Returns the channel on which this socket is bound.
-     *
-     * @hide
-     */
-    public int getChannel() {
-        return mChannel;
-    }
-
-    /**
-     * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
-     * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
-     * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link
-     * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this
-     * method is called on non-L2CAP server sockets.
-     *
-     * @return the assigned PSM or LE_PSM value depending on transport
-     */
-    public int getPsm() {
-        return mChannel;
-    }
-
-    /**
-     * Sets the channel on which future sockets are bound.
-     * Currently used only when a channel is auto generated.
-     */
-    /*package*/ void setChannel(int newChannel) {
-        /* TODO: From a design/architecture perspective this is wrong.
-         *       The bind operation should be conducted through this class
-         *       and the resulting port should be kept in mChannel, and
-         *       not set from BluetoothAdapter. */
-        if (mSocket != null) {
-            if (mSocket.getPort() != newChannel) {
-                Log.w(TAG, "The port set is different that the underlying port. mSocket.getPort(): "
-                        + mSocket.getPort() + " requested newChannel: " + newChannel);
-            }
-        }
-        mChannel = newChannel;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("ServerSocket: Type: ");
-        switch (mSocket.getConnectionType()) {
-            case BluetoothSocket.TYPE_RFCOMM: {
-                sb.append("TYPE_RFCOMM");
-                break;
-            }
-            case BluetoothSocket.TYPE_L2CAP: {
-                sb.append("TYPE_L2CAP");
-                break;
-            }
-            case BluetoothSocket.TYPE_L2CAP_LE: {
-                sb.append("TYPE_L2CAP_LE");
-                break;
-            }
-            case BluetoothSocket.TYPE_SCO: {
-                sb.append("TYPE_SCO");
-                break;
-            }
-        }
-        sb.append(" Channel: ").append(mChannel);
-        return sb.toString();
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
deleted file mode 100644
index db5b751..0000000
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ /dev/null
@@ -1,809 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.net.LocalSocket;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.Locale;
-import java.util.UUID;
-
-/**
- * A connected or connecting Bluetooth socket.
- *
- * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
- * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
- * side, use a {@link BluetoothServerSocket} to create a listening server
- * socket. When a connection is accepted by the {@link BluetoothServerSocket},
- * it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both initiate
- * an outgoing connection and to manage the connection.
- *
- * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
- * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
- * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
- *
- * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
- * {@link BluetoothDevice#createRfcommSocketToServiceRecord
- * BluetoothDevice.createRfcommSocketToServiceRecord()}.
- * Then call {@link #connect()} to attempt a connection to the remote device.
- * This call will block until a connection is established or the connection
- * fails.
- *
- * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
- * {@link BluetoothServerSocket} documentation.
- *
- * <p>Once the socket is connected, whether initiated as a client or accepted
- * as a server, open the IO streams by calling {@link #getInputStream} and
- * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
- * and {@link java.io.OutputStream} objects, respectively, which are
- * automatically connected to the socket.
- *
- * <p>{@link BluetoothSocket} is thread
- * safe. In particular, {@link #close} will always immediately abort ongoing
- * operations and close the socket.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using Bluetooth, read the
- * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
- * </div>
- *
- * {@see BluetoothServerSocket}
- * {@see java.io.InputStream}
- * {@see java.io.OutputStream}
- */
-public final class BluetoothSocket implements Closeable {
-    private static final String TAG = "BluetoothSocket";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
-
-    /** @hide */
-    public static final int MAX_RFCOMM_CHANNEL = 30;
-    /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
-
-    /** RFCOMM socket */
-    public static final int TYPE_RFCOMM = 1;
-
-    /** SCO socket */
-    public static final int TYPE_SCO = 2;
-
-    /** L2CAP socket */
-    public static final int TYPE_L2CAP = 3;
-
-    /** L2CAP socket on BR/EDR transport
-     * @hide
-     */
-    public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;
-
-    /** L2CAP socket on LE transport
-     * @hide
-     */
-    public static final int TYPE_L2CAP_LE = 4;
-
-    /*package*/ static final int EBADFD = 77;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    /*package*/ static final int EADDRINUSE = 98;
-
-    /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
-    /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
-    /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2;
-    /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3;
-    /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4;
-
-    private final int mType;  /* one of TYPE_RFCOMM etc */
-    private BluetoothDevice mDevice;    /* remote device */
-    private String mAddress;    /* remote address */
-    private final boolean mAuth;
-    private final boolean mEncrypt;
-    private final BluetoothInputStream mInputStream;
-    private final BluetoothOutputStream mOutputStream;
-    private final ParcelUuid mUuid;
-    /** when true no SPP SDP record will be created */
-    private boolean mExcludeSdp = false;
-    /** when true Person-in-the-middle protection will be enabled */
-    private boolean mAuthMitm = false;
-    /** Minimum 16 digit pin for sec mode 2 connections */
-    private boolean mMin16DigitPin = false;
-    @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.")
-    private ParcelFileDescriptor mPfd;
-    @UnsupportedAppUsage
-    private LocalSocket mSocket;
-    private InputStream mSocketIS;
-    private OutputStream mSocketOS;
-    @UnsupportedAppUsage
-    private int mPort;  /* RFCOMM channel or L2CAP psm */
-    private int mFd;
-    private String mServiceName;
-    private static final int PROXY_CONNECTION_TIMEOUT = 5000;
-
-    private static final int SOCK_SIGNAL_SIZE = 20;
-
-    private ByteBuffer mL2capBuffer = null;
-    private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
-    private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
-
-    private enum SocketState {
-        INIT,
-        CONNECTED,
-        LISTENING,
-        CLOSED,
-    }
-
-    /** prevents all native calls after destroyNative() */
-    private volatile SocketState mSocketState;
-
-    /** protects mSocketState */
-    //private final ReentrantReadWriteLock mLock;
-
-    /**
-     * Construct a BluetoothSocket.
-     *
-     * @param type type of socket
-     * @param fd fd to use for connected socket, or -1 for a new socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param device remote device that this socket can connect to
-     * @param port remote port
-     * @param uuid SDP uuid
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
-            BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
-        this(type, fd, auth, encrypt, device, port, uuid, false, false);
-    }
-
-    /**
-     * Construct a BluetoothSocket.
-     *
-     * @param type type of socket
-     * @param fd fd to use for connected socket, or -1 for a new socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param device remote device that this socket can connect to
-     * @param port remote port
-     * @param uuid SDP uuid
-     * @param mitm enforce person-in-the-middle protection.
-     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
-            BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin)
-            throws IOException {
-        if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
-        if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
-                && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
-                throw new IOException("Invalid RFCOMM channel: " + port);
-            }
-        }
-        if (uuid != null) {
-            mUuid = uuid;
-        } else {
-            mUuid = new ParcelUuid(new UUID(0, 0));
-        }
-        mType = type;
-        mAuth = auth;
-        mAuthMitm = mitm;
-        mMin16DigitPin = min16DigitPin;
-        mEncrypt = encrypt;
-        mDevice = device;
-        mPort = port;
-        mFd = fd;
-
-        mSocketState = SocketState.INIT;
-
-        if (device == null) {
-            // Server socket
-            mAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
-        } else {
-            // Remote socket
-            mAddress = device.getAddress();
-        }
-        mInputStream = new BluetoothInputStream(this);
-        mOutputStream = new BluetoothOutputStream(this);
-    }
-
-    private BluetoothSocket(BluetoothSocket s) {
-        if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
-        mUuid = s.mUuid;
-        mType = s.mType;
-        mAuth = s.mAuth;
-        mEncrypt = s.mEncrypt;
-        mPort = s.mPort;
-        mInputStream = new BluetoothInputStream(this);
-        mOutputStream = new BluetoothOutputStream(this);
-        mMaxRxPacketSize = s.mMaxRxPacketSize;
-        mMaxTxPacketSize = s.mMaxTxPacketSize;
-
-        mServiceName = s.mServiceName;
-        mExcludeSdp = s.mExcludeSdp;
-        mAuthMitm = s.mAuthMitm;
-        mMin16DigitPin = s.mMin16DigitPin;
-    }
-
-    private BluetoothSocket acceptSocket(String remoteAddr) throws IOException {
-        BluetoothSocket as = new BluetoothSocket(this);
-        as.mSocketState = SocketState.CONNECTED;
-        FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
-        if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds));
-        if (fds == null || fds.length != 1) {
-            Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds));
-            as.close();
-            throw new IOException("bt socket acept failed");
-        }
-
-        as.mPfd = ParcelFileDescriptor.dup(fds[0]);
-        as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]);
-        as.mSocketIS = as.mSocket.getInputStream();
-        as.mSocketOS = as.mSocket.getOutputStream();
-        as.mAddress = remoteAddr;
-        as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
-        return as;
-    }
-
-    /**
-     * Construct a BluetoothSocket from address. Used by native code.
-     *
-     * @param type type of socket
-     * @param fd fd to use for connected socket, or -1 for a new socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param address remote device that this socket can connect to
-     * @param port remote port
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
-            int port) throws IOException {
-        this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false);
-    }
-
-    /** @hide */
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private int getSecurityFlags() {
-        int flags = 0;
-        if (mAuth) {
-            flags |= SEC_FLAG_AUTH;
-        }
-        if (mEncrypt) {
-            flags |= SEC_FLAG_ENCRYPT;
-        }
-        if (mExcludeSdp) {
-            flags |= BTSOCK_FLAG_NO_SDP;
-        }
-        if (mAuthMitm) {
-            flags |= SEC_FLAG_AUTH_MITM;
-        }
-        if (mMin16DigitPin) {
-            flags |= SEC_FLAG_AUTH_16_DIGIT;
-        }
-        return flags;
-    }
-
-    /**
-     * Get the remote device this socket is connecting, or connected, to.
-     *
-     * @return remote device
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getRemoteDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Get the input stream associated with this socket.
-     * <p>The input stream will be returned even if the socket is not yet
-     * connected, but operations on that stream will throw IOException until
-     * the associated socket is connected.
-     *
-     * @return InputStream
-     */
-    @RequiresNoPermission
-    public InputStream getInputStream() throws IOException {
-        return mInputStream;
-    }
-
-    /**
-     * Get the output stream associated with this socket.
-     * <p>The output stream will be returned even if the socket is not yet
-     * connected, but operations on that stream will throw IOException until
-     * the associated socket is connected.
-     *
-     * @return OutputStream
-     */
-    @RequiresNoPermission
-    public OutputStream getOutputStream() throws IOException {
-        return mOutputStream;
-    }
-
-    /**
-     * Get the connection status of this socket, ie, whether there is an active connection with
-     * remote device.
-     *
-     * @return true if connected false if not connected
-     */
-    @RequiresNoPermission
-    public boolean isConnected() {
-        return mSocketState == SocketState.CONNECTED;
-    }
-
-    /*package*/ void setServiceName(String name) {
-        mServiceName = name;
-    }
-
-    /**
-     * Attempt to connect to a remote device.
-     * <p>This method will block until a connection is made or the connection
-     * fails. If this method returns without an exception then this socket
-     * is now connected.
-     * <p>Creating new connections to
-     * remote Bluetooth devices should not be attempted while device discovery
-     * is in progress. Device discovery is a heavyweight procedure on the
-     * Bluetooth adapter and will significantly slow a device connection.
-     * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
-     * discovery. Discovery is not managed by the Activity,
-     * but is run as a system service, so an application should always call
-     * {@link BluetoothAdapter#cancelDiscovery()} even if it
-     * did not directly request a discovery, just to be sure.
-     * <p>{@link #close} can be used to abort this call from another thread.
-     *
-     * @throws IOException on error, for example connection failure
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void connect() throws IOException {
-        if (mDevice == null) throw new IOException("Connect is called on null device");
-
-        try {
-            if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
-            IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
-            if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
-            mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
-                    mUuid, mPort, getSecurityFlags());
-            synchronized (this) {
-                if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
-                if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
-                if (mPfd == null) throw new IOException("bt socket connect failed");
-                FileDescriptor fd = mPfd.getFileDescriptor();
-                mSocket = LocalSocket.createConnectedLocalSocket(fd);
-                mSocketIS = mSocket.getInputStream();
-                mSocketOS = mSocket.getOutputStream();
-            }
-            int channel = readInt(mSocketIS);
-            if (channel <= 0) {
-                throw new IOException("bt socket connect failed");
-            }
-            mPort = channel;
-            waitSocketSignal(mSocketIS);
-            synchronized (this) {
-                if (mSocketState == SocketState.CLOSED) {
-                    throw new IOException("bt socket closed");
-                }
-                mSocketState = SocketState.CONNECTED;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            throw new IOException("unable to send RPC: " + e.getMessage());
-        }
-    }
-
-    /**
-     * Currently returns unix errno instead of throwing IOException,
-     * so that BluetoothAdapter can check the error code for EADDRINUSE
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ int bindListen() {
-        int ret;
-        if (mSocketState == SocketState.CLOSED) return EBADFD;
-        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
-        if (bluetoothProxy == null) {
-            Log.e(TAG, "bindListen fail, reason: bluetooth is off");
-            return -1;
-        }
-        try {
-            if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
-            mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
-                    mUuid, mPort, getSecurityFlags());
-        } catch (RemoteException e) {
-            Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            return -1;
-        }
-
-        // read out port number
-        try {
-            synchronized (this) {
-                if (DBG) {
-                    Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
-                }
-                if (mSocketState != SocketState.INIT) return EBADFD;
-                if (mPfd == null) return -1;
-                FileDescriptor fd = mPfd.getFileDescriptor();
-                if (fd == null) {
-                    Log.e(TAG, "bindListen(), null file descriptor");
-                    return -1;
-                }
-
-                if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
-                mSocket = LocalSocket.createConnectedLocalSocket(fd);
-                if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
-                mSocketIS = mSocket.getInputStream();
-                mSocketOS = mSocket.getOutputStream();
-            }
-            if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
-            int channel = readInt(mSocketIS);
-            synchronized (this) {
-                if (mSocketState == SocketState.INIT) {
-                    mSocketState = SocketState.LISTENING;
-                }
-            }
-            if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
-            if (mPort <= -1) {
-                mPort = channel;
-            } // else ASSERT(mPort == channel)
-            ret = 0;
-        } catch (IOException e) {
-            if (mPfd != null) {
-                try {
-                    mPfd.close();
-                } catch (IOException e1) {
-                    Log.e(TAG, "bindListen, close mPfd: " + e1);
-                }
-                mPfd = null;
-            }
-            Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
-            return -1;
-        }
-        return ret;
-    }
-
-    /*package*/ BluetoothSocket accept(int timeout) throws IOException {
-        BluetoothSocket acceptedSocket;
-        if (mSocketState != SocketState.LISTENING) {
-            throw new IOException("bt socket is not in listen state");
-        }
-        if (timeout > 0) {
-            Log.d(TAG, "accept() set timeout (ms):" + timeout);
-            mSocket.setSoTimeout(timeout);
-        }
-        String RemoteAddr = waitSocketSignal(mSocketIS);
-        if (timeout > 0) {
-            mSocket.setSoTimeout(0);
-        }
-        synchronized (this) {
-            if (mSocketState != SocketState.LISTENING) {
-                throw new IOException("bt socket is not in listen state");
-            }
-            acceptedSocket = acceptSocket(RemoteAddr);
-            //quick drop the reference of the file handle
-        }
-        return acceptedSocket;
-    }
-
-    /*package*/ int available() throws IOException {
-        if (VDBG) Log.d(TAG, "available: " + mSocketIS);
-        return mSocketIS.available();
-    }
-
-    /*package*/ int read(byte[] b, int offset, int length) throws IOException {
-        int ret = 0;
-        if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
-        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
-            int bytesToRead = length;
-            if (VDBG) {
-                Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
-                        + "mL2capBuffer= " + mL2capBuffer);
-            }
-            if (mL2capBuffer == null) {
-                createL2capRxBuffer();
-            }
-            if (mL2capBuffer.remaining() == 0) {
-                if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
-                if (fillL2capRxBuffer() == -1) {
-                    return -1;
-                }
-            }
-            if (bytesToRead > mL2capBuffer.remaining()) {
-                bytesToRead = mL2capBuffer.remaining();
-            }
-            if (VDBG) {
-                Log.v(TAG, "get(): offset: " + offset
-                        + " bytesToRead: " + bytesToRead);
-            }
-            mL2capBuffer.get(b, offset, bytesToRead);
-            ret = bytesToRead;
-        } else {
-            if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
-            ret = mSocketIS.read(b, offset, length);
-        }
-        if (ret < 0) {
-            throw new IOException("bt socket closed, read return: " + ret);
-        }
-        if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
-        return ret;
-    }
-
-    /*package*/ int write(byte[] b, int offset, int length) throws IOException {
-
-        //TODO: Since bindings can exist between the SDU size and the
-        //      protocol, we might need to throw an exception instead of just
-        //      splitting the write into multiple smaller writes.
-        //      Rfcomm uses dynamic allocation, and should not have any bindings
-        //      to the actual message length.
-        if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
-        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
-            if (length <= mMaxTxPacketSize) {
-                mSocketOS.write(b, offset, length);
-            } else {
-                if (DBG) {
-                    Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
-                            + "Packet will be divided into SDU packets of size "
-                            + mMaxTxPacketSize);
-                }
-                int tmpOffset = offset;
-                int bytesToWrite = length;
-                while (bytesToWrite > 0) {
-                    int tmpLength = (bytesToWrite > mMaxTxPacketSize)
-                            ? mMaxTxPacketSize
-                            : bytesToWrite;
-                    mSocketOS.write(b, tmpOffset, tmpLength);
-                    tmpOffset += tmpLength;
-                    bytesToWrite -= tmpLength;
-                }
-            }
-        } else {
-            mSocketOS.write(b, offset, length);
-        }
-        // There is no good way to confirm since the entire process is asynchronous anyway
-        if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
-        return length;
-    }
-
-    @Override
-    public void close() throws IOException {
-        Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS
-                + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket + ", mSocketState: "
-                + mSocketState);
-        if (mSocketState == SocketState.CLOSED) {
-            return;
-        } else {
-            synchronized (this) {
-                if (mSocketState == SocketState.CLOSED) {
-                    return;
-                }
-                mSocketState = SocketState.CLOSED;
-                if (mSocket != null) {
-                    if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
-                    mSocket.shutdownInput();
-                    mSocket.shutdownOutput();
-                    mSocket.close();
-                    mSocket = null;
-                }
-                if (mPfd != null) {
-                    mPfd.close();
-                    mPfd = null;
-                }
-            }
-        }
-    }
-
-    /*package */ void removeChannel() {
-    }
-
-    /*package */ int getPort() {
-        return mPort;
-    }
-
-    /**
-     * Get the maximum supported Transmit packet size for the underlying transport.
-     * Use this to optimize the writes done to the output socket, to avoid sending
-     * half full packets.
-     *
-     * @return the maximum supported Transmit packet size for the underlying transport.
-     */
-    @RequiresNoPermission
-    public int getMaxTransmitPacketSize() {
-        return mMaxTxPacketSize;
-    }
-
-    /**
-     * Get the maximum supported Receive packet size for the underlying transport.
-     * Use this to optimize the reads done on the input stream, as any call to read
-     * will return a maximum of this amount of bytes - or for some transports a
-     * multiple of this value.
-     *
-     * @return the maximum supported Receive packet size for the underlying transport.
-     */
-    @RequiresNoPermission
-    public int getMaxReceivePacketSize() {
-        return mMaxRxPacketSize;
-    }
-
-    /**
-     * Get the type of the underlying connection.
-     *
-     * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
-     */
-    @RequiresNoPermission
-    public int getConnectionType() {
-        if (mType == TYPE_L2CAP_LE) {
-            // Treat the LE CoC to be the same type as L2CAP.
-            return TYPE_L2CAP;
-        }
-        return mType;
-    }
-
-    /**
-     * Change if a SDP entry should be automatically created.
-     * Must be called before calling .bind, for the call to have any effect.
-     *
-     * @param excludeSdp <li>TRUE - do not auto generate SDP record. <li>FALSE - default - auto
-     * generate SPP SDP record.
-     * @hide
-     */
-    @RequiresNoPermission
-    public void setExcludeSdp(boolean excludeSdp) {
-        mExcludeSdp = excludeSdp;
-    }
-
-    /**
-     * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This
-     * parameter is used by the BT Controller to set the maximum transmission packet size on this
-     * connection. This function is currently used for testing only.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void requestMaximumTxDataLength() throws IOException {
-        if (mDevice == null) {
-            throw new IOException("requestMaximumTxDataLength is called on null device");
-        }
-
-        try {
-            if (mSocketState == SocketState.CLOSED) {
-                throw new IOException("socket closed");
-            }
-            IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
-            if (bluetoothProxy == null) {
-                throw new IOException("Bluetooth is off");
-            }
-
-            if (DBG) Log.d(TAG, "requestMaximumTxDataLength");
-            bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice);
-        } catch (RemoteException e) {
-            Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            throw new IOException("unable to send RPC: " + e.getMessage());
-        }
-    }
-
-    private String convertAddr(final byte[] addr) {
-        return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
-                addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
-    }
-
-    private String waitSocketSignal(InputStream is) throws IOException {
-        byte[] sig = new byte[SOCK_SIGNAL_SIZE];
-        int ret = readAll(is, sig);
-        if (VDBG) {
-            Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + " bytes signal ret: " + ret);
-        }
-        ByteBuffer bb = ByteBuffer.wrap(sig);
-        /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
-        bb.order(ByteOrder.nativeOrder());
-        int size = bb.getShort();
-        if (size != SOCK_SIGNAL_SIZE) {
-            throw new IOException("Connection failure, wrong signal size: " + size);
-        }
-        byte[] addr = new byte[6];
-        bb.get(addr);
-        int channel = bb.getInt();
-        int status = bb.getInt();
-        mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
-        mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
-        String RemoteAddr = convertAddr(addr);
-        if (VDBG) {
-            Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
-                    + RemoteAddr + ", channel: " + channel + ", status: " + status
-                    + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
-        }
-        if (status != 0) {
-            throw new IOException("Connection failure, status: " + status);
-        }
-        return RemoteAddr;
-    }
-
-    private void createL2capRxBuffer() {
-        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
-            // Allocate the buffer to use for reads.
-            if (VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
-            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
-            if (VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
-            mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
-            if (VDBG) {
-                Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + mL2capBuffer.remaining());
-            }
-        }
-    }
-
-    private int readAll(InputStream is, byte[] b) throws IOException {
-        int left = b.length;
-        while (left > 0) {
-            int ret = is.read(b, b.length - left, left);
-            if (ret <= 0) {
-                throw new IOException("read failed, socket might closed or timeout, read ret: "
-                        + ret);
-            }
-            left -= ret;
-            if (left != 0) {
-                Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left)
-                        + ", expect size: " + b.length);
-            }
-        }
-        return b.length;
-    }
-
-    private int readInt(InputStream is) throws IOException {
-        byte[] ibytes = new byte[4];
-        int ret = readAll(is, ibytes);
-        if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
-        ByteBuffer bb = ByteBuffer.wrap(ibytes);
-        bb.order(ByteOrder.nativeOrder());
-        return bb.getInt();
-    }
-
-    private int fillL2capRxBuffer() throws IOException {
-        mL2capBuffer.rewind();
-        int ret = mSocketIS.read(mL2capBuffer.array());
-        if (ret == -1) {
-            // reached end of stream - return -1
-            mL2capBuffer.limit(0);
-            return -1;
-        }
-        mL2capBuffer.limit(ret);
-        return ret;
-    }
-
-
-}
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
deleted file mode 100644
index fff32ff..0000000
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.SystemApi;
-
-/**
- * A class with constants representing possible return values for Bluetooth APIs. General return
- * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999.
- * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which
- * occupies the max integer value.
- */
-public final class BluetoothStatusCodes {
-
-    private BluetoothStatusCodes() {}
-
-    /**
-     * Indicates that the API call was successful
-     */
-    public static final int SUCCESS = 0;
-
-    /**
-     * Error code indicating that Bluetooth is not enabled
-     */
-    public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1;
-
-    /**
-     * Error code indicating that the API call was initiated by neither the system nor the active
-     * user
-     */
-    public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;
-
-    /**
-     * Error code indicating that the Bluetooth Device specified is not bonded
-     */
-    public static final int ERROR_DEVICE_NOT_BONDED = 3;
-
-    /**
-     * Error code indicating that the Bluetooth Device specified is not connected, but is bonded
-     *
-     * @hide
-     */
-    public static final int ERROR_DEVICE_NOT_CONNECTED = 4;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission
-     *
-     * @hide
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_ADVERTISE_PERMISSION = 5;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission
-     *
-     * @hide
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8;
-
-    /**
-     * Error code indicating that the profile service is not bound. You can bind a profile service
-     * by calling {@link BluetoothAdapter#getProfileProxy}
-     */
-    public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9;
-
-    /**
-     * Error code indicating that the feature is not supported.
-     */
-    public static final int ERROR_FEATURE_NOT_SUPPORTED = 10;
-
-    /**
-     * A GATT writeCharacteristic request is not permitted on the remote device.
-     */
-    public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101;
-
-    /**
-     * A GATT writeCharacteristic request is issued to a busy remote device.
-     */
-    public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102;
-
-    /**
-     * If another application has already requested {@link OobData} then another fetch will be
-     * disallowed until the callback is removed.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000;
-
-    /**
-     * Indicates that the ACL disconnected due to an explicit request from the local device.
-     * <p>
-     * Example cause: This is a normal disconnect reason, e.g., user/app initiates
-     * disconnection.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_LOCAL_REQUEST = 1100;
-
-    /**
-     * Indicates that the ACL disconnected due to an explicit request from the remote device.
-     * <p>
-     * Example cause: This is a normal disconnect reason, e.g., user/app initiates
-     * disconnection.
-     * <p>
-     * Example solution: The app can also prompt the user to check their remote device.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_REMOTE_REQUEST = 1101;
-
-    /**
-     * Generic disconnect reason indicating the ACL disconnected due to an error on the local
-     * device.
-     * <p>
-     * Example solution: Prompt the user to check their local device (e.g., phone, car
-     * headunit).
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_LOCAL = 1102;
-
-    /**
-     * Generic disconnect reason indicating the ACL disconnected due to an error on the remote
-     * device.
-     * <p>
-     * Example solution: Prompt the user to check their remote device (e.g., headset, car
-     * headunit, watch).
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_REMOTE = 1103;
-
-    /**
-     * Indicates that the ACL disconnected due to a timeout.
-     * <p>
-     * Example cause: remote device might be out of range.
-     * <p>
-     * Example solution: Prompt user to verify their remote device is on or in
-     * connection/pairing mode.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_TIMEOUT = 1104;
-
-    /**
-     * Indicates that the ACL disconnected due to link key issues.
-     * <p>
-     * Example cause: Devices are either unpaired or remote device is refusing our pairing
-     * request.
-     * <p>
-     * Example solution: Prompt user to unpair and pair again.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_SECURITY = 1105;
-
-    /**
-     * Indicates that the ACL disconnected due to the local device's system policy.
-     * <p>
-     * Example cause: privacy policy, power management policy, permissions, etc.
-     * <p>
-     * Example solution: Prompt the user to check settings, or check with their system
-     * administrator (e.g. some corp-managed devices do not allow OPP connection).
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_SYSTEM_POLICY = 1106;
-
-    /**
-     * Indicates that the ACL disconnected due to resource constraints, either on the local
-     * device or the remote device.
-     * <p>
-     * Example cause: controller is busy, memory limit reached, maximum number of connections
-     * reached.
-     * <p>
-     * Example solution: The app should wait and try again. If still failing, prompt the user
-     * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED = 1107;
-
-    /**
-     * Indicates that the ACL disconnected because another ACL connection already exists.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS = 1108;
-
-    /**
-     * Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109;
-
-    /**
-     * Indicates that setting the LE Audio Broadcast mode failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED = 1110;
-
-    /**
-     * Indicates that setting a new encryption key for Bluetooth LE Audio Broadcast Source failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED = 1111;
-
-    /**
-     * Indicates that connecting to a remote Broadcast Audio Scan Service failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_CONNECT_FAILED = 1112;
-
-    /**
-     * Indicates that disconnecting from a remote Broadcast Audio Scan Service failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_DISCONNECT_FAILED = 1113;
-
-    /**
-     * Indicates that enabling LE Audio Broadcast encryption failed
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED = 1114;
-
-    /**
-     * Indicates that disabling LE Audio Broadcast encryption failed
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115;
-
-    /**
-     * Indicates that an unknown error has occurred has occurred.
-     */
-    public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;
-}
diff --git a/core/java/android/bluetooth/BluetoothUtils.java b/core/java/android/bluetooth/BluetoothUtils.java
deleted file mode 100644
index 8674692..0000000
--- a/core/java/android/bluetooth/BluetoothUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import java.time.Duration;
-
-/**
- * {@hide}
- */
-public final class BluetoothUtils {
-    /**
-     * This utility class cannot be instantiated
-     */
-    private BluetoothUtils() {}
-
-    /**
-     * Timeout value for synchronous binder call
-     */
-    private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5);
-
-    /**
-     * @return timeout value for synchronous binder call
-     */
-    static Duration getSyncTimeout() {
-        return SYNC_CALLS_TIMEOUT;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
deleted file mode 100644
index 2a8ff51..0000000
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.ParcelUuid;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.UUID;
-
-/**
- * Static helper methods and constants to decode the ParcelUuid of remote devices.
- *
- * @hide
- */
-@SystemApi
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class BluetoothUuid {
-
-    /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
-     * for the various services.
-     *
-     * The following 128 bit values are calculated as:
-     *  uuid * 2^96 + BASE_UUID
-     */
-
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid A2DP_SINK =
-            ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid A2DP_SOURCE =
-            ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid ADV_AUDIO_DIST =
-            ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HSP =
-            ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HSP_AG =
-            ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HFP =
-            ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HFP_AG =
-            ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid AVRCP_CONTROLLER =
-            ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid AVRCP_TARGET =
-            ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid OBEX_OBJECT_PUSH =
-            ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HID =
-            ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HOGP =
-            ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid PANU =
-            ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid NAP =
-            ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid BNEP =
-            ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid PBAP_PCE =
-            ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid PBAP_PSE =
-            ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MAP =
-            ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MNS =
-            ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MAS =
-            ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid SAP =
-            ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HEARING_AID =
-            ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid LE_AUDIO =
-            ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid DIP =
-            ParcelUuid.fromString("00001200-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid VOLUME_CONTROL =
-            ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid GENERIC_MEDIA_CONTROL =
-            ParcelUuid.fromString("00001849-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MEDIA_CONTROL =
-            ParcelUuid.fromString("00001848-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid COORDINATED_SET =
-            ParcelUuid.fromString("00001846-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid CAP =
-            ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid BASE_UUID =
-            ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
-
-    /**
-     * Length of bytes for 16 bit UUID
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int UUID_BYTES_16_BIT = 2;
-    /**
-     * Length of bytes for 32 bit UUID
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int UUID_BYTES_32_BIT = 4;
-    /**
-     * Length of bytes for 128 bit UUID
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int UUID_BYTES_128_BIT = 16;
-
-    /**
-     * Returns true if there any common ParcelUuids in uuidA and uuidB.
-     *
-     * @param uuidA - List of ParcelUuids
-     * @param uuidB - List of ParcelUuids
-     *
-     * @hide
-     */
-    @SystemApi
-    public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA,
-            @Nullable ParcelUuid[] uuidB) {
-        if (uuidA == null && uuidB == null) return true;
-
-        if (uuidA == null) {
-            return uuidB.length == 0;
-        }
-
-        if (uuidB == null) {
-            return uuidA.length == 0;
-        }
-
-        HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA));
-        for (ParcelUuid uuid : uuidB) {
-            if (uuidSet.contains(uuid)) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Extract the Service Identifier or the actual uuid from the Parcel Uuid.
-     * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid,
-     * this function will return 110B
-     *
-     * @param parcelUuid
-     * @return the service identifier.
-     */
-    private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
-        UUID uuid = parcelUuid.getUuid();
-        long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32;
-        return (int) value;
-    }
-
-    /**
-     * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
-     * but the returned UUID is always in 128-bit format.
-     * Note UUID is little endian in Bluetooth.
-     *
-     * @param uuidBytes Byte representation of uuid.
-     * @return {@link ParcelUuid} parsed from bytes.
-     * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) {
-        if (uuidBytes == null) {
-            throw new IllegalArgumentException("uuidBytes cannot be null");
-        }
-        int length = uuidBytes.length;
-        if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT
-                && length != UUID_BYTES_128_BIT) {
-            throw new IllegalArgumentException("uuidBytes length invalid - " + length);
-        }
-
-        // Construct a 128 bit UUID.
-        if (length == UUID_BYTES_128_BIT) {
-            ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
-            long msb = buf.getLong(8);
-            long lsb = buf.getLong(0);
-            return new ParcelUuid(new UUID(msb, lsb));
-        }
-
-        // For 16 bit and 32 bit UUID we need to convert them to 128 bit value.
-        // 128_bit_value = uuid * 2^96 + BASE_UUID
-        long shortUuid;
-        if (length == UUID_BYTES_16_BIT) {
-            shortUuid = uuidBytes[0] & 0xFF;
-            shortUuid += (uuidBytes[1] & 0xFF) << 8;
-        } else {
-            shortUuid = uuidBytes[0] & 0xFF;
-            shortUuid += (uuidBytes[1] & 0xFF) << 8;
-            shortUuid += (uuidBytes[2] & 0xFF) << 16;
-            shortUuid += (uuidBytes[3] & 0xFF) << 24;
-        }
-        long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);
-        long lsb = BASE_UUID.getUuid().getLeastSignificantBits();
-        return new ParcelUuid(new UUID(msb, lsb));
-    }
-
-    /**
-     * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or
-     * 128-bit UUID, Note returned value is little endian (Bluetooth).
-     *
-     * @param uuid uuid to parse.
-     * @return shortest representation of {@code uuid} as bytes.
-     * @throws IllegalArgumentException If the {@code uuid} is null.
-     *
-     * @hide
-     */
-    public static byte[] uuidToBytes(ParcelUuid uuid) {
-        if (uuid == null) {
-            throw new IllegalArgumentException("uuid cannot be null");
-        }
-
-        if (is16BitUuid(uuid)) {
-            byte[] uuidBytes = new byte[UUID_BYTES_16_BIT];
-            int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
-            uuidBytes[0] = (byte) (uuidVal & 0xFF);
-            uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
-            return uuidBytes;
-        }
-
-        if (is32BitUuid(uuid)) {
-            byte[] uuidBytes = new byte[UUID_BYTES_32_BIT];
-            int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
-            uuidBytes[0] = (byte) (uuidVal & 0xFF);
-            uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
-            uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16);
-            uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24);
-            return uuidBytes;
-        }
-
-        // Construct a 128 bit UUID.
-        long msb = uuid.getUuid().getMostSignificantBits();
-        long lsb = uuid.getUuid().getLeastSignificantBits();
-
-        byte[] uuidBytes = new byte[UUID_BYTES_128_BIT];
-        ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
-        buf.putLong(8, msb);
-        buf.putLong(0, lsb);
-        return uuidBytes;
-    }
-
-    /**
-     * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
-     *
-     * @param parcelUuid
-     * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static boolean is16BitUuid(ParcelUuid parcelUuid) {
-        UUID uuid = parcelUuid.getUuid();
-        if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
-            return false;
-        }
-        return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
-    }
-
-
-    /**
-     * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid.
-     *
-     * @param parcelUuid
-     * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static boolean is32BitUuid(ParcelUuid parcelUuid) {
-        UUID uuid = parcelUuid.getUuid();
-        if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
-            return false;
-        }
-        if (is16BitUuid(parcelUuid)) {
-            return false;
-        }
-        return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
-    }
-
-    private BluetoothUuid() {}
-}
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
deleted file mode 100644
index 27532aa..0000000
--- a/core/java/android/bluetooth/BluetoothVolumeControl.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth Volume Control service.
- *
- * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothVolumeControl proxy object.
- * @hide
- */
-@SystemApi
-public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable {
-    private static final String TAG = "BluetoothVolumeControl";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Volume Control
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @SystemApi
-    @SuppressLint("ActionValue")
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
-
-    private BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
-                    IBluetoothVolumeControl.class.getName()) {
-                @Override
-                public IBluetoothVolumeControl getServiceInterface(IBinder service) {
-                    return IBluetoothVolumeControl.Stub.asInterface(service);
-                }
-            };
-
-    /**
-     * Create a BluetoothVolumeControl proxy object for interacting with the local
-     * Bluetooth Volume Control service.
-     */
-    /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
-        final IBluetoothVolumeControl service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothVolumeControl service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothVolumeControl service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Tells remote device to set an absolute volume.
-     *
-     * @param volume Absolute volume to be set on remote device.
-     *               Minimum value is 0 and maximum value is 255
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void setVolume(@Nullable BluetoothDevice device,
-            @IntRange(from = 0, to = 255) int volume) {
-        if (DBG) log("setVolume(" + volume + ")");
-        final IBluetoothVolumeControl service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setVolume(device, volume, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothVolumeControl service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothVolumeControl service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BufferConstraint.java b/core/java/android/bluetooth/BufferConstraint.java
deleted file mode 100644
index cbffc78..0000000
--- a/core/java/android/bluetooth/BufferConstraint.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Stores a codec's constraints on buffering length in milliseconds.
- *
- * {@hide}
- */
-@SystemApi
-public final class BufferConstraint implements Parcelable {
-
-    private static final String TAG = "BufferConstraint";
-    private int mDefaultMillis;
-    private int mMaxMillis;
-    private int mMinMillis;
-
-    public BufferConstraint(int defaultMillis, int maxMillis,
-            int minMillis) {
-        mDefaultMillis = defaultMillis;
-        mMaxMillis = maxMillis;
-        mMinMillis = minMillis;
-    }
-
-    BufferConstraint(Parcel in) {
-        mDefaultMillis = in.readInt();
-        mMaxMillis = in.readInt();
-        mMinMillis = in.readInt();
-    }
-
-    public static final @NonNull Parcelable.Creator<BufferConstraint> CREATOR =
-            new Parcelable.Creator<BufferConstraint>() {
-                public BufferConstraint createFromParcel(Parcel in) {
-                    return new BufferConstraint(in);
-                }
-
-                public BufferConstraint[] newArray(int size) {
-                    return new BufferConstraint[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeInt(mDefaultMillis);
-        out.writeInt(mMaxMillis);
-        out.writeInt(mMinMillis);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Get the default buffer millis
-     *
-     * @return default buffer millis
-     * @hide
-     */
-    @SystemApi
-    public int getDefaultMillis() {
-        return mDefaultMillis;
-    }
-
-    /**
-     * Get the maximum buffer millis
-     *
-     * @return maximum buffer millis
-     * @hide
-     */
-    @SystemApi
-    public int getMaxMillis() {
-        return mMaxMillis;
-    }
-
-    /**
-     * Get the minimum buffer millis
-     *
-     * @return minimum buffer millis
-     * @hide
-     */
-    @SystemApi
-    public int getMinMillis() {
-        return mMinMillis;
-    }
-}
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
deleted file mode 100644
index 06b45ee..0000000
--- a/core/java/android/bluetooth/BufferConstraints.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * A parcelable collection of buffer constraints by codec type.
- *
- * {@hide}
- */
-@SystemApi
-public final class BufferConstraints implements Parcelable {
-    public static final int BUFFER_CODEC_MAX_NUM = 32;
-
-    private static final String TAG = "BufferConstraints";
-
-    private Map<Integer, BufferConstraint> mBufferConstraints;
-    private List<BufferConstraint> mBufferConstraintList;
-
-    public BufferConstraints(@NonNull List<BufferConstraint>
-            bufferConstraintList) {
-
-        mBufferConstraintList = new ArrayList<BufferConstraint>(bufferConstraintList);
-        mBufferConstraints = new HashMap<Integer, BufferConstraint>();
-        for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) {
-            mBufferConstraints.put(i, bufferConstraintList.get(i));
-        }
-    }
-
-    BufferConstraints(Parcel in) {
-        mBufferConstraintList = new ArrayList<BufferConstraint>();
-        mBufferConstraints = new HashMap<Integer, BufferConstraint>();
-        in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader(), android.bluetooth.BufferConstraint.class);
-        for (int i = 0; i < mBufferConstraintList.size(); i++) {
-            mBufferConstraints.put(i, mBufferConstraintList.get(i));
-        }
-    }
-
-    public static final @NonNull Parcelable.Creator<BufferConstraints> CREATOR =
-            new Parcelable.Creator<BufferConstraints>() {
-                public BufferConstraints createFromParcel(Parcel in) {
-                    return new BufferConstraints(in);
-                }
-
-                public BufferConstraints[] newArray(int size) {
-                    return new BufferConstraints[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeList(mBufferConstraintList);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Get the buffer constraints by codec type.
-     *
-     * @param codec Audio codec
-     * @return buffer constraints by codec type.
-     * @hide
-     */
-    @SystemApi
-    public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
-        return mBufferConstraints.get(codec);
-    }
-}
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
deleted file mode 100644
index fbee577..0000000
--- a/core/java/android/bluetooth/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 27441
-
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
deleted file mode 100644
index bb0b956..0000000
--- a/core/java/android/bluetooth/OobData.java
+++ /dev/null
@@ -1,958 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Out Of Band Data for Bluetooth device pairing.
- *
- * <p>This object represents optional data obtained from a remote device through
- * an out-of-band channel (eg. NFC, QR).
- *
- * <p>References:
- * NFC AD Forum SSP 1.1 (AD)
- * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf}
- * Core Specification Supplement (CSS) V9
- *
- * <p>There are several BR/EDR Examples
- *
- * <p>Negotiated Handover:
- *   Bluetooth Carrier Configuration Record:
- *    - OOB Data Length
- *    - Device Address
- *    - Class of Device
- *    - Simple Pairing Hash C
- *    - Simple Pairing Randomizer R
- *    - Service Class UUID
- *    - Bluetooth Local Name
- *
- * <p>Static Handover:
- *   Bluetooth Carrier Configuration Record:
- *    - OOB Data Length
- *    - Device Address
- *    - Class of Device
- *    - Service Class UUID
- *    - Bluetooth Local Name
- *
- * <p>Simplified Tag Format for Single BT Carrier:
- *   Bluetooth OOB Data Record:
- *    - OOB Data Length
- *    - Device Address
- *    - Class of Device
- *    - Service Class UUID
- *    - Bluetooth Local Name
- *
- * @hide
- */
-@SystemApi
-public final class OobData implements Parcelable {
-
-    private static final String TAG = "OobData";
-    /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int OOB_LENGTH_OCTETS = 2;
-    /**
-     * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1).
-     * (AD 3.1.2) (CSS 1.6.2)
-     * @hide
-     */
-    @SystemApi
-    public static final int DEVICE_ADDRESS_OCTETS = 7;
-    /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int CLASS_OF_DEVICE_OCTETS = 3;
-    /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int CONFIRMATION_OCTETS = 16;
-    /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int RANDOMIZER_OCTETS = 16;
-    /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_OCTETS = 1;
-    /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
-    @SystemApi
-    public static final int LE_TK_OCTETS = 16;
-    /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
-    @SystemApi
-    public static final int LE_APPEARANCE_OCTETS = 2;
-    /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
-    @SystemApi
-    public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
-
-    // Le Roles
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "LE_DEVICE_ROLE_" },
-        value = {
-            LE_DEVICE_ROLE_PERIPHERAL_ONLY,
-            LE_DEVICE_ROLE_CENTRAL_ONLY,
-            LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
-            LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
-        }
-    )
-    public @interface LeRole {}
-
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
-
-    // Le Flags
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "LE_FLAG_" },
-        value = {
-            LE_FLAG_LIMITED_DISCOVERY_MODE,
-            LE_FLAG_GENERAL_DISCOVERY_MODE,
-            LE_FLAG_BREDR_NOT_SUPPORTED,
-            LE_FLAG_SIMULTANEOUS_CONTROLLER,
-            LE_FLAG_SIMULTANEOUS_HOST
-        }
-    )
-    public @interface LeFlag {}
-
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
-
-    /**
-     * Builds an {@link OobData} object and validates that the required combination
-     * of values are present to create the LE specific OobData type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class LeBuilder {
-
-        /**
-         * It is recommended that this Hash C is generated anew for each
-         * pairing.
-         *
-         * <p>It should be noted that on passive NFC this isn't possible as the data is static
-         * and immutable.
-         */
-        private byte[] mConfirmationHash = null;
-
-        /**
-         * Optional, but adds more validity to the pairing.
-         *
-         * <p>If not present a value of 0 is assumed.
-         */
-        private byte[] mRandomizerHash = new byte[] {
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        };
-
-        /**
-         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
-         *
-         * <p>This is the name that may be displayed to the device user as part of the UI.
-         */
-        private byte[] mDeviceName = null;
-
-        /**
-         * Sets the Bluetooth Device name to be used for UI purposes.
-         *
-         * <p>Optional attribute.
-         *
-         * @param deviceName byte array representing the name, may be 0 in length, not null.
-         *
-         * @return {@link OobData#ClassicBuilder}
-         *
-         * @throws NullPointerException if deviceName is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
-            requireNonNull(deviceName);
-            this.mDeviceName = deviceName;
-            return this;
-        }
-
-        /**
-         * The Bluetooth Device Address is the address to which the OOB data belongs.
-         *
-         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
-         *
-         * <p> Address is encoded in Little Endian order.
-         *
-         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
-         */
-        private final byte[] mDeviceAddressWithType;
-
-        /**
-         * During an LE connection establishment, one must be in the Peripheral mode and the other
-         * in the Central role.
-         *
-         * <p>Possible Values:
-         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
-         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
-         * Peripheral Preferred
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
-         * 0x04 - 0xFF Reserved
-         */
-        private final @LeRole int mLeDeviceRole;
-
-        /**
-         * Temporary key value from the Security Manager.
-         *
-         * <p> Must be {@link LE_TK_OCTETS} in size
-         */
-        private byte[] mLeTemporaryKey = null;
-
-        /**
-         * Defines the representation of the external appearance of the device.
-         *
-         * <p>For example, a mouse, remote control, or keyboard.
-         *
-         * <p>Used for visual on discovering device to represent icon/string/etc...
-         */
-        private byte[] mLeAppearance = null;
-
-        /**
-         * Contains which discoverable mode to use, BR/EDR support and capability.
-         *
-         * <p>Possible LE Flags:
-         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
-         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
-         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
-         * LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Controller).
-         * Bit 49 of LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Host).
-         * Bit 55 of LMP Feature Mask Definitions.
-         * <b>0x05- 0x07 Reserved</b>
-         */
-        private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
-
-        /**
-         * Main creation method for creating a LE version of {@link OobData}.
-         *
-         * <p>This object will allow the caller to call {@link LeBuilder#build()}
-         * to build the data object or add any option information to the builder.
-         *
-         * @param deviceAddressWithType the LE device address plus the address type (7 octets);
-         * not null.
-         * @param leDeviceRole whether the device supports Peripheral, Central,
-         * Both including preference; not null. (1 octet)
-         * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
-         * of data. Data is derived from controller/host stack and is
-         * required for pairing OOB.
-         *
-         * <p>Possible Values:
-         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
-         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
-         * Peripheral Preferred
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
-         * 0x04 - 0xFF Reserved
-         *
-         * @throws IllegalArgumentException if any of the values fail to be set.
-         * @throws NullPointerException if any argument is null.
-         *
-         * @hide
-         */
-        @SystemApi
-        public LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType,
-                @LeRole int leDeviceRole) {
-            requireNonNull(confirmationHash);
-            requireNonNull(deviceAddressWithType);
-            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
-                throw new IllegalArgumentException("confirmationHash must be "
-                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
-            }
-            this.mConfirmationHash = confirmationHash;
-            if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
-                throw new IllegalArgumentException("confirmationHash must be "
-                    + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length.");
-            }
-            this.mDeviceAddressWithType = deviceAddressWithType;
-            if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
-                    || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
-                throw new IllegalArgumentException("leDeviceRole must be a valid value.");
-            }
-            this.mLeDeviceRole = leDeviceRole;
-        }
-
-        /**
-         * Sets the Temporary Key value to be used by the LE Security Manager during
-         * LE pairing.
-         *
-         * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6,
-         * Part A 1.8 for a detailed description.
-         *
-         * @return {@link OobData#Builder}
-         *
-         * @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
-         * @throws NullinterException if leTemporaryKey is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
-            requireNonNull(leTemporaryKey);
-            if (leTemporaryKey.length != LE_TK_OCTETS) {
-                throw new IllegalArgumentException("leTemporaryKey must be "
-                        + LE_TK_OCTETS + " octets in length.");
-            }
-            this.mLeTemporaryKey = leTemporaryKey;
-            return this;
-        }
-
-        /**
-         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
-         * of data. Data is derived from controller/host stack and is required for pairing OOB.
-         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
-         *
-         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
-         * @throws NullPointerException if randomizerHash is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
-            requireNonNull(randomizerHash);
-            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
-                throw new IllegalArgumentException("randomizerHash must be "
-                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
-            }
-            this.mRandomizerHash = randomizerHash;
-            return this;
-        }
-
-        /**
-         * Sets the LE Flags necessary for the pairing scenario or discovery mode.
-         *
-         * @param leFlags enum value representing the 1 octet of data about discovery modes.
-         *
-         * <p>Possible LE Flags:
-         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
-         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
-         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
-         * LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Host).
-         * Bit 55 of LMP Feature Mask Definitions.
-         * 0x05- 0x07 Reserved
-         *
-         * @throws IllegalArgumentException for invalid flag
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setLeFlags(@LeFlag int leFlags) {
-            if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
-                throw new IllegalArgumentException("leFlags must be a valid value.");
-            }
-            this.mLeFlags = leFlags;
-            return this;
-        }
-
-        /**
-         * Validates and builds the {@link OobData} object for LE Security.
-         *
-         * @return {@link OobData} with given builder values
-         *
-         * @throws IllegalStateException if either of the 2 required fields were not set.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public OobData build() {
-            final OobData oob =
-                    new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole,
-                            this.mConfirmationHash);
-
-            // If we have values, set them, otherwise use default
-            oob.mLeTemporaryKey =
-                    (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
-            oob.mLeAppearance = (this.mLeAppearance != null)
-                    ? this.mLeAppearance : oob.mLeAppearance;
-            oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
-            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
-            oob.mRandomizerHash = this.mRandomizerHash;
-            return oob;
-        }
-    }
-
-    /**
-     * Builds an {@link OobData} object and validates that the required combination
-     * of values are present to create the Classic specific OobData type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class ClassicBuilder {
-        // Used by both Classic and LE
-        /**
-         * It is recommended that this Hash C is generated anew for each
-         * pairing.
-         *
-         * <p>It should be noted that on passive NFC this isn't possible as the data is static
-         * and immutable.
-         *
-         * @hide
-         */
-        private byte[] mConfirmationHash = null;
-
-        /**
-         * Optional, but adds more validity to the pairing.
-         *
-         * <p>If not present a value of 0 is assumed.
-         *
-         * @hide
-         */
-        private byte[] mRandomizerHash = new byte[] {
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        };
-
-        /**
-         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
-         *
-         * <p>This is the name that may be displayed to the device user as part of the UI.
-         *
-         * @hide
-         */
-        private byte[] mDeviceName = null;
-
-        /**
-         * This length value provides the absolute length of total OOB data block used for
-         * Bluetooth BR/EDR
-         *
-         * <p>OOB communication, which includes the length field itself and the Bluetooth
-         * Device Address.
-         *
-         * <p>The minimum length that may be represented in this field is 8.
-         *
-         * @hide
-         */
-        private final byte[] mClassicLength;
-
-        /**
-         * The Bluetooth Device Address is the address to which the OOB data belongs.
-         *
-         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
-         *
-         * <p> Address is encoded in Little Endian order.
-         *
-         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
-         *
-         * @hide
-         */
-        private final byte[] mDeviceAddressWithType;
-
-        /**
-         * Class of Device information is to be used to provide a graphical representation
-         * to the user as part of UI involving operations.
-         *
-         * <p>This is not to be used to determine a particular service can be used.
-         *
-         * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
-         *
-         * @hide
-         */
-        private byte[] mClassOfDevice = null;
-
-        /**
-         * Main creation method for creating a Classic version of {@link OobData}.
-         *
-         * <p>This object will allow the caller to call {@link ClassicBuilder#build()}
-         * to build the data object or add any option information to the builder.
-         *
-         * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
-         * octets of data. Data is derived from controller/host stack and is required for pairing
-         * OOB.
-         * @param classicLength byte array representing the length of data from 8-65535 across 2
-         * octets (0xXXXX).
-         * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
-         * that owns the OOB data. (i.e. the originator) [6 octets]
-         *
-         * @throws IllegalArgumentException if any of the values fail to be set.
-         * @throws NullPointerException if any argument is null.
-         *
-         * @hide
-         */
-        @SystemApi
-        public ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength,
-                @NonNull byte[] deviceAddressWithType) {
-            requireNonNull(confirmationHash);
-            requireNonNull(classicLength);
-            requireNonNull(deviceAddressWithType);
-            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
-                throw new IllegalArgumentException("confirmationHash must be "
-                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
-            }
-            this.mConfirmationHash = confirmationHash;
-            if (classicLength.length != OOB_LENGTH_OCTETS) {
-                throw new IllegalArgumentException("classicLength must be "
-                        + OOB_LENGTH_OCTETS + " octets in length.");
-            }
-            this.mClassicLength = classicLength;
-            if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
-                throw new IllegalArgumentException("deviceAddressWithType must be "
-                        + DEVICE_ADDRESS_OCTETS + " octets in length.");
-            }
-            this.mDeviceAddressWithType = deviceAddressWithType;
-        }
-
-        /**
-         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
-         * of data. Data is derived from controller/host stack and is required for pairing OOB.
-         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
-         *
-         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
-         * @throws NullPointerException if randomizerHash is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
-            requireNonNull(randomizerHash);
-            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
-                throw new IllegalArgumentException("randomizerHash must be "
-                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
-            }
-            this.mRandomizerHash = randomizerHash;
-            return this;
-        }
-
-        /**
-         * Sets the Bluetooth Device name to be used for UI purposes.
-         *
-         * <p>Optional attribute.
-         *
-         * @param deviceName byte array representing the name, may be 0 in length, not null.
-         *
-         * @return {@link OobData#ClassicBuilder}
-         *
-         * @throws NullPointerException if deviceName is null
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
-            requireNonNull(deviceName);
-            this.mDeviceName = deviceName;
-            return this;
-        }
-
-        /**
-         * Sets the Bluetooth Class of Device; used for UI purposes only.
-         *
-         * <p>Not an indicator of available services!
-         *
-         * <p>Optional attribute.
-         *
-         * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
-         *
-         * @return {@link OobData#ClassicBuilder}
-         *
-         * @throws IllegalArgumentException if length is not equal to
-         * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
-         * @throws NullPointerException if classOfDevice is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
-            requireNonNull(classOfDevice);
-            if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
-                throw new IllegalArgumentException("classOfDevice must be "
-                        + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length.");
-            }
-            this.mClassOfDevice = classOfDevice;
-            return this;
-        }
-
-        /**
-         * Validates and builds the {@link OobDat object for Classic Security.
-         *
-         * @return {@link OobData} with previously given builder values.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public OobData build() {
-            final OobData oob =
-                    new OobData(this.mClassicLength, this.mDeviceAddressWithType,
-                            this.mConfirmationHash);
-            // If we have values, set them, otherwise use default
-            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
-            oob.mClassOfDevice = (this.mClassOfDevice != null)
-                    ? this.mClassOfDevice : oob.mClassOfDevice;
-            oob.mRandomizerHash = this.mRandomizerHash;
-            return oob;
-        }
-    }
-
-    // Members (Defaults for Optionals must be set or Parceling fails on NPE)
-    // Both
-    private final byte[] mDeviceAddressWithType;
-    private final byte[] mConfirmationHash;
-    private byte[] mRandomizerHash = new byte[] {
-        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-    };
-    // Default the name to "Bluetooth Device"
-    private byte[] mDeviceName = new byte[] {
-        // Bluetooth
-        0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
-        // <space>Device
-        0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65
-    };
-
-    // Classic
-    private final byte[] mClassicLength;
-    private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
-
-    // LE
-    private final @LeRole int mLeDeviceRole;
-    private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
-    private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
-    private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
-
-    /**
-     * @return byte array representing the MAC address of a bluetooth device.
-     * The Address is 6 octets long with a 1 octet address type associated with the address.
-     *
-     * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type.
-     * For LE there are more choices for Address Type.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getDeviceAddressWithType() {
-        return mDeviceAddressWithType;
-    }
-
-    /**
-     * @return byte array representing the confirmationHash value
-     * which is used to confirm the identity to the controller.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getConfirmationHash() {
-        return mConfirmationHash;
-    }
-
-    /**
-     * @return byte array representing the randomizerHash value
-     * which is used to verify the identity of the controller.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getRandomizerHash() {
-        return mRandomizerHash;
-    }
-
-    /**
-     * @return Device Name used for displaying name in UI.
-     *
-     * <p>Also, this will be populated with the LE Local Name if the data is for LE.
-     *
-     * @hide
-     */
-    @Nullable
-    @SystemApi
-    public byte[] getDeviceName() {
-        return mDeviceName;
-    }
-
-    /**
-     * @return byte array representing the oob data length which is the length
-     * of all of the data including these octets.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getClassicLength() {
-        return mClassicLength;
-    }
-
-    /**
-     * @return byte array representing the class of device for UI display.
-     *
-     * <p>Does not indicate services available; for display only.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getClassOfDevice() {
-        return mClassOfDevice;
-    }
-
-    /**
-     * @return Temporary Key used for LE pairing.
-     *
-     * @hide
-     */
-    @Nullable
-    @SystemApi
-    public byte[] getLeTemporaryKey() {
-        return mLeTemporaryKey;
-    }
-
-    /**
-     * @return Appearance used for LE pairing. For use in UI situations
-     * when determining what sort of icons or text to display regarding
-     * the device.
-     *
-     * @hide
-     */
-    @Nullable
-    @SystemApi
-    public byte[] getLeAppearance() {
-        return mLeAppearance;
-    }
-
-    /**
-     * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability.
-     *
-     * <p>Possible LE Flags:
-     * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
-     * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
-     * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
-     * LMP Feature Mask Definitions.
-     * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
-     * Same Device Capable (Controller).
-     * Bit 49 of LMP Feature Mask Definitions.
-     * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
-     * Same Device Capable (Host).
-     * Bit 55 of LMP Feature Mask Definitions.
-     * <b>0x05- 0x07 Reserved</b>
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    @LeFlag
-    public int getLeFlags() {
-        return mLeFlags;
-    }
-
-    /**
-     * @return the supported and preferred roles of the LE device.
-     *
-     * <p>Possible Values:
-     * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
-     * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
-     * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
-     * Peripheral Preferred
-     * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
-     * 0x04 - 0xFF Reserved
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    @LeRole
-    public int getLeDeviceRole() {
-        return mLeDeviceRole;
-    }
-
-    /**
-     * Classic Security Constructor
-     */
-    private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType,
-            @NonNull byte[] confirmationHash) {
-        mClassicLength = classicLength;
-        mDeviceAddressWithType = deviceAddressWithType;
-        mConfirmationHash = confirmationHash;
-        mLeDeviceRole = -1; // Satisfy final
-    }
-
-    /**
-     * LE Security Constructor
-     */
-    private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole,
-            @NonNull byte[] confirmationHash) {
-        mDeviceAddressWithType = deviceAddressWithType;
-        mLeDeviceRole = leDeviceRole;
-        mConfirmationHash = confirmationHash;
-        mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
-    }
-
-    private OobData(Parcel in) {
-        // Both
-        mDeviceAddressWithType = in.createByteArray();
-        mConfirmationHash = in.createByteArray();
-        mRandomizerHash = in.createByteArray();
-        mDeviceName = in.createByteArray();
-
-        // Classic
-        mClassicLength = in.createByteArray();
-        mClassOfDevice = in.createByteArray();
-
-        // LE
-        mLeDeviceRole = in.readInt();
-        mLeTemporaryKey = in.createByteArray();
-        mLeAppearance = in.createByteArray();
-        mLeFlags = in.readInt();
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        // Both
-        // Required
-        out.writeByteArray(mDeviceAddressWithType);
-        // Required
-        out.writeByteArray(mConfirmationHash);
-        // Optional
-        out.writeByteArray(mRandomizerHash);
-        // Optional
-        out.writeByteArray(mDeviceName);
-
-        // Classic
-        // Required
-        out.writeByteArray(mClassicLength);
-        // Optional
-        out.writeByteArray(mClassOfDevice);
-
-        // LE
-        // Required
-        out.writeInt(mLeDeviceRole);
-        // Required
-        out.writeByteArray(mLeTemporaryKey);
-        // Optional
-        out.writeByteArray(mLeAppearance);
-        // Optional
-        out.writeInt(mLeFlags);
-    }
-
-    // For Parcelable
-    public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
-            new Parcelable.Creator<OobData>() {
-        public OobData createFromParcel(Parcel in) {
-            return new OobData(in);
-        }
-
-        public OobData[] newArray(int size) {
-            return new OobData[size];
-        }
-    };
-
-    /**
-     * @return a {@link String} representation of the OobData object.
-     *
-     * @hide
-     */
-    @Override
-    @NonNull
-    public String toString() {
-        return "OobData: \n\t"
-            // Both
-            + "Device Address With Type: " +  toHexString(mDeviceAddressWithType) + "\n\t"
-            + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t"
-            + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t"
-            + "Device Name: " + toHexString(mDeviceName) + "\n\t"
-            // Classic
-            + "OobData Length: " +  toHexString(mClassicLength) + "\n\t"
-            + "Class of Device: " +  toHexString(mClassOfDevice) + "\n\t"
-            // LE
-            + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t"
-            + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t"
-            + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t"
-            + "LE Flags: " + toHexString(mLeFlags) + "\n\t";
-    }
-
-    @NonNull
-    private String toHexString(int b) {
-        return toHexString(new byte[] {(byte) b});
-    }
-
-    @NonNull
-    private String toHexString(byte b) {
-        return toHexString(new byte[] {b});
-    }
-
-    @NonNull
-    private String toHexString(byte[] array) {
-        if (array == null) return "null";
-        StringBuilder builder = new StringBuilder(array.length * 2);
-        for (byte b: array) {
-            builder.append(String.format("%02x", b));
-        }
-        return builder.toString();
-    }
-}
diff --git a/core/java/android/bluetooth/SdpDipRecord.java b/core/java/android/bluetooth/SdpDipRecord.java
deleted file mode 100644
index 84b0eef..0000000
--- a/core/java/android/bluetooth/SdpDipRecord.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.bluetooth;
-
-import java.util.Arrays;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Data representation of a Object Push Profile Server side SDP record.
- */
-/** @hide */
-public class SdpDipRecord implements Parcelable {
-    private final int mSpecificationId;
-    private final int mVendorId;
-    private final int mVendorIdSource;
-    private final int mProductId;
-    private final int mVersion;
-    private final boolean mPrimaryRecord;
-
-    public SdpDipRecord(int specificationId,
-            int vendorId, int vendorIdSource,
-            int productId, int version,
-            boolean primaryRecord) {
-        super();
-        this.mSpecificationId = specificationId;
-        this.mVendorId = vendorId;
-        this.mVendorIdSource = vendorIdSource;
-        this.mProductId = productId;
-        this.mVersion = version;
-        this.mPrimaryRecord = primaryRecord;
-    }
-
-    public SdpDipRecord(Parcel in) {
-        this.mSpecificationId = in.readInt();
-        this.mVendorId = in.readInt();
-        this.mVendorIdSource = in.readInt();
-        this.mProductId = in.readInt();
-        this.mVersion = in.readInt();
-        this.mPrimaryRecord = in.readBoolean();
-    }
-
-    public int getSpecificationId() {
-        return mSpecificationId;
-    }
-
-    public int getVendorId() {
-        return mVendorId;
-    }
-
-    public int getVendorIdSource() {
-        return mVendorIdSource;
-    }
-
-    public int getProductId() {
-        return mProductId;
-    }
-
-    public int getVersion() {
-        return mVersion;
-    }
-
-    public boolean getPrimaryRecord() {
-        return mPrimaryRecord;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSpecificationId);
-        dest.writeInt(mVendorId);
-        dest.writeInt(mVendorIdSource);
-        dest.writeInt(mProductId);
-        dest.writeInt(mVersion);
-        dest.writeBoolean(mPrimaryRecord);
-    }
-
-    @Override
-    public int describeContents() {
-        /* No special objects */
-        return 0;
-    }
-
-    public static  final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpDipRecord createFromParcel(Parcel in) {
-            return new SdpDipRecord(in);
-        }
-        public SdpDipRecord[] newArray(int size) {
-            return new SdpDipRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpMasRecord.java b/core/java/android/bluetooth/SdpMasRecord.java
deleted file mode 100644
index 72d4938..0000000
--- a/core/java/android/bluetooth/SdpMasRecord.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpMasRecord implements Parcelable {
-    private final int mMasInstanceId;
-    private final int mL2capPsm;
-    private final int mRfcommChannelNumber;
-    private final int mProfileVersion;
-    private final int mSupportedFeatures;
-    private final int mSupportedMessageTypes;
-    private final String mServiceName;
-
-    /** Message type */
-    public static final class MessageType {
-        public static final int EMAIL = 0x01;
-        public static final int SMS_GSM = 0x02;
-        public static final int SMS_CDMA = 0x04;
-        public static final int MMS = 0x08;
-    }
-
-    public SdpMasRecord(int masInstanceId,
-            int l2capPsm,
-            int rfcommChannelNumber,
-            int profileVersion,
-            int supportedFeatures,
-            int supportedMessageTypes,
-            String serviceName) {
-        mMasInstanceId = masInstanceId;
-        mL2capPsm = l2capPsm;
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mProfileVersion = profileVersion;
-        mSupportedFeatures = supportedFeatures;
-        mSupportedMessageTypes = supportedMessageTypes;
-        mServiceName = serviceName;
-    }
-
-    public SdpMasRecord(Parcel in) {
-        mMasInstanceId = in.readInt();
-        mL2capPsm = in.readInt();
-        mRfcommChannelNumber = in.readInt();
-        mProfileVersion = in.readInt();
-        mSupportedFeatures = in.readInt();
-        mSupportedMessageTypes = in.readInt();
-        mServiceName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    public int getMasInstanceId() {
-        return mMasInstanceId;
-    }
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getRfcommCannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public int getSupportedFeatures() {
-        return mSupportedFeatures;
-    }
-
-    public int getSupportedMessageTypes() {
-        return mSupportedMessageTypes;
-    }
-
-    public boolean msgSupported(int msg) {
-        return (mSupportedMessageTypes & msg) != 0;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mMasInstanceId);
-        dest.writeInt(mL2capPsm);
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mProfileVersion);
-        dest.writeInt(mSupportedFeatures);
-        dest.writeInt(mSupportedMessageTypes);
-        dest.writeString(mServiceName);
-    }
-
-    @Override
-    public String toString() {
-        String ret = "Bluetooth MAS SDP Record:\n";
-
-        if (mMasInstanceId != -1) {
-            ret += "Mas Instance Id: " + mMasInstanceId + "\n";
-        }
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mL2capPsm != -1) {
-            ret += "L2CAP PSM: " + mL2capPsm + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "Profile version: " + mProfileVersion + "\n";
-        }
-        if (mSupportedMessageTypes != -1) {
-            ret += "Supported msg types: " + mSupportedMessageTypes + "\n";
-        }
-        if (mSupportedFeatures != -1) {
-            ret += "Supported features: " + mSupportedFeatures + "\n";
-        }
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpMasRecord createFromParcel(Parcel in) {
-            return new SdpMasRecord(in);
-        }
-
-        public SdpRecord[] newArray(int size) {
-            return new SdpRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpMnsRecord.java b/core/java/android/bluetooth/SdpMnsRecord.java
deleted file mode 100644
index a781d5d..0000000
--- a/core/java/android/bluetooth/SdpMnsRecord.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpMnsRecord implements Parcelable {
-    private final int mL2capPsm;
-    private final int mRfcommChannelNumber;
-    private final int mSupportedFeatures;
-    private final int mProfileVersion;
-    private final String mServiceName;
-
-    public SdpMnsRecord(int l2capPsm,
-            int rfcommChannelNumber,
-            int profileVersion,
-            int supportedFeatures,
-            String serviceName) {
-        mL2capPsm = l2capPsm;
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mSupportedFeatures = supportedFeatures;
-        mServiceName = serviceName;
-        mProfileVersion = profileVersion;
-    }
-
-    public SdpMnsRecord(Parcel in) {
-        mRfcommChannelNumber = in.readInt();
-        mL2capPsm = in.readInt();
-        mServiceName = in.readString();
-        mSupportedFeatures = in.readInt();
-        mProfileVersion = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getRfcommChannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getSupportedFeatures() {
-        return mSupportedFeatures;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mL2capPsm);
-        dest.writeString(mServiceName);
-        dest.writeInt(mSupportedFeatures);
-        dest.writeInt(mProfileVersion);
-    }
-
-    public String toString() {
-        String ret = "Bluetooth MNS SDP Record:\n";
-
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mL2capPsm != -1) {
-            ret += "L2CAP PSM: " + mL2capPsm + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mSupportedFeatures != -1) {
-            ret += "Supported features: " + mSupportedFeatures + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "Profile_version: " + mProfileVersion + "\n";
-        }
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpMnsRecord createFromParcel(Parcel in) {
-            return new SdpMnsRecord(in);
-        }
-
-        public SdpMnsRecord[] newArray(int size) {
-            return new SdpMnsRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpOppOpsRecord.java b/core/java/android/bluetooth/SdpOppOpsRecord.java
deleted file mode 100644
index e30745b8..0000000
--- a/core/java/android/bluetooth/SdpOppOpsRecord.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * Data representation of a Object Push Profile Server side SDP record.
- */
-
-/** @hide */
-public class SdpOppOpsRecord implements Parcelable {
-
-    private final String mServiceName;
-    private final int mRfcommChannel;
-    private final int mL2capPsm;
-    private final int mProfileVersion;
-    private final byte[] mFormatsList;
-
-    public SdpOppOpsRecord(String serviceName, int rfcommChannel,
-            int l2capPsm, int version, byte[] formatsList) {
-        super();
-        mServiceName = serviceName;
-        mRfcommChannel = rfcommChannel;
-        mL2capPsm = l2capPsm;
-        mProfileVersion = version;
-        mFormatsList = formatsList;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    public int getRfcommChannel() {
-        return mRfcommChannel;
-    }
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public byte[] getFormatsList() {
-        return mFormatsList;
-    }
-
-    @Override
-    public int describeContents() {
-        /* No special objects */
-        return 0;
-    }
-
-    public SdpOppOpsRecord(Parcel in) {
-        mRfcommChannel = in.readInt();
-        mL2capPsm = in.readInt();
-        mProfileVersion = in.readInt();
-        mServiceName = in.readString();
-        int arrayLength = in.readInt();
-        if (arrayLength > 0) {
-            byte[] bytes = new byte[arrayLength];
-            in.readByteArray(bytes);
-            mFormatsList = bytes;
-        } else {
-            mFormatsList = null;
-        }
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannel);
-        dest.writeInt(mL2capPsm);
-        dest.writeInt(mProfileVersion);
-        dest.writeString(mServiceName);
-        if (mFormatsList != null && mFormatsList.length > 0) {
-            dest.writeInt(mFormatsList.length);
-            dest.writeByteArray(mFormatsList);
-        } else {
-            dest.writeInt(0);
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n");
-        sb.append("  RFCOMM Chan Number: ").append(mRfcommChannel);
-        sb.append("\n  L2CAP PSM: ").append(mL2capPsm);
-        sb.append("\n  Profile version: ").append(mProfileVersion);
-        sb.append("\n  Service Name: ").append(mServiceName);
-        sb.append("\n  Formats List: ").append(Arrays.toString(mFormatsList));
-        return sb.toString();
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpOppOpsRecord createFromParcel(Parcel in) {
-            return new SdpOppOpsRecord(in);
-        }
-
-        public SdpOppOpsRecord[] newArray(int size) {
-            return new SdpOppOpsRecord[size];
-        }
-    };
-
-}
diff --git a/core/java/android/bluetooth/SdpPseRecord.java b/core/java/android/bluetooth/SdpPseRecord.java
deleted file mode 100644
index 72249d0..0000000
--- a/core/java/android/bluetooth/SdpPseRecord.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpPseRecord implements Parcelable {
-    private final int mL2capPsm;
-    private final int mRfcommChannelNumber;
-    private final int mProfileVersion;
-    private final int mSupportedFeatures;
-    private final int mSupportedRepositories;
-    private final String mServiceName;
-
-    public SdpPseRecord(int l2capPsm,
-            int rfcommChannelNumber,
-            int profileVersion,
-            int supportedFeatures,
-            int supportedRepositories,
-            String serviceName) {
-        mL2capPsm = l2capPsm;
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mProfileVersion = profileVersion;
-        mSupportedFeatures = supportedFeatures;
-        mSupportedRepositories = supportedRepositories;
-        mServiceName = serviceName;
-    }
-
-    public SdpPseRecord(Parcel in) {
-        mRfcommChannelNumber = in.readInt();
-        mL2capPsm = in.readInt();
-        mProfileVersion = in.readInt();
-        mSupportedFeatures = in.readInt();
-        mSupportedRepositories = in.readInt();
-        mServiceName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getRfcommChannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getSupportedFeatures() {
-        return mSupportedFeatures;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public int getSupportedRepositories() {
-        return mSupportedRepositories;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mL2capPsm);
-        dest.writeInt(mProfileVersion);
-        dest.writeInt(mSupportedFeatures);
-        dest.writeInt(mSupportedRepositories);
-        dest.writeString(mServiceName);
-
-    }
-
-    @Override
-    public String toString() {
-        String ret = "Bluetooth MNS SDP Record:\n";
-
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mL2capPsm != -1) {
-            ret += "L2CAP PSM: " + mL2capPsm + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "profile version: " + mProfileVersion + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mSupportedFeatures != -1) {
-            ret += "Supported features: " + mSupportedFeatures + "\n";
-        }
-        if (mSupportedRepositories != -1) {
-            ret += "Supported repositories: " + mSupportedRepositories + "\n";
-        }
-
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpPseRecord createFromParcel(Parcel in) {
-            return new SdpPseRecord(in);
-        }
-
-        public SdpPseRecord[] newArray(int size) {
-            return new SdpPseRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpRecord.java b/core/java/android/bluetooth/SdpRecord.java
deleted file mode 100644
index 730862e..0000000
--- a/core/java/android/bluetooth/SdpRecord.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/** @hide */
-public class SdpRecord implements Parcelable {
-
-    private final byte[] mRawData;
-    private final int mRawSize;
-
-    @Override
-    public String toString() {
-        return "BluetoothSdpRecord [rawData=" + Arrays.toString(mRawData)
-                + ", rawSize=" + mRawSize + "]";
-    }
-
-    public SdpRecord(int sizeRecord, byte[] record) {
-        mRawData = record;
-        mRawSize = sizeRecord;
-    }
-
-    public SdpRecord(Parcel in) {
-        mRawSize = in.readInt();
-        mRawData = new byte[mRawSize];
-        in.readByteArray(mRawData);
-
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRawSize);
-        dest.writeByteArray(mRawData);
-
-
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpRecord createFromParcel(Parcel in) {
-            return new SdpRecord(in);
-        }
-
-        public SdpRecord[] newArray(int size) {
-            return new SdpRecord[size];
-        }
-    };
-
-    public byte[] getRawData() {
-        return mRawData;
-    }
-
-    public int getRawSize() {
-        return mRawSize;
-    }
-}
diff --git a/core/java/android/bluetooth/SdpSapsRecord.java b/core/java/android/bluetooth/SdpSapsRecord.java
deleted file mode 100644
index a1e2f7b..0000000
--- a/core/java/android/bluetooth/SdpSapsRecord.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpSapsRecord implements Parcelable {
-    private final int mRfcommChannelNumber;
-    private final int mProfileVersion;
-    private final String mServiceName;
-
-    public SdpSapsRecord(int rfcommChannelNumber, int profileVersion, String serviceName) {
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mProfileVersion = profileVersion;
-        mServiceName = serviceName;
-    }
-
-    public SdpSapsRecord(Parcel in) {
-        mRfcommChannelNumber = in.readInt();
-        mProfileVersion = in.readInt();
-        mServiceName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public int getRfcommCannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mProfileVersion);
-        dest.writeString(mServiceName);
-
-    }
-
-    @Override
-    public String toString() {
-        String ret = "Bluetooth MAS SDP Record:\n";
-
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "Profile version: " + mProfileVersion + "\n";
-        }
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpSapsRecord createFromParcel(Parcel in) {
-            return new SdpSapsRecord(in);
-        }
-
-        public SdpRecord[] newArray(int size) {
-            return new SdpRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/UidTraffic.java b/core/java/android/bluetooth/UidTraffic.java
deleted file mode 100644
index 9982fa61..0000000
--- a/core/java/android/bluetooth/UidTraffic.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.bluetooth;
-
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Record of data traffic (in bytes) by an application identified by its UID.
- *
- * @hide
- */
-@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
-public final class UidTraffic implements Cloneable, Parcelable {
-    private final int mAppUid;
-    private long mRxBytes;
-    private long mTxBytes;
-
-    /** @hide */
-    public UidTraffic(int appUid, long rx, long tx) {
-        mAppUid = appUid;
-        mRxBytes = rx;
-        mTxBytes = tx;
-    }
-
-    /** @hide */
-    private UidTraffic(Parcel in) {
-        mAppUid = in.readInt();
-        mRxBytes = in.readLong();
-        mTxBytes = in.readLong();
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mAppUid);
-        dest.writeLong(mRxBytes);
-        dest.writeLong(mTxBytes);
-    }
-
-    /** @hide */
-    public void setRxBytes(long bytes) {
-        mRxBytes = bytes;
-    }
-
-    /** @hide */
-    public void setTxBytes(long bytes) {
-        mTxBytes = bytes;
-    }
-
-    /** @hide */
-    public void addRxBytes(long bytes) {
-        mRxBytes += bytes;
-    }
-
-    /** @hide */
-    public void addTxBytes(long bytes) {
-        mTxBytes += bytes;
-    }
-
-    /**
-     * @return corresponding app Uid
-     */
-    public int getUid() {
-        return mAppUid;
-    }
-
-    /**
-     * @return rx bytes count
-     */
-    public long getRxBytes() {
-        return mRxBytes;
-    }
-
-    /**
-     * @return tx bytes count
-     */
-    public long getTxBytes() {
-        return mTxBytes;
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public UidTraffic clone() {
-        return new UidTraffic(mAppUid, mRxBytes, mTxBytes);
-    }
-
-    /** @hide */
-    @Override
-    public String toString() {
-        return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes="
-                + mTxBytes + '}';
-    }
-
-    public static final @android.annotation.NonNull Creator<UidTraffic> CREATOR = new Creator<UidTraffic>() {
-        @Override
-        public UidTraffic createFromParcel(Parcel source) {
-            return new UidTraffic(source);
-        }
-
-        @Override
-        public UidTraffic[] newArray(int size) {
-            return new UidTraffic[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
deleted file mode 100644
index c508c2c..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
- *            this requires the {@link Manifest.permission#BLUETOOTH_ADVERTISE}
- *            permission which can be gained with
- *            {@link android.app.Activity#requestPermissions(String[], int)}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothAdvertisePermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
deleted file mode 100644
index e159eaa..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
- *            this requires the {@link Manifest.permission#BLUETOOTH_CONNECT}
- *            permission which can be gained with
- *            {@link android.app.Activity#requestPermissions(String[], int)}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothConnectPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
deleted file mode 100644
index 2bb3204..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc In addition, this requires either the
- *            {@link Manifest.permission#ACCESS_FINE_LOCATION}
- *            permission or a strong assertion that you will never derive the
- *            physical location of the device. You can make this assertion by
- *            declaring {@code usesPermissionFlags="neverForLocation"} on the
- *            relevant {@code <uses-permission>} manifest tag, but it may
- *            restrict the types of Bluetooth devices you can interact with.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothLocationPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
deleted file mode 100644
index 800ff39..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
- *            this requires the {@link Manifest.permission#BLUETOOTH_SCAN}
- *            permission which can be gained with
- *            {@link android.app.Activity#requestPermissions(String[], int)}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothScanPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
deleted file mode 100644
index 9adf695..0000000
--- a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
- *            requires the {@link Manifest.permission#BLUETOOTH_ADMIN}
- *            permission which can be gained with a simple
- *            {@code <uses-permission>} manifest tag.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresLegacyBluetoothAdminPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
deleted file mode 100644
index 79621c3..0000000
--- a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
- *            requires the {@link Manifest.permission#BLUETOOTH} permission
- *            which can be gained with a simple {@code <uses-permission>}
- *            manifest tag.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresLegacyBluetoothPermission {
-}
diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java
deleted file mode 100644
index 4fa8c4f..0000000
--- a/core/java/android/bluetooth/le/AdvertiseCallback.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-/**
- * Bluetooth LE advertising callbacks, used to deliver advertising operation status.
- */
-public abstract class AdvertiseCallback {
-
-    /**
-     * The requested operation was successful.
-     *
-     * @hide
-     */
-    public static final int ADVERTISE_SUCCESS = 0;
-
-    /**
-     * Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.
-     */
-    public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
-
-    /**
-     * Failed to start advertising because no advertising instance is available.
-     */
-    public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
-
-    /**
-     * Failed to start advertising as the advertising is already started.
-     */
-    public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
-
-    /**
-     * Operation failed due to an internal error.
-     */
-    public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4;
-
-    /**
-     * This feature is not supported on this platform.
-     */
-    public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5;
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertising} indicating
-     * that the advertising has been started successfully.
-     *
-     * @param settingsInEffect The actual settings used for advertising, which may be different from
-     * what has been requested.
-     */
-    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
-    }
-
-    /**
-     * Callback when advertising could not be started.
-     *
-     * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for advertising start
-     * failures.
-     */
-    public void onStartFailure(int errorCode) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
deleted file mode 100644
index fdf62ec..0000000
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Advertise data packet container for Bluetooth LE advertising. This represents the data to be
- * advertised as well as the scan response data for active scans.
- * <p>
- * Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be
- * advertised.
- *
- * @see BluetoothLeAdvertiser
- * @see ScanRecord
- */
-public final class AdvertiseData implements Parcelable {
-
-    @Nullable
-    private final List<ParcelUuid> mServiceUuids;
-
-    @NonNull
-    private final List<ParcelUuid> mServiceSolicitationUuids;
-
-    @Nullable
-    private final List<TransportDiscoveryData> mTransportDiscoveryData;
-
-    private final SparseArray<byte[]> mManufacturerSpecificData;
-    private final Map<ParcelUuid, byte[]> mServiceData;
-    private final boolean mIncludeTxPowerLevel;
-    private final boolean mIncludeDeviceName;
-
-    private AdvertiseData(List<ParcelUuid> serviceUuids,
-            List<ParcelUuid> serviceSolicitationUuids,
-            List<TransportDiscoveryData> transportDiscoveryData,
-            SparseArray<byte[]> manufacturerData,
-            Map<ParcelUuid, byte[]> serviceData,
-            boolean includeTxPowerLevel,
-            boolean includeDeviceName) {
-        mServiceUuids = serviceUuids;
-        mServiceSolicitationUuids = serviceSolicitationUuids;
-        mTransportDiscoveryData = transportDiscoveryData;
-        mManufacturerSpecificData = manufacturerData;
-        mServiceData = serviceData;
-        mIncludeTxPowerLevel = includeTxPowerLevel;
-        mIncludeDeviceName = includeDeviceName;
-    }
-
-    /**
-     * Returns a list of service UUIDs within the advertisement that are used to identify the
-     * Bluetooth GATT services.
-     */
-    public List<ParcelUuid> getServiceUuids() {
-        return mServiceUuids;
-    }
-
-    /**
-     * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
-     */
-    @NonNull
-    public List<ParcelUuid> getServiceSolicitationUuids() {
-        return mServiceSolicitationUuids;
-    }
-
-    /**
-     * Returns a list of {@link TransportDiscoveryData} within the advertisement.
-     */
-    @NonNull
-    public List<TransportDiscoveryData> getTransportDiscoveryData() {
-        if (mTransportDiscoveryData == null) {
-            return Collections.emptyList();
-        }
-        return mTransportDiscoveryData;
-    }
-
-    /**
-     * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The
-     * manufacturer id is a non-negative number assigned by Bluetooth SIG.
-     */
-    public SparseArray<byte[]> getManufacturerSpecificData() {
-        return mManufacturerSpecificData;
-    }
-
-    /**
-     * Returns a map of 16-bit UUID and its corresponding service data.
-     */
-    public Map<ParcelUuid, byte[]> getServiceData() {
-        return mServiceData;
-    }
-
-    /**
-     * Whether the transmission power level will be included in the advertisement packet.
-     */
-    public boolean getIncludeTxPowerLevel() {
-        return mIncludeTxPowerLevel;
-    }
-
-    /**
-     * Whether the device name will be included in the advertisement packet.
-     */
-    public boolean getIncludeDeviceName() {
-        return mIncludeDeviceName;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData,
-                mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        AdvertiseData other = (AdvertiseData) obj;
-        return Objects.equals(mServiceUuids, other.mServiceUuids)
-                && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids)
-                && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData)
-                && BluetoothLeUtils.equals(mManufacturerSpecificData,
-                    other.mManufacturerSpecificData)
-                && BluetoothLeUtils.equals(mServiceData, other.mServiceData)
-                && mIncludeDeviceName == other.mIncludeDeviceName
-                && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel;
-    }
-
-    @Override
-    public String toString() {
-        return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids="
-                + mServiceSolicitationUuids + ", mTransportDiscoveryData="
-                + mTransportDiscoveryData + ", mManufacturerSpecificData="
-                + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData="
-                + BluetoothLeUtils.toString(mServiceData)
-                + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName="
-                + mIncludeDeviceName + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags);
-        dest.writeTypedArray(mServiceSolicitationUuids.toArray(
-                new ParcelUuid[mServiceSolicitationUuids.size()]), flags);
-
-        dest.writeTypedList(mTransportDiscoveryData);
-
-        // mManufacturerSpecificData could not be null.
-        dest.writeInt(mManufacturerSpecificData.size());
-        for (int i = 0; i < mManufacturerSpecificData.size(); ++i) {
-            dest.writeInt(mManufacturerSpecificData.keyAt(i));
-            dest.writeByteArray(mManufacturerSpecificData.valueAt(i));
-        }
-        dest.writeInt(mServiceData.size());
-        for (ParcelUuid uuid : mServiceData.keySet()) {
-            dest.writeTypedObject(uuid, flags);
-            dest.writeByteArray(mServiceData.get(uuid));
-        }
-        dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
-        dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0));
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseData> CREATOR =
-            new Creator<AdvertiseData>() {
-                @Override
-                public AdvertiseData[] newArray(int size) {
-                    return new AdvertiseData[size];
-                }
-
-                @Override
-                public AdvertiseData createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-                    ArrayList<ParcelUuid> uuids = in.createTypedArrayList(ParcelUuid.CREATOR);
-                    for (ParcelUuid uuid : uuids) {
-                        builder.addServiceUuid(uuid);
-                    }
-
-                    ArrayList<ParcelUuid> solicitationUuids = in.createTypedArrayList(ParcelUuid.CREATOR);
-                    for (ParcelUuid uuid : solicitationUuids) {
-                        builder.addServiceSolicitationUuid(uuid);
-                    }
-
-                    List<TransportDiscoveryData> transportDiscoveryData =
-                            in.createTypedArrayList(TransportDiscoveryData.CREATOR);
-                    for (TransportDiscoveryData tdd : transportDiscoveryData) {
-                        builder.addTransportDiscoveryData(tdd);
-                    }
-
-                    int manufacturerSize = in.readInt();
-                    for (int i = 0; i < manufacturerSize; ++i) {
-                        int manufacturerId = in.readInt();
-                        byte[] manufacturerData = in.createByteArray();
-                        builder.addManufacturerData(manufacturerId, manufacturerData);
-                    }
-                    int serviceDataSize = in.readInt();
-                    for (int i = 0; i < serviceDataSize; ++i) {
-                        ParcelUuid serviceDataUuid = in.readTypedObject(ParcelUuid.CREATOR);
-                        byte[] serviceData = in.createByteArray();
-                        builder.addServiceData(serviceDataUuid, serviceData);
-                    }
-                    builder.setIncludeTxPowerLevel(in.readByte() == 1);
-                    builder.setIncludeDeviceName(in.readByte() == 1);
-                    return builder.build();
-                }
-            };
-
-    /**
-     * Builder for {@link AdvertiseData}.
-     */
-    public static final class Builder {
-        @Nullable
-        private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
-        @NonNull
-        private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
-        @Nullable
-        private List<TransportDiscoveryData> mTransportDiscoveryData =
-                new ArrayList<TransportDiscoveryData>();
-        private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
-        private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
-        private boolean mIncludeTxPowerLevel;
-        private boolean mIncludeDeviceName;
-
-        /**
-         * Add a service UUID to advertise data.
-         *
-         * @param serviceUuid A service UUID to be advertised.
-         * @throws IllegalArgumentException If the {@code serviceUuid} is null.
-         */
-        public Builder addServiceUuid(ParcelUuid serviceUuid) {
-            if (serviceUuid == null) {
-                throw new IllegalArgumentException("serviceUuid is null");
-            }
-            mServiceUuids.add(serviceUuid);
-            return this;
-        }
-
-        /**
-         * Add a service solicitation UUID to advertise data.
-         *
-         * @param serviceSolicitationUuid A service solicitation UUID to be advertised.
-         * @throws IllegalArgumentException If the {@code serviceSolicitationUuid} is null.
-         */
-        @NonNull
-        public Builder addServiceSolicitationUuid(@NonNull ParcelUuid serviceSolicitationUuid) {
-            if (serviceSolicitationUuid == null) {
-                throw new IllegalArgumentException("serviceSolicitationUuid is null");
-            }
-            mServiceSolicitationUuids.add(serviceSolicitationUuid);
-            return this;
-        }
-
-        /**
-         * Add service data to advertise data.
-         *
-         * @param serviceDataUuid 16-bit UUID of the service the data is associated with
-         * @param serviceData Service data
-         * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is
-         * empty.
-         */
-        public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
-            if (serviceDataUuid == null || serviceData == null) {
-                throw new IllegalArgumentException(
-                        "serviceDataUuid or serviceDataUuid is null");
-            }
-            mServiceData.put(serviceDataUuid, serviceData);
-            return this;
-        }
-
-        /**
-         * Add Transport Discovery Data to advertise data.
-         *
-         * @param transportDiscoveryData Transport Discovery Data, consisting of one or more
-         * Transport Blocks. Transport Discovery Data AD Type Code is already included.
-         * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty
-         */
-        @NonNull
-        public Builder addTransportDiscoveryData(
-                @NonNull TransportDiscoveryData transportDiscoveryData) {
-            if (transportDiscoveryData == null) {
-                throw new IllegalArgumentException("transportDiscoveryData is null");
-            }
-            mTransportDiscoveryData.add(transportDiscoveryData);
-            return this;
-        }
-
-        /**
-         * Add manufacturer specific data.
-         * <p>
-         * Please refer to the Bluetooth Assigned Numbers document provided by the <a
-         * href="https://www.bluetooth.org">Bluetooth SIG</a> for a list of existing company
-         * identifiers.
-         *
-         * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG.
-         * @param manufacturerSpecificData Manufacturer specific data
-         * @throws IllegalArgumentException If the {@code manufacturerId} is negative or {@code
-         * manufacturerSpecificData} is null.
-         */
-        public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
-            if (manufacturerId < 0) {
-                throw new IllegalArgumentException(
-                        "invalid manufacturerId - " + manufacturerId);
-            }
-            if (manufacturerSpecificData == null) {
-                throw new IllegalArgumentException("manufacturerSpecificData is null");
-            }
-            mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData);
-            return this;
-        }
-
-        /**
-         * Whether the transmission power level should be included in the advertise packet. Tx power
-         * level field takes 3 bytes in advertise packet.
-         */
-        public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) {
-            mIncludeTxPowerLevel = includeTxPowerLevel;
-            return this;
-        }
-
-        /**
-         * Set whether the device name should be included in advertise packet.
-         */
-        public Builder setIncludeDeviceName(boolean includeDeviceName) {
-            mIncludeDeviceName = includeDeviceName;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertiseData}.
-         */
-        public AdvertiseData build() {
-            return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids,
-                    mTransportDiscoveryData, mManufacturerSpecificData, mServiceData,
-                    mIncludeTxPowerLevel, mIncludeDeviceName);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
deleted file mode 100644
index 7129d76..0000000
--- a/core/java/android/bluetooth/le/AdvertiseSettings.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each
- * Bluetooth LE advertisement instance. Use {@link AdvertiseSettings.Builder} to create an
- * instance of this class.
- */
-public final class AdvertiseSettings implements Parcelable {
-    /**
-     * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
-     * advertising mode as it consumes the least power.
-     */
-    public static final int ADVERTISE_MODE_LOW_POWER = 0;
-
-    /**
-     * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising
-     * frequency and power consumption.
-     */
-    public static final int ADVERTISE_MODE_BALANCED = 1;
-
-    /**
-     * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power
-     * consumption and should not be used for continuous background advertising.
-     */
-    public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
-
-    /**
-     * Advertise using the lowest transmission (TX) power level. Low transmission power can be used
-     * to restrict the visibility range of advertising packets.
-     */
-    public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
-
-    /**
-     * Advertise using low TX power level.
-     */
-    public static final int ADVERTISE_TX_POWER_LOW = 1;
-
-    /**
-     * Advertise using medium TX power level.
-     */
-    public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
-
-    /**
-     * Advertise using high TX power level. This corresponds to largest visibility range of the
-     * advertising packet.
-     */
-    public static final int ADVERTISE_TX_POWER_HIGH = 3;
-
-    /**
-     * The maximum limited advertisement duration as specified by the Bluetooth SIG
-     */
-    private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
-
-    private final int mAdvertiseMode;
-    private final int mAdvertiseTxPowerLevel;
-    private final int mAdvertiseTimeoutMillis;
-    private final boolean mAdvertiseConnectable;
-
-    private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
-            boolean advertiseConnectable, int advertiseTimeout) {
-        mAdvertiseMode = advertiseMode;
-        mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
-        mAdvertiseConnectable = advertiseConnectable;
-        mAdvertiseTimeoutMillis = advertiseTimeout;
-    }
-
-    private AdvertiseSettings(Parcel in) {
-        mAdvertiseMode = in.readInt();
-        mAdvertiseTxPowerLevel = in.readInt();
-        mAdvertiseConnectable = in.readInt() != 0;
-        mAdvertiseTimeoutMillis = in.readInt();
-    }
-
-    /**
-     * Returns the advertise mode.
-     */
-    public int getMode() {
-        return mAdvertiseMode;
-    }
-
-    /**
-     * Returns the TX power level for advertising.
-     */
-    public int getTxPowerLevel() {
-        return mAdvertiseTxPowerLevel;
-    }
-
-    /**
-     * Returns whether the advertisement will indicate connectable.
-     */
-    public boolean isConnectable() {
-        return mAdvertiseConnectable;
-    }
-
-    /**
-     * Returns the advertising time limit in milliseconds.
-     */
-    public int getTimeout() {
-        return mAdvertiseTimeoutMillis;
-    }
-
-    @Override
-    public String toString() {
-        return "Settings [mAdvertiseMode=" + mAdvertiseMode
-                + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel
-                + ", mAdvertiseConnectable=" + mAdvertiseConnectable
-                + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mAdvertiseMode);
-        dest.writeInt(mAdvertiseTxPowerLevel);
-        dest.writeInt(mAdvertiseConnectable ? 1 : 0);
-        dest.writeInt(mAdvertiseTimeoutMillis);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR =
-            new Creator<AdvertiseSettings>() {
-                @Override
-                public AdvertiseSettings[] newArray(int size) {
-                    return new AdvertiseSettings[size];
-                }
-
-                @Override
-                public AdvertiseSettings createFromParcel(Parcel in) {
-                    return new AdvertiseSettings(in);
-                }
-            };
-
-    /**
-     * Builder class for {@link AdvertiseSettings}.
-     */
-    public static final class Builder {
-        private int mMode = ADVERTISE_MODE_LOW_POWER;
-        private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
-        private int mTimeoutMillis = 0;
-        private boolean mConnectable = true;
-
-        /**
-         * Set advertise mode to control the advertising power and latency.
-         *
-         * @param advertiseMode Bluetooth LE Advertising mode, can only be one of {@link
-         * AdvertiseSettings#ADVERTISE_MODE_LOW_POWER},
-         * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED},
-         * or {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}.
-         * @throws IllegalArgumentException If the advertiseMode is invalid.
-         */
-        public Builder setAdvertiseMode(int advertiseMode) {
-            if (advertiseMode < ADVERTISE_MODE_LOW_POWER
-                    || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
-                throw new IllegalArgumentException("unknown mode " + advertiseMode);
-            }
-            mMode = advertiseMode;
-            return this;
-        }
-
-        /**
-         * Set advertise TX power level to control the transmission power level for the advertising.
-         *
-         * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of
-         * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, {@link
-         * AdvertiseSettings#ADVERTISE_TX_POWER_LOW},
-         * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM}
-         * or {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}.
-         * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
-         */
-        public Builder setTxPowerLevel(int txPowerLevel) {
-            if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
-                    || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
-                throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
-            }
-            mTxPowerLevel = txPowerLevel;
-            return this;
-        }
-
-        /**
-         * Set whether the advertisement type should be connectable or non-connectable.
-         *
-         * @param connectable Controls whether the advertisment type will be connectable (true) or
-         * non-connectable (false).
-         */
-        public Builder setConnectable(boolean connectable) {
-            mConnectable = connectable;
-            return this;
-        }
-
-        /**
-         * Limit advertising to a given amount of time.
-         *
-         * @param timeoutMillis Advertising time limit. May not exceed 180000 milliseconds. A value
-         * of 0 will disable the time limit.
-         * @throws IllegalArgumentException If the provided timeout is over 180000 ms.
-         */
-        public Builder setTimeout(int timeoutMillis) {
-            if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) {
-                throw new IllegalArgumentException("timeoutMillis invalid (must be 0-"
-                        + LIMITED_ADVERTISING_MAX_MILLIS + " milliseconds)");
-            }
-            mTimeoutMillis = timeoutMillis;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertiseSettings} object.
-         */
-        public AdvertiseSettings build() {
-            return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
deleted file mode 100644
index bbdb695..0000000
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ /dev/null
@@ -1,230 +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 android.bluetooth.le;
-
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * This class provides a way to control single Bluetooth LE advertising instance.
- * <p>
- * To get an instance of {@link AdvertisingSet}, call the
- * {@link BluetoothLeAdvertiser#startAdvertisingSet} method.
- *
- * @see AdvertiseData
- */
-public final class AdvertisingSet {
-    private static final String TAG = "AdvertisingSet";
-
-    private final IBluetoothGatt mGatt;
-    private int mAdvertiserId;
-    private AttributionSource mAttributionSource;
-
-    /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager,
-            AttributionSource attributionSource) {
-        mAdvertiserId = advertiserId;
-        mAttributionSource = attributionSource;
-        try {
-            mGatt = bluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-            throw new IllegalStateException("Failed to get Bluetooth");
-        }
-    }
-
-    /* package */ void setAdvertiserId(int advertiserId) {
-        mAdvertiserId = advertiserId;
-    }
-
-    /**
-     * Enables Advertising. This method returns immediately, the operation status is
-     * delivered through {@code callback.onAdvertisingEnabled()}.
-     *
-     * @param enable whether the advertising should be enabled (true), or disabled (false)
-     * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
-     * (655,350 ms)
-     * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the
-     * controller shall attempt to send prior to terminating the extended advertising, even if the
-     * duration has not expired. Valid range is from 1 to 255.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void enableAdvertising(boolean enable, int duration,
-            int maxExtendedAdvertisingEvents) {
-        try {
-            mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration,
-                    maxExtendedAdvertisingEvents, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for
-     * specified AdvertisingSetParameters. This method returns immediately, the operation status is
-     * delivered through {@code callback.onAdvertisingDataSet()}.
-     * <p>
-     * Advertising data must be empty if non-legacy scannable advertising is used.
-     *
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags. If the update takes place when the advertising set is
-     * enabled, the data can be maximum 251 bytes long.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setAdvertisingData(AdvertiseData advertiseData) {
-        try {
-            mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Set/update scan response data. Make sure that data doesn't exceed the size limit for
-     * specified AdvertisingSetParameters. This method returns immediately, the operation status
-     * is delivered through {@code callback.onScanResponseDataSet()}.
-     *
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place
-     * when the advertising set is enabled, the data can be maximum 251 bytes long.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setScanResponseData(AdvertiseData scanResponse) {
-        try {
-            mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Update advertising parameters associated with this AdvertisingSet. Must be called when
-     * advertising is not active. This method returns immediately, the operation status is delivered
-     * through {@code callback.onAdvertisingParametersUpdated}.
-     *
-     * @param parameters advertising set parameters.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
-        try {
-            mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Update periodic advertising parameters associated with this set. Must be called when
-     * periodic advertising is not enabled. This method returns immediately, the operation
-     * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
-        try {
-            mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters,
-     * or after advertising was started with periodic advertising data set. This method returns
-     * immediately, the operation status is delivered through
-     * {@code callback.onPeriodicAdvertisingDataSet()}.
-     *
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the
-     * periodic advertising is enabled for this set, the data can be maximum 251 bytes long.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setPeriodicAdvertisingData(AdvertiseData periodicData) {
-        try {
-            mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Used to enable/disable periodic advertising. This method returns immediately, the operation
-     * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}.
-     *
-     * @param enable whether the periodic advertising should be enabled (true), or disabled
-     * (false).
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setPeriodicAdvertisingEnabled(boolean enable) {
-        try {
-            mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Returns address associated with this advertising set.
-     * This method is exposed only for Bluetooth PTS tests, no app or system service
-     * should ever use it.
-     *
-     * @hide
-     */
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_ADVERTISE,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void getOwnAddress() {
-        try {
-            mGatt.getOwnAddress(mAdvertiserId, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Returns advertiserId associated with this advertising set.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public int getAdvertiserId() {
-        return mAdvertiserId;
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetCallback.java b/core/java/android/bluetooth/le/AdvertisingSetCallback.java
deleted file mode 100644
index 51324fd..0000000
--- a/core/java/android/bluetooth/le/AdvertisingSetCallback.java
+++ /dev/null
@@ -1,164 +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 android.bluetooth.le;
-
-/**
- * Bluetooth LE advertising set callbacks, used to deliver advertising operation
- * status.
- */
-public abstract class AdvertisingSetCallback {
-
-    /**
-     * The requested operation was successful.
-     */
-    public static final int ADVERTISE_SUCCESS = 0;
-
-    /**
-     * Failed to start advertising as the advertise data to be broadcasted is too
-     * large.
-     */
-    public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
-
-    /**
-     * Failed to start advertising because no advertising instance is available.
-     */
-    public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
-
-    /**
-     * Failed to start advertising as the advertising is already started.
-     */
-    public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
-
-    /**
-     * Operation failed due to an internal error.
-     */
-    public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4;
-
-    /**
-     * This feature is not supported on this platform.
-     */
-    public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5;
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
-     * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet
-     * contains the started set and it is advertising. If error occurred, advertisingSet is
-     * null, and status will be set to proper error code.
-     *
-     * @param advertisingSet The advertising set that was started or null if error.
-     * @param txPower tx power that will be used for this set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet}
-     * indicating advertising set is stopped.
-     *
-     * @param advertisingSet The advertising set.
-     */
-    public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
-    }
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
-     * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is
-     * advertising.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
-     * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
-     * result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param txPower tx power that will be used for this set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
-            int txPower, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet,
-            int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnabled}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
-            int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#getOwnAddress()}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param addressType type of address.
-     * @param address advertising set bluetooth address.
-     * @hide
-     */
-    public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
deleted file mode 100644
index e39b198..0000000
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ /dev/null
@@ -1,437 +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 android.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The {@link AdvertisingSetParameters} provide a way to adjust advertising
- * preferences for each
- * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to
- * create an
- * instance of this class.
- */
-public final class AdvertisingSetParameters implements Parcelable {
-
-    /**
-     * Advertise on low frequency, around every 1000ms. This is the default and
-     * preferred advertising mode as it consumes the least power.
-     */
-    public static final int INTERVAL_HIGH = 1600;
-
-    /**
-     * Advertise on medium frequency, around every 250ms. This is balanced
-     * between advertising frequency and power consumption.
-     */
-    public static final int INTERVAL_MEDIUM = 400;
-
-    /**
-     * Perform high frequency, low latency advertising, around every 100ms. This
-     * has the highest power consumption and should not be used for continuous
-     * background advertising.
-     */
-    public static final int INTERVAL_LOW = 160;
-
-    /**
-     * Minimum value for advertising interval.
-     */
-    public static final int INTERVAL_MIN = 160;
-
-    /**
-     * Maximum value for advertising interval.
-     */
-    public static final int INTERVAL_MAX = 16777215;
-
-    /**
-     * Advertise using the lowest transmission (TX) power level. Low transmission
-     * power can be used to restrict the visibility range of advertising packets.
-     */
-    public static final int TX_POWER_ULTRA_LOW = -21;
-
-    /**
-     * Advertise using low TX power level.
-     */
-    public static final int TX_POWER_LOW = -15;
-
-    /**
-     * Advertise using medium TX power level.
-     */
-    public static final int TX_POWER_MEDIUM = -7;
-
-    /**
-     * Advertise using high TX power level. This corresponds to largest visibility
-     * range of the advertising packet.
-     */
-    public static final int TX_POWER_HIGH = 1;
-
-    /**
-     * Minimum value for TX power.
-     */
-    public static final int TX_POWER_MIN = -127;
-
-    /**
-     * Maximum value for TX power.
-     */
-    public static final int TX_POWER_MAX = 1;
-
-    /**
-     * The maximum limited advertisement duration as specified by the Bluetooth
-     * SIG
-     */
-    private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
-
-    private final boolean mIsLegacy;
-    private final boolean mIsAnonymous;
-    private final boolean mIncludeTxPower;
-    private final int mPrimaryPhy;
-    private final int mSecondaryPhy;
-    private final boolean mConnectable;
-    private final boolean mScannable;
-    private final int mInterval;
-    private final int mTxPowerLevel;
-
-    private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy,
-            boolean isAnonymous, boolean includeTxPower,
-            int primaryPhy, int secondaryPhy,
-            int interval, int txPowerLevel) {
-        mConnectable = connectable;
-        mScannable = scannable;
-        mIsLegacy = isLegacy;
-        mIsAnonymous = isAnonymous;
-        mIncludeTxPower = includeTxPower;
-        mPrimaryPhy = primaryPhy;
-        mSecondaryPhy = secondaryPhy;
-        mInterval = interval;
-        mTxPowerLevel = txPowerLevel;
-    }
-
-    private AdvertisingSetParameters(Parcel in) {
-        mConnectable = in.readInt() != 0;
-        mScannable = in.readInt() != 0;
-        mIsLegacy = in.readInt() != 0;
-        mIsAnonymous = in.readInt() != 0;
-        mIncludeTxPower = in.readInt() != 0;
-        mPrimaryPhy = in.readInt();
-        mSecondaryPhy = in.readInt();
-        mInterval = in.readInt();
-        mTxPowerLevel = in.readInt();
-    }
-
-    /**
-     * Returns whether the advertisement will be connectable.
-     */
-    public boolean isConnectable() {
-        return mConnectable;
-    }
-
-    /**
-     * Returns whether the advertisement will be scannable.
-     */
-    public boolean isScannable() {
-        return mScannable;
-    }
-
-    /**
-     * Returns whether the legacy advertisement will be used.
-     */
-    public boolean isLegacy() {
-        return mIsLegacy;
-    }
-
-    /**
-     * Returns whether the advertisement will be anonymous.
-     */
-    public boolean isAnonymous() {
-        return mIsAnonymous;
-    }
-
-    /**
-     * Returns whether the TX Power will be included.
-     */
-    public boolean includeTxPower() {
-        return mIncludeTxPower;
-    }
-
-    /**
-     * Returns the primary advertising phy.
-     */
-    public int getPrimaryPhy() {
-        return mPrimaryPhy;
-    }
-
-    /**
-     * Returns the secondary advertising phy.
-     */
-    public int getSecondaryPhy() {
-        return mSecondaryPhy;
-    }
-
-    /**
-     * Returns the advertising interval.
-     */
-    public int getInterval() {
-        return mInterval;
-    }
-
-    /**
-     * Returns the TX power level for advertising.
-     */
-    public int getTxPowerLevel() {
-        return mTxPowerLevel;
-    }
-
-    @Override
-    public String toString() {
-        return "AdvertisingSetParameters [connectable=" + mConnectable
-                + ", isLegacy=" + mIsLegacy
-                + ", isAnonymous=" + mIsAnonymous
-                + ", includeTxPower=" + mIncludeTxPower
-                + ", primaryPhy=" + mPrimaryPhy
-                + ", secondaryPhy=" + mSecondaryPhy
-                + ", interval=" + mInterval
-                + ", txPowerLevel=" + mTxPowerLevel + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mConnectable ? 1 : 0);
-        dest.writeInt(mScannable ? 1 : 0);
-        dest.writeInt(mIsLegacy ? 1 : 0);
-        dest.writeInt(mIsAnonymous ? 1 : 0);
-        dest.writeInt(mIncludeTxPower ? 1 : 0);
-        dest.writeInt(mPrimaryPhy);
-        dest.writeInt(mSecondaryPhy);
-        dest.writeInt(mInterval);
-        dest.writeInt(mTxPowerLevel);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR =
-            new Creator<AdvertisingSetParameters>() {
-                @Override
-                public AdvertisingSetParameters[] newArray(int size) {
-                    return new AdvertisingSetParameters[size];
-                }
-
-                @Override
-                public AdvertisingSetParameters createFromParcel(Parcel in) {
-                    return new AdvertisingSetParameters(in);
-                }
-            };
-
-    /**
-     * Builder class for {@link AdvertisingSetParameters}.
-     */
-    public static final class Builder {
-        private boolean mConnectable = false;
-        private boolean mScannable = false;
-        private boolean mIsLegacy = false;
-        private boolean mIsAnonymous = false;
-        private boolean mIncludeTxPower = false;
-        private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
-        private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
-        private int mInterval = INTERVAL_LOW;
-        private int mTxPowerLevel = TX_POWER_MEDIUM;
-
-        /**
-         * Set whether the advertisement type should be connectable or
-         * non-connectable.
-         * Legacy advertisements can be both connectable and scannable. Non-legacy
-         * advertisements can be only scannable or only connectable.
-         *
-         * @param connectable Controls whether the advertisement type will be connectable (true) or
-         * non-connectable (false).
-         */
-        public Builder setConnectable(boolean connectable) {
-            mConnectable = connectable;
-            return this;
-        }
-
-        /**
-         * Set whether the advertisement type should be scannable.
-         * Legacy advertisements can be both connectable and scannable. Non-legacy
-         * advertisements can be only scannable or only connectable.
-         *
-         * @param scannable Controls whether the advertisement type will be scannable (true) or
-         * non-scannable (false).
-         */
-        public Builder setScannable(boolean scannable) {
-            mScannable = scannable;
-            return this;
-        }
-
-        /**
-         * When set to true, advertising set will advertise 4.x Spec compliant
-         * advertisements.
-         *
-         * @param isLegacy whether legacy advertising mode should be used.
-         */
-        public Builder setLegacyMode(boolean isLegacy) {
-            mIsLegacy = isLegacy;
-            return this;
-        }
-
-        /**
-         * Set whether advertiser address should be ommited from all packets. If this
-         * mode is used, periodic advertising can't be enabled for this set.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * @param isAnonymous whether anonymous advertising should be used.
-         */
-        public Builder setAnonymous(boolean isAnonymous) {
-            mIsAnonymous = isAnonymous;
-            return this;
-        }
-
-        /**
-         * Set whether TX power should be included in the extended header.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * @param includeTxPower whether TX power should be included in extended header
-         */
-        public Builder setIncludeTxPower(boolean includeTxPower) {
-            mIncludeTxPower = includeTxPower;
-            return this;
-        }
-
-        /**
-         * Set the primary physical channel used for this advertising set.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is
-         * supported on this device.
-         *
-         * @param primaryPhy Primary advertising physical channel, can only be {@link
-         * BluetoothDevice#PHY_LE_1M} or {@link BluetoothDevice#PHY_LE_CODED}.
-         * @throws IllegalArgumentException If the primaryPhy is invalid.
-         */
-        public Builder setPrimaryPhy(int primaryPhy) {
-            if (primaryPhy != BluetoothDevice.PHY_LE_1M
-                    && primaryPhy != BluetoothDevice.PHY_LE_CODED) {
-                throw new IllegalArgumentException("bad primaryPhy " + primaryPhy);
-            }
-            mPrimaryPhy = primaryPhy;
-            return this;
-        }
-
-        /**
-         * Set the secondary physical channel used for this advertising set.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * Use {@link BluetoothAdapter#isLeCodedPhySupported} and
-         * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is
-         * supported on this device.
-         *
-         * @param secondaryPhy Secondary advertising physical channel, can only be one of {@link
-         * BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M} or {@link
-         * BluetoothDevice#PHY_LE_CODED}.
-         * @throws IllegalArgumentException If the secondaryPhy is invalid.
-         */
-        public Builder setSecondaryPhy(int secondaryPhy) {
-            if (secondaryPhy != BluetoothDevice.PHY_LE_1M
-                    && secondaryPhy != BluetoothDevice.PHY_LE_2M
-                    && secondaryPhy != BluetoothDevice.PHY_LE_CODED) {
-                throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy);
-            }
-            mSecondaryPhy = secondaryPhy;
-            return this;
-        }
-
-        /**
-         * Set advertising interval.
-         *
-         * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid range is from
-         * 160 (100ms) to 16777215 (10,485.759375 s). Recommended values are: {@link
-         * AdvertisingSetParameters#INTERVAL_LOW}, {@link AdvertisingSetParameters#INTERVAL_MEDIUM},
-         * or {@link AdvertisingSetParameters#INTERVAL_HIGH}.
-         * @throws IllegalArgumentException If the interval is invalid.
-         */
-        public Builder setInterval(int interval) {
-            if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
-                throw new IllegalArgumentException("unknown interval " + interval);
-            }
-            mInterval = interval;
-            return this;
-        }
-
-        /**
-         * Set the transmission power level for the advertising.
-         *
-         * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in dBm. The valid
-         * range is [-127, 1] Recommended values are:
-         * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW},
-         * {@link AdvertisingSetParameters#TX_POWER_LOW},
-         * {@link AdvertisingSetParameters#TX_POWER_MEDIUM},
-         * or {@link AdvertisingSetParameters#TX_POWER_HIGH}.
-         * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
-         */
-        public Builder setTxPowerLevel(int txPowerLevel) {
-            if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
-                throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel);
-            }
-            mTxPowerLevel = txPowerLevel;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertisingSetParameters} object.
-         *
-         * @throws IllegalStateException if invalid combination of parameters is used.
-         */
-        public AdvertisingSetParameters build() {
-            if (mIsLegacy) {
-                if (mIsAnonymous) {
-                    throw new IllegalArgumentException("Legacy advertising can't be anonymous");
-                }
-
-                if (mConnectable && !mScannable) {
-                    throw new IllegalStateException(
-                            "Legacy advertisement can't be connectable and non-scannable");
-                }
-
-                if (mIncludeTxPower) {
-                    throw new IllegalStateException(
-                            "Legacy advertising can't include TX power level in header");
-                }
-            } else {
-                if (mConnectable && mScannable) {
-                    throw new IllegalStateException(
-                            "Advertising can't be both connectable and scannable");
-                }
-
-                if (mIsAnonymous && mConnectable) {
-                    throw new IllegalStateException(
-                            "Advertising can't be both connectable and anonymous");
-                }
-            }
-
-            return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous,
-                    mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
deleted file mode 100644
index b9f8a57..0000000
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class provides a way to perform Bluetooth LE advertise operations, such as starting and
- * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data
- * represented by {@link AdvertiseData}.
- * <p>
- * To get an instance of {@link BluetoothLeAdvertiser}, call the
- * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
- *
- * @see AdvertiseData
- */
-public final class BluetoothLeAdvertiser {
-
-    private static final String TAG = "BluetoothLeAdvertiser";
-
-    private static final int MAX_ADVERTISING_DATA_BYTES = 1650;
-    private static final int MAX_LEGACY_ADVERTISING_DATA_BYTES = 31;
-    // Each fields need one byte for field length and another byte for field type.
-    private static final int OVERHEAD_BYTES_PER_FIELD = 2;
-    // Flags field will be set by system.
-    private static final int FLAGS_FIELD_BYTES = 3;
-    private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2;
-
-    private final BluetoothAdapter mBluetoothAdapter;
-    private final IBluetoothManager mBluetoothManager;
-    private final AttributionSource mAttributionSource;
-
-    private final Handler mHandler;
-    private final Map<AdvertiseCallback, AdvertisingSetCallback>
-            mLegacyAdvertisers = new HashMap<>();
-    private final Map<AdvertisingSetCallback, IAdvertisingSetCallback>
-            mCallbackWrappers = Collections.synchronizedMap(new HashMap<>());
-    private final Map<Integer, AdvertisingSet>
-            mAdvertisingSets = Collections.synchronizedMap(new HashMap<>());
-
-    /**
-     * Use BluetoothAdapter.getLeAdvertiser() instead.
-     *
-     * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management
-     * @hide
-     */
-    public BluetoothLeAdvertiser(BluetoothAdapter bluetoothAdapter) {
-        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
-        mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
-        mAttributionSource = mBluetoothAdapter.getAttributionSource();
-        mHandler = new Handler(Looper.getMainLooper());
-    }
-
-    /**
-     * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted.
-     * Returns immediately, the operation status is delivered through {@code callback}.
-     *
-     * @param settings Settings for Bluetooth LE advertising.
-     * @param advertiseData Advertisement data to be broadcasted.
-     * @param callback Callback for advertising status.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertising(AdvertiseSettings settings,
-            AdvertiseData advertiseData, final AdvertiseCallback callback) {
-        startAdvertising(settings, advertiseData, null, callback);
-    }
-
-    /**
-     * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the
-     * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an
-     * active scan request. This method returns immediately, the operation status is delivered
-     * through {@code callback}.
-     *
-     * @param settings Settings for Bluetooth LE advertising.
-     * @param advertiseData Advertisement data to be advertised in advertisement packet.
-     * @param scanResponse Scan response associated with the advertisement data.
-     * @param callback Callback for advertising status.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertising(AdvertiseSettings settings,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            final AdvertiseCallback callback) {
-        synchronized (mLegacyAdvertisers) {
-            BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-            if (callback == null) {
-                throw new IllegalArgumentException("callback cannot be null");
-            }
-            boolean isConnectable = settings.isConnectable();
-            if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES
-                    || totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
-                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
-                return;
-            }
-            if (mLegacyAdvertisers.containsKey(callback)) {
-                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
-                return;
-            }
-
-            AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder();
-            parameters.setLegacyMode(true);
-            parameters.setConnectable(isConnectable);
-            parameters.setScannable(true); // legacy advertisements we support are always scannable
-            if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
-                parameters.setInterval(1600); // 1s
-            } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
-                parameters.setInterval(400); // 250ms
-            } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) {
-                parameters.setInterval(160); // 100ms
-            }
-
-            if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW) {
-                parameters.setTxPowerLevel(-21);
-            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_LOW) {
-                parameters.setTxPowerLevel(-15);
-            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) {
-                parameters.setTxPowerLevel(-7);
-            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) {
-                parameters.setTxPowerLevel(1);
-            }
-
-            int duration = 0;
-            int timeoutMillis = settings.getTimeout();
-            if (timeoutMillis > 0) {
-                duration = (timeoutMillis < 10) ? 1 : timeoutMillis / 10;
-            }
-
-            AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings);
-            mLegacyAdvertisers.put(callback, wrapped);
-            startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null,
-                    duration, 0, wrapped);
-        }
-    }
-
-    @SuppressLint({
-            "AndroidFrameworkBluetoothPermission",
-            "AndroidFrameworkRequiresPermission",
-    })
-    AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) {
-        return new AdvertisingSetCallback() {
-            @Override
-            public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
-                    int status) {
-                if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
-                    postStartFailure(callback, status);
-                    return;
-                }
-
-                postStartSuccess(callback, settings);
-            }
-
-            /* Legacy advertiser is disabled on timeout */
-            @Override
-            public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled,
-                    int status) {
-                if (enabled) {
-                    Log.e(TAG, "Legacy advertiser should be only disabled on timeout,"
-                            + " but was enabled!");
-                    return;
-                }
-
-                stopAdvertising(callback);
-            }
-
-        };
-    }
-
-    /**
-     * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
-     * {@link BluetoothLeAdvertiser#startAdvertising}.
-     *
-     * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void stopAdvertising(final AdvertiseCallback callback) {
-        synchronized (mLegacyAdvertisers) {
-            if (callback == null) {
-                throw new IllegalArgumentException("callback cannot be null");
-            }
-            AdvertisingSetCallback wrapper = mLegacyAdvertisers.get(callback);
-            if (wrapper == null) return;
-
-            stopAdvertisingSet(wrapper);
-
-            mLegacyAdvertisers.remove(callback);
-        }
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param callback Callback for advertising set.
-     * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, AdvertisingSetCallback callback) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                periodicData, 0, 0, callback, new Handler(Looper.getMainLooper()));
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param callback Callback for advertising set.
-     * @param handler thread upon which the callbacks will be invoked.
-     * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, AdvertisingSetCallback callback,
-            Handler handler) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                periodicData, 0, 0, callback, handler);
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
-     * (655,350 ms). 0 means advertising should continue until stopped.
-     * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the
-     * controller shall attempt to send prior to terminating the extended advertising, even if the
-     * duration has not expired. Valid range is from 1 to 255. 0 means no maximum.
-     * @param callback Callback for advertising set.
-     * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, int duration,
-            int maxExtendedAdvertisingEvents,
-            AdvertisingSetCallback callback) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                periodicData, duration, maxExtendedAdvertisingEvents, callback,
-                new Handler(Looper.getMainLooper()));
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters Advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}
-     * @param periodicParameters Periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}
-     * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
-     * (655,350 ms). 0 means advertising should continue until stopped.
-     * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the
-     * controller shall attempt to send prior to terminating the extended advertising, even if the
-     * duration has not expired. Valid range is from 1 to 255. 0 means no maximum.
-     * @param callback Callback for advertising set.
-     * @param handler Thread upon which the callbacks will be invoked.
-     * @throws IllegalArgumentException When any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller, or when
-     * maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended
-     * Advertising
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, int duration,
-            int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback,
-            Handler handler) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-
-        boolean isConnectable = parameters.isConnectable();
-        if (parameters.isLegacy()) {
-            if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
-                throw new IllegalArgumentException("Legacy advertising data too big");
-            }
-
-            if (totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
-                throw new IllegalArgumentException("Legacy scan response data too big");
-            }
-        } else {
-            boolean supportCodedPhy = mBluetoothAdapter.isLeCodedPhySupported();
-            boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported();
-            int pphy = parameters.getPrimaryPhy();
-            int sphy = parameters.getSecondaryPhy();
-            if (pphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) {
-                throw new IllegalArgumentException("Unsupported primary PHY selected");
-            }
-
-            if ((sphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy)
-                    || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) {
-                throw new IllegalArgumentException("Unsupported secondary PHY selected");
-            }
-
-            int maxData = mBluetoothAdapter.getLeMaximumAdvertisingDataLength();
-            if (totalBytes(advertiseData, isConnectable) > maxData) {
-                throw new IllegalArgumentException("Advertising data too big");
-            }
-
-            if (totalBytes(scanResponse, false) > maxData) {
-                throw new IllegalArgumentException("Scan response data too big");
-            }
-
-            if (totalBytes(periodicData, false) > maxData) {
-                throw new IllegalArgumentException("Periodic advertising data too big");
-            }
-
-            boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported();
-            if (periodicParameters != null && !supportPeriodic) {
-                throw new IllegalArgumentException(
-                        "Controller does not support LE Periodic Advertising");
-            }
-        }
-
-        if (maxExtendedAdvertisingEvents < 0 || maxExtendedAdvertisingEvents > 255) {
-            throw new IllegalArgumentException(
-                    "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents);
-        }
-
-        if (maxExtendedAdvertisingEvents != 0
-                && !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) {
-            throw new IllegalArgumentException(
-                    "Can't use maxExtendedAdvertisingEvents with controller that don't support "
-                            + "LE Extended Advertising");
-        }
-
-        if (duration < 0 || duration > 65535) {
-            throw new IllegalArgumentException("duration out of range: " + duration);
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth GATT - ", e);
-            postStartSetFailure(handler, callback,
-                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-            return;
-        }
-
-        if (gatt == null) {
-            Log.e(TAG, "Bluetooth GATT is null");
-            postStartSetFailure(handler, callback,
-                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-            return;
-        }
-
-        IAdvertisingSetCallback wrapped = wrap(callback, handler);
-        if (mCallbackWrappers.putIfAbsent(callback, wrapped) != null) {
-            throw new IllegalArgumentException(
-                    "callback instance already associated with advertising");
-        }
-
-        try {
-            gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                    periodicData, duration, maxExtendedAdvertisingEvents, wrapped,
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to start advertising set - ", e);
-            postStartSetFailure(handler, callback,
-                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-            return;
-        }
-    }
-
-    /**
-     * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link
-     * BluetoothLeAdvertiser#startAdvertisingSet}.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void stopAdvertisingSet(AdvertisingSetCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-
-        IAdvertisingSetCallback wrapped = mCallbackWrappers.remove(callback);
-        if (wrapped == null) {
-            return;
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-            gatt.stopAdvertisingSet(wrapped, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to stop advertising - ", e);
-        }
-    }
-
-    /**
-     * Cleans up advertisers. Should be called when bluetooth is down.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public void cleanup() {
-        mLegacyAdvertisers.clear();
-        mCallbackWrappers.clear();
-        mAdvertisingSets.clear();
-    }
-
-    // Compute the size of advertisement data or scan resp
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) {
-        if (data == null) return 0;
-        // Flags field is omitted if the advertising is not connectable.
-        int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0;
-        if (data.getServiceUuids() != null) {
-            int num16BitUuids = 0;
-            int num32BitUuids = 0;
-            int num128BitUuids = 0;
-            for (ParcelUuid uuid : data.getServiceUuids()) {
-                if (BluetoothUuid.is16BitUuid(uuid)) {
-                    ++num16BitUuids;
-                } else if (BluetoothUuid.is32BitUuid(uuid)) {
-                    ++num32BitUuids;
-                } else {
-                    ++num128BitUuids;
-                }
-            }
-            // 16 bit service uuids are grouped into one field when doing advertising.
-            if (num16BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
-            }
-            // 32 bit service uuids are grouped into one field when doing advertising.
-            if (num32BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
-            }
-            // 128 bit service uuids are grouped into one field when doing advertising.
-            if (num128BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD
-                        + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
-            }
-        }
-        if (data.getServiceSolicitationUuids() != null) {
-            int num16BitUuids = 0;
-            int num32BitUuids = 0;
-            int num128BitUuids = 0;
-            for (ParcelUuid uuid : data.getServiceSolicitationUuids()) {
-                if (BluetoothUuid.is16BitUuid(uuid)) {
-                    ++num16BitUuids;
-                } else if (BluetoothUuid.is32BitUuid(uuid)) {
-                    ++num32BitUuids;
-                } else {
-                    ++num128BitUuids;
-                }
-            }
-            // 16 bit service uuids are grouped into one field when doing advertising.
-            if (num16BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
-            }
-            // 32 bit service uuids are grouped into one field when doing advertising.
-            if (num32BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
-            }
-            // 128 bit service uuids are grouped into one field when doing advertising.
-            if (num128BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD
-                        + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
-            }
-        }
-        for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) {
-            size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes();
-        }
-        for (ParcelUuid uuid : data.getServiceData().keySet()) {
-            int uuidLen = BluetoothUuid.uuidToBytes(uuid).length;
-            size += OVERHEAD_BYTES_PER_FIELD + uuidLen
-                    + byteLength(data.getServiceData().get(uuid));
-        }
-        for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) {
-            size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH
-                    + byteLength(data.getManufacturerSpecificData().valueAt(i));
-        }
-        if (data.getIncludeTxPowerLevel()) {
-            size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
-        }
-        if (data.getIncludeDeviceName()) {
-            final int length = mBluetoothAdapter.getNameLengthForAdvertise();
-            if (length >= 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + length;
-            }
-        }
-        return size;
-    }
-
-    private int byteLength(byte[] array) {
-        return array == null ? 0 : array.length;
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) {
-        return new IAdvertisingSetCallback.Stub() {
-            @Override
-            public void onAdvertisingSetStarted(int advertiserId, int txPower, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
-                            callback.onAdvertisingSetStarted(null, 0, status);
-                            mCallbackWrappers.remove(callback);
-                            return;
-                        }
-
-                        AdvertisingSet advertisingSet = new AdvertisingSet(
-                                advertiserId, mBluetoothManager, mAttributionSource);
-                        mAdvertisingSets.put(advertiserId, advertisingSet);
-                        callback.onAdvertisingSetStarted(advertisingSet, txPower, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onOwnAddressRead(int advertiserId, int addressType, String address) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onOwnAddressRead(advertisingSet, addressType, address);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingSetStopped(int advertiserId) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingSetStopped(advertisingSet);
-                        mAdvertisingSets.remove(advertiserId);
-                        mCallbackWrappers.remove(callback);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingEnabled(advertisingSet, enabled, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingDataSet(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingDataSet(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onScanResponseDataSet(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onScanResponseDataSet(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingParametersUpdated(advertisingSet, txPower, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onPeriodicAdvertisingDataSet(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onPeriodicAdvertisingEnabled(advertisingSet, enable, status);
-                    }
-                });
-            }
-        };
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback,
-            final int error) {
-        handler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onAdvertisingSetStarted(null, 0, error);
-            }
-        });
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postStartFailure(final AdvertiseCallback callback, final int error) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onStartFailure(error);
-            }
-        });
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postStartSuccess(final AdvertiseCallback callback,
-            final AdvertiseSettings settings) {
-        mHandler.post(new Runnable() {
-
-            @Override
-            public void run() {
-                callback.onStartSuccess(settings);
-            }
-        });
-    }
-}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
deleted file mode 100644
index 540e5a7..0000000
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.bluetooth.Attributable;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothGatt;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.WorkSource;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class provides methods to perform scan related operations for Bluetooth LE devices. An
- * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It
- * can also request different types of callbacks for delivering the result.
- * <p>
- * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
- * {@link BluetoothLeScanner}.
- *
- * @see ScanFilter
- */
-public final class BluetoothLeScanner {
-
-    private static final String TAG = "BluetoothLeScanner";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Extra containing a list of ScanResults. It can have one or more results if there was no
-     * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this
-     * extra will not be available.
-     */
-    public static final String EXTRA_LIST_SCAN_RESULT =
-            "android.bluetooth.le.extra.LIST_SCAN_RESULT";
-
-    /**
-     * Optional extra indicating the error code, if any. The error code will be one of the
-     * SCAN_FAILED_* codes in {@link ScanCallback}.
-     */
-    public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
-
-    /**
-     * Optional extra indicating the callback type, which will be one of
-     * CALLBACK_TYPE_* constants in {@link ScanSettings}.
-     *
-     * @see ScanCallback#onScanResult(int, ScanResult)
-     */
-    public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
-
-    private final BluetoothAdapter mBluetoothAdapter;
-    private final IBluetoothManager mBluetoothManager;
-    private final AttributionSource mAttributionSource;
-
-    private final Handler mHandler;
-    private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
-
-    /**
-     * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
-     *
-     * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
-     * @param opPackageName The opPackageName of the context this object was created from
-     * @param featureId  The featureId of the context this object was created from
-     * @hide
-     */
-    public BluetoothLeScanner(BluetoothAdapter bluetoothAdapter) {
-        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
-        mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
-        mAttributionSource = mBluetoothAdapter.getAttributionSource();
-        mHandler = new Handler(Looper.getMainLooper());
-        mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
-    }
-
-    /**
-     * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
-     * delivered through {@code callback}. For unfiltered scans, scanning is stopped on screen
-     * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use
-     * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}.
-     * <p>
-     * An app must have
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
-     * in order to get results. An App targeting Android Q or later must have
-     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get results.
-     *
-     * @param callback Callback used to deliver scan results.
-     * @throws IllegalArgumentException If {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void startScan(final ScanCallback callback) {
-        startScan(null, new ScanSettings.Builder().build(), callback);
-    }
-
-    /**
-     * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
-     * For unfiltered scans, scanning is stopped on screen off to save power. Scanning is
-     * resumed when screen is turned on again. To avoid this, do filetered scanning by
-     * using proper {@link ScanFilter}.
-     * <p>
-     * An app must have
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
-     * in order to get results. An App targeting Android Q or later must have
-     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get results.
-     *
-     * @param filters {@link ScanFilter}s for finding exact BLE devices.
-     * @param settings Settings for the scan.
-     * @param callback Callback used to deliver scan results.
-     * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void startScan(List<ScanFilter> filters, ScanSettings settings,
-            final ScanCallback callback) {
-        startScan(filters, settings, null, callback, /*callbackIntent=*/ null);
-    }
-
-    /**
-     * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via
-     * the PendingIntent. Use this method of scanning if your process is not always running and it
-     * should be started when scan results are available.
-     * <p>
-     * An app must have
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
-     * in order to get results. An App targeting Android Q or later must have
-     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get results.
-     * <p>
-     * When the PendingIntent is delivered, the Intent passed to the receiver or activity
-     * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE},
-     * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of
-     * the scan.
-     *
-     * @param filters Optional list of ScanFilters for finding exact BLE devices.
-     * @param settings Optional settings for the scan.
-     * @param callbackIntent The PendingIntent to deliver the result to.
-     * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request
-     * could not be sent.
-     * @see #stopScan(PendingIntent)
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings,
-            @NonNull PendingIntent callbackIntent) {
-        return startScan(filters,
-                settings != null ? settings : new ScanSettings.Builder().build(),
-                null, null, callbackIntent);
-    }
-
-    /**
-     * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to
-     * specify on behalf of which application(s) the work is being done.
-     *
-     * @param workSource {@link WorkSource} identifying the application(s) for which to blame for
-     * the scan.
-     * @param callback Callback used to deliver scan results.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.UPDATE_DEVICE_STATS
-    })
-    public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) {
-        startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback);
-    }
-
-    /**
-     * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but
-     * allows the caller to specify on behalf of which application(s) the work is being done.
-     *
-     * @param filters {@link ScanFilter}s for finding exact BLE devices.
-     * @param settings Settings for the scan.
-     * @param workSource {@link WorkSource} identifying the application(s) for which to blame for
-     * the scan.
-     * @param callback Callback used to deliver scan results.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.UPDATE_DEVICE_STATS
-    })
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
-            final WorkSource workSource, final ScanCallback callback) {
-        startScan(filters, settings, workSource, callback, null);
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    private int startScan(List<ScanFilter> filters, ScanSettings settings,
-            final WorkSource workSource, final ScanCallback callback,
-            final PendingIntent callbackIntent) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        if (callback == null && callbackIntent == null) {
-            throw new IllegalArgumentException("callback is null");
-        }
-        if (settings == null) {
-            throw new IllegalArgumentException("settings is null");
-        }
-        synchronized (mLeScanClients) {
-            if (callback != null && mLeScanClients.containsKey(callback)) {
-                return postCallbackErrorOrReturn(callback,
-                            ScanCallback.SCAN_FAILED_ALREADY_STARTED);
-            }
-            IBluetoothGatt gatt;
-            try {
-                gatt = mBluetoothManager.getBluetoothGatt();
-            } catch (RemoteException e) {
-                gatt = null;
-            }
-            if (gatt == null) {
-                return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
-            }
-            if (!isSettingsConfigAllowedForScan(settings)) {
-                return postCallbackErrorOrReturn(callback,
-                        ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
-            }
-            if (!isHardwareResourcesAvailableForScan(settings)) {
-                return postCallbackErrorOrReturn(callback,
-                        ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
-            }
-            if (!isSettingsAndFilterComboAllowed(settings, filters)) {
-                return postCallbackErrorOrReturn(callback,
-                        ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
-            }
-            if (callback != null) {
-                BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
-                        settings, workSource, callback);
-                wrapper.startRegistration();
-            } else {
-                try {
-                    gatt.startScanForIntent(callbackIntent, settings, filters,
-                            mAttributionSource);
-                } catch (RemoteException e) {
-                    return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
-                }
-            }
-        }
-        return ScanCallback.NO_ERROR;
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE scan.
-     *
-     * @param callback
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void stopScan(ScanCallback callback) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        synchronized (mLeScanClients) {
-            BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
-            if (wrapper == null) {
-                if (DBG) Log.d(TAG, "could not find callback wrapper");
-                return;
-            }
-            wrapper.stopLeScan();
-        }
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the
-     * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop
-     * scan may have no effect.
-     *
-     * @param callbackIntent The PendingIntent that was used to start the scan.
-     * @see #startScan(List, ScanSettings, PendingIntent)
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void stopScan(PendingIntent callbackIntent) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-            gatt.stopScanForIntent(callbackIntent, mAttributionSource);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
-     * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
-     * will be delivered through the {@code callback}.
-     *
-     * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
-     * used to start scan.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void flushPendingScanResults(ScanCallback callback) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null!");
-        }
-        synchronized (mLeScanClients) {
-            BleScanCallbackWrapper wrapper = mLeScanClients.get(callback);
-            if (wrapper == null) {
-                return;
-            }
-            wrapper.flushPendingBatchResults();
-        }
-    }
-
-    /**
-     * Start truncated scan.
-     *
-     * @deprecated this is not used anywhere
-     *
-     * @hide
-     */
-    @Deprecated
-    @SystemApi
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings,
-            final ScanCallback callback) {
-        int filterSize = truncatedFilters.size();
-        List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
-        for (TruncatedFilter filter : truncatedFilters) {
-            scanFilters.add(filter.getFilter());
-        }
-        startScan(scanFilters, settings, null, callback, null);
-    }
-
-    /**
-     * Cleans up scan clients. Should be called when bluetooth is down.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public void cleanup() {
-        mLeScanClients.clear();
-    }
-
-    /**
-     * Bluetooth GATT interface callbacks
-     */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private class BleScanCallbackWrapper extends IScannerCallback.Stub {
-        private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
-
-        private final ScanCallback mScanCallback;
-        private final List<ScanFilter> mFilters;
-        private final WorkSource mWorkSource;
-        private ScanSettings mSettings;
-        private IBluetoothGatt mBluetoothGatt;
-
-        // mLeHandle 0: not registered
-        // -2: registration failed because app is scanning to frequently
-        // -1: scan stopped or registration failed
-        // > 0: registered and scan started
-        private int mScannerId;
-
-        public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
-                List<ScanFilter> filters, ScanSettings settings,
-                WorkSource workSource, ScanCallback scanCallback) {
-            mBluetoothGatt = bluetoothGatt;
-            mFilters = filters;
-            mSettings = settings;
-            mWorkSource = workSource;
-            mScanCallback = scanCallback;
-            mScannerId = 0;
-        }
-
-        public void startRegistration() {
-            synchronized (this) {
-                // Scan stopped.
-                if (mScannerId == -1 || mScannerId == -2) return;
-                try {
-                    mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource);
-                    wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
-                } catch (InterruptedException | RemoteException e) {
-                    Log.e(TAG, "application registeration exception", e);
-                    postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
-                }
-                if (mScannerId > 0) {
-                    mLeScanClients.put(mScanCallback, this);
-                } else {
-                    // Registration timed out or got exception, reset RscannerId to -1 so no
-                    // subsequent operations can proceed.
-                    if (mScannerId == 0) mScannerId = -1;
-
-                    // If scanning too frequently, don't report anything to the app.
-                    if (mScannerId == -2) return;
-
-                    postCallbackError(mScanCallback,
-                            ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
-                }
-            }
-        }
-
-        @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-        public void stopLeScan() {
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
-                    return;
-                }
-                try {
-                    mBluetoothGatt.stopScan(mScannerId, mAttributionSource);
-                    mBluetoothGatt.unregisterScanner(mScannerId, mAttributionSource);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to stop scan and unregister", e);
-                }
-                mScannerId = -1;
-            }
-        }
-
-        @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-        void flushPendingBatchResults() {
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
-                    return;
-                }
-                try {
-                    mBluetoothGatt.flushPendingBatchResults(mScannerId, mAttributionSource);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to get pending scan results", e);
-                }
-            }
-        }
-
-        /**
-         * Application interface registered - app is ready to go
-         */
-        @Override
-        public void onScannerRegistered(int status, int scannerId) {
-            Log.d(TAG, "onScannerRegistered() - status=" + status
-                    + " scannerId=" + scannerId + " mScannerId=" + mScannerId);
-            synchronized (this) {
-                if (status == BluetoothGatt.GATT_SUCCESS) {
-                    try {
-                        if (mScannerId == -1) {
-                            // Registration succeeds after timeout, unregister scanner.
-                            mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource);
-                        } else {
-                            mScannerId = scannerId;
-                            mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
-                                    mAttributionSource);
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "fail to start le scan: " + e);
-                        mScannerId = -1;
-                    }
-                } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) {
-                    // applicaiton was scanning too frequently
-                    mScannerId = -2;
-                } else {
-                    // registration failed
-                    mScannerId = -1;
-                }
-                notifyAll();
-            }
-        }
-
-        /**
-         * Callback reporting an LE scan result.
-         *
-         * @hide
-         */
-        @Override
-        public void onScanResult(final ScanResult scanResult) {
-            Attributable.setAttributionSource(scanResult, mAttributionSource);
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId);
-            }
-            if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
-
-            // Check null in case the scan has been stopped
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Ignoring result as scan stopped.");
-                    }
-                    return;
-                };
-            }
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "onScanResult() - handler run");
-                    }
-                    mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
-                }
-            });
-        }
-
-        @Override
-        public void onBatchScanResults(final List<ScanResult> results) {
-            Attributable.setAttributionSource(results, mAttributionSource);
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mScanCallback.onBatchScanResults(results);
-                }
-            });
-        }
-
-        @Override
-        public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
-            Attributable.setAttributionSource(scanResult, mAttributionSource);
-            if (VDBG) {
-                Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString());
-            }
-
-            // Check null in case the scan has been stopped
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    return;
-                }
-            }
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (onFound) {
-                        mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
-                                scanResult);
-                    } else {
-                        mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
-                                scanResult);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onScanManagerErrorCallback(final int errorCode) {
-            if (VDBG) {
-                Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
-            }
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    return;
-                }
-            }
-            postCallbackError(mScanCallback, errorCode);
-        }
-    }
-
-    private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) {
-        if (callback == null) {
-            return errorCode;
-        } else {
-            postCallbackError(callback, errorCode);
-            return ScanCallback.NO_ERROR;
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postCallbackError(final ScanCallback callback, final int errorCode) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onScanFailed(errorCode);
-            }
-        });
-    }
-
-    private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
-        if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
-            return true;
-        }
-        final int callbackType = settings.getCallbackType();
-        // Only support regular scan if no offloaded filter support.
-        if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
-                && settings.getReportDelayMillis() == 0) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isSettingsAndFilterComboAllowed(ScanSettings settings,
-            List<ScanFilter> filterList) {
-        final int callbackType = settings.getCallbackType();
-        // If onlost/onfound is requested, a non-empty filter is expected
-        if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
-                | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
-            if (filterList == null) {
-                return false;
-            }
-            for (ScanFilter filter : filterList) {
-                if (filter.isAllFieldsEmpty()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
-        final int callbackType = settings.getCallbackType();
-        if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
-                || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
-            // For onlost/onfound, we required hw support be available
-            return (mBluetoothAdapter.isOffloadedFilteringSupported()
-                    && mBluetoothAdapter.isHardwareTrackingFiltersAvailable());
-        }
-        return true;
-    }
-}
diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java
deleted file mode 100644
index ed50b09..0000000
--- a/core/java/android/bluetooth/le/BluetoothLeUtils.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.util.SparseArray;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * Helper class for Bluetooth LE utils.
- *
- * @hide
- */
-public class BluetoothLeUtils {
-
-    /**
-     * Returns a string composed from a {@link SparseArray}.
-     */
-    static String toString(SparseArray<byte[]> array) {
-        if (array == null) {
-            return "null";
-        }
-        if (array.size() == 0) {
-            return "{}";
-        }
-        StringBuilder buffer = new StringBuilder();
-        buffer.append('{');
-        for (int i = 0; i < array.size(); ++i) {
-            buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i)));
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-    /**
-     * Returns a string composed from a {@link Map}.
-     */
-    static <T> String toString(Map<T, byte[]> map) {
-        if (map == null) {
-            return "null";
-        }
-        if (map.isEmpty()) {
-            return "{}";
-        }
-        StringBuilder buffer = new StringBuilder();
-        buffer.append('{');
-        Iterator<Map.Entry<T, byte[]>> it = map.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry<T, byte[]> entry = it.next();
-            Object key = entry.getKey();
-            buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
-            if (it.hasNext()) {
-                buffer.append(", ");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-    /**
-     * Check whether two {@link SparseArray} equal.
-     */
-    static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) {
-        if (array == otherArray) {
-            return true;
-        }
-        if (array == null || otherArray == null) {
-            return false;
-        }
-        if (array.size() != otherArray.size()) {
-            return false;
-        }
-
-        // Keys are guaranteed in ascending order when indices are in ascending order.
-        for (int i = 0; i < array.size(); ++i) {
-            if (array.keyAt(i) != otherArray.keyAt(i)
-                    || !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Check whether two {@link Map} equal.
-     */
-    static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) {
-        if (map == otherMap) {
-            return true;
-        }
-        if (map == null || otherMap == null) {
-            return false;
-        }
-        if (map.size() != otherMap.size()) {
-            return false;
-        }
-        Set<T> keys = map.keySet();
-        if (!keys.equals(otherMap.keySet())) {
-            return false;
-        }
-        for (T key : keys) {
-            if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Ensure Bluetooth is turned on.
-     *
-     * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not {@link
-     * BluetoothAdapter#STATE_ON}.
-     */
-    static void checkAdapterStateOn(BluetoothAdapter adapter) {
-        if (adapter == null || !adapter.isLeEnabled()) {
-            throw new IllegalStateException("BT Adapter is not turned ON");
-        }
-    }
-
-    /**
-     * Compares two UUIDs with a UUID mask.
-     *
-     * @param data first {@link #UUID} to compare.
-     * @param uuid second {@link #UUID} to compare.
-     * @param mask mask {@link #UUID}.
-     * @return true if both UUIDs are equals when masked, false otherwise.
-     */
-    static boolean maskedEquals(UUID data, UUID uuid, UUID mask) {
-        if (mask == null) {
-            return Objects.equals(data, uuid);
-        }
-        return (data.getLeastSignificantBits() & mask.getLeastSignificantBits())
-                == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
-                && (data.getMostSignificantBits() & mask.getMostSignificantBits())
-                == (uuid.getMostSignificantBits() & mask.getMostSignificantBits());
-    }
-}
diff --git a/core/java/android/bluetooth/le/OWNERS b/core/java/android/bluetooth/le/OWNERS
deleted file mode 100644
index 3523ee0..0000000
--- a/core/java/android/bluetooth/le/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 27441
-
[email protected]
[email protected]
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
deleted file mode 100644
index 14ac911..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
+++ /dev/null
@@ -1,81 +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 android.bluetooth.le;
-
-import android.bluetooth.BluetoothDevice;
-
-/**
- * Bluetooth LE periodic advertising callbacks, used to deliver periodic
- * advertising operation status.
- *
- * @hide
- * @see PeriodicAdvertisingManager#createSync
- */
-public abstract class PeriodicAdvertisingCallback {
-
-    /**
-     * The requested operation was successful.
-     *
-     * @hide
-     */
-    public static final int SYNC_SUCCESS = 0;
-
-    /**
-     * Sync failed to be established because remote device did not respond.
-     */
-    public static final int SYNC_NO_RESPONSE = 1;
-
-    /**
-     * Sync failed to be established because controller can't support more syncs.
-     */
-    public static final int SYNC_NO_RESOURCES = 2;
-
-
-    /**
-     * Callback when synchronization was established.
-     *
-     * @param syncHandle handle used to identify this synchronization.
-     * @param device remote device.
-     * @param advertisingSid synchronized advertising set id.
-     * @param skip The number of periodic advertising packets that can be skipped after a successful
-     * receive in force. @see PeriodicAdvertisingManager#createSync
-     * @param timeout Synchronization timeout for the periodic advertising in force. One unit is
-     * 10ms. @see PeriodicAdvertisingManager#createSync
-     * @param timeout
-     * @param status operation status.
-     */
-    public void onSyncEstablished(int syncHandle, BluetoothDevice device,
-            int advertisingSid, int skip, int timeout,
-            int status) {
-    }
-
-    /**
-     * Callback when periodic advertising report is received.
-     *
-     * @param report periodic advertising report.
-     */
-    public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
-    }
-
-    /**
-     * Callback when periodic advertising synchronization was lost.
-     *
-     * @param syncHandle handle used to identify this synchronization.
-     */
-    public void onSyncLost(int syncHandle) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
deleted file mode 100644
index bbd3117..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ /dev/null
@@ -1,264 +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 android.bluetooth.le;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.Attributable;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class provides methods to perform periodic advertising related
- * operations. An application can register for periodic advertisements using
- * {@link PeriodicAdvertisingManager#registerSync}.
- * <p>
- * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an
- * instance of {@link PeriodicAdvertisingManager}.
- *
- * @hide
- */
-public final class PeriodicAdvertisingManager {
-
-    private static final String TAG = "PeriodicAdvertisingManager";
-
-    private static final int SKIP_MIN = 0;
-    private static final int SKIP_MAX = 499;
-    private static final int TIMEOUT_MIN = 10;
-    private static final int TIMEOUT_MAX = 16384;
-
-    private static final int SYNC_STARTING = -1;
-
-    private final BluetoothAdapter mBluetoothAdapter;
-    private final IBluetoothManager mBluetoothManager;
-    private final AttributionSource mAttributionSource;
-
-    /* maps callback, to callback wrapper and sync handle */
-    Map<PeriodicAdvertisingCallback,
-            IPeriodicAdvertisingCallback /* callbackWrapper */> mCallbackWrappers;
-
-    /**
-     * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
-     *
-     * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
-     * @hide
-     */
-    public PeriodicAdvertisingManager(BluetoothAdapter bluetoothAdapter) {
-        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
-        mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
-        mAttributionSource = mBluetoothAdapter.getAttributionSource();
-        mCallbackWrappers = new IdentityHashMap<>();
-    }
-
-    /**
-     * Synchronize with periodic advertising pointed to by the {@code scanResult}.
-     * The {@code scanResult} used must contain a valid advertisingSid. First
-     * call to registerSync will use the {@code skip} and {@code timeout} provided.
-     * Subsequent calls from other apps, trying to sync with same set will reuse
-     * existing sync, thus {@code skip} and {@code timeout} values will not take
-     * effect. The values in effect will be returned in
-     * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
-     *
-     * @param scanResult Scan result containing advertisingSid.
-     * @param skip The number of periodic advertising packets that can be skipped after a successful
-     * receive. Must be between 0 and 499.
-     * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must
-     * be between 10 (100ms) and 16384 (163.84s).
-     * @param callback Callback used to deliver all operations status.
-     * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
-     * {@code timeout} is invalid or {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void registerSync(ScanResult scanResult, int skip, int timeout,
-            PeriodicAdvertisingCallback callback) {
-        registerSync(scanResult, skip, timeout, callback, null);
-    }
-
-    /**
-     * Synchronize with periodic advertising pointed to by the {@code scanResult}.
-     * The {@code scanResult} used must contain a valid advertisingSid. First
-     * call to registerSync will use the {@code skip} and {@code timeout} provided.
-     * Subsequent calls from other apps, trying to sync with same set will reuse
-     * existing sync, thus {@code skip} and {@code timeout} values will not take
-     * effect. The values in effect will be returned in
-     * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
-     *
-     * @param scanResult Scan result containing advertisingSid.
-     * @param skip The number of periodic advertising packets that can be skipped after a successful
-     * receive. Must be between 0 and 499.
-     * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must
-     * be between 10 (100ms) and 16384 (163.84s).
-     * @param callback Callback used to deliver all operations status.
-     * @param handler thread upon which the callbacks will be invoked.
-     * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
-     * {@code timeout} is invalid or {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void registerSync(ScanResult scanResult, int skip, int timeout,
-            PeriodicAdvertisingCallback callback, Handler handler) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback can't be null");
-        }
-
-        if (scanResult == null) {
-            throw new IllegalArgumentException("scanResult can't be null");
-        }
-
-        if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) {
-            throw new IllegalArgumentException("scanResult must contain a valid sid");
-        }
-
-        if (skip < SKIP_MIN || skip > SKIP_MAX) {
-            throw new IllegalArgumentException(
-                    "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
-        }
-
-        if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) {
-            throw new IllegalArgumentException(
-                    "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-            callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(),
-                    skip, timeout,
-                    PeriodicAdvertisingCallback.SYNC_NO_RESOURCES);
-            return;
-        }
-
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-
-        IPeriodicAdvertisingCallback wrapped = wrap(callback, handler);
-        mCallbackWrappers.put(callback, wrapped);
-
-        try {
-            gatt.registerSync(
-                    scanResult, skip, timeout, wrapped, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to register sync - ", e);
-            return;
-        }
-    }
-
-    /**
-     * Cancel pending attempt to create sync, or terminate existing sync.
-     *
-     * @param callback Callback used to deliver all operations status.
-     * @throws IllegalArgumentException if {@code callback} is null, or not a properly registered
-     * callback.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void unregisterSync(PeriodicAdvertisingCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback can't be null");
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-            return;
-        }
-
-        IPeriodicAdvertisingCallback wrapper = mCallbackWrappers.remove(callback);
-        if (wrapper == null) {
-            throw new IllegalArgumentException("callback was not properly registered");
-        }
-
-        try {
-            gatt.unregisterSync(wrapper, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to cancel sync creation - ", e);
-            return;
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback,
-            Handler handler) {
-        return new IPeriodicAdvertisingCallback.Stub() {
-            public void onSyncEstablished(int syncHandle, BluetoothDevice device,
-                    int advertisingSid, int skip, int timeout, int status) {
-                Attributable.setAttributionSource(device, mAttributionSource);
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onSyncEstablished(syncHandle, device, advertisingSid, skip,
-                                timeout,
-                                status);
-
-                        if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) {
-                            // App can still unregister the sync until notified it failed. Remove
-                            // callback
-                            // after app was notifed.
-                            mCallbackWrappers.remove(callback);
-                        }
-                    }
-                });
-            }
-
-            public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onPeriodicAdvertisingReport(report);
-                    }
-                });
-            }
-
-            public void onSyncLost(int syncHandle) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onSyncLost(syncHandle);
-                        // App can still unregister the sync until notified it's lost.
-                        // Remove callback after app was notifed.
-                        mCallbackWrappers.remove(callback);
-                    }
-                });
-            }
-        };
-    }
-}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
deleted file mode 100644
index 4e64dbe..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
+++ /dev/null
@@ -1,121 +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 android.bluetooth.le;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic
- * advertising preferences for each Bluetooth LE advertising set. Use {@link
- * PeriodicAdvertisingParameters.Builder} to create an instance of this class.
- */
-public final class PeriodicAdvertisingParameters implements Parcelable {
-
-    private static final int INTERVAL_MIN = 80;
-    private static final int INTERVAL_MAX = 65519;
-
-    private final boolean mIncludeTxPower;
-    private final int mInterval;
-
-    private PeriodicAdvertisingParameters(boolean includeTxPower, int interval) {
-        mIncludeTxPower = includeTxPower;
-        mInterval = interval;
-    }
-
-    private PeriodicAdvertisingParameters(Parcel in) {
-        mIncludeTxPower = in.readInt() != 0;
-        mInterval = in.readInt();
-    }
-
-    /**
-     * Returns whether the TX Power will be included.
-     */
-    public boolean getIncludeTxPower() {
-        return mIncludeTxPower;
-    }
-
-    /**
-     * Returns the periodic advertising interval, in 1.25ms unit.
-     * Valid values are from 80 (100ms) to 65519 (81.89875s).
-     */
-    public int getInterval() {
-        return mInterval;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mIncludeTxPower ? 1 : 0);
-        dest.writeInt(mInterval);
-    }
-
-    public static final Parcelable
-            .Creator<PeriodicAdvertisingParameters> CREATOR =
-            new Creator<PeriodicAdvertisingParameters>() {
-                @Override
-                public PeriodicAdvertisingParameters[] newArray(int size) {
-                    return new PeriodicAdvertisingParameters[size];
-                }
-
-                @Override
-                public PeriodicAdvertisingParameters createFromParcel(Parcel in) {
-                    return new PeriodicAdvertisingParameters(in);
-                }
-            };
-
-    public static final class Builder {
-        private boolean mIncludeTxPower = false;
-        private int mInterval = INTERVAL_MAX;
-
-        /**
-         * Whether the transmission power level should be included in the periodic
-         * packet.
-         */
-        public Builder setIncludeTxPower(boolean includeTxPower) {
-            mIncludeTxPower = includeTxPower;
-            return this;
-        }
-
-        /**
-         * Set advertising interval for periodic advertising, in 1.25ms unit.
-         * Valid values are from 80 (100ms) to 65519 (81.89875s).
-         * Value from range [interval, interval+20ms] will be picked as the actual value.
-         *
-         * @throws IllegalArgumentException If the interval is invalid.
-         */
-        public Builder setInterval(int interval) {
-            if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
-                throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN
-                        + "-" + INTERVAL_MAX + ")");
-            }
-            mInterval = interval;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertisingSetParameters} object.
-         */
-        public PeriodicAdvertisingParameters build() {
-            return new PeriodicAdvertisingParameters(mIncludeTxPower, mInterval);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
deleted file mode 100644
index 54b953c..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
+++ /dev/null
@@ -1,186 +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 android.bluetooth.le;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising.
- *
- * @hide
- */
-public final class PeriodicAdvertisingReport implements Parcelable {
-
-    /**
-     * The data returned is complete
-     */
-    public static final int DATA_COMPLETE = 0;
-
-    /**
-     * The data returned is incomplete. The controller was unsuccessfull to
-     * receive all chained packets, returning only partial data.
-     */
-    public static final int DATA_INCOMPLETE_TRUNCATED = 2;
-
-    private int mSyncHandle;
-    private int mTxPower;
-    private int mRssi;
-    private int mDataStatus;
-
-    // periodic advertising data.
-    @Nullable
-    private ScanRecord mData;
-
-    // Device timestamp when the result was last seen.
-    private long mTimestampNanos;
-
-    /**
-     * Constructor of periodic advertising result.
-     */
-    public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi,
-            int dataStatus, ScanRecord data) {
-        mSyncHandle = syncHandle;
-        mTxPower = txPower;
-        mRssi = rssi;
-        mDataStatus = dataStatus;
-        mData = data;
-    }
-
-    private PeriodicAdvertisingReport(Parcel in) {
-        readFromParcel(in);
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSyncHandle);
-        dest.writeInt(mTxPower);
-        dest.writeInt(mRssi);
-        dest.writeInt(mDataStatus);
-        if (mData != null) {
-            dest.writeInt(1);
-            dest.writeByteArray(mData.getBytes());
-        } else {
-            dest.writeInt(0);
-        }
-    }
-
-    private void readFromParcel(Parcel in) {
-        mSyncHandle = in.readInt();
-        mTxPower = in.readInt();
-        mRssi = in.readInt();
-        mDataStatus = in.readInt();
-        if (in.readInt() == 1) {
-            mData = ScanRecord.parseFromBytes(in.createByteArray());
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Returns the synchronization handle.
-     */
-    public int getSyncHandle() {
-        return mSyncHandle;
-    }
-
-    /**
-     * Returns the transmit power in dBm. The valid range is [-127, 126]. Value
-     * of 127 means information was not available.
-     */
-    public int getTxPower() {
-        return mTxPower;
-    }
-
-    /**
-     * Returns the received signal strength in dBm. The valid range is [-127, 20].
-     */
-    public int getRssi() {
-        return mRssi;
-    }
-
-    /**
-     * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE}
-     * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}.
-     */
-    public int getDataStatus() {
-        return mDataStatus;
-    }
-
-    /**
-     * Returns the data contained in this periodic advertising report.
-     */
-    @Nullable
-    public ScanRecord getData() {
-        return mData;
-    }
-
-    /**
-     * Returns timestamp since boot when the scan record was observed.
-     */
-    public long getTimestampNanos() {
-        return mTimestampNanos;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mSyncHandle, mTxPower, mRssi, mDataStatus, mData, mTimestampNanos);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj;
-        return (mSyncHandle == other.mSyncHandle)
-                && (mTxPower == other.mTxPower)
-                && (mRssi == other.mRssi)
-                && (mDataStatus == other.mDataStatus)
-                && Objects.equals(mData, other.mData)
-                && (mTimestampNanos == other.mTimestampNanos);
-    }
-
-    @Override
-    public String toString() {
-        return "PeriodicAdvertisingReport{syncHandle=" + mSyncHandle
-                + ", txPower=" + mTxPower + ", rssi=" + mRssi + ", dataStatus=" + mDataStatus
-                + ", data=" + Objects.toString(mData) + ", timestampNanos=" + mTimestampNanos + '}';
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<PeriodicAdvertisingReport> CREATOR =
-            new Creator<PeriodicAdvertisingReport>() {
-                @Override
-                public PeriodicAdvertisingReport createFromParcel(Parcel source) {
-                    return new PeriodicAdvertisingReport(source);
-                }
-
-                @Override
-                public PeriodicAdvertisingReport[] newArray(int size) {
-                    return new PeriodicAdvertisingReport[size];
-                }
-            };
-}
diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
deleted file mode 100644
index f650489..0000000
--- a/core/java/android/bluetooth/le/ResultStorageDescriptor.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Describes the way to store scan result.
- *
- * @deprecated this is not used anywhere
- *
- * @hide
- */
-@Deprecated
-@SystemApi
-public final class ResultStorageDescriptor implements Parcelable {
-    private int mType;
-    private int mOffset;
-    private int mLength;
-
-    public int getType() {
-        return mType;
-    }
-
-    public int getOffset() {
-        return mOffset;
-    }
-
-    public int getLength() {
-        return mLength;
-    }
-
-    /**
-     * Constructor of {@link ResultStorageDescriptor}
-     *
-     * @param type Type of the data.
-     * @param offset Offset from start of the advertise packet payload.
-     * @param length Byte length of the data
-     */
-    public ResultStorageDescriptor(int type, int offset, int length) {
-        mType = type;
-        mOffset = offset;
-        mLength = length;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mType);
-        dest.writeInt(mOffset);
-        dest.writeInt(mLength);
-    }
-
-    private ResultStorageDescriptor(Parcel in) {
-        ReadFromParcel(in);
-    }
-
-    private void ReadFromParcel(Parcel in) {
-        mType = in.readInt();
-        mOffset = in.readInt();
-        mLength = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ResultStorageDescriptor> CREATOR =
-            new Creator<ResultStorageDescriptor>() {
-        @Override
-        public ResultStorageDescriptor createFromParcel(Parcel source) {
-            return new ResultStorageDescriptor(source);
-        }
-
-        @Override
-        public ResultStorageDescriptor[] newArray(int size) {
-            return new ResultStorageDescriptor[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
deleted file mode 100644
index 53d9310..0000000
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import java.util.List;
-
-/**
- * Bluetooth LE scan callbacks. Scan results are reported using these callbacks.
- *
- * @see BluetoothLeScanner#startScan
- */
-public abstract class ScanCallback {
-    /**
-     * Fails to start scan as BLE scan with the same settings is already started by the app.
-     */
-    public static final int SCAN_FAILED_ALREADY_STARTED = 1;
-
-    /**
-     * Fails to start scan as app cannot be registered.
-     */
-    public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2;
-
-    /**
-     * Fails to start scan due an internal error
-     */
-    public static final int SCAN_FAILED_INTERNAL_ERROR = 3;
-
-    /**
-     * Fails to start power optimized scan as this feature is not supported.
-     */
-    public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4;
-
-    /**
-     * Fails to start scan as it is out of hardware resources.
-     *
-     * @hide
-     */
-    public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
-
-    /**
-     * Fails to start scan as application tries to scan too frequently.
-     * @hide
-     */
-    public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6;
-
-    static final int NO_ERROR = 0;
-
-    /**
-     * Callback when a BLE advertisement has been found.
-     *
-     * @param callbackType Determines how this callback was triggered. Could be one of {@link
-     * ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
-     * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST}
-     * @param result A Bluetooth LE scan result.
-     */
-    public void onScanResult(int callbackType, ScanResult result) {
-    }
-
-    /**
-     * Callback when batch results are delivered.
-     *
-     * @param results List of scan results that are previously scanned.
-     */
-    public void onBatchScanResults(List<ScanResult> results) {
-    }
-
-    /**
-     * Callback when scan could not be started.
-     *
-     * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure.
-     */
-    public void onScanFailed(int errorCode) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
deleted file mode 100644
index 675fe05..0000000
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothDevice.AddressType;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to
- * restrict scan results to only those that are of interest to them.
- * <p>
- * Current filtering on the following fields are supported:
- * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
- * <li>Name of remote Bluetooth LE device.
- * <li>Mac address of the remote device.
- * <li>Service data which is the data associated with a service.
- * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
- *
- * @see ScanResult
- * @see BluetoothLeScanner
- */
-public final class ScanFilter implements Parcelable {
-
-    @Nullable
-    private final String mDeviceName;
-
-    @Nullable
-    private final String mDeviceAddress;
-
-    private final @AddressType int mAddressType;
-
-    @Nullable
-    private final byte[] mIrk;
-
-    @Nullable
-    private final ParcelUuid mServiceUuid;
-    @Nullable
-    private final ParcelUuid mServiceUuidMask;
-
-    @Nullable
-    private final ParcelUuid mServiceSolicitationUuid;
-    @Nullable
-    private final ParcelUuid mServiceSolicitationUuidMask;
-
-    @Nullable
-    private final ParcelUuid mServiceDataUuid;
-    @Nullable
-    private final byte[] mServiceData;
-    @Nullable
-    private final byte[] mServiceDataMask;
-
-    private final int mManufacturerId;
-    @Nullable
-    private final byte[] mManufacturerData;
-    @Nullable
-    private final byte[] mManufacturerDataMask;
-
-    /** @hide */
-    public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
-
-    private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
-            ParcelUuid uuidMask, ParcelUuid solicitationUuid,
-            ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
-            byte[] serviceData, byte[] serviceDataMask,
-            int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
-            @AddressType int addressType, @Nullable byte[] irk) {
-        mDeviceName = name;
-        mServiceUuid = uuid;
-        mServiceUuidMask = uuidMask;
-        mServiceSolicitationUuid = solicitationUuid;
-        mServiceSolicitationUuidMask = solicitationUuidMask;
-        mDeviceAddress = deviceAddress;
-        mServiceDataUuid = serviceDataUuid;
-        mServiceData = serviceData;
-        mServiceDataMask = serviceDataMask;
-        mManufacturerId = manufacturerId;
-        mManufacturerData = manufacturerData;
-        mManufacturerDataMask = manufacturerDataMask;
-        mAddressType = addressType;
-        mIrk = irk;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mDeviceName == null ? 0 : 1);
-        if (mDeviceName != null) {
-            dest.writeString(mDeviceName);
-        }
-        dest.writeInt(mDeviceAddress == null ? 0 : 1);
-        if (mDeviceAddress != null) {
-            dest.writeString(mDeviceAddress);
-        }
-        dest.writeInt(mServiceUuid == null ? 0 : 1);
-        if (mServiceUuid != null) {
-            dest.writeParcelable(mServiceUuid, flags);
-            dest.writeInt(mServiceUuidMask == null ? 0 : 1);
-            if (mServiceUuidMask != null) {
-                dest.writeParcelable(mServiceUuidMask, flags);
-            }
-        }
-        dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1);
-        if (mServiceSolicitationUuid != null) {
-            dest.writeParcelable(mServiceSolicitationUuid, flags);
-            dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1);
-            if (mServiceSolicitationUuidMask != null) {
-                dest.writeParcelable(mServiceSolicitationUuidMask, flags);
-            }
-        }
-        dest.writeInt(mServiceDataUuid == null ? 0 : 1);
-        if (mServiceDataUuid != null) {
-            dest.writeParcelable(mServiceDataUuid, flags);
-            dest.writeInt(mServiceData == null ? 0 : 1);
-            if (mServiceData != null) {
-                dest.writeInt(mServiceData.length);
-                dest.writeByteArray(mServiceData);
-
-                dest.writeInt(mServiceDataMask == null ? 0 : 1);
-                if (mServiceDataMask != null) {
-                    dest.writeInt(mServiceDataMask.length);
-                    dest.writeByteArray(mServiceDataMask);
-                }
-            }
-        }
-        dest.writeInt(mManufacturerId);
-        dest.writeInt(mManufacturerData == null ? 0 : 1);
-        if (mManufacturerData != null) {
-            dest.writeInt(mManufacturerData.length);
-            dest.writeByteArray(mManufacturerData);
-
-            dest.writeInt(mManufacturerDataMask == null ? 0 : 1);
-            if (mManufacturerDataMask != null) {
-                dest.writeInt(mManufacturerDataMask.length);
-                dest.writeByteArray(mManufacturerDataMask);
-            }
-        }
-
-        // IRK
-        if (mDeviceAddress != null) {
-            dest.writeInt(mAddressType);
-            dest.writeInt(mIrk == null ? 0 : 1);
-            if (mIrk != null) {
-                dest.writeByteArray(mIrk);
-            }
-        }
-    }
-
-    /**
-     * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel.
-     */
-    public static final @android.annotation.NonNull Creator<ScanFilter> CREATOR =
-            new Creator<ScanFilter>() {
-
-        @Override
-        public ScanFilter[] newArray(int size) {
-            return new ScanFilter[size];
-        }
-
-        @Override
-        public ScanFilter createFromParcel(Parcel in) {
-            Builder builder = new Builder();
-            if (in.readInt() == 1) {
-                builder.setDeviceName(in.readString());
-            }
-            String address = null;
-            // If we have a non-null address
-            if (in.readInt() == 1) {
-                address = in.readString();
-            }
-            if (in.readInt() == 1) {
-                ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
-                builder.setServiceUuid(uuid);
-                if (in.readInt() == 1) {
-                    ParcelUuid uuidMask = in.readParcelable(
-                            ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
-                    builder.setServiceUuid(uuid, uuidMask);
-                }
-            }
-            if (in.readInt() == 1) {
-                ParcelUuid solicitationUuid = in.readParcelable(
-                        ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
-                builder.setServiceSolicitationUuid(solicitationUuid);
-                if (in.readInt() == 1) {
-                    ParcelUuid solicitationUuidMask = in.readParcelable(
-                            ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
-                    builder.setServiceSolicitationUuid(solicitationUuid,
-                            solicitationUuidMask);
-                }
-            }
-            if (in.readInt() == 1) {
-                ParcelUuid servcieDataUuid =
-                        in.readParcelable(ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
-                if (in.readInt() == 1) {
-                    int serviceDataLength = in.readInt();
-                    byte[] serviceData = new byte[serviceDataLength];
-                    in.readByteArray(serviceData);
-                    if (in.readInt() == 0) {
-                        builder.setServiceData(servcieDataUuid, serviceData);
-                    } else {
-                        int serviceDataMaskLength = in.readInt();
-                        byte[] serviceDataMask = new byte[serviceDataMaskLength];
-                        in.readByteArray(serviceDataMask);
-                        builder.setServiceData(
-                                servcieDataUuid, serviceData, serviceDataMask);
-                    }
-                }
-            }
-
-            int manufacturerId = in.readInt();
-            if (in.readInt() == 1) {
-                int manufacturerDataLength = in.readInt();
-                byte[] manufacturerData = new byte[manufacturerDataLength];
-                in.readByteArray(manufacturerData);
-                if (in.readInt() == 0) {
-                    builder.setManufacturerData(manufacturerId, manufacturerData);
-                } else {
-                    int manufacturerDataMaskLength = in.readInt();
-                    byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
-                    in.readByteArray(manufacturerDataMask);
-                    builder.setManufacturerData(manufacturerId, manufacturerData,
-                            manufacturerDataMask);
-                }
-            }
-
-            // IRK
-            if (address != null) {
-                final int addressType = in.readInt();
-                if (in.readInt() == 1) {
-                    final byte[] irk = new byte[16];
-                    in.readByteArray(irk);
-                    builder.setDeviceAddress(address, addressType, irk);
-                } else {
-                    builder.setDeviceAddress(address, addressType);
-                }
-            }
-            return builder.build();
-        }
-    };
-
-    /**
-     * Returns the filter set the device name field of Bluetooth advertisement data.
-     */
-    @Nullable
-    public String getDeviceName() {
-        return mDeviceName;
-    }
-
-    /**
-     * Returns the filter set on the service uuid.
-     */
-    @Nullable
-    public ParcelUuid getServiceUuid() {
-        return mServiceUuid;
-    }
-
-    @Nullable
-    public ParcelUuid getServiceUuidMask() {
-        return mServiceUuidMask;
-    }
-
-    /**
-     * Returns the filter set on the service Solicitation uuid.
-     */
-    @Nullable
-    public ParcelUuid getServiceSolicitationUuid() {
-        return mServiceSolicitationUuid;
-    }
-
-    /**
-     * Returns the filter set on the service Solicitation uuid mask.
-     */
-    @Nullable
-    public ParcelUuid getServiceSolicitationUuidMask() {
-        return mServiceSolicitationUuidMask;
-    }
-
-    @Nullable
-    public String getDeviceAddress() {
-        return mDeviceAddress;
-    }
-
-    /**
-     * @hide
-     */
-    @SystemApi
-    public @AddressType int getAddressType() {
-        return mAddressType;
-    }
-
-    /**
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    public byte[] getIrk() {
-        return mIrk;
-    }
-
-    @Nullable
-    public byte[] getServiceData() {
-        return mServiceData;
-    }
-
-    @Nullable
-    public byte[] getServiceDataMask() {
-        return mServiceDataMask;
-    }
-
-    @Nullable
-    public ParcelUuid getServiceDataUuid() {
-        return mServiceDataUuid;
-    }
-
-    /**
-     * Returns the manufacturer id. -1 if the manufacturer filter is not set.
-     */
-    public int getManufacturerId() {
-        return mManufacturerId;
-    }
-
-    @Nullable
-    public byte[] getManufacturerData() {
-        return mManufacturerData;
-    }
-
-    @Nullable
-    public byte[] getManufacturerDataMask() {
-        return mManufacturerDataMask;
-    }
-
-    /**
-     * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
-     * if it matches all the field filters.
-     */
-    public boolean matches(ScanResult scanResult) {
-        if (scanResult == null) {
-            return false;
-        }
-        BluetoothDevice device = scanResult.getDevice();
-        // Device match.
-        if (mDeviceAddress != null
-                && (device == null || !mDeviceAddress.equals(device.getAddress()))) {
-            return false;
-        }
-
-        ScanRecord scanRecord = scanResult.getScanRecord();
-
-        // Scan record is null but there exist filters on it.
-        if (scanRecord == null
-                && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null
-                || mServiceData != null || mServiceSolicitationUuid != null)) {
-            return false;
-        }
-
-        // Local name match.
-        if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) {
-            return false;
-        }
-
-        // UUID match.
-        if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask,
-                scanRecord.getServiceUuids())) {
-            return false;
-        }
-
-        // solicitation UUID match.
-        if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids(
-                mServiceSolicitationUuid, mServiceSolicitationUuidMask,
-                scanRecord.getServiceSolicitationUuids())) {
-            return false;
-        }
-
-        // Service data match
-        if (mServiceDataUuid != null) {
-            if (!matchesPartialData(mServiceData, mServiceDataMask,
-                    scanRecord.getServiceData(mServiceDataUuid))) {
-                return false;
-            }
-        }
-
-        // Manufacturer data match.
-        if (mManufacturerId >= 0) {
-            if (!matchesPartialData(mManufacturerData, mManufacturerDataMask,
-                    scanRecord.getManufacturerSpecificData(mManufacturerId))) {
-                return false;
-            }
-        }
-        // All filters match.
-        return true;
-    }
-
-    /**
-     * Check if the uuid pattern is contained in a list of parcel uuids.
-     *
-     * @hide
-     */
-    public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask,
-            List<ParcelUuid> uuids) {
-        if (uuid == null) {
-            return true;
-        }
-        if (uuids == null) {
-            return false;
-        }
-
-        for (ParcelUuid parcelUuid : uuids) {
-            UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
-            if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Check if the uuid pattern matches the particular service uuid.
-    private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
-        return BluetoothLeUtils.maskedEquals(data, uuid, mask);
-    }
-
-    /**
-     * Check if the solicitation uuid pattern is contained in a list of parcel uuids.
-     *
-     */
-    private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid,
-            ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids) {
-        if (solicitationUuid == null) {
-            return true;
-        }
-        if (solicitationUuids == null) {
-            return false;
-        }
-
-        for (ParcelUuid parcelSolicitationUuid : solicitationUuids) {
-            UUID solicitationUuidMask = parcelSolicitationUuidMask == null
-                    ? null : parcelSolicitationUuidMask.getUuid();
-            if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask,
-                    parcelSolicitationUuid.getUuid())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Check if the solicitation uuid pattern matches the particular service solicitation uuid.
-    private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
-            UUID solicitationUuidMask, UUID data) {
-        return BluetoothLeUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
-    }
-
-    // Check whether the data pattern matches the parsed data.
-    private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
-        if (parsedData == null || parsedData.length < data.length) {
-            return false;
-        }
-        if (dataMask == null) {
-            for (int i = 0; i < data.length; ++i) {
-                if (parsedData[i] != data[i]) {
-                    return false;
-                }
-            }
-            return true;
-        }
-        for (int i = 0; i < data.length; ++i) {
-            if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress="
-                + mDeviceAddress
-                + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
-                + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid
-                + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask
-                + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData="
-                + Arrays.toString(mServiceData) + ", mServiceDataMask="
-                + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
-                + ", mManufacturerData=" + Arrays.toString(mManufacturerData)
-                + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]";
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId,
-                Arrays.hashCode(mManufacturerData),
-                Arrays.hashCode(mManufacturerDataMask),
-                mServiceDataUuid,
-                Arrays.hashCode(mServiceData),
-                Arrays.hashCode(mServiceDataMask),
-                mServiceUuid, mServiceUuidMask,
-                mServiceSolicitationUuid, mServiceSolicitationUuidMask);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        ScanFilter other = (ScanFilter) obj;
-        return Objects.equals(mDeviceName, other.mDeviceName)
-                && Objects.equals(mDeviceAddress, other.mDeviceAddress)
-                && mManufacturerId == other.mManufacturerId
-                && Objects.deepEquals(mManufacturerData, other.mManufacturerData)
-                && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask)
-                && Objects.equals(mServiceDataUuid, other.mServiceDataUuid)
-                && Objects.deepEquals(mServiceData, other.mServiceData)
-                && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask)
-                && Objects.equals(mServiceUuid, other.mServiceUuid)
-                && Objects.equals(mServiceUuidMask, other.mServiceUuidMask)
-                && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid)
-                && Objects.equals(mServiceSolicitationUuidMask,
-                        other.mServiceSolicitationUuidMask);
-    }
-
-    /**
-     * Checks if the scanfilter is empty
-     *
-     * @hide
-     */
-    public boolean isAllFieldsEmpty() {
-        return EMPTY.equals(this);
-    }
-
-    /**
-     * Builder class for {@link ScanFilter}.
-     */
-    public static final class Builder {
-
-        /**
-         * @hide
-         */
-        @SystemApi
-        public static final int LEN_IRK_OCTETS = 16;
-
-        private String mDeviceName;
-        private String mDeviceAddress;
-        private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC;
-        private byte[] mIrk;
-
-        private ParcelUuid mServiceUuid;
-        private ParcelUuid mUuidMask;
-
-        private ParcelUuid mServiceSolicitationUuid;
-        private ParcelUuid mServiceSolicitationUuidMask;
-
-        private ParcelUuid mServiceDataUuid;
-        private byte[] mServiceData;
-        private byte[] mServiceDataMask;
-
-        private int mManufacturerId = -1;
-        private byte[] mManufacturerData;
-        private byte[] mManufacturerDataMask;
-
-        /**
-         * Set filter on device name.
-         */
-        public Builder setDeviceName(String deviceName) {
-            mDeviceName = deviceName;
-            return this;
-        }
-
-        /**
-         * Set filter on device address.
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.  The @AddressType is defaulted to {@link
-         * BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         */
-        public Builder setDeviceAddress(String deviceAddress) {
-            if (deviceAddress == null) {
-                mDeviceAddress = deviceAddress;
-                return this;
-            }
-            return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
-        }
-
-        /**
-         * Set filter on Address with AddressType
-         *
-         * <p>This key is used to resolve a private address from a public address.
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}. May be any type of address.
-         * @param addressType indication of the type of address
-         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
-         *
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         * @throws IllegalArgumentException If the {@code addressType} is invalid length
-         * @throws NullPointerException if {@code deviceAddress} is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public Builder setDeviceAddress(@NonNull String deviceAddress,
-                                        @AddressType int addressType) {
-            return setDeviceAddressInternal(deviceAddress, addressType, null);
-        }
-
-        /**
-         * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
-         *
-         * <p>The IRK is used to resolve a {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} from
-         * a PRIVATE_ADDRESS type.
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.  This Address type must only be PUBLIC OR RANDOM
-         * STATIC.
-         * @param addressType indication of the type of address
-         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
-         * @param irk non-null byte array representing the Identity Resolving Key
-         *
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         * @throws IllegalArgumentException if the {@code irk} is invalid length.
-         * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not
-         * PUBLIC or RANDOM STATIC when an IRK is present.
-         * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public Builder setDeviceAddress(@NonNull String deviceAddress,
-                                        @AddressType int addressType,
-                                        @NonNull byte[] irk) {
-            requireNonNull(irk);
-            if (irk.length != LEN_IRK_OCTETS) {
-                throw new IllegalArgumentException("'irk' is invalid length!");
-            }
-            return setDeviceAddressInternal(deviceAddress, addressType, irk);
-        }
-
-        /**
-         * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
-         *
-         * <p>Internal setter for the device address
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.
-         * @param addressType indication of the type of address
-         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * @param irk non-null byte array representing the Identity Resolving Address; nullable
-         * internally.
-         *
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         * @throws IllegalArgumentException If the {@code addressType} is invalid length.
-         * @throws NullPointerException if {@code deviceAddress} is null.
-         *
-         * @hide
-         */
-        @NonNull
-        private Builder setDeviceAddressInternal(@NonNull String deviceAddress,
-                                                 @AddressType int addressType,
-                                                 @Nullable byte[] irk) {
-
-            // Make sure our deviceAddress is valid!
-            requireNonNull(deviceAddress);
-            if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
-                throw new IllegalArgumentException("invalid device address " + deviceAddress);
-            }
-
-            // Verify type range
-            if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC
-                || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) {
-                throw new IllegalArgumentException("'addressType' is invalid!");
-            }
-
-            // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address.
-            if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) {
-                // Don't want a bad combination of address and irk!
-                if (irk != null) {
-                    // Since there are 3 possible RANDOM subtypes we must check to make sure
-                    // the correct type of address is used.
-                    if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) {
-                        throw new IllegalArgumentException(
-                                "Invalid combination: IRK requires either a PUBLIC or "
-                                + "RANDOM (STATIC) Address");
-                    }
-                }
-            }
-
-            // PUBLIC doesn't require extra work
-            // Without an IRK any address may be accepted
-
-            mDeviceAddress = deviceAddress;
-            mAddressType = addressType;
-            mIrk = irk;
-            return this;
-        }
-
-        /**
-         * Set filter on service uuid.
-         */
-        public Builder setServiceUuid(ParcelUuid serviceUuid) {
-            mServiceUuid = serviceUuid;
-            mUuidMask = null; // clear uuid mask
-            return this;
-        }
-
-        /**
-         * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
-         * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
-         * bit in {@code serviceUuid}, and 0 to ignore that bit.
-         *
-         * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code
-         * uuidMask} is not {@code null}.
-         */
-        public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) {
-            if (mUuidMask != null && mServiceUuid == null) {
-                throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
-            }
-            mServiceUuid = serviceUuid;
-            mUuidMask = uuidMask;
-            return this;
-        }
-
-
-        /**
-         * Set filter on service solicitation uuid.
-         */
-        public @NonNull Builder setServiceSolicitationUuid(
-                @Nullable ParcelUuid serviceSolicitationUuid) {
-            mServiceSolicitationUuid = serviceSolicitationUuid;
-            if (serviceSolicitationUuid == null) {
-                mServiceSolicitationUuidMask = null;
-            }
-            return this;
-        }
-
-
-        /**
-         * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the
-         * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to
-         * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to
-         * ignore that bit.
-         *
-         * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null.
-         * @param solicitationUuidMask can be null or a mask with no restriction.
-         *
-         * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
-         *             {@code serviceSolicitationUuidMask} is not {@code null}.
-         */
-        public @NonNull Builder setServiceSolicitationUuid(
-                @Nullable ParcelUuid serviceSolicitationUuid,
-                @Nullable ParcelUuid solicitationUuidMask) {
-            if (solicitationUuidMask != null && serviceSolicitationUuid == null) {
-                throw new IllegalArgumentException(
-                        "SolicitationUuid is null while SolicitationUuidMask is not null!");
-            }
-            mServiceSolicitationUuid = serviceSolicitationUuid;
-            mServiceSolicitationUuidMask = solicitationUuidMask;
-            return this;
-        }
-
-        /**
-         * Set filtering on service data.
-         *
-         * @throws IllegalArgumentException If {@code serviceDataUuid} is null.
-         */
-        public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
-            if (serviceDataUuid == null) {
-                throw new IllegalArgumentException("serviceDataUuid is null");
-            }
-            mServiceDataUuid = serviceDataUuid;
-            mServiceData = serviceData;
-            mServiceDataMask = null; // clear service data mask
-            return this;
-        }
-
-        /**
-         * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
-         * match the one in service data, otherwise set it to 0 to ignore that bit.
-         * <p>
-         * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
-         *
-         * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code
-         * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code
-         * serviceDataMask} and {@code serviceData} has different length.
-         */
-        public Builder setServiceData(ParcelUuid serviceDataUuid,
-                byte[] serviceData, byte[] serviceDataMask) {
-            if (serviceDataUuid == null) {
-                throw new IllegalArgumentException("serviceDataUuid is null");
-            }
-            if (mServiceDataMask != null) {
-                if (mServiceData == null) {
-                    throw new IllegalArgumentException(
-                            "serviceData is null while serviceDataMask is not null");
-                }
-                // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
-                // byte array need to be the same.
-                if (mServiceData.length != mServiceDataMask.length) {
-                    throw new IllegalArgumentException(
-                            "size mismatch for service data and service data mask");
-                }
-            }
-            mServiceDataUuid = serviceDataUuid;
-            mServiceData = serviceData;
-            mServiceDataMask = serviceDataMask;
-            return this;
-        }
-
-        /**
-         * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
-         *
-         * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
-         */
-        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) {
-            if (manufacturerData != null && manufacturerId < 0) {
-                throw new IllegalArgumentException("invalid manufacture id");
-            }
-            mManufacturerId = manufacturerId;
-            mManufacturerData = manufacturerData;
-            mManufacturerDataMask = null; // clear manufacturer data mask
-            return this;
-        }
-
-        /**
-         * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs
-         * to match the one in manufacturer data, otherwise set it to 0.
-         * <p>
-         * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
-         *
-         * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code
-         * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code
-         * manufacturerData} and {@code manufacturerDataMask} have different length.
-         */
-        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData,
-                byte[] manufacturerDataMask) {
-            if (manufacturerData != null && manufacturerId < 0) {
-                throw new IllegalArgumentException("invalid manufacture id");
-            }
-            if (mManufacturerDataMask != null) {
-                if (mManufacturerData == null) {
-                    throw new IllegalArgumentException(
-                            "manufacturerData is null while manufacturerDataMask is not null");
-                }
-                // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
-                // of the two byte array need to be the same.
-                if (mManufacturerData.length != mManufacturerDataMask.length) {
-                    throw new IllegalArgumentException(
-                            "size mismatch for manufacturerData and manufacturerDataMask");
-                }
-            }
-            mManufacturerId = manufacturerId;
-            mManufacturerData = manufacturerData;
-            mManufacturerDataMask = manufacturerDataMask;
-            return this;
-        }
-
-        /**
-         * Build {@link ScanFilter}.
-         *
-         * @throws IllegalArgumentException If the filter cannot be built.
-         */
-        public ScanFilter build() {
-            return new ScanFilter(mDeviceName, mDeviceAddress,
-                    mServiceUuid, mUuidMask, mServiceSolicitationUuid,
-                    mServiceSolicitationUuidMask,
-                    mServiceDataUuid, mServiceData, mServiceDataMask,
-                    mManufacturerId, mManufacturerData, mManufacturerDataMask,
-                    mAddressType, mIrk);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
deleted file mode 100644
index 9b8c2ea..0000000
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.bluetooth.BluetoothUuid;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.ParcelUuid;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-
-/**
- * Represents a scan record from Bluetooth LE scan.
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class ScanRecord {
-
-    private static final String TAG = "ScanRecord";
-
-    // The following data type values are assigned by Bluetooth SIG.
-    // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
-    private static final int DATA_TYPE_FLAGS = 0x01;
-    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
-    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
-    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
-    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
-    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
-    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
-    private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
-    private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
-    private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
-    private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
-    private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
-    private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
-    private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14;
-    private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F;
-    private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15;
-    private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
-
-    // Flags of the advertising data.
-    private final int mAdvertiseFlags;
-
-    @Nullable
-    private final List<ParcelUuid> mServiceUuids;
-    @Nullable
-    private final List<ParcelUuid> mServiceSolicitationUuids;
-
-    private final SparseArray<byte[]> mManufacturerSpecificData;
-
-    private final Map<ParcelUuid, byte[]> mServiceData;
-
-    // Transmission power level(in dB).
-    private final int mTxPowerLevel;
-
-    // Local name of the Bluetooth LE device.
-    private final String mDeviceName;
-
-    // Raw bytes of scan record.
-    private final byte[] mBytes;
-
-    /**
-     * Returns the advertising flags indicating the discoverable mode and capability of the device.
-     * Returns -1 if the flag field is not set.
-     */
-    public int getAdvertiseFlags() {
-        return mAdvertiseFlags;
-    }
-
-    /**
-     * Returns a list of service UUIDs within the advertisement that are used to identify the
-     * bluetooth GATT services.
-     */
-    public List<ParcelUuid> getServiceUuids() {
-        return mServiceUuids;
-    }
-
-    /**
-     * Returns a list of service solicitation UUIDs within the advertisement that are used to
-     * identify the Bluetooth GATT services.
-     */
-    @NonNull
-    public List<ParcelUuid> getServiceSolicitationUuids() {
-        return mServiceSolicitationUuids;
-    }
-
-    /**
-     * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
-     * data.
-     */
-    public SparseArray<byte[]> getManufacturerSpecificData() {
-        return mManufacturerSpecificData;
-    }
-
-    /**
-     * Returns the manufacturer specific data associated with the manufacturer id. Returns
-     * {@code null} if the {@code manufacturerId} is not found.
-     */
-    @Nullable
-    public byte[] getManufacturerSpecificData(int manufacturerId) {
-        if (mManufacturerSpecificData == null) {
-            return null;
-        }
-        return mManufacturerSpecificData.get(manufacturerId);
-    }
-
-    /**
-     * Returns a map of service UUID and its corresponding service data.
-     */
-    public Map<ParcelUuid, byte[]> getServiceData() {
-        return mServiceData;
-    }
-
-    /**
-     * Returns the service data byte array associated with the {@code serviceUuid}. Returns
-     * {@code null} if the {@code serviceDataUuid} is not found.
-     */
-    @Nullable
-    public byte[] getServiceData(ParcelUuid serviceDataUuid) {
-        if (serviceDataUuid == null || mServiceData == null) {
-            return null;
-        }
-        return mServiceData.get(serviceDataUuid);
-    }
-
-    /**
-     * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
-     * if the field is not set. This value can be used to calculate the path loss of a received
-     * packet using the following equation:
-     * <p>
-     * <code>pathloss = txPowerLevel - rssi</code>
-     */
-    public int getTxPowerLevel() {
-        return mTxPowerLevel;
-    }
-
-    /**
-     * Returns the local name of the BLE device. This is a UTF-8 encoded string.
-     */
-    @Nullable
-    public String getDeviceName() {
-        return mDeviceName;
-    }
-
-    /**
-     * Returns raw bytes of scan record.
-     */
-    public byte[] getBytes() {
-        return mBytes;
-    }
-
-    /**
-     * Test if any fields contained inside this scan record are matched by the
-     * given matcher.
-     *
-     * @hide
-     */
-    public boolean matchesAnyField(@NonNull Predicate<byte[]> matcher) {
-        int pos = 0;
-        while (pos < mBytes.length) {
-            final int length = mBytes[pos] & 0xFF;
-            if (length == 0) {
-                break;
-            }
-            if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) {
-                return true;
-            }
-            pos += length + 1;
-        }
-        return false;
-    }
-
-    private ScanRecord(List<ParcelUuid> serviceUuids,
-            List<ParcelUuid> serviceSolicitationUuids,
-            SparseArray<byte[]> manufacturerData,
-            Map<ParcelUuid, byte[]> serviceData,
-            int advertiseFlags, int txPowerLevel,
-            String localName, byte[] bytes) {
-        mServiceSolicitationUuids = serviceSolicitationUuids;
-        mServiceUuids = serviceUuids;
-        mManufacturerSpecificData = manufacturerData;
-        mServiceData = serviceData;
-        mDeviceName = localName;
-        mAdvertiseFlags = advertiseFlags;
-        mTxPowerLevel = txPowerLevel;
-        mBytes = bytes;
-    }
-
-    /**
-     * Parse scan record bytes to {@link ScanRecord}.
-     * <p>
-     * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
-     * <p>
-     * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
-     * order.
-     *
-     * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static ScanRecord parseFromBytes(byte[] scanRecord) {
-        if (scanRecord == null) {
-            return null;
-        }
-
-        int currentPos = 0;
-        int advertiseFlag = -1;
-        List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
-        List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>();
-        String localName = null;
-        int txPowerLevel = Integer.MIN_VALUE;
-
-        SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
-        Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
-
-        try {
-            while (currentPos < scanRecord.length) {
-                // length is unsigned int.
-                int length = scanRecord[currentPos++] & 0xFF;
-                if (length == 0) {
-                    break;
-                }
-                // Note the length includes the length of the field type itself.
-                int dataLength = length - 1;
-                // fieldType is unsigned int.
-                int fieldType = scanRecord[currentPos++] & 0xFF;
-                switch (fieldType) {
-                    case DATA_TYPE_FLAGS:
-                        advertiseFlag = scanRecord[currentPos] & 0xFF;
-                        break;
-                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
-                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
-                        parseServiceUuid(scanRecord, currentPos,
-                                dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
-                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
-                        parseServiceUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
-                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
-                        parseServiceUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT:
-                        parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT:
-                        parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT:
-                        parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids);
-                        break;
-                    case DATA_TYPE_LOCAL_NAME_SHORT:
-                    case DATA_TYPE_LOCAL_NAME_COMPLETE:
-                        localName = new String(
-                                extractBytes(scanRecord, currentPos, dataLength));
-                        break;
-                    case DATA_TYPE_TX_POWER_LEVEL:
-                        txPowerLevel = scanRecord[currentPos];
-                        break;
-                    case DATA_TYPE_SERVICE_DATA_16_BIT:
-                    case DATA_TYPE_SERVICE_DATA_32_BIT:
-                    case DATA_TYPE_SERVICE_DATA_128_BIT:
-                        int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
-                        if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
-                            serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
-                        } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
-                            serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
-                        }
-
-                        byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
-                                serviceUuidLength);
-                        ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
-                                serviceDataUuidBytes);
-                        byte[] serviceDataArray = extractBytes(scanRecord,
-                                currentPos + serviceUuidLength, dataLength - serviceUuidLength);
-                        serviceData.put(serviceDataUuid, serviceDataArray);
-                        break;
-                    case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
-                        // The first two bytes of the manufacturer specific data are
-                        // manufacturer ids in little endian.
-                        int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8)
-                                + (scanRecord[currentPos] & 0xFF);
-                        byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
-                                dataLength - 2);
-                        manufacturerData.put(manufacturerId, manufacturerDataBytes);
-                        break;
-                    default:
-                        // Just ignore, we don't handle such data type.
-                        break;
-                }
-                currentPos += dataLength;
-            }
-
-            if (serviceUuids.isEmpty()) {
-                serviceUuids = null;
-            }
-            return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
-                    serviceData, advertiseFlag, txPowerLevel, localName, scanRecord);
-        } catch (Exception e) {
-            Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
-            // As the record is invalid, ignore all the parsed results for this packet
-            // and return an empty record with raw scanRecord bytes in results
-            return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
-                + ", mServiceSolicitationUuids=" + mServiceSolicitationUuids
-                + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(
-                mManufacturerSpecificData)
-                + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
-                + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
-    }
-
-    // Parse service UUIDs.
-    private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
-            int uuidLength, List<ParcelUuid> serviceUuids) {
-        while (dataLength > 0) {
-            byte[] uuidBytes = extractBytes(scanRecord, currentPos,
-                    uuidLength);
-            serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
-            dataLength -= uuidLength;
-            currentPos += uuidLength;
-        }
-        return currentPos;
-    }
-
-    /**
-     * Parse service Solicitation UUIDs.
-     */
-    private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos,
-            int dataLength, int uuidLength, List<ParcelUuid> serviceSolicitationUuids) {
-        while (dataLength > 0) {
-            byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength);
-            serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
-            dataLength -= uuidLength;
-            currentPos += uuidLength;
-        }
-        return currentPos;
-    }
-
-    // Helper method to extract bytes from byte array.
-    private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
-        byte[] bytes = new byte[length];
-        System.arraycopy(scanRecord, start, bytes, 0, length);
-        return bytes;
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
deleted file mode 100644
index f437d86..0000000
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.Attributable;
-import android.bluetooth.BluetoothDevice;
-import android.content.AttributionSource;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * ScanResult for Bluetooth LE scan.
- */
-public final class ScanResult implements Parcelable, Attributable {
-
-    /**
-     * For chained advertisements, inidcates tha the data contained in this
-     * scan result is complete.
-     */
-    public static final int DATA_COMPLETE = 0x00;
-
-    /**
-     * For chained advertisements, indicates that the controller was
-     * unable to receive all chained packets and the scan result contains
-     * incomplete truncated data.
-     */
-    public static final int DATA_TRUNCATED = 0x02;
-
-    /**
-     * Indicates that the secondary physical layer was not used.
-     */
-    public static final int PHY_UNUSED = 0x00;
-
-    /**
-     * Advertising Set ID is not present in the packet.
-     */
-    public static final int SID_NOT_PRESENT = 0xFF;
-
-    /**
-     * TX power is not present in the packet.
-     */
-    public static final int TX_POWER_NOT_PRESENT = 0x7F;
-
-    /**
-     * Periodic advertising interval is not present in the packet.
-     */
-    public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
-
-    /**
-     * Mask for checking whether event type represents legacy advertisement.
-     */
-    private static final int ET_LEGACY_MASK = 0x10;
-
-    /**
-     * Mask for checking whether event type represents connectable advertisement.
-     */
-    private static final int ET_CONNECTABLE_MASK = 0x01;
-
-    // Remote Bluetooth device.
-    private BluetoothDevice mDevice;
-
-    // Scan record, including advertising data and scan response data.
-    @Nullable
-    private ScanRecord mScanRecord;
-
-    // Received signal strength.
-    private int mRssi;
-
-    // Device timestamp when the result was last seen.
-    private long mTimestampNanos;
-
-    private int mEventType;
-    private int mPrimaryPhy;
-    private int mSecondaryPhy;
-    private int mAdvertisingSid;
-    private int mTxPower;
-    private int mPeriodicAdvertisingInterval;
-
-    /**
-     * Constructs a new ScanResult.
-     *
-     * @param device Remote Bluetooth device found.
-     * @param scanRecord Scan record including both advertising data and scan response data.
-     * @param rssi Received signal strength.
-     * @param timestampNanos Timestamp at which the scan result was observed.
-     * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int,
-     * ScanRecord, long)}
-     */
-    @Deprecated
-    public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
-            long timestampNanos) {
-        mDevice = device;
-        mScanRecord = scanRecord;
-        mRssi = rssi;
-        mTimestampNanos = timestampNanos;
-        mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
-        mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
-        mSecondaryPhy = PHY_UNUSED;
-        mAdvertisingSid = SID_NOT_PRESENT;
-        mTxPower = 127;
-        mPeriodicAdvertisingInterval = 0;
-    }
-
-    /**
-     * Constructs a new ScanResult.
-     *
-     * @param device Remote Bluetooth device found.
-     * @param eventType Event type.
-     * @param primaryPhy Primary advertising phy.
-     * @param secondaryPhy Secondary advertising phy.
-     * @param advertisingSid Advertising set ID.
-     * @param txPower Transmit power.
-     * @param rssi Received signal strength.
-     * @param periodicAdvertisingInterval Periodic advertising interval.
-     * @param scanRecord Scan record including both advertising data and scan response data.
-     * @param timestampNanos Timestamp at which the scan result was observed.
-     */
-    public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
-            int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
-            ScanRecord scanRecord, long timestampNanos) {
-        mDevice = device;
-        mEventType = eventType;
-        mPrimaryPhy = primaryPhy;
-        mSecondaryPhy = secondaryPhy;
-        mAdvertisingSid = advertisingSid;
-        mTxPower = txPower;
-        mRssi = rssi;
-        mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
-        mScanRecord = scanRecord;
-        mTimestampNanos = timestampNanos;
-    }
-
-    private ScanResult(Parcel in) {
-        readFromParcel(in);
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        if (mDevice != null) {
-            dest.writeInt(1);
-            mDevice.writeToParcel(dest, flags);
-        } else {
-            dest.writeInt(0);
-        }
-        if (mScanRecord != null) {
-            dest.writeInt(1);
-            dest.writeByteArray(mScanRecord.getBytes());
-        } else {
-            dest.writeInt(0);
-        }
-        dest.writeInt(mRssi);
-        dest.writeLong(mTimestampNanos);
-        dest.writeInt(mEventType);
-        dest.writeInt(mPrimaryPhy);
-        dest.writeInt(mSecondaryPhy);
-        dest.writeInt(mAdvertisingSid);
-        dest.writeInt(mTxPower);
-        dest.writeInt(mPeriodicAdvertisingInterval);
-    }
-
-    private void readFromParcel(Parcel in) {
-        if (in.readInt() == 1) {
-            mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
-        }
-        if (in.readInt() == 1) {
-            mScanRecord = ScanRecord.parseFromBytes(in.createByteArray());
-        }
-        mRssi = in.readInt();
-        mTimestampNanos = in.readLong();
-        mEventType = in.readInt();
-        mPrimaryPhy = in.readInt();
-        mSecondaryPhy = in.readInt();
-        mAdvertisingSid = in.readInt();
-        mTxPower = in.readInt();
-        mPeriodicAdvertisingInterval = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** {@hide} */
-    public void setAttributionSource(@NonNull AttributionSource attributionSource) {
-        Attributable.setAttributionSource(mDevice, attributionSource);
-    }
-
-    /**
-     * Returns the remote Bluetooth device identified by the Bluetooth device address.
-     */
-    public BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Returns the scan record, which is a combination of advertisement and scan response.
-     */
-    @Nullable
-    public ScanRecord getScanRecord() {
-        return mScanRecord;
-    }
-
-    /**
-     * Returns the received signal strength in dBm. The valid range is [-127, 126].
-     */
-    public int getRssi() {
-        return mRssi;
-    }
-
-    /**
-     * Returns timestamp since boot when the scan record was observed.
-     */
-    public long getTimestampNanos() {
-        return mTimestampNanos;
-    }
-
-    /**
-     * Returns true if this object represents legacy scan result.
-     * Legacy scan results do not contain advanced advertising information
-     * as specified in the Bluetooth Core Specification v5.
-     */
-    public boolean isLegacy() {
-        return (mEventType & ET_LEGACY_MASK) != 0;
-    }
-
-    /**
-     * Returns true if this object represents connectable scan result.
-     */
-    public boolean isConnectable() {
-        return (mEventType & ET_CONNECTABLE_MASK) != 0;
-    }
-
-    /**
-     * Returns the data status.
-     * Can be one of {@link ScanResult#DATA_COMPLETE} or
-     * {@link ScanResult#DATA_TRUNCATED}.
-     */
-    public int getDataStatus() {
-        // return bit 5 and 6
-        return (mEventType >> 5) & 0x03;
-    }
-
-    /**
-     * Returns the primary Physical Layer
-     * on which this advertisment was received.
-     * Can be one of {@link BluetoothDevice#PHY_LE_1M} or
-     * {@link BluetoothDevice#PHY_LE_CODED}.
-     */
-    public int getPrimaryPhy() {
-        return mPrimaryPhy;
-    }
-
-    /**
-     * Returns the secondary Physical Layer
-     * on which this advertisment was received.
-     * Can be one of {@link BluetoothDevice#PHY_LE_1M},
-     * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED}
-     * or {@link ScanResult#PHY_UNUSED} - if the advertisement
-     * was not received on a secondary physical channel.
-     */
-    public int getSecondaryPhy() {
-        return mSecondaryPhy;
-    }
-
-    /**
-     * Returns the advertising set id.
-     * May return {@link ScanResult#SID_NOT_PRESENT} if
-     * no set id was is present.
-     */
-    public int getAdvertisingSid() {
-        return mAdvertisingSid;
-    }
-
-    /**
-     * Returns the transmit power in dBm.
-     * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
-     * indicates that the TX power is not present.
-     */
-    public int getTxPower() {
-        return mTxPower;
-    }
-
-    /**
-     * Returns the periodic advertising interval in units of 1.25ms.
-     * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
-     * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
-     * advertising interval is not present.
-     */
-    public int getPeriodicAdvertisingInterval() {
-        return mPeriodicAdvertisingInterval;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
-                mEventType, mPrimaryPhy, mSecondaryPhy,
-                mAdvertisingSid, mTxPower,
-                mPeriodicAdvertisingInterval);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        ScanResult other = (ScanResult) obj;
-        return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi)
-                && Objects.equals(mScanRecord, other.mScanRecord)
-                && (mTimestampNanos == other.mTimestampNanos)
-                && mEventType == other.mEventType
-                && mPrimaryPhy == other.mPrimaryPhy
-                && mSecondaryPhy == other.mSecondaryPhy
-                && mAdvertisingSid == other.mAdvertisingSid
-                && mTxPower == other.mTxPower
-                && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
-    }
-
-    @Override
-    public String toString() {
-        return "ScanResult{" + "device=" + mDevice + ", scanRecord="
-                + Objects.toString(mScanRecord) + ", rssi=" + mRssi
-                + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType
-                + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy
-                + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower
-                + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
-        @Override
-        public ScanResult createFromParcel(Parcel source) {
-            return new ScanResult(source);
-        }
-
-        @Override
-        public ScanResult[] newArray(int size) {
-            return new ScanResult[size];
-        }
-    };
-
-}
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
deleted file mode 100644
index 1aa7cb5..0000000
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.SystemApi;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the
- * parameters for the scan.
- */
-public final class ScanSettings implements Parcelable {
-
-    /**
-     * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
-     * other scan results without starting BLE scans themselves.
-     */
-    public static final int SCAN_MODE_OPPORTUNISTIC = -1;
-
-    /**
-     * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
-     * least power. This mode is enforced if the scanning application is not in foreground.
-     */
-    public static final int SCAN_MODE_LOW_POWER = 0;
-
-    /**
-     * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that
-     * provides a good trade-off between scan frequency and power consumption.
-     */
-    public static final int SCAN_MODE_BALANCED = 1;
-
-    /**
-     * Scan using highest duty cycle. It's recommended to only use this mode when the application is
-     * running in the foreground.
-     */
-    public static final int SCAN_MODE_LOW_LATENCY = 2;
-
-    /**
-     * Perform Bluetooth LE scan in ambient discovery mode. This mode has lower duty cycle and more
-     * aggressive scan interval than balanced mode that provides a good trade-off between scan
-     * latency and power consumption.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3;
-
-    /**
-     * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
-     * If no filter is active, all advertisement packets are reported.
-     */
-    public static final int CALLBACK_TYPE_ALL_MATCHES = 1;
-
-    /**
-     * A result callback is only triggered for the first advertisement packet received that matches
-     * the filter criteria.
-     */
-    public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
-
-    /**
-     * Receive a callback when advertisements are no longer received from a device that has been
-     * previously reported by a first match callback.
-     */
-    public static final int CALLBACK_TYPE_MATCH_LOST = 4;
-
-
-    /**
-     * Determines how many advertisements to match per filter, as this is scarce hw resource
-     */
-    /**
-     * Match one advertisement per filter
-     */
-    public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
-
-    /**
-     * Match few advertisement per filter, depends on current capability and availibility of
-     * the resources in hw
-     */
-    public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
-
-    /**
-     * Match as many advertisement per filter as hw could allow, depends on current
-     * capability and availibility of the resources in hw
-     */
-    public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
-
-
-    /**
-     * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
-     * and few number of sightings/match in a duration.
-     */
-    public static final int MATCH_MODE_AGGRESSIVE = 1;
-
-    /**
-     * For sticky mode, higher threshold of signal strength and sightings is required
-     * before reporting by hw
-     */
-    public static final int MATCH_MODE_STICKY = 2;
-
-    /**
-     * Request full scan results which contain the device, rssi, advertising data, scan response
-     * as well as the scan timestamp.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int SCAN_RESULT_TYPE_FULL = 0;
-
-    /**
-     * Request abbreviated scan results which contain the device, rssi and scan timestamp.
-     * <p>
-     * <b>Note:</b> It is possible for an application to get more scan results than it asked for, if
-     * there are multiple apps using this type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1;
-
-    /**
-     * Use all supported PHYs for scanning.
-     * This will check the controller capabilities, and start
-     * the scan on 1Mbit and LE Coded PHYs if supported, or on
-     * the 1Mbit PHY only.
-     */
-    public static final int PHY_LE_ALL_SUPPORTED = 255;
-
-    // Bluetooth LE scan mode.
-    private int mScanMode;
-
-    // Bluetooth LE scan callback type
-    private int mCallbackType;
-
-    // Bluetooth LE scan result type
-    private int mScanResultType;
-
-    // Time of delay for reporting the scan result
-    private long mReportDelayMillis;
-
-    private int mMatchMode;
-
-    private int mNumOfMatchesPerFilter;
-
-    // Include only legacy advertising results
-    private boolean mLegacy;
-
-    private int mPhy;
-
-    public int getScanMode() {
-        return mScanMode;
-    }
-
-    public int getCallbackType() {
-        return mCallbackType;
-    }
-
-    public int getScanResultType() {
-        return mScanResultType;
-    }
-
-    /**
-     * @hide
-     */
-    public int getMatchMode() {
-        return mMatchMode;
-    }
-
-    /**
-     * @hide
-     */
-    public int getNumOfMatches() {
-        return mNumOfMatchesPerFilter;
-    }
-
-    /**
-     * Returns whether only legacy advertisements will be returned.
-     * Legacy advertisements include advertisements as specified
-     * by the Bluetooth core specification 4.2 and below.
-     */
-    public boolean getLegacy() {
-        return mLegacy;
-    }
-
-    /**
-     * Returns the physical layer used during a scan.
-     */
-    public int getPhy() {
-        return mPhy;
-    }
-
-    /**
-     * Returns report delay timestamp based on the device clock.
-     */
-    public long getReportDelayMillis() {
-        return mReportDelayMillis;
-    }
-
-    private ScanSettings(int scanMode, int callbackType, int scanResultType,
-            long reportDelayMillis, int matchMode,
-            int numOfMatchesPerFilter, boolean legacy, int phy) {
-        mScanMode = scanMode;
-        mCallbackType = callbackType;
-        mScanResultType = scanResultType;
-        mReportDelayMillis = reportDelayMillis;
-        mNumOfMatchesPerFilter = numOfMatchesPerFilter;
-        mMatchMode = matchMode;
-        mLegacy = legacy;
-        mPhy = phy;
-    }
-
-    private ScanSettings(Parcel in) {
-        mScanMode = in.readInt();
-        mCallbackType = in.readInt();
-        mScanResultType = in.readInt();
-        mReportDelayMillis = in.readLong();
-        mMatchMode = in.readInt();
-        mNumOfMatchesPerFilter = in.readInt();
-        mLegacy = in.readInt() != 0;
-        mPhy = in.readInt();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mScanMode);
-        dest.writeInt(mCallbackType);
-        dest.writeInt(mScanResultType);
-        dest.writeLong(mReportDelayMillis);
-        dest.writeInt(mMatchMode);
-        dest.writeInt(mNumOfMatchesPerFilter);
-        dest.writeInt(mLegacy ? 1 : 0);
-        dest.writeInt(mPhy);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ScanSettings> CREATOR =
-            new Creator<ScanSettings>() {
-        @Override
-        public ScanSettings[] newArray(int size) {
-            return new ScanSettings[size];
-        }
-
-        @Override
-        public ScanSettings createFromParcel(Parcel in) {
-            return new ScanSettings(in);
-        }
-    };
-
-    /**
-     * Builder for {@link ScanSettings}.
-     */
-    public static final class Builder {
-        private int mScanMode = SCAN_MODE_LOW_POWER;
-        private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
-        private int mScanResultType = SCAN_RESULT_TYPE_FULL;
-        private long mReportDelayMillis = 0;
-        private int mMatchMode = MATCH_MODE_AGGRESSIVE;
-        private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
-        private boolean mLegacy = true;
-        private int mPhy = PHY_LE_ALL_SUPPORTED;
-
-        /**
-         * Set scan mode for Bluetooth LE scan.
-         *
-         * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER},
-         * {@link ScanSettings#SCAN_MODE_BALANCED} or {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
-         * @throws IllegalArgumentException If the {@code scanMode} is invalid.
-         */
-        public Builder setScanMode(int scanMode) {
-            switch (scanMode) {
-                case SCAN_MODE_OPPORTUNISTIC:
-                case SCAN_MODE_LOW_POWER:
-                case SCAN_MODE_BALANCED:
-                case SCAN_MODE_LOW_LATENCY:
-                case SCAN_MODE_AMBIENT_DISCOVERY:
-                    mScanMode = scanMode;
-                    break;
-                default:
-                    throw new IllegalArgumentException("invalid scan mode " + scanMode);
-            }
-            return this;
-        }
-
-        /**
-         * Set callback type for Bluetooth LE scan.
-         *
-         * @param callbackType The callback type flags for the scan.
-         * @throws IllegalArgumentException If the {@code callbackType} is invalid.
-         */
-        public Builder setCallbackType(int callbackType) {
-
-            if (!isValidCallbackType(callbackType)) {
-                throw new IllegalArgumentException("invalid callback type - " + callbackType);
-            }
-            mCallbackType = callbackType;
-            return this;
-        }
-
-        // Returns true if the callbackType is valid.
-        private boolean isValidCallbackType(int callbackType) {
-            if (callbackType == CALLBACK_TYPE_ALL_MATCHES
-                    || callbackType == CALLBACK_TYPE_FIRST_MATCH
-                    || callbackType == CALLBACK_TYPE_MATCH_LOST) {
-                return true;
-            }
-            return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
-        }
-
-        /**
-         * Set scan result type for Bluetooth LE scan.
-         *
-         * @param scanResultType Type for scan result, could be either {@link
-         * ScanSettings#SCAN_RESULT_TYPE_FULL} or {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}.
-         * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
-         * @hide
-         */
-        @SystemApi
-        public Builder setScanResultType(int scanResultType) {
-            if (scanResultType < SCAN_RESULT_TYPE_FULL
-                    || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) {
-                throw new IllegalArgumentException(
-                        "invalid scanResultType - " + scanResultType);
-            }
-            mScanResultType = scanResultType;
-            return this;
-        }
-
-        /**
-         * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of
-         * scan results immediately. If &gt; 0, scan results are queued up and delivered after the
-         * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be
-         * delivered sooner if the internal buffers fill up.
-         *
-         * @param reportDelayMillis         how frequently scan results should be delivered in
-         *                                  milliseconds
-         * @throws IllegalArgumentException if {@code reportDelayMillis} &lt; 0
-         */
-        public Builder setReportDelay(long reportDelayMillis) {
-            if (reportDelayMillis < 0) {
-                throw new IllegalArgumentException("reportDelay must be > 0");
-            }
-            mReportDelayMillis = reportDelayMillis;
-            return this;
-        }
-
-        /**
-         * Set the number of matches for Bluetooth LE scan filters hardware match
-         *
-         * @param numOfMatches The num of matches can be one of
-         * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT}
-         * or {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or {@link
-         * ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
-         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
-         */
-        public Builder setNumOfMatches(int numOfMatches) {
-            if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
-                    || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
-                throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
-            }
-            mNumOfMatchesPerFilter = numOfMatches;
-            return this;
-        }
-
-        /**
-         * Set match mode for Bluetooth LE scan filters hardware match
-         *
-         * @param matchMode The match mode can be one of {@link ScanSettings#MATCH_MODE_AGGRESSIVE}
-         * or {@link ScanSettings#MATCH_MODE_STICKY}
-         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
-         */
-        public Builder setMatchMode(int matchMode) {
-            if (matchMode < MATCH_MODE_AGGRESSIVE
-                    || matchMode > MATCH_MODE_STICKY) {
-                throw new IllegalArgumentException("invalid matchMode " + matchMode);
-            }
-            mMatchMode = matchMode;
-            return this;
-        }
-
-        /**
-         * Set whether only legacy advertisments should be returned in scan results.
-         * Legacy advertisements include advertisements as specified by the
-         * Bluetooth core specification 4.2 and below. This is true by default
-         * for compatibility with older apps.
-         *
-         * @param legacy true if only legacy advertisements will be returned
-         */
-        public Builder setLegacy(boolean legacy) {
-            mLegacy = legacy;
-            return this;
-        }
-
-        /**
-         * Set the Physical Layer to use during this scan.
-         * This is used only if {@link ScanSettings.Builder#setLegacy}
-         * is set to false.
-         * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
-         * may be used to check whether LE Coded phy is supported by calling
-         * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
-         * Selecting an unsupported phy will result in failure to start scan.
-         *
-         * @param phy Can be one of {@link BluetoothDevice#PHY_LE_1M}, {@link
-         * BluetoothDevice#PHY_LE_CODED} or {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
-         */
-        public Builder setPhy(int phy) {
-            mPhy = phy;
-            return this;
-        }
-
-        /**
-         * Build {@link ScanSettings}.
-         */
-        public ScanSettings build() {
-            return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
-                    mReportDelayMillis, mMatchMode,
-                    mNumOfMatchesPerFilter, mLegacy, mPhy);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java
deleted file mode 100644
index 18bad9c..0000000
--- a/core/java/android/bluetooth/le/TransportBlock.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * Wrapper for Transport Discovery Data Transport Blocks.
- * This class represents a Transport Block from a Transport Discovery Data.
- *
- * @see TransportDiscoveryData
- * @see AdvertiseData
- */
-public final class TransportBlock implements Parcelable {
-    private static final String TAG = "TransportBlock";
-    private final int mOrgId;
-    private final int mTdsFlags;
-    private final int mTransportDataLength;
-    private final byte[] mTransportData;
-
-    /**
-     * Creates an instance of TransportBlock from raw data.
-     *
-     * @param orgId the Organization ID
-     * @param tdsFlags the TDS flags
-     * @param transportDataLength the total length of the Transport Data
-     * @param transportData the Transport Data
-     */
-    public TransportBlock(int orgId, int tdsFlags, int transportDataLength,
-            @Nullable byte[] transportData) {
-        mOrgId = orgId;
-        mTdsFlags = tdsFlags;
-        mTransportDataLength = transportDataLength;
-        mTransportData = transportData;
-    }
-
-    private TransportBlock(Parcel in) {
-        mOrgId = in.readInt();
-        mTdsFlags = in.readInt();
-        mTransportDataLength = in.readInt();
-        if (mTransportDataLength > 0) {
-            mTransportData = new byte[mTransportDataLength];
-            in.readByteArray(mTransportData);
-        } else {
-            mTransportData = null;
-        }
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mOrgId);
-        dest.writeInt(mTdsFlags);
-        dest.writeInt(mTransportDataLength);
-        if (mTransportData != null) {
-            dest.writeByteArray(mTransportData);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        TransportBlock other = (TransportBlock) obj;
-        return Arrays.equals(toByteArray(), other.toByteArray());
-    }
-
-    public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() {
-        @Override
-        public TransportBlock createFromParcel(Parcel in) {
-            return new TransportBlock(in);
-        }
-
-        @Override
-        public TransportBlock[] newArray(int size) {
-            return new TransportBlock[size];
-        }
-    };
-
-    /**
-     * Gets the Organization ID of the Transport Block which corresponds to one of the
-     * the Bluetooth SIG Assigned Numbers.
-     */
-    public int getOrgId() {
-        return mOrgId;
-    }
-
-    /**
-     * Gets the TDS flags of the Transport Block which represents the role of the device and
-     * information about its state and supported features.
-     */
-    public int getTdsFlags() {
-        return mTdsFlags;
-    }
-
-    /**
-     * Gets the total number of octets in the Transport Data field in this Transport Block.
-     */
-    public int getTransportDataLength() {
-        return mTransportDataLength;
-    }
-
-    /**
-     * Gets the Transport Data of the Transport Block which contains organization-specific data.
-     */
-    @Nullable
-    public byte[] getTransportData() {
-        return mTransportData;
-    }
-
-    /**
-     * Converts this TransportBlock to byte array
-     *
-     * @return byte array representation of this Transport Block or null if the conversion failed
-     */
-    @Nullable
-    public byte[] toByteArray() {
-        try {
-            ByteBuffer buffer = ByteBuffer.allocate(totalBytes());
-            buffer.put((byte) mOrgId);
-            buffer.put((byte) mTdsFlags);
-            buffer.put((byte) mTransportDataLength);
-            if (mTransportData != null) {
-                buffer.put(mTransportData);
-            }
-            return buffer.array();
-        } catch (BufferOverflowException e) {
-            Log.e(TAG, "Error converting to byte array: " + e.toString());
-            return null;
-        }
-    }
-
-    /**
-     * @return total byte count of this TransportBlock
-     */
-    public int totalBytes() {
-        // 3 uint8 + byte[] length
-        int size = 3 + mTransportDataLength;
-        return size;
-    }
-}
diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java
deleted file mode 100644
index 2b52f19..0000000
--- a/core/java/android/bluetooth/le/TransportDiscoveryData.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.nio.BufferOverflowException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Wrapper for Transport Discovery Data AD Type.
- * This class contains the Transport Discovery Data AD Type Code as well as
- * a list of potential Transport Blocks.
- *
- * @see AdvertiseData
- */
-public final class TransportDiscoveryData implements Parcelable {
-    private static final String TAG = "TransportDiscoveryData";
-    private final int mTransportDataType;
-    private final List<TransportBlock> mTransportBlocks;
-
-    /**
-     * Creates a TransportDiscoveryData instance.
-     *
-     * @param transportDataType the Transport Discovery Data AD Type
-     * @param transportBlocks the list of Transport Blocks
-     */
-    public TransportDiscoveryData(int transportDataType,
-            @NonNull List<TransportBlock> transportBlocks) {
-        mTransportDataType = transportDataType;
-        mTransportBlocks = transportBlocks;
-    }
-
-    /**
-     * Creates a TransportDiscoveryData instance from byte arrays.
-     *
-     * Uses the transport discovery data bytes and parses them into an usable class.
-     *
-     * @param transportDiscoveryData the raw discovery data
-     */
-    public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) {
-        ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData);
-        mTransportBlocks = new ArrayList();
-        if (byteBuffer.remaining() > 0) {
-            mTransportDataType = byteBuffer.get();
-        } else {
-            mTransportDataType = -1;
-        }
-        try {
-            while (byteBuffer.remaining() > 0) {
-                int orgId = byteBuffer.get();
-                int tdsFlags = byteBuffer.get();
-                int transportDataLength = byteBuffer.get();
-                byte[] transportData = new byte[transportDataLength];
-                byteBuffer.get(transportData, 0, transportDataLength);
-                mTransportBlocks.add(new TransportBlock(orgId, tdsFlags,
-                        transportDataLength, transportData));
-            }
-        } catch (BufferUnderflowException e) {
-            Log.e(TAG, "Error while parsing data: " + e.toString());
-        }
-    }
-
-    private TransportDiscoveryData(Parcel in) {
-        mTransportDataType = in.readInt();
-        mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        TransportDiscoveryData other = (TransportDiscoveryData) obj;
-        return Arrays.equals(toByteArray(), other.toByteArray());
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mTransportDataType);
-        dest.writeTypedList(mTransportBlocks);
-    }
-
-    public static final @NonNull Creator<TransportDiscoveryData> CREATOR =
-            new Creator<TransportDiscoveryData>() {
-                @Override
-                public TransportDiscoveryData createFromParcel(Parcel in) {
-                    return new TransportDiscoveryData(in);
-                }
-
-                @Override
-                public TransportDiscoveryData[] newArray(int size) {
-                    return new TransportDiscoveryData[size];
-                }
-    };
-
-    /**
-     * Gets the transport data type.
-     */
-    public int getTransportDataType() {
-        return mTransportDataType;
-    }
-
-    /**
-     * @return the list of {@link TransportBlock} in this TransportDiscoveryData
-     *         or an empty list if there are no Transport Blocks
-     */
-    @NonNull
-    public List<TransportBlock> getTransportBlocks() {
-        if (mTransportBlocks == null) {
-            return Collections.emptyList();
-        }
-        return mTransportBlocks;
-    }
-
-    /**
-     * Converts this TransportDiscoveryData to byte array
-     *
-     * @return byte array representation of this Transport Discovery Data or null if the
-     *         conversion failed
-     */
-    @Nullable
-    public byte[] toByteArray() {
-        try {
-            ByteBuffer buffer = ByteBuffer.allocate(totalBytes());
-            buffer.put((byte) mTransportDataType);
-            for (TransportBlock transportBlock : getTransportBlocks()) {
-                buffer.put(transportBlock.toByteArray());
-            }
-            return buffer.array();
-        } catch (BufferOverflowException e) {
-            Log.e(TAG, "Error converting to byte array: " + e.toString());
-            return null;
-        }
-    }
-
-    /**
-     * @return total byte count of this TransportDataDiscovery
-     */
-    public int totalBytes() {
-        int size = 1; // Counting Transport Data Type here.
-        for (TransportBlock transportBlock : getTransportBlocks()) {
-            size += transportBlock.totalBytes();
-        }
-        return size;
-    }
-}
diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java
deleted file mode 100644
index 2592588..0000000
--- a/core/java/android/bluetooth/le/TruncatedFilter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-
-import java.util.List;
-
-/**
- * A special scan filter that lets the client decide how the scan record should be stored.
- *
- * @deprecated this is not used anywhere
- *
- * @hide
- */
-@Deprecated
-@SystemApi
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class TruncatedFilter {
-    private final ScanFilter mFilter;
-    private final List<ResultStorageDescriptor> mStorageDescriptors;
-
-    /**
-     * Constructor for {@link TruncatedFilter}.
-     *
-     * @param filter Scan filter of the truncated filter.
-     * @param storageDescriptors Describes how the scan should be stored.
-     */
-    public TruncatedFilter(ScanFilter filter, List<ResultStorageDescriptor> storageDescriptors) {
-        mFilter = filter;
-        mStorageDescriptors = storageDescriptors;
-    }
-
-    /**
-     * Returns the scan filter.
-     */
-    public ScanFilter getFilter() {
-        return mFilter;
-    }
-
-    /**
-     * Returns a list of descriptor for scan result storage.
-     */
-    public List<ResultStorageDescriptor> getStorageDescriptors() {
-        return mStorageDescriptors;
-    }
-
-
-}
diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html
deleted file mode 100644
index d9ca4f1..0000000
--- a/core/java/android/bluetooth/package.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<HTML>
-<BODY>
-<p>Provides classes that manage Bluetooth functionality, such as scanning for
-devices, connecting with devices, and managing data transfer between devices.
-The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.</p>
-
-<p>For more information about Classic Bluetooth, see the
-<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> guide.
-For more information about Bluetooth Low Energy, see the
-<a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">
-Bluetooth Low Energy</a> (BLE) guide.</p>
-{@more}
-
-<p>The Bluetooth APIs let applications:</p>
-<ul>
-  <li>Scan for other Bluetooth devices (including BLE devices).</li>
-  <li>Query the local Bluetooth adapter for paired Bluetooth devices.</li>
-  <li>Establish RFCOMM channels/sockets.</li>
-  <li>Connect to specified sockets on other devices.</li>
-  <li>Transfer data to and from other devices.</li>
-  <li>Communicate with BLE devices, such as proximity sensors, heart rate
-    monitors, fitness devices, and so on.</li>
-  <li>Act as a GATT client or a GATT server (BLE).</li>
-</ul>
-
-<p>
-To perform Bluetooth communication using these APIs, an application must
-declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some
-additional functionality, such as requesting device discovery,
-also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-permission.
-</p>
-
-<p class="note"><strong>Note:</strong>
-Not all Android-powered devices provide Bluetooth functionality.</p>
-
-</BODY>
-</HTML>
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 78d5137..373a8d9 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -36,6 +36,10 @@
  */
 public final class AssociationInfo implements Parcelable {
     /**
+     * A String indicates the selfManaged device is not connected.
+     */
+    private static final String LAST_TIME_CONNECTED_NONE = "None";
+    /**
      * A unique ID of this Association record.
      * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
      * {@code disassociate()} API call).
@@ -50,8 +54,13 @@
     private final @Nullable String mDeviceProfile;
 
     private final boolean mSelfManaged;
-    private boolean mNotifyOnDeviceNearby;
+    private final boolean mNotifyOnDeviceNearby;
     private final long mTimeApprovedMs;
+    /**
+     * A long value indicates the last time connected reported by selfManaged devices
+     * Default value is Long.MAX_VALUE.
+     */
+    private final long mLastTimeConnectedMs;
 
     /**
      * Creates a new Association.
@@ -62,7 +71,7 @@
     public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
             @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
             @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
-            long timeApprovedMs) {
+            long timeApprovedMs, long lastTimeConnectedMs) {
         if (id <= 0) {
             throw new IllegalArgumentException("Association ID should be greater than 0");
         }
@@ -83,6 +92,7 @@
         mSelfManaged = selfManaged;
         mNotifyOnDeviceNearby = notifyOnDeviceNearby;
         mTimeApprovedMs = timeApprovedMs;
+        mLastTimeConnectedMs = lastTimeConnectedMs;
     }
 
     /**
@@ -150,14 +160,6 @@
         return mSelfManaged;
     }
 
-    /**
-     * Should only be used by the CdmService.
-     * @hide
-     */
-    public void setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
-        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
-    }
-
     /** @hide */
     public boolean isNotifyOnDeviceNearby() {
         return mNotifyOnDeviceNearby;
@@ -174,6 +176,14 @@
     }
 
     /**
+     * @return the last time self reported disconnected for selfManaged only.
+     * @hide
+     */
+    public Long getLastTimeConnectedMs() {
+        return mLastTimeConnectedMs;
+    }
+
+    /**
      * Utility method for checking if the association represents a device with the given MAC
      * address.
      *
@@ -223,6 +233,9 @@
                 + ", mSelfManaged=" + mSelfManaged
                 + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
                 + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
+                + ", mLastTimeConnectedMs=" + (
+                    mLastTimeConnectedMs == Long.MAX_VALUE
+                        ? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs))
                 + '}';
     }
 
@@ -236,6 +249,7 @@
                 && mSelfManaged == that.mSelfManaged
                 && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
                 && mTimeApprovedMs == that.mTimeApprovedMs
+                && mLastTimeConnectedMs == that.mLastTimeConnectedMs
                 && Objects.equals(mPackageName, that.mPackageName)
                 && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
                 && Objects.equals(mDisplayName, that.mDisplayName)
@@ -245,7 +259,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
-                mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs);
+                mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs,
+                mLastTimeConnectedMs);
     }
 
     @Override
@@ -267,6 +282,7 @@
         dest.writeBoolean(mSelfManaged);
         dest.writeBoolean(mNotifyOnDeviceNearby);
         dest.writeLong(mTimeApprovedMs);
+        dest.writeLong(mLastTimeConnectedMs);
     }
 
     private AssociationInfo(@NonNull Parcel in) {
@@ -282,6 +298,7 @@
         mSelfManaged = in.readBoolean();
         mNotifyOnDeviceNearby = in.readBoolean();
         mTimeApprovedMs = in.readLong();
+        mLastTimeConnectedMs = in.readLong();
     }
 
     @NonNull
@@ -297,4 +314,112 @@
             return new AssociationInfo(in);
         }
     };
+
+    /**
+     * Use this method to obtain a builder that you can use to create a copy of the
+     * given {@link AssociationInfo} with modified values of {@code mLastTimeConnected}
+     * or {@code mNotifyOnDeviceNearby}.
+     * <p>
+     *     Note that you <b>must</b> call either {@link Builder#setLastTimeConnected(long)
+     *     setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean)
+     *     setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}.
+     *
+     *     This is ensured statically at compile time.
+     *
+     * @hide
+     */
+    @NonNull
+    public static NonActionableBuilder builder(@NonNull AssociationInfo info) {
+        return new Builder(info);
+    }
+
+    /**
+     * @hide
+     */
+    public static final class Builder implements NonActionableBuilder {
+        @NonNull
+        private final AssociationInfo mOriginalInfo;
+        private boolean mNotifyOnDeviceNearby;
+        private long mLastTimeConnectedMs;
+
+        private Builder(@NonNull AssociationInfo info) {
+            mOriginalInfo = info;
+            mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
+            mLastTimeConnectedMs = info.mLastTimeConnectedMs;
+        }
+
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @Override
+        @NonNull
+        public Builder setLastTimeConnected(long lastTimeConnectedMs) {
+            if (lastTimeConnectedMs < 0) {
+                throw new IllegalArgumentException(
+                        "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs
+                                + " )");
+            }
+            mLastTimeConnectedMs = lastTimeConnectedMs;
+            return this;
+        }
+
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @Override
+        @NonNull
+        public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
+            mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public AssociationInfo build() {
+            return new AssociationInfo(
+                    mOriginalInfo.mId,
+                    mOriginalInfo.mUserId,
+                    mOriginalInfo.mPackageName,
+                    mOriginalInfo.mDeviceMacAddress,
+                    mOriginalInfo.mDisplayName,
+                    mOriginalInfo.mDeviceProfile,
+                    mOriginalInfo.mSelfManaged,
+                    mNotifyOnDeviceNearby,
+                    mOriginalInfo.mTimeApprovedMs,
+                    mLastTimeConnectedMs
+            );
+        }
+    }
+
+    /**
+     * This interface is returned from the
+     * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point
+     * to indicate that this builder is not yet in a state that can produce a meaningful
+     * {@link AssociationInfo} object that is different from the one originally passed in.
+     *
+     * <p>
+     * Only by calling one of the setter methods is this builder turned into one where calling
+     * {@link Builder#build() build()} makes sense.
+     *
+     * @hide
+     */
+    public interface NonActionableBuilder {
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @NonNull
+        Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby);
+
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @NonNull
+        Builder setLastTimeConnected(long lastTimeConnectedMs);
+    }
 }
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 3237f7c..610b7ee 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -28,35 +28,74 @@
 import android.os.IBinder;
 import android.util.Log;
 
-
-import com.android.internal.util.function.pooled.PooledLambda;
-
 import java.util.Objects;
 
 /**
- * Service to be implemented by apps that manage a companion device.
+ * A service that receives calls from the system when the associated companion device appears
+ * nearby or is connected, as well as when the device is no longer "present" or connected.
+ * See {@link #onDeviceAppeared(AssociationInfo)}/{@link #onDeviceDisappeared(AssociationInfo)}.
  *
- * System will keep this service bound whenever an associated device is nearby for Bluetooth
- * devices or companion app manages the connectivity and reports disappeared, ensuring app stays
- * alive
+ * <p>
+ * Additionally, the service will receive a call from the system, if and when the system needs to
+ * transfer data to the companion device.
+ * See {@link #dispatchMessage(int, int, byte[])}).
  *
- * An app must be {@link CompanionDeviceManager#associate associated} with at leas one device,
- * before it can take advantage of this service.
+ * <p>
+ * Companion applications must create a service that {@code extends}
+ * {@link CompanionDeviceService}, and declare it in their AndroidManifest.xml with the
+ * "android.permission.BIND_COMPANION_DEVICE_SERVICE" permission
+ * (see {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}),
+ * as well as add an intent filter for the "android.companion.CompanionDeviceService" action
+ * (see {@link #SERVICE_INTERFACE}).
  *
- * You must declare this service in your manifest with an
- * intent-filter action of {@link #SERVICE_INTERFACE} and
- * permission of {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}
+ * <p>
+ * Following is an example of such declaration:
+ * <pre>{@code
+ * <service
+ *        android:name=".CompanionService"
+ *        android:label="@string/service_name"
+ *        android:exported="true"
+ *        android:permission="android.permission.BIND_COMPANION_DEVICE_SERVICE">
+ *    <intent-filter>
+ *        <action android:name="android.companion.CompanionDeviceService" />
+ *    </intent-filter>
+ * </service>
+ * }</pre>
  *
- * <p>If you want to declare more than one of these services, you must declare the meta-data in the
- * service of your manifest with the corresponding name and value to true to indicate the
- * primary service.
- * Only the primary one will get the callback from
- * {@link #onDeviceAppeared(AssociationInfo associationInfo)}.</p>
+ * <p>
+ * If the companion application has requested observing device presence (see
+ * {@link CompanionDeviceManager#startObservingDevicePresence(String)}) the system will
+ * <a href="https://developer.android.com/guide/components/bound-services"> bind the service</a>
+ * when it detects the device nearby (for BLE devices) or when the device is connected
+ * (for Bluetooth devices).
  *
- * Example:
+ * <p>
+ * The system binding {@link CompanionDeviceService} elevates the priority of the process that
+ * the service is running in, and thus may prevent
+ * <a href="https://developer.android.com/topic/performance/memory-management#low-memory_killer">
+ * the Low-memory killer</a> from killing the process at expense of other processes with lower
+ * priority.
+ *
+ * <p>
+ * It is possible for an application to declare multiple {@link CompanionDeviceService}-s.
+ * In such case, the system will bind all declared services, but will deliver
+ * {@link #onDeviceAppeared(AssociationInfo)}, {@link #onDeviceDisappeared(AssociationInfo)} and
+ * {@link #dispatchMessage(int, int, byte[])} only to one "primary" services.
+ * Applications that declare multiple {@link CompanionDeviceService}-s should indicate the "primary"
+ * service using "android.companion.primary" tag.
+ * <pre>{@code
  * <meta-data
- *   android:name="primary"
- *   android:value="true" />
+ *       android:name="android.companion.primary"
+ *       android:value="true" />
+ * }</pre>
+ *
+ * <p>
+ * If the application declares multiple {@link CompanionDeviceService}-s, but does not indicate
+ * the "primary" one, the system will pick one of the declared services to use as "primary".
+ *
+ * <p>
+ * If the application declares multiple "primary" {@link CompanionDeviceService}-s, the system
+ * will pick single one of them to use as "primary".
  */
 public abstract class CompanionDeviceService extends Service {
 
@@ -185,31 +224,24 @@
     public void onBindCompanionDeviceService(@NonNull Intent intent) {
     }
 
-    class Stub extends ICompanionDeviceService.Stub {
+    private class Stub extends ICompanionDeviceService.Stub {
+        final Handler mMainHandler = Handler.getMain();
+        final CompanionDeviceService mService = CompanionDeviceService.this;
 
         @Override
         public void onDeviceAppeared(AssociationInfo associationInfo) {
-            Handler.getMain().post(
-                    () -> CompanionDeviceService.this.onDeviceAppeared(associationInfo));
+            mMainHandler.postAtFrontOfQueue(() -> mService.onDeviceAppeared(associationInfo));
         }
 
         @Override
         public void onDeviceDisappeared(AssociationInfo associationInfo) {
-            Handler.getMain().post(
-                    () -> CompanionDeviceService.this.onDeviceDisappeared(associationInfo));
+            mMainHandler.postAtFrontOfQueue(() -> mService.onDeviceDisappeared(associationInfo));
         }
 
+        @Override
         public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
-            Handler.getMain().sendMessage(PooledLambda.obtainMessage(
-                    CompanionDeviceService::onDispatchMessage,
-                    CompanionDeviceService.this, messageId, associationId, message));
-        }
-
-        public final void dispatchMessage(int messageId, int associationId,
-                @NonNull byte[] message) {
-            Handler.getMain().sendMessage(PooledLambda.obtainMessage(
-                    CompanionDeviceService::dispatchMessage,
-                    CompanionDeviceService.this, messageId, associationId, message));
+            mMainHandler.postAtFrontOfQueue(
+                    () -> mService.onDispatchMessage(messageId, associationId, message));
         }
     }
 }
diff --git a/core/java/android/companion/IOnAssociationsChangedListener.aidl b/core/java/android/companion/IOnAssociationsChangedListener.aidl
index e6794b7..d369456 100644
--- a/core/java/android/companion/IOnAssociationsChangedListener.aidl
+++ b/core/java/android/companion/IOnAssociationsChangedListener.aidl
@@ -20,5 +20,22 @@
 
 /** @hide */
 interface IOnAssociationsChangedListener {
-    oneway void onAssociationsChanged(in List<AssociationInfo> associations);
+
+    /*
+     * IMPORTANT: This method is intentionally NOT "oneway".
+     *
+     * The method is intentionally "blocking" to make sure that the clients of the
+     * addOnAssociationsChangedListener() API (@SystemAPI guarded by a "signature" permission) are
+     * able to prevent race conditions that may arise if their own clients (applications)
+     * effectively get notified about the changes before system services do.
+     *
+     * This is safe for 2 reasons:
+     *  1. The addOnAssociationsChangedListener() is only available to the system components
+     *     (guarded by a "signature" permission).
+     *     See android.permission.MANAGE_COMPANION_DEVICES.
+     *  2. On the Java side addOnAssociationsChangedListener() in CDM takes an Executor, and the
+     *     proxy implementation of onAssociationsChanged() simply "post" a Runnable to it.
+     *     See CompanionDeviceManager.OnAssociationsChangedListenerProxy class.
+     */
+    void onAssociationsChanged(in List<AssociationInfo> associations);
 }
\ No newline at end of file
diff --git a/core/java/android/companion/SystemDataTransferRequest.aidl b/core/java/android/companion/SystemDataTransferRequest.aidl
new file mode 100644
index 0000000..19ae60e
--- /dev/null
+++ b/core/java/android/companion/SystemDataTransferRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+parcelable SystemDataTransferRequest;
diff --git a/core/java/android/companion/SystemDataTransferRequest.java b/core/java/android/companion/SystemDataTransferRequest.java
new file mode 100644
index 0000000..e3b0369
--- /dev/null
+++ b/core/java/android/companion/SystemDataTransferRequest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.OneTimeUseBuilder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A request for users to allow the companion app to transfer system data to the companion devices.
+ *
+ * @hide
+ */
+public final class SystemDataTransferRequest implements Parcelable {
+
+    private final int mAssociationId;
+    private final boolean mPermissionSyncAllPackages;
+    private final List<String> mPermissionSyncPackages;
+
+    /**
+     * @hide
+     */
+    public SystemDataTransferRequest(int associationId, boolean syncAllPackages,
+            @Nullable List<String> permissionSyncPackages) {
+        mAssociationId = associationId;
+        mPermissionSyncAllPackages = syncAllPackages;
+        mPermissionSyncPackages = permissionSyncPackages;
+    }
+
+    public int getAssociationId() {
+        return mAssociationId;
+    }
+
+    @NonNull
+    public boolean isPermissionSyncAllPackages() {
+        return mPermissionSyncAllPackages;
+    }
+
+    @NonNull
+    public List<String> getPermissionSyncPackages() {
+        return mPermissionSyncPackages;
+    }
+
+    /**
+     * A builder for {@link SystemDataTransferRequest}.
+     *
+     * <p>You have to call one of the below methods to create a valid request</p>
+     * <br>1. {@link #setPermissionSyncAllPackages()}
+     * <br>2. {@link #setPermissionSyncPackages(List)}
+     */
+    public static final class Builder extends OneTimeUseBuilder<SystemDataTransferRequest> {
+
+        private final int mAssociationId;
+        private boolean mPermissionSyncAllPackages;
+        private List<String> mPermissionSyncPackages = new ArrayList<>();
+
+        public Builder(int associationId) {
+            mAssociationId = associationId;
+        }
+
+        /**
+         * Call to sync permissions for all the packages. You can optionally call
+         * {@link #setPermissionSyncPackages(List)} to specify the packages to sync permissions.
+         *
+         * <p>The system will only sync permissions that are explicitly granted by the user.</p>
+         *
+         * <p>If a permission is granted or revoked by the system or a policy, even if the user has
+         * explicitly granted or revoked the permission earlier, the permission will be ignored.</p>
+         *
+         * <p>If a system or policy granted or revoked permission is granted or revoked by the user
+         * later, the permission will be ignored.</p>
+         *
+         * @see #setPermissionSyncPackages(List)
+         *
+         * @return the builder
+         */
+        @NonNull
+        public Builder setPermissionSyncAllPackages() {
+            mPermissionSyncAllPackages = true;
+            return this;
+        }
+
+        /**
+         * Set a list of packages to sync permissions. You can optionally call
+         * {@link #setPermissionSyncAllPackages()} to sync permissions for all the packages.
+         *
+         * @see #setPermissionSyncAllPackages()
+         *
+         * @param permissionSyncPackages packages to sync permissions
+         * @return builder
+         */
+        @NonNull
+        public Builder setPermissionSyncPackages(@NonNull List<String> permissionSyncPackages) {
+            mPermissionSyncPackages = permissionSyncPackages;
+            return this;
+        }
+
+        @Override
+        @NonNull
+        public SystemDataTransferRequest build() {
+            return new SystemDataTransferRequest(mAssociationId, mPermissionSyncAllPackages,
+                    mPermissionSyncPackages);
+        }
+    }
+
+    SystemDataTransferRequest(Parcel in) {
+        mAssociationId = in.readInt();
+        mPermissionSyncAllPackages = in.readBoolean();
+        mPermissionSyncPackages = Arrays.asList(in.createString8Array());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mAssociationId);
+        dest.writeBoolean(mPermissionSyncAllPackages);
+        dest.writeString8Array(mPermissionSyncPackages.toArray(new String[0]));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<SystemDataTransferRequest> CREATOR =
+            new Creator<SystemDataTransferRequest>() {
+                @Override
+                public SystemDataTransferRequest createFromParcel(Parcel in) {
+                    return new SystemDataTransferRequest(in);
+                }
+
+                @Override
+                public SystemDataTransferRequest[] newArray(int size) {
+                    return new SystemDataTransferRequest[size];
+                }
+            };
+}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 82ad150..339e9a2 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -16,12 +16,15 @@
 
 package android.companion.virtual;
 
+import android.app.PendingIntent;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
 import android.hardware.input.VirtualMouseScrollEvent;
 import android.hardware.input.VirtualTouchEvent;
+import android.os.ResultReceiver;
 
 /**
  * Interface for a virtual device.
@@ -41,6 +44,7 @@
      * Closes the virtual device and frees all associated resources.
      */
     void close();
+
     void createVirtualKeyboard(
             int displayId,
             String inputDeviceName,
@@ -66,4 +70,11 @@
     boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
     boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
     boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
+
+    /**
+     * Launches a pending intent on the given display that is owned by this virtual device.
+     */
+    void launchPendingIntent(
+            int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
+    PointF getCursorPosition(IBinder token);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
new file mode 100644
index 0000000..53af4c5
--- /dev/null
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+import android.content.ComponentName;
+
+/**
+ * Interface to listen for activity changes in a virtual device.
+ *
+ * @hide
+ */
+interface IVirtualDeviceActivityListener {
+
+    /**
+     * Called when the top activity is changed.
+     *
+     * @param displayId The display ID on which the activity change happened.
+     * @param topActivity The component name of the top activity.
+     */
+    void onTopActivityChanged(int displayId, in ComponentName topActivity);
+
+    /**
+     * Called when the display becomes empty (e.g. if the user hits back on the last
+     * activity of the root task).
+     *
+     * @param displayId The display ID that became empty.
+     */
+    void onDisplayEmpty(int displayId);
+}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 2dfa202..b7f826a 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -17,6 +17,8 @@
 package android.companion.virtual;
 
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.VirtualDeviceParams;
 
 /**
  * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
@@ -33,6 +35,10 @@
      *   that this belongs to the calling UID.
      * @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
      *   CDM. Virtual devices must have a corresponding association with CDM in order to be created.
+     * @param params The parameters for creating this virtual device. See {@link
+     *   VirtualDeviceManager.VirtualDeviceParams}.
      */
-    IVirtualDevice createVirtualDevice(in IBinder token, String packageName, int associationId);
+    IVirtualDevice createVirtualDevice(
+            in IBinder token, String packageName, int associationId,
+            in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener);
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index bace45b..64f16ac 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -16,14 +16,16 @@
 
 package android.companion.virtual;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.Activity;
+import android.app.PendingIntent;
 import android.companion.AssociationInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Point;
 import android.hardware.display.DisplayManager;
@@ -33,15 +35,16 @@
 import android.hardware.input.VirtualMouse;
 import android.hardware.input.VirtualTouchscreen;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.ArrayMap;
 import android.view.Surface;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.util.concurrent.Executor;
 
 /**
  * System level service for managing virtual devices.
@@ -55,23 +58,6 @@
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "VirtualDeviceManager";
 
-    /** @hide */
-    @IntDef(prefix = "DISPLAY_FLAG_",
-            flag = true,
-            value = {DISPLAY_FLAG_TRUSTED})
-    @Retention(RetentionPolicy.SOURCE)
-    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
-    public @interface DisplayFlags {}
-
-    /**
-     * Indicates that the display is trusted to show system decorations and receive inputs without
-     * users' touch.
-     *
-     * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
-     * @hide  // TODO(b/194949534): Unhide this API
-     */
-    public static final int DISPLAY_FLAG_TRUSTED = 1;
-
     private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
             DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
                     | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
@@ -96,16 +82,14 @@
      * @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
      *   Companion Device Manager. Virtual devices must have a corresponding association with CDM in
      *   order to be created.
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Nullable
-    public VirtualDevice createVirtualDevice(int associationId) {
-        // TODO(b/194949534): Unhide this API
+    public VirtualDevice createVirtualDevice(
+            int associationId,
+            @NonNull VirtualDeviceParams params) {
         try {
-            IVirtualDevice virtualDevice = mService.createVirtualDevice(
-                    new Binder(), mContext.getPackageName(), associationId);
-            return new VirtualDevice(mContext, virtualDevice);
+            return new VirtualDevice(mService, mContext, associationId, params);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -122,15 +106,99 @@
 
         private final Context mContext;
         private final IVirtualDevice mVirtualDevice;
+        private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
+                new ArrayMap<>();
+        private final IVirtualDeviceActivityListener mActivityListenerBinder =
+                new IVirtualDeviceActivityListener.Stub() {
 
-        private VirtualDevice(Context context, IVirtualDevice virtualDevice) {
+                    @Override
+                    public void onTopActivityChanged(int displayId, ComponentName topActivity) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            for (int i = 0; i < mActivityListeners.size(); i++) {
+                                mActivityListeners.valueAt(i)
+                                        .onTopActivityChanged(displayId, topActivity);
+                            }
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+
+                    @Override
+                    public void onDisplayEmpty(int displayId) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            for (int i = 0; i < mActivityListeners.size(); i++) {
+                                mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
+                            }
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                };
+
+        private VirtualDevice(
+                IVirtualDeviceManager service,
+                Context context,
+                int associationId,
+                VirtualDeviceParams params) throws RemoteException {
             mContext = context.getApplicationContext();
-            mVirtualDevice = virtualDevice;
+            mVirtualDevice = service.createVirtualDevice(
+                    new Binder(),
+                    mContext.getPackageName(),
+                    associationId,
+                    params,
+                    mActivityListenerBinder);
+        }
+
+        /**
+         * Launches a given pending intent on the give display ID.
+         *
+         * @param displayId The display to launch the pending intent on. This display must be
+         *   created from this virtual device.
+         * @param pendingIntent The pending intent to be launched. If the intent is an activity
+         *   intent, the activity will be started on the virtual display using
+         *   {@link android.app.ActivityOptions#setLaunchDisplayId}. If the intent is a service or
+         *   broadcast intent, an attempt will be made to catch activities started as a result of
+         *   sending the pending intent and move them to the given display.
+         * @param executor The executor to run {@code launchCallback} on.
+         * @param launchCallback Callback that is called when the pending intent launching is
+         *   complete.
+         *
+         * @hide
+         */
+        public void launchPendingIntent(
+                int displayId,
+                @NonNull PendingIntent pendingIntent,
+                @NonNull Executor executor,
+                @NonNull LaunchCallback launchCallback) {
+            try {
+                mVirtualDevice.launchPendingIntent(
+                        displayId,
+                        pendingIntent,
+                        new ResultReceiver(new Handler(Looper.getMainLooper())) {
+                            @Override
+                            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                                super.onReceiveResult(resultCode, resultData);
+                                executor.execute(() -> {
+                                    if (resultCode == Activity.RESULT_OK) {
+                                        launchCallback.onLaunchSuccess();
+                                    } else {
+                                        launchCallback.onLaunchFailed();
+                                    }
+                                });
+                            }
+                        });
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
         }
 
         /**
          * Creates a virtual display for this virtual device. All displays created on the same
-         * device belongs to the same display group.
+         * device belongs to the same display group. Requires the ADD_TRUSTED_DISPLAY permission
+         * to create a virtual display which is not in the default DisplayGroup, and to create
+         * trusted displays.
          *
          * @param width The width of the virtual display in pixels, must be greater than 0.
          * @param height The height of the virtual display in pixels, must be greater than 0.
@@ -138,7 +206,12 @@
          * @param surface The surface to which the content of the virtual display should
          * be rendered, or null if there is none initially. The surface can also be set later using
          * {@link VirtualDisplay#setSurface(Surface)}.
-         * @param flags Either 0, or {@link #DISPLAY_FLAG_TRUSTED}.
+         * @param flags A combination of virtual display flags accepted by
+         * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
+         * automatically set for all virtual devices:
+         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
+         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+         * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @param handler The handler on which the listener should be invoked, or null
          * if the listener should be invoked on the calling thread's looper.
@@ -146,9 +219,7 @@
          * not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
-         * @hide
          */
-        // TODO(b/194949534): Unhide this API
         // Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a
         // handler
         @SuppressLint("ExecutorRegistration")
@@ -158,7 +229,7 @@
                 int height,
                 int densityDpi,
                 @Nullable Surface surface,
-                @DisplayFlags int flags,
+                int flags,
                 @Nullable Handler handler,
                 @Nullable VirtualDisplay.Callback callback) {
             // TODO(b/205343547): Handle display groups properly instead of creating a new display
@@ -197,7 +268,6 @@
          * @param inputDeviceName the name to call this input device
          * @param vendorId the vendor id
          * @param productId the product id
-         * @hide
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -224,7 +294,6 @@
          * @param inputDeviceName the name to call this input device
          * @param vendorId the vendor id
          * @param productId the product id
-         * @hide
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -251,7 +320,6 @@
          * @param inputDeviceName the name to call this input device
          * @param vendorId the vendor id
          * @param productId the product id
-         * @hide
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -273,12 +341,14 @@
             }
         }
 
-        private int getVirtualDisplayFlags(@DisplayFlags int flags) {
-            int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
-            if ((flags & DISPLAY_FLAG_TRUSTED) != 0) {
-                virtualDisplayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
-            }
-            return virtualDisplayFlags;
+        /**
+         * Returns the display flags that should be added to a particular virtual display.
+         * Additional device-level flags from {@link
+         * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
+         * be added by DisplayManagerService.
+         */
+        private int getVirtualDisplayFlags(int flags) {
+            return DEFAULT_VIRTUAL_DISPLAY_FLAGS | flags;
         }
 
         private String getVirtualDisplayName() {
@@ -292,5 +362,110 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+
+        /**
+         * Adds an activity listener to listen for events such as top activity change or virtual
+         * display task stack became empty.
+         *
+         * @param listener The listener to add.
+         * @see #removeActivityListener(ActivityListener)
+         * @hide
+         */
+        // TODO(b/194949534): Unhide this API
+        public void addActivityListener(@NonNull ActivityListener listener) {
+            addActivityListener(listener, mContext.getMainExecutor());
+        }
+
+        /**
+         * Adds an activity listener to listen for events such as top activity change or virtual
+         * display task stack became empty.
+         *
+         * @param listener The listener to add.
+         * @param executor The executor where the callback is executed on.
+         * @see #removeActivityListener(ActivityListener)
+         * @hide
+         */
+        // TODO(b/194949534): Unhide this API
+        public void addActivityListener(
+                @NonNull ActivityListener listener, @NonNull Executor executor) {
+            mActivityListeners.put(listener, new ActivityListenerDelegate(listener, executor));
+        }
+
+        /**
+         * Removes an activity listener previously added with
+         * {@link #addActivityListener}.
+         *
+         * @param listener The listener to remove.
+         * @see #addActivityListener(ActivityListener, Executor)
+         * @hide
+         */
+        // TODO(b/194949534): Unhide this API
+        public void removeActivityListener(@NonNull ActivityListener listener) {
+            mActivityListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Callback for launching pending intents on the virtual device.
+     *
+     * @hide
+     */
+    // TODO(b/194949534): Unhide this API
+    public interface LaunchCallback {
+        /**
+         * Called when the pending intent launched successfully.
+         */
+        void onLaunchSuccess();
+
+        /**
+         * Called when the pending intent failed to launch.
+         */
+        void onLaunchFailed();
+    }
+
+    /**
+     * Listener for activity changes in this virtual device.
+     *
+     * @hide
+     */
+    // TODO(b/194949534): Unhide this API
+    public interface ActivityListener {
+
+        /**
+         * Called when the top activity is changed.
+         *
+         * @param displayId The display ID on which the activity change happened.
+         * @param topActivity The component name of the top activity.
+         */
+        void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity);
+
+        /**
+         * Called when the display becomes empty (e.g. if the user hits back on the last
+         * activity of the root task).
+         *
+         * @param displayId The display ID that became empty.
+         */
+        void onDisplayEmpty(int displayId);
+    }
+
+    /**
+     * A wrapper for {@link ActivityListener} that executes callbacks on the given executor.
+     */
+    private static class ActivityListenerDelegate {
+        @NonNull private final ActivityListener mActivityListener;
+        @NonNull private final Executor mExecutor;
+
+        ActivityListenerDelegate(@NonNull ActivityListener listener, @NonNull Executor executor) {
+            mActivityListener = listener;
+            mExecutor = executor;
+        }
+
+        public void onTopActivityChanged(int displayId, ComponentName topActivity) {
+            mExecutor.execute(() -> mActivityListener.onTopActivityChanged(displayId, topActivity));
+        }
+
+        public void onDisplayEmpty(int displayId) {
+            mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId));
+        }
     }
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.aidl b/core/java/android/companion/virtual/VirtualDeviceParams.aidl
new file mode 100644
index 0000000..9b3974a
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+parcelable VirtualDeviceParams;
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
new file mode 100644
index 0000000..2ddfeb4
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.util.ArraySet;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Params that can be configured when creating virtual devices.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualDeviceParams implements Parcelable {
+
+    /** @hide */
+    @IntDef(prefix = "LOCK_STATE_",
+            value = {LOCK_STATE_ALWAYS_LOCKED, LOCK_STATE_ALWAYS_UNLOCKED})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    public @interface LockState {}
+
+    /**
+     * Indicates that the lock state of the virtual device should be always locked.
+     */
+    public static final int LOCK_STATE_ALWAYS_LOCKED = 0;
+
+    /**
+     * Indicates that the lock state of the virtual device should be always unlocked.
+     */
+    public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
+
+    private final int mLockState;
+    private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
+    @Nullable private final ArraySet<ComponentName> mAllowedActivities;
+    @Nullable private final ArraySet<ComponentName> mBlockedActivities;
+
+    private VirtualDeviceParams(
+            @LockState int lockState,
+            @NonNull Set<UserHandle> usersWithMatchingAccounts,
+            @Nullable Set<ComponentName> allowedActivities,
+            @Nullable Set<ComponentName> blockedActivities) {
+        mLockState = lockState;
+        mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
+        mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
+        mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+    }
+
+    @SuppressWarnings("unchecked")
+    private VirtualDeviceParams(Parcel parcel) {
+        mLockState = parcel.readInt();
+        mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null);
+        mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
+        mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
+    }
+
+    /**
+     * Returns the lock state of the virtual device.
+     */
+    @LockState
+    public int getLockState() {
+        return mLockState;
+    }
+
+    /**
+     * Returns the user handles with matching managed accounts on the remote device to which
+     * this virtual device is streaming.
+     *
+     * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+     */
+    @NonNull
+    public Set<UserHandle> getUsersWithMatchingAccounts() {
+        return Collections.unmodifiableSet(mUsersWithMatchingAccounts);
+    }
+
+    /**
+     * Returns the set of activities allowed to be streamed, or {@code null} if this is not set.
+     *
+     * @see Builder#setAllowedActivities(Set)
+     * @hide  // TODO(b/194949534): Unhide this API
+     */
+    @Nullable
+    public Set<ComponentName> getAllowedActivities() {
+        if (mAllowedActivities == null) {
+            return null;
+        }
+        return Collections.unmodifiableSet(mAllowedActivities);
+    }
+
+    /**
+     * Returns the set of activities that are blocked from streaming, or {@code null} if this is not
+     * set.
+     *
+     * @see Builder#setBlockedActivities(Set)
+     * @hide  // TODO(b/194949534): Unhide this API
+     */
+    @Nullable
+    public Set<ComponentName> getBlockedActivities() {
+        if (mBlockedActivities == null) {
+            return null;
+        }
+        return Collections.unmodifiableSet(mBlockedActivities);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mLockState);
+        dest.writeArraySet(mUsersWithMatchingAccounts);
+        dest.writeArraySet(mAllowedActivities);
+        dest.writeArraySet(mBlockedActivities);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof VirtualDeviceParams)) {
+            return false;
+        }
+        VirtualDeviceParams that = (VirtualDeviceParams) o;
+        return mLockState == that.mLockState
+                && mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
+                && Objects.equals(mAllowedActivities, that.mAllowedActivities)
+                && Objects.equals(mBlockedActivities, that.mBlockedActivities);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLockState, mUsersWithMatchingAccounts);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "VirtualDeviceParams("
+                + " mLockState=" + mLockState
+                + " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts
+                + " mAllowedActivities=" + mAllowedActivities
+                + " mBlockedActivities=" + mBlockedActivities
+                + ")";
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
+            new Parcelable.Creator<VirtualDeviceParams>() {
+                public VirtualDeviceParams createFromParcel(Parcel in) {
+                    return new VirtualDeviceParams(in);
+                }
+
+                public VirtualDeviceParams[] newArray(int size) {
+                    return new VirtualDeviceParams[size];
+                }
+            };
+
+    /**
+     * Builder for {@link VirtualDeviceParams}.
+     */
+    public static final class Builder {
+
+        private @LockState int mLockState = LOCK_STATE_ALWAYS_LOCKED;
+        private Set<UserHandle> mUsersWithMatchingAccounts;
+        @Nullable private Set<ComponentName> mBlockedActivities;
+        @Nullable private Set<ComponentName> mAllowedActivities;
+
+        /**
+         * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
+         * is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
+         * The default is {@link #LOCK_STATE_ALWAYS_LOCKED}.
+         *
+         * @param lockState The lock state, either {@link #LOCK_STATE_ALWAYS_LOCKED} or
+         *   {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
+         */
+        @RequiresPermission(value = ADD_ALWAYS_UNLOCKED_DISPLAY, conditional = true)
+        @NonNull
+        public Builder setLockState(@LockState int lockState) {
+            mLockState = lockState;
+            return this;
+        }
+
+        /**
+         * Sets the user handles with matching managed accounts on the remote device to which
+         * this virtual device is streaming. The caller is responsible for verifying the presence
+         * and legitimacy of a matching managed account on the remote device.
+         *
+         * <p>If the app streaming policy is
+         * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+         * NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY}, activities not in
+         * {@code usersWithMatchingAccounts} will be blocked from starting.
+         *
+         * <p> If {@code usersWithMatchingAccounts} is empty (the default), streaming is allowed
+         * only if there is no device policy, or if the nearby streaming policy is
+         * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_ENABLED
+         * NEARBY_STREAMING_ENABLED}.
+         *
+         * @param usersWithMatchingAccounts A set of user handles with matching managed
+         *   accounts on the remote device this is streaming to.
+         *
+         * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+         */
+        @NonNull
+        public Builder setUsersWithMatchingAccounts(
+                @NonNull Set<UserHandle> usersWithMatchingAccounts) {
+            mUsersWithMatchingAccounts = usersWithMatchingAccounts;
+            return this;
+        }
+
+        /**
+         * Sets the activities allowed to be launched in the virtual device. If
+         * {@code allowedActivities} is non-null, all activities other than the ones in the set will
+         * be blocked from launching.
+         *
+         * <p>{@code allowedActivities} and the set in {@link #setBlockedActivities(Set)} cannot
+         * both be non-null at the same time.
+         *
+         * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been set to a
+         *   non-null value.
+         *
+         * @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
+         *   in the virtual device.
+         * @hide  // TODO(b/194949534): Unhide this API
+         */
+        public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
+            if (mBlockedActivities != null && allowedActivities != null) {
+                throw new IllegalArgumentException(
+                        "Allowed activities and Blocked activities cannot both be set.");
+            }
+            mAllowedActivities = allowedActivities;
+            return this;
+        }
+
+        /**
+         * Sets the activities blocked from launching in the virtual device. If the {@code
+         * blockedActivities} is non-null, activities in the set are blocked from launching in the
+         * virtual device.
+         *
+         * {@code blockedActivities} and the set in {@link #setAllowedActivities(Set)} cannot both
+         * be non-null at the same time.
+         *
+         * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been set to a
+         *   non-null value.
+         *
+         * @param blockedActivities A set of {@link ComponentName} to be blocked launching from
+         *   virtual device.
+         * @hide  // TODO(b/194949534): Unhide this API
+         */
+        public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
+            if (mAllowedActivities != null && blockedActivities != null) {
+                throw new IllegalArgumentException(
+                        "Allowed activities and Blocked activities cannot both be set.");
+            }
+            mBlockedActivities = blockedActivities;
+            return this;
+        }
+
+        /**
+         * Builds the {@link VirtualDeviceParams} instance.
+         */
+        @NonNull
+        public VirtualDeviceParams build() {
+            if (mUsersWithMatchingAccounts == null) {
+                mUsersWithMatchingAccounts = Collections.emptySet();
+            }
+            if (mAllowedActivities != null && mBlockedActivities != null) {
+                // Should never reach here because the setters block this as well.
+                throw new IllegalStateException(
+                        "Allowed activities and Blocked activities cannot both be set.");
+            }
+            return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts,
+                    mAllowedActivities, mBlockedActivities);
+        }
+    }
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c714f507..b4f2302 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -48,10 +48,13 @@
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.permission.PermissionCheckerManager;
+import android.provider.MediaStore;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -134,9 +137,18 @@
     private boolean mExported;
     private boolean mNoPerms;
     private boolean mSingleUser;
+    private SparseBooleanArray mUsersRedirectedToOwner = new SparseBooleanArray();
 
     private ThreadLocal<AttributionSource> mCallingAttributionSource;
 
+    /**
+     * @hide
+     */
+    public static boolean isAuthorityRedirectedForCloneProfile(String authority) {
+        // For now, only MediaProvider gets redirected.
+        return MediaStore.AUTHORITY.equals(authority);
+    }
+
     private Transport mTransport = new Transport();
 
     /**
@@ -726,13 +738,47 @@
     }
 
     boolean checkUser(int pid, int uid, Context context) {
-        if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+        int callingUserId = UserHandle.getUserId(uid);
+
+        if (callingUserId == context.getUserId() || mSingleUser) {
             return true;
         }
-        return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED
+        if (context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+                == PackageManager.PERMISSION_GRANTED
                 || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED;
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
+
+        if (isAuthorityRedirectedForCloneProfile(mAuthority)) {
+            if (mUsersRedirectedToOwner.indexOfKey(callingUserId) >= 0) {
+                return mUsersRedirectedToOwner.get(callingUserId);
+            }
+
+            // Haven't seen this user yet, look it up
+            try {
+                UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
+                Context callingUserContext = mContext.createPackageContextAsUser("system",
+                        0, callingUser);
+                UserManager um = callingUserContext.getSystemService(UserManager.class);
+
+                if (um != null && um.isCloneProfile()) {
+                    UserHandle parent = um.getProfileParent(callingUser);
+
+                    if (parent != null && parent.equals(context.getUser())) {
+                        mUsersRedirectedToOwner.put(callingUser.getIdentifier(), true);
+                        return true;
+                    }
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // ignore
+            }
+
+            mUsersRedirectedToOwner.put(UserHandle.getUserId(uid), false);
+            return false;
+        }
+
+        return false;
     }
 
     /**
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 01d231c..7b9d37e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2670,6 +2670,9 @@
      * {@link ContentObserver#onChange(boolean, Collection, int, UserHandle)} should be
      * overwritten to get the corresponding {@link UserHandle} for that notification.
      *
+     * <p> If you don't hold the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+     * permission, you can register the {@link ContentObserver} only for current user.
+     *
      * @param uri                  The URI to watch for changes. This can be a specific row URI,
      *                             or a base URI for a whole class of content.
      * @param notifyForDescendants When false, the observer will be notified
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 120a0a6..ce2efcf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -41,6 +41,7 @@
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.VrManager;
+import android.app.ambientcontext.AmbientContextManager;
 import android.app.people.PeopleManager;
 import android.app.time.TimeManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -94,8 +95,11 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Interface to global information about an application environment.  This is
@@ -3598,10 +3602,18 @@
      * Binds to a service in the given {@code user} in the same manner as
      * {@link #bindService(Intent, ServiceConnection, int)}.
      *
-     * <p>If the given {@code user} is in the same profile group and the target package is the
-     * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is
-     * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS}
-     * for interacting with other users.
+     * <p>Requires that one of the following conditions are met:
+     * <ul>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}</li>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is the same
+     *      package as the {@code service} (determined by its component's package) and the Android
+     *      version is at least {@link android.os.Build.VERSION_CODES#S}</li>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is in same
+     *      profile group as the given {@code user}</li>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} and is in same
+     *      profile group as the given {@code user} and is the same package as the {@code service}
+     *      </li>
+     * </ul>
      *
      * @param service Identifies the service to connect to.  The Intent must
      *      specify an explicit component name.
@@ -3623,8 +3635,9 @@
     @SuppressWarnings("unused")
     @RequiresPermission(anyOf = {
             android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
             android.Manifest.permission.INTERACT_ACROSS_PROFILES
-    })
+            }, conditional = true)
     public boolean bindServiceAsUser(
             @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags,
             @NonNull UserHandle user) {
@@ -3637,7 +3650,11 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES
+            }, conditional = true)
     @UnsupportedAppUsage(trackingBug = 136728678)
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             Handler handler, UserHandle user) {
@@ -3813,7 +3830,7 @@
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
-            TV_IAPP_SERVICE,
+            TV_INTERACTIVE_APP_SERVICE,
             TV_INPUT_SERVICE,
             //@hide: TV_TUNER_RESOURCE_MGR_SERVICE,
             //@hide: NETWORK_SCORE_SERVICE,
@@ -5340,13 +5357,13 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link android.media.tv.interactive.TvIAppManager} for interacting with TV interactive
-     * applications (TV iApp) on the device.
+     * {@link android.media.tv.interactive.TvInteractiveAppManager} for interacting with TV
+     * interactive applications on the device.
      *
      * @see #getSystemService(String)
-     * @see android.media.tv.interactive.TvIAppManager
+     * @see android.media.tv.interactive.TvInteractiveAppManager
      */
-    public static final String TV_IAPP_SERVICE = "tv_iapp";
+    public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
@@ -5879,17 +5896,6 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link android.app.communal.CommunalManager} for interacting with the global system state.
-     *
-     * @see #getSystemService(String)
-     * @see android.app.communal.CommunalManager
-     * @hide
-     */
-    @SystemApi
-    public static final String COMMUNAL_SERVICE = "communal";
-
-    /**
-     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.LocaleManager}.
      *
      * @see #getSystemService(String)
@@ -5922,9 +5928,21 @@
      * @see android.nearby.NearbyManager
      * @hide
      */
+    @SystemApi
     public static final String NEARBY_SERVICE = "nearby";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.ambientcontext.AmbientContextManager}.
+     *
+     * @see #getSystemService(String)
+     * @see AmbientContextManager
+     * @hide
+     */
+    @SystemApi
+    public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
@@ -6406,6 +6424,43 @@
             @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags,
             @Nullable String message);
 
+
+    /**
+     * Triggers the asynchronous revocation of a permission.
+     *
+     * @param permName The name of the permission to be revoked.
+     * @see #revokeOwnPermissionsOnKill(Collection)
+     */
+    public void revokeOwnPermissionOnKill(@NonNull String permName) {
+        revokeOwnPermissionsOnKill(Collections.singletonList(permName));
+    }
+
+    /**
+     * Triggers the revocation of one or more permissions for the calling package. A package is only
+     * able to revoke a permission under the following conditions:
+     * <ul>
+     * <li>Each permission in {@code permissions} must be granted to the calling package.
+     * <li>Each permission in {@code permissions} must be a runtime permission.
+     * </ul>
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. The revocation happens asynchronously and kills all processes running in the
+     * calling UID. It will be triggered once it is safe to do so. In particular, it will not be
+     * triggered as long as the package remains in the foreground, or has any active manifest
+     * components (e.g. when another app is accessing a content provider in the package).
+     * <p>
+     * If you want to revoke the permissions right away, you could call {@code System.exit()}, but
+     * this could affect other apps that are accessing your app at the moment. For example, apps
+     * accessing a content provider in your app will all crash.
+     *
+     * @param permissions Collection of permissions to be revoked.
+     * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)
+     * @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)
+     */
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
     /** @hide */
     @IntDef(flag = true, prefix = { "CONTEXT_" }, value = {
             CONTEXT_INCLUDE_CODE,
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3a02004..6ae768a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -53,6 +53,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -1015,6 +1016,11 @@
     }
 
     @Override
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+        mBase.revokeOwnPermissionsOnKill(permissions);
+    }
+
+    @Override
     public Context createPackageContext(String packageName, int flags)
         throws PackageManager.NameNotFoundException {
         return mBase.createPackageContext(packageName, flags);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index af84392..7f00bcb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2974,6 +2974,9 @@
      * Broadcast Action: A uid has been removed from the system.  The uid
      * number is stored in the extra data under {@link #EXTRA_UID}.
      *
+     * In certain instances, {@link #EXTRA_REPLACING} is set to true if the UID is not being
+     * fully removed.
+     *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      */
@@ -4391,9 +4394,9 @@
      * restored from (corresponds to {@link android.os.Build.VERSION#SDK_INT}). The first three
      * values are represented as strings, the fourth one as int.
      *
-     * <p>This broadcast is sent only for settings provider entries known to require special handling
-     * around restore time.  These entries are found in the BROADCAST_ON_RESTORE table within
-     * the provider's backup agent implementation.
+     * <p>This broadcast is sent only for settings provider entries known to require special
+     * handling around restore time to specific receivers. These entries are found in the
+     * BROADCAST_ON_RESTORE table within the provider's backup agent implementation.
      *
      * @see #EXTRA_SETTING_NAME
      * @see #EXTRA_SETTING_PREVIOUS_VALUE
@@ -4401,15 +4404,46 @@
      * @see #EXTRA_SETTING_RESTORED_FROM_SDK_INT
      * {@hide}
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
 
-    /** {@hide} */
+    /**
+     * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the name of the restored setting.
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_NAME = "setting_name";
-    /** {@hide} */
+
+    /**
+     * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry prior to the restore
+     * operation.
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
-    /** {@hide} */
+
+    /**
+     * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry being restored.
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
-    /** {@hide} */
+
+    /**
+     * Int intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the version of the SDK that the setting has been restored from (corresponds to
+     * {@link android.os.Build.VERSION#SDK_INT}).
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
 
     /**
@@ -8156,6 +8190,37 @@
                     hasIntentInfo = true;
                 }
                 break;
+                case "--ed": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    intent.putExtra(key, Double.valueOf(value));
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--eda": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    double[] list = new double[strings.length];
+                    for (int i = 0; i < strings.length; i++) {
+                        list[i] = Double.valueOf(strings[i]);
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--edal": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    ArrayList<Double> list = new ArrayList<>(strings.length);
+                    for (int i = 0; i < strings.length; i++) {
+                        list.add(Double.valueOf(strings[i]));
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
                 case "--esa": {
                     String key = cmd.getNextArgRequired();
                     String value = cmd.getNextArgRequired();
@@ -8401,25 +8466,30 @@
                 "    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]",
                 "    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]",
                 "    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]",
+                "    [--ed <EXTRA_KEY> <EXTRA_DOUBLE_VALUE> ...]",
                 "    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]",
                 "    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]",
                 "    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
-                "        (mutiple extras passed as Integer[])",
+                "        (multiple extras passed as Integer[])",
                 "    [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
-                "        (mutiple extras passed as List<Integer>)",
+                "        (multiple extras passed as List<Integer>)",
                 "    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
-                "        (mutiple extras passed as Long[])",
+                "        (multiple extras passed as Long[])",
                 "    [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
-                "        (mutiple extras passed as List<Long>)",
+                "        (multiple extras passed as List<Long>)",
                 "    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
-                "        (mutiple extras passed as Float[])",
+                "        (multiple extras passed as Float[])",
                 "    [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
-                "        (mutiple extras passed as List<Float>)",
+                "        (multiple extras passed as List<Float>)",
+                "    [--eda <EXTRA_KEY> <EXTRA_DOUBLE_VALUE>[,<EXTRA_DOUBLE_VALUE...]]",
+                "        (multiple extras passed as Double[])",
+                "    [--edal <EXTRA_KEY> <EXTRA_DOUBLE_VALUE>[,<EXTRA_DOUBLE_VALUE...]]",
+                "        (multiple extras passed as List<Double>)",
                 "    [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
-                "        (mutiple extras passed as String[]; to embed a comma into a string,",
+                "        (multiple extras passed as String[]; to embed a comma into a string,",
                 "         escape it using \"\\,\")",
                 "    [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
-                "        (mutiple extras passed as List<String>; to embed a comma into a string,",
+                "        (multiple extras passed as List<String>; to embed a comma into a string,",
                 "         escape it using \"\\,\")",
                 "    [-f <FLAG>]",
                 "    [--grant-read-uri-permission] [--grant-write-uri-permission]",
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 32827ae..b3435b1 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1182,7 +1182,7 @@
         public int match(Uri data, boolean wildcardSupported) {
             String host = data.getHost();
             if (host == null) {
-                if (wildcardSupported && mWild) {
+                if (wildcardSupported && mWild && mHost.isEmpty()) {
                     // special case, if no host is provided, but the Authority is wildcard, match
                     return MATCH_CATEGORY_HOST;
                 } else {
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 63fcb49..8fd41f2 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -505,7 +505,7 @@
         mChoiceValues = in.readStringArray();
         mCurrentValue = in.readString();
         mCurrentValues = in.readStringArray();
-        Parcelable[] parcelables = in.readParcelableArray(null);
+        Parcelable[] parcelables = in.readParcelableArray(null, RestrictionEntry.class);
         if (parcelables != null) {
             mRestrictions = new RestrictionEntry[parcelables.length];
             for (int i = 0; i < parcelables.length; i++) {
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 806091e2..8d9ef853 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -423,7 +423,7 @@
                 shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage,
                 disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras,
                 getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
-                disabledReason, persons, locusId, null);
+                disabledReason, persons, locusId, null, null);
         si.setImplicitRank(implicitRank);
         if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) {
             si.setRankChanged();
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 76b4e5c..9e9dd1e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1543,6 +1543,11 @@
     @Nullable
     private ArrayMap<String, String> mAppClassNamesByProcess;
 
+    /**
+     * Resource file providing the application's locales configuration.
+     */
+    private int localeConfigRes;
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -1660,6 +1665,10 @@
                 pw.println(prefix + "requestRawExternalStorageAccess="
                         + requestRawExternalStorageAccess);
             }
+            if (localeConfigRes != 0) {
+                pw.println(prefix + "localeConfigRes=0x"
+                        + Integer.toHexString(localeConfigRes));
+            }
         }
         pw.println(prefix + "createTimestamp=" + createTimestamp);
         super.dumpBack(pw, prefix);
@@ -1891,6 +1900,7 @@
         memtagMode = orig.memtagMode;
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
         requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
+        localeConfigRes = orig.localeConfigRes;
         createTimestamp = System.currentTimeMillis();
     }
 
@@ -1993,6 +2003,7 @@
                 dest.writeString(mAppClassNamesByProcess.valueAt(i));
             }
         }
+        dest.writeInt(localeConfigRes);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2088,6 +2099,7 @@
                 mAppClassNamesByProcess.put(source.readString(), source.readString());
             }
         }
+        localeConfigRes = source.readInt();
     }
 
     /**
@@ -2631,4 +2643,16 @@
         }
         return className;
     }
+
+    /** @hide */ public void setLocaleConfigRes(int value) { localeConfigRes = value; }
+
+    /**
+     * Return the resource id pointing to the resource file that provides the application's locales
+     * configuration.
+     *
+     * @hide
+     */
+    public int getLocaleConfigRes() {
+        return localeConfigRes;
+    }
 }
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index 7bade74..1e0deff 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -50,10 +50,12 @@
      */
     private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
 
-    private final List<T> mList;
+    private List<T> mList;
 
     private int mInlineCountLimit = Integer.MAX_VALUE;
 
+    private boolean mHasBeenParceled = false;
+
     public BaseParceledListSlice(List<T> list) {
         mList = list;
     }
@@ -151,9 +153,17 @@
      * Write this to another Parcel. Note that this discards the internal Parcel
      * and should not be used anymore. This is so we can pass this to a Binder
      * where we won't have a chance to call recycle on this.
+     *
+     * This method can only be called once per BaseParceledListSlice to ensure that
+     * the referenced list can be cleaned up before the recipient cleans up the
+     * Binder reference.
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        if (mHasBeenParceled) {
+            throw new IllegalStateException("Can't Parcel a ParceledListSlice more than once");
+        }
+        mHasBeenParceled = true;
         final int N = mList.size();
         final int callFlags = flags;
         dest.writeInt(N);
@@ -180,9 +190,17 @@
                             throws RemoteException {
                         if (code != FIRST_CALL_TRANSACTION) {
                             return super.onTransact(code, data, reply, flags);
+                        } else if (mList == null) {
+                            throw new IllegalArgumentException("Attempt to transfer null list, "
+                                    + "did transfer finish?");
                         }
                         int i = data.readInt();
-                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
+
+                        if (DEBUG) {
+                            Log.d(TAG, "Writing more @" + i + " of " + N + " to "
+                                    + Binder.getCallingPid() + ", sender=" + this);
+                        }
+
                         while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
                             reply.writeInt(1);
 
@@ -196,6 +214,9 @@
                         if (i < N) {
                             if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
                             reply.writeInt(0);
+                        } else {
+                            if (DEBUG) Log.d(TAG, "Transfer complete, clearing mList reference");
+                            mList = null;
                         }
                         return true;
                     }
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 48b634e..1e88758 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -15,6 +15,9 @@
  */
 package android.content.pm;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -22,6 +25,7 @@
 import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.AppOpsManager.Mode;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -180,6 +184,7 @@
      * {@link #startMainActivity}, this can start any activity of the caller package, not just
      * the main activity.
      * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
+     * or {@link android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES}
      * permission and both the caller and target user profiles must be in the same profile group.
      *
      * @param component The ComponentName of the activity to launch. It must be exported.
@@ -189,7 +194,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+            android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES})
     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
@@ -238,12 +245,24 @@
     public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
         verifyCanAccessUser(userHandle);
 
-        final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
-                ? R.string.managed_profile_label
-                : R.string.user_owner_label;
+        final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier());
+        final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(
+                getUpdatableProfileSwitchingLabelId(isManagedProfile),
+                () -> getDefaultProfileSwitchingLabel(isManagedProfile));
+    }
+
+    private String getUpdatableProfileSwitchingLabelId(boolean isManagedProfile) {
+        return isManagedProfile ? SWITCH_TO_WORK_LABEL : SWITCH_TO_PERSONAL_LABEL;
+    }
+
+    private String getDefaultProfileSwitchingLabel(boolean isManagedProfile) {
+        final int stringRes = isManagedProfile
+                ? R.string.managed_profile_label : R.string.user_owner_label;
         return mResources.getString(stringRes);
     }
 
+
     /**
      * Return a drawable that calling app can show to user for the semantic of profile switching --
      * launching its own activity in specified user profile. For example, it may return a briefcase
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 37fd3ff..cb8988e 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -38,6 +38,8 @@
 import android.os.UserHandle;
 import android.os.ParcelFileDescriptor;
 
+import com.android.internal.infra.AndroidFuture;
+
 import java.util.List;
 
 /**
@@ -73,6 +75,8 @@
 
     ParceledListSlice getShortcuts(String callingPackage, in ShortcutQueryWrapper query,
             in UserHandle user);
+    void getShortcutsAsync(String callingPackage, in ShortcutQueryWrapper query,
+            in UserHandle user, in AndroidFuture<List<ShortcutInfo>> cb);
     void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
             in UserHandle user);
     boolean startShortcut(String callingPackage, String packageName, String featureId, String id,
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 617d3ab..a0d348f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -69,6 +69,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.FileNotFoundException;
@@ -84,6 +85,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
 /**
@@ -440,6 +442,17 @@
         public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
 
         /**
+         * Includes shortcuts from persistence layer in the search result.
+         *
+         * <p>The caller should make the query on a worker thread since accessing persistence layer
+         * is considered asynchronous.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final int FLAG_GET_PERSISTED_DATA = 1 << 12;
+
+        /**
          * Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}.
          *
          * <p>The caller must have the system {@code ACCESS_SHORTCUTS} permission.
@@ -459,6 +472,7 @@
                 FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
                 FLAG_GET_KEY_FIELDS_ONLY,
                 FLAG_GET_PERSONS_DATA,
+                FLAG_GET_PERSISTED_DATA
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface QueryFlags {}
@@ -1137,6 +1151,9 @@
             @NonNull UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
+            if ((query.mQueryFlags & ShortcutQuery.FLAG_GET_PERSISTED_DATA) != 0) {
+                return getShortcutsBlocked(query, user);
+            }
             // Note this is the only case we need to update the disabled message for shortcuts
             // that weren't restored.
             // The restore problem messages are only shown by the user, and publishers will never
@@ -1144,13 +1161,29 @@
             // changed callback, but that only returns shortcuts with the "key" information, so
             // that won't return disabled message.
             return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
-                    new ShortcutQueryWrapper(query), user)
-                    .getList());
+                                new ShortcutQueryWrapper(query), user)
+                        .getList());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    private List<ShortcutInfo> getShortcutsBlocked(@NonNull ShortcutQuery query,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
+        future.thenApply(this::maybeUpdateDisabledMessage);
+        try {
+            mService.getShortcutsAsync(mContext.getPackageName(),
+                            new ShortcutQueryWrapper(query), user, future);
+            return future.get();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
      */
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 495100b..08b07a7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2367,42 +2367,72 @@
         private static final int[] NO_SESSIONS = {};
 
         /** @hide */
-        @IntDef(prefix = { "STAGED_SESSION_" }, value = {
-                STAGED_SESSION_NO_ERROR,
-                STAGED_SESSION_VERIFICATION_FAILED,
-                STAGED_SESSION_ACTIVATION_FAILED,
-                STAGED_SESSION_UNKNOWN,
-                STAGED_SESSION_CONFLICT})
+        @IntDef(prefix = { "SESSION_" }, value = {
+                SESSION_NO_ERROR,
+                SESSION_VERIFICATION_FAILED,
+                SESSION_ACTIVATION_FAILED,
+                SESSION_UNKNOWN_ERROR,
+                SESSION_CONFLICT})
         @Retention(RetentionPolicy.SOURCE)
         public @interface SessionErrorCode {}
         /**
-         * Constant indicating that no error occurred during the preparation or the activation of
-         * this staged session.
+         * @deprecated use {@link #SESSION_NO_ERROR}.
          */
+        @Deprecated
         public static final int STAGED_SESSION_NO_ERROR = 0;
 
         /**
-         * Constant indicating that an error occurred during the verification phase (pre-reboot) of
-         * this staged session.
+         * @deprecated use {@link #SESSION_VERIFICATION_FAILED}.
          */
+        @Deprecated
         public static final int STAGED_SESSION_VERIFICATION_FAILED = 1;
 
         /**
-         * Constant indicating that an error occurred during the activation phase (post-reboot) of
-         * this staged session.
+         * @deprecated use {@link #SESSION_ACTIVATION_FAILED}.
          */
+        @Deprecated
         public static final int STAGED_SESSION_ACTIVATION_FAILED = 2;
 
         /**
-         * Constant indicating that an unknown error occurred while processing this staged session.
+         * @deprecated use {@link #SESSION_UNKNOWN_ERROR}.
          */
+        @Deprecated
         public static final int STAGED_SESSION_UNKNOWN = 3;
 
         /**
-         * Constant indicating that the session was in conflict with another staged session and had
+         * @deprecated use {@link #SESSION_CONFLICT}.
+         */
+        @Deprecated
+        public static final int STAGED_SESSION_CONFLICT = 4;
+
+        /**
+         * Constant indicating that no error occurred during the preparation or the activation of
+         * this session.
+         */
+        public static final int SESSION_NO_ERROR = 0;
+
+        /**
+         * Constant indicating that an error occurred during the verification phase of
+         * this session.
+         */
+        public static final int SESSION_VERIFICATION_FAILED = 1;
+
+        /**
+         * Constant indicating that an error occurred during the activation phase of
+         * this session.
+         */
+        public static final int SESSION_ACTIVATION_FAILED = 2;
+
+        /**
+         * Constant indicating that an unknown error occurred while processing this session.
+         */
+        public static final int SESSION_UNKNOWN_ERROR = 3;
+
+        /**
+         * Constant indicating that the session was in conflict with another session and had
          * to be sacrificed for resolution.
          */
-        public static final int STAGED_SESSION_CONFLICT = 4;
+        public static final int SESSION_CONFLICT = 4;
 
         private static String userActionToString(int requireUserAction) {
             switch(requireUserAction) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9a7aeef..1021d3e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -50,6 +50,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 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;
@@ -88,6 +89,7 @@
 
 import dalvik.system.VMRuntime;
 
+import java.io.File;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.cert.Certificate;
@@ -145,6 +147,20 @@
             "android.media.PROPERTY_MEDIA_CAPABILITIES";
 
     /**
+     * Application level property that an app can specify to opt-out from having private data
+     * directories both on the internal and external storages.
+     *
+     * <p>Changing the value of this property during app update is not supported, and such updates
+     * will be rejected.
+     *
+     * <p>This should only be set by platform apps that know what they are doing.
+     *
+     * @hide
+     */
+    public static final String PROPERTY_NO_APP_DATA_STORAGE =
+            "android.internal.PROPERTY_NO_APP_DATA_STORAGE";
+
+    /**
      * A property value set within the manifest.
      * <p>
      * The value of a property will only have a single type, as defined by
@@ -2720,6 +2736,8 @@
      * API shipped in Android 11.
      * <li><code>202101</code>: corresponds to the features included in the Identity Credential
      * API shipped in Android 12.
+     * <li><code>202201</code>: corresponds to the features included in the Identity Credential
+     * API shipped in Android 13.
      * </ul>
      */
     @SdkConstant(SdkConstantType.FEATURE)
@@ -4050,6 +4068,23 @@
     @TestApi
     public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
 
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports dream overlay feature, which is an informational layer shown on top of dreams.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_DREAM_OVERLAY = "android.software.dream_overlay";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports window magnification.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WINDOW_MAGNIFICATION =
+            "android.software.window_magnification";
+
     /** @hide */
     public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
 
@@ -4207,6 +4242,17 @@
             "android.content.pm.action.REQUEST_PERMISSIONS";
 
     /**
+     * The action used to request that the user approve a permission request
+     * from the application. Sent from an application other than the one whose permissions
+     * will be granted. Can only be used by the system server.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER =
+            "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER";
+
+    /**
      * The names of the requested permissions.
      * <p>
      * <strong>Type:</strong> String[]
@@ -4314,8 +4360,9 @@
     public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT =  1 << 5;
 
     /**
-     * Permission flag: The permission has to be reviewed before any of
-     * the app components can run.
+     * Permission flag: If app targetSDK < M, then the permission has to be reviewed before any of
+     * the app components can run. If app targetSDK >= M, then the system might need to show a
+     * request dialog for this permission on behalf of an app.
      *
      * @hide
      */
@@ -7854,8 +7901,7 @@
     @Deprecated
     @Nullable
     public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
-        throw new UnsupportedOperationException(
-                "getPackageArchiveInfo() not implemented in subclass");
+        return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
     }
 
     /**
@@ -7864,8 +7910,29 @@
     @Nullable
     public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
             @NonNull PackageInfoFlags flags) {
-        throw new UnsupportedOperationException(
-                "getPackageArchiveInfo() not implemented in subclass");
+        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) {
+                PackageParser.collectCertificates(pkg, false /* skipVerify */);
+            }
+            return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
+                    FrameworkPackageUserState.DEFAULT);
+        } catch (PackageParser.PackageParserException e) {
+            return null;
+        }
     }
 
     /**
@@ -10173,16 +10240,15 @@
                     16, PermissionManager.CACHE_KEY_PACKAGE_INFO,
                     "getApplicationInfo") {
                 @Override
-                protected ApplicationInfo recompute(ApplicationInfoQuery query) {
+                public ApplicationInfo recompute(ApplicationInfoQuery query) {
                     return getApplicationInfoAsUserUncached(
                             query.packageName, query.flags, query.userId);
                 }
                 @Override
-                protected ApplicationInfo maybeCheckConsistency(
-                        ApplicationInfoQuery query, ApplicationInfo proposedResult) {
+                public boolean resultEquals(ApplicationInfo cached, ApplicationInfo fetched) {
                     // Implementing this debug check for ApplicationInfo would require a
                     // complicated deep comparison, so just bypass it for now.
-                    return proposedResult;
+                    return true;
                 }
             };
 
@@ -10275,16 +10341,15 @@
                     32, PermissionManager.CACHE_KEY_PACKAGE_INFO,
                     "getPackageInfo") {
                 @Override
-                protected PackageInfo recompute(PackageInfoQuery query) {
+                public PackageInfo recompute(PackageInfoQuery query) {
                     return getPackageInfoAsUserUncached(
                             query.packageName, query.flags, query.userId);
                 }
                 @Override
-                protected PackageInfo maybeCheckConsistency(
-                        PackageInfoQuery query, PackageInfo proposedResult) {
+                public boolean resultEquals(PackageInfo cached, PackageInfo fetched) {
                     // Implementing this debug check for PackageInfo would require a
                     // complicated deep comparison, so just bypass it for now.
-                    return proposedResult;
+                    return true;
                 }
             };
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f31f78f..98cc8f6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 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.
@@ -37,6 +37,8 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
@@ -55,11 +57,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.pkg.FrameworkPackageUserState;
-import android.content.pm.pkg.PackageUserStateUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.pkg.FrameworkPackageUserState;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
@@ -68,6 +68,7 @@
 import android.content.res.XmlResourceParser;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.FileUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -83,6 +84,7 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Base64;
+import android.util.DebugUtils;
 import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.Log;
@@ -128,6 +130,7 @@
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
@@ -148,7 +151,7 @@
  * </ul>
  *
  * @deprecated This class is mostly unused and no new changes should be added to it. Use
- * {@link android.content.pm.parsing.ParsingPackageUtils} and related parsing v2 infrastructure in
+ * ParsingPackageUtils and related parsing v2 infrastructure in
  * the core/services parsing subpackages. Or for a quick parse of a provided APK, use
  * {@link PackageManager#getPackageArchiveInfo(String, int)}.
  *
@@ -655,7 +658,7 @@
 
         // If available for the target user, or trying to match uninstalled packages and it's
         // a system app.
-        return PackageUserStateUtils.isAvailable(state, flags)
+        return isAvailable(state, flags)
                 || (appInfo != null && appInfo.isSystemApp()
                         && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
                         || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
@@ -765,7 +768,7 @@
                 final ActivityInfo[] res = new ActivityInfo[N];
                 for (int i = 0; i < N; i++) {
                     final Activity a = p.activities.get(i);
-                    if (PackageUserStateUtils.isMatch(state, a.info, flags)) {
+                    if (isMatch(state, a.info, flags)) {
                         if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(a.className)) {
                             continue;
                         }
@@ -782,7 +785,7 @@
                 final ActivityInfo[] res = new ActivityInfo[N];
                 for (int i = 0; i < N; i++) {
                     final Activity a = p.receivers.get(i);
-                    if (PackageUserStateUtils.isMatch(state, a.info, flags)) {
+                    if (isMatch(state, a.info, flags)) {
                         res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
@@ -796,7 +799,7 @@
                 final ServiceInfo[] res = new ServiceInfo[N];
                 for (int i = 0; i < N; i++) {
                     final Service s = p.services.get(i);
-                    if (PackageUserStateUtils.isMatch(state, s.info, flags)) {
+                    if (isMatch(state, s.info, flags)) {
                         res[num++] = generateServiceInfo(s, flags, state, userId);
                     }
                 }
@@ -810,7 +813,7 @@
                 final ProviderInfo[] res = new ProviderInfo[N];
                 for (int i = 0; i < N; i++) {
                     final Provider pr = p.providers.get(i);
-                    if (PackageUserStateUtils.isMatch(state, pr.info, flags)) {
+                    if (isMatch(state, pr.info, flags)) {
                         res[num++] = generateProviderInfo(pr, flags, state, userId);
                     }
                 }
@@ -7428,7 +7431,7 @@
             mCompileSdkVersionCodename = dest.readString();
             mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
 
-            mKeySetMapping = ParsingPackageUtils.readKeySetMapping(dest);
+            mKeySetMapping = readKeySetMapping(dest);
 
             cpuAbiOverride = dest.readString();
             use32bitAbi = (dest.readInt() == 1);
@@ -7554,7 +7557,7 @@
             dest.writeInt(mCompileSdkVersion);
             dest.writeString(mCompileSdkVersionCodename);
             dest.writeArraySet(mUpgradeKeySets);
-            ParsingPackageUtils.writeKeySetMapping(dest, mKeySetMapping);
+            writeKeySetMapping(dest, mKeySetMapping);
             dest.writeString(cpuAbiOverride);
             dest.writeInt(use32bitAbi ? 1 : 0);
             dest.writeByteArray(restrictUpdateHash);
@@ -7977,7 +7980,7 @@
         if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
             ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
         }
-        ai.seInfoUser = SELinuxUtil.getSeinfoUser(state);
+        ai.seInfoUser = getSeinfoUser(state);
         final OverlayPaths overlayPaths = state.getAllOverlayPaths();
         if (overlayPaths != null) {
             ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
@@ -9074,4 +9077,194 @@
             return mCachedSplitApks[0][0];
         }
     }
+
+
+
+    public static boolean isMatch(@NonNull FrameworkPackageUserState state,
+            ComponentInfo componentInfo, long flags) {
+        return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
+                componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.directBootAware, componentInfo.name, flags);
+    }
+
+    public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
+            boolean isPackageEnabled, ComponentInfo component, long flags) {
+        return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
+                component.directBootAware, component.name, flags);
+    }
+
+    /**
+     * Test if the given component is considered installed, 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>
+     */
+    public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
+            boolean isPackageEnabled, boolean isComponentEnabled,
+            boolean isComponentDirectBootAware, String componentName, long flags) {
+        final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
+        if (!isAvailable(state, flags) && !(isSystem && matchUninstalled)) {
+            return reportIfDebug(false, flags);
+        }
+
+        if (!isEnabled(state, isPackageEnabled, isComponentEnabled, componentName, flags)) {
+            return reportIfDebug(false, flags);
+        }
+
+        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);
+    }
+
+    public static boolean isAvailable(@NonNull FrameworkPackageUserState state, long flags) {
+        // True if it is installed for this user and it is not hidden. If it is hidden,
+        // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
+        final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
+        final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+        return matchAnyUser
+                || (state.isInstalled()
+                && (!state.isHidden() || matchUninstalled));
+    }
+
+    public static boolean reportIfDebug(boolean result, long flags) {
+        if (DEBUG_PARSER && !result) {
+            Slog.i(TAG, "No match!; flags: "
+                    + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
+                    + Debug.getCaller());
+        }
+        return result;
+    }
+
+    public static boolean isEnabled(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
+            long flags) {
+        return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.name, flags);
+    }
+
+    public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
+            ComponentInfo parsedComponent, long flags) {
+        return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
+                parsedComponent.name, flags);
+    }
+
+    /**
+     * Test if the given component is considered enabled.
+     */
+    public static boolean isEnabled(@NonNull FrameworkPackageUserState state,
+            boolean isPackageEnabled, boolean isComponentEnabled, String componentName,
+            long flags) {
+        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+            return true;
+        }
+
+        // First check if the overall package is disabled; if the package is
+        // enabled then fall through to check specific component
+        switch (state.getEnabledState()) {
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+                return false;
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+                    return false;
+                }
+                // fallthrough
+            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+                if (!isPackageEnabled) {
+                    return false;
+                }
+                // fallthrough
+            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                break;
+        }
+
+        // Check if component has explicit state before falling through to
+        // the manifest default
+        if (state.isComponentEnabled(componentName)) {
+            return true;
+        } else if (state.isComponentDisabled(componentName)) {
+            return false;
+        }
+
+        return isComponentEnabled;
+    }
+
+    /**
+     * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
+     */
+    public static void writeKeySetMapping(@NonNull Parcel dest,
+            @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
+        if (keySetMapping == null) {
+            dest.writeInt(-1);
+            return;
+        }
+
+        final int N = keySetMapping.size();
+        dest.writeInt(N);
+
+        for (String key : keySetMapping.keySet()) {
+            dest.writeString(key);
+            ArraySet<PublicKey> keys = keySetMapping.get(key);
+            if (keys == null) {
+                dest.writeInt(-1);
+                continue;
+            }
+
+            final int M = keys.size();
+            dest.writeInt(M);
+            for (int j = 0; j < M; j++) {
+                dest.writeSerializable(keys.valueAt(j));
+            }
+        }
+    }
+
+    /**
+     * Reads a keyset mapping from the given parcel at the given data position. May return
+     * {@code null} if the serialized mapping was {@code null}.
+     */
+    @NonNull
+    public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
+        final int N = in.readInt();
+        if (N == -1) {
+            return null;
+        }
+
+        ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
+        for (int i = 0; i < N; ++i) {
+            String key = in.readString();
+            final int M = in.readInt();
+            if (M == -1) {
+                keySetMapping.put(key, null);
+                continue;
+            }
+
+            ArraySet<PublicKey> keys = new ArraySet<>(M);
+            for (int j = 0; j < M; ++j) {
+                PublicKey pk =
+                        in.readSerializable(PublicKey.class.getClassLoader(), PublicKey.class);
+                keys.add(pk);
+            }
+
+            keySetMapping.put(key, keys);
+        }
+
+        return keySetMapping;
+    }
+
+    public static String getSeinfoUser(FrameworkPackageUserState userState) {
+        if (userState.isInstantApp()) {
+            return ":ephemeralapp:complete";
+        }
+        return ":complete";
+    }
 }
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 7696cbe..78984bd 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -174,7 +174,7 @@
 
         // Register for user-related events
         IntentFilter userFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_USER_REMOVED);
+        userFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, handler);
     }
 
diff --git a/core/java/android/content/pm/SELinuxUtil.java b/core/java/android/content/pm/SELinuxUtil.java
deleted file mode 100644
index 898dddf..0000000
--- a/core/java/android/content/pm/SELinuxUtil.java
+++ /dev/null
@@ -1,40 +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 android.content.pm;
-
-import android.content.pm.pkg.FrameworkPackageUserState;
-
-/**
- * Utility methods that need to be used in application space.
- * @hide
- */
-public final class SELinuxUtil {
-
-    /** Append to existing seinfo label for instant apps @hide */
-    private static final String INSTANT_APP_STR = ":ephemeralapp";
-
-    /** Append to existing seinfo when modifications are complete @hide */
-    public static final String COMPLETE_STR = ":complete";
-
-    /** @hide */
-    public static String getSeinfoUser(FrameworkPackageUserState userState) {
-        if (userState.isInstantApp()) {
-           return INSTANT_APP_STR + COMPLETE_STR;
-        }
-        return COMPLETE_STR;
-    }
-}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 3443c757..43a4b17 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -34,9 +34,10 @@
 
 /**
  * This class provides information for a shared library. There are
- * three types of shared libraries: builtin - non-updatable part of
+ * four types of shared libraries: builtin - non-updatable part of
  * the OS; dynamic - updatable backwards-compatible dynamically linked;
- * static - non backwards-compatible emulating static linking.
+ * static - non backwards-compatible emulating static linking;
+ * SDK - updatable backwards-incompatible dynamically loaded.
  */
 public final class SharedLibraryInfo implements Parcelable {
 
@@ -45,6 +46,7 @@
             TYPE_BUILTIN,
             TYPE_DYNAMIC,
             TYPE_STATIC,
+            TYPE_SDK,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Type{}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 7d4f7ec..ab827aa 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -41,6 +41,7 @@
 import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.contentcapture.ContentCaptureContext;
@@ -50,9 +51,15 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Represents a shortcut that can be published via {@link ShortcutManager}.
@@ -463,6 +470,9 @@
 
     private int mExcludedSurfaces;
 
+    @Nullable
+    private Map<String, Map<String, List<String>>> mCapabilityBindings;
+
     private ShortcutInfo(Builder b) {
         mUserId = b.mContext.getUserId();
 
@@ -490,7 +500,7 @@
         mRank = b.mRank;
         mExtras = b.mExtras;
         mLocusId = b.mLocusId;
-
+        mCapabilityBindings = b.mCapabilityBindings;
         mStartingThemeResName = b.mStartingThemeResId != 0
                 ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
         updateTimestamp();
@@ -602,7 +612,7 @@
         mLocusId = source.mLocusId;
         mExcludedSurfaces = source.mExcludedSurfaces;
 
-        // Just always keep it since it's cheep.
+        // Just always keep it since it's cheap.
         mIconResId = source.mIconResId;
 
         if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
@@ -641,6 +651,7 @@
             // Set this bit.
             mFlags |= FLAG_KEY_FIELDS_ONLY;
         }
+        mCapabilityBindings = source.mCapabilityBindings;
         mStartingThemeResName = source.mStartingThemeResName;
     }
 
@@ -968,6 +979,9 @@
         if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) {
             mStartingThemeResName = source.mStartingThemeResName;
         }
+        if (source.mCapabilityBindings != null) {
+            mCapabilityBindings = source.mCapabilityBindings;
+        }
     }
 
     /**
@@ -1039,6 +1053,9 @@
 
         private int mStartingThemeResId;
 
+        @Nullable
+        private Map<String, Map<String, List<String>>> mCapabilityBindings;
+
         private int mExcludedSurfaces;
 
         /**
@@ -1401,6 +1418,53 @@
         }
 
         /**
+         * Associates a shortcut with a capability, and a parameter of that capability. Used when
+         * the shortcut is an instance of a capability.
+         *
+         * <P>This method can be called multiple times to add multiple parameters to the same
+         * capability.
+         *
+         * @param capability capability associated with the shortcut. e.g. actions.intent
+         *                   .START_EXERCISE.
+         * @param parameterName name of the parameter associated with given capability.
+         *                      e.g. exercise.name.
+         * @param parameterValues a list of values for that parameters. The first value will be
+         *                        the primary name, while the rest will be alternative names. If
+         *                        the values are empty, then the parameter will not be saved in
+         *                        the shortcut.
+         */
+        @NonNull
+        public Builder addCapabilityBinding(@NonNull String capability,
+                @Nullable String parameterName, @Nullable List<String> parameterValues) {
+            Objects.requireNonNull(capability);
+            if (capability.contains("/")) {
+                throw new IllegalArgumentException("Illegal character '/' is found in capability");
+            }
+            if (mCapabilityBindings == null) {
+                mCapabilityBindings = new ArrayMap<>(1);
+            }
+            if (!mCapabilityBindings.containsKey(capability)) {
+                mCapabilityBindings.put(capability, new ArrayMap<>(0));
+            }
+            if (parameterName == null || parameterValues == null || parameterValues.isEmpty()) {
+                return this;
+            }
+            if (parameterName.contains("/")) {
+                throw new IllegalArgumentException(
+                        "Illegal character '/' is found in parameter name");
+            }
+            final Map<String, List<String>> params = mCapabilityBindings.get(capability);
+            if (!params.containsKey(parameterName)) {
+                params.put(parameterName, parameterValues);
+                return this;
+            }
+            params.put(parameterName,
+                    Stream.of(params.get(parameterName), parameterValues)
+                            .flatMap(Collection::stream).collect(Collectors.toList()));
+            return this;
+        }
+
+        /**
          * Sets which surfaces a shortcut will be excluded from.
          *
          * If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be
@@ -2176,6 +2240,44 @@
         return (mExcludedSurfaces & surface) == 0;
     }
 
+    /**
+     * @hide
+     */
+    public Map<String, Map<String, List<String>>> getCapabilityBindings() {
+        return mCapabilityBindings;
+    }
+
+    /**
+     * Return true if the shortcut is or can be used in specified capability.
+     */
+    public boolean hasCapability(@NonNull String capability) {
+        Objects.requireNonNull(capability);
+        return mCapabilityBindings != null && mCapabilityBindings.containsKey(capability);
+    }
+
+    /**
+     *  Returns the values of specified parameter in associated with given capability.
+     *
+     *  @param capability capability associated with the shortcut. e.g. actions.intent
+     *                   .START_EXERCISE.
+     *  @param parameterName name of the parameter associated with given capability.
+     *                       e.g. exercise.name.
+     */
+    @NonNull
+    public List<String> getCapabilityParameterValues(
+            @NonNull String capability, @NonNull String parameterName) {
+        Objects.requireNonNull(capability);
+        Objects.requireNonNull(parameterName);
+        if (mCapabilityBindings == null) {
+            return Collections.emptyList();
+        }
+        final Map<String, List<String>> param = mCapabilityBindings.get(capability);
+        if (param == null || !param.containsKey(parameterName)) {
+            return Collections.emptyList();
+        }
+        return param.get(parameterName);
+    }
+
     private ShortcutInfo(Parcel source) {
         final ClassLoader cl = getClass().getClassLoader();
 
@@ -2225,6 +2327,15 @@
         mIconUri = source.readString8();
         mStartingThemeResName = source.readString8();
         mExcludedSurfaces = source.readInt();
+
+        final Map<String, Map> rawCapabilityBindings = source.readHashMap(
+                /*loader*/ null, /*clazzKey*/ String.class, /*clazzValue*/ HashMap.class);
+        if (rawCapabilityBindings != null && !rawCapabilityBindings.isEmpty()) {
+            final Map<String, Map<String, List<String>>> capabilityBindings =
+                    new ArrayMap<>(rawCapabilityBindings.size());
+            rawCapabilityBindings.forEach(capabilityBindings::put);
+            mCapabilityBindings = capabilityBindings;
+        }
     }
 
     @Override
@@ -2278,6 +2389,7 @@
         dest.writeString8(mIconUri);
         dest.writeString8(mStartingThemeResName);
         dest.writeInt(mExcludedSurfaces);
+        dest.writeMap(mCapabilityBindings);
     }
 
     public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2529,7 +2641,8 @@
             long lastChangedTimestamp,
             int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
             int disabledReason, Person[] persons, LocusId locusId,
-            @Nullable String startingThemeResName) {
+            @Nullable String startingThemeResName,
+            @Nullable Map<String, Map<String, List<String>>> capabilityBindings) {
         mUserId = userId;
         mId = id;
         mPackageName = packageName;
@@ -2559,5 +2672,6 @@
         mPersons = persons;
         mLocusId = locusId;
         mStartingThemeResName = startingThemeResName;
+        mCapabilityBindings = capabilityBindings;
     }
 }
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 3ed5c64..087a795 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -29,6 +29,8 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 
+import com.android.internal.infra.AndroidFuture;
+
 import java.util.List;
 
 /**
@@ -50,6 +52,19 @@
             @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
             @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid);
 
+    /**
+     * Retrieves shortcuts asynchronously. Query will go through persistence layer (thus making the
+     * call async) if querying by shortcutIds in a specific package; otherwise it's effectively the
+     * same as calling {@link #getShortcuts}.
+     */
+    public abstract void
+            getShortcutsAsync(int launcherUserId,
+            @NonNull String callingPackage, long changedSince,
+            @Nullable String packageName, @Nullable List<String> shortcutIds,
+            @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
+            @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid,
+            AndroidFuture<List<ShortcutInfo>> cb);
+
     public abstract boolean
             isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
             @NonNull String packageName, @NonNull String id, int userId);
@@ -63,6 +78,14 @@
             @NonNull String packageName, @NonNull String shortcutId, int userId,
             int callingPid, int callingUid);
 
+    /**
+     * Retrieves the intents from a specified shortcut asynchronously.
+     */
+    public abstract void createShortcutIntentsAsync(
+            int launcherUserId, @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId,
+            int callingPid, int callingUid, @NonNull AndroidFuture<Intent[]> cb);
+
     public abstract void addListener(@NonNull ShortcutChangeListener listener);
 
     public abstract void addShortcutChangeCallback(
@@ -82,6 +105,13 @@
             @NonNull String callingPackage,
             @NonNull String packageName, @NonNull String shortcutId, int userId);
 
+    /**
+     * Retrieves a file descriptor from the icon in a specified shortcut asynchronously.
+     */
+    public abstract void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId,
+            @NonNull AndroidFuture<ParcelFileDescriptor> cb);
+
     public abstract boolean hasShortcutHostPermission(int launcherUserId,
             @NonNull String callingPackage, int callingPid, int callingUid);
 
@@ -117,6 +147,14 @@
     public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
             @NonNull String packageName, @NonNull String shortcutId, int userId);
 
+    /**
+     * Retrieves the icon Uri of the shortcut asynchronously, and grants Uri read permission to the
+     * caller.
+     */
+    public abstract void getShortcutIconUriAsync(int launcherUserId,
+            @NonNull String launcherPackage, @NonNull String packageName,
+            @NonNull String shortcutId, int userId, @NonNull AndroidFuture<String> cb);
+
     public abstract boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage,
             @NonNull String packageName, @NonNull String shortcutId, int userId,
             @NonNull IntentFilter filter);
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 26f0826..a503d14 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -25,6 +25,9 @@
       "path": "cts/hostsidetests/packagemanager"
     },
     {
+      "path": "cts/hostsidetests/os/test_mappings/packagemanager"
+    },
+    {
       "path": "system/apex/tests"
     }
   ],
@@ -37,6 +40,9 @@
       "name": "CarrierAppIntegrationTestCases"
     },
     {
+      "name": "ApkVerityTest"
+    },
+    {
       "name": "CtsIncrementalInstallHostTestCases",
       "options": [
         {
@@ -59,6 +65,89 @@
           "include-filter": "android.content.pm.cts"
         }
       ]
+    },
+    {
+      "name": "CtsUsesNativeLibraryTest",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsAppSearchHostTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsSilentUpdateHostTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsSuspendAppsTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsSecureFrpInstallTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsSuspendAppsPermissionTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsAppSecurityHostTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
     }
   ],
   "postsubmit": [
@@ -69,6 +158,9 @@
           "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
         }
       ]
+    },
+    {
+      "name": "CtsInstallHostTestCases"
     }
   ]
 }
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 1639ee9..d5498a0 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -18,13 +18,6 @@
 
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-import static android.content.pm.parsing.ParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY;
-import static android.content.pm.parsing.ParsingPackageUtils.checkRequiredSystemProperties;
-import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
-import static android.content.pm.parsing.ParsingPackageUtils.validateName;
-import static android.content.pm.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
-import static android.content.pm.parsing.ParsingUtils.DEFAULT_MIN_SDK_VERSION;
-import static android.content.pm.parsing.ParsingUtils.DEFAULT_TARGET_SDK_VERSION;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
 import android.annotation.NonNull;
@@ -37,6 +30,7 @@
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.ApkAssets;
 import android.content.res.XmlResourceParser;
+import android.os.Build;
 import android.os.Trace;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -66,7 +60,7 @@
 /** @hide */
 public class ApkLiteParseUtils {
 
-    private static final String TAG = ParsingUtils.TAG;
+    private static final String TAG = "ApkLiteParseUtils";
 
     private static final int PARSE_DEFAULT_INSTALL_LOCATION =
             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
@@ -75,6 +69,26 @@
 
     public static final String APK_FILE_EXTENSION = ".apk";
 
+
+    // Constants copied from services.jar side since they're not accessible
+    private static final String ANDROID_RES_NAMESPACE =
+            "http://schemas.android.com/apk/res/android";
+    private static final int DEFAULT_MIN_SDK_VERSION = 1;
+    private static final int DEFAULT_TARGET_SDK_VERSION = 0;
+    public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+    private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
+    private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
+    private static final String TAG_APPLICATION = "application";
+    private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+    private static final String TAG_PROFILEABLE = "profileable";
+    private static final String TAG_RECEIVER = "receiver";
+    private static final String TAG_OVERLAY = "overlay";
+    private static final String TAG_USES_SDK = "uses-sdk";
+    private static final String TAG_USES_SPLIT = "uses-split";
+    private static final String TAG_MANIFEST = "manifest";
+    private static final int SDK_VERSION = Build.VERSION.SDK_INT;
+    private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
+
     /**
      * Parse only lightweight details about the package at the given location.
      * Automatically detects if the package is a monolithic style (single APK
@@ -312,15 +326,16 @@
                         "Failed to parse " + apkPath, e);
             }
 
-            parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
+            parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
 
             final SigningDetails signingDetails;
-            if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
-                final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
+                final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                 try {
                     final ParseResult<SigningDetails> result =
-                            ParsingPackageUtils.getSigningDetails(input, apkFile.getAbsolutePath(),
+                            FrameworkParsingPackageUtils.getSigningDetails(input,
+                                    apkFile.getAbsolutePath(),
                                     skipVerify, /* isStaticSharedLibrary */ false,
                                     SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
                     if (result.isError()) {
@@ -417,12 +432,12 @@
                 continue;
             }
 
-            if (ParsingPackageUtils.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+            if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
                 final VerifierInfo verifier = parseVerifier(parser);
                 if (verifier != null) {
                     verifiers.add(verifier);
                 }
-            } else if (ParsingPackageUtils.TAG_APPLICATION.equals(parser.getName())) {
+            } else if (TAG_APPLICATION.equals(parser.getName())) {
                 debuggable = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "debuggable",
                         false);
                 multiArch = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "multiArch",
@@ -453,15 +468,15 @@
                         continue;
                     }
 
-                    if (ParsingPackageUtils.TAG_PROFILEABLE.equals(parser.getName())) {
+                    if (TAG_PROFILEABLE.equals(parser.getName())) {
                         profilableByShell = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
                                 "shell", profilableByShell);
-                    } else if (ParsingPackageUtils.TAG_RECEIVER.equals(parser.getName())) {
+                    } else if (TAG_RECEIVER.equals(parser.getName())) {
                         hasDeviceAdminReceiver |= isDeviceAdminReceiver(
                                 parser, hasBindDeviceAdminPermission);
                     }
                 }
-            } else if (ParsingPackageUtils.TAG_OVERLAY.equals(parser.getName())) {
+            } else if (TAG_OVERLAY.equals(parser.getName())) {
                 requiredSystemPropertyName = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
                         "requiredSystemPropertyName");
                 requiredSystemPropertyValue = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
@@ -470,7 +485,7 @@
                 overlayIsStatic = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "isStatic",
                         false);
                 overlayPriority = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "priority", 0);
-            } else if (ParsingPackageUtils.TAG_USES_SPLIT.equals(parser.getName())) {
+            } else if (TAG_USES_SPLIT.equals(parser.getName())) {
                 if (usesSplitName != null) {
                     Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
                     continue;
@@ -481,8 +496,8 @@
                     return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                             "<uses-split> tag requires 'android:name' attribute");
                 }
-            } else if (ParsingPackageUtils.TAG_USES_SDK.equals(parser.getName())) {
-                // Mirrors ParsingPackageUtils#parseUsesSdk until lite and full parsing is combined
+            } else if (TAG_USES_SDK.equals(parser.getName())) {
+                // Mirrors FrameworkParsingPackageUtils#parseUsesSdk until lite and full parsing is combined
                 String minSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
                         "minSdkVersion");
                 String targetSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
@@ -515,16 +530,15 @@
                     targetCode = minCode;
                 }
 
-                ParseResult<Integer> targetResult = ParsingPackageUtils.computeTargetSdkVersion(
-                        targetVer, targetCode, ParsingPackageUtils.SDK_CODENAMES, input);
+                ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion(
+                        targetVer, targetCode, SDK_CODENAMES, input);
                 if (targetResult.isError()) {
                     return input.error(targetResult);
                 }
                 targetSdkVersion = targetResult.getResult();
 
-                ParseResult<Integer> minResult = ParsingPackageUtils.computeMinSdkVersion(
-                        minVer, minCode, ParsingPackageUtils.SDK_VERSION,
-                        ParsingPackageUtils.SDK_CODENAMES, input);
+                ParseResult<Integer> minResult = FrameworkParsingPackageUtils.computeMinSdkVersion(
+                        minVer, minCode, SDK_VERSION, SDK_CODENAMES, input);
                 if (minResult.isError()) {
                     return input.error(minResult);
                 }
@@ -533,9 +547,9 @@
         }
 
         // Check to see if overlay should be excluded based on system property condition
-        if ((flags & PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY) == 0
-                && !checkRequiredSystemProperties(
-                        requiredSystemPropertyName, requiredSystemPropertyValue)) {
+        if ((flags & FrameworkParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY)
+                == 0 && !FrameworkParsingPackageUtils.checkRequiredSystemProperties(
+                requiredSystemPropertyName, requiredSystemPropertyValue)) {
             String message = "Skipping target and overlay pair " + targetPackage + " and "
                     + codePath + ": overlay ignored due to required system property: "
                     + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue;
@@ -600,14 +614,15 @@
             return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "No start tag found");
         }
-        if (!parser.getName().equals(ParsingPackageUtils.TAG_MANIFEST)) {
+        if (!parser.getName().equals(TAG_MANIFEST)) {
             return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "No <manifest> tag");
         }
 
         final String packageName = parser.getAttributeValue(null, "package");
         if (!"android".equals(packageName)) {
-            final ParseResult<?> nameResult = validateName(input, packageName, true, true);
+            final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input,
+                    packageName, true, true);
             if (nameResult.isError()) {
                 return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                         "Invalid manifest package: " + nameResult.getErrorMessage());
@@ -619,7 +634,8 @@
             if (splitName.length() == 0) {
                 splitName = null;
             } else {
-                final ParseResult<?> nameResult = validateName(input, splitName, false, false);
+                final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input,
+                        splitName, false, false);
                 if (nameResult.isError()) {
                     return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                             "Invalid manifest split: " + nameResult.getErrorMessage());
@@ -666,7 +682,7 @@
             final String type = value.trim();
             // Using requireFilename as true because it limits length of the name to the
             // {@link #MAX_FILE_NAME_SIZE}.
-            final ParseResult<?> nameResult = validateName(input, type,
+            final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, type,
                     false /* requireSeparator */, true /* requireFilename */);
             if (nameResult.isError()) {
                 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -688,7 +704,7 @@
             return null;
         }
 
-        final PublicKey publicKey = parsePublicKey(encodedPublicKey);
+        final PublicKey publicKey = FrameworkParsingPackageUtils.parsePublicKey(encodedPublicKey);
         if (publicKey == null) {
             Slog.i(TAG, "Unable to parse verifier public key for " + packageName);
             return null;
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
new file mode 100644
index 0000000..8b86a16
--- /dev/null
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+
+import android.annotation.CheckResult;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.os.Build;
+import android.os.FileUtils;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+
+/** @hide */
+public class FrameworkParsingPackageUtils {
+
+    private static final String TAG = "FrameworkParsingPackageUtils";
+
+    /**
+     * For those names would be used as a part of the file name. Limits size to 223 and reserves 32
+     * for the OS.
+     */
+    private static final int MAX_FILE_NAME_SIZE = 223;
+
+    public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
+
+    /**
+     * Check if the given name is valid.
+     *
+     * @param name The name to check.
+     * @param requireSeparator {@code true} if the name requires containing a separator at least.
+     * @param requireFilename {@code true} to apply file name validation to the given name. It also
+     *                        limits length of the name to the {@link #MAX_FILE_NAME_SIZE}.
+     * @return Success if it's valid.
+     */
+    public static String validateName(String name, boolean requireSeparator,
+            boolean requireFilename) {
+        final int N = name.length();
+        boolean hasSep = false;
+        boolean front = true;
+        for (int i = 0; i < N; i++) {
+            final char c = name.charAt(i);
+            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                front = false;
+                continue;
+            }
+            if (!front) {
+                if ((c >= '0' && c <= '9') || c == '_') {
+                    continue;
+                }
+            }
+            if (c == '.') {
+                hasSep = true;
+                front = true;
+                continue;
+            }
+            return "bad character '" + c + "'";
+        }
+        if (requireFilename) {
+            if (!FileUtils.isValidExtFilename(name)) {
+                return "Invalid filename";
+            } else if (N > MAX_FILE_NAME_SIZE) {
+                return "the length of the name is greater than " + MAX_FILE_NAME_SIZE;
+            }
+        }
+        return hasSep || !requireSeparator ? null : "must have at least one '.' separator";
+    }
+
+    /**
+     * @see #validateName(String, boolean, boolean)
+     */
+    public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+            boolean requireFilename) {
+        final String errorMessage = validateName(name, requireSeparator, requireFilename);
+        if (errorMessage != null) {
+            return input.error(errorMessage);
+        }
+        return input.success(null);
+    }
+
+    /**
+     * @return {@link PublicKey} of a given encoded public key.
+     */
+    public static PublicKey parsePublicKey(final String encodedPublicKey) {
+        if (encodedPublicKey == null) {
+            Slog.w(TAG, "Could not parse null public key");
+            return null;
+        }
+
+        try {
+            return parsePublicKey(Base64.decode(encodedPublicKey, Base64.DEFAULT));
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
+            return null;
+        }
+    }
+
+    /**
+     * @return {@link PublicKey} of the given byte array of a public key.
+     */
+    public static PublicKey parsePublicKey(final byte[] publicKey) {
+        if (publicKey == null) {
+            Slog.w(TAG, "Could not parse null public key");
+            return null;
+        }
+
+        final EncodedKeySpec keySpec;
+        try {
+            keySpec = new X509EncodedKeySpec(publicKey);
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
+            return null;
+        }
+
+        /* First try the key as an RSA key. */
+        try {
+            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            return keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.wtf(TAG, "Could not parse public key: RSA KeyFactory not included in build");
+        } catch (InvalidKeySpecException e) {
+            // Not a RSA public key.
+        }
+
+        /* Now try it as a ECDSA key. */
+        try {
+            final KeyFactory keyFactory = KeyFactory.getInstance("EC");
+            return keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.wtf(TAG, "Could not parse public key: EC KeyFactory not included in build");
+        } catch (InvalidKeySpecException e) {
+            // Not a ECDSA public key.
+        }
+
+        /* Now try it as a DSA key. */
+        try {
+            final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+            return keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.wtf(TAG, "Could not parse public key: DSA KeyFactory not included in build");
+        } catch (InvalidKeySpecException e) {
+            // Not a DSA public key.
+        }
+
+        /* Not a supported key type */
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if both the property name and value are empty or if the given system
+     * property is set to the specified value. Properties can be one or more, and if properties are
+     * more than one, they must be separated by comma, and count of names and values must be equal,
+     * and also every given system property must be set to the corresponding value.
+     * In all other cases, returns {@code false}
+     */
+    public static boolean checkRequiredSystemProperties(@Nullable String rawPropNames,
+            @Nullable String rawPropValues) {
+        if (TextUtils.isEmpty(rawPropNames) || TextUtils.isEmpty(rawPropValues)) {
+            if (!TextUtils.isEmpty(rawPropNames) || !TextUtils.isEmpty(rawPropValues)) {
+                // malformed condition - incomplete
+                Slog.w(TAG, "Disabling overlay - incomplete property :'" + rawPropNames
+                        + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
+                        + " AND requiredSystemPropertyValue to be specified.");
+                return false;
+            }
+            // no valid condition set - so no exclusion criteria, overlay will be included.
+            return true;
+        }
+
+        final String[] propNames = rawPropNames.split(",");
+        final String[] propValues = rawPropValues.split(",");
+
+        if (propNames.length != propValues.length) {
+            Slog.w(TAG, "Disabling overlay - property :'" + rawPropNames
+                    + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
+                    + " AND requiredSystemPropertyValue lists to have the same size.");
+            return false;
+        }
+        for (int i = 0; i < propNames.length; i++) {
+            // Check property value: make sure it is both set and equal to expected value
+            final String currValue = SystemProperties.get(propNames[i]);
+            if (!TextUtils.equals(currValue, propValues[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @CheckResult
+    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
+            String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
+            @NonNull SigningDetails existingSigningDetails, int targetSdk) {
+        int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+                targetSdk);
+        if (isStaticSharedLibrary) {
+            // must use v2 signing scheme
+            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+        }
+        final ParseResult<SigningDetails> verified;
+        if (skipVerify) {
+            // systemDir APKs are already trusted, save time by not verifying; since the
+            // signature is not verified and some system apps can have their V2+ signatures
+            // stripped allow pulling the certs from the jar signature.
+            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
+                    SigningDetails.SignatureSchemeVersion.JAR);
+        } else {
+            verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
+        }
+
+        if (verified.isError()) {
+            return input.error(verified);
+        }
+
+        // Verify that entries are signed consistently with the first pkg
+        // we encountered. Note that for splits, certificates may have
+        // already been populated during an earlier parse of a base APK.
+        if (existingSigningDetails == SigningDetails.UNKNOWN) {
+            return verified;
+        } else {
+            if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
+                    verified.getResult().getSignatures())) {
+                return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                        baseCodePath + " has mismatched certificates");
+            }
+
+            return input.success(existingSigningDetails);
+        }
+    }
+
+    /**
+     * Computes the minSdkVersion to use at runtime. If the package is not compatible with this
+     * platform, populates {@code outError[0]} with an error message.
+     * <p>
+     * If {@code minCode} is not specified, e.g. the value is {@code null}, then behavior varies
+     * based on the {@code platformSdkVersion}:
+     * <ul>
+     * <li>If the platform SDK version is greater than or equal to the
+     * {@code minVers}, returns the {@code mniVers} unmodified.
+     * <li>Otherwise, returns -1 to indicate that the package is not
+     * compatible with this platform.
+     * </ul>
+     * <p>
+     * Otherwise, the behavior varies based on whether the current platform
+     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+     * has length > 0:
+     * <ul>
+     * <li>If this is a pre-release platform and the value specified by
+     * {@code targetCode} is contained within the array of allowed pre-release
+     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+     * <li>If this is a released platform, this method will return -1 to
+     * indicate that the package is not compatible with this platform.
+     * </ul>
+     *
+     * @param minVers              minSdkVersion number, if specified in the application manifest,
+     *                             or 1 otherwise
+     * @param minCode              minSdkVersion code, if specified in the application manifest, or
+     *                             {@code null} otherwise
+     * @param platformSdkVersion   platform SDK version number, typically Build.VERSION.SDK_INT
+     * @param platformSdkCodenames array of allowed prerelease SDK codenames for this platform
+     * @return the minSdkVersion to use at runtime if successful
+     */
+    public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
+            @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
+            @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) {
+        // If it's a release SDK, make sure we meet the minimum SDK requirement.
+        if (minCode == null) {
+            if (minVers <= platformSdkVersion) {
+                return input.success(minVers);
+            }
+
+            // We don't meet the minimum SDK requirement.
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires newer sdk version #" + minVers
+                            + " (current version is #" + platformSdkVersion + ")");
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, we
+        // definitely meet the minimum SDK requirement.
+        if (matchTargetCode(platformSdkCodenames, minCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + minCode
+                            + " (current platform is any of "
+                            + Arrays.toString(platformSdkCodenames) + ")");
+        } else {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + minCode
+                            + " but this is a release platform.");
+        }
+    }
+
+    /**
+     * Computes the targetSdkVersion to use at runtime. If the package is not compatible with this
+     * platform, populates {@code outError[0]} with an error message.
+     * <p>
+     * If {@code targetCode} is not specified, e.g. the value is {@code null}, then the {@code
+     * targetVers} will be returned unmodified.
+     * <p>
+     * Otherwise, the behavior varies based on whether the current platform is a pre-release
+     * version, e.g. the {@code platformSdkCodenames} array has length > 0:
+     * <ul>
+     * <li>If this is a pre-release platform and the value specified by
+     * {@code targetCode} is contained within the array of allowed pre-release
+     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+     * <li>If this is a released platform, this method will return -1 to
+     * indicate that the package is not compatible with this platform.
+     * </ul>
+     *
+     * @param targetVers           targetSdkVersion number, if specified in the application
+     *                             manifest, or 0 otherwise
+     * @param targetCode           targetSdkVersion code, if specified in the application manifest,
+     *                             or {@code null} otherwise
+     * @param platformSdkCodenames array of allowed pre-release SDK codenames for this platform
+     * @return the targetSdkVersion to use at runtime if successful
+     */
+    public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
+            @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
+            @NonNull ParseInput input) {
+        // If it's a release SDK, return the version number unmodified.
+        if (targetCode == null) {
+            return input.success(targetVers);
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + targetCode
+                            + " (current platform is any of "
+                            + Arrays.toString(platformSdkCodenames) + ")");
+        } else {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + targetCode
+                            + " but this is a release platform.");
+        }
+    }
+
+    /**
+     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form {@code
+     * [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+     */
+    private static boolean matchTargetCode(@NonNull String[] codeNames,
+            @NonNull String targetCode) {
+        final String targetCodeName;
+        final int targetCodeIdx = targetCode.indexOf('.');
+        if (targetCodeIdx == -1) {
+            targetCodeName = targetCode;
+        } else {
+            targetCodeName = targetCode.substring(0, targetCodeIdx);
+        }
+        return ArrayUtils.contains(codeNames, targetCodeName);
+    }
+}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
deleted file mode 100644
index 28290d7..0000000
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ /dev/null
@@ -1,972 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.annotation.CheckResult;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.apex.ApexInfo;
-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.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.SELinuxUtil;
-import android.content.pm.ServiceInfo;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.content.pm.SigningInfo;
-import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.component.ComponentParseUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedAttribution;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.content.pm.pkg.FrameworkPackageUserState;
-import android.content.pm.pkg.PackageUserStateUtils;
-import android.os.Environment;
-import android.os.UserHandle;
-
-import com.android.internal.util.ArrayUtils;
-
-import libcore.util.EmptyArray;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * @hide
- **/
-public class PackageInfoWithoutStateUtils {
-
-    public static final String SYSTEM_DATA_PATH =
-            Environment.getDataDirectoryPath() + File.separator + "system";
-
-    @Nullable
-    public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
-            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
-            int userId) {
-        return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
-                state, userId, null);
-    }
-
-    @Nullable
-    public static PackageInfo generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags) {
-        return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
-                FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId(), apexInfo);
-    }
-
-    @Nullable
-    private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
-            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
-            int userId, @Nullable ApexInfo apexInfo) {
-        ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
-        if (applicationInfo == null) {
-            return null;
-        }
-        PackageInfo info = generateWithoutComponents(pkg, gids, flags, firstInstallTime,
-                lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
-
-        if (info == null) {
-            return null;
-        }
-
-        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
-            final int N = pkg.getActivities().size();
-            if (N > 0) {
-                int num = 0;
-                final ActivityInfo[] res = new ActivityInfo[N];
-                for (int i = 0; i < N; i++) {
-                    final ParsedActivity a = pkg.getActivities().get(i);
-                    if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
-                            flags)) {
-                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
-                                a.getName())) {
-                            continue;
-                        }
-                        res[num++] = generateActivityInfo(pkg, a, flags, state,
-                                applicationInfo, userId);
-                    }
-                }
-                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 (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
-                            flags)) {
-                        res[num++] = generateActivityInfo(pkg, a, flags, state,
-                                applicationInfo, userId);
-                    }
-                }
-                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 (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), s,
-                            flags)) {
-                        res[num++] = generateServiceInfo(pkg, s, flags, state,
-                                applicationInfo, userId);
-                    }
-                }
-                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 (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), pr,
-                            flags)) {
-                        res[num++] = generateProviderInfo(pkg, pr, flags, state,
-                                applicationInfo, userId);
-                    }
-                }
-                info.providers = ArrayUtils.trimToSize(res, num);
-            }
-        }
-        if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
-            int N = pkg.getInstrumentations().size();
-            if (N > 0) {
-                info.instrumentation = new InstrumentationInfo[N];
-                for (int i = 0; i < N; i++) {
-                    info.instrumentation[i] = generateInstrumentationInfo(
-                            pkg.getInstrumentations().get(i), pkg, flags, userId,
-                            true /* assignUserFields */);
-                }
-            }
-        }
-
-        return info;
-    }
-
-    @Nullable
-    public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
-            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
-            int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
-        if (!checkUseInstalled(pkg, state, flags)) {
-            return null;
-        }
-
-        return generateWithoutComponentsUnchecked(pkg, gids, flags, firstInstallTime,
-                lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
-    }
-
-    /**
-     * This bypasses critical checks that are necessary for usage with data passed outside of system
-     * server.
-     * <p>
-     * Prefer {@link #generateWithoutComponents(ParsingPackageRead, int[], int, long, long, Set,
-     * FrameworkPackageUserState, int, ApexInfo, ApplicationInfo)}.
-     */
-    @NonNull
-    public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
-            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
-            int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
-        PackageInfo pi = new PackageInfo();
-        pi.packageName = pkg.getPackageName();
-        pi.splitNames = pkg.getSplitNames();
-        pi.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
-        pi.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
-        pi.baseRevisionCode = pkg.getBaseRevisionCode();
-        pi.splitRevisionCodes = pkg.getSplitRevisionCodes();
-        pi.versionName = pkg.getVersionName();
-        pi.sharedUserId = pkg.getSharedUserId();
-        pi.sharedUserLabel = pkg.getSharedUserLabel();
-        pi.applicationInfo = applicationInfo;
-        pi.installLocation = pkg.getInstallLocation();
-        if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
-                || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
-            pi.requiredForAllUsers = pkg.isRequiredForAllUsers();
-        }
-        pi.restrictedAccountType = pkg.getRestrictedAccountType();
-        pi.requiredAccountType = pkg.getRequiredAccountType();
-        pi.overlayTarget = pkg.getOverlayTarget();
-        pi.targetOverlayableName = pkg.getOverlayTargetOverlayableName();
-        pi.overlayCategory = pkg.getOverlayCategory();
-        pi.overlayPriority = pkg.getOverlayPriority();
-        pi.mOverlayIsStatic = pkg.isOverlayIsStatic();
-        pi.compileSdkVersion = pkg.getCompileSdkVersion();
-        pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
-        pi.firstInstallTime = firstInstallTime;
-        pi.lastUpdateTime = lastUpdateTime;
-        if ((flags & PackageManager.GET_GIDS) != 0) {
-            pi.gids = gids;
-        }
-        if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
-            int size = pkg.getConfigPreferences().size();
-            if (size > 0) {
-                pi.configPreferences = new ConfigurationInfo[size];
-                pkg.getConfigPreferences().toArray(pi.configPreferences);
-            }
-            size = pkg.getRequestedFeatures().size();
-            if (size > 0) {
-                pi.reqFeatures = new FeatureInfo[size];
-                pkg.getRequestedFeatures().toArray(pi.reqFeatures);
-            }
-            size = pkg.getFeatureGroups().size();
-            if (size > 0) {
-                pi.featureGroups = new FeatureGroupInfo[size];
-                pkg.getFeatureGroups().toArray(pi.featureGroups);
-            }
-        }
-        if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
-            int size = ArrayUtils.size(pkg.getPermissions());
-            if (size > 0) {
-                pi.permissions = new PermissionInfo[size];
-                for (int i = 0; i < size; i++) {
-                    pi.permissions[i] = generatePermissionInfo(pkg.getPermissions().get(i),
-                            flags);
-                }
-            }
-            final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
-            size = usesPermissions.size();
-            if (size > 0) {
-                pi.requestedPermissions = new String[size];
-                pi.requestedPermissionsFlags = new int[size];
-                for (int i = 0; i < size; i++) {
-                    final ParsedUsesPermission usesPermission = usesPermissions.get(i);
-                    pi.requestedPermissions[i] = usesPermission.getName();
-                    // The notion of required permissions is deprecated but for compatibility.
-                    pi.requestedPermissionsFlags[i] |=
-                            PackageInfo.REQUESTED_PERMISSION_REQUIRED;
-                    if (grantedPermissions != null
-                            && grantedPermissions.contains(usesPermission.getName())) {
-                        pi.requestedPermissionsFlags[i] |=
-                                PackageInfo.REQUESTED_PERMISSION_GRANTED;
-                    }
-                    if ((usesPermission.getUsesPermissionFlags()
-                            & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
-                        pi.requestedPermissionsFlags[i] |=
-                                PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
-                    }
-                }
-            }
-        }
-        if ((flags & PackageManager.GET_ATTRIBUTIONS) != 0) {
-            int size = ArrayUtils.size(pkg.getAttributions());
-            if (size > 0) {
-                pi.attributions = new Attribution[size];
-                for (int i = 0; i < size; i++) {
-                    pi.attributions[i] = generateAttribution(pkg.getAttributions().get(i));
-                }
-            }
-            if (pkg.areAttributionsUserVisible()) {
-                pi.applicationInfo.privateFlagsExt
-                        |= ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
-            } else {
-                pi.applicationInfo.privateFlagsExt
-                        &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
-            }
-        } else {
-            pi.applicationInfo.privateFlagsExt
-                    &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
-        }
-
-        if (apexInfo != null) {
-            File apexFile = new File(apexInfo.modulePath);
-
-            pi.applicationInfo.sourceDir = apexFile.getPath();
-            pi.applicationInfo.publicSourceDir = apexFile.getPath();
-            if (apexInfo.isFactory) {
-                pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
-                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-            } else {
-                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
-                pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-            }
-            if (apexInfo.isActive) {
-                pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
-            } else {
-                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
-            }
-            pi.isApex = true;
-        }
-
-        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.
-                pi.signatures = new Signature[1];
-                pi.signatures[0] = signingDetails.getPastSigningCertificates()[0];
-            } else if (signingDetails.hasSignatures()) {
-                // otherwise keep old behavior
-                int numberOfSigs = signingDetails.getSignatures().length;
-                pi.signatures = new Signature[numberOfSigs];
-                System.arraycopy(signingDetails.getSignatures(), 0, pi.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
-                pi.signingInfo = new SigningInfo(signingDetails);
-            } else {
-                pi.signingInfo = null;
-            }
-        }
-
-        return pi;
-    }
-
-    @Nullable
-    public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
-            @PackageManager.ApplicationInfoFlagsBits long flags, FrameworkPackageUserState state,
-            int userId) {
-        if (pkg == null) {
-            return null;
-        }
-
-        if (!checkUseInstalled(pkg, state, flags)) {
-            return null;
-        }
-
-        return generateApplicationInfoUnchecked(pkg, flags, state, userId,
-                true /* assignUserFields */);
-    }
-
-    /**
-     * This bypasses critical checks that are necessary for usage with data passed outside of system
-     * server.
-     * <p>
-     * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, FrameworkPackageUserState, int)}.
-     *
-     * @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user
-     *                         specific fields. This can be skipped when building from a system
-     *                         server package, as there are cached strings which can be used rather
-     *                         than querying and concatenating the comparatively expensive {@link
-     *                         Environment#getDataDirectory(String)}}.
-     */
-    @NonNull
-    public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
-            @PackageManager.ApplicationInfoFlagsBits long flags,
-            @NonNull FrameworkPackageUserState state, int userId, boolean assignUserFields) {
-        // Make shallow copy so we can store the metadata/libraries safely
-        ApplicationInfo ai = ((ParsingPackageHidden) pkg).toAppInfoWithoutState();
-
-        if (assignUserFields) {
-            assignUserFields(pkg, ai, userId);
-        }
-
-        updateApplicationInfo(ai, flags, state);
-
-        return ai;
-    }
-
-    private static void updateApplicationInfo(ApplicationInfo ai, long flags,
-            FrameworkPackageUserState state) {
-        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();
-        }
-
-        ai.flags |= flag(state.isStopped(), ApplicationInfo.FLAG_STOPPED)
-                | flag(state.isInstalled(), ApplicationInfo.FLAG_INSTALLED)
-                | flag(state.isSuspended(), ApplicationInfo.FLAG_SUSPENDED);
-        ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
-                | flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
-                | flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
-
-        if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
-            ai.enabled = true;
-        } else if (state.getEnabledState()
-                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
-            ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
-        } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                || state.getEnabledState()
-                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
-            ai.enabled = false;
-        }
-        ai.enabledSetting = state.getEnabledState();
-        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
-            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
-        }
-        ai.seInfoUser = SELinuxUtil.getSeinfoUser(state);
-        final OverlayPaths overlayPaths = state.getAllOverlayPaths();
-        if (overlayPaths != null) {
-            ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
-            ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
-        }
-    }
-
-    @Nullable
-    public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
-            @PackageManager.ApplicationInfoFlagsBits long flags,
-            @NonNull FrameworkPackageUserState state, int userId) {
-        if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
-            return null;
-        }
-        // This is used to return the ResolverActivity or instantAppInstallerActivity;
-        // we will just always make a copy.
-        ai = new ApplicationInfo(ai);
-        ai.initForUser(userId);
-        ai.icon = (ParsingPackageUtils.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes
-                : ai.iconRes;
-        updateApplicationInfo(ai, flags, state);
-        return ai;
-    }
-
-    @Nullable
-    public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
-            @PackageManager.ComponentInfoFlagsBits long flags,
-            @NonNull FrameworkPackageUserState state, int userId) {
-        if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
-            return null;
-        }
-        // This is used to return the ResolverActivity or instantAppInstallerActivity;
-        // we will just always make a copy.
-        final ActivityInfo ai = new ActivityInfo(a);
-        ai.applicationInfo =
-                generateDelegateApplicationInfo(ai.applicationInfo, flags, state, userId);
-        return ai;
-    }
-
-    @Nullable
-    public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
-            @Nullable ApplicationInfo applicationInfo, int userId) {
-        if (a == null) return null;
-        if (!checkUseInstalled(pkg, state, flags)) {
-            return null;
-        }
-        if (applicationInfo == null) {
-            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
-        }
-        if (applicationInfo == null) {
-            return null;
-        }
-
-        return generateActivityInfoUnchecked(a, flags, applicationInfo);
-    }
-
-    /**
-     * This bypasses critical checks that are necessary for usage with data passed outside of system
-     * server.
-     * <p>
-     * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, long,
-     * FrameworkPackageUserState, ApplicationInfo, int)}.
-     */
-    @NonNull
-    public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
-            @PackageManager.ComponentInfoFlagsBits long flags,
-            @NonNull ApplicationInfo applicationInfo) {
-        // Make shallow copies so we can store the metadata safely
-        ActivityInfo ai = new ActivityInfo();
-        assignSharedFieldsForComponentInfo(ai, a);
-        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) {
-            ai.metaData = a.getMetaData();
-        }
-        ai.applicationInfo = applicationInfo;
-        return ai;
-    }
-
-    @Nullable
-    public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
-            int userId) {
-        return generateActivityInfo(pkg, a, flags, state, null, userId);
-    }
-
-    @Nullable
-    public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
-            @Nullable ApplicationInfo applicationInfo, int userId) {
-        if (s == null) return null;
-        if (!checkUseInstalled(pkg, state, flags)) {
-            return null;
-        }
-        if (applicationInfo == null) {
-            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
-        }
-        if (applicationInfo == null) {
-            return null;
-        }
-
-        return generateServiceInfoUnchecked(s, flags,  applicationInfo);
-    }
-
-    /**
-     * This bypasses critical checks that are necessary for usage with data passed outside of system
-     * server.
-     * <p>
-     * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, long,
-     * FrameworkPackageUserState, ApplicationInfo, int)}.
-     */
-    @NonNull
-    public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
-            @PackageManager.ComponentInfoFlagsBits long flags,
-            @NonNull ApplicationInfo applicationInfo) {
-        // Make shallow copies so we can store the metadata safely
-        ServiceInfo si = new ServiceInfo();
-        assignSharedFieldsForComponentInfo(si, s);
-        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) {
-            si.metaData = s.getMetaData();
-        }
-        return si;
-    }
-
-    @Nullable
-    public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
-            int userId) {
-        return generateServiceInfo(pkg, s, flags, state, null, userId);
-    }
-
-    @Nullable
-    public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
-            @Nullable ApplicationInfo applicationInfo, int userId) {
-        if (p == null) return null;
-        if (!checkUseInstalled(pkg, state, flags)) {
-            return null;
-        }
-        if (applicationInfo == null) {
-            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
-        }
-        if (applicationInfo == null) {
-            return null;
-        }
-
-        return generateProviderInfoUnchecked(p, flags, applicationInfo);
-    }
-
-    /**
-     * This bypasses critical checks that are necessary for usage with data passed outside of system
-     * server.
-     * <p>
-     * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, long,
-     * FrameworkPackageUserState, ApplicationInfo, int)}.
-     */
-    @NonNull
-    public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
-            @PackageManager.ComponentInfoFlagsBits long flags,
-            @NonNull ApplicationInfo applicationInfo) {
-        // Make shallow copies so we can store the metadata safely
-        ProviderInfo pi = new ProviderInfo();
-        assignSharedFieldsForComponentInfo(pi, p);
-        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();
-        pi.pathPermissions = p.getPathPermissions();
-        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
-            pi.uriPermissionPatterns = null;
-        }
-        if ((flags & PackageManager.GET_META_DATA) != 0) {
-            pi.metaData = p.getMetaData();
-        }
-        pi.applicationInfo = applicationInfo;
-        return pi;
-    }
-
-    @Nullable
-    public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
-            int userId) {
-        return generateProviderInfo(pkg, p, flags, state, null, userId);
-    }
-
-    /**
-     * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead,
-     * long, FrameworkPackageUserState, int, boolean)}
-     */
-    @Nullable
-    public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
-            ParsingPackageRead pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId,
-            boolean assignUserFields) {
-        if (i == null) return null;
-
-        InstrumentationInfo ii = new InstrumentationInfo();
-        assignSharedFieldsForPackageItemInfo(ii, i);
-        ii.targetPackage = i.getTargetPackage();
-        ii.targetProcesses = i.getTargetProcesses();
-        ii.handleProfiling = i.isHandleProfiling();
-        ii.functionalTest = i.isFunctionalTest();
-
-        ii.sourceDir = pkg.getBaseApkPath();
-        ii.publicSourceDir = pkg.getBaseApkPath();
-        ii.splitNames = pkg.getSplitNames();
-        ii.splitSourceDirs = pkg.getSplitCodePaths();
-        ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
-        ii.splitDependencies = pkg.getSplitDependencies();
-
-        if (assignUserFields) {
-            assignUserFields(pkg, ii, userId);
-        }
-
-        if ((flags & PackageManager.GET_META_DATA) == 0) {
-            return ii;
-        }
-        ii.metaData = i.getMetaData();
-        return ii;
-    }
-
-    @Nullable
-    public static PermissionInfo generatePermissionInfo(ParsedPermission p,
-            @PackageManager.ComponentInfoFlagsBits long flags) {
-        if (p == null) return null;
-
-        PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
-
-        assignSharedFieldsForPackageItemInfo(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) {
-            return pi;
-        }
-        pi.metaData = p.getMetaData();
-        return pi;
-    }
-
-    @Nullable
-    public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
-            @PackageManager.ComponentInfoFlagsBits long flags) {
-        if (pg == null) return null;
-
-        PermissionGroupInfo pgi = new PermissionGroupInfo(
-                pg.getRequestDetailResourceId(),
-                pg.getBackgroundRequestResourceId(),
-                pg.getBackgroundRequestDetailResourceId()
-        );
-
-        assignSharedFieldsForPackageItemInfo(pgi, pg);
-        pgi.descriptionRes = pg.getDescriptionRes();
-        pgi.priority = pg.getPriority();
-        pgi.requestRes = pg.getRequestRes();
-        pgi.flags = pg.getFlags();
-
-        if ((flags & PackageManager.GET_META_DATA) == 0) {
-            return pgi;
-        }
-        pgi.metaData = pg.getMetaData();
-        return pgi;
-    }
-
-    @Nullable
-    public static Attribution generateAttribution(ParsedAttribution pa) {
-        if (pa == null) return null;
-        return new Attribution(pa.getTag(), pa.getLabel());
-    }
-
-    private static boolean checkUseInstalledOrHidden(long flags,
-            @NonNull FrameworkPackageUserState state, @Nullable ApplicationInfo appInfo) {
-        // Returns false if the package is hidden system app until installed.
-        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
-                && !state.isInstalled()
-                && appInfo != null && appInfo.hiddenUntilInstalled) {
-            return false;
-        }
-
-        // If available for the target user, or trying to match uninstalled packages and it's
-        // a system app.
-        return PackageUserStateUtils.isAvailable(state, flags)
-                || (appInfo != null && appInfo.isSystemApp()
-                && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
-                || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
-    }
-
-    private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
-            @NonNull ParsedMainComponent mainComponent) {
-        assignSharedFieldsForPackageItemInfo(componentInfo, mainComponent);
-        componentInfo.descriptionRes = mainComponent.getDescriptionRes();
-        componentInfo.directBootAware = mainComponent.isDirectBootAware();
-        componentInfo.enabled = mainComponent.isEnabled();
-        componentInfo.splitName = mainComponent.getSplitName();
-        componentInfo.attributionTags = mainComponent.getAttributionTags();
-    }
-
-    private static void assignSharedFieldsForPackageItemInfo(
-            @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();
-    }
-
-    @CheckResult
-    private static int flag(boolean hasFlag, int flag) {
-        if (hasFlag) {
-            return flag;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * @see ApplicationInfo#flags
-     */
-    public static int appInfoFlags(ParsingPackageRead pkg) {
-        // @formatter:off
-        return flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE)
-                | flag(pkg.isBaseHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED)
-                | flag(pkg.isAllowBackup(), ApplicationInfo.FLAG_ALLOW_BACKUP)
-                | flag(pkg.isKillAfterRestore(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
-                | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
-                | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY)
-                | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT)
-                | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE)
-                | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE)
-                | flag(pkg.isHasCode(), ApplicationInfo.FLAG_HAS_CODE)
-                | flag(pkg.isAllowTaskReparenting(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
-                | flag(pkg.isAllowClearUserData(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
-                | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP)
-                | flag(pkg.isUsesCleartextTraffic(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
-                | flag(pkg.isSupportsRtl(), ApplicationInfo.FLAG_SUPPORTS_RTL)
-                | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY)
-                | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH)
-                | flag(pkg.isExtractNativeLibs(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS)
-                | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME)
-                | flag(pkg.isSupportsSmallScreens(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS)
-                | flag(pkg.isSupportsNormalScreens(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS)
-                | flag(pkg.isSupportsLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS)
-                | flag(pkg.isSupportsExtraLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
-                | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
-                | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES);
-        // @formatter:on
-    }
-
-    /** @see ApplicationInfo#privateFlags */
-    public static int appInfoPrivateFlags(ParsingPackageRead pkg) {
-        // @formatter:off
-        int privateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY)
-                | flag(pkg.isOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY)
-                | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING)
-                | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS)
-                | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL)
-                | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND)
-                | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX)
-                | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
-                | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE)
-                | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE)
-                | flag(pkg.isAllowClearUserDataOnFailedRestore(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE)
-                | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE)
-                | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
-                | flag(pkg.isUsesNonSdkApi(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API)
-                | flag(pkg.isHasFragileUserData(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
-                | flag(pkg.isCantSaveState(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
-                | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
-                | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING);
-        // @formatter:on
-
-        Boolean resizeableActivity = pkg.getResizeableActivity();
-        if (resizeableActivity != null) {
-            if (resizeableActivity) {
-                privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
-            } else {
-                privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
-            }
-        }
-
-        return privateFlags;
-    }
-
-    /** @see ApplicationInfo#privateFlagsExt */
-    public static int appInfoPrivateFlagsExt(ParsingPackageRead pkg) {
-        // @formatter:off
-        int privateFlagsExt =
-                flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE)
-                | flag(pkg.hasRequestForegroundServiceExemption(),
-                        ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION)
-                | flag(pkg.areAttributionsUserVisible(),
-                        ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE);
-        // @formatter:on
-        return privateFlagsExt;
-    }
-
-    private static boolean checkUseInstalled(ParsingPackageRead pkg,
-            FrameworkPackageUserState state, @PackageManager.PackageInfoFlagsBits long flags) {
-        // If available for the target user
-        return PackageUserStateUtils.isAvailable(state, flags);
-    }
-
-    @NonNull
-    public static File getDataDir(ParsingPackageRead pkg, int userId) {
-        if ("android".equals(pkg.getPackageName())) {
-            return Environment.getDataSystemDirectory();
-        }
-
-        if (pkg.isDefaultToDeviceProtectedStorage()
-                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
-            return getDeviceProtectedDataDir(pkg, userId);
-        } else {
-            return getCredentialProtectedDataDir(pkg, userId);
-        }
-    }
-
-    @NonNull
-    public static File getDeviceProtectedDataDir(ParsingPackageRead pkg, int userId) {
-        return Environment.getDataUserDePackageDirectory(pkg.getVolumeUuid(), userId,
-                pkg.getPackageName());
-    }
-
-    @NonNull
-    public static File getCredentialProtectedDataDir(ParsingPackageRead pkg, int userId) {
-        return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId,
-                pkg.getPackageName());
-    }
-
-    private static void assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId) {
-        // This behavior is undefined for no-state ApplicationInfos when called by a public API,
-        // since the uid is never assigned by the system. It will always effectively be appId 0.
-        info.uid = UserHandle.getUid(userId, UserHandle.getAppId(info.uid));
-
-        String pkgName = pkg.getPackageName();
-        if ("android".equals(pkgName)) {
-            info.dataDir = SYSTEM_DATA_PATH;
-            return;
-        }
-
-        // For performance reasons, all these paths are built as strings
-        String baseDataDirPrefix =
-                Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator;
-        String userIdPkgSuffix = File.separator + userId + File.separator + pkgName;
-        info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE
-                + userIdPkgSuffix;
-        info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix;
-
-        if (pkg.isDefaultToDeviceProtectedStorage()
-                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
-            info.dataDir = info.deviceProtectedDataDir;
-        } else {
-            info.dataDir = info.credentialProtectedDataDir;
-        }
-    }
-
-    private static void assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info,
-            int userId) {
-        String pkgName = pkg.getPackageName();
-        if ("android".equals(pkgName)) {
-            info.dataDir = SYSTEM_DATA_PATH;
-            return;
-        }
-
-        // For performance reasons, all these paths are built as strings
-        String baseDataDirPrefix =
-                Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator;
-        String userIdPkgSuffix = File.separator + userId + File.separator + pkgName;
-        info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE
-                + userIdPkgSuffix;
-        info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix;
-
-        if (pkg.isDefaultToDeviceProtectedStorage()
-                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
-            info.dataDir = info.deviceProtectedDataDir;
-        } else {
-            info.dataDir = info.credentialProtectedDataDir;
-        }
-    }
-}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
deleted file mode 100644
index fc9f1a5..0000000
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedApexSystemService;
-import android.content.pm.parsing.component.ParsedAttribution;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-
-import java.security.PublicKey;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Methods used for mutation during direct package parsing.
- *
- * @hide
- */
-@SuppressWarnings("UnusedReturnValue")
-public interface ParsingPackage extends ParsingPackageRead {
-
-    ParsingPackage addActivity(ParsedActivity parsedActivity);
-
-    ParsingPackage addAdoptPermission(String adoptPermission);
-
-    ParsingPackage addApexSystemService(ParsedApexSystemService parsedApexSystemService);
-
-    ParsingPackage addConfigPreference(ConfigurationInfo configPreference);
-
-    ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup);
-
-    ParsingPackage addImplicitPermission(String permission);
-
-    ParsingPackage addInstrumentation(ParsedInstrumentation instrumentation);
-
-    ParsingPackage addKeySet(String keySetName, PublicKey publicKey);
-
-    ParsingPackage addLibraryName(String libraryName);
-
-    ParsingPackage addOriginalPackage(String originalPackage);
-
-    ParsingPackage addOverlayable(String overlayableName, String actorName);
-
-    ParsingPackage addPermission(ParsedPermission permission);
-
-    ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
-
-    ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo);
-
-    /** Add a property to the application scope */
-    ParsingPackage addProperty(Property property);
-
-    ParsingPackage addProtectedBroadcast(String protectedBroadcast);
-
-    ParsingPackage addProvider(ParsedProvider parsedProvider);
-
-    ParsingPackage addAttribution(ParsedAttribution attribution);
-
-    ParsingPackage addReceiver(ParsedActivity parsedReceiver);
-
-    ParsingPackage addReqFeature(FeatureInfo reqFeature);
-
-    ParsingPackage addUsesPermission(ParsedUsesPermission parsedUsesPermission);
-
-    ParsingPackage addService(ParsedService parsedService);
-
-    ParsingPackage addUsesLibrary(String libraryName);
-
-    ParsingPackage addUsesOptionalLibrary(String libraryName);
-
-    ParsingPackage addUsesNativeLibrary(String libraryName);
-
-    ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
-
-    ParsingPackage addUsesSdkLibrary(String libraryName, long versionMajor,
-            String[] certSha256Digests);
-
-    ParsingPackage addUsesStaticLibrary(String libraryName, long version,
-            String[] certSha256Digests);
-
-    ParsingPackage addQueriesIntent(Intent intent);
-
-    ParsingPackage addQueriesPackage(String packageName);
-
-    ParsingPackage addQueriesProvider(String authority);
-
-    /** Sets a process name -> {@link ParsedProcess} map coming from the <processes> tag. */
-    ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
-
-    ParsingPackage asSplit(
-            String[] splitNames,
-            String[] splitCodePaths,
-            int[] splitRevisionCodes,
-            @Nullable SparseArray<int[]> splitDependencies
-    );
-
-    ParsingPackage setMetaData(Bundle metaData);
-
-    ParsingPackage setForceQueryable(boolean forceQueryable);
-
-    ParsingPackage setMaxAspectRatio(float maxAspectRatio);
-
-    ParsingPackage setMinAspectRatio(float minAspectRatio);
-
-    ParsingPackage setPermission(String permission);
-
-    ParsingPackage setProcessName(String processName);
-
-    ParsingPackage setSharedUserId(String sharedUserId);
-
-    ParsingPackage setStaticSharedLibName(String staticSharedLibName);
-
-    ParsingPackage setTaskAffinity(String taskAffinity);
-
-    ParsingPackage setTargetSdkVersion(int targetSdkVersion);
-
-    ParsingPackage setUiOptions(int uiOptions);
-
-    ParsingPackage setBaseHardwareAccelerated(boolean baseHardwareAccelerated);
-
-    ParsingPackage setResizeableActivity(Boolean resizeable);
-
-    ParsingPackage setResizeableActivityViaSdkVersion(boolean resizeableViaSdkVersion);
-
-    ParsingPackage setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture);
-
-    ParsingPackage setAllowBackup(boolean allowBackup);
-
-    ParsingPackage setAllowClearUserData(boolean allowClearUserData);
-
-    ParsingPackage setAllowClearUserDataOnFailedRestore(boolean allowClearUserDataOnFailedRestore);
-
-    ParsingPackage setAllowTaskReparenting(boolean allowTaskReparenting);
-
-    ParsingPackage setOverlay(boolean isOverlay);
-
-    ParsingPackage setBackupInForeground(boolean backupInForeground);
-
-    ParsingPackage setCantSaveState(boolean cantSaveState);
-
-    ParsingPackage setDebuggable(boolean debuggable);
-
-    ParsingPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage);
-
-    ParsingPackage setDirectBootAware(boolean directBootAware);
-
-    ParsingPackage setExternalStorage(boolean externalStorage);
-
-    ParsingPackage setExtractNativeLibs(boolean extractNativeLibs);
-
-    ParsingPackage setFullBackupOnly(boolean fullBackupOnly);
-
-    ParsingPackage setHasCode(boolean hasCode);
-
-    ParsingPackage setHasFragileUserData(boolean hasFragileUserData);
-
-    ParsingPackage setGame(boolean isGame);
-
-    ParsingPackage setIsolatedSplitLoading(boolean isolatedSplitLoading);
-
-    ParsingPackage setKillAfterRestore(boolean killAfterRestore);
-
-    ParsingPackage setLargeHeap(boolean largeHeap);
-
-    ParsingPackage setMultiArch(boolean multiArch);
-
-    ParsingPackage setPartiallyDirectBootAware(boolean partiallyDirectBootAware);
-
-    ParsingPackage setPersistent(boolean persistent);
-
-    ParsingPackage setProfileableByShell(boolean profileableByShell);
-
-    ParsingPackage setProfileable(boolean profileable);
-
-    ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
-
-    ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
-
-    ParsingPackage setAutoRevokePermissions(int autoRevokePermissions);
-
-    ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
-
-    ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
-
-    ParsingPackage setSdkLibName(String sdkLibName);
-
-    ParsingPackage setSdkLibVersionMajor(int sdkLibVersionMajor);
-
-    ParsingPackage setSdkLibrary(boolean sdkLibrary);
-
-    ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
-
-    ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary);
-
-    ParsingPackage setSupportsRtl(boolean supportsRtl);
-
-    ParsingPackage setTestOnly(boolean testOnly);
-
-    ParsingPackage setUseEmbeddedDex(boolean useEmbeddedDex);
-
-    ParsingPackage setUsesCleartextTraffic(boolean usesCleartextTraffic);
-
-    ParsingPackage setUsesNonSdkApi(boolean usesNonSdkApi);
-
-    ParsingPackage setVisibleToInstantApps(boolean visibleToInstantApps);
-
-    ParsingPackage setVmSafeMode(boolean vmSafeMode);
-
-    ParsingPackage removeUsesOptionalLibrary(String libraryName);
-
-    ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
-
-    ParsingPackage setAnyDensity(int anyDensity);
-
-    ParsingPackage setAppComponentFactory(String appComponentFactory);
-
-    ParsingPackage setBackupAgentName(String backupAgentName);
-
-    ParsingPackage setBanner(int banner);
-
-    ParsingPackage setCategory(int category);
-
-    ParsingPackage setClassLoaderName(String classLoaderName);
-
-    ParsingPackage setClassName(String className);
-
-    ParsingPackage setCompatibleWidthLimitDp(int compatibleWidthLimitDp);
-
-    ParsingPackage setDescriptionRes(int descriptionRes);
-
-    ParsingPackage setEnabled(boolean enabled);
-
-    ParsingPackage setGwpAsanMode(@ApplicationInfo.GwpAsanMode int gwpAsanMode);
-
-    ParsingPackage setMemtagMode(@ApplicationInfo.MemtagMode int memtagMode);
-
-    ParsingPackage setNativeHeapZeroInitialized(
-            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
-
-    ParsingPackage setRequestRawExternalStorageAccess(
-            @Nullable Boolean requestRawExternalStorageAccess);
-
-    ParsingPackage setCrossProfile(boolean crossProfile);
-
-    ParsingPackage setFullBackupContent(int fullBackupContent);
-
-    ParsingPackage setDataExtractionRules(int dataExtractionRules);
-
-    ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
-
-    ParsingPackage setIconRes(int iconRes);
-
-    ParsingPackage setInstallLocation(int installLocation);
-
-    ParsingPackage setLabelRes(int labelRes);
-
-    ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
-
-    ParsingPackage setLogo(int logo);
-
-    ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
-
-    ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions);
-
-    ParsingPackage setMinSdkVersion(int minSdkVersion);
-
-    ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
-
-    ParsingPackage setNonLocalizedLabel(CharSequence nonLocalizedLabel);
-
-    ParsingPackage setOverlayCategory(String overlayCategory);
-
-    ParsingPackage setOverlayIsStatic(boolean overlayIsStatic);
-
-    ParsingPackage setOverlayPriority(int overlayPriority);
-
-    ParsingPackage setOverlayTarget(String overlayTarget);
-
-    ParsingPackage setOverlayTargetOverlayableName(String overlayTargetOverlayableName);
-
-    ParsingPackage setRequiredAccountType(String requiredAccountType);
-
-    ParsingPackage setRequiredForAllUsers(boolean requiredForAllUsers);
-
-    ParsingPackage setRequiresSmallestWidthDp(int requiresSmallestWidthDp);
-
-    ParsingPackage setResizeable(int resizeable);
-
-    ParsingPackage setRestrictUpdateHash(byte[] restrictUpdateHash);
-
-    ParsingPackage setRestrictedAccountType(String restrictedAccountType);
-
-    ParsingPackage setRoundIconRes(int roundIconRes);
-
-    ParsingPackage setSharedUserLabel(int sharedUserLabel);
-
-    ParsingPackage setSigningDetails(SigningDetails signingDetails);
-
-    ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
-
-    ParsingPackage setStaticSharedLibVersion(long staticSharedLibVersion);
-
-    ParsingPackage setSupportsLargeScreens(int supportsLargeScreens);
-
-    ParsingPackage setSupportsNormalScreens(int supportsNormalScreens);
-
-    ParsingPackage setSupportsSmallScreens(int supportsSmallScreens);
-
-    ParsingPackage setSupportsExtraLargeScreens(int supportsExtraLargeScreens);
-
-    ParsingPackage setTargetSandboxVersion(int targetSandboxVersion);
-
-    ParsingPackage setTheme(int theme);
-
-    ParsingPackage setRequestForegroundServiceExemption(boolean requestForegroundServiceExemption);
-
-    ParsingPackage setUpgradeKeySets(@NonNull Set<String> upgradeKeySets);
-
-    ParsingPackage setUse32BitAbi(boolean use32BitAbi);
-
-    ParsingPackage setVolumeUuid(@Nullable String volumeUuid);
-
-    ParsingPackage setZygotePreloadName(String zygotePreloadName);
-
-    ParsingPackage sortActivities();
-
-    ParsingPackage sortReceivers();
-
-    ParsingPackage sortServices();
-
-    ParsingPackage setBaseRevisionCode(int baseRevisionCode);
-
-    ParsingPackage setVersionName(String versionName);
-
-    ParsingPackage setCompileSdkVersion(int compileSdkVersion);
-
-    ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName);
-
-    ParsingPackage setAttributionsAreUserVisible(boolean attributionsAreUserVisible);
-
-    ParsingPackage setResetEnabledSettingsOnAppDataCleared(
-            boolean resetEnabledSettingsOnAppDataCleared);
-
-    // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
-    //  for moving to the next step
-    @CallSuper
-    Object hideAsParsed();
-}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageHidden.java b/core/java/android/content/pm/parsing/ParsingPackageHidden.java
deleted file mode 100644
index c49d11e..0000000
--- a/core/java/android/content/pm/parsing/ParsingPackageHidden.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-
-/**
- * Methods that normal consumers should not have access to. This usually means the field is stateful
- * or deprecated and should be access through a utility class or a system manager class.
- * <p>
- * This is a separate interface, not implemented by the base {@link ParsingPackageRead} because Java
- * doesn't support non-public interface methods. The class must be cast to this interface.
- *
- * @hide
- */
-interface ParsingPackageHidden {
-
-    /**
-     * @see PackageInfo#versionCode
-     * @see ApplicationInfo#versionCode
-     */
-    int getVersionCode();
-
-    /**
-     * @see PackageInfo#versionCodeMajor
-     */
-    int getVersionCodeMajor();
-
-    // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
-    ApplicationInfo toAppInfoWithoutState();
-}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
deleted file mode 100644
index dbd3d5c..0000000
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ /dev/null
@@ -1,2939 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import static java.util.Collections.emptyList;
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.emptySet;
-
-import android.annotation.CallSuper;
-import android.annotation.LongDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityImpl;
-import android.content.pm.parsing.component.ParsedApexSystemService;
-import android.content.pm.parsing.component.ParsedApexSystemServiceImpl;
-import android.content.pm.parsing.component.ParsedAttribution;
-import android.content.pm.parsing.component.ParsedAttributionImpl;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedInstrumentationImpl;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedPermissionGroupImpl;
-import android.content.pm.parsing.component.ParsedPermissionImpl;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedProviderImpl;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.component.ParsedServiceImpl;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.storage.StorageManager;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArray;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringList;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap;
-import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
-
-import java.security.PublicKey;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * The backing data for a package that was parsed from disk.
- *
- * The field nullability annotations here are for internal reference. For effective nullability,
- * see the parent interfaces.
- *
- * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case
- *
- * @hide
- */
-public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, Parcelable {
-
-    public static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
-    public static ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
-            ForInternedString.class);
-    public static ForInternedStringArray sForInternedStringArray = Parcelling.Cache.getOrCreate(
-            ForInternedStringArray.class);
-    public static ForInternedStringList sForInternedStringList = Parcelling.Cache.getOrCreate(
-            ForInternedStringList.class);
-    public static ForInternedStringValueMap sForInternedStringValueMap =
-            Parcelling.Cache.getOrCreate(ForInternedStringValueMap.class);
-    public static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
-    public static ForInternedStringSet sForInternedStringSet =
-            Parcelling.Cache.getOrCreate(ForInternedStringSet.class);
-    protected static ParsingUtils.StringPairListParceler sForIntentInfoPairs =
-            new ParsingUtils.StringPairListParceler();
-
-    private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
-            (first, second) -> Integer.compare(second.getOrder(), first.getOrder());
-
-    // These are objects because null represents not explicitly set
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean supportsSmallScreens;
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean supportsNormalScreens;
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean supportsLargeScreens;
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean supportsExtraLargeScreens;
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean resizeable;
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean anyDensity;
-
-    protected int versionCode;
-    protected int versionCodeMajor;
-    private int baseRevisionCode;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String versionName;
-
-    private int compileSdkVersion;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String compileSdkVersionCodeName;
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    protected String packageName;
-
-    @NonNull
-    protected String mBaseApkPath;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String restrictedAccountType;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String requiredAccountType;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String overlayTarget;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String overlayTargetOverlayableName;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String overlayCategory;
-    private int overlayPriority;
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringValueMap.class)
-    private Map<String, String> overlayables = emptyMap();
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String sdkLibName;
-    private int sdkLibVersionMajor;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String staticSharedLibName;
-    private long staticSharedLibVersion;
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    private List<String> libraryNames = emptyList();
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> usesLibraries = emptyList();
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> usesOptionalLibraries = emptyList();
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> usesNativeLibraries = emptyList();
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> usesOptionalNativeLibraries = emptyList();
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    private List<String> usesStaticLibraries = emptyList();
-    @Nullable
-    private long[] usesStaticLibrariesVersions;
-    @Nullable
-    private String[][] usesStaticLibrariesCertDigests;
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    private List<String> usesSdkLibraries = emptyList();
-    @Nullable
-    private long[] usesSdkLibrariesVersionsMajor;
-    @Nullable
-    private String[][] usesSdkLibrariesCertDigests;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String sharedUserId;
-
-    private int sharedUserLabel;
-    @NonNull
-    private List<ConfigurationInfo> configPreferences = emptyList();
-    @NonNull
-    private List<FeatureInfo> reqFeatures = emptyList();
-    @NonNull
-    private List<FeatureGroupInfo> featureGroups = emptyList();
-
-    @Nullable
-    private byte[] restrictUpdateHash;
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> originalPackages = emptyList();
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> adoptPermissions = emptyList();
-    /**
-     * @deprecated consider migrating to {@link #getUsesPermissions} which has
-     *             more parsed details, such as flags
-     */
-    @NonNull
-    @Deprecated
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> requestedPermissions = emptyList();
-
-    @NonNull
-    private List<ParsedUsesPermission> usesPermissions = emptyList();
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    private List<String> implicitPermissions = emptyList();
-
-    @NonNull
-    private Set<String> upgradeKeySets = emptySet();
-    @NonNull
-    private Map<String, ArraySet<PublicKey>> keySetMapping = emptyMap();
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    protected List<String> protectedBroadcasts = emptyList();
-
-    @NonNull
-    protected List<ParsedActivity> activities = emptyList();
-
-    @NonNull
-    protected List<ParsedApexSystemService> apexSystemServices = emptyList();
-
-    @NonNull
-    protected List<ParsedActivity> receivers = emptyList();
-
-    @NonNull
-    protected List<ParsedService> services = emptyList();
-
-    @NonNull
-    protected List<ParsedProvider> providers = emptyList();
-
-    @NonNull
-    private List<ParsedAttribution> attributions = emptyList();
-
-    @NonNull
-    protected List<ParsedPermission> permissions = emptyList();
-
-    @NonNull
-    protected List<ParsedPermissionGroup> permissionGroups = emptyList();
-
-    @NonNull
-    protected List<ParsedInstrumentation> instrumentations = emptyList();
-
-    @NonNull
-//    @DataClass.ParcelWith(ParsingUtils.StringPairListParceler.class)
-    private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
-
-    /**
-     * Map from a process name to a {@link ParsedProcess}.
-     */
-    @NonNull
-    private Map<String, ParsedProcess> processes = emptyMap();
-
-    @Nullable
-    private Bundle metaData;
-
-    @NonNull
-    private Map<String, Property> mProperties = emptyMap();
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    protected String volumeUuid;
-    @Nullable
-    private SigningDetails signingDetails;
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    protected String mPath;
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    private List<Intent> queriesIntents = emptyList();
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringList.class)
-    private List<String> queriesPackages = emptyList();
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedStringSet.class)
-    private Set<String> queriesProviders = emptySet();
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedStringArray.class)
-    private String[] splitClassLoaderNames;
-    @Nullable
-    protected String[] splitCodePaths;
-    @Nullable
-    private SparseArray<int[]> splitDependencies;
-    @Nullable
-    private int[] splitFlags;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedStringArray.class)
-    private String[] splitNames;
-    @Nullable
-    private int[] splitRevisionCodes;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String appComponentFactory;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String backupAgentName;
-    private int banner;
-    private int category = ApplicationInfo.CATEGORY_UNDEFINED;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String classLoaderName;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String className;
-    private int compatibleWidthLimitDp;
-    private int descriptionRes;
-
-    private int fullBackupContent;
-    private int dataExtractionRules;
-    private int iconRes;
-    private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION;
-    private int labelRes;
-    private int largestWidthLimitDp;
-    private int logo;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String manageSpaceActivityName;
-    private float maxAspectRatio;
-    private float minAspectRatio;
-    @Nullable
-    private SparseIntArray minExtensionVersions;
-    private int minSdkVersion = ParsingUtils.DEFAULT_MIN_SDK_VERSION;
-    private int networkSecurityConfigRes;
-    @Nullable
-    private CharSequence nonLocalizedLabel;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String permission;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String processName;
-    private int requiresSmallestWidthDp;
-    private int roundIconRes;
-    private int targetSandboxVersion;
-    private int targetSdkVersion = ParsingUtils.DEFAULT_TARGET_SDK_VERSION;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String taskAffinity;
-    private int theme;
-
-    private int uiOptions;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String zygotePreloadName;
-
-    /**
-     * @see ParsingPackageRead#getResizeableActivity()
-     */
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean resizeableActivity;
-
-    private int autoRevokePermissions;
-
-    @ApplicationInfo.GwpAsanMode
-    private int gwpAsanMode;
-
-    @ApplicationInfo.MemtagMode
-    private int memtagMode;
-
-    @ApplicationInfo.NativeHeapZeroInitialized
-    private int nativeHeapZeroInitialized;
-
-    @Nullable
-    @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean requestRawExternalStorageAccess;
-
-    // TODO(chiuwinson): Non-null
-    @Nullable
-    private ArraySet<String> mimeGroups;
-
-    // Usually there's code to set enabled to true during parsing, but it's possible to install
-    // an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
-    // and never assign this, so initialize this to true for those cases.
-    private long mBooleans = Booleans.ENABLED;
-
-    /**
-     * Flags used for a internal bitset. These flags should never be persisted or exposed outside
-     * of this class. It is expected that PackageCacher explicitly clears itself whenever the
-     * Parcelable implementation changes such that all these flags can be re-ordered or invalidated.
-     */
-    protected static class Booleans {
-        @LongDef({
-                EXTERNAL_STORAGE,
-                BASE_HARDWARE_ACCELERATED,
-                ALLOW_BACKUP,
-                KILL_AFTER_RESTORE,
-                RESTORE_ANY_VERSION,
-                FULL_BACKUP_ONLY,
-                PERSISTENT,
-                DEBUGGABLE,
-                VM_SAFE_MODE,
-                HAS_CODE,
-                ALLOW_TASK_REPARENTING,
-                ALLOW_CLEAR_USER_DATA,
-                LARGE_HEAP,
-                USES_CLEARTEXT_TRAFFIC,
-                SUPPORTS_RTL,
-                TEST_ONLY,
-                MULTI_ARCH,
-                EXTRACT_NATIVE_LIBS,
-                GAME,
-                STATIC_SHARED_LIBRARY,
-                OVERLAY,
-                ISOLATED_SPLIT_LOADING,
-                HAS_DOMAIN_URLS,
-                PROFILEABLE_BY_SHELL,
-                BACKUP_IN_FOREGROUND,
-                USE_EMBEDDED_DEX,
-                DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
-                DIRECT_BOOT_AWARE,
-                PARTIALLY_DIRECT_BOOT_AWARE,
-                RESIZEABLE_ACTIVITY_VIA_SDK_VERSION,
-                ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
-                ALLOW_AUDIO_PLAYBACK_CAPTURE,
-                REQUEST_LEGACY_EXTERNAL_STORAGE,
-                USES_NON_SDK_API,
-                HAS_FRAGILE_USER_DATA,
-                CANT_SAVE_STATE,
-                ALLOW_NATIVE_HEAP_POINTER_TAGGING,
-                PRESERVE_LEGACY_EXTERNAL_STORAGE,
-                REQUIRED_FOR_ALL_USERS,
-                OVERLAY_IS_STATIC,
-                USE_32_BIT_ABI,
-                VISIBLE_TO_INSTANT_APPS,
-                FORCE_QUERYABLE,
-                CROSS_PROFILE,
-                ENABLED,
-                DISALLOW_PROFILING,
-                REQUEST_FOREGROUND_SERVICE_EXEMPTION,
-        })
-        public @interface Values {}
-        private static final long EXTERNAL_STORAGE = 1L;
-        private static final long BASE_HARDWARE_ACCELERATED = 1L << 1;
-        private static final long ALLOW_BACKUP = 1L << 2;
-        private static final long KILL_AFTER_RESTORE = 1L << 3;
-        private static final long RESTORE_ANY_VERSION = 1L << 4;
-        private static final long FULL_BACKUP_ONLY = 1L << 5;
-        private static final long PERSISTENT = 1L << 6;
-        private static final long DEBUGGABLE = 1L << 7;
-        private static final long VM_SAFE_MODE = 1L << 8;
-        private static final long HAS_CODE = 1L << 9;
-        private static final long ALLOW_TASK_REPARENTING = 1L << 10;
-        private static final long ALLOW_CLEAR_USER_DATA = 1L << 11;
-        private static final long LARGE_HEAP = 1L << 12;
-        private static final long USES_CLEARTEXT_TRAFFIC = 1L << 13;
-        private static final long SUPPORTS_RTL = 1L << 14;
-        private static final long TEST_ONLY = 1L << 15;
-        private static final long MULTI_ARCH = 1L << 16;
-        private static final long EXTRACT_NATIVE_LIBS = 1L << 17;
-        private static final long GAME = 1L << 18;
-        private static final long STATIC_SHARED_LIBRARY = 1L << 19;
-        private static final long OVERLAY = 1L << 20;
-        private static final long ISOLATED_SPLIT_LOADING = 1L << 21;
-        private static final long HAS_DOMAIN_URLS = 1L << 22;
-        private static final long PROFILEABLE_BY_SHELL = 1L << 23;
-        private static final long BACKUP_IN_FOREGROUND = 1L << 24;
-        private static final long USE_EMBEDDED_DEX = 1L << 25;
-        private static final long DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1L << 26;
-        private static final long DIRECT_BOOT_AWARE = 1L << 27;
-        private static final long PARTIALLY_DIRECT_BOOT_AWARE = 1L << 28;
-        private static final long RESIZEABLE_ACTIVITY_VIA_SDK_VERSION = 1L << 29;
-        private static final long ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1L << 30;
-        private static final long ALLOW_AUDIO_PLAYBACK_CAPTURE = 1L << 31;
-        private static final long REQUEST_LEGACY_EXTERNAL_STORAGE = 1L << 32;
-        private static final long USES_NON_SDK_API = 1L << 33;
-        private static final long HAS_FRAGILE_USER_DATA = 1L << 34;
-        private static final long CANT_SAVE_STATE = 1L << 35;
-        private static final long ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1L << 36;
-        private static final long PRESERVE_LEGACY_EXTERNAL_STORAGE = 1L << 37;
-        private static final long REQUIRED_FOR_ALL_USERS = 1L << 38;
-        private static final long OVERLAY_IS_STATIC = 1L << 39;
-        private static final long USE_32_BIT_ABI = 1L << 40;
-        private static final long VISIBLE_TO_INSTANT_APPS = 1L << 41;
-        private static final long FORCE_QUERYABLE = 1L << 42;
-        private static final long CROSS_PROFILE = 1L << 43;
-        private static final long ENABLED = 1L << 44;
-        private static final long DISALLOW_PROFILING = 1L << 45;
-        private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46;
-        private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47;
-        private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
-        private static final long SDK_LIBRARY = 1L << 49;
-    }
-
-    private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
-        if (value) {
-            mBooleans |= flag;
-        } else {
-            mBooleans &= ~flag;
-        }
-        return this;
-    }
-
-    private boolean getBoolean(@Booleans.Values long flag) {
-        return (mBooleans & flag) != 0;
-    }
-
-    // Derived fields
-    @NonNull
-    private UUID mStorageUuid;
-    private long mLongVersionCode;
-
-    @VisibleForTesting
-    public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
-            @NonNull String path, @Nullable TypedArray manifestArray) {
-        this.packageName = TextUtils.safeIntern(packageName);
-        this.mBaseApkPath = baseApkPath;
-        this.mPath = path;
-
-        if (manifestArray != null) {
-            versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
-            versionCodeMajor = manifestArray.getInteger(
-                    R.styleable.AndroidManifest_versionCodeMajor, 0);
-            setBaseRevisionCode(
-                    manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode, 0));
-            setVersionName(manifestArray.getNonConfigurationString(
-                    R.styleable.AndroidManifest_versionName, 0));
-
-            setCompileSdkVersion(manifestArray.getInteger(
-                    R.styleable.AndroidManifest_compileSdkVersion, 0));
-            setCompileSdkVersionCodeName(manifestArray.getNonConfigurationString(
-                    R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
-
-            setIsolatedSplitLoading(manifestArray.getBoolean(
-                    R.styleable.AndroidManifest_isolatedSplits, false));
-
-        }
-    }
-
-    public boolean isSupportsSmallScreens() {
-        if (supportsSmallScreens == null) {
-            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
-        }
-
-        return supportsSmallScreens;
-    }
-
-    public boolean isSupportsNormalScreens() {
-        return supportsNormalScreens == null || supportsNormalScreens;
-    }
-
-    public boolean isSupportsLargeScreens() {
-        if (supportsLargeScreens == null) {
-            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
-        }
-
-        return supportsLargeScreens;
-    }
-
-    public boolean isSupportsExtraLargeScreens() {
-        if (supportsExtraLargeScreens == null) {
-            return targetSdkVersion >= Build.VERSION_CODES.GINGERBREAD;
-        }
-
-        return supportsExtraLargeScreens;
-    }
-
-    public boolean isResizeable() {
-        if (resizeable == null) {
-            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
-        }
-
-        return resizeable;
-    }
-
-    public boolean isAnyDensity() {
-        if (anyDensity == null) {
-            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
-        }
-
-        return anyDensity;
-    }
-
-    @Override
-    public ParsingPackageImpl sortActivities() {
-        Collections.sort(this.activities, ORDER_COMPARATOR);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl sortReceivers() {
-        Collections.sort(this.receivers, ORDER_COMPARATOR);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl sortServices() {
-        Collections.sort(this.services, ORDER_COMPARATOR);
-        return this;
-    }
-
-    @CallSuper
-    @Override
-    public Object hideAsParsed() {
-        assignDerivedFields();
-        return this;
-    }
-
-    private void assignDerivedFields() {
-        mStorageUuid = StorageManager.convert(volumeUuid);
-        mLongVersionCode = PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
-    }
-
-    @Override
-    public ParsingPackageImpl addConfigPreference(ConfigurationInfo configPreference) {
-        this.configPreferences = CollectionUtils.add(this.configPreferences, configPreference);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addReqFeature(FeatureInfo reqFeature) {
-        this.reqFeatures = CollectionUtils.add(this.reqFeatures, reqFeature);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) {
-        this.featureGroups = CollectionUtils.add(this.featureGroups, featureGroup);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addProperty(@Nullable Property property) {
-        if (property == null) {
-            return this;
-        }
-        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) {
-        if (!this.protectedBroadcasts.contains(protectedBroadcast)) {
-            this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts,
-                    TextUtils.safeIntern(protectedBroadcast));
-        }
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addInstrumentation(ParsedInstrumentation instrumentation) {
-        this.instrumentations = CollectionUtils.add(this.instrumentations, instrumentation);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addOriginalPackage(String originalPackage) {
-        this.originalPackages = CollectionUtils.add(this.originalPackages, originalPackage);
-        return this;
-    }
-
-    @Override
-    public ParsingPackage addOverlayable(String overlayableName, String actorName) {
-        this.overlayables = CollectionUtils.add(this.overlayables, overlayableName,
-                TextUtils.safeIntern(actorName));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addAdoptPermission(String adoptPermission) {
-        this.adoptPermissions = CollectionUtils.add(this.adoptPermissions,
-                TextUtils.safeIntern(adoptPermission));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addPermission(ParsedPermission permission) {
-        this.permissions = CollectionUtils.add(this.permissions, permission);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) {
-        this.permissionGroups = CollectionUtils.add(this.permissionGroups, permissionGroup);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addUsesPermission(ParsedUsesPermission permission) {
-        this.usesPermissions = CollectionUtils.add(this.usesPermissions, permission);
-
-        // Continue populating legacy data structures to avoid performance
-        // issues until all that code can be migrated
-        this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
-                permission.getName());
-
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addImplicitPermission(String permission) {
-        addUsesPermission(new ParsedUsesPermissionImpl(permission, 0 /*usesPermissionFlags*/));
-        this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
-                TextUtils.safeIntern(permission));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addKeySet(String keySetName, PublicKey publicKey) {
-        ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName);
-        if (publicKeys == null) {
-            publicKeys = new ArraySet<>();
-        }
-        publicKeys.add(publicKey);
-        keySetMapping = CollectionUtils.add(this.keySetMapping, keySetName, publicKeys);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addActivity(ParsedActivity parsedActivity) {
-        this.activities = CollectionUtils.add(this.activities, parsedActivity);
-        addMimeGroupsFromComponent(parsedActivity);
-        return this;
-    }
-
-    @Override
-    public final ParsingPackageImpl addApexSystemService(
-            ParsedApexSystemService parsedApexSystemService) {
-        this.apexSystemServices = CollectionUtils.add(
-                this.apexSystemServices, parsedApexSystemService);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
-        this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
-        addMimeGroupsFromComponent(parsedReceiver);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addService(ParsedService parsedService) {
-        this.services = CollectionUtils.add(this.services, parsedService);
-        addMimeGroupsFromComponent(parsedService);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addProvider(ParsedProvider parsedProvider) {
-        this.providers = CollectionUtils.add(this.providers, parsedProvider);
-        addMimeGroupsFromComponent(parsedProvider);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addAttribution(ParsedAttribution attribution) {
-        this.attributions = CollectionUtils.add(this.attributions, attribution);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addLibraryName(String libraryName) {
-        this.libraryNames = CollectionUtils.add(this.libraryNames,
-                TextUtils.safeIntern(libraryName));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addUsesOptionalLibrary(String libraryName) {
-        this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
-                TextUtils.safeIntern(libraryName));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addUsesLibrary(String libraryName) {
-        this.usesLibraries = CollectionUtils.add(this.usesLibraries,
-                TextUtils.safeIntern(libraryName));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl removeUsesOptionalLibrary(String libraryName) {
-        this.usesOptionalLibraries = CollectionUtils.remove(this.usesOptionalLibraries,
-                libraryName);
-        return this;
-    }
-
-    @Override
-    public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
-        this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
-                TextUtils.safeIntern(libraryName));
-        return this;
-    }
-
-    @Override
-    public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
-        this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
-                TextUtils.safeIntern(libraryName));
-        return this;
-    }
-
-    @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
-        this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
-                libraryName);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addUsesSdkLibrary(String libraryName, long versionMajor,
-            String[] certSha256Digests) {
-        this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries,
-                TextUtils.safeIntern(libraryName));
-        this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
-                this.usesSdkLibrariesVersionsMajor, versionMajor, true);
-        this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
-                this.usesSdkLibrariesCertDigests, certSha256Digests, true);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addUsesStaticLibrary(String libraryName, long version,
-            String[] certSha256Digests) {
-        this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
-                TextUtils.safeIntern(libraryName));
-        this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
-                version, true);
-        this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
-                this.usesStaticLibrariesCertDigests, certSha256Digests, true);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addPreferredActivityFilter(String className,
-            ParsedIntentInfo intentInfo) {
-        this.preferredActivityFilters = CollectionUtils.add(this.preferredActivityFilters,
-                Pair.create(className, intentInfo));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addQueriesIntent(Intent intent) {
-        this.queriesIntents = CollectionUtils.add(this.queriesIntents, intent);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addQueriesPackage(String packageName) {
-        this.queriesPackages = CollectionUtils.add(this.queriesPackages,
-                TextUtils.safeIntern(packageName));
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl addQueriesProvider(String authority) {
-        this.queriesProviders = CollectionUtils.add(this.queriesProviders, authority);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
-        if (supportsSmallScreens == 1) {
-            return this;
-        }
-
-        this.supportsSmallScreens = supportsSmallScreens < 0;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSupportsNormalScreens(int supportsNormalScreens) {
-        if (supportsNormalScreens == 1) {
-            return this;
-        }
-
-        this.supportsNormalScreens = supportsNormalScreens < 0;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSupportsLargeScreens(int supportsLargeScreens) {
-        if (supportsLargeScreens == 1) {
-            return this;
-        }
-
-        this.supportsLargeScreens = supportsLargeScreens < 0;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSupportsExtraLargeScreens(int supportsExtraLargeScreens) {
-        if (supportsExtraLargeScreens == 1) {
-            return this;
-        }
-
-        this.supportsExtraLargeScreens = supportsExtraLargeScreens < 0;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setResizeable(int resizeable) {
-        if (resizeable == 1) {
-            return this;
-        }
-
-        this.resizeable = resizeable < 0;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setAnyDensity(int anyDensity) {
-        if (anyDensity == 1) {
-            return this;
-        }
-
-        this.anyDensity = anyDensity < 0;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl asSplit(String[] splitNames, String[] splitCodePaths,
-            int[] splitRevisionCodes, SparseArray<int[]> splitDependencies) {
-        this.splitNames = splitNames;
-        this.splitCodePaths = splitCodePaths;
-        this.splitRevisionCodes = splitRevisionCodes;
-        this.splitDependencies = splitDependencies;
-
-        int count = splitNames.length;
-        this.splitFlags = new int[count];
-        this.splitClassLoaderNames = new String[count];
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) {
-        this.splitFlags[splitIndex] = splitHasCode
-                ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE
-                : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) {
-        this.splitClassLoaderNames[splitIndex] = classLoaderName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRequiredAccountType(@Nullable String requiredAccountType) {
-        this.requiredAccountType = TextUtils.nullIfEmpty(requiredAccountType);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setOverlayTarget(@Nullable String overlayTarget) {
-        this.overlayTarget = TextUtils.safeIntern(overlayTarget);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setVolumeUuid(@Nullable String volumeUuid) {
-        this.volumeUuid = TextUtils.safeIntern(volumeUuid);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setStaticSharedLibName(String staticSharedLibName) {
-        this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSharedUserId(String sharedUserId) {
-        this.sharedUserId = TextUtils.safeIntern(sharedUserId);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) {
-        nonLocalizedLabel = value == null ? null : value.toString().trim();
-        return this;
-    }
-
-    @NonNull
-    @Override
-    public String getProcessName() {
-        return processName != null ? processName : packageName;
-    }
-
-    @Override
-    public String toString() {
-        return "Package{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + packageName + "}";
-    }
-
-    @Deprecated
-    @Override
-    public ApplicationInfo toAppInfoWithoutState() {
-        ApplicationInfo appInfo = toAppInfoWithoutStateWithoutFlags();
-        appInfo.flags = PackageInfoWithoutStateUtils.appInfoFlags(this);
-        appInfo.privateFlags = PackageInfoWithoutStateUtils.appInfoPrivateFlags(this);
-        appInfo.privateFlagsExt = PackageInfoWithoutStateUtils.appInfoPrivateFlagsExt(this);
-        return appInfo;
-    }
-
-    public ApplicationInfo toAppInfoWithoutStateWithoutFlags() {
-        ApplicationInfo appInfo = new ApplicationInfo();
-
-        // Lines that are commented below are state related and should not be assigned here.
-        // They are left in as placeholders, since there is no good backwards compatible way to
-        // separate these.
-        appInfo.appComponentFactory = appComponentFactory;
-        appInfo.backupAgentName = backupAgentName;
-        appInfo.banner = banner;
-        appInfo.category = category;
-        appInfo.classLoaderName = classLoaderName;
-        appInfo.className = className;
-        appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
-        appInfo.compileSdkVersion = compileSdkVersion;
-        appInfo.compileSdkVersionCodename = compileSdkVersionCodeName;
-//        appInfo.credentialProtectedDataDir
-        appInfo.crossProfile = isCrossProfile();
-//        appInfo.dataDir
-        appInfo.descriptionRes = descriptionRes;
-//        appInfo.deviceProtectedDataDir
-        appInfo.enabled = getBoolean(Booleans.ENABLED);
-//        appInfo.enabledSetting
-        appInfo.fullBackupContent = fullBackupContent;
-        appInfo.dataExtractionRulesRes = dataExtractionRules;
-        // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
-//        appInfo.mHiddenApiPolicy
-//        appInfo.hiddenUntilInstalled
-        appInfo.icon =
-                (ParsingPackageUtils.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
-        appInfo.iconRes = iconRes;
-        appInfo.roundIconRes = roundIconRes;
-        appInfo.installLocation = installLocation;
-        appInfo.labelRes = labelRes;
-        appInfo.largestWidthLimitDp = largestWidthLimitDp;
-        appInfo.logo = logo;
-        appInfo.manageSpaceActivityName = manageSpaceActivityName;
-        appInfo.maxAspectRatio = maxAspectRatio;
-        appInfo.metaData = metaData;
-        appInfo.minAspectRatio = minAspectRatio;
-        appInfo.minSdkVersion = minSdkVersion;
-        appInfo.name = className;
-//        appInfo.nativeLibraryDir
-//        appInfo.nativeLibraryRootDir
-//        appInfo.nativeLibraryRootRequiresIsa
-        appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
-        appInfo.nonLocalizedLabel = nonLocalizedLabel;
-        appInfo.packageName = packageName;
-        appInfo.permission = permission;
-//        appInfo.primaryCpuAbi
-        appInfo.processName = getProcessName();
-        appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
-//        appInfo.resourceDirs
-//        appInfo.secondaryCpuAbi
-//        appInfo.secondaryNativeLibraryDir
-//        appInfo.seInfo
-//        appInfo.seInfoUser
-//        appInfo.sharedLibraryFiles
-//        appInfo.sharedLibraryInfos
-//        appInfo.showUserIcon
-        appInfo.splitClassLoaderNames = splitClassLoaderNames;
-        appInfo.splitDependencies = splitDependencies;
-        appInfo.splitNames = splitNames;
-        appInfo.storageUuid = mStorageUuid;
-        appInfo.targetSandboxVersion = targetSandboxVersion;
-        appInfo.targetSdkVersion = targetSdkVersion;
-        appInfo.taskAffinity = taskAffinity;
-        appInfo.theme = theme;
-//        appInfo.uid
-        appInfo.uiOptions = uiOptions;
-        appInfo.volumeUuid = volumeUuid;
-        appInfo.zygotePreloadName = zygotePreloadName;
-        appInfo.setGwpAsanMode(gwpAsanMode);
-        appInfo.setMemtagMode(memtagMode);
-        appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
-        appInfo.setRequestRawExternalStorageAccess(requestRawExternalStorageAccess);
-        appInfo.setBaseCodePath(mBaseApkPath);
-        appInfo.setBaseResourcePath(mBaseApkPath);
-        appInfo.setCodePath(mPath);
-        appInfo.setResourcePath(mPath);
-        appInfo.setSplitCodePaths(splitCodePaths);
-        appInfo.setSplitResourcePaths(splitCodePaths);
-        appInfo.setVersionCode(mLongVersionCode);
-        appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
-
-        return appInfo;
-    }
-
-    /**
-     * Create a map from a process name to the custom application class for this process,
-     * which comes from <processes><process android:name="xxx">.
-     *
-     * The original information is stored in {@link #processes}, but it's stored in
-     * a form of: [process name] -[1:N]-> [package name] -[1:N]-> [class name].
-     * We scan it and collect the process names and their app class names, only for this package.
-     *
-     * The resulting map only contains processes with a custom application class set.
-     */
-    @Nullable
-    private ArrayMap<String, String> buildAppClassNamesByProcess() {
-        if (processes == null) {
-            return null;
-        }
-        final ArrayMap<String, String> ret = new ArrayMap<>(4);
-        for (String processName : processes.keySet()) {
-            final ParsedProcess process = processes.get(processName);
-            final ArrayMap<String, String> appClassesByPackage =
-                    process.getAppClassNamesByPackage();
-
-            for (int i = 0; i < appClassesByPackage.size(); i++) {
-                final String packageName = appClassesByPackage.keyAt(i);
-
-                if (this.packageName.equals(packageName)) {
-                    final String appClassName = appClassesByPackage.valueAt(i);
-                    if (!TextUtils.isEmpty(appClassName)) {
-                        ret.put(processName, appClassName);
-                    }
-                }
-            }
-        }
-        return ret;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
-        sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
-        sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
-        sForBoolean.parcel(this.supportsExtraLargeScreens, dest, flags);
-        sForBoolean.parcel(this.resizeable, dest, flags);
-        sForBoolean.parcel(this.anyDensity, dest, flags);
-        dest.writeInt(this.versionCode);
-        dest.writeInt(this.versionCodeMajor);
-        dest.writeInt(this.baseRevisionCode);
-        sForInternedString.parcel(this.versionName, dest, flags);
-        dest.writeInt(this.compileSdkVersion);
-        dest.writeString(this.compileSdkVersionCodeName);
-        sForInternedString.parcel(this.packageName, dest, flags);
-        dest.writeString(this.mBaseApkPath);
-        dest.writeString(this.restrictedAccountType);
-        dest.writeString(this.requiredAccountType);
-        sForInternedString.parcel(this.overlayTarget, dest, flags);
-        dest.writeString(this.overlayTargetOverlayableName);
-        dest.writeString(this.overlayCategory);
-        dest.writeInt(this.overlayPriority);
-        sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
-        sForInternedString.parcel(this.sdkLibName, dest, flags);
-        dest.writeInt(this.sdkLibVersionMajor);
-        sForInternedString.parcel(this.staticSharedLibName, dest, flags);
-        dest.writeLong(this.staticSharedLibVersion);
-        sForInternedStringList.parcel(this.libraryNames, dest, flags);
-        sForInternedStringList.parcel(this.usesLibraries, dest, flags);
-        sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
-        sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
-        sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
-
-        sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
-        dest.writeLongArray(this.usesStaticLibrariesVersions);
-        if (this.usesStaticLibrariesCertDigests == null) {
-            dest.writeInt(-1);
-        } else {
-            dest.writeInt(this.usesStaticLibrariesCertDigests.length);
-            for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) {
-                dest.writeStringArray(this.usesStaticLibrariesCertDigests[index]);
-            }
-        }
-
-        sForInternedStringList.parcel(this.usesSdkLibraries, dest, flags);
-        dest.writeLongArray(this.usesSdkLibrariesVersionsMajor);
-        if (this.usesSdkLibrariesCertDigests == null) {
-            dest.writeInt(-1);
-        } else {
-            dest.writeInt(this.usesSdkLibrariesCertDigests.length);
-            for (int index = 0; index < this.usesSdkLibrariesCertDigests.length; index++) {
-                dest.writeStringArray(this.usesSdkLibrariesCertDigests[index]);
-            }
-        }
-
-        sForInternedString.parcel(this.sharedUserId, dest, flags);
-        dest.writeInt(this.sharedUserLabel);
-        dest.writeTypedList(this.configPreferences);
-        dest.writeTypedList(this.reqFeatures);
-        dest.writeTypedList(this.featureGroups);
-        dest.writeByteArray(this.restrictUpdateHash);
-        dest.writeStringList(this.originalPackages);
-        sForInternedStringList.parcel(this.adoptPermissions, dest, flags);
-        sForInternedStringList.parcel(this.requestedPermissions, dest, flags);
-        dest.writeTypedList(this.usesPermissions);
-        sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
-        sForStringSet.parcel(this.upgradeKeySets, dest, flags);
-        ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
-        sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
-        dest.writeTypedList(this.activities);
-        dest.writeTypedList(this.apexSystemServices);
-        dest.writeTypedList(this.receivers);
-        dest.writeTypedList(this.services);
-        dest.writeTypedList(this.providers);
-        dest.writeTypedList(this.attributions);
-        dest.writeTypedList(this.permissions);
-        dest.writeTypedList(this.permissionGroups);
-        dest.writeTypedList(this.instrumentations);
-        sForIntentInfoPairs.parcel(this.preferredActivityFilters, dest, flags);
-        dest.writeMap(this.processes);
-        dest.writeBundle(this.metaData);
-        sForInternedString.parcel(this.volumeUuid, dest, flags);
-        dest.writeParcelable(this.signingDetails, flags);
-        dest.writeString(this.mPath);
-        dest.writeTypedList(this.queriesIntents, flags);
-        sForInternedStringList.parcel(this.queriesPackages, dest, flags);
-        sForInternedStringSet.parcel(this.queriesProviders, dest, flags);
-        dest.writeString(this.appComponentFactory);
-        dest.writeString(this.backupAgentName);
-        dest.writeInt(this.banner);
-        dest.writeInt(this.category);
-        dest.writeString(this.classLoaderName);
-        dest.writeString(this.className);
-        dest.writeInt(this.compatibleWidthLimitDp);
-        dest.writeInt(this.descriptionRes);
-        dest.writeInt(this.fullBackupContent);
-        dest.writeInt(this.dataExtractionRules);
-        dest.writeInt(this.iconRes);
-        dest.writeInt(this.installLocation);
-        dest.writeInt(this.labelRes);
-        dest.writeInt(this.largestWidthLimitDp);
-        dest.writeInt(this.logo);
-        dest.writeString(this.manageSpaceActivityName);
-        dest.writeFloat(this.maxAspectRatio);
-        dest.writeFloat(this.minAspectRatio);
-        dest.writeInt(this.minSdkVersion);
-        dest.writeInt(this.networkSecurityConfigRes);
-        dest.writeCharSequence(this.nonLocalizedLabel);
-        dest.writeString(this.permission);
-        dest.writeString(this.processName);
-        dest.writeInt(this.requiresSmallestWidthDp);
-        dest.writeInt(this.roundIconRes);
-        dest.writeInt(this.targetSandboxVersion);
-        dest.writeInt(this.targetSdkVersion);
-        dest.writeString(this.taskAffinity);
-        dest.writeInt(this.theme);
-        dest.writeInt(this.uiOptions);
-        dest.writeString(this.zygotePreloadName);
-        dest.writeStringArray(this.splitClassLoaderNames);
-        dest.writeStringArray(this.splitCodePaths);
-        dest.writeSparseArray(this.splitDependencies);
-        dest.writeIntArray(this.splitFlags);
-        dest.writeStringArray(this.splitNames);
-        dest.writeIntArray(this.splitRevisionCodes);
-        sForBoolean.parcel(this.resizeableActivity, dest, flags);
-        dest.writeInt(this.autoRevokePermissions);
-        dest.writeArraySet(this.mimeGroups);
-        dest.writeInt(this.gwpAsanMode);
-        dest.writeSparseIntArray(this.minExtensionVersions);
-        dest.writeLong(this.mBooleans);
-        dest.writeMap(this.mProperties);
-        dest.writeInt(this.memtagMode);
-        dest.writeInt(this.nativeHeapZeroInitialized);
-        sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
-    }
-
-    public ParsingPackageImpl(Parcel in) {
-        // We use the boot classloader for all classes that we load.
-        final ClassLoader boot = Object.class.getClassLoader();
-        this.supportsSmallScreens = sForBoolean.unparcel(in);
-        this.supportsNormalScreens = sForBoolean.unparcel(in);
-        this.supportsLargeScreens = sForBoolean.unparcel(in);
-        this.supportsExtraLargeScreens = sForBoolean.unparcel(in);
-        this.resizeable = sForBoolean.unparcel(in);
-        this.anyDensity = sForBoolean.unparcel(in);
-        this.versionCode = in.readInt();
-        this.versionCodeMajor = in.readInt();
-        this.baseRevisionCode = in.readInt();
-        this.versionName = sForInternedString.unparcel(in);
-        this.compileSdkVersion = in.readInt();
-        this.compileSdkVersionCodeName = in.readString();
-        this.packageName = sForInternedString.unparcel(in);
-        this.mBaseApkPath = in.readString();
-        this.restrictedAccountType = in.readString();
-        this.requiredAccountType = in.readString();
-        this.overlayTarget = sForInternedString.unparcel(in);
-        this.overlayTargetOverlayableName = in.readString();
-        this.overlayCategory = in.readString();
-        this.overlayPriority = in.readInt();
-        this.overlayables = sForInternedStringValueMap.unparcel(in);
-        this.sdkLibName = sForInternedString.unparcel(in);
-        this.sdkLibVersionMajor = in.readInt();
-        this.staticSharedLibName = sForInternedString.unparcel(in);
-        this.staticSharedLibVersion = in.readLong();
-        this.libraryNames = sForInternedStringList.unparcel(in);
-        this.usesLibraries = sForInternedStringList.unparcel(in);
-        this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
-        this.usesNativeLibraries = sForInternedStringList.unparcel(in);
-        this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
-
-        this.usesStaticLibraries = sForInternedStringList.unparcel(in);
-        this.usesStaticLibrariesVersions = in.createLongArray();
-        {
-            int digestsSize = in.readInt();
-            if (digestsSize >= 0) {
-                this.usesStaticLibrariesCertDigests = new String[digestsSize][];
-                for (int index = 0; index < digestsSize; index++) {
-                    this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(
-                            in);
-                }
-            }
-        }
-
-        this.usesSdkLibraries = sForInternedStringList.unparcel(in);
-        this.usesSdkLibrariesVersionsMajor = in.createLongArray();
-        {
-            int digestsSize = in.readInt();
-            if (digestsSize >= 0) {
-                this.usesSdkLibrariesCertDigests = new String[digestsSize][];
-                for (int index = 0; index < digestsSize; index++) {
-                    this.usesSdkLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
-                }
-            }
-        }
-
-        this.sharedUserId = sForInternedString.unparcel(in);
-        this.sharedUserLabel = in.readInt();
-        this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);
-        this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);
-        this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR);
-        this.restrictUpdateHash = in.createByteArray();
-        this.originalPackages = in.createStringArrayList();
-        this.adoptPermissions = sForInternedStringList.unparcel(in);
-        this.requestedPermissions = sForInternedStringList.unparcel(in);
-        this.usesPermissions = ParsingUtils.createTypedInterfaceList(in,
-                ParsedUsesPermissionImpl.CREATOR);
-        this.implicitPermissions = sForInternedStringList.unparcel(in);
-        this.upgradeKeySets = sForStringSet.unparcel(in);
-        this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in);
-        this.protectedBroadcasts = sForInternedStringList.unparcel(in);
-
-        this.activities = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
-        this.apexSystemServices = ParsingUtils.createTypedInterfaceList(in,
-                ParsedApexSystemServiceImpl.CREATOR);
-        this.receivers = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
-        this.services = ParsingUtils.createTypedInterfaceList(in, ParsedServiceImpl.CREATOR);
-        this.providers = ParsingUtils.createTypedInterfaceList(in, ParsedProviderImpl.CREATOR);
-        this.attributions = ParsingUtils.createTypedInterfaceList(in,
-                ParsedAttributionImpl.CREATOR);
-        this.permissions = ParsingUtils.createTypedInterfaceList(in, ParsedPermissionImpl.CREATOR);
-        this.permissionGroups = ParsingUtils.createTypedInterfaceList(in,
-                ParsedPermissionGroupImpl.CREATOR);
-        this.instrumentations = ParsingUtils.createTypedInterfaceList(in,
-                ParsedInstrumentationImpl.CREATOR);
-        this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
-        this.processes = in.readHashMap(boot);
-        this.metaData = in.readBundle(boot);
-        this.volumeUuid = sForInternedString.unparcel(in);
-        this.signingDetails = in.readParcelable(boot, android.content.pm.SigningDetails.class);
-        this.mPath = in.readString();
-        this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
-        this.queriesPackages = sForInternedStringList.unparcel(in);
-        this.queriesProviders = sForInternedStringSet.unparcel(in);
-        this.appComponentFactory = in.readString();
-        this.backupAgentName = in.readString();
-        this.banner = in.readInt();
-        this.category = in.readInt();
-        this.classLoaderName = in.readString();
-        this.className = in.readString();
-        this.compatibleWidthLimitDp = in.readInt();
-        this.descriptionRes = in.readInt();
-        this.fullBackupContent = in.readInt();
-        this.dataExtractionRules = in.readInt();
-        this.iconRes = in.readInt();
-        this.installLocation = in.readInt();
-        this.labelRes = in.readInt();
-        this.largestWidthLimitDp = in.readInt();
-        this.logo = in.readInt();
-        this.manageSpaceActivityName = in.readString();
-        this.maxAspectRatio = in.readFloat();
-        this.minAspectRatio = in.readFloat();
-        this.minSdkVersion = in.readInt();
-        this.networkSecurityConfigRes = in.readInt();
-        this.nonLocalizedLabel = in.readCharSequence();
-        this.permission = in.readString();
-        this.processName = in.readString();
-        this.requiresSmallestWidthDp = in.readInt();
-        this.roundIconRes = in.readInt();
-        this.targetSandboxVersion = in.readInt();
-        this.targetSdkVersion = in.readInt();
-        this.taskAffinity = in.readString();
-        this.theme = in.readInt();
-        this.uiOptions = in.readInt();
-        this.zygotePreloadName = in.readString();
-        this.splitClassLoaderNames = in.createStringArray();
-        this.splitCodePaths = in.createStringArray();
-        this.splitDependencies = in.readSparseArray(boot);
-        this.splitFlags = in.createIntArray();
-        this.splitNames = in.createStringArray();
-        this.splitRevisionCodes = in.createIntArray();
-        this.resizeableActivity = sForBoolean.unparcel(in);
-
-        this.autoRevokePermissions = in.readInt();
-        this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
-        this.gwpAsanMode = in.readInt();
-        this.minExtensionVersions = in.readSparseIntArray();
-        this.mBooleans = in.readLong();
-        this.mProperties = in.readHashMap(boot);
-        this.memtagMode = in.readInt();
-        this.nativeHeapZeroInitialized = in.readInt();
-        this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
-        assignDerivedFields();
-    }
-
-    public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
-            new Parcelable.Creator<ParsingPackageImpl>() {
-                @Override
-                public ParsingPackageImpl createFromParcel(Parcel source) {
-                    return new ParsingPackageImpl(source);
-                }
-
-                @Override
-                public ParsingPackageImpl[] newArray(int size) {
-                    return new ParsingPackageImpl[size];
-                }
-            };
-
-    @Override
-    public int getVersionCode() {
-        return versionCode;
-    }
-
-    @Override
-    public int getVersionCodeMajor() {
-        return versionCodeMajor;
-    }
-
-    @Override
-    public long getLongVersionCode() {
-        return mLongVersionCode;
-    }
-
-    @Override
-    public int getBaseRevisionCode() {
-        return baseRevisionCode;
-    }
-
-    @Nullable
-    @Override
-    public String getVersionName() {
-        return versionName;
-    }
-
-    @Override
-    public int getCompileSdkVersion() {
-        return compileSdkVersion;
-    }
-
-    @Nullable
-    @Override
-    public String getCompileSdkVersionCodeName() {
-        return compileSdkVersionCodeName;
-    }
-
-    @NonNull
-    @Override
-    public String getPackageName() {
-        return packageName;
-    }
-
-    @NonNull
-    @Override
-    public String getBaseApkPath() {
-        return mBaseApkPath;
-    }
-
-    @Override
-    public boolean isRequiredForAllUsers() {
-        return getBoolean(Booleans.REQUIRED_FOR_ALL_USERS);
-    }
-
-    @Nullable
-    @Override
-    public String getRestrictedAccountType() {
-        return restrictedAccountType;
-    }
-
-    @Nullable
-    @Override
-    public String getRequiredAccountType() {
-        return requiredAccountType;
-    }
-
-    @Nullable
-    @Override
-    public String getOverlayTarget() {
-        return overlayTarget;
-    }
-
-    @Nullable
-    @Override
-    public String getOverlayTargetOverlayableName() {
-        return overlayTargetOverlayableName;
-    }
-
-    @Nullable
-    @Override
-    public String getOverlayCategory() {
-        return overlayCategory;
-    }
-
-    @Override
-    public int getOverlayPriority() {
-        return overlayPriority;
-    }
-
-    @Override
-    public boolean isOverlayIsStatic() {
-        return getBoolean(Booleans.OVERLAY_IS_STATIC);
-    }
-
-    @NonNull
-    @Override
-    public Map<String,String> getOverlayables() {
-        return overlayables;
-    }
-
-    @Nullable
-    @Override
-    public String getSdkLibName() {
-        return sdkLibName;
-    }
-
-    @Override
-    public int getSdkLibVersionMajor() {
-        return sdkLibVersionMajor;
-    }
-
-    @Nullable
-    @Override
-    public String getStaticSharedLibName() {
-        return staticSharedLibName;
-    }
-
-    @Override
-    public long getStaticSharedLibVersion() {
-        return staticSharedLibVersion;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getLibraryNames() {
-        return libraryNames;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getUsesLibraries() {
-        return usesLibraries;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getUsesOptionalLibraries() {
-        return usesOptionalLibraries;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getUsesNativeLibraries() {
-        return usesNativeLibraries;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getUsesOptionalNativeLibraries() {
-        return usesOptionalNativeLibraries;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getUsesStaticLibraries() {
-        return usesStaticLibraries;
-    }
-
-    @Nullable
-    @Override
-    public long[] getUsesStaticLibrariesVersions() {
-        return usesStaticLibrariesVersions;
-    }
-
-    @Nullable
-    @Override
-    public String[][] getUsesStaticLibrariesCertDigests() {
-        return usesStaticLibrariesCertDigests;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getUsesSdkLibraries() { return usesSdkLibraries; }
-
-    @Nullable
-    @Override
-    public long[] getUsesSdkLibrariesVersionsMajor() { return usesSdkLibrariesVersionsMajor; }
-
-    @Nullable
-    @Override
-    public String[][] getUsesSdkLibrariesCertDigests() { return usesSdkLibrariesCertDigests; }
-
-    @Nullable
-    @Override
-    public String getSharedUserId() {
-        return sharedUserId;
-    }
-
-    @Override
-    public int getSharedUserLabel() {
-        return sharedUserLabel;
-    }
-
-    @NonNull
-    @Override
-    public List<ConfigurationInfo> getConfigPreferences() {
-        return configPreferences;
-    }
-
-    @NonNull
-    @Override
-    public List<FeatureInfo> getRequestedFeatures() {
-        return reqFeatures;
-    }
-
-    @NonNull
-    @Override
-    public List<FeatureGroupInfo> getFeatureGroups() {
-        return featureGroups;
-    }
-
-    @Nullable
-    @Override
-    public byte[] getRestrictUpdateHash() {
-        return restrictUpdateHash;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getOriginalPackages() {
-        return originalPackages;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getAdoptPermissions() {
-        return adoptPermissions;
-    }
-
-    /**
-     * @deprecated consider migrating to {@link #getUsesPermissions} which has
-     *             more parsed details, such as flags
-     */
-    @NonNull
-    @Override
-    @Deprecated
-    public List<String> getRequestedPermissions() {
-        return requestedPermissions;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedUsesPermission> getUsesPermissions() {
-        return usesPermissions;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getImplicitPermissions() {
-        return implicitPermissions;
-    }
-
-    @NonNull
-    @Override
-    public Map<String, Property> getProperties() {
-        return mProperties;
-    }
-
-    @NonNull
-    @Override
-    public Set<String> getUpgradeKeySets() {
-        return upgradeKeySets;
-    }
-
-    @NonNull
-    @Override
-    public Map<String,ArraySet<PublicKey>> getKeySetMapping() {
-        return keySetMapping;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getProtectedBroadcasts() {
-        return protectedBroadcasts;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedActivity> getActivities() {
-        return activities;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedApexSystemService> getApexSystemServices() {
-        return apexSystemServices;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedActivity> getReceivers() {
-        return receivers;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedService> getServices() {
-        return services;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedProvider> getProviders() {
-        return providers;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedAttribution> getAttributions() {
-        return attributions;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedPermission> getPermissions() {
-        return permissions;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedPermissionGroup> getPermissionGroups() {
-        return permissionGroups;
-    }
-
-    @NonNull
-    @Override
-    public List<ParsedInstrumentation> getInstrumentations() {
-        return instrumentations;
-    }
-
-    @NonNull
-    @Override
-    public List<Pair<String,ParsedIntentInfo>> getPreferredActivityFilters() {
-        return preferredActivityFilters;
-    }
-
-    @NonNull
-    @Override
-    public Map<String,ParsedProcess> getProcesses() {
-        return processes;
-    }
-
-    @Nullable
-    @Override
-    public Bundle getMetaData() {
-        return metaData;
-    }
-
-    private void addMimeGroupsFromComponent(ParsedComponent component) {
-        for (int i = component.getIntents().size() - 1; i >= 0; i--) {
-            IntentFilter filter = component.getIntents().get(i).getIntentFilter();
-            for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
-                mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
-            }
-        }
-    }
-
-    @Override
-    @Nullable
-    public Set<String> getMimeGroups() {
-        return mimeGroups;
-    }
-
-    @Nullable
-    @Override
-    public String getVolumeUuid() {
-        return volumeUuid;
-    }
-
-    @Nullable
-    @Override
-    public SigningDetails getSigningDetails() {
-        return signingDetails;
-    }
-
-    @NonNull
-    @Override
-    public String getPath() {
-        return mPath;
-    }
-
-    @Override
-    public boolean isUse32BitAbi() {
-        return getBoolean(Booleans.USE_32_BIT_ABI);
-    }
-
-    @Override
-    public boolean isVisibleToInstantApps() {
-        return getBoolean(Booleans.VISIBLE_TO_INSTANT_APPS);
-    }
-
-    @Override
-    public boolean isForceQueryable() {
-        return getBoolean(Booleans.FORCE_QUERYABLE);
-    }
-
-    @NonNull
-    @Override
-    public List<Intent> getQueriesIntents() {
-        return queriesIntents;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getQueriesPackages() {
-        return queriesPackages;
-    }
-
-    @NonNull
-    @Override
-    public Set<String> getQueriesProviders() {
-        return queriesProviders;
-    }
-
-    @Nullable
-    @Override
-    public String[] getSplitClassLoaderNames() {
-        return splitClassLoaderNames;
-    }
-
-    @Nullable
-    @Override
-    public String[] getSplitCodePaths() {
-        return splitCodePaths;
-    }
-
-    @Nullable
-    @Override
-    public SparseArray<int[]> getSplitDependencies() {
-        return splitDependencies;
-    }
-
-    @Nullable
-    @Override
-    public int[] getSplitFlags() {
-        return splitFlags;
-    }
-
-    @Nullable
-    @Override
-    public String[] getSplitNames() {
-        return splitNames;
-    }
-
-    @Nullable
-    @Override
-    public int[] getSplitRevisionCodes() {
-        return splitRevisionCodes;
-    }
-
-    @Nullable
-    @Override
-    public String getAppComponentFactory() {
-        return appComponentFactory;
-    }
-
-    @Nullable
-    @Override
-    public String getBackupAgentName() {
-        return backupAgentName;
-    }
-
-    @Override
-    public int getBanner() {
-        return banner;
-    }
-
-    @Override
-    public int getCategory() {
-        return category;
-    }
-
-    @Nullable
-    @Override
-    public String getClassLoaderName() {
-        return classLoaderName;
-    }
-
-    @Nullable
-    @Override
-    public String getClassName() {
-        return className;
-    }
-
-    @Override
-    public int getCompatibleWidthLimitDp() {
-        return compatibleWidthLimitDp;
-    }
-
-    @Override
-    public int getDescriptionRes() {
-        return descriptionRes;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return getBoolean(Booleans.ENABLED);
-    }
-
-    @Override
-    public boolean isCrossProfile() {
-        return getBoolean(Booleans.CROSS_PROFILE);
-    }
-
-    @Override
-    public int getFullBackupContent() {
-        return fullBackupContent;
-    }
-
-    @Override
-    public int getDataExtractionRules() {
-        return dataExtractionRules;
-    }
-
-    @Override
-    public int getIconRes() {
-        return iconRes;
-    }
-
-    @Override
-    public int getInstallLocation() {
-        return installLocation;
-    }
-
-    @Override
-    public int getLabelRes() {
-        return labelRes;
-    }
-
-    @Override
-    public int getLargestWidthLimitDp() {
-        return largestWidthLimitDp;
-    }
-
-    @Override
-    public int getLogo() {
-        return logo;
-    }
-
-    @Nullable
-    @Override
-    public String getManageSpaceActivityName() {
-        return manageSpaceActivityName;
-    }
-
-    @Override
-    public float getMaxAspectRatio() {
-        return maxAspectRatio;
-    }
-
-    @Override
-    public float getMinAspectRatio() {
-        return minAspectRatio;
-    }
-
-    @Nullable
-    @Override
-    public SparseIntArray getMinExtensionVersions() {
-        return minExtensionVersions;
-    }
-
-    @Override
-    public int getMinSdkVersion() {
-        return minSdkVersion;
-    }
-
-    @Override
-    public int getNetworkSecurityConfigRes() {
-        return networkSecurityConfigRes;
-    }
-
-    @Nullable
-    @Override
-    public CharSequence getNonLocalizedLabel() {
-        return nonLocalizedLabel;
-    }
-
-    @Nullable
-    @Override
-    public String getPermission() {
-        return permission;
-    }
-
-    @Override
-    public int getRequiresSmallestWidthDp() {
-        return requiresSmallestWidthDp;
-    }
-
-    @Override
-    public int getRoundIconRes() {
-        return roundIconRes;
-    }
-
-    @Override
-    public int getTargetSdkVersion() {
-        return targetSdkVersion;
-    }
-
-    @Override
-    public int getTargetSandboxVersion() {
-        return targetSandboxVersion;
-    }
-
-    @Nullable
-    @Override
-    public String getTaskAffinity() {
-        return taskAffinity;
-    }
-
-    @Override
-    public int getTheme() {
-        return theme;
-    }
-
-    @Override
-    public int getUiOptions() {
-        return uiOptions;
-    }
-
-    @Nullable
-    @Override
-    public String getZygotePreloadName() {
-        return zygotePreloadName;
-    }
-
-    @Override
-    public boolean isExternalStorage() {
-        return getBoolean(Booleans.EXTERNAL_STORAGE);
-    }
-
-    @Override
-    public boolean isBaseHardwareAccelerated() {
-        return getBoolean(Booleans.BASE_HARDWARE_ACCELERATED);
-    }
-
-    @Override
-    public boolean isAllowBackup() {
-        return getBoolean(Booleans.ALLOW_BACKUP);
-    }
-
-    @Override
-    public boolean isKillAfterRestore() {
-        return getBoolean(Booleans.KILL_AFTER_RESTORE);
-    }
-
-    @Override
-    public boolean isRestoreAnyVersion() {
-        return getBoolean(Booleans.RESTORE_ANY_VERSION);
-    }
-
-    @Override
-    public boolean isFullBackupOnly() {
-        return getBoolean(Booleans.FULL_BACKUP_ONLY);
-    }
-
-    @Override
-    public boolean isPersistent() {
-        return getBoolean(Booleans.PERSISTENT);
-    }
-
-    @Override
-    public boolean isDebuggable() {
-        return getBoolean(Booleans.DEBUGGABLE);
-    }
-
-    @Override
-    public boolean isVmSafeMode() {
-        return getBoolean(Booleans.VM_SAFE_MODE);
-    }
-
-    @Override
-    public boolean isHasCode() {
-        return getBoolean(Booleans.HAS_CODE);
-    }
-
-    @Override
-    public boolean isAllowTaskReparenting() {
-        return getBoolean(Booleans.ALLOW_TASK_REPARENTING);
-    }
-
-    @Override
-    public boolean isAllowClearUserData() {
-        return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA);
-    }
-
-    @Override
-    public boolean isLargeHeap() {
-        return getBoolean(Booleans.LARGE_HEAP);
-    }
-
-    @Override
-    public boolean isUsesCleartextTraffic() {
-        return getBoolean(Booleans.USES_CLEARTEXT_TRAFFIC);
-    }
-
-    @Override
-    public boolean isSupportsRtl() {
-        return getBoolean(Booleans.SUPPORTS_RTL);
-    }
-
-    @Override
-    public boolean isTestOnly() {
-        return getBoolean(Booleans.TEST_ONLY);
-    }
-
-    @Override
-    public boolean isMultiArch() {
-        return getBoolean(Booleans.MULTI_ARCH);
-    }
-
-    @Override
-    public boolean isExtractNativeLibs() {
-        return getBoolean(Booleans.EXTRACT_NATIVE_LIBS);
-    }
-
-    @Override
-    public boolean isGame() {
-        return getBoolean(Booleans.GAME);
-    }
-
-    /**
-     * @see ParsingPackageRead#getResizeableActivity()
-     */
-    @Nullable
-    @Override
-    public Boolean getResizeableActivity() {
-        return resizeableActivity;
-    }
-
-    @Override
-    public boolean isStaticSharedLibrary() {
-        return getBoolean(Booleans.STATIC_SHARED_LIBRARY);
-    }
-
-    @Override
-    public boolean isSdkLibrary() {
-        return getBoolean(Booleans.SDK_LIBRARY);
-    }
-
-    @Override
-    public boolean isOverlay() {
-        return getBoolean(Booleans.OVERLAY);
-    }
-
-    @Override
-    public boolean isIsolatedSplitLoading() {
-        return getBoolean(Booleans.ISOLATED_SPLIT_LOADING);
-    }
-
-    @Override
-    public boolean isHasDomainUrls() {
-        return getBoolean(Booleans.HAS_DOMAIN_URLS);
-    }
-
-    @Override
-    public boolean isProfileableByShell() {
-        return isProfileable() && getBoolean(Booleans.PROFILEABLE_BY_SHELL);
-    }
-
-    @Override
-    public boolean isProfileable() {
-        return !getBoolean(Booleans.DISALLOW_PROFILING);
-    }
-
-    @Override
-    public boolean isBackupInForeground() {
-        return getBoolean(Booleans.BACKUP_IN_FOREGROUND);
-    }
-
-    @Override
-    public boolean isUseEmbeddedDex() {
-        return getBoolean(Booleans.USE_EMBEDDED_DEX);
-    }
-
-    @Override
-    public boolean isDefaultToDeviceProtectedStorage() {
-        return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE);
-    }
-
-    @Override
-    public boolean isDirectBootAware() {
-        return getBoolean(Booleans.DIRECT_BOOT_AWARE);
-    }
-
-    @ApplicationInfo.GwpAsanMode
-    @Override
-    public int getGwpAsanMode() {
-        return gwpAsanMode;
-    }
-
-    @ApplicationInfo.MemtagMode
-    @Override
-    public int getMemtagMode() {
-        return memtagMode;
-    }
-
-    @ApplicationInfo.NativeHeapZeroInitialized
-    @Override
-    public int getNativeHeapZeroInitialized() {
-        return nativeHeapZeroInitialized;
-    }
-
-    @Nullable
-    @Override
-    public Boolean hasRequestRawExternalStorageAccess() {
-        return requestRawExternalStorageAccess;
-    }
-
-    @Override
-    public boolean isPartiallyDirectBootAware() {
-        return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE);
-    }
-
-    @Override
-    public boolean isResizeableActivityViaSdkVersion() {
-        return getBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION);
-    }
-
-    @Override
-    public boolean isAllowClearUserDataOnFailedRestore() {
-        return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE);
-    }
-
-    @Override
-    public boolean isAllowAudioPlaybackCapture() {
-        return getBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE);
-    }
-
-    @Override
-    public boolean isRequestLegacyExternalStorage() {
-        return getBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE);
-    }
-
-    @Override
-    public boolean isUsesNonSdkApi() {
-        return getBoolean(Booleans.USES_NON_SDK_API);
-    }
-
-    @Override
-    public boolean isHasFragileUserData() {
-        return getBoolean(Booleans.HAS_FRAGILE_USER_DATA);
-    }
-
-    @Override
-    public boolean isCantSaveState() {
-        return getBoolean(Booleans.CANT_SAVE_STATE);
-    }
-
-    @Override
-    public boolean isAllowNativeHeapPointerTagging() {
-        return getBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING);
-    }
-
-    @Override
-    public int getAutoRevokePermissions() {
-        return autoRevokePermissions;
-    }
-
-    @Override
-    public boolean hasPreserveLegacyExternalStorage() {
-        return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE);
-    }
-
-    @Override
-    public boolean hasRequestForegroundServiceExemption() {
-        return getBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION);
-    }
-
-    @Override
-    public boolean areAttributionsUserVisible() {
-        return getBoolean(Booleans.ATTRIBUTIONS_ARE_USER_VISIBLE);
-    }
-
-    @Override
-    public boolean isResetEnabledSettingsOnAppDataCleared() {
-        return getBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED);
-    }
-
-    @Override
-    public ParsingPackageImpl setBaseRevisionCode(int value) {
-        baseRevisionCode = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setCompileSdkVersion(int value) {
-        compileSdkVersion = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRequiredForAllUsers(boolean value) {
-        return setBoolean(Booleans.REQUIRED_FOR_ALL_USERS, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setOverlayPriority(int value) {
-        overlayPriority = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setOverlayIsStatic(boolean value) {
-        return setBoolean(Booleans.OVERLAY_IS_STATIC, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setStaticSharedLibVersion(long value) {
-        staticSharedLibVersion = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSharedUserLabel(int value) {
-        sharedUserLabel = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRestrictUpdateHash(@Nullable byte... value) {
-        restrictUpdateHash = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setUpgradeKeySets(@NonNull Set<String> value) {
-        upgradeKeySets = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setProcesses(@NonNull Map<String,ParsedProcess> value) {
-        processes = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setMetaData(@Nullable Bundle value) {
-        metaData = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
-        signingDetails = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setUse32BitAbi(boolean value) {
-        return setBoolean(Booleans.USE_32_BIT_ABI, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setVisibleToInstantApps(boolean value) {
-        return setBoolean(Booleans.VISIBLE_TO_INSTANT_APPS, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setForceQueryable(boolean value) {
-        return setBoolean(Booleans.FORCE_QUERYABLE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setBanner(int value) {
-        banner = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setCategory(int value) {
-        category = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setCompatibleWidthLimitDp(int value) {
-        compatibleWidthLimitDp = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setDescriptionRes(int value) {
-        descriptionRes = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setEnabled(boolean value) {
-        return setBoolean(Booleans.ENABLED, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setCrossProfile(boolean value) {
-        return setBoolean(Booleans.CROSS_PROFILE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setFullBackupContent(int value) {
-        fullBackupContent = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setDataExtractionRules(int value) {
-        dataExtractionRules = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setIconRes(int value) {
-        iconRes = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setInstallLocation(int value) {
-        installLocation = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setLabelRes(int value) {
-        labelRes = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setLargestWidthLimitDp(int value) {
-        largestWidthLimitDp = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setLogo(int value) {
-        logo = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setMaxAspectRatio(float value) {
-        maxAspectRatio = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setMinAspectRatio(float value) {
-        minAspectRatio = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) {
-        minExtensionVersions = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setMinSdkVersion(int value) {
-        minSdkVersion = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setNetworkSecurityConfigRes(int value) {
-        networkSecurityConfigRes = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRequiresSmallestWidthDp(int value) {
-        requiresSmallestWidthDp = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRoundIconRes(int value) {
-        roundIconRes = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setTargetSandboxVersion(int value) {
-        targetSandboxVersion = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setTargetSdkVersion(int value) {
-        targetSdkVersion = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setTheme(int value) {
-        theme = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRequestForegroundServiceExemption(boolean value) {
-        return setBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setUiOptions(int value) {
-        uiOptions = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setExternalStorage(boolean value) {
-        return setBoolean(Booleans.EXTERNAL_STORAGE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) {
-        return setBoolean(Booleans.BASE_HARDWARE_ACCELERATED, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowBackup(boolean value) {
-        return setBoolean(Booleans.ALLOW_BACKUP, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setKillAfterRestore(boolean value) {
-        return setBoolean(Booleans.KILL_AFTER_RESTORE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setRestoreAnyVersion(boolean value) {
-        return setBoolean(Booleans.RESTORE_ANY_VERSION, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setFullBackupOnly(boolean value) {
-        return setBoolean(Booleans.FULL_BACKUP_ONLY, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setPersistent(boolean value) {
-        return setBoolean(Booleans.PERSISTENT, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setDebuggable(boolean value) {
-        return setBoolean(Booleans.DEBUGGABLE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setVmSafeMode(boolean value) {
-        return setBoolean(Booleans.VM_SAFE_MODE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setHasCode(boolean value) {
-        return setBoolean(Booleans.HAS_CODE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowTaskReparenting(boolean value) {
-        return setBoolean(Booleans.ALLOW_TASK_REPARENTING, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowClearUserData(boolean value) {
-        return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setLargeHeap(boolean value) {
-        return setBoolean(Booleans.LARGE_HEAP, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setUsesCleartextTraffic(boolean value) {
-        return setBoolean(Booleans.USES_CLEARTEXT_TRAFFIC, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setSupportsRtl(boolean value) {
-        return setBoolean(Booleans.SUPPORTS_RTL, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setTestOnly(boolean value) {
-        return setBoolean(Booleans.TEST_ONLY, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setMultiArch(boolean value) {
-        return setBoolean(Booleans.MULTI_ARCH, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setExtractNativeLibs(boolean value) {
-        return setBoolean(Booleans.EXTRACT_NATIVE_LIBS, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setGame(boolean value) {
-        return setBoolean(Booleans.GAME, value);
-    }
-
-    /**
-     * @see ParsingPackageRead#getResizeableActivity()
-     */
-    @Override
-    public ParsingPackageImpl setResizeableActivity(@Nullable Boolean value) {
-        resizeableActivity = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSdkLibName(String sdkLibName) {
-        this.sdkLibName = TextUtils.safeIntern(sdkLibName);
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSdkLibVersionMajor(int sdkLibVersionMajor) {
-        this.sdkLibVersionMajor = sdkLibVersionMajor;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setSdkLibrary(boolean value) {
-        return setBoolean(Booleans.SDK_LIBRARY, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
-        return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setOverlay(boolean value) {
-        return setBoolean(Booleans.OVERLAY, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setIsolatedSplitLoading(boolean value) {
-        return setBoolean(Booleans.ISOLATED_SPLIT_LOADING, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setHasDomainUrls(boolean value) {
-        return setBoolean(Booleans.HAS_DOMAIN_URLS, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setProfileableByShell(boolean value) {
-        return setBoolean(Booleans.PROFILEABLE_BY_SHELL, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setProfileable(boolean value) {
-        return setBoolean(Booleans.DISALLOW_PROFILING, !value);
-    }
-
-    @Override
-    public ParsingPackageImpl setBackupInForeground(boolean value) {
-        return setBoolean(Booleans.BACKUP_IN_FOREGROUND, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setUseEmbeddedDex(boolean value) {
-        return setBoolean(Booleans.USE_EMBEDDED_DEX, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) {
-        return setBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setDirectBootAware(boolean value) {
-        return setBoolean(Booleans.DIRECT_BOOT_AWARE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
-        gwpAsanMode = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
-        memtagMode = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setNativeHeapZeroInitialized(
-            @ApplicationInfo.NativeHeapZeroInitialized int value) {
-        nativeHeapZeroInitialized = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRequestRawExternalStorageAccess(@Nullable Boolean value) {
-        requestRawExternalStorageAccess = value;
-        return this;
-    }
-    @Override
-    public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
-        return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) {
-        return setBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) {
-        return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) {
-        return setBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) {
-        return setBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setUsesNonSdkApi(boolean value) {
-        return setBoolean(Booleans.USES_NON_SDK_API, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setHasFragileUserData(boolean value) {
-        return setBoolean(Booleans.HAS_FRAGILE_USER_DATA, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setCantSaveState(boolean value) {
-        return setBoolean(Booleans.CANT_SAVE_STATE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) {
-        return setBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setAutoRevokePermissions(int value) {
-        autoRevokePermissions = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) {
-        return setBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE, value);
-    }
-
-    @Override
-    public ParsingPackageImpl setVersionName(String versionName) {
-        this.versionName = versionName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName) {
-        this.compileSdkVersionCodeName = compileSdkVersionCodeName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setProcessName(String processName) {
-        this.processName = processName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setRestrictedAccountType(@Nullable String restrictedAccountType) {
-        this.restrictedAccountType = restrictedAccountType;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setOverlayTargetOverlayableName(
-            @Nullable String overlayTargetOverlayableName) {
-        this.overlayTargetOverlayableName = overlayTargetOverlayableName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setOverlayCategory(@Nullable String overlayCategory) {
-        this.overlayCategory = overlayCategory;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setAppComponentFactory(@Nullable String appComponentFactory) {
-        this.appComponentFactory = appComponentFactory;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setBackupAgentName(@Nullable String backupAgentName) {
-        this.backupAgentName = backupAgentName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setClassLoaderName(@Nullable String classLoaderName) {
-        this.classLoaderName = classLoaderName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setClassName(@Nullable String className) {
-        this.className = className == null ? null : className.trim();
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setManageSpaceActivityName(@Nullable String manageSpaceActivityName) {
-        this.manageSpaceActivityName = manageSpaceActivityName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setPermission(@Nullable String permission) {
-        this.permission = permission;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setTaskAffinity(@Nullable String taskAffinity) {
-        this.taskAffinity = taskAffinity;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setZygotePreloadName(@Nullable String zygotePreloadName) {
-        this.zygotePreloadName = zygotePreloadName;
-        return this;
-    }
-
-    @Override
-    public ParsingPackage setAttributionsAreUserVisible(boolean attributionsAreUserVisible) {
-        setBoolean(Booleans.ATTRIBUTIONS_ARE_USER_VISIBLE, attributionsAreUserVisible);
-        return this;
-    }
-
-    @Override
-    public ParsingPackage setResetEnabledSettingsOnAppDataCleared(
-            boolean resetEnabledSettingsOnAppDataCleared) {
-        setBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED,
-                resetEnabledSettingsOnAppDataCleared);
-        return this;
-    }
-}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageInternal.java b/core/java/android/content/pm/parsing/ParsingPackageInternal.java
deleted file mode 100644
index ca16fa2d..0000000
--- a/core/java/android/content/pm/parsing/ParsingPackageInternal.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.annotation.Nullable;
-import android.content.pm.PackageInfo;
-
-import com.android.internal.R;
-
-/**
- * Methods which would've been a part of {@link PkgWithoutStatePackageInfo} or {@link
- * PkgWithoutStateAppInfo}, but are removed/deprecated.
- * <p>
- * This is different from {@link ParsingPackageHidden}. The methods in that interface cannot be
- * accessed by anyone except the parsing utilities, whereas the methods in this interface are valid
- * and can be accessed by any internal caller that needs it.
- *
- * @hide
- */
-interface ParsingPackageInternal {
-
-    /**
-     * @see PackageInfo#overlayCategory
-     * @see R.styleable#AndroidManifestResourceOverlay_category
-     */
-    @Nullable
-    String getOverlayCategory();
-
-    /**
-     * @see PackageInfo#overlayPriority
-     * @see R.styleable#AndroidManifestResourceOverlay_priority
-     */
-    int getOverlayPriority();
-
-    /**
-     * @see PackageInfo#overlayTarget
-     * @see R.styleable#AndroidManifestResourceOverlay_targetPackage
-     */
-    @Nullable
-    String getOverlayTarget();
-
-    /**
-     * @see PackageInfo#targetOverlayableName
-     * @see R.styleable#AndroidManifestResourceOverlay_targetName
-     */
-    @Nullable
-    String getOverlayTargetOverlayableName();
-
-    /**
-     * @see PackageInfo#mOverlayIsStatic
-     */
-    boolean isOverlayIsStatic();
-}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
deleted file mode 100644
index c8113ef..0000000
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.component.ParsedApexSystemService;
-import android.content.pm.parsing.component.ParsedAttribution;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.os.Bundle;
-import android.util.ArraySet;
-import android.util.Pair;
-import android.util.SparseIntArray;
-
-import java.security.PublicKey;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Everything written by {@link ParsingPackage} and readable back.
- *
- * @hide
- */
-public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutStatePackageInfo,
-        ParsingPackageInternal {
-
-    /**
-     * The names of packages to adopt ownership of permissions from, parsed under {@link
-     * PackageParser#TAG_ADOPT_PERMISSIONS}.
-     *
-     * @see R.styleable#AndroidManifestOriginalPackage_name
-     */
-    @NonNull
-    List<String> getAdoptPermissions();
-
-    /**
-     * @see R.styleable#AndroidManifestApexSystemService
-     */
-    @NonNull
-    List<ParsedApexSystemService> getApexSystemServices();
-
-    @NonNull
-    List<ParsedAttribution> getAttributions();
-
-    /**
-     * Permissions requested but not in the manifest. These may have been split or migrated from
-     * previous versions/definitions.
-     */
-    @NonNull
-    List<String> getImplicitPermissions();
-
-    @NonNull
-    List<ParsedUsesPermission> getUsesPermissions();
-
-    /**
-     * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
-     * PackageParser#TAG_KEY_SETS}.
-     *
-     * @see R.styleable#AndroidManifestKeySet
-     * @see R.styleable#AndroidManifestPublicKey
-     */
-    @NonNull
-    Map<String, ArraySet<PublicKey>> getKeySetMapping();
-
-    /**
-     * Library names this package is declared as, for use by other packages with "uses-library".
-     *
-     * @see R.styleable#AndroidManifestLibrary
-     */
-    @NonNull
-    List<String> getLibraryNames();
-
-    /**
-     * TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
-     */
-    @Nullable
-    Bundle getMetaData();
-
-    @Nullable
-    Set<String> getMimeGroups();
-
-    /**
-     * @see R.styleable#AndroidManifestExtensionSdk
-     */
-    @Nullable
-    SparseIntArray getMinExtensionVersions();
-
-    /**
-     * For system use to migrate from an old package name to a new one, moving over data if
-     * available.
-     *
-     * @see R.styleable#AndroidManifestOriginalPackage}
-     */
-    @NonNull
-    List<String> getOriginalPackages();
-
-    /**
-     * Map of overlayable name to actor name.
-     */
-    @NonNull
-    Map<String, String> getOverlayables();
-
-    /**
-     * @see android.content.pm.PermissionGroupInfo
-     */
-    @NonNull
-    List<ParsedPermissionGroup> getPermissionGroups();
-
-    /**
-     * Used to determine the default preferred handler of an {@link Intent}.
-     * <p>
-     * Map of component className to intent info inside that component. TODO(b/135203078): Is this
-     * actually used/working?
-     */
-    @NonNull
-    List<Pair<String, ParsedIntentInfo>> getPreferredActivityFilters();
-
-    /**
-     * @see android.content.pm.ProcessInfo
-     */
-    @NonNull
-    Map<String, ParsedProcess> getProcesses();
-
-    /**
-     * System protected broadcasts.
-     *
-     * @see R.styleable#AndroidManifestProtectedBroadcast
-     */
-    @NonNull
-    List<String> getProtectedBroadcasts();
-
-    /**
-     * Intents that this package may query or require and thus requires visibility into.
-     *
-     * @see R.styleable#AndroidManifestQueriesIntent
-     */
-    @NonNull
-    List<Intent> getQueriesIntents();
-
-    /**
-     * Other packages that this package may query or require and thus requires visibility into.
-     *
-     * @see R.styleable#AndroidManifestQueriesPackage
-     */
-    @NonNull
-    List<String> getQueriesPackages();
-
-    /**
-     * Authorities that this package may query or require and thus requires visibility into.
-     *
-     * @see R.styleable#AndroidManifestQueriesProvider
-     */
-    @NonNull
-    Set<String> getQueriesProviders();
-
-    /**
-     * SHA-512 hash of the only APK that can be used to update a system package.
-     *
-     * @see R.styleable#AndroidManifestRestrictUpdate
-     */
-    @Nullable
-    byte[] getRestrictUpdateHash();
-
-    /**
-     * The signature data of all APKs in this package, which must be exactly the same across the
-     * base and splits.
-     */
-    SigningDetails getSigningDetails();
-
-    /**
-     * Returns the properties set on the application
-     */
-    @NonNull
-    Map<String, Property> getProperties();
-
-    /**
-     * Flags of any split APKs; ordered by parsed splitName
-     */
-    @Nullable
-    int[] getSplitFlags();
-
-    /**
-     * @see R.styleable#AndroidManifestSdkLibrary_name
-     */
-    @Nullable
-    String getSdkLibName();
-
-    /**
-     * @see R.styleable#AndroidManifestSdkLibrary_versionMajor
-     */
-    int getSdkLibVersionMajor();
-
-    /**
-     * @see R.styleable#AndroidManifestStaticLibrary_name
-     */
-    @Nullable
-    String getStaticSharedLibName();
-
-    /**
-     * @see R.styleable#AndroidManifestStaticLibrary_version
-     */
-    long getStaticSharedLibVersion();
-
-    /**
-     * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
-     * PackageParser#TAG_KEY_SETS}.
-     *
-     * @see R.styleable#AndroidManifestUpgradeKeySet
-     */
-    @NonNull
-    Set<String> getUpgradeKeySets();
-
-    /**
-     * @see R.styleable#AndroidManifestUsesLibrary
-     */
-    @NonNull
-    List<String> getUsesLibraries();
-
-    /**
-     * @see R.styleable#AndroidManifestUsesNativeLibrary
-     */
-    @NonNull
-    List<String> getUsesNativeLibraries();
-
-    /**
-     * Like {@link #getUsesLibraries()}, but marked optional by setting {@link
-     * R.styleable#AndroidManifestUsesLibrary_required} to false . Application is expected to handle
-     * absence manually.
-     *
-     * @see R.styleable#AndroidManifestUsesLibrary
-     */
-    @NonNull
-    List<String> getUsesOptionalLibraries();
-
-    /**
-     * Like {@link #getUsesNativeLibraries()}, but marked optional by setting {@link
-     * R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is expected to
-     * handle absence manually.
-     *
-     * @see R.styleable#AndroidManifestUsesNativeLibrary
-     */
-    @NonNull
-    List<String> getUsesOptionalNativeLibraries();
-
-    /**
-     * TODO(b/135203078): Move static library stuff to an inner data class
-     *
-     * @see R.styleable#AndroidManifestUsesStaticLibrary
-     */
-    @NonNull
-    List<String> getUsesStaticLibraries();
-
-    /**
-     * @see R.styleable#AndroidManifestUsesStaticLibrary_certDigest
-     */
-    @Nullable
-    String[][] getUsesStaticLibrariesCertDigests();
-
-    /**
-     * @see R.styleable#AndroidManifestUsesStaticLibrary_version
-     */
-    @Nullable
-    long[] getUsesStaticLibrariesVersions();
-
-    /**
-     * TODO(b/135203078): Move SDK library stuff to an inner data class
-     *
-     * @see R.styleable#AndroidManifestUsesSdkLibrary
-     */
-    @NonNull
-    List<String> getUsesSdkLibraries();
-
-    /**
-     * @see R.styleable#AndroidManifestUsesSdkLibrary_certDigest
-     */
-    @Nullable
-    String[][] getUsesSdkLibrariesCertDigests();
-
-    /**
-     * @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
-     */
-    @Nullable
-    long[] getUsesSdkLibrariesVersionsMajor();
-
-    boolean hasPreserveLegacyExternalStorage();
-
-    /**
-     * @see R.styleable#AndroidManifestApplication_forceQueryable
-     */
-    boolean isForceQueryable();
-
-    /**
-     * @see ApplicationInfo#FLAG_IS_GAME
-     */
-    @Deprecated
-    boolean isGame();
-
-    /**
-     * The install time abi override to choose 32bit abi's when multiple abi's are present. This is
-     * only meaningful for multiarch applications. The use32bitAbi attribute is ignored if
-     * cpuAbiOverride is also set.
-     *
-     * @see R.attr#use32bitAbi
-     */
-    boolean isUse32BitAbi();
-
-    /**
-     * Set if the any of components are visible to instant applications.
-     *
-     * @see R.styleable#AndroidManifestActivity_visibleToInstantApps
-     * @see R.styleable#AndroidManifestProvider_visibleToInstantApps
-     * @see R.styleable#AndroidManifestService_visibleToInstantApps
-     */
-    boolean isVisibleToInstantApps();
-
-    /**
-     * Whether the enabled settings of components in the application should be reset to the default,
-     * when the application's user data is cleared.
-     *
-     * @see R.styleable#AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared
-     */
-    boolean isResetEnabledSettingsOnAppDataCleared();
-}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
deleted file mode 100644
index 16deaa0..0000000
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ /dev/null
@@ -1,3536 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
-import static android.os.Build.VERSION_CODES.DONUT;
-import static android.os.Build.VERSION_CODES.O;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-
-import android.annotation.AnyRes;
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StyleableRes;
-import android.app.ActivityThread;
-import android.app.ResourcesManager;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ComponentParseUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityUtils;
-import android.content.pm.parsing.component.ParsedApexSystemService;
-import android.content.pm.parsing.component.ParsedApexSystemServiceUtils;
-import android.content.pm.parsing.component.ParsedAttribution;
-import android.content.pm.parsing.component.ParsedAttributionUtils;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedInstrumentationUtils;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedIntentInfoUtils;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedPermissionUtils;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProcessUtils;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedProviderUtils;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.component.ParsedServiceUtils;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseInput.DeferredError;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
-import android.content.pm.permission.CompatibilityPermissionInfo;
-import android.content.pm.split.DefaultSplitAssetLoader;
-import android.content.pm.split.SplitAssetDependencyLoader;
-import android.content.pm.split.SplitAssetLoader;
-import android.content.res.ApkAssets;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.FileUtils;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.ext.SdkExtensions;
-import android.permission.PermissionManager;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Base64;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.TypedValue;
-import android.util.apk.ApkSignatureVerifier;
-
-import com.android.internal.R;
-import com.android.internal.os.ClassLoaderFactory;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-import libcore.util.HexEncoding;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.spec.EncodedKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-/**
- * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
- * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
- *
- * @hide
- */
-public class ParsingPackageUtils {
-
-    private static final String TAG = ParsingUtils.TAG;
-
-    public static final boolean DEBUG_JAR = false;
-    public static final boolean DEBUG_BACKUP = false;
-    public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
-    public static final float ASPECT_RATIO_NOT_SET = -1f;
-
-    /** File name in an APK for the Android manifest. */
-    public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
-
-    /** Path prefix for apps on expanded storage */
-    public static final String MNT_EXPAND = "/mnt/expand/";
-
-    public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
-    public static final String TAG_APPLICATION = "application";
-    public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
-    public static final String TAG_EAT_COMMENT = "eat-comment";
-    public static final String TAG_FEATURE_GROUP = "feature-group";
-    public static final String TAG_INSTRUMENTATION = "instrumentation";
-    public static final String TAG_KEY_SETS = "key-sets";
-    public static final String TAG_MANIFEST = "manifest";
-    public static final String TAG_ORIGINAL_PACKAGE = "original-package";
-    public static final String TAG_OVERLAY = "overlay";
-    public static final String TAG_PACKAGE = "package";
-    public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
-    public static final String TAG_ATTRIBUTION = "attribution";
-    public static final String TAG_PERMISSION = "permission";
-    public static final String TAG_PERMISSION_GROUP = "permission-group";
-    public static final String TAG_PERMISSION_TREE = "permission-tree";
-    public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
-    public static final String TAG_QUERIES = "queries";
-    public static final String TAG_RESTRICT_UPDATE = "restrict-update";
-    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
-    public static final String TAG_SUPPORTS_INPUT = "supports-input";
-    public static final String TAG_USES_CONFIGURATION = "uses-configuration";
-    public static final String TAG_USES_FEATURE = "uses-feature";
-    public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
-    public static final String TAG_USES_PERMISSION = "uses-permission";
-    public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
-    public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
-    public static final String TAG_USES_SDK = "uses-sdk";
-    public static final String TAG_USES_SPLIT = "uses-split";
-    public static final String TAG_PROFILEABLE = "profileable";
-    public static final String TAG_RECEIVER = "receiver";
-
-    public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
-    public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
-    public static final String METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES =
-            "android.can_display_on_remote_devices";
-    public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
-            "android.activity_window_layout_affinity";
-    public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
-
-    public static final int SDK_VERSION = Build.VERSION.SDK_INT;
-    public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
-
-    public static boolean sCompatibilityModeEnabled = true;
-    public static boolean sUseRoundIcon = false;
-
-    public static final int PARSE_DEFAULT_INSTALL_LOCATION =
-            PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
-    public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
-
-    /** If set to true, we will only allow package files that exactly match
-     *  the DTD. Otherwise, we try to get as much from the package as we
-     *  can without failing. This should normally be set to false, to
-     *  support extensions to the DTD in future versions. */
-    public static final boolean RIGID_PARSER = false;
-
-    public static final int PARSE_MUST_BE_APK = 1 << 0;
-    public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
-    public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
-    public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
-    public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
-    public static final int PARSE_ENFORCE_CODE = 1 << 6;
-    /**
-     * This flag is applied in the ApkLiteParser. Used by OverlayConfigParser to ignore the
-     * checks of required system property within the overlay tag.
-     */
-    public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
-    public static final int PARSE_CHATTY = 1 << 31;
-
-    @IntDef(flag = true, prefix = { "PARSE_" }, value = {
-            PARSE_CHATTY,
-            PARSE_COLLECT_CERTIFICATES,
-            PARSE_ENFORCE_CODE,
-            PARSE_EXTERNAL_STORAGE,
-            PARSE_IGNORE_PROCESSES,
-            PARSE_IS_SYSTEM_DIR,
-            PARSE_MUST_BE_APK,
-            PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ParseFlags {}
-
-    /**
-     * For those names would be used as a part of the file name. Limits size to 223 and reserves 32
-     * for the OS.
-     */
-    static final int MAX_FILE_NAME_SIZE = 223;
-
-    /**
-     * @see #parseDefault(ParseInput, File, int, List, boolean)
-     */
-    @NonNull
-    public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
-            @ParseFlags int parseFlags,
-            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
-            boolean collectCertificates) {
-        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
-        return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates);
-    }
-
-    /**
-     * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
-     * request, without caching the input object and without querying the internal system state
-     * for feature support.
-     */
-    @NonNull
-    public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
-            @ParseFlags int parseFlags,
-            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
-            boolean collectCertificates) {
-        ParseResult<ParsingPackage> result;
-
-        ParsingPackageUtils parser = new ParsingPackageUtils(false, null /*separateProcesses*/,
-                null /*displayMetrics*/, splitPermissions, new Callback() {
-            @Override
-            public boolean hasFeature(String feature) {
-                // Assume the device doesn't support anything. This will affect permission
-                // parsing and will force <uses-permission/> declarations to include all
-                // requiredNotFeature permissions and exclude all requiredFeature
-                // permissions. This mirrors the old behavior.
-                return false;
-            }
-
-            @Override
-            public ParsingPackage startParsingPackage(
-                    @NonNull String packageName,
-                    @NonNull String baseApkPath,
-                    @NonNull String path,
-                    @NonNull TypedArray manifestArray, boolean isCoreApp) {
-                return new ParsingPackageImpl(packageName, baseApkPath, path,
-                        manifestArray);
-            }
-        });
-        result = parser.parsePackage(input, file, parseFlags);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        final ParsingPackage pkg = result.getResult();
-        if (collectCertificates) {
-            final ParseResult<SigningDetails> ret =
-                    ParsingPackageUtils.getSigningDetails(input, pkg, false /*skipVerify*/);
-            if (ret.isError()) {
-                return input.error(ret);
-            }
-            pkg.setSigningDetails(ret.getResult());
-        }
-
-        // Need to call this to finish the parsing stage
-        pkg.hideAsParsed();
-
-        return input.success(pkg);
-    }
-
-    private boolean mOnlyCoreApps;
-    private String[] mSeparateProcesses;
-    private DisplayMetrics mDisplayMetrics;
-    @NonNull
-    private List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
-    private Callback mCallback;
-
-    public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
-            DisplayMetrics displayMetrics,
-            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
-            @NonNull Callback callback) {
-        mOnlyCoreApps = onlyCoreApps;
-        mSeparateProcesses = separateProcesses;
-        mDisplayMetrics = displayMetrics;
-        mSplitPermissionInfos = splitPermissions;
-        mCallback = callback;
-    }
-
-    /**
-     * Parse the package at the given location. Automatically detects if the
-     * package is a monolithic style (single APK file) or cluster style
-     * (directory of APKs).
-     * <p>
-     * This performs validity checking on cluster style packages, such as
-     * requiring identical package name and version codes, a single base APK,
-     * and unique split names.
-     * <p>
-     * Note that this <em>does not</em> perform signature verification; that must
-     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
-     *
-     * If {@code useCaches} is true, the package parser might return a cached
-     * result from a previous parse of the same {@code packageFile} with the same
-     * {@code flags}. Note that this method does not check whether {@code packageFile}
-     * has changed since the last parse, it's up to callers to do so.
-     */
-    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
-        if (packageFile.isDirectory()) {
-            return parseClusterPackage(input, packageFile, flags);
-        } else {
-            return parseMonolithicPackage(input, packageFile, flags);
-        }
-    }
-
-    /**
-     * Parse all APKs contained in the given directory, treating them as a
-     * single package. This also performs validity checking, such as requiring
-     * identical package name and version codes, a single base APK, and unique
-     * split names.
-     * <p>
-     * Note that this <em>does not</em> perform signature verification; that must
-     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
-     */
-    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
-            int flags) {
-        final ParseResult<PackageLite> liteResult =
-                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
-        if (liteResult.isError()) {
-            return input.error(liteResult);
-        }
-
-        final PackageLite lite = liteResult.getResult();
-        if (mOnlyCoreApps && !lite.isCoreApp()) {
-            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
-                    "Not a coreApp: " + packageDir);
-        }
-
-        // Build the split dependency tree.
-        SparseArray<int[]> splitDependencies = null;
-        final SplitAssetLoader assetLoader;
-        if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) {
-            try {
-                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
-                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
-            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
-                return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
-            }
-        } else {
-            assetLoader = new DefaultSplitAssetLoader(lite, flags);
-        }
-
-        try {
-            final File baseApk = new File(lite.getBaseApkPath());
-            final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
-                    lite.getPath(), assetLoader, flags);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            ParsingPackage pkg = result.getResult();
-            if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
-                pkg.asSplit(
-                        lite.getSplitNames(),
-                        lite.getSplitApkPaths(),
-                        lite.getSplitRevisionCodes(),
-                        splitDependencies
-                );
-                final int num = lite.getSplitNames().length;
-
-                for (int i = 0; i < num; i++) {
-                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
-                    final ParseResult<ParsingPackage> split =
-                            parseSplitApk(input, pkg, i, splitAssets, flags);
-                    if (split.isError()) {
-                        return input.error(split);
-                    }
-                }
-            }
-
-            pkg.setUse32BitAbi(lite.isUse32bitAbi());
-            return input.success(pkg);
-        } catch (IllegalArgumentException e) {
-            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
-                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
-        } finally {
-            IoUtils.closeQuietly(assetLoader);
-        }
-    }
-
-    /**
-     * Parse the given APK file, treating it as as a single monolithic package.
-     * <p>
-     * Note that this <em>does not</em> perform signature verification; that must
-     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
-     */
-    private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
-            int flags) {
-        final ParseResult<PackageLite> liteResult =
-                ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
-        if (liteResult.isError()) {
-            return input.error(liteResult);
-        }
-
-        final PackageLite lite = liteResult.getResult();
-        if (mOnlyCoreApps && !lite.isCoreApp()) {
-            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
-                    "Not a coreApp: " + apkFile);
-        }
-
-        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
-        try {
-            final ParseResult<ParsingPackage> result = parseBaseApk(input,
-                    apkFile,
-                    apkFile.getCanonicalPath(),
-                    assetLoader, flags);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            return input.success(result.getResult()
-                    .setUse32BitAbi(lite.isUse32bitAbi()));
-        } catch (IOException e) {
-            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Failed to get path: " + apkFile, e);
-        } finally {
-            IoUtils.closeQuietly(assetLoader);
-        }
-    }
-
-    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
-            String codePath, SplitAssetLoader assetLoader, int flags) {
-        final String apkPath = apkFile.getAbsolutePath();
-
-        String volumeUuid = null;
-        if (apkPath.startsWith(MNT_EXPAND)) {
-            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
-            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
-        }
-
-        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
-
-        final AssetManager assets;
-        try {
-            assets = assetLoader.getBaseAssetManager();
-        } catch (IllegalArgumentException e) {
-            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
-                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
-        }
-        final int cookie = assets.findCookieForPath(apkPath);
-        if (cookie == 0) {
-            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                    "Failed adding asset path: " + apkPath);
-        }
-
-        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
-                ANDROID_MANIFEST_FILENAME)) {
-            final Resources res = new Resources(assets, mDisplayMetrics, null);
-
-            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
-                    parser, flags);
-            if (result.isError()) {
-                return input.error(result.getErrorCode(),
-                        apkPath + " (at " + parser.getPositionDescription() + "): "
-                                + result.getErrorMessage());
-            }
-
-            final ParsingPackage pkg = result.getResult();
-            if (assets.containsAllocatedTable()) {
-                final ParseResult<?> deferResult = input.deferError(
-                        "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
-                                + " the resources.arsc of installed APKs to be stored uncompressed"
-                                + " and aligned on a 4-byte boundary",
-                        DeferredError.RESOURCES_ARSC_COMPRESSED);
-                if (deferResult.isError()) {
-                    return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
-                            deferResult.getErrorMessage());
-                }
-            }
-
-            ApkAssets apkAssets = assetLoader.getBaseApkAssets();
-            boolean definesOverlayable = false;
-            try {
-                definesOverlayable = apkAssets.definesOverlayable();
-            } catch (IOException ignored) {
-                // Will fail if there's no packages in the ApkAssets, which can be treated as false
-            }
-
-            if (definesOverlayable) {
-                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
-                int size = packageNames.size();
-                for (int index = 0; index < size; index++) {
-                    String packageName = packageNames.valueAt(index);
-                    Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
-                    if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
-                        for (String overlayable : overlayableToActor.keySet()) {
-                            pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
-                        }
-                    }
-                }
-            }
-
-            pkg.setVolumeUuid(volumeUuid);
-
-            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
-                final ParseResult<SigningDetails> ret =
-                        getSigningDetails(input, pkg, false /*skipVerify*/);
-                if (ret.isError()) {
-                    return input.error(ret);
-                }
-                pkg.setSigningDetails(ret.getResult());
-            } else {
-                pkg.setSigningDetails(SigningDetails.UNKNOWN);
-            }
-
-            return input.success(pkg);
-        } catch (Exception e) {
-            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Failed to read manifest from " + apkPath, e);
-        }
-    }
-
-    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
-            ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
-        final String apkPath = pkg.getSplitCodePaths()[splitIndex];
-
-        if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
-
-        // This must always succeed, as the path has been added to the AssetManager before.
-        final int cookie = assets.findCookieForPath(apkPath);
-        if (cookie == 0) {
-            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                    "Failed adding asset path: " + apkPath);
-        }
-        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
-                ANDROID_MANIFEST_FILENAME)) {
-            Resources res = new Resources(assets, mDisplayMetrics, null);
-            ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
-                    parser, flags, splitIndex);
-            if (parseResult.isError()) {
-                return input.error(parseResult.getErrorCode(),
-                        apkPath + " (at " + parser.getPositionDescription() + "): "
-                                + parseResult.getErrorMessage());
-            }
-
-            return parseResult;
-        } catch (Exception e) {
-            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Failed to read manifest from " + apkPath, e);
-        }
-    }
-
-    /**
-     * Parse the manifest of a <em>base APK</em>. When adding new features you
-     * need to consider whether they should be supported by split APKs and child
-     * packages.
-     *
-     * @param apkPath The package apk file path
-     * @param res     The resources from which to resolve values
-     * @param parser  The manifest parser
-     * @param flags   Flags how to parse
-     * @return Parsed package or null on error.
-     */
-    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
-            String codePath, Resources res, XmlResourceParser parser, int flags)
-            throws XmlPullParserException, IOException {
-        final String splitName;
-        final String pkgName;
-
-        ParseResult<Pair<String, String>> packageSplitResult =
-                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
-        if (packageSplitResult.isError()) {
-            return input.error(packageSplitResult);
-        }
-
-        Pair<String, String> packageSplit = packageSplitResult.getResult();
-        pkgName = packageSplit.first;
-        splitName = packageSplit.second;
-
-        if (!TextUtils.isEmpty(splitName)) {
-            return input.error(
-                    PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
-                    "Expected base APK, but found split " + splitName
-            );
-        }
-
-        final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
-        try {
-            final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
-                    "coreApp",false);
-            final ParsingPackage pkg = mCallback.startParsingPackage(
-                    pkgName, apkPath, codePath, manifestArray, isCoreApp);
-            final ParseResult<ParsingPackage> result =
-                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
-            if (result.isError()) {
-                return result;
-            }
-
-            return input.success(pkg);
-        } finally {
-            manifestArray.recycle();
-        }
-    }
-
-    /**
-     * Parse the manifest of a <em>split APK</em>.
-     * <p>
-     * Note that split APKs have many more restrictions on what they're capable
-     * of doing, so many valid features of a base APK have been carefully
-     * omitted here.
-     *
-     * @param pkg builder to fill
-     * @return false on failure
-     */
-    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser, int flags, int splitIndex)
-            throws XmlPullParserException, IOException {
-        // We parsed manifest tag earlier; just skip past it
-        final ParseResult<Pair<String, String>> packageSplitResult =
-                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
-        if (packageSplitResult.isError()) {
-            return input.error(packageSplitResult);
-        }
-
-        int type;
-
-        boolean foundApp = false;
-
-        int outerDepth = parser.getDepth();
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-            if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final ParseResult result;
-            String tagName = parser.getName();
-            if (TAG_APPLICATION.equals(tagName)) {
-                if (foundApp) {
-                    if (RIGID_PARSER) {
-                        result = input.error("<manifest> has more than one <application>");
-                    } else {
-                        Slog.w(TAG, "<manifest> has more than one <application>");
-                        result = input.success(null);
-                    }
-                } else {
-                    foundApp = true;
-                    result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
-                }
-            } else {
-                result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        if (!foundApp) {
-            ParseResult<?> deferResult = input.deferError(
-                    "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG);
-            if (deferResult.isError()) {
-                return input.error(deferResult);
-            }
-        }
-
-        return input.success(pkg);
-    }
-
-    /**
-     * Parse the {@code application} XML tree at the current parse location in a
-     * <em>split APK</em> manifest.
-     * <p>
-     * Note that split APKs have many more restrictions on what they're capable
-     * of doing, so many valid features of a base APK have been carefully
-     * omitted here.
-     */
-    private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
-            throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
-        try {
-            pkg.setSplitHasCode(splitIndex, sa.getBoolean(
-                    R.styleable.AndroidManifestApplication_hasCode, true));
-
-            final String classLoaderName = sa.getString(
-                    R.styleable.AndroidManifestApplication_classLoader);
-            if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
-                    classLoaderName)) {
-                pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
-            } else {
-                return input.error("Invalid class loader name: " + classLoaderName);
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        // If the loaded component did not specify a split, inherit the split name
-        // based on the split it is defined in.
-        // This is used to later load the correct split when starting this
-        // component.
-        String defaultSplitName = pkg.getSplitNames()[splitIndex];
-
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            ParsedMainComponent mainComponent = null;
-
-            final ParseResult result;
-            String tagName = parser.getName();
-            boolean isActivity = false;
-            switch (tagName) {
-                case "activity":
-                    isActivity = true;
-                    // fall-through
-                case "receiver":
-                    ParseResult<ParsedActivity> activityResult =
-                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
-                                    res, parser, flags, sUseRoundIcon, defaultSplitName, input);
-                    if (activityResult.isSuccess()) {
-                        ParsedActivity activity = activityResult.getResult();
-                        if (isActivity) {
-                            pkg.addActivity(activity);
-                        } else {
-                            pkg.addReceiver(activity);
-                        }
-                        mainComponent = activity;
-                    }
-                    result = activityResult;
-                    break;
-                case "service":
-                    ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
-                            mSeparateProcesses, pkg, res, parser, flags, sUseRoundIcon,
-                            defaultSplitName, input);
-                    if (serviceResult.isSuccess()) {
-                        ParsedService service = serviceResult.getResult();
-                        pkg.addService(service);
-                        mainComponent = service;
-                    }
-                    result = serviceResult;
-                    break;
-                case "provider":
-                    ParseResult<ParsedProvider> providerResult =
-                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
-                                    flags, sUseRoundIcon, defaultSplitName, input);
-                    if (providerResult.isSuccess()) {
-                        ParsedProvider provider = providerResult.getResult();
-                        pkg.addProvider(provider);
-                        mainComponent = provider;
-                    }
-                    result = providerResult;
-                    break;
-                case "activity-alias":
-                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
-                            sUseRoundIcon, defaultSplitName, input);
-                    if (activityResult.isSuccess()) {
-                        ParsedActivity activity = activityResult.getResult();
-                        pkg.addActivity(activity);
-                        mainComponent = activity;
-                    }
-
-                    result = activityResult;
-                    break;
-                default:
-                    result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
-                    break;
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        return input.success(pkg);
-    }
-
-    /**
-     * For parsing non-MainComponents. Main ones have an order and some special handling which is
-     * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
-     * XmlResourceParser, int, int)}.
-     */
-    private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
-        switch (tag) {
-            case "meta-data":
-                // note: application meta-data is stored off to the side, so it can
-                // remain null in the primary copy (we like to avoid extra copies because
-                // it can be large)
-                ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
-                        res, parser, "<meta-data>", input);
-                if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
-                    pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
-                }
-                return metaDataResult;
-            case "property":
-                ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
-                        res, parser, "<property>", input);
-                if (propertyResult.isSuccess()) {
-                    pkg.addProperty(propertyResult.getResult());
-                }
-                return propertyResult;
-            case "uses-sdk-library":
-                return parseUsesSdkLibrary(input, pkg, res, parser);
-            case "uses-static-library":
-                return parseUsesStaticLibrary(input, pkg, res, parser);
-            case "uses-library":
-                return parseUsesLibrary(input, pkg, res, parser);
-            case "uses-native-library":
-                return parseUsesNativeLibrary(input, pkg, res, parser);
-            case "uses-package":
-                // Dependencies for app installers; we don't currently try to
-                // enforce this.
-                return input.success(null);
-            default:
-                return ParsingUtils.unknownTag("<application>", pkg, parser, input);
-        }
-    }
-
-    private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
-            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
-            throws XmlPullParserException, IOException {
-        ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
-        if (sharedUserResult.isError()) {
-            return sharedUserResult;
-        }
-
-        pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
-                R.styleable.AndroidManifest_installLocation, sa))
-                .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
-                        R.styleable.AndroidManifest_targetSandboxVersion, sa))
-                /* Set the global "on SD card" flag */
-                .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
-
-        boolean foundApp = false;
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            final ParseResult result;
-
-            // <application> has special logic, so it's handled outside the general method
-            if (TAG_APPLICATION.equals(tagName)) {
-                if (foundApp) {
-                    if (RIGID_PARSER) {
-                        result = input.error("<manifest> has more than one <application>");
-                    } else {
-                        Slog.w(TAG, "<manifest> has more than one <application>");
-                        result = input.success(null);
-                    }
-                } else {
-                    foundApp = true;
-                    result = parseBaseApplication(input, pkg, res, parser, flags);
-                }
-            } else {
-                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
-            ParseResult<?> deferResult = input.deferError(
-                    "<manifest> does not contain an <application> or <instrumentation>",
-                    DeferredError.MISSING_APP_TAG);
-            if (deferResult.isError()) {
-                return input.error(deferResult);
-            }
-        }
-
-        if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
-            return input.error(
-                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                    "Combination <attribution> tags are not valid"
-            );
-        }
-
-        convertCompatPermissions(pkg);
-
-        convertSplitPermissions(pkg);
-
-        // At this point we can check if an application is not supporting densities and hence
-        // cannot be windowed / resized. Note that an SDK version of 0 is common for
-        // pre-Doughnut applications.
-        if (pkg.getTargetSdkVersion() < DONUT
-                || (!pkg.isSupportsSmallScreens()
-                && !pkg.isSupportsNormalScreens()
-                && !pkg.isSupportsLargeScreens()
-                && !pkg.isSupportsExtraLargeScreens()
-                && !pkg.isResizeable()
-                && !pkg.isAnyDensity())) {
-            adjustPackageToBeUnresizeableAndUnpipable(pkg);
-        }
-
-        return input.success(pkg);
-    }
-
-    private ParseResult parseBaseApkTag(String tag, ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
-            throws IOException, XmlPullParserException {
-        switch (tag) {
-            case TAG_OVERLAY:
-                return parseOverlay(input, pkg, res, parser);
-            case TAG_KEY_SETS:
-                return parseKeySets(input, pkg, res, parser);
-            case "feature": // TODO moltmann: Remove
-            case TAG_ATTRIBUTION:
-                return parseAttribution(input, pkg, res, parser);
-            case TAG_PERMISSION_GROUP:
-                return parsePermissionGroup(input, pkg, res, parser);
-            case TAG_PERMISSION:
-                return parsePermission(input, pkg, res, parser);
-            case TAG_PERMISSION_TREE:
-                return parsePermissionTree(input, pkg, res, parser);
-            case TAG_USES_PERMISSION:
-            case TAG_USES_PERMISSION_SDK_M:
-            case TAG_USES_PERMISSION_SDK_23:
-                return parseUsesPermission(input, pkg, res, parser);
-            case TAG_USES_CONFIGURATION:
-                return parseUsesConfiguration(input, pkg, res, parser);
-            case TAG_USES_FEATURE:
-                return parseUsesFeature(input, pkg, res, parser);
-            case TAG_FEATURE_GROUP:
-                return parseFeatureGroup(input, pkg, res, parser);
-            case TAG_USES_SDK:
-                return parseUsesSdk(input, pkg, res, parser);
-            case TAG_SUPPORT_SCREENS:
-                return parseSupportScreens(input, pkg, res, parser);
-            case TAG_PROTECTED_BROADCAST:
-                return parseProtectedBroadcast(input, pkg, res, parser);
-            case TAG_INSTRUMENTATION:
-                return parseInstrumentation(input, pkg, res, parser);
-            case TAG_ORIGINAL_PACKAGE:
-                return parseOriginalPackage(input, pkg, res, parser);
-            case TAG_ADOPT_PERMISSIONS:
-                return parseAdoptPermissions(input, pkg, res, parser);
-            case TAG_USES_GL_TEXTURE:
-            case TAG_COMPATIBLE_SCREENS:
-            case TAG_SUPPORTS_INPUT:
-            case TAG_EAT_COMMENT:
-                // Just skip this tag
-                XmlUtils.skipCurrentTag(parser);
-                return input.success(pkg);
-            case TAG_RESTRICT_UPDATE:
-                return parseRestrictUpdateHash(flags, input, pkg, res, parser);
-            case TAG_QUERIES:
-                return parseQueries(input, pkg, res, parser);
-            default:
-                return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
-            ParsingPackage pkg, TypedArray sa) {
-        int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
-        if ((maxSdkVersion != 0) && maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT) {
-            return input.success(pkg);
-        }
-
-        String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
-        if (TextUtils.isEmpty(str)) {
-            return input.success(pkg);
-        }
-
-        if (!"android".equals(pkg.getPackageName())) {
-            ParseResult<?> nameResult = validateName(input, str, true, true);
-            if (nameResult.isError()) {
-                return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
-                        "<manifest> specifies bad sharedUserId name \"" + str + "\": "
-                                + nameResult.getErrorMessage());
-            }
-        }
-
-        return input.success(pkg
-                .setSharedUserId(str.intern())
-                .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
-    }
-
-    private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        // we've encountered the 'key-sets' tag
-        // all the keys and keysets that we want must be defined here
-        // so we're going to iterate over the parser and pull out the things we want
-        int outerDepth = parser.getDepth();
-        int currentKeySetDepth = -1;
-        int type;
-        String currentKeySet = null;
-        ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
-        ArraySet<String> upgradeKeySets = new ArraySet<>();
-        ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
-        ArraySet<String> improperKeySets = new ArraySet<>();
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG) {
-                if (parser.getDepth() == currentKeySetDepth) {
-                    currentKeySet = null;
-                    currentKeySetDepth = -1;
-                }
-                continue;
-            }
-            String tagName = parser.getName();
-            switch (tagName) {
-                case "key-set": {
-                    if (currentKeySet != null) {
-                        return input.error("Improperly nested 'key-set' tag at "
-                                + parser.getPositionDescription());
-                    }
-                    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
-                    try {
-                        final String keysetName = sa.getNonResourceString(
-                                R.styleable.AndroidManifestKeySet_name);
-                        definedKeySets.put(keysetName, new ArraySet<>());
-                        currentKeySet = keysetName;
-                        currentKeySetDepth = parser.getDepth();
-                    } finally {
-                        sa.recycle();
-                    }
-                } break;
-                case "public-key": {
-                    if (currentKeySet == null) {
-                        return input.error("Improperly nested 'key-set' tag at "
-                                + parser.getPositionDescription());
-                    }
-                    TypedArray sa = res.obtainAttributes(parser,
-                            R.styleable.AndroidManifestPublicKey);
-                    try {
-                        final String publicKeyName = nonResString(
-                                R.styleable.AndroidManifestPublicKey_name, sa);
-                        final String encodedKey = nonResString(
-                                R.styleable.AndroidManifestPublicKey_value, sa);
-                        if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
-                            return input.error("'public-key' " + publicKeyName
-                                    + " must define a public-key value on first use at "
-                                    + parser.getPositionDescription());
-                        } else if (encodedKey != null) {
-                            PublicKey currentKey = parsePublicKey(encodedKey);
-                            if (currentKey == null) {
-                                Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
-                                        + parser.getPositionDescription() + " key-set "
-                                        + currentKeySet
-                                        + " will not be added to the package's defined key-sets.");
-                                improperKeySets.add(currentKeySet);
-                                XmlUtils.skipCurrentTag(parser);
-                                continue;
-                            }
-                            if (publicKeys.get(publicKeyName) == null
-                                    || publicKeys.get(publicKeyName).equals(currentKey)) {
-
-                                /* public-key first definition, or matches old definition */
-                                publicKeys.put(publicKeyName, currentKey);
-                            } else {
-                                return input.error("Value of 'public-key' " + publicKeyName
-                                        + " conflicts with previously defined value at "
-                                        + parser.getPositionDescription());
-                            }
-                        }
-                        definedKeySets.get(currentKeySet).add(publicKeyName);
-                        XmlUtils.skipCurrentTag(parser);
-                    } finally {
-                        sa.recycle();
-                    }
-                } break;
-                case "upgrade-key-set": {
-                    TypedArray sa = res.obtainAttributes(parser,
-                            R.styleable.AndroidManifestUpgradeKeySet);
-                    try {
-                        String name = sa.getNonResourceString(
-                                R.styleable.AndroidManifestUpgradeKeySet_name);
-                        upgradeKeySets.add(name);
-                        XmlUtils.skipCurrentTag(parser);
-                    } finally {
-                        sa.recycle();
-                    }
-                } break;
-                default:
-                    ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
-                            input);
-                    if (result.isError()) {
-                        return input.error(result);
-                    }
-                    break;
-            }
-        }
-        String packageName = pkg.getPackageName();
-        Set<String> publicKeyNames = publicKeys.keySet();
-        if (publicKeyNames.removeAll(definedKeySets.keySet())) {
-            return input.error("Package" + packageName
-                    + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
-        }
-
-        for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
-            final String keySetName = e.getKey();
-            if (e.getValue().size() == 0) {
-                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
-                        + "'key-set' " + keySetName + " has no valid associated 'public-key'."
-                        + " Not including in package's defined key-sets.");
-                continue;
-            } else if (improperKeySets.contains(keySetName)) {
-                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
-                        + "'key-set' " + keySetName + " contained improper 'public-key'"
-                        + " tags. Not including in package's defined key-sets.");
-                continue;
-            }
-
-            for (String s : e.getValue()) {
-                pkg.addKeySet(keySetName, publicKeys.get(s));
-            }
-        }
-        if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
-            pkg.setUpgradeKeySets(upgradeKeySets);
-        } else {
-            return input.error("Package" + packageName
-                    + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
-        }
-
-        return input.success(pkg);
-    }
-
-    private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws IOException, XmlPullParserException {
-        ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
-                parser, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        return input.success(pkg.addAttribution(result.getResult()));
-    }
-
-    private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
-                pkg, res, parser, sUseRoundIcon, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        return input.success(pkg.addPermissionGroup(result.getResult()));
-    }
-
-    private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
-                pkg, res, parser, sUseRoundIcon, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        return input.success(pkg.addPermission(result.getResult()));
-    }
-
-    private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
-                pkg, res, parser, sUseRoundIcon, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        return input.success(pkg.addPermission(result.getResult()));
-    }
-
-    private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws IOException, XmlPullParserException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            String name = sa.getNonResourceString(
-                    R.styleable.AndroidManifestUsesPermission_name);
-
-            int maxSdkVersion = 0;
-            TypedValue val = sa.peekValue(
-                    R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
-            if (val != null) {
-                if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
-                    maxSdkVersion = val.data;
-                }
-            }
-
-            final ArraySet<String> requiredFeatures = new ArraySet<>();
-            String feature = sa.getNonConfigurationString(
-                    com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature,
-                    0);
-            if (feature != null) {
-                requiredFeatures.add(feature);
-            }
-
-            final ArraySet<String> requiredNotFeatures = new ArraySet<>();
-            feature = sa.getNonConfigurationString(
-                    com.android.internal.R.styleable
-                            .AndroidManifestUsesPermission_requiredNotFeature,
-                    0);
-            if (feature != null) {
-                requiredNotFeatures.add(feature);
-            }
-
-            final int usesPermissionFlags = sa.getInt(
-                com.android.internal.R.styleable.AndroidManifestUsesPermission_usesPermissionFlags,
-                0);
-
-            final int outerDepth = parser.getDepth();
-            int type;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                    && (type != XmlPullParser.END_TAG
-                    || parser.getDepth() > outerDepth)) {
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                    continue;
-                }
-
-                final ParseResult<?> result;
-                switch (parser.getName()) {
-                    case "required-feature":
-                        result = parseRequiredFeature(input, res, parser);
-                        if (result.isSuccess()) {
-                            requiredFeatures.add((String) result.getResult());
-                        }
-                        break;
-
-                    case "required-not-feature":
-                        result = parseRequiredNotFeature(input, res, parser);
-                        if (result.isSuccess()) {
-                            requiredNotFeatures.add((String) result.getResult());
-                        }
-                        break;
-
-                    default:
-                        result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input);
-                        break;
-                }
-
-                if (result.isError()) {
-                    return input.error(result);
-                }
-            }
-
-            // Can only succeed from here on out
-            ParseResult<ParsingPackage> success = input.success(pkg);
-
-            if (name == null) {
-                return success;
-            }
-
-            if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
-                return success;
-            }
-
-            if (mCallback != null) {
-                // Only allow requesting this permission if the platform supports all of the
-                // "required-feature"s.
-                for (int i = requiredFeatures.size() - 1; i >= 0; i--) {
-                    if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) {
-                        return success;
-                    }
-                }
-
-                // Only allow requesting this permission if the platform does not supports any of
-                // the "required-not-feature"s.
-                for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) {
-                    if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) {
-                        return success;
-                    }
-                }
-            }
-
-            // Quietly ignore duplicate permission requests, but fail loudly if
-            // the two requests have conflicting flags
-            boolean found = false;
-            final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
-            final int size = usesPermissions.size();
-            for (int i = 0; i < size; i++) {
-                final ParsedUsesPermission usesPermission = usesPermissions.get(i);
-                if (Objects.equals(usesPermission.getName(), name)) {
-                    if (usesPermission.getUsesPermissionFlags() != usesPermissionFlags) {
-                        return input.error("Conflicting uses-permissions flags: "
-                                + name + " in package: " + pkg.getPackageName() + " at: "
-                                + parser.getPositionDescription());
-                    } else {
-                        Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
-                                + name + " in package: " + pkg.getPackageName() + " at: "
-                                + parser.getPositionDescription());
-                    }
-                    found = true;
-                    break;
-                }
-            }
-
-            if (!found) {
-                pkg.addUsesPermission(new ParsedUsesPermissionImpl(name, usesPermissionFlags));
-            }
-            return success;
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res,
-            AttributeSet attrs) {
-        final TypedArray sa = res.obtainAttributes(attrs,
-                com.android.internal.R.styleable.AndroidManifestRequiredFeature);
-        try {
-            final String featureName = sa.getString(
-                    R.styleable.AndroidManifestRequiredFeature_name);
-            return TextUtils.isEmpty(featureName)
-                    ? input.error("Feature name is missing from <required-feature> tag.")
-                    : input.success(featureName);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res,
-            AttributeSet attrs) {
-        final TypedArray sa = res.obtainAttributes(attrs,
-                com.android.internal.R.styleable.AndroidManifestRequiredNotFeature);
-        try {
-            final String featureName = sa.getString(
-                    R.styleable.AndroidManifestRequiredNotFeature_name);
-            return TextUtils.isEmpty(featureName)
-                    ? input.error("Feature name is missing from <required-not-feature> tag.")
-                    : input.success(featureName);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        ConfigurationInfo cPref = new ConfigurationInfo();
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
-        try {
-            cPref.reqTouchScreen = sa.getInt(
-                    R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
-                    Configuration.TOUCHSCREEN_UNDEFINED);
-            cPref.reqKeyboardType = sa.getInt(
-                    R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
-                    Configuration.KEYBOARD_UNDEFINED);
-            if (sa.getBoolean(
-                    R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
-                    false)) {
-                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
-            }
-            cPref.reqNavigation = sa.getInt(
-                    R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
-                    Configuration.NAVIGATION_UNDEFINED);
-            if (sa.getBoolean(
-                    R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
-                    false)) {
-                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
-            }
-            pkg.addConfigPreference(cPref);
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        FeatureInfo fi = parseFeatureInfo(res, parser);
-        pkg.addReqFeature(fi);
-
-        if (fi.name == null) {
-            ConfigurationInfo cPref = new ConfigurationInfo();
-            cPref.reqGlEsVersion = fi.reqGlEsVersion;
-            pkg.addConfigPreference(cPref);
-        }
-
-        return input.success(pkg);
-    }
-
-    private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
-        FeatureInfo fi = new FeatureInfo();
-        TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
-            fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
-            if (fi.name == null) {
-                fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
-                        FeatureInfo.GL_ES_VERSION_UNDEFINED);
-            }
-            if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
-                fi.flags |= FeatureInfo.FLAG_REQUIRED;
-            }
-            return fi;
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws IOException, XmlPullParserException {
-        FeatureGroupInfo group = new FeatureGroupInfo();
-        ArrayList<FeatureInfo> features = null;
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final String innerTagName = parser.getName();
-            if (innerTagName.equals("uses-feature")) {
-                FeatureInfo featureInfo = parseFeatureInfo(res, parser);
-                // FeatureGroups are stricter and mandate that
-                // any <uses-feature> declared are mandatory.
-                featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
-                features = ArrayUtils.add(features, featureInfo);
-            } else {
-                Slog.w(TAG,
-                        "Unknown element under <feature-group>: " + innerTagName
-                                + " at " + pkg.getBaseApkPath() + " "
-                                + parser.getPositionDescription());
-            }
-        }
-
-        if (features != null) {
-            group.features = new FeatureInfo[features.size()];
-            group.features = features.toArray(group.features);
-        }
-
-        pkg.addFeatureGroup(group);
-        return input.success(pkg);
-    }
-
-    private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws IOException, XmlPullParserException {
-        if (SDK_VERSION > 0) {
-            TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
-            try {
-                int minVers = ParsingUtils.DEFAULT_MIN_SDK_VERSION;
-                String minCode = null;
-                int targetVers = ParsingUtils.DEFAULT_TARGET_SDK_VERSION;
-                String targetCode = null;
-
-                TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
-                if (val != null) {
-                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
-                        minCode = val.string.toString();
-                    } else {
-                        // If it's not a string, it's an integer.
-                        minVers = val.data;
-                    }
-                }
-
-                val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
-                if (val != null) {
-                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
-                        targetCode = val.string.toString();
-                        if (minCode == null) {
-                            minCode = targetCode;
-                        }
-                    } else {
-                        // If it's not a string, it's an integer.
-                        targetVers = val.data;
-                    }
-                } else {
-                    targetVers = minVers;
-                    targetCode = minCode;
-                }
-
-                ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
-                        targetVers, targetCode, SDK_CODENAMES, input);
-                if (targetSdkVersionResult.isError()) {
-                    return input.error(targetSdkVersionResult);
-                }
-
-                int targetSdkVersion = targetSdkVersionResult.getResult();
-
-                ParseResult<?> deferResult =
-                        input.enableDeferredError(pkg.getPackageName(), targetSdkVersion);
-                if (deferResult.isError()) {
-                    return input.error(deferResult);
-                }
-
-                ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
-                        SDK_VERSION, SDK_CODENAMES, input);
-                if (minSdkVersionResult.isError()) {
-                    return input.error(minSdkVersionResult);
-                }
-
-                int minSdkVersion = minSdkVersionResult.getResult();
-
-                pkg.setMinSdkVersion(minSdkVersion)
-                        .setTargetSdkVersion(targetSdkVersion);
-
-                int type;
-                final int innerDepth = parser.getDepth();
-                SparseIntArray minExtensionVersions = null;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
-                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                        continue;
-                    }
-
-                    final ParseResult result;
-                    if (parser.getName().equals("extension-sdk")) {
-                        if (minExtensionVersions == null) {
-                            minExtensionVersions = new SparseIntArray();
-                        }
-                        result = parseExtensionSdk(input, res, parser, minExtensionVersions);
-                        XmlUtils.skipCurrentTag(parser);
-                    } else {
-                        result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
-                    }
-
-                    if (result.isError()) {
-                        return input.error(result);
-                    }
-                }
-                pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
-            } finally {
-                sa.recycle();
-            }
-        }
-        return input.success(pkg);
-    }
-
-    @Nullable
-    private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
-        if (input == null) {
-            return null;
-        }
-        SparseIntArray output = new SparseIntArray(input.size());
-        for (int i = 0; i < input.size(); i++) {
-            output.put(input.keyAt(i), input.valueAt(i));
-        }
-        return output;
-    }
-
-    private static ParseResult<SparseIntArray> parseExtensionSdk(
-            ParseInput input, Resources res, XmlResourceParser parser,
-            SparseIntArray minExtensionVersions) {
-        int sdkVersion;
-        int minVersion;
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
-        try {
-            sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
-            minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
-        } finally {
-            sa.recycle();
-        }
-
-        if (sdkVersion < 0) {
-            return input.error(
-                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                    "<extension-sdk> must specify an sdkVersion >= 0");
-        }
-        if (minVersion < 0) {
-            return input.error(
-                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                    "<extension-sdk> must specify minExtensionVersion >= 0");
-        }
-
-        try {
-            int version = SdkExtensions.getExtensionVersion(sdkVersion);
-            if (version < minVersion) {
-                return input.error(
-                        PackageManager.INSTALL_FAILED_OLDER_SDK,
-                        "Package requires " + sdkVersion + " extension version " + minVersion
-                                + " which exceeds device version " + version);
-            }
-        } catch (RuntimeException e) {
-            return input.error(
-                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                    "Specified sdkVersion " + sdkVersion + " is not valid");
-        }
-        minExtensionVersions.put(sdkVersion, minVersion);
-        return input.success(minExtensionVersions);
-    }
-
-    /**
-     * Computes the minSdkVersion to use at runtime. If the package is not
-     * compatible with this platform, populates {@code outError[0]} with an
-     * error message.
-     * <p>
-     * If {@code minCode} is not specified, e.g. the value is {@code null},
-     * then behavior varies based on the {@code platformSdkVersion}:
-     * <ul>
-     * <li>If the platform SDK version is greater than or equal to the
-     * {@code minVers}, returns the {@code mniVers} unmodified.
-     * <li>Otherwise, returns -1 to indicate that the package is not
-     * compatible with this platform.
-     * </ul>
-     * <p>
-     * Otherwise, the behavior varies based on whether the current platform
-     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
-     * has length > 0:
-     * <ul>
-     * <li>If this is a pre-release platform and the value specified by
-     * {@code targetCode} is contained within the array of allowed pre-release
-     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
-     * <li>If this is a released platform, this method will return -1 to
-     * indicate that the package is not compatible with this platform.
-     * </ul>
-     *
-     * @param minVers minSdkVersion number, if specified in the application
-     *                manifest, or 1 otherwise
-     * @param minCode minSdkVersion code, if specified in the application
-     *                manifest, or {@code null} otherwise
-     * @param platformSdkVersion platform SDK version number, typically
-     *                           Build.VERSION.SDK_INT
-     * @param platformSdkCodenames array of allowed prerelease SDK codenames
-     *                             for this platform
-     * @return the minSdkVersion to use at runtime if successful
-     */
-    public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
-            @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
-            @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) {
-        // If it's a release SDK, make sure we meet the minimum SDK requirement.
-        if (minCode == null) {
-            if (minVers <= platformSdkVersion) {
-                return input.success(minVers);
-            }
-
-            // We don't meet the minimum SDK requirement.
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
-                    "Requires newer sdk version #" + minVers
-                            + " (current version is #" + platformSdkVersion + ")");
-        }
-
-        // If it's a pre-release SDK and the codename matches this platform, we
-        // definitely meet the minimum SDK requirement.
-        if (matchTargetCode(platformSdkCodenames, minCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
-        }
-
-        // Otherwise, we're looking at an incompatible pre-release SDK.
-        if (platformSdkCodenames.length > 0) {
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
-                    "Requires development platform " + minCode
-                            + " (current platform is any of "
-                            + Arrays.toString(platformSdkCodenames) + ")");
-        } else {
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
-                    "Requires development platform " + minCode
-                            + " but this is a release platform.");
-        }
-    }
-
-    /**
-     * Computes the targetSdkVersion to use at runtime. If the package is not
-     * compatible with this platform, populates {@code outError[0]} with an
-     * error message.
-     * <p>
-     * If {@code targetCode} is not specified, e.g. the value is {@code null},
-     * then the {@code targetVers} will be returned unmodified.
-     * <p>
-     * Otherwise, the behavior varies based on whether the current platform
-     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
-     * has length > 0:
-     * <ul>
-     * <li>If this is a pre-release platform and the value specified by
-     * {@code targetCode} is contained within the array of allowed pre-release
-     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
-     * <li>If this is a released platform, this method will return -1 to
-     * indicate that the package is not compatible with this platform.
-     * </ul>
-     *
-     * @param targetVers targetSdkVersion number, if specified in the
-     *                   application manifest, or 0 otherwise
-     * @param targetCode targetSdkVersion code, if specified in the application
-     *                   manifest, or {@code null} otherwise
-     * @param platformSdkCodenames array of allowed pre-release SDK codenames
-     *                             for this platform
-     * @return the targetSdkVersion to use at runtime if successful
-     */
-    public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
-            @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
-            @NonNull ParseInput input) {
-        // If it's a release SDK, return the version number unmodified.
-        if (targetCode == null) {
-            return input.success(targetVers);
-        }
-
-        // If it's a pre-release SDK and the codename matches this platform, it
-        // definitely targets this SDK.
-        if (matchTargetCode(platformSdkCodenames, targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
-        }
-
-        // Otherwise, we're looking at an incompatible pre-release SDK.
-        if (platformSdkCodenames.length > 0) {
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
-                    "Requires development platform " + targetCode
-                            + " (current platform is any of "
-                            + Arrays.toString(platformSdkCodenames) + ")");
-        } else {
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
-                    "Requires development platform " + targetCode
-                            + " but this is a release platform.");
-        }
-    }
-
-    /**
-     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
-     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
-     * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
-     */
-    private static boolean matchTargetCode(@NonNull String[] codeNames,
-            @NonNull String targetCode) {
-        final String targetCodeName;
-        final int targetCodeIdx = targetCode.indexOf('.');
-        if (targetCodeIdx == -1) {
-            targetCodeName = targetCode;
-        } else {
-            targetCodeName = targetCode.substring(0, targetCodeIdx);
-        }
-        return ArrayUtils.contains(codeNames, targetCodeName);
-    }
-
-    private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
-            TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
-            try {
-                final String hash = sa.getNonConfigurationString(
-                        R.styleable.AndroidManifestRestrictUpdate_hash,
-                        0);
-
-                if (hash != null) {
-                    final int hashLength = hash.length();
-                    final byte[] hashBytes = new byte[hashLength / 2];
-                    for (int i = 0; i < hashLength; i += 2) {
-                        hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
-                                << 4)
-                                + Character.digit(hash.charAt(i + 1), 16));
-                    }
-                    pkg.setRestrictUpdateHash(hashBytes);
-                } else {
-                    pkg.setRestrictUpdateHash(null);
-                }
-            } finally {
-                sa.recycle();
-            }
-        }
-        return input.success(pkg);
-    }
-
-    private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            if (parser.getName().equals("intent")) {
-                ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(
-                        null /*className*/, pkg, res, parser, true /*allowGlobs*/,
-                        true /*allowAutoVerify*/, input);
-                if (result.isError()) {
-                    return input.error(result);
-                }
-
-                IntentFilter intentInfo = result.getResult().getIntentFilter();
-
-                Uri data = null;
-                String dataType = null;
-                String host = null;
-                final int numActions = intentInfo.countActions();
-                final int numSchemes = intentInfo.countDataSchemes();
-                final int numTypes = intentInfo.countDataTypes();
-                final int numHosts = intentInfo.getHosts().length;
-                if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
-                    return input.error("intent tags must contain either an action or data.");
-                }
-                if (numActions > 1) {
-                    return input.error("intent tag may have at most one action.");
-                }
-                if (numTypes > 1) {
-                    return input.error("intent tag may have at most one data type.");
-                }
-                if (numSchemes > 1) {
-                    return input.error("intent tag may have at most one data scheme.");
-                }
-                if (numHosts > 1) {
-                    return input.error("intent tag may have at most one data host.");
-                }
-                Intent intent = new Intent();
-                for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
-                    intent.addCategory(intentInfo.getCategory(i));
-                }
-                if (numHosts == 1) {
-                    host = intentInfo.getHosts()[0];
-                }
-                if (numSchemes == 1) {
-                    data = new Uri.Builder()
-                            .scheme(intentInfo.getDataScheme(0))
-                            .authority(host)
-                            .path(IntentFilter.WILDCARD_PATH)
-                            .build();
-                }
-                if (numTypes == 1) {
-                    dataType = intentInfo.getDataType(0);
-                    // The dataType may have had the '/' removed for the dynamic mimeType feature.
-                    // If we detect that case, we add the * back.
-                    if (!dataType.contains("/")) {
-                        dataType = dataType + "/*";
-                    }
-                    if (data == null) {
-                        data = new Uri.Builder()
-                                .scheme("content")
-                                .authority(IntentFilter.WILDCARD)
-                                .path(IntentFilter.WILDCARD_PATH)
-                                .build();
-                    }
-                }
-                intent.setDataAndType(data, dataType);
-                if (numActions == 1) {
-                    intent.setAction(intentInfo.getAction(0));
-                }
-                pkg.addQueriesIntent(intent);
-            } else if (parser.getName().equals("package")) {
-                final TypedArray sa = res.obtainAttributes(parser,
-                        R.styleable.AndroidManifestQueriesPackage);
-                final String packageName = sa.getNonConfigurationString(
-                        R.styleable.AndroidManifestQueriesPackage_name, 0);
-                if (TextUtils.isEmpty(packageName)) {
-                    return input.error("Package name is missing from package tag.");
-                }
-                pkg.addQueriesPackage(packageName.intern());
-            } else if (parser.getName().equals("provider")) {
-                final TypedArray sa = res.obtainAttributes(parser,
-                        R.styleable.AndroidManifestQueriesProvider);
-                try {
-                    final String authorities = sa.getNonConfigurationString(
-                            R.styleable.AndroidManifestQueriesProvider_authorities, 0);
-                    if (TextUtils.isEmpty(authorities)) {
-                        return input.error(
-                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                                "Authority missing from provider tag."
-                        );
-                    }
-                    StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
-                    while (authoritiesTokenizer.hasMoreElements()) {
-                        pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
-                    }
-                } finally {
-                    sa.recycle();
-                }
-            }
-        }
-        return input.success(pkg);
-    }
-
-    /**
-     * Parse the {@code application} XML tree at the current parse location in a
-     * <em>base APK</em> manifest.
-     * <p>
-     * When adding new features, carefully consider if they should also be
-     * supported by split APKs.
-     *
-     * This method should avoid using a getter for fields set by this method. Prefer assigning
-     * a local variable and using it. Otherwise there's an ordering problem which can be broken
-     * if any code moves around.
-     */
-    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
-            throws XmlPullParserException, IOException {
-        final String pkgName = pkg.getPackageName();
-        int targetSdk = pkg.getTargetSdkVersion();
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
-        try {
-            // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
-            // This case can only happen in unit tests where we sometimes need to create fakes
-            // of various package parser data structures.
-            if (sa == null) {
-                return input.error("<application> does not contain any attributes");
-            }
-
-            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
-                    0);
-            if (name != null) {
-                String packageName = pkg.getPackageName();
-                String outInfoName = ParsingUtils.buildClassName(packageName, name);
-                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
-                    return input.error("<application> invalid android:name");
-                } else if (outInfoName == null) {
-                    return input.error("Empty class name in package " + packageName);
-                }
-
-                pkg.setClassName(outInfoName);
-            }
-
-            TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
-            if (labelValue != null) {
-                pkg.setLabelRes(labelValue.resourceId);
-                if (labelValue.resourceId == 0) {
-                    pkg.setNonLocalizedLabel(labelValue.coerceToString());
-                }
-            }
-
-            parseBaseAppBasicFlags(pkg, sa);
-
-            String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
-                    R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
-            if (manageSpaceActivity != null) {
-                String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
-                        manageSpaceActivity);
-
-                if (manageSpaceActivityName == null) {
-                    return input.error("Empty class name in package " + pkgName);
-                }
-
-                pkg.setManageSpaceActivityName(manageSpaceActivityName);
-            }
-
-            if (pkg.isAllowBackup()) {
-                // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
-                // and restoreAnyVersion are only relevant if backup is possible for the
-                // given application.
-                String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
-                        R.styleable.AndroidManifestApplication_backupAgent, sa);
-                if (backupAgent != null) {
-                    String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
-                    if (backupAgentName == null) {
-                        return input.error("Empty class name in package " + pkgName);
-                    }
-
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "android:backupAgent = " + backupAgentName
-                                + " from " + pkgName + "+" + backupAgent);
-                    }
-
-                    pkg.setBackupAgentName(backupAgentName)
-                            .setKillAfterRestore(bool(true,
-                                    R.styleable.AndroidManifestApplication_killAfterRestore, sa))
-                            .setRestoreAnyVersion(bool(false,
-                                    R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
-                            .setFullBackupOnly(bool(false,
-                                    R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
-                            .setBackupInForeground(bool(false,
-                                    R.styleable.AndroidManifestApplication_backupInForeground, sa));
-                }
-
-                TypedValue v = sa.peekValue(
-                        R.styleable.AndroidManifestApplication_fullBackupContent);
-                int fullBackupContent = 0;
-
-                if (v != null) {
-                    fullBackupContent = v.resourceId;
-
-                    if (v.resourceId == 0) {
-                        if (DEBUG_BACKUP) {
-                            Slog.v(TAG, "fullBackupContent specified as boolean=" +
-                                    (v.data == 0 ? "false" : "true"));
-                        }
-                        // "false" => -1, "true" => 0
-                        fullBackupContent = v.data == 0 ? -1 : 0;
-                    }
-
-                    pkg.setFullBackupContent(fullBackupContent);
-                }
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
-                }
-            }
-
-            if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
-                // Check if persistence is based on a feature being present
-                final String requiredFeature = sa.getNonResourceString(R.styleable
-                        .AndroidManifestApplication_persistentWhenFeatureAvailable);
-                pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
-            }
-
-            if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
-                pkg.setResizeableActivity(sa.getBoolean(
-                        R.styleable.AndroidManifestApplication_resizeableActivity, true));
-            } else {
-                pkg.setResizeableActivityViaSdkVersion(
-                        targetSdk >= Build.VERSION_CODES.N);
-            }
-
-            String taskAffinity;
-            if (targetSdk >= Build.VERSION_CODES.FROYO) {
-                taskAffinity = sa.getNonConfigurationString(
-                        R.styleable.AndroidManifestApplication_taskAffinity,
-                        Configuration.NATIVE_CONFIG_VERSION);
-            } else {
-                // Some older apps have been seen to use a resource reference
-                // here that on older builds was ignored (with a warning).  We
-                // need to continue to do this for them so they don't break.
-                taskAffinity = sa.getNonResourceString(
-                        R.styleable.AndroidManifestApplication_taskAffinity);
-            }
-
-            ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
-                    pkgName, pkgName, taskAffinity, input);
-            if (taskAffinityResult.isError()) {
-                return input.error(taskAffinityResult);
-            }
-
-            pkg.setTaskAffinity(taskAffinityResult.getResult());
-            String factory = sa.getNonResourceString(
-                    R.styleable.AndroidManifestApplication_appComponentFactory);
-            if (factory != null) {
-                String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
-                if (appComponentFactory == null) {
-                    return input.error("Empty class name in package " + pkgName);
-                }
-
-                pkg.setAppComponentFactory(appComponentFactory);
-            }
-
-            CharSequence pname;
-            if (targetSdk >= Build.VERSION_CODES.FROYO) {
-                pname = sa.getNonConfigurationString(
-                        R.styleable.AndroidManifestApplication_process,
-                        Configuration.NATIVE_CONFIG_VERSION);
-            } else {
-                // Some older apps have been seen to use a resource reference
-                // here that on older builds was ignored (with a warning).  We
-                // need to continue to do this for them so they don't break.
-                pname = sa.getNonResourceString(
-                        R.styleable.AndroidManifestApplication_process);
-            }
-            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
-                    pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
-            if (processNameResult.isError()) {
-                return input.error(processNameResult);
-            }
-
-            String processName = processNameResult.getResult();
-            pkg.setProcessName(processName);
-
-            if (pkg.isCantSaveState()) {
-                // A heavy-weight application can not be in a custom process.
-                // We can do direct compare because we intern all strings.
-                if (processName != null && !processName.equals(pkgName)) {
-                    return input.error(
-                            "cantSaveState applications can not use custom processes");
-                }
-            }
-
-            String classLoaderName = pkg.getClassLoaderName();
-            if (classLoaderName != null
-                    && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
-                return input.error("Invalid class loader name: " + classLoaderName);
-            }
-
-            pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
-            pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
-            if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
-                final boolean v = sa.getBoolean(
-                        R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
-                pkg.setNativeHeapZeroInitialized(
-                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
-            }
-            if (sa.hasValue(
-                    R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
-                pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
-                                .AndroidManifestApplication_requestRawExternalStorageAccess,
-                        false));
-            }
-            if (sa.hasValue(
-                    R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) {
-                pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable
-                                .AndroidManifestApplication_requestForegroundServiceExemption,
-                        false));
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        boolean hasActivityOrder = false;
-        boolean hasReceiverOrder = false;
-        boolean hasServiceOrder = false;
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final ParseResult result;
-            String tagName = parser.getName();
-            boolean isActivity = false;
-            switch (tagName) {
-                case "activity":
-                    isActivity = true;
-                    // fall-through
-                case "receiver":
-                    ParseResult<ParsedActivity> activityResult =
-                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
-                                    res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
-                                    input);
-
-                    if (activityResult.isSuccess()) {
-                        ParsedActivity activity = activityResult.getResult();
-                        if (isActivity) {
-                            hasActivityOrder |= (activity.getOrder() != 0);
-                            pkg.addActivity(activity);
-                        } else {
-                            hasReceiverOrder |= (activity.getOrder() != 0);
-                            pkg.addReceiver(activity);
-                        }
-                    }
-
-                    result = activityResult;
-                    break;
-                case "service":
-                    ParseResult<ParsedService> serviceResult =
-                            ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
-                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
-                                    input);
-                    if (serviceResult.isSuccess()) {
-                        ParsedService service = serviceResult.getResult();
-                        hasServiceOrder |= (service.getOrder() != 0);
-                        pkg.addService(service);
-                    }
-
-                    result = serviceResult;
-                    break;
-                case "provider":
-                    ParseResult<ParsedProvider> providerResult =
-                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
-                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
-                                    input);
-                    if (providerResult.isSuccess()) {
-                        pkg.addProvider(providerResult.getResult());
-                    }
-
-                    result = providerResult;
-                    break;
-                case "activity-alias":
-                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
-                            parser, sUseRoundIcon, null /*defaultSplitName*/,
-                            input);
-                    if (activityResult.isSuccess()) {
-                        ParsedActivity activity = activityResult.getResult();
-                        hasActivityOrder |= (activity.getOrder() != 0);
-                        pkg.addActivity(activity);
-                    }
-
-                    result = activityResult;
-                    break;
-                case "apex-system-service":
-                    ParseResult<ParsedApexSystemService> systemServiceResult =
-                            ParsedApexSystemServiceUtils.parseApexSystemService(res,
-                                    parser, input);
-                    if (systemServiceResult.isSuccess()) {
-                        ParsedApexSystemService systemService =
-                                systemServiceResult.getResult();
-                        pkg.addApexSystemService(systemService);
-                    }
-
-                    result = systemServiceResult;
-                    break;
-                default:
-                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
-                    break;
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty(
-                pkg.getSdkLibName())) {
-            // Add a hidden app detail activity to normal apps which forwards user to App Details
-            // page.
-            ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
-            if (a.isError()) {
-                // Error should be impossible here, as the only failure case as of SDK R is a
-                // string validation error on a constant ":app_details" string passed in by the
-                // parsing code itself. For this reason, this is just a hard failure instead of
-                // deferred.
-                return input.error(a);
-            }
-
-            pkg.addActivity(a.getResult());
-        }
-
-        if (hasActivityOrder) {
-            pkg.sortActivities();
-        }
-        if (hasReceiverOrder) {
-            pkg.sortReceivers();
-        }
-        if (hasServiceOrder) {
-            pkg.sortServices();
-        }
-
-        // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
-        // every activity info has had a chance to set it from its attributes.
-        setMaxAspectRatio(pkg);
-        setMinAspectRatio(pkg);
-        setSupportsSizeChanges(pkg);
-
-        pkg.setHasDomainUrls(hasDomainURLs(pkg));
-
-        return input.success(pkg);
-    }
-
-    /**
-     * Collection of single-line, no (or little) logic assignments. Separated for readability.
-     *
-     * Flags are separated by type and by default value. They are sorted alphabetically within each
-     * section.
-     */
-    private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
-        int targetSdk = pkg.getTargetSdkVersion();
-        //@formatter:off
-        // CHECKSTYLE:off
-        pkg
-                // Default true
-                .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
-                .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
-                .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
-                .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
-                .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
-                .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
-                .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
-                // Default false
-                .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
-                .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
-                .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa))
-                .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
-                .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
-                .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
-                .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
-                .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
-                .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
-                .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
-                .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
-                .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
-                .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
-                .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
-                .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
-                .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
-                .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
-                .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
-                .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
-                .setAttributionsAreUserVisible(bool(false, R.styleable.AndroidManifestApplication_attributionsAreUserVisible, sa))
-                .setResetEnabledSettingsOnAppDataCleared(bool(false,
-                    R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
-                    sa))
-                // targetSdkVersion gated
-                .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
-                .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
-                .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
-                .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
-                // Ints Default 0
-                .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
-                // Ints
-                .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
-                // Floats Default 0f
-                .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
-                .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
-                // Resource ID
-                .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
-                .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
-                .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))
-                .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
-                .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
-                .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
-                .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
-                .setDataExtractionRules(
-                        resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa))
-                // Strings
-                .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
-                .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
-                .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
-                .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
-                // Non-Config String
-                .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
-        // CHECKSTYLE:on
-        //@formatter:on
-    }
-
-    /**
-     * For parsing non-MainComponents. Main ones have an order and some special handling which is
-     * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
-     * XmlResourceParser, int)}.
-     */
-    private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser, int flags)
-            throws IOException, XmlPullParserException {
-        switch (tag) {
-            case "meta-data":
-                // TODO(b/135203078): I have no idea what this comment means
-                // note: application meta-data is stored off to the side, so it can
-                // remain null in the primary copy (we like to avoid extra copies because
-                // it can be large)
-                final ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
-                        res, parser, "<meta-data>", input);
-                if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
-                    pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
-                }
-                return metaDataResult;
-            case "property":
-                final ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
-                        res, parser, "<property>", input);
-                if (propertyResult.isSuccess()) {
-                    pkg.addProperty(propertyResult.getResult());
-                }
-                return propertyResult;
-            case "sdk-library":
-                return parseSdkLibrary(pkg, res, parser, input);
-            case "static-library":
-                return parseStaticLibrary(pkg, res, parser, input);
-            case "library":
-                return parseLibrary(pkg, res, parser, input);
-            case "uses-sdk-library":
-                return parseUsesSdkLibrary(input, pkg, res, parser);
-            case "uses-static-library":
-                return parseUsesStaticLibrary(input, pkg, res, parser);
-            case "uses-library":
-                return parseUsesLibrary(input, pkg, res, parser);
-            case "uses-native-library":
-                return parseUsesNativeLibrary(input, pkg, res, parser);
-            case "processes":
-                return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
-            case "uses-package":
-                // Dependencies for app installers; we don't currently try to
-                // enforce this.
-                return input.success(null);
-            case "profileable":
-                return parseProfileable(input, pkg, res, parser);
-            default:
-                return ParsingUtils.unknownTag("<application>", pkg, parser, input);
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseSdkLibrary(
-            ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, ParseInput input) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSdkLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource that may change.
-            String lname = sa.getNonResourceString(
-                    R.styleable.AndroidManifestSdkLibrary_name);
-            final int versionMajor = sa.getInt(
-                    R.styleable.AndroidManifestSdkLibrary_versionMajor,
-                    -1);
-
-            // Fail if malformed.
-            if (lname == null || versionMajor < 0) {
-                return input.error("Bad sdk-library declaration name: " + lname
-                        + " version: " + versionMajor);
-            } else if (pkg.getSharedUserId() != null) {
-                return input.error(
-                        PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
-                        "sharedUserId not allowed in SDK library"
-                );
-            } else if (pkg.getSdkLibName() != null) {
-                return input.error("Multiple SDKs for package "
-                        + pkg.getPackageName());
-            }
-
-            return input.success(pkg.setSdkLibName(lname.intern())
-                    .setSdkLibVersionMajor(versionMajor)
-                    .setSdkLibrary(true));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseStaticLibrary(
-            ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, ParseInput input) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            String lname = sa.getNonResourceString(
-                    R.styleable.AndroidManifestStaticLibrary_name);
-            final int version = sa.getInt(
-                    R.styleable.AndroidManifestStaticLibrary_version, -1);
-            final int versionMajor = sa.getInt(
-                    R.styleable.AndroidManifestStaticLibrary_versionMajor,
-                    0);
-
-            // Since the app canot run without a static lib - fail if malformed
-            if (lname == null || version < 0) {
-                return input.error("Bad static-library declaration name: " + lname
-                        + " version: " + version);
-            } else if (pkg.getSharedUserId() != null) {
-                return input.error(
-                        PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
-                        "sharedUserId not allowed in static shared library"
-                );
-            } else if (pkg.getStaticSharedLibName() != null) {
-                return input.error("Multiple static-shared libs for package "
-                        + pkg.getPackageName());
-            }
-
-            return input.success(pkg.setStaticSharedLibName(lname.intern())
-                    .setStaticSharedLibVersion(
-                            PackageInfo.composeLongVersionCode(versionMajor, version))
-                    .setStaticSharedLibrary(true));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseLibrary(
-            ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, ParseInput input) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
-
-            if (lname != null) {
-                lname = lname.intern();
-                if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
-                    pkg.addLibraryName(lname);
-                }
-            }
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseUsesSdkLibrary(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdkLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource that may change.
-            String lname = sa.getNonResourceString(
-                    R.styleable.AndroidManifestUsesSdkLibrary_name);
-            final int versionMajor = sa.getInt(
-                    R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
-            String certSha256Digest = sa.getNonResourceString(R.styleable
-                    .AndroidManifestUsesSdkLibrary_certDigest);
-
-            // Since an APK providing a static shared lib can only provide the lib - fail if
-            // malformed
-            if (lname == null || versionMajor < 0 || certSha256Digest == null) {
-                return input.error("Bad uses-sdk-library declaration name: " + lname
-                        + " version: " + versionMajor + " certDigest" + certSha256Digest);
-            }
-
-            // Can depend only on one version of the same library
-            List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
-            if (usesSdkLibraries.contains(lname)) {
-                return input.error(
-                        "Depending on multiple versions of SDK library " + lname);
-            }
-
-            lname = lname.intern();
-            // We allow ":" delimiters in the SHA declaration as this is the format
-            // emitted by the certtool making it easy for developers to copy/paste.
-            certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
-
-            if ("".equals(certSha256Digest)) {
-                // Test-only uses-sdk-library empty certificate digest override.
-                certSha256Digest = SystemProperties.get(
-                        "debug.pm.uses_sdk_library_default_cert_digest", "");
-                // Validate the overridden digest.
-                try {
-                    HexEncoding.decode(certSha256Digest, false);
-                } catch (IllegalArgumentException e) {
-                    certSha256Digest = "";
-                }
-            }
-
-            ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
-            if (certResult.isError()) {
-                return input.error(certResult);
-            }
-            String[] additionalCertSha256Digests = certResult.getResult();
-
-            final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
-            certSha256Digests[0] = certSha256Digest;
-            System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
-                    1, additionalCertSha256Digests.length);
-
-            return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource that may change.
-            String lname = sa.getNonResourceString(
-                    R.styleable.AndroidManifestUsesLibrary_name);
-            final int version = sa.getInt(
-                    R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
-            String certSha256Digest = sa.getNonResourceString(R.styleable
-                    .AndroidManifestUsesStaticLibrary_certDigest);
-
-            // Since an APK providing a static shared lib can only provide the lib - fail if
-            // malformed
-            if (lname == null || version < 0 || certSha256Digest == null) {
-                return input.error("Bad uses-static-library declaration name: " + lname
-                        + " version: " + version + " certDigest" + certSha256Digest);
-            }
-
-            // Can depend only on one version of the same library
-            List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
-            if (usesStaticLibraries.contains(lname)) {
-                return input.error(
-                        "Depending on multiple versions of static library " + lname);
-            }
-
-            lname = lname.intern();
-            // We allow ":" delimiters in the SHA declaration as this is the format
-            // emitted by the certtool making it easy for developers to copy/paste.
-            certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
-
-            // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
-            String[] additionalCertSha256Digests = EmptyArray.STRING;
-            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
-                ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
-                if (certResult.isError()) {
-                    return input.error(certResult);
-                }
-                additionalCertSha256Digests = certResult.getResult();
-            }
-
-            final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
-            certSha256Digests[0] = certSha256Digest;
-            System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
-                    1, additionalCertSha256Digests.length);
-
-            return input.success(pkg.addUsesStaticLibrary(lname, version, certSha256Digests));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
-            boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
-
-            if (lname != null) {
-                lname = lname.intern();
-                if (req) {
-                    // Upgrade to treat as stronger constraint
-                    pkg.addUsesLibrary(lname)
-                            .removeUsesOptionalLibrary(lname);
-                } else {
-                    // Ignore if someone already defined as required
-                    if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
-                        pkg.addUsesOptionalLibrary(lname);
-                    }
-                }
-            }
-
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            String lname = sa.getNonResourceString(
-                    R.styleable.AndroidManifestUsesNativeLibrary_name);
-            boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
-                    true);
-
-            if (lname != null) {
-                if (req) {
-                    // Upgrade to treat as stronger constraint
-                    pkg.addUsesNativeLibrary(lname)
-                            .removeUsesOptionalNativeLibrary(lname);
-                } else {
-                    // Ignore if someone already defined as required
-                    if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
-                        pkg.addUsesOptionalNativeLibrary(lname);
-                    }
-                }
-            }
-
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
-            throws IOException, XmlPullParserException {
-        ParseResult<ArrayMap<String, ParsedProcess>> result =
-                ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
-                        input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        return input.success(pkg.setProcesses(result.getResult()));
-    }
-
-    @NonNull
-    private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
-        try {
-            ParsingPackage newPkg = pkg.setProfileableByShell(pkg.isProfileableByShell()
-                    || bool(false, R.styleable.AndroidManifestProfileable_shell, sa));
-            return input.success(newPkg.setProfileable(newPkg.isProfileable()
-                    && bool(true, R.styleable.AndroidManifestProfileable_enabled, sa)));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
-            Resources resources, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        String[] certSha256Digests = EmptyArray.STRING;
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final String nodeName = parser.getName();
-            if (nodeName.equals("additional-certificate")) {
-                TypedArray sa = resources.obtainAttributes(parser,
-                        R.styleable.AndroidManifestAdditionalCertificate);
-                try {
-                    String certSha256Digest = sa.getNonResourceString(
-                            R.styleable.AndroidManifestAdditionalCertificate_certDigest);
-
-                    if (TextUtils.isEmpty(certSha256Digest)) {
-                        return input.error("Bad additional-certificate declaration with empty"
-                                + " certDigest:" + certSha256Digest);
-                    }
-
-
-                    // We allow ":" delimiters in the SHA declaration as this is the format
-                    // emitted by the certtool making it easy for developers to copy/paste.
-                    certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
-                    certSha256Digests = ArrayUtils.appendElement(String.class,
-                            certSha256Digests, certSha256Digest);
-                } finally {
-                    sa.recycle();
-                }
-            }
-        }
-
-        return input.success(certSha256Digests);
-    }
-
-    /**
-     * Generate activity object that forwards user to App Details page automatically.
-     * This activity should be invisible to user and user should not know or see it.
-     */
-    @NonNull
-    private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
-            ParsingPackage pkg) {
-        String packageName = pkg.getPackageName();
-        ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName(
-                packageName, packageName, ":app_details", input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        String taskAffinity = result.getResult();
-
-        // Build custom App Details activity info instead of parsing it from xml
-        return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
-                pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
-                pkg.isBaseHardwareAccelerated()));
-    }
-
-    /**
-     * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
-     *
-     * This is distinct from any of the functionality of app links domain verification, and cannot
-     * be converted to remain backwards compatible. It's possible the presence of this flag does
-     * not indicate a valid package for domain verification.
-     */
-    private static boolean hasDomainURLs(ParsingPackage pkg) {
-        final List<ParsedActivity> activities = pkg.getActivities();
-        final int activitiesSize = activities.size();
-        for (int index = 0; index < activitiesSize; index++) {
-            ParsedActivity activity = activities.get(index);
-            List<ParsedIntentInfo> filters = activity.getIntents();
-            final int filtersSize = filters.size();
-            for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
-                IntentFilter aii = filters.get(filtersIndex).getIntentFilter();
-                if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
-                if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
-                if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
-                        aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Sets the max aspect ratio of every child activity that doesn't already have an aspect
-     * ratio set.
-     */
-    private static void setMaxAspectRatio(ParsingPackage pkg) {
-        // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
-        // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
-        float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
-
-        float packageMaxAspectRatio = pkg.getMaxAspectRatio();
-        if (packageMaxAspectRatio != 0) {
-            // Use the application max aspect ration as default if set.
-            maxAspectRatio = packageMaxAspectRatio;
-        } else {
-            Bundle appMetaData = pkg.getMetaData();
-            if (appMetaData != null && appMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
-                maxAspectRatio = appMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
-            }
-        }
-
-        List<ParsedActivity> activities = pkg.getActivities();
-        int activitiesSize = activities.size();
-        for (int index = 0; index < activitiesSize; index++) {
-            ParsedActivity activity = activities.get(index);
-            // If the max aspect ratio for the activity has already been set, skip.
-            if (activity.getMaxAspectRatio() != ASPECT_RATIO_NOT_SET) {
-                continue;
-            }
-
-            // By default we prefer to use a values defined on the activity directly than values
-            // defined on the application. We do not check the styled attributes on the activity
-            // as it would have already been set when we processed the activity. We wait to
-            // process the meta data here since this method is called at the end of processing
-            // the application and all meta data is guaranteed.
-            final float activityAspectRatio = activity.getMetaData() != null
-                    ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
-                    : maxAspectRatio;
-
-            ComponentMutateUtils.setMaxAspectRatio(activity, activity.getResizeMode(),
-                    activityAspectRatio);
-        }
-    }
-
-    /**
-     * Sets the min aspect ratio of every child activity that doesn't already have an aspect
-     * ratio set.
-     */
-    private void setMinAspectRatio(ParsingPackage pkg) {
-        // Use the application max aspect ration as default if set.
-        final float minAspectRatio = pkg.getMinAspectRatio();
-
-        List<ParsedActivity> activities = pkg.getActivities();
-        int activitiesSize = activities.size();
-        for (int index = 0; index < activitiesSize; index++) {
-            ParsedActivity activity = activities.get(index);
-            if (activity.getMinAspectRatio() == ASPECT_RATIO_NOT_SET) {
-                ComponentMutateUtils.setMinAspectRatio(activity, activity.getResizeMode(),
-                        minAspectRatio);
-            }
-        }
-    }
-
-    private void setSupportsSizeChanges(ParsingPackage pkg) {
-        final Bundle appMetaData = pkg.getMetaData();
-        final boolean supportsSizeChanges = appMetaData != null
-                && appMetaData.getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false);
-
-        List<ParsedActivity> activities = pkg.getActivities();
-        int activitiesSize = activities.size();
-        for (int index = 0; index < activitiesSize; index++) {
-            ParsedActivity activity = activities.get(index);
-            if (supportsSizeChanges || (activity.getMetaData() != null
-                    && activity.getMetaData().getBoolean(
-                            METADATA_SUPPORTS_SIZE_CHANGES, false))) {
-                ComponentMutateUtils.setSupportsSizeChanges(activity, true);
-            }
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
-        try {
-            String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
-            int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
-
-            if (target == null) {
-                return input.error("<overlay> does not specify a target package");
-            } else if (priority < 0 || priority > 9999) {
-                return input.error("<overlay> priority must be between 0 and 9999");
-            }
-
-            // check to see if overlay should be excluded based on system property condition
-            String propName = sa.getString(
-                    R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
-            String propValue = sa.getString(
-                    R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
-            if (!checkRequiredSystemProperties(propName, propValue)) {
-                String message = "Skipping target and overlay pair " + target + " and "
-                        + pkg.getBaseApkPath()
-                        + ": overlay ignored due to required system property: "
-                        + propName + " with value: " + propValue;
-                Slog.i(TAG, message);
-                return input.skip(message);
-            }
-
-            return input.success(pkg.setOverlay(true)
-                    .setOverlayTarget(target)
-                    .setOverlayPriority(priority)
-                    .setOverlayTargetOverlayableName(
-                            sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
-                    .setOverlayCategory(
-                            sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
-                    .setOverlayIsStatic(
-                            bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
-        try {
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
-            if (name != null) {
-                pkg.addProtectedBroadcast(name);
-            }
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
-        try {
-            int requiresSmallestWidthDp = anInt(0,
-                    R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
-            int compatibleWidthLimitDp = anInt(0,
-                    R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
-            int largestWidthLimitDp = anInt(0,
-                    R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
-
-            // This is a trick to get a boolean and still able to detect
-            // if a value was actually set.
-            return input.success(pkg
-                    .setSupportsSmallScreens(
-                            anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
-                    .setSupportsNormalScreens(
-                            anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
-                    .setSupportsLargeScreens(
-                            anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
-                    .setSupportsExtraLargeScreens(
-                            anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
-                    .setResizeable(
-                            anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
-                    .setAnyDensity(
-                            anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
-                    .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
-                    .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
-                    .setLargestWidthLimitDp(largestWidthLimitDp));
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
-                pkg, res, parser, sUseRoundIcon, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        return input.success(pkg.addInstrumentation(result.getResult()));
-    }
-
-    private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
-        try {
-            String orig = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestOriginalPackage_name,
-                    0);
-            if (!pkg.getPackageName().equals(orig)) {
-                pkg.addOriginalPackage(orig);
-            }
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
-        try {
-            String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
-            if (name != null) {
-                pkg.addAdoptPermission(name);
-            }
-            return input.success(pkg);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    private static void convertCompatPermissions(ParsingPackage pkg) {
-        for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
-            final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
-            if (pkg.getTargetSdkVersion() >= info.getSdkVersion()) {
-                break;
-            }
-            if (!pkg.getRequestedPermissions().contains(info.getName())) {
-                pkg.addImplicitPermission(info.getName());
-            }
-        }
-    }
-
-    private void convertSplitPermissions(ParsingPackage pkg) {
-        final int listSize = mSplitPermissionInfos.size();
-        for (int is = 0; is < listSize; is++) {
-            final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
-            List<String> requestedPermissions = pkg.getRequestedPermissions();
-            if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
-                    || !requestedPermissions.contains(spi.getSplitPermission())) {
-                continue;
-            }
-            final List<String> newPerms = spi.getNewPermissions();
-            for (int in = 0; in < newPerms.size(); in++) {
-                final String perm = newPerms.get(in);
-                if (!requestedPermissions.contains(perm)) {
-                    pkg.addImplicitPermission(perm);
-                }
-            }
-        }
-    }
-
-    /**
-     * This is a pre-density application which will get scaled - instead of being pixel perfect.
-     * This type of application is not resizable.
-     *
-     * @param pkg The package which needs to be marked as unresizable.
-     */
-    private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
-        List<ParsedActivity> activities = pkg.getActivities();
-        int activitiesSize = activities.size();
-        for (int index = 0; index < activitiesSize; index++) {
-            ParsedActivity activity = activities.get(index);
-            ComponentMutateUtils.setResizeMode(activity, RESIZE_MODE_UNRESIZEABLE);
-            ComponentMutateUtils.setExactFlags(activity,
-                    activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
-        }
-    }
-
-    /**
-     * Check if the given name is valid.
-     *
-     * @param name The name to check.
-     * @param requireSeparator {@code true} if the name requires containing a separator at least.
-     * @param requireFilename {@code true} to apply file name validation to the given name. It also
-     *                        limits length of the name to the {@link #MAX_FILE_NAME_SIZE}.
-     * @return Success if it's valid.
-     */
-    public static String validateName(String name, boolean requireSeparator,
-            boolean requireFilename) {
-        final int N = name.length();
-        boolean hasSep = false;
-        boolean front = true;
-        for (int i = 0; i < N; i++) {
-            final char c = name.charAt(i);
-            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
-                front = false;
-                continue;
-            }
-            if (!front) {
-                if ((c >= '0' && c <= '9') || c == '_') {
-                    continue;
-                }
-            }
-            if (c == '.') {
-                hasSep = true;
-                front = true;
-                continue;
-            }
-            return "bad character '" + c + "'";
-        }
-        if (requireFilename) {
-            if (!FileUtils.isValidExtFilename(name)) {
-                return "Invalid filename";
-            } else if (N > MAX_FILE_NAME_SIZE) {
-                return "the length of the name is greater than " + MAX_FILE_NAME_SIZE;
-            }
-        }
-        return hasSep || !requireSeparator ? null : "must have at least one '.' separator";
-    }
-
-    /**
-     * @see #validateName(String, boolean, boolean)
-     */
-    public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
-            boolean requireFilename) {
-        final String errorMessage = validateName(name, requireSeparator, requireFilename);
-        if (errorMessage != null) {
-            return input.error(errorMessage);
-        }
-        return input.success(null);
-    }
-
-    /**
-     * Parse a meta data defined on the enclosing tag.
-     * <p>Meta data can be defined by either &lt;meta-data&gt; or &lt;property&gt; elements.
-     */
-    public static ParseResult<Property> parseMetaData(ParsingPackage pkg, ParsedComponent component,
-            Resources res, XmlResourceParser parser, String tagName, ParseInput input) {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
-        try {
-            final Property property;
-            final String name = TextUtils.safeIntern(
-                    nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
-            if (name == null) {
-                return input.error(tagName + " requires an android:name attribute");
-            }
-
-            final String packageName = pkg.getPackageName();
-            final String className = component != null ? component.getName() : null;
-            TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
-            if (v != null && v.resourceId != 0) {
-                property = new Property(name, v.resourceId, true, packageName, className);
-            } else {
-                v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
-                if (v != null) {
-                    if (v.type == TypedValue.TYPE_STRING) {
-                        final CharSequence cs = v.coerceToString();
-                        final String stringValue = cs != null ? cs.toString() : null;
-                        property = new Property(name, stringValue, packageName, className);
-                    } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
-                        property = new Property(name, v.data != 0, packageName, className);
-                    } else if (v.type >= TypedValue.TYPE_FIRST_INT
-                            && v.type <= TypedValue.TYPE_LAST_INT) {
-                        property = new Property(name, v.data, false, packageName, className);
-                    } else if (v.type == TypedValue.TYPE_FLOAT) {
-                        property = new Property(name, v.getFloat(), packageName, className);
-                    } else {
-                        if (!RIGID_PARSER) {
-                            Slog.w(TAG,
-                                    tagName + " only supports string, integer, float, color, "
-                                            + "boolean, and resource reference types: "
-                                            + parser.getName() + " at "
-                                            + pkg.getBaseApkPath() + " "
-                                            + parser.getPositionDescription());
-                            property = null;
-                        } else {
-                            return input.error(tagName + " only supports string, integer, float, "
-                                    + "color, boolean, and resource reference types");
-                        }
-                    }
-                } else {
-                    return input.error(tagName + " requires an android:value "
-                            + "or android:resource attribute");
-                }
-            }
-            return input.success(property);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    /**
-     * @return {@link PublicKey} of a given encoded public key.
-     */
-    public static final PublicKey parsePublicKey(final String encodedPublicKey) {
-        if (encodedPublicKey == null) {
-            Slog.w(TAG, "Could not parse null public key");
-            return null;
-        }
-
-        try {
-            return parsePublicKey(Base64.decode(encodedPublicKey, Base64.DEFAULT));
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
-            return null;
-        }
-    }
-
-    /**
-     * @return {@link PublicKey} of the given byte array of a public key.
-     */
-    public static final PublicKey parsePublicKey(final byte[] publicKey) {
-        if (publicKey == null) {
-            Slog.w(TAG, "Could not parse null public key");
-            return null;
-        }
-
-        final EncodedKeySpec keySpec;
-        try {
-            keySpec = new X509EncodedKeySpec(publicKey);
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
-            return null;
-        }
-
-        /* First try the key as an RSA key. */
-        try {
-            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
-            return keyFactory.generatePublic(keySpec);
-        } catch (NoSuchAlgorithmException e) {
-            Slog.wtf(TAG, "Could not parse public key: RSA KeyFactory not included in build");
-        } catch (InvalidKeySpecException e) {
-            // Not a RSA public key.
-        }
-
-        /* Now try it as a ECDSA key. */
-        try {
-            final KeyFactory keyFactory = KeyFactory.getInstance("EC");
-            return keyFactory.generatePublic(keySpec);
-        } catch (NoSuchAlgorithmException e) {
-            Slog.wtf(TAG, "Could not parse public key: EC KeyFactory not included in build");
-        } catch (InvalidKeySpecException e) {
-            // Not a ECDSA public key.
-        }
-
-        /* Now try it as a DSA key. */
-        try {
-            final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
-            return keyFactory.generatePublic(keySpec);
-        } catch (NoSuchAlgorithmException e) {
-            Slog.wtf(TAG, "Could not parse public key: DSA KeyFactory not included in build");
-        } catch (InvalidKeySpecException e) {
-            // Not a DSA public key.
-        }
-
-        /* Not a supported key type */
-        return null;
-    }
-
-    /**
-     * Returns {@code true} if both the property name and value are empty or if the given system
-     * property is set to the specified value. Properties can be one or more, and if properties are
-     * more than one, they must be separated by comma, and count of names and values must be equal,
-     * and also every given system property must be set to the corresponding value.
-     * In all other cases, returns {@code false}
-     */
-    public static boolean checkRequiredSystemProperties(@Nullable String rawPropNames,
-            @Nullable String rawPropValues) {
-        if (TextUtils.isEmpty(rawPropNames) || TextUtils.isEmpty(rawPropValues)) {
-            if (!TextUtils.isEmpty(rawPropNames) || !TextUtils.isEmpty(rawPropValues)) {
-                // malformed condition - incomplete
-                Slog.w(TAG, "Disabling overlay - incomplete property :'" + rawPropNames
-                        + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
-                        + " AND requiredSystemPropertyValue to be specified.");
-                return false;
-            }
-            // no valid condition set - so no exclusion criteria, overlay will be included.
-            return true;
-        }
-
-        final String[] propNames = rawPropNames.split(",");
-        final String[] propValues = rawPropValues.split(",");
-
-        if (propNames.length != propValues.length) {
-            Slog.w(TAG, "Disabling overlay - property :'" + rawPropNames
-                    + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
-                    + " AND requiredSystemPropertyValue lists to have the same size.");
-            return false;
-        }
-        for (int i = 0; i < propNames.length; i++) {
-            // Check property value: make sure it is both set and equal to expected value
-            final String currValue = SystemProperties.get(propNames[i]);
-            if (!TextUtils.equals(currValue, propValues[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Collect certificates from all the APKs described in the given package. Also asserts that
-     * all APK contents are signed correctly and consistently.
-     *
-     * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
-     *  call if requested. Leaving this as an optional method for the caller means we have to
-     *  construct a dummy ParseInput.
-     */
-    @CheckResult
-    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
-            ParsingPackageRead pkg, boolean skipVerify) {
-        SigningDetails signingDetails = SigningDetails.UNKNOWN;
-
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
-        try {
-            ParseResult<SigningDetails> result = getSigningDetails(
-                    input,
-                    pkg.getBaseApkPath(),
-                    skipVerify,
-                    pkg.isStaticSharedLibrary(),
-                    signingDetails,
-                    pkg.getTargetSdkVersion()
-            );
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            signingDetails = result.getResult();
-
-            String[] splitCodePaths = pkg.getSplitCodePaths();
-            if (!ArrayUtils.isEmpty(splitCodePaths)) {
-                for (int i = 0; i < splitCodePaths.length; i++) {
-                    result = getSigningDetails(
-                            input,
-                            splitCodePaths[i],
-                            skipVerify,
-                            pkg.isStaticSharedLibrary(),
-                            signingDetails,
-                            pkg.getTargetSdkVersion()
-                    );
-                    if (result.isError()) {
-                        return input.error(result);
-                    }
-                }
-            }
-            return result;
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
-
-    @CheckResult
-    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
-            String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
-            @NonNull SigningDetails existingSigningDetails, int targetSdk) {
-        int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
-                targetSdk);
-        if (isStaticSharedLibrary) {
-            // must use v2 signing scheme
-            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
-        }
-        final ParseResult<SigningDetails> verified;
-        if (skipVerify) {
-            // systemDir APKs are already trusted, save time by not verifying; since the
-            // signature is not verified and some system apps can have their V2+ signatures
-            // stripped allow pulling the certs from the jar signature.
-            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
-                    SigningDetails.SignatureSchemeVersion.JAR);
-        } else {
-            verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
-        }
-
-        if (verified.isError()) {
-            return input.error(verified);
-        }
-
-        // Verify that entries are signed consistently with the first pkg
-        // we encountered. Note that for splits, certificates may have
-        // already been populated during an earlier parse of a base APK.
-        if (existingSigningDetails == SigningDetails.UNKNOWN) {
-            return verified;
-        } else {
-            if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
-                    verified.getResult().getSignatures())) {
-                return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
-                        baseCodePath + " has mismatched certificates");
-            }
-
-            return input.success(existingSigningDetails);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
-        sCompatibilityModeEnabled = compatibilityModeEnabled;
-    }
-
-    /**
-     * @hide
-     */
-    public static void readConfigUseRoundIcon(Resources r) {
-        if (r != null) {
-            sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
-            return;
-        }
-
-        final ApplicationInfo androidAppInfo;
-        try {
-            androidAppInfo = ActivityThread.getPackageManager().getApplicationInfo(
-                    "android", 0 /* flags */,
-                    UserHandle.myUserId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        final Resources systemResources = Resources.getSystem();
-
-        // Create in-flight as this overlayable resource is only used when config changes
-        final Resources overlayableRes = ResourcesManager.getInstance().getResources(
-                null /* activityToken */,
-                null /* resDir */,
-                null /* splitResDirs */,
-                androidAppInfo.resourceDirs,
-                androidAppInfo.overlayPaths,
-                androidAppInfo.sharedLibraryFiles,
-                null /* overrideDisplayId */,
-                null /* overrideConfig */,
-                systemResources.getCompatibilityInfo(),
-                systemResources.getClassLoader(),
-                null /* loaders */);
-
-        sUseRoundIcon = overlayableRes.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
-    }
-
-    /*
-     The following set of methods makes code easier to read by re-ordering the TypedArray methods.
-
-     The first parameter is the default, which is the most important to understand for someone
-     reading through the parsing code.
-
-     That's followed by the attribute name, which is usually irrelevant during reading because
-     it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
-     the "setSomeValue" part is enough to communicate what the line does.
-
-     Last comes the TypedArray, which is by far the least important since each try-with-resources
-     should only have 1.
-    */
-
-    // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
-    // is important to specify when adding an attribute.
-    private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
-        return sa.getBoolean(attribute, defaultValue);
-    }
-
-    private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
-        return sa.getFloat(attribute, defaultValue);
-    }
-
-    private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
-        return sa.getFloat(attribute, 0f);
-    }
-
-    private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
-        return sa.getInt(attribute, defaultValue);
-    }
-
-    private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
-        return sa.getInteger(attribute, defaultValue);
-    }
-
-    private static int anInt(@StyleableRes int attribute, TypedArray sa) {
-        return sa.getInt(attribute, 0);
-    }
-
-    @AnyRes
-    private static int resId(@StyleableRes int attribute, TypedArray sa) {
-        return sa.getResourceId(attribute, 0);
-    }
-
-    private static String string(@StyleableRes int attribute, TypedArray sa) {
-        return sa.getString(attribute);
-    }
-
-    private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
-            TypedArray sa) {
-        return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
-    }
-
-    private static String nonResString(@StyleableRes int index, TypedArray sa) {
-        return sa.getNonResourceString(index);
-    }
-
-    /**
-     * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
-     */
-    public static void writeKeySetMapping(@NonNull Parcel dest,
-            @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
-        if (keySetMapping == null) {
-            dest.writeInt(-1);
-            return;
-        }
-
-        final int N = keySetMapping.size();
-        dest.writeInt(N);
-
-        for (String key : keySetMapping.keySet()) {
-            dest.writeString(key);
-            ArraySet<PublicKey> keys = keySetMapping.get(key);
-            if (keys == null) {
-                dest.writeInt(-1);
-                continue;
-            }
-
-            final int M = keys.size();
-            dest.writeInt(M);
-            for (int j = 0; j < M; j++) {
-                dest.writeSerializable(keys.valueAt(j));
-            }
-        }
-    }
-
-    /**
-     * Reads a keyset mapping from the given parcel at the given data position. May return
-     * {@code null} if the serialized mapping was {@code null}.
-     */
-    @NonNull
-    public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
-        final int N = in.readInt();
-        if (N == -1) {
-            return null;
-        }
-
-        ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
-        for (int i = 0; i < N; ++i) {
-            String key = in.readString();
-            final int M = in.readInt();
-            if (M == -1) {
-                keySetMapping.put(key, null);
-                continue;
-            }
-
-            ArraySet<PublicKey> keys = new ArraySet<>(M);
-            for (int j = 0; j < M; ++j) {
-                PublicKey pk = (PublicKey) in.readSerializable(java.security.PublicKey.class.getClassLoader(), java.security.PublicKey.class);
-                keys.add(pk);
-            }
-
-            keySetMapping.put(key, keys);
-        }
-
-        return keySetMapping;
-    }
-
-
-    /**
-     * Callback interface for retrieving information that may be needed while parsing
-     * a package.
-     */
-    public interface Callback {
-        boolean hasFeature(String feature);
-
-        ParsingPackage startParsingPackage(@NonNull String packageName,
-                @NonNull String baseApkPath, @NonNull String path,
-                @NonNull TypedArray manifestArray, boolean isCoreApp);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
deleted file mode 100644
index 6dfb268..0000000
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import static android.content.pm.parsing.ParsingPackageUtils.RIGID_PARSER;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedIntentInfoImpl;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.XmlResourceParser;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Pair;
-import android.util.Slog;
-
-import com.android.internal.util.Parcelling;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/** @hide **/
-public class ParsingUtils {
-
-    public static final String TAG = "PackageParsing";
-
-    public static final String ANDROID_RES_NAMESPACE = "http://schemas.android.com/apk/res/android";
-
-    public static final int DEFAULT_MIN_SDK_VERSION = 1;
-    public static final int DEFAULT_TARGET_SDK_VERSION = 0;
-
-    public static final int NOT_SET = -1;
-
-    @Nullable
-    public static String buildClassName(String pkg, CharSequence clsSeq) {
-        if (clsSeq == null || clsSeq.length() <= 0) {
-            return null;
-        }
-        String cls = clsSeq.toString();
-        char c = cls.charAt(0);
-        if (c == '.') {
-            return pkg + cls;
-        }
-        if (cls.indexOf('.') < 0) {
-            StringBuilder b = new StringBuilder(pkg);
-            b.append('.');
-            b.append(cls);
-            return b.toString();
-        }
-        return cls;
-    }
-
-    @NonNull
-    public static ParseResult unknownTag(String parentTag, ParsingPackage pkg,
-            XmlResourceParser parser, ParseInput input) throws IOException, XmlPullParserException {
-        if (RIGID_PARSER) {
-            return input.error("Bad element under " + parentTag + ": " + parser.getName());
-        }
-        Slog.w(TAG, "Unknown element under " + parentTag + ": "
-                + parser.getName() + " at " + pkg.getBaseApkPath() + " "
-                + parser.getPositionDescription());
-        XmlUtils.skipCurrentTag(parser);
-        return input.success(null); // Type doesn't matter
-    }
-
-    /**
-     * Use with {@link Parcel#writeTypedList(List)}
-     *
-     * @see Parcel#createTypedArrayList(Parcelable.Creator)
-     */
-    @NonNull
-    public static <Interface, Impl extends Interface> List<Interface> createTypedInterfaceList(
-            @NonNull Parcel parcel, @NonNull Parcelable.Creator<Impl> creator) {
-        int size = parcel.readInt();
-        if (size < 0) {
-            return new ArrayList<>();
-        }
-        ArrayList<Interface> list = new ArrayList<Interface>(size);
-        while (size > 0) {
-            list.add(parcel.readTypedObject(creator));
-            size--;
-        }
-        return list;
-    }
-
-    public static class StringPairListParceler implements
-            Parcelling<List<Pair<String, ParsedIntentInfo>>> {
-
-        @Override
-        public void parcel(List<Pair<String, ParsedIntentInfo>> item, Parcel dest,
-                int parcelFlags) {
-            if (item == null) {
-                dest.writeInt(-1);
-                return;
-            }
-
-            final int size = item.size();
-            dest.writeInt(size);
-
-            for (int index = 0; index < size; index++) {
-                Pair<String, ParsedIntentInfo> pair = item.get(index);
-                dest.writeString(pair.first);
-                dest.writeParcelable(pair.second, parcelFlags);
-            }
-        }
-
-        @Override
-        public List<Pair<String, ParsedIntentInfo>> unparcel(Parcel source) {
-            int size = source.readInt();
-            if (size == -1) {
-                return null;
-            }
-
-            if (size == 0) {
-                return new ArrayList<>(0);
-            }
-
-            final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
-            for (int i = 0; i < size; ++i) {
-                list.add(Pair.create(source.readString(), source.readParcelable(
-                        ParsedIntentInfoImpl.class.getClassLoader(), android.content.pm.parsing.component.ParsedIntentInfo.class)));
-            }
-
-            return list;
-        }
-    }
-}
diff --git a/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java b/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
deleted file mode 100644
index 625b9d1..0000000
--- a/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.util.SparseArray;
-
-/**
- * Container for fields that are eventually exposed through {@link ApplicationInfo}.
- * <p>
- * The following are dependent on system state and explicitly removed from this interface. They must
- * be accessed by other means:
- * <ul>
- *    <li>{@link ApplicationInfo#credentialProtectedDataDir}</li>
- *    <li>{@link ApplicationInfo#dataDir}</li>
- *    <li>{@link ApplicationInfo#deviceProtectedDataDir}</li>
- *    <li>{@link ApplicationInfo#enabledSetting}</li>
- *    <li>{@link ApplicationInfo#enabled}</li>
- *    <li>{@link ApplicationInfo#FLAG_INSTALLED}</li>
- *    <li>{@link ApplicationInfo#FLAG_STOPPED}</li>
- *    <li>{@link ApplicationInfo#FLAG_SUSPENDED}</li>
- *    <li>{@link ApplicationInfo#FLAG_UPDATED_SYSTEM_APP}</li>
- *    <li>{@link ApplicationInfo#hiddenUntilInstalled}</li>
- *    <li>{@link ApplicationInfo#primaryCpuAbi}</li>
- *    <li>{@link ApplicationInfo#PRIVATE_FLAG_HIDDEN}</li>
- *    <li>{@link ApplicationInfo#PRIVATE_FLAG_INSTANT}</li>
- *    <li>{@link ApplicationInfo#PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER}</li>
- *    <li>{@link ApplicationInfo#PRIVATE_FLAG_VIRTUAL_PRELOAD}</li>
- *    <li>{@link ApplicationInfo#resourceDirs}</li>
- *    <li>{@link ApplicationInfo#secondaryCpuAbi}</li>
- *    <li>{@link ApplicationInfo#seInfoUser}</li>
- *    <li>{@link ApplicationInfo#seInfo}</li>
- *    <li>{@link ApplicationInfo#sharedLibraryFiles}</li>
- *    <li>{@link ApplicationInfo#sharedLibraryInfos}</li>
- *    <li>{@link ApplicationInfo#uid}</li>
- * </ul>
- * The following are derived from other fields and thus not provided specifically:
- * <ul>
- *    <li>{@link ApplicationInfo#getBaseResourcePath}</li>
- *    <li>{@link ApplicationInfo#getResourcePath}</li>
- *    <li>{@link ApplicationInfo#getSplitResourcePaths}</li>
- *    <li>{@link ApplicationInfo#publicSourceDir}</li>
- *    <li>{@link ApplicationInfo#scanPublicSourceDir}</li>
- *    <li>{@link ApplicationInfo#splitPublicSourceDirs}</li>
- *    <li>{@link ApplicationInfo#storageUuid}</li>
- * </ul>
- * The following were deprecated at migration time and thus removed from this interface:
- * <ul>
- *    <li>{@link ApplicationInfo#FLAG_IS_GAME}</li>
- *    <li>{@link ApplicationInfo#targetSandboxVersion}</li>
- *    <li>{@link ApplicationInfo#versionCode}</li>
- * </ul>
- * TODO: The following fields are just not available at all. Never filled, even by legacy parsing?
- * <ul>
- *    <li>{@link ApplicationInfo#FLAG_IS_DATA_ONLY}</li>
- * </ul>
- *
- * @hide
- */
-public interface PkgWithoutStateAppInfo {
-
-    /**
-     * @see ApplicationInfo#areAttributionsUserVisible()
-     * @see R.styleable#AndroidManifestApplication_attributionsAreUserVisible
-     */
-    @Nullable
-    boolean areAttributionsUserVisible();
-
-    /**
-     * @see ApplicationInfo#appComponentFactory
-     * @see R.styleable#AndroidManifestApplication_appComponentFactory
-     */
-    @Nullable
-    String getAppComponentFactory();
-
-    /**
-     * @see ApplicationInfo#AUTO_REVOKE_ALLOWED
-     * @see ApplicationInfo#AUTO_REVOKE_DISCOURAGED
-     * @see ApplicationInfo#AUTO_REVOKE_DISALLOWED
-     */
-    int getAutoRevokePermissions();
-
-    /**
-     * @see ApplicationInfo#backupAgentName
-     * @see R.styleable#AndroidManifestApplication_backupAgent
-     */
-    @Nullable
-    String getBackupAgentName();
-
-    /**
-     * @see ApplicationInfo#banner
-     * @see R.styleable#AndroidManifestApplication_banner
-     */
-    int getBanner();
-
-    /**
-     * @see ApplicationInfo#sourceDir
-     * @see ApplicationInfo#getBaseCodePath
-     */
-    @NonNull
-    String getBaseApkPath();
-
-    /**
-     * @see ApplicationInfo#category
-     * @see R.styleable#AndroidManifestApplication_appCategory
-     */
-    int getCategory();
-
-    /**
-     * @see ApplicationInfo#classLoaderName
-     * @see R.styleable#AndroidManifestApplication_classLoader
-     */
-    @Nullable
-    String getClassLoaderName();
-
-    /**
-     * @see ApplicationInfo#className
-     * @see R.styleable#AndroidManifestApplication_name
-     */
-    @Nullable
-    String getClassName();
-
-    /**
-     * @see ApplicationInfo#compatibleWidthLimitDp
-     * @see R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
-     */
-    int getCompatibleWidthLimitDp();
-
-    /**
-     * @see ApplicationInfo#compileSdkVersion
-     * @see R.styleable#AndroidManifest_compileSdkVersion
-     */
-    int getCompileSdkVersion();
-
-    /**
-     * @see ApplicationInfo#compileSdkVersionCodename
-     * @see R.styleable#AndroidManifest_compileSdkVersionCodename
-     */
-    @Nullable
-    String getCompileSdkVersionCodeName();
-
-    /**
-     * @see ApplicationInfo#dataExtractionRulesRes
-     * @see R.styleable#AndroidManifestApplication_dataExtractionRules
-     */
-    int getDataExtractionRules();
-
-    /**
-     * @see ApplicationInfo#descriptionRes
-     * @see R.styleable#AndroidManifestApplication_description
-     */
-    int getDescriptionRes();
-
-    /**
-     * @see ApplicationInfo#fullBackupContent
-     * @see R.styleable#AndroidManifestApplication_fullBackupContent
-     */
-    int getFullBackupContent();
-
-    /**
-     * @see ApplicationInfo#getGwpAsanMode()
-     * @see R.styleable#AndroidManifestApplication_gwpAsanMode
-     */
-    @ApplicationInfo.GwpAsanMode
-    int getGwpAsanMode();
-
-    /**
-     * @see ApplicationInfo#iconRes
-     * @see R.styleable#AndroidManifestApplication_icon
-     */
-    int getIconRes();
-
-    /**
-     * @see ApplicationInfo#installLocation
-     * @see R.styleable#AndroidManifest_installLocation
-     */
-    int getInstallLocation();
-
-    /**
-     * @see ApplicationInfo#labelRes
-     * @see R.styleable#AndroidManifestApplication_label
-     */
-    int getLabelRes();
-
-    /**
-     * @see ApplicationInfo#largestWidthLimitDp
-     * @see R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
-     */
-    int getLargestWidthLimitDp();
-
-    /**
-     * @see ApplicationInfo#logo
-     * @see R.styleable#AndroidManifestApplication_logo
-     */
-    int getLogo();
-
-    /**
-     * @see ApplicationInfo#longVersionCode
-     */
-    long getLongVersionCode();
-
-    /**
-     * @see ApplicationInfo#manageSpaceActivityName
-     * @see R.styleable#AndroidManifestApplication_manageSpaceActivity
-     */
-    @Nullable
-    String getManageSpaceActivityName();
-
-    /**
-     * @see ApplicationInfo#maxAspectRatio
-     * @see R.styleable#AndroidManifestApplication_maxAspectRatio
-     */
-    float getMaxAspectRatio();
-
-    /**
-     * @see ApplicationInfo#getMemtagMode()
-     * @see R.styleable#AndroidManifestApplication_memtagMode
-     */
-    @ApplicationInfo.MemtagMode
-    int getMemtagMode();
-
-    /**
-     * @see ApplicationInfo#minAspectRatio
-     * @see R.styleable#AndroidManifestApplication_minAspectRatio
-     */
-    float getMinAspectRatio();
-
-    /**
-     * @see ApplicationInfo#minSdkVersion
-     * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
-     */
-    int getMinSdkVersion();
-
-    /**
-     * @see ApplicationInfo#getNativeHeapZeroInitialized()
-     * @see R.styleable#AndroidManifestApplication_nativeHeapZeroInitialized
-     */
-    @ApplicationInfo.NativeHeapZeroInitialized
-    int getNativeHeapZeroInitialized();
-
-    /**
-     * @see ApplicationInfo#networkSecurityConfigRes
-     * @see R.styleable#AndroidManifestApplication_networkSecurityConfig
-     */
-    int getNetworkSecurityConfigRes();
-
-    /**
-     * If {@link R.styleable#AndroidManifestApplication_label} is a string literal, this is it.
-     * Otherwise, it's stored as {@link #getLabelRes()}.
-     *
-     * @see ApplicationInfo#nonLocalizedLabel
-     * @see R.styleable#AndroidManifestApplication_label
-     */
-    @Nullable
-    CharSequence getNonLocalizedLabel();
-
-    /**
-     * @see ApplicationInfo#scanSourceDir
-     * @see ApplicationInfo#getCodePath
-     */
-    @NonNull
-    String getPath();
-
-    /**
-     * @see ApplicationInfo#permission
-     * @see R.styleable#AndroidManifestApplication_permission
-     */
-    @Nullable
-    String getPermission();
-
-    /**
-     * @see ApplicationInfo#processName
-     * @see R.styleable#AndroidManifestApplication_process
-     */
-    @NonNull
-    String getProcessName();
-
-    /**
-     * @see ApplicationInfo#requiresSmallestWidthDp
-     * @see R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
-     */
-    int getRequiresSmallestWidthDp();
-
-    /**
-     * Whether or not the app requested explicitly resizeable Activities. Null value means nothing
-     * was explicitly requested.
-     *
-     * @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
-     * @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE
-     */
-    @Nullable
-    Boolean getResizeableActivity();
-
-    /**
-     * @see ApplicationInfo#roundIconRes
-     * @see R.styleable#AndroidManifestApplication_roundIcon
-     */
-    int getRoundIconRes();
-
-    /**
-     * @see ApplicationInfo#splitClassLoaderNames
-     * @see R.styleable#AndroidManifestApplication_classLoader
-     */
-    @Nullable
-    String[] getSplitClassLoaderNames();
-
-    /**
-     * @see ApplicationInfo#splitSourceDirs
-     * @see ApplicationInfo#getSplitCodePaths
-     */
-    @Nullable
-    String[] getSplitCodePaths();
-
-    /**
-     * @see ApplicationInfo#splitDependencies
-     */
-    @Nullable
-    SparseArray<int[]> getSplitDependencies();
-
-    /**
-     * @see ApplicationInfo#targetSdkVersion
-     * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
-     */
-    int getTargetSdkVersion();
-
-    /**
-     * @see ApplicationInfo#targetSandboxVersion
-     * @see R.styleable#AndroidManifest_targetSandboxVersion
-     */
-    int getTargetSandboxVersion();
-
-    /**
-     * @see ApplicationInfo#taskAffinity
-     * @see R.styleable#AndroidManifestApplication_taskAffinity
-     */
-    @Nullable
-    String getTaskAffinity();
-
-    /**
-     * @see ApplicationInfo#theme
-     * @see R.styleable#AndroidManifestApplication_theme
-     */
-    int getTheme();
-
-    /**
-     * @see ApplicationInfo#uiOptions
-     * @see R.styleable#AndroidManifestApplication_uiOptions
-     */
-    int getUiOptions();
-
-    /**
-     * @see ApplicationInfo#volumeUuid
-     */
-    @Nullable
-    String getVolumeUuid();
-
-    /**
-     * @see ApplicationInfo#zygotePreloadName
-     */
-    @Nullable
-    String getZygotePreloadName();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION
-     * @see R.styleable#AndroidManifestApplication_requestForegroundServiceExemption
-     */
-    boolean hasRequestForegroundServiceExemption();
-
-    /**
-     * @see ApplicationInfo#getRequestRawExternalStorageAccess()
-     * @see R.styleable#AndroidManifestApplication_requestRawExternalStorageAccess
-     */
-    Boolean hasRequestRawExternalStorageAccess();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
-     */
-    boolean isAllowAudioPlaybackCapture();
-
-    /**
-     * @see ApplicationInfo#FLAG_ALLOW_BACKUP
-     */
-    boolean isAllowBackup();
-
-    /**
-     * @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA
-     */
-    boolean isAllowClearUserData();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
-     */
-    boolean isAllowClearUserDataOnFailedRestore();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
-     */
-    boolean isAllowNativeHeapPointerTagging();
-
-    /**
-     * @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING
-     */
-    boolean isAllowTaskReparenting();
-
-    /**
-     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
-     * android.os.Build.VERSION_CODES#DONUT}.
-     *
-     * @see R.styleable#AndroidManifestSupportsScreens_anyDensity
-     * @see ApplicationInfo#FLAG_SUPPORTS_SCREEN_DENSITIES
-     */
-    boolean isAnyDensity();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND
-     */
-    boolean isBackupInForeground();
-
-    /**
-     * @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED
-     */
-    boolean isBaseHardwareAccelerated();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE
-     */
-    boolean isCantSaveState();
-
-    /**
-     * @see ApplicationInfo#crossProfile
-     */
-    boolean isCrossProfile();
-
-    /**
-     * @see ApplicationInfo#FLAG_DEBUGGABLE
-     */
-    boolean isDebuggable();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE
-     */
-    boolean isDefaultToDeviceProtectedStorage();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE
-     */
-    boolean isDirectBootAware();
-
-    /**
-     * @see ApplicationInfo#enabled
-     * @see R.styleable#AndroidManifestApplication_enabled
-     */
-    boolean isEnabled();
-
-    /**
-     * @see ApplicationInfo#FLAG_EXTERNAL_STORAGE
-     */
-    boolean isExternalStorage();
-
-    /**
-     * @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS
-     */
-    boolean isExtractNativeLibs();
-
-    /**
-     * @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY
-     */
-    boolean isFullBackupOnly();
-
-    /**
-     * @see ApplicationInfo#FLAG_HAS_CODE
-     */
-    boolean isHasCode();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS
-     */
-    boolean isHasDomainUrls();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA
-     */
-    boolean isHasFragileUserData();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
-     */
-    boolean isIsolatedSplitLoading();
-
-    /**
-     * @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE
-     */
-    boolean isKillAfterRestore();
-
-    /**
-     * @see ApplicationInfo#FLAG_LARGE_HEAP
-     */
-    boolean isLargeHeap();
-
-    /**
-     * @see ApplicationInfo#FLAG_MULTIARCH
-     */
-    boolean isMultiArch();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
-     * @see ApplicationInfo#isResourceOverlay()
-     */
-    boolean isOverlay();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE
-     */
-    boolean isPartiallyDirectBootAware();
-
-    /**
-     * @see ApplicationInfo#FLAG_PERSISTENT
-     */
-    boolean isPersistent();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_EXT_PROFILEABLE
-     */
-    boolean isProfileable();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL
-     */
-    boolean isProfileableByShell();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE
-     */
-    boolean isRequestLegacyExternalStorage();
-
-    /**
-     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
-     * android.os.Build.VERSION_CODES#DONUT}.
-     *
-     * @see R.styleable#AndroidManifestSupportsScreens_resizeable
-     * @see ApplicationInfo#FLAG_RESIZEABLE_FOR_SCREENS
-     */
-    boolean isResizeable();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
-     */
-    boolean isResizeableActivityViaSdkVersion();
-
-    /**
-     * @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION
-     */
-    boolean isRestoreAnyVersion();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY
-     */
-    boolean isStaticSharedLibrary();
-
-    /**
-     * True means that this package/app contains an SDK library.
-     */
-    boolean isSdkLibrary();
-
-    /**
-     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
-     * android.os.Build.VERSION_CODES#GINGERBREAD}.
-     *
-     * @see R.styleable#AndroidManifestSupportsScreens_xlargeScreens
-     * @see ApplicationInfo#FLAG_SUPPORTS_XLARGE_SCREENS
-     */
-    boolean isSupportsExtraLargeScreens();
-
-    /**
-     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
-     * android.os.Build.VERSION_CODES#DONUT}.
-     *
-     * @see R.styleable#AndroidManifestSupportsScreens_largeScreens
-     * @see ApplicationInfo#FLAG_SUPPORTS_LARGE_SCREENS
-     */
-    boolean isSupportsLargeScreens();
-
-    /**
-     * If omitted from manifest, returns true.
-     *
-     * @see R.styleable#AndroidManifestSupportsScreens_normalScreens
-     * @see ApplicationInfo#FLAG_SUPPORTS_NORMAL_SCREENS
-     */
-    boolean isSupportsNormalScreens();
-
-    /**
-     * @see ApplicationInfo#FLAG_SUPPORTS_RTL
-     */
-    boolean isSupportsRtl();
-
-    /**
-     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
-     * android.os.Build.VERSION_CODES#DONUT}.
-     *
-     * @see R.styleable#AndroidManifestSupportsScreens_smallScreens
-     * @see ApplicationInfo#FLAG_SUPPORTS_SMALL_SCREENS
-     */
-    boolean isSupportsSmallScreens();
-
-    /**
-     * @see ApplicationInfo#FLAG_TEST_ONLY
-     */
-    boolean isTestOnly();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX
-     */
-    boolean isUseEmbeddedDex();
-
-    /**
-     * @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC
-     */
-    boolean isUsesCleartextTraffic();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API
-     */
-    boolean isUsesNonSdkApi();
-
-    /**
-     * @see ApplicationInfo#FLAG_VM_SAFE_MODE
-     */
-    boolean isVmSafeMode();
-}
diff --git a/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java b/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java
deleted file mode 100644
index 7d758a8..0000000
--- a/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.InstrumentationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.pkg.FrameworkPackageUserState;
-
-import com.android.internal.R;
-
-import java.util.List;
-
-/**
- * Container for fields that are eventually exposed through {@link PackageInfo}.
- * <p>
- * The following are dependent on system state and explicitly removed from this interface. They must
- * be accessed by other means:
- * <ul>
- *    <li>{@link PackageInfo#firstInstallTime}</li>
- *    <li>{@link PackageInfo#lastUpdateTime}</li>
- *    <li>{@link PackageInfo#gids}</li>
- * </ul>
- * The following are derived from other fields and thus not provided specifically:
- * <ul>
- *    <li>{@link PackageInfo#requestedPermissionsFlags}</li>
- * </ul>
- * The following were deprecated at migration time and thus removed from this interface:
- * <ul>
- *    <li>{@link PackageInfo#mOverlayIsStatic}</li>
- *    <li>{@link PackageInfo#overlayCategory}</li>
- *    <li>{@link PackageInfo#overlayPriority}</li>
- *    <li>{@link PackageInfo#overlayTarget}</li>
- *    <li>{@link PackageInfo#signatures}</li>
- *    <li>{@link PackageInfo#targetOverlayableName}</li>
- *    <li>{@link PackageInfo#versionCodeMajor}</li>
- *    <li>{@link PackageInfo#versionCode}</li>
- * </ul>
- * The following are retrieved through other APIs:
- * <ul>
- *    <li>{@link PackageInfo#signingInfo}</li>
- *    <li>{@link PackageInfo#isApex}</li>
- * </ul>
- *
- * @hide
- */
-public interface PkgWithoutStatePackageInfo {
-
-    /**
-     * Set of Activities parsed from the manifest.
-     *
-     * This contains minimal system state and does not
-     * provide the same information as {@link ActivityInfo}. Effective state can be queried through
-     * {@link android.content.pm.PackageManager#getActivityInfo(ComponentName, int)} or by
-     * combining state from from com.android.server.pm.pkg.PackageState and
-     * {@link FrameworkPackageUserState}.
-     *
-     * @see ActivityInfo
-     * @see PackageInfo#activities
-     */
-    @NonNull
-    List<ParsedActivity> getActivities();
-
-    /**
-     * @see PackageInfo#baseRevisionCode
-     */
-    int getBaseRevisionCode();
-
-    /**
-     * @see PackageInfo#compileSdkVersion
-     * @see R.styleable#AndroidManifest_compileSdkVersion
-     */
-    int getCompileSdkVersion();
-
-    /**
-     * @see ApplicationInfo#compileSdkVersionCodename
-     * @see R.styleable#AndroidManifest_compileSdkVersionCodename
-     */
-    @Nullable
-    String getCompileSdkVersionCodeName();
-
-    /**
-     * @see PackageInfo#configPreferences
-     * @see R.styleable#AndroidManifestUsesConfiguration
-     */
-    @NonNull
-    List<ConfigurationInfo> getConfigPreferences();
-
-    /**
-     * @see PackageInfo#featureGroups
-     * @see R.styleable#AndroidManifestUsesFeature
-     */
-    @NonNull
-    List<FeatureGroupInfo> getFeatureGroups();
-
-    /**
-     * @see InstrumentationInfo
-     * @see PackageInfo#instrumentation
-     */
-    @NonNull
-    List<ParsedInstrumentation> getInstrumentations();
-
-    /**
-     * @see PackageInfo#getLongVersionCode()
-     */
-    long getLongVersionCode();
-
-    /**
-     * @see PackageInfo#packageName
-     */
-    String getPackageName();
-
-    /**
-     * @see PermissionInfo
-     * @see PackageInfo#permissions
-     */
-    @NonNull
-    List<ParsedPermission> getPermissions();
-
-    /**
-     * Set of {@link android.content.ContentProvider ContentProviders} parsed from the manifest.
-     *
-     * This contains minimal system state and does not
-     * provide the same information as {@link ProviderInfo}. Effective state can be queried through
-     * {@link android.content.pm.PackageManager#getProviderInfo(ComponentName, int)} or by
-     * combining state from from com.android.server.pm.pkg.PackageState and
-     * {@link FrameworkPackageUserState}.
-     *
-     * @see ProviderInfo
-     * @see PackageInfo#providers
-     */
-    @NonNull
-    List<ParsedProvider> getProviders();
-
-    /**
-     * Set of {@link android.content.BroadcastReceiver BroadcastReceivers} parsed from the manifest.
-     *
-     * This contains minimal system state and does not
-     * provide the same information as {@link ActivityInfo}. Effective state can be queried through
-     * {@link android.content.pm.PackageManager#getReceiverInfo(ComponentName, int)} or by
-     * combining state from from com.android.server.pm.pkg.PackageState and
-     * {@link FrameworkPackageUserState}.
-     *
-     * Since they share several attributes, receivers are parsed as {@link ParsedActivity}, even
-     * though they represent different functionality.
-     *
-     * TODO(b/135203078): Reconsider this and maybe make ParsedReceiver so it's not so confusing
-     *
-     * @see ActivityInfo
-     * @see PackageInfo#receivers
-     */
-    @NonNull
-    List<ParsedActivity> getReceivers();
-
-    /**
-     * @see PackageInfo#reqFeatures
-     * @see R.styleable#AndroidManifestUsesFeature
-     */
-    @NonNull
-    List<FeatureInfo> getRequestedFeatures();
-
-    /**
-     * All the permissions declared. This is an effective set, and may include permissions
-     * transformed from split/migrated permissions from previous versions, so may not be exactly
-     * what the package declares in its manifest.
-     *
-     * @see PackageInfo#requestedPermissions
-     * @see R.styleable#AndroidManifestUsesPermission
-     */
-    @NonNull
-    List<String> getRequestedPermissions();
-
-    /**
-     * @see PackageInfo#requiredAccountType
-     * @see R.styleable#AndroidManifestApplication_requiredAccountType
-     */
-    @Nullable
-    String getRequiredAccountType();
-
-    /**
-     * The restricted account authenticator type that is used by this application.
-     *
-     * @see PackageInfo#restrictedAccountType
-     * @see R.styleable#AndroidManifestApplication_restrictedAccountType
-     */
-    @Nullable
-    String getRestrictedAccountType();
-
-    /**
-     * Set of {@link android.app.Service Services} parsed from the manifest.
-     *
-     * This contains minimal system state and does not
-     * provide the same information as {@link ServiceInfo}. Effective state can be queried through
-     * {@link android.content.pm.PackageManager#getServiceInfo(ComponentName, int)} or by
-     * combining state from from com.android.server.pm.pkg.PackageState and
-     * {@link FrameworkPackageUserState}.
-     *
-     * @see ServiceInfo
-     * @see PackageInfo#services
-     */
-    @NonNull
-    List<ParsedService> getServices();
-
-    /**
-     * @see PackageInfo#sharedUserId
-     * @see R.styleable#AndroidManifest_sharedUserId
-     */
-    @Nullable
-    String getSharedUserId();
-
-    /**
-     * @see PackageInfo#sharedUserLabel
-     * @see R.styleable#AndroidManifest_sharedUserLabel
-     */
-    int getSharedUserLabel();
-
-    /**
-     * TODO(b/135203078): Move split stuff to an inner data class
-     *
-     * @see ApplicationInfo#splitNames
-     * @see PackageInfo#splitNames
-     */
-    @Nullable
-    String[] getSplitNames();
-
-    /**
-     * @see PackageInfo#splitRevisionCodes
-     */
-    int[] getSplitRevisionCodes();
-
-    /**
-     * @see PackageInfo#versionName
-     */
-    @Nullable
-    String getVersionName();
-
-    /**
-     * @see PackageInfo#requiredForAllUsers
-     * @see R.styleable#AndroidManifestApplication_requiredForAllUsers
-     */
-    boolean isRequiredForAllUsers();
-}
diff --git a/core/java/android/content/pm/parsing/component/ComponentMutateUtils.java b/core/java/android/content/pm/parsing/component/ComponentMutateUtils.java
deleted file mode 100644
index 73d0b9f..0000000
--- a/core/java/android/content/pm/parsing/component/ComponentMutateUtils.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually
- * be removed once all post-parsing mutation is moved to parsing.
- *
- * @hide
- */
-public class ComponentMutateUtils {
-
-    public static void setMaxAspectRatio(@NonNull ParsedActivity activity, int resizeMode,
-            float maxAspectRatio) {
-        ((ParsedActivityImpl) activity).setMaxAspectRatio(resizeMode, maxAspectRatio);
-    }
-
-    public static void setMinAspectRatio(@NonNull ParsedActivity activity, int resizeMode,
-            float minAspectRatio) {
-        ((ParsedActivityImpl) activity).setMinAspectRatio(resizeMode, minAspectRatio);
-    }
-
-    public static void setSupportsSizeChanges(@NonNull ParsedActivity activity,
-            boolean supportsSizeChanges) {
-        ((ParsedActivityImpl) activity).setSupportsSizeChanges(supportsSizeChanges);
-    }
-
-    public static void setResizeMode(@NonNull ParsedActivity activity, int resizeMode) {
-        ((ParsedActivityImpl) activity).setResizeMode(resizeMode);
-    }
-
-    public static void setExactFlags(ParsedComponent component, int exactFlags) {
-        ((ParsedComponentImpl) component).setFlags(exactFlags);
-    }
-
-    public static void setEnabled(@NonNull ParsedMainComponent component, boolean enabled) {
-        ((ParsedMainComponentImpl) component).setEnabled(enabled);
-    }
-
-    public static void setPackageName(@NonNull ParsedComponent component,
-            @NonNull String packageName) {
-        ((ParsedComponentImpl) component).setPackageName(packageName);
-    }
-
-    public static void setDirectBootAware(@NonNull ParsedMainComponent component,
-            boolean directBootAware) {
-        ((ParsedMainComponentImpl) component).setDirectBootAware(directBootAware);
-    }
-
-    public static void setExported(@NonNull ParsedMainComponent component, boolean exported) {
-        ((ParsedMainComponentImpl) component).setExported(exported);
-    }
-
-    public static void setAuthority(@NonNull ParsedProvider provider, @Nullable String authority) {
-        ((ParsedProviderImpl) provider).setAuthority(authority);
-    }
-
-    public static void setSyncable(@NonNull ParsedProvider provider, boolean syncable) {
-        ((ParsedProviderImpl) provider).setSyncable(syncable);
-    }
-
-    public static void setProtectionLevel(@NonNull ParsedPermission permission,
-            int protectionLevel) {
-        ((ParsedPermissionImpl) permission).setProtectionLevel(protectionLevel);
-    }
-
-    public static void setParsedPermissionGroup(@NonNull ParsedPermission permission,
-            @NonNull ParsedPermissionGroup permissionGroup) {
-        ((ParsedPermissionImpl) permission).setParsedPermissionGroup(permissionGroup);
-    }
-
-    public static void setPriority(@NonNull ParsedPermissionGroup parsedPermissionGroup,
-            int priority) {
-        ((ParsedPermissionGroupImpl) parsedPermissionGroup).setPriority(priority);
-    }
-
-    public static void addStateFrom(@NonNull ParsedProcess oldProcess,
-            @NonNull ParsedProcess newProcess) {
-        ((ParsedProcessImpl) oldProcess).addStateFrom(newProcess);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
deleted file mode 100644
index 0334601..0000000
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageUtils.validateName;
-
-import android.annotation.AttrRes;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.pkg.FrameworkPackageUserState;
-import android.content.pm.pkg.PackageUserStateUtils;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.text.TextUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * @hide
- */
-public class ComponentParseUtils {
-
-    public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
-        IntentFilter intentFilter = intentInfo.getIntentFilter();
-        return intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE)
-                || intentFilter.hasAction(Intent.ACTION_SEND)
-                || intentFilter.hasAction(Intent.ACTION_SENDTO)
-                || intentFilter.hasAction(Intent.ACTION_SEND_MULTIPLE);
-    }
-
-    static <Component extends ParsedComponentImpl> ParseResult<Component> parseAllMetaData(
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
-            Component component, ParseInput input) throws XmlPullParserException, IOException {
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final ParseResult result;
-            if ("meta-data".equals(parser.getName())) {
-                result = ParsedComponentUtils.addMetaData(component, pkg, res, parser, input);
-            } else {
-                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        return input.success(component);
-    }
-
-    @NonNull
-    public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
-            CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
-        if ((flags & ParsingPackageUtils.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
-                procSeq)) {
-            return input.success(defProc != null ? defProc : pkg);
-        }
-        if (separateProcesses != null) {
-            for (int i = separateProcesses.length - 1; i >= 0; i--) {
-                String sp = separateProcesses[i];
-                if (sp.equals(pkg) || sp.equals(defProc) || sp.contentEquals(procSeq)) {
-                    return input.success(pkg);
-                }
-            }
-        }
-        if (procSeq == null || procSeq.length() <= 0) {
-            return input.success(defProc);
-        }
-
-        ParseResult<String> nameResult = ComponentParseUtils.buildCompoundName(pkg, procSeq,
-                "process", input);
-        return input.success(TextUtils.safeIntern(nameResult.getResult()));
-    }
-
-    @NonNull
-    public static ParseResult<String> buildTaskAffinityName(String pkg, String defProc,
-            CharSequence procSeq, ParseInput input) {
-        if (procSeq == null) {
-            return input.success(defProc);
-        }
-        if (procSeq.length() <= 0) {
-            return input.success(null);
-        }
-        return buildCompoundName(pkg, procSeq, "taskAffinity", input);
-    }
-
-    public static ParseResult<String> buildCompoundName(String pkg, CharSequence procSeq,
-            String type, ParseInput input) {
-        String proc = procSeq.toString();
-        char c = proc.charAt(0);
-        if (pkg != null && c == ':') {
-            if (proc.length() < 2) {
-                return input.error("Bad " + type + " name " + proc + " in package " + pkg
-                        + ": must be at least two characters");
-            }
-            String subName = proc.substring(1);
-            final ParseResult<?> nameResult = validateName(input, subName, false, false);
-            if (nameResult.isError()) {
-                return input.error("Invalid " + type + " name " + proc + " in package " + pkg
-                        + ": " + nameResult.getErrorMessage());
-            }
-            return input.success(pkg + proc);
-        }
-        if (!"system".equals(proc)) {
-            final ParseResult<?> nameResult = validateName(input, proc, true, false);
-            if (nameResult.isError()) {
-                return input.error("Invalid " + type + " name " + proc + " in package " + pkg
-                        + ": " + nameResult.getErrorMessage());
-            }
-        }
-        return input.success(proc);
-    }
-
-    public static int flag(int flag, @AttrRes int attribute, TypedArray typedArray) {
-        return typedArray.getBoolean(attribute, false) ? flag : 0;
-    }
-
-    public static int flag(int flag, @AttrRes int attribute, boolean defaultValue,
-            TypedArray typedArray) {
-        return typedArray.getBoolean(attribute, defaultValue) ? flag : 0;
-    }
-
-    /**
-     * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
-     */
-    @Nullable
-    public static CharSequence getNonLocalizedLabel(
-            ParsedComponent component) {
-        return component.getNonLocalizedLabel();
-    }
-
-    /**
-     * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
-     * <p>
-     * This is a method of the utility class to discourage use.
-     */
-    public static int getIcon(ParsedComponent component) {
-        return component.getIcon();
-    }
-
-    public static boolean isMatch(FrameworkPackageUserState state, boolean isSystem,
-            boolean isPackageEnabled, ParsedMainComponent component, long flags) {
-        return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled,
-                component.isEnabled(), component.isDirectBootAware(), component.getName(), flags);
-    }
-
-    public static boolean isEnabled(FrameworkPackageUserState state, boolean isPackageEnabled,
-            ParsedMainComponent parsedComponent, long flags) {
-        return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
-                parsedComponent.getName(), flags);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
deleted file mode 100644
index a661b51..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ActivityInfo;
-
-/** @hide **/
-public interface ParsedActivity extends ParsedMainComponent {
-
-    /**
-     * Generate activity object that forwards user to App Details page automatically.
-     * This activity should be invisible to user and user should not know or see it.
-     */
-    @NonNull
-    static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
-            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
-        // Proxy method since ParsedActivityImpl is supposed to be package visibility
-        return ParsedActivityImpl.makeAppDetailsActivity(packageName, processName, uiOptions,
-                taskAffinity, hardwareAccelerated);
-    }
-
-    int getColorMode();
-
-    int getConfigChanges();
-
-    int getDocumentLaunchMode();
-
-    int getLaunchMode();
-
-    int getLockTaskLaunchMode();
-
-    int getMaxRecents();
-
-    float getMaxAspectRatio();
-
-    float getMinAspectRatio();
-
-    @Nullable
-    String getParentActivityName();
-
-    @Nullable
-    String getPermission();
-
-    int getPersistableMode();
-
-    int getPrivateFlags();
-
-    @Nullable
-    String getRequestedVrComponent();
-
-    int getRotationAnimation();
-
-    int getResizeMode();
-
-    int getScreenOrientation();
-
-    int getSoftInputMode();
-
-    @Nullable
-    String getTargetActivity();
-
-    @Nullable
-    String getTaskAffinity();
-
-    int getTheme();
-
-    int getUiOptions();
-
-    @Nullable
-    ActivityInfo.WindowLayout getWindowLayout();
-
-    boolean isSupportsSizeChanges();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java b/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
deleted file mode 100644
index 93dc5a4..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
+++ /dev/null
@@ -1,655 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.parsing.ParsingUtils;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-/** @hide **/
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity {
-
-    private int theme;
-    private int uiOptions;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String targetActivity;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String parentActivityName;
-    @Nullable
-    private String taskAffinity;
-    private int privateFlags;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String permission;
-
-    private int launchMode;
-    private int documentLaunchMode;
-    private int maxRecents;
-    private int configChanges;
-    private int softInputMode;
-    private int persistableMode;
-    private int lockTaskLaunchMode;
-
-    private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-    private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
-
-    private float maxAspectRatio = ParsingUtils.NOT_SET;
-    private float minAspectRatio = ParsingUtils.NOT_SET;
-
-    private boolean supportsSizeChanges;
-
-    @Nullable
-    private String requestedVrComponent;
-    private int rotationAnimation = -1;
-    private int colorMode;
-
-    @Nullable
-    private ActivityInfo.WindowLayout windowLayout;
-
-    public ParsedActivityImpl(ParsedActivityImpl other) {
-        super(other);
-        this.theme = other.theme;
-        this.uiOptions = other.uiOptions;
-        this.targetActivity = other.targetActivity;
-        this.parentActivityName = other.parentActivityName;
-        this.taskAffinity = other.taskAffinity;
-        this.privateFlags = other.privateFlags;
-        this.permission = other.permission;
-        this.launchMode = other.launchMode;
-        this.documentLaunchMode = other.documentLaunchMode;
-        this.maxRecents = other.maxRecents;
-        this.configChanges = other.configChanges;
-        this.softInputMode = other.softInputMode;
-        this.persistableMode = other.persistableMode;
-        this.lockTaskLaunchMode = other.lockTaskLaunchMode;
-        this.screenOrientation = other.screenOrientation;
-        this.resizeMode = other.resizeMode;
-        this.maxAspectRatio = other.maxAspectRatio;
-        this.minAspectRatio = other.minAspectRatio;
-        this.supportsSizeChanges = other.supportsSizeChanges;
-        this.requestedVrComponent = other.requestedVrComponent;
-        this.rotationAnimation = other.rotationAnimation;
-        this.colorMode = other.colorMode;
-        this.windowLayout = other.windowLayout;
-    }
-
-    /**
-     * Generate activity object that forwards user to App Details page automatically.
-     * This activity should be invisible to user and user should not know or see it.
-     */
-    @NonNull
-    static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
-            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
-        ParsedActivityImpl activity = new ParsedActivityImpl();
-        activity.setPackageName(packageName);
-        activity.theme = android.R.style.Theme_NoDisplay;
-        activity.setExported(true);
-        activity.setName(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
-        activity.setProcessName(processName);
-        activity.uiOptions = uiOptions;
-        activity.taskAffinity = taskAffinity;
-        activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-        activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
-        activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
-        activity.configChanges = ParsedActivityUtils.getActivityConfigChanges(0, 0);
-        activity.softInputMode = 0;
-        activity.persistableMode = ActivityInfo.PERSIST_NEVER;
-        activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-        activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
-        activity.lockTaskLaunchMode = 0;
-        activity.setDirectBootAware(false);
-        activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
-        activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
-        if (hardwareAccelerated) {
-            activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_HARDWARE_ACCELERATED);
-        }
-        return activity;
-    }
-
-    @NonNull
-    static ParsedActivityImpl makeAlias(String targetActivityName, ParsedActivity target) {
-        ParsedActivityImpl alias = new ParsedActivityImpl();
-        alias.setPackageName(target.getPackageName());
-        alias.setTargetActivity(targetActivityName);
-        alias.configChanges = target.getConfigChanges();
-        alias.setFlags(target.getFlags());
-        alias.privateFlags = target.getPrivateFlags();
-        alias.setIcon(target.getIcon());
-        alias.setLogo(target.getLogo());
-        alias.setBanner(target.getBanner());
-        alias.setLabelRes(target.getLabelRes());
-        alias.setNonLocalizedLabel(target.getNonLocalizedLabel());
-        alias.launchMode = target.getLaunchMode();
-        alias.lockTaskLaunchMode = target.getLockTaskLaunchMode();
-        alias.documentLaunchMode = target.getDocumentLaunchMode();
-        alias.setDescriptionRes(target.getDescriptionRes());
-        alias.screenOrientation = target.getScreenOrientation();
-        alias.taskAffinity = target.getTaskAffinity();
-        alias.theme = target.getTheme();
-        alias.softInputMode = target.getSoftInputMode();
-        alias.uiOptions = target.getUiOptions();
-        alias.parentActivityName = target.getParentActivityName();
-        alias.maxRecents = target.getMaxRecents();
-        alias.windowLayout = target.getWindowLayout();
-        alias.resizeMode = target.getResizeMode();
-        alias.maxAspectRatio = target.getMaxAspectRatio();
-        alias.minAspectRatio = target.getMinAspectRatio();
-        alias.supportsSizeChanges = target.isSupportsSizeChanges();
-        alias.requestedVrComponent = target.getRequestedVrComponent();
-        alias.setDirectBootAware(target.isDirectBootAware());
-        alias.setProcessName(target.getProcessName());
-        return alias;
-
-        // Not all attributes from the target ParsedActivity are copied to the alias.
-        // Careful when adding an attribute and determine whether or not it should be copied.
-//        alias.enabled = target.enabled;
-//        alias.exported = target.exported;
-//        alias.permission = target.permission;
-//        alias.splitName = target.splitName;
-//        alias.persistableMode = target.persistableMode;
-//        alias.rotationAnimation = target.rotationAnimation;
-//        alias.colorMode = target.colorMode;
-//        alias.intents.addAll(target.intents);
-//        alias.order = target.order;
-//        alias.metaData = target.metaData;
-    }
-
-    public ParsedActivityImpl setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
-        if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
-                || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
-            // Resizeable activities can be put in any aspect ratio.
-            return this;
-        }
-
-        if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
-            // Ignore any value lesser than 1.0.
-            return this;
-        }
-
-        this.maxAspectRatio = maxAspectRatio;
-        return this;
-    }
-
-    public ParsedActivityImpl setMinAspectRatio(int resizeMode, float minAspectRatio) {
-        if (resizeMode == RESIZE_MODE_RESIZEABLE
-                || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
-            // Resizeable activities can be put in any aspect ratio.
-            return this;
-        }
-
-        if (minAspectRatio < 1.0f && minAspectRatio != 0) {
-            // Ignore any value lesser than 1.0.
-            return this;
-        }
-
-        this.minAspectRatio = minAspectRatio;
-        return this;
-    }
-
-    public ParsedActivityImpl setTargetActivity(String targetActivity) {
-        this.targetActivity = TextUtils.safeIntern(targetActivity);
-        return this;
-    }
-
-    public ParsedActivityImpl setPermission(String permission) {
-        // Empty string must be converted to null
-        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Activity{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(this.theme);
-        dest.writeInt(this.uiOptions);
-        dest.writeString(this.targetActivity);
-        dest.writeString(this.parentActivityName);
-        dest.writeString(this.taskAffinity);
-        dest.writeInt(this.privateFlags);
-        sForInternedString.parcel(this.permission, dest, flags);
-        dest.writeInt(this.launchMode);
-        dest.writeInt(this.documentLaunchMode);
-        dest.writeInt(this.maxRecents);
-        dest.writeInt(this.configChanges);
-        dest.writeInt(this.softInputMode);
-        dest.writeInt(this.persistableMode);
-        dest.writeInt(this.lockTaskLaunchMode);
-        dest.writeInt(this.screenOrientation);
-        dest.writeInt(this.resizeMode);
-        dest.writeValue(this.maxAspectRatio);
-        dest.writeValue(this.minAspectRatio);
-        dest.writeBoolean(this.supportsSizeChanges);
-        dest.writeString(this.requestedVrComponent);
-        dest.writeInt(this.rotationAnimation);
-        dest.writeInt(this.colorMode);
-        dest.writeBundle(this.getMetaData());
-
-        if (windowLayout != null) {
-            dest.writeInt(1);
-            windowLayout.writeToParcel(dest);
-        } else {
-            dest.writeBoolean(false);
-        }
-    }
-
-    public ParsedActivityImpl() {
-    }
-
-    protected ParsedActivityImpl(Parcel in) {
-        super(in);
-        this.theme = in.readInt();
-        this.uiOptions = in.readInt();
-        this.targetActivity = in.readString();
-        this.parentActivityName = in.readString();
-        this.taskAffinity = in.readString();
-        this.privateFlags = in.readInt();
-        this.permission = sForInternedString.unparcel(in);
-        this.launchMode = in.readInt();
-        this.documentLaunchMode = in.readInt();
-        this.maxRecents = in.readInt();
-        this.configChanges = in.readInt();
-        this.softInputMode = in.readInt();
-        this.persistableMode = in.readInt();
-        this.lockTaskLaunchMode = in.readInt();
-        this.screenOrientation = in.readInt();
-        this.resizeMode = in.readInt();
-        this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
-        this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
-        this.supportsSizeChanges = in.readBoolean();
-        this.requestedVrComponent = in.readString();
-        this.rotationAnimation = in.readInt();
-        this.colorMode = in.readInt();
-        this.setMetaData(in.readBundle());
-        if (in.readBoolean()) {
-            windowLayout = new ActivityInfo.WindowLayout(in);
-        }
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedActivityImpl> CREATOR =
-            new Parcelable.Creator<ParsedActivityImpl>() {
-        @Override
-        public ParsedActivityImpl createFromParcel(Parcel source) {
-            return new ParsedActivityImpl(source);
-        }
-
-        @Override
-        public ParsedActivityImpl[] newArray(int size) {
-            return new ParsedActivityImpl[size];
-        }
-    };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedActivityImpl(
-            int theme,
-            int uiOptions,
-            @Nullable String targetActivity,
-            @Nullable String parentActivityName,
-            @Nullable String taskAffinity,
-            int privateFlags,
-            @Nullable String permission,
-            int launchMode,
-            int documentLaunchMode,
-            int maxRecents,
-            int configChanges,
-            int softInputMode,
-            int persistableMode,
-            int lockTaskLaunchMode,
-            int screenOrientation,
-            int resizeMode,
-            float maxAspectRatio,
-            float minAspectRatio,
-            boolean supportsSizeChanges,
-            @Nullable String requestedVrComponent,
-            int rotationAnimation,
-            int colorMode,
-            @Nullable ActivityInfo.WindowLayout windowLayout) {
-        this.theme = theme;
-        this.uiOptions = uiOptions;
-        this.targetActivity = targetActivity;
-        this.parentActivityName = parentActivityName;
-        this.taskAffinity = taskAffinity;
-        this.privateFlags = privateFlags;
-        this.permission = permission;
-        this.launchMode = launchMode;
-        this.documentLaunchMode = documentLaunchMode;
-        this.maxRecents = maxRecents;
-        this.configChanges = configChanges;
-        this.softInputMode = softInputMode;
-        this.persistableMode = persistableMode;
-        this.lockTaskLaunchMode = lockTaskLaunchMode;
-        this.screenOrientation = screenOrientation;
-        this.resizeMode = resizeMode;
-        this.maxAspectRatio = maxAspectRatio;
-        this.minAspectRatio = minAspectRatio;
-        this.supportsSizeChanges = supportsSizeChanges;
-        this.requestedVrComponent = requestedVrComponent;
-        this.rotationAnimation = rotationAnimation;
-        this.colorMode = colorMode;
-        this.windowLayout = windowLayout;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public int getTheme() {
-        return theme;
-    }
-
-    @DataClass.Generated.Member
-    public int getUiOptions() {
-        return uiOptions;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getTargetActivity() {
-        return targetActivity;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getParentActivityName() {
-        return parentActivityName;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getTaskAffinity() {
-        return taskAffinity;
-    }
-
-    @DataClass.Generated.Member
-    public int getPrivateFlags() {
-        return privateFlags;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getPermission() {
-        return permission;
-    }
-
-    @DataClass.Generated.Member
-    public int getLaunchMode() {
-        return launchMode;
-    }
-
-    @DataClass.Generated.Member
-    public int getDocumentLaunchMode() {
-        return documentLaunchMode;
-    }
-
-    @DataClass.Generated.Member
-    public int getMaxRecents() {
-        return maxRecents;
-    }
-
-    @DataClass.Generated.Member
-    public int getConfigChanges() {
-        return configChanges;
-    }
-
-    @DataClass.Generated.Member
-    public int getSoftInputMode() {
-        return softInputMode;
-    }
-
-    @DataClass.Generated.Member
-    public int getPersistableMode() {
-        return persistableMode;
-    }
-
-    @DataClass.Generated.Member
-    public int getLockTaskLaunchMode() {
-        return lockTaskLaunchMode;
-    }
-
-    @DataClass.Generated.Member
-    public int getScreenOrientation() {
-        return screenOrientation;
-    }
-
-    @DataClass.Generated.Member
-    public int getResizeMode() {
-        return resizeMode;
-    }
-
-    @DataClass.Generated.Member
-    public float getMaxAspectRatio() {
-        return maxAspectRatio;
-    }
-
-    @DataClass.Generated.Member
-    public float getMinAspectRatio() {
-        return minAspectRatio;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isSupportsSizeChanges() {
-        return supportsSizeChanges;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getRequestedVrComponent() {
-        return requestedVrComponent;
-    }
-
-    @DataClass.Generated.Member
-    public int getRotationAnimation() {
-        return rotationAnimation;
-    }
-
-    @DataClass.Generated.Member
-    public int getColorMode() {
-        return colorMode;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable ActivityInfo.WindowLayout getWindowLayout() {
-        return windowLayout;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setTheme( int value) {
-        theme = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setUiOptions( int value) {
-        uiOptions = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setParentActivityName(@NonNull String value) {
-        parentActivityName = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setTaskAffinity(@NonNull String value) {
-        taskAffinity = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setPrivateFlags( int value) {
-        privateFlags = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setLaunchMode( int value) {
-        launchMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setDocumentLaunchMode( int value) {
-        documentLaunchMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setMaxRecents( int value) {
-        maxRecents = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setConfigChanges( int value) {
-        configChanges = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setSoftInputMode( int value) {
-        softInputMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setPersistableMode( int value) {
-        persistableMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setLockTaskLaunchMode( int value) {
-        lockTaskLaunchMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setScreenOrientation( int value) {
-        screenOrientation = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setResizeMode( int value) {
-        resizeMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setMaxAspectRatio( float value) {
-        maxAspectRatio = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setMinAspectRatio( float value) {
-        minAspectRatio = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setSupportsSizeChanges( boolean value) {
-        supportsSizeChanges = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setRequestedVrComponent(@NonNull String value) {
-        requestedVrComponent = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setRotationAnimation( int value) {
-        rotationAnimation = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setColorMode( int value) {
-        colorMode = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setWindowLayout(@NonNull ActivityInfo.WindowLayout value) {
-        windowLayout = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1630600615936L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java",
-            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
deleted file mode 100644
index 2ddf923..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.parsing.ParsingUtils.NOT_SET;
-import static android.content.pm.parsing.component.ComponentParseUtils.flag;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseInput.DeferredError;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.WindowManager;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/** @hide */
-public class ParsedActivityUtils {
-
-    private static final String TAG = ParsingUtils.TAG;
-
-    public static final boolean LOG_UNSAFE_BROADCASTS = false;
-
-    // Set of broadcast actions that are safe for manifest receivers
-    public static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
-    static {
-        SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
-    }
-
-    /**
-     * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
-     */
-    private static final int RECREATE_ON_CONFIG_CHANGES_MASK =
-            ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
-
-    @NonNull
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            boolean useRoundIcon, @Nullable String defaultSplitName, ParseInput input)
-            throws XmlPullParserException, IOException {
-        final String packageName = pkg.getPackageName();
-        final ParsedActivityImpl activity = new ParsedActivityImpl();
-
-        boolean receiver = "receiver".equals(parser.getName());
-        String tag = "<" + parser.getName() + ">";
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
-        try {
-            ParseResult<ParsedActivityImpl> result =
-                    ParsedMainComponentUtils.parseMainComponent(activity, tag, separateProcesses,
-                            pkg, sa, flags, useRoundIcon, defaultSplitName, input,
-                            R.styleable.AndroidManifestActivity_banner,
-                            R.styleable.AndroidManifestActivity_description,
-                            R.styleable.AndroidManifestActivity_directBootAware,
-                            R.styleable.AndroidManifestActivity_enabled,
-                            R.styleable.AndroidManifestActivity_icon,
-                            R.styleable.AndroidManifestActivity_label,
-                            R.styleable.AndroidManifestActivity_logo,
-                            R.styleable.AndroidManifestActivity_name,
-                            R.styleable.AndroidManifestActivity_process,
-                            R.styleable.AndroidManifestActivity_roundIcon,
-                            R.styleable.AndroidManifestActivity_splitName,
-                            R.styleable.AndroidManifestActivity_attributionTags);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            if (receiver && pkg.isCantSaveState()) {
-                // A heavy-weight application can not have receivers in its main process
-                if (Objects.equals(activity.getProcessName(), packageName)) {
-                    return input.error("Heavy-weight applications can not have receivers "
-                            + "in main process");
-                }
-            }
-
-            // The following section has formatting off to make it easier to read the flags.
-            // Multi-lining them to fit within the column restriction makes it hard to tell what
-            // field is assigned where.
-            // @formatter:off
-            activity.setTheme(sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0))
-                    .setUiOptions(sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions()));
-
-            activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
-                                | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
-                                | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
-                                | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
-                                | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
-                                | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
-                                | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
-                                | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
-                                | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
-                                | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
-                                | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
-                                | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
-                                | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa)));
-
-            if (!receiver) {
-                activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
-                                        | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
-                                        | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
-                                        | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
-                                        | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
-                                        | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
-                                        | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
-                                        | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
-                                        | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
-                                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
-
-                activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
-                                        R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
-                                        | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND,
-                                        R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa)));
-
-                activity.setColorMode(sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT))
-                        .setDocumentLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE))
-                        .setLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE))
-                        .setLockTaskLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0))
-                        .setMaxRecents(sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic()))
-                        .setPersistableMode(sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY))
-                        .setRequestedVrComponent(sa.getString(R.styleable.AndroidManifestActivity_enableVrMode))
-                        .setRotationAnimation(sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED))
-                        .setSoftInputMode(sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0))
-                        .setConfigChanges(getActivityConfigChanges(
-                                sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
-                                sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0))
-                        );
-
-                int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
-                int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
-                activity.setScreenOrientation(screenOrientation)
-                        .setResizeMode(resizeMode);
-
-                if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
-                        && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
-                        == TypedValue.TYPE_FLOAT) {
-                    activity.setMaxAspectRatio(resizeMode,
-                            sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
-                                    0 /*default*/));
-                }
-
-                if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
-                        && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
-                        == TypedValue.TYPE_FLOAT) {
-                    activity.setMinAspectRatio(resizeMode,
-                            sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
-                                    0 /*default*/));
-                }
-            } else {
-                activity.setLaunchMode(ActivityInfo.LAUNCH_MULTIPLE)
-                        .setConfigChanges(0)
-                        .setFlags(activity.getFlags()|flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa));
-            }
-            // @formatter:on
-
-            String taskAffinity = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestActivity_taskAffinity,
-                    Configuration.NATIVE_CONFIG_VERSION);
-
-            ParseResult<String> affinityNameResult = ComponentParseUtils.buildTaskAffinityName(
-                    packageName, pkg.getTaskAffinity(), taskAffinity, input);
-            if (affinityNameResult.isError()) {
-                return input.error(affinityNameResult);
-            }
-
-            activity.setTaskAffinity(affinityNameResult.getResult());
-
-            boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
-            if (visibleToEphemeral) {
-                activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
-                pkg.setVisibleToInstantApps(true);
-            }
-
-            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
-                    false /*isAlias*/, visibleToEphemeral, input,
-                    R.styleable.AndroidManifestActivity_parentActivityName,
-                    R.styleable.AndroidManifestActivity_permission,
-                    R.styleable.AndroidManifestActivity_exported
-            );
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, boolean useRoundIcon, @Nullable String defaultSplitName,
-            @NonNull ParseInput input) throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivityAlias);
-        try {
-            String targetActivity = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestActivityAlias_targetActivity,
-                    Configuration.NATIVE_CONFIG_VERSION);
-            if (targetActivity == null) {
-                return input.error("<activity-alias> does not specify android:targetActivity");
-            }
-
-            String packageName = pkg.getPackageName();
-            targetActivity = ParsingUtils.buildClassName(packageName, targetActivity);
-            if (targetActivity == null) {
-                return input.error("Empty class name in package " + packageName);
-            }
-
-            ParsedActivity target = null;
-
-            List<ParsedActivity> activities = pkg.getActivities();
-            final int activitiesSize = ArrayUtils.size(activities);
-            for (int i = 0; i < activitiesSize; i++) {
-                ParsedActivity t = activities.get(i);
-                if (targetActivity.equals(t.getName())) {
-                    target = t;
-                    break;
-                }
-            }
-
-            if (target == null) {
-                return input.error("<activity-alias> target activity " + targetActivity
-                        + " not found in manifest with activities = "
-                        + pkg.getActivities()
-                        + ", parsedActivities = " + activities);
-            }
-
-            ParsedActivityImpl activity = ParsedActivityImpl.makeAlias(targetActivity, target);
-            String tag = "<" + parser.getName() + ">";
-
-            ParseResult<ParsedActivityImpl> result = ParsedMainComponentUtils.parseMainComponent(
-                    activity, tag, null, pkg, sa, 0, useRoundIcon, defaultSplitName, input,
-                    R.styleable.AndroidManifestActivityAlias_banner,
-                    R.styleable.AndroidManifestActivityAlias_description,
-                    NOT_SET /*directBootAwareAttr*/,
-                    R.styleable.AndroidManifestActivityAlias_enabled,
-                    R.styleable.AndroidManifestActivityAlias_icon,
-                    R.styleable.AndroidManifestActivityAlias_label,
-                    R.styleable.AndroidManifestActivityAlias_logo,
-                    R.styleable.AndroidManifestActivityAlias_name,
-                    NOT_SET /*processAttr*/,
-                    R.styleable.AndroidManifestActivityAlias_roundIcon,
-                    NOT_SET /*splitNameAttr*/,
-                    R.styleable.AndroidManifestActivityAlias_attributionTags);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            // TODO add visibleToInstantApps attribute to activity alias
-            final boolean visibleToEphemeral =
-                    ((activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
-
-            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, false /*isReceiver*/, true /*isAlias*/,
-                    visibleToEphemeral, input,
-                    R.styleable.AndroidManifestActivityAlias_parentActivityName,
-                    R.styleable.AndroidManifestActivityAlias_permission,
-                    R.styleable.AndroidManifestActivityAlias_exported);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    /**
-     * This method shares parsing logic between Activity/Receiver/alias instances, but requires
-     * passing in booleans for isReceiver/isAlias, since there's no indicator in the other
-     * parameters.
-     *
-     * They're used to filter the parsed tags and their behavior. This makes the method rather
-     * messy, but it's more maintainable than writing 3 separate methods for essentially the same
-     * type of logic.
-     */
-    @NonNull
-    private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivityImpl activity,
-            ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
-            TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
-            ParseInput input, int parentActivityNameAttr, int permissionAttr,
-            int exportedAttr) throws IOException, XmlPullParserException {
-        String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
-        if (parentActivityName != null) {
-            String packageName = pkg.getPackageName();
-            String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);
-            if (parentClassName == null) {
-                Log.e(TAG, "Activity " + activity.getName()
-                        + " specified invalid parentActivityName " + parentActivityName);
-            } else {
-                activity.setParentActivityName(parentClassName);
-            }
-        }
-
-        String permission = array.getNonConfigurationString(permissionAttr, 0);
-        if (isAlias) {
-            // An alias will override permissions to allow referencing an Activity through its alias
-            // without needing the original permission. If an alias needs the same permission,
-            // it must be re-declared.
-            activity.setPermission(permission);
-        } else {
-            activity.setPermission(permission != null ? permission : pkg.getPermission());
-        }
-
-        final boolean setExported = array.hasValue(exportedAttr);
-        if (setExported) {
-            activity.setExported(array.getBoolean(exportedAttr, false));
-        }
-
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final ParseResult result;
-            if (parser.getName().equals("intent-filter")) {
-                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
-                        !isReceiver, visibleToEphemeral, resources, parser, input);
-                if (intentResult.isSuccess()) {
-                    ParsedIntentInfo intentInfo = intentResult.getResult();
-                    if (intentInfo != null) {
-                        IntentFilter intentFilter = intentInfo.getIntentFilter();
-                        activity.setOrder(Math.max(intentFilter.getOrder(), activity.getOrder()));
-                        activity.addIntent(intentInfo);
-                        if (LOG_UNSAFE_BROADCASTS && isReceiver
-                                && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
-                            int actionCount = intentFilter.countActions();
-                            for (int i = 0; i < actionCount; i++) {
-                                final String action = intentFilter.getAction(i);
-                                if (action == null || !action.startsWith("android.")) {
-                                    continue;
-                                }
-
-                                if (!SAFE_BROADCASTS.contains(action)) {
-                                    Slog.w(TAG,
-                                            "Broadcast " + action + " may never be delivered to "
-                                                    + pkg.getPackageName() + " as requested at: "
-                                                    + parser.getPositionDescription());
-                                }
-                            }
-                        }
-                    }
-                }
-                result = intentResult;
-            } else if (parser.getName().equals("meta-data")) {
-                result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
-            } else if (parser.getName().equals("property")) {
-                result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
-            } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
-                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
-                        true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
-                        resources, parser, input);
-                if (intentResult.isSuccess()) {
-                    ParsedIntentInfo intent = intentResult.getResult();
-                    if (intent != null) {
-                        pkg.addPreferredActivityFilter(activity.getClassName(), intent);
-                    }
-                }
-                result = intentResult;
-            } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
-                ParseResult<ActivityInfo.WindowLayout> layoutResult =
-                        parseActivityWindowLayout(resources, parser, input);
-                if (layoutResult.isSuccess()) {
-                    activity.setWindowLayout(layoutResult.getResult());
-                }
-                result = layoutResult;
-            } else {
-                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        if (!isAlias && activity.getLaunchMode() != LAUNCH_SINGLE_INSTANCE_PER_TASK
-                && activity.getMetaData() != null && activity.getMetaData().containsKey(
-                ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
-            final String launchMode = activity.getMetaData().getString(
-                    ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
-            if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
-                activity.setLaunchMode(LAUNCH_SINGLE_INSTANCE_PER_TASK);
-            }
-        }
-
-        if (!isAlias) {
-            // Default allow the activity to be displayed on a remote device unless it explicitly
-            // set to false.
-            boolean canDisplayOnRemoteDevices = array.getBoolean(
-                    R.styleable.AndroidManifestActivity_canDisplayOnRemoteDevices, true);
-            if (activity.getMetaData() != null && !activity.getMetaData().getBoolean(
-                    ParsingPackageUtils.METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES, true)) {
-                canDisplayOnRemoteDevices = false;
-            }
-            if (canDisplayOnRemoteDevices) {
-                activity.setFlags(activity.getFlags()
-                        | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES);
-            }
-        }
-
-        ParseResult<ActivityInfo.WindowLayout> layoutResult =
-                resolveActivityWindowLayout(activity, input);
-        if (layoutResult.isError()) {
-            return input.error(layoutResult);
-        }
-        activity.setWindowLayout(layoutResult.getResult());
-
-        if (!setExported) {
-            boolean hasIntentFilters = activity.getIntents().size() > 0;
-            if (hasIntentFilters) {
-                final ParseResult exportedCheckResult = input.deferError(
-                        activity.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
-                        + " and above) requires that an explicit value for android:exported be"
-                        + " defined when intent filters are present",
-                        DeferredError.MISSING_EXPORTED_FLAG);
-                if (exportedCheckResult.isError()) {
-                    return input.error(exportedCheckResult);
-                }
-            }
-            activity.setExported(hasIntentFilters);
-        }
-
-        return input.success(activity);
-    }
-
-    @NonNull
-    private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg,
-            ParsedActivityImpl activity, boolean allowImplicitEphemeralVisibility,
-            boolean visibleToEphemeral, Resources resources, XmlResourceParser parser,
-            ParseInput input) throws IOException, XmlPullParserException {
-        ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity,
-                pkg, resources, parser, visibleToEphemeral, true /*allowGlobs*/,
-                true /*allowAutoVerify*/, allowImplicitEphemeralVisibility,
-                true /*failOnNoActions*/, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        ParsedIntentInfo intent = result.getResult();
-        if (intent != null) {
-            final IntentFilter intentFilter = intent.getIntentFilter();
-            if (intentFilter.isVisibleToInstantApp()) {
-                activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
-            }
-            if (intentFilter.isImplicitlyVisibleToInstantApp()) {
-                activity.setFlags(
-                        activity.getFlags() | ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP);
-            }
-        }
-
-        return input.success(intent);
-    }
-
-    private static int getActivityResizeMode(ParsingPackage pkg, TypedArray sa,
-            int screenOrientation) {
-        Boolean resizeableActivity = pkg.getResizeableActivity();
-
-        if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
-                || resizeableActivity != null) {
-            // Activity or app explicitly set if it is resizeable or not;
-            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
-                    resizeableActivity != null && resizeableActivity)) {
-                return ActivityInfo.RESIZE_MODE_RESIZEABLE;
-            } else {
-                return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-            }
-        }
-
-        if (pkg.isResizeableActivityViaSdkVersion()) {
-            // The activity or app didn't explicitly set the resizing option, however we want to
-            // make it resize due to the sdk version it is targeting.
-            return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-        }
-
-        // resize preference isn't set and target sdk version doesn't support resizing apps by
-        // default. For the app to be resizeable if it isn't fixed orientation or immersive.
-        if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
-            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
-        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
-            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
-        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
-            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
-        } else {
-            return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res,
-            AttributeSet attrs, ParseInput input) {
-        TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
-        try {
-            int width = -1;
-            float widthFraction = -1f;
-            int height = -1;
-            float heightFraction = -1f;
-            final int widthType = sw.getType(R.styleable.AndroidManifestLayout_defaultWidth);
-            if (widthType == TypedValue.TYPE_FRACTION) {
-                widthFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultWidth, 1, 1,
-                        -1);
-            } else if (widthType == TypedValue.TYPE_DIMENSION) {
-                width = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultWidth,
-                        -1);
-            }
-            final int heightType = sw.getType(R.styleable.AndroidManifestLayout_defaultHeight);
-            if (heightType == TypedValue.TYPE_FRACTION) {
-                heightFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultHeight, 1,
-                        1, -1);
-            } else if (heightType == TypedValue.TYPE_DIMENSION) {
-                height = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultHeight,
-                        -1);
-            }
-            int gravity = sw.getInt(R.styleable.AndroidManifestLayout_gravity, Gravity.CENTER);
-            int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
-            int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
-                    -1);
-            String windowLayoutAffinity =
-                    sw.getNonConfigurationString(
-                            R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0);
-            final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width,
-                    widthFraction, height, heightFraction, gravity, minWidth, minHeight,
-                    windowLayoutAffinity);
-            return input.success(windowLayout);
-        } finally {
-            sw.recycle();
-        }
-    }
-
-    /**
-     * Resolves values in {@link ActivityInfo.WindowLayout}.
-     *
-     * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
-     * Android R and some variants of pre-R.
-     */
-    private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
-            ParsedActivity activity, ParseInput input) {
-        // There isn't a metadata for us to fall back. Whatever is in layout is correct.
-        if (activity.getMetaData() == null || !activity.getMetaData().containsKey(
-                ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
-            return input.success(activity.getWindowLayout());
-        }
-
-        // Layout already specifies a value. We should just use that one.
-        if (activity.getWindowLayout() != null && activity.getWindowLayout().windowLayoutAffinity != null) {
-            return input.success(activity.getWindowLayout());
-        }
-
-        String windowLayoutAffinity = activity.getMetaData().getString(
-                ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
-        ActivityInfo.WindowLayout layout = activity.getWindowLayout();
-        if (layout == null) {
-            layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
-                    -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
-                    -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity);
-        } else {
-            layout.windowLayoutAffinity = windowLayoutAffinity;
-        }
-        return input.success(layout);
-    }
-
-    /**
-     * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
-     * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
-     *                                AndroidManifest.xml.
-     * @hide
-     */
-    static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) {
-        return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
deleted file mode 100644
index fe821e0..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcelable;
-
-/** @hide */
-public interface ParsedApexSystemService extends Parcelable {
-
-    @NonNull
-    String getName();
-
-    @Nullable
-    String getJarPath();
-
-    @Nullable
-    String getMinSdkVersion();
-
-    @Nullable
-    String getMaxSdkVersion();
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
deleted file mode 100644
index 54196fd..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-
-/** @hide **/
-@DataClass(genGetters = true, genAidl = false, genSetters = true, genParcelable = true)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedApexSystemServiceImpl implements ParsedApexSystemService {
-
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
-    @NonNull
-    private String name;
-
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
-    @Nullable
-    private String jarPath;
-
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
-    @Nullable
-    private String minSdkVersion;
-
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
-    @Nullable
-    private String maxSdkVersion;
-
-    public ParsedApexSystemServiceImpl() {
-    }
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedApexSystemServiceImpl(
-            @NonNull String name,
-            @Nullable String jarPath,
-            @Nullable String minSdkVersion,
-            @Nullable String maxSdkVersion) {
-        this.name = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.jarPath = jarPath;
-        this.minSdkVersion = minSdkVersion;
-        this.maxSdkVersion = maxSdkVersion;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return name;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getJarPath() {
-        return jarPath;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getMinSdkVersion() {
-        return minSdkVersion;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getMaxSdkVersion() {
-        return maxSdkVersion;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) {
-        name = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedApexSystemServiceImpl setJarPath(@NonNull String value) {
-        jarPath = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedApexSystemServiceImpl setMinSdkVersion(@NonNull String value) {
-        minSdkVersion = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedApexSystemServiceImpl setMaxSdkVersion(@NonNull String value) {
-        maxSdkVersion = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<String> sParcellingForName =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedString.class);
-    static {
-        if (sParcellingForName == null) {
-            sParcellingForName = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedString());
-        }
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<String> sParcellingForJarPath =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedString.class);
-    static {
-        if (sParcellingForJarPath == null) {
-            sParcellingForJarPath = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedString());
-        }
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<String> sParcellingForMinSdkVersion =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedString.class);
-    static {
-        if (sParcellingForMinSdkVersion == null) {
-            sParcellingForMinSdkVersion = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedString());
-        }
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<String> sParcellingForMaxSdkVersion =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedString.class);
-    static {
-        if (sParcellingForMaxSdkVersion == null) {
-            sParcellingForMaxSdkVersion = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedString());
-        }
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        byte flg = 0;
-        if (jarPath != null) flg |= 0x2;
-        if (minSdkVersion != null) flg |= 0x4;
-        if (maxSdkVersion != null) flg |= 0x8;
-        dest.writeByte(flg);
-        sParcellingForName.parcel(name, dest, flags);
-        sParcellingForJarPath.parcel(jarPath, dest, flags);
-        sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags);
-        sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedApexSystemServiceImpl(@NonNull android.os.Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        byte flg = in.readByte();
-        String _name = sParcellingForName.unparcel(in);
-        String _jarPath = sParcellingForJarPath.unparcel(in);
-        String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in);
-        String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in);
-
-        this.name = _name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.jarPath = _jarPath;
-        this.minSdkVersion = _minSdkVersion;
-        this.maxSdkVersion = _maxSdkVersion;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull android.os.Parcelable.Creator<ParsedApexSystemServiceImpl> CREATOR
-            = new android.os.Parcelable.Creator<ParsedApexSystemServiceImpl>() {
-        @Override
-        public ParsedApexSystemServiceImpl[] newArray(int size) {
-            return new ParsedApexSystemServiceImpl[size];
-        }
-
-        @Override
-        public ParsedApexSystemServiceImpl createFromParcel(@NonNull android.os.Parcel in) {
-            return new ParsedApexSystemServiceImpl(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1638903241144L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java",
-            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService]\[email protected](genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java
deleted file mode 100644
index 26abf48..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.R;
-import android.annotation.NonNull;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.text.TextUtils;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/** @hide */
-public class ParsedApexSystemServiceUtils {
-
-    @NonNull
-    public static ParseResult<ParsedApexSystemService> parseApexSystemService(
-            Resources res, XmlResourceParser parser, ParseInput input)
-            throws XmlPullParserException, IOException {
-        final ParsedApexSystemServiceImpl systemService =
-                new ParsedApexSystemServiceImpl();
-        TypedArray sa = res.obtainAttributes(parser,
-                R.styleable.AndroidManifestApexSystemService);
-        try {
-            String className = sa.getString(
-                    R.styleable.AndroidManifestApexSystemService_name);
-            if (TextUtils.isEmpty(className)) {
-                return input.error("<apex-system-service> does not have name attribute");
-            }
-
-            String jarPath = sa.getString(
-                    R.styleable.AndroidManifestApexSystemService_path);
-            String minSdkVersion = sa.getString(
-                    R.styleable.AndroidManifestApexSystemService_minSdkVersion);
-            String maxSdkVersion = sa.getString(
-                    R.styleable.AndroidManifestApexSystemService_maxSdkVersion);
-
-            systemService.setName(className)
-                    .setMinSdkVersion(minSdkVersion)
-                    .setMaxSdkVersion(maxSdkVersion);
-            if (!TextUtils.isEmpty(jarPath)) {
-                systemService.setJarPath(jarPath);
-            }
-
-            return input.success(systemService);
-        } finally {
-            sa.recycle();
-        }
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
deleted file mode 100644
index ac7a928..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArraySet;
-
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
- * manifest.
- *
- * @hide
- */
-public interface ParsedAttribution extends Parcelable {
-
-    /**
-     * Maximum length of attribution tag
-     * @hide
-     */
-    int MAX_ATTRIBUTION_TAG_LEN = 50;
-
-    /**
-     * Ids of previously declared attributions this attribution inherits from
-     */
-    @NonNull List<String> getInheritFrom();
-
-    /**
-     * User visible label for the attribution
-     */
-    @StringRes int getLabel();
-
-    /**
-     * Tag of the attribution
-     */
-    @NonNull String getTag();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java b/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
deleted file mode 100644
index 510425f..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
- * manifest.
- *
- * @hide
- */
-@DataClass(genAidl = false, genSetters = true, genBuilder = false, genParcelable = true)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedAttributionImpl implements ParsedAttribution {
-
-    /** Maximum amount of attributions per package */
-    static final int MAX_NUM_ATTRIBUTIONS = 10000;
-
-    /** Tag of the attribution */
-    private @NonNull String tag;
-
-    /** User visible label fo the attribution */
-    private @StringRes int label;
-
-    /** Ids of previously declared attributions this attribution inherits from */
-    private @NonNull List<String> inheritFrom;
-
-    public ParsedAttributionImpl() {}
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    /**
-     * Creates a new ParsedAttributionImpl.
-     *
-     * @param tag
-     *   Tag of the attribution
-     * @param label
-     *   User visible label fo the attribution
-     * @param inheritFrom
-     *   Ids of previously declared attributions this attribution inherits from
-     */
-    @DataClass.Generated.Member
-    public ParsedAttributionImpl(
-            @NonNull String tag,
-            @StringRes int label,
-            @NonNull List<String> inheritFrom) {
-        this.tag = tag;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, tag);
-        this.label = label;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        this.inheritFrom = inheritFrom;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    /**
-     * Tag of the attribution
-     */
-    @DataClass.Generated.Member
-    public @NonNull String getTag() {
-        return tag;
-    }
-
-    /**
-     * User visible label fo the attribution
-     */
-    @DataClass.Generated.Member
-    public @StringRes int getLabel() {
-        return label;
-    }
-
-    /**
-     * Ids of previously declared attributions this attribution inherits from
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<String> getInheritFrom() {
-        return inheritFrom;
-    }
-
-    /**
-     * Tag of the attribution
-     */
-    @DataClass.Generated.Member
-    public @NonNull ParsedAttributionImpl setTag(@NonNull String value) {
-        tag = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, tag);
-        return this;
-    }
-
-    /**
-     * User visible label fo the attribution
-     */
-    @DataClass.Generated.Member
-    public @NonNull ParsedAttributionImpl setLabel(@StringRes int value) {
-        label = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        return this;
-    }
-
-    /**
-     * Ids of previously declared attributions this attribution inherits from
-     */
-    @DataClass.Generated.Member
-    public @NonNull ParsedAttributionImpl setInheritFrom(@NonNull List<String> value) {
-        inheritFrom = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-        return this;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeString(tag);
-        dest.writeInt(label);
-        dest.writeStringList(inheritFrom);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedAttributionImpl(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _tag = in.readString();
-        int _label = in.readInt();
-        List<String> _inheritFrom = new ArrayList<>();
-        in.readStringList(_inheritFrom);
-
-        this.tag = _tag;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, tag);
-        this.label = _label;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        this.inheritFrom = _inheritFrom;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedAttributionImpl> CREATOR
-            = new Parcelable.Creator<ParsedAttributionImpl>() {
-        @Override
-        public ParsedAttributionImpl[] newArray(int size) {
-            return new ParsedAttributionImpl[size];
-        }
-
-        @Override
-        public ParsedAttributionImpl createFromParcel(@NonNull Parcel in) {
-            return new ParsedAttributionImpl(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1627594502974L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java",
-            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution]\[email protected](genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
deleted file mode 100644
index 84f1d44..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.util.ArraySet;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** @hide */
-public class ParsedAttributionUtils {
-
-    @NonNull
-    public static ParseResult<ParsedAttribution> parseAttribution(Resources res,
-            XmlResourceParser parser, ParseInput input)
-            throws IOException, XmlPullParserException {
-        String attributionTag;
-        int label;
-        List<String> inheritFrom = null;
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution);
-        if (sa == null) {
-            return input.error("<attribution> could not be parsed");
-        }
-
-        try {
-            attributionTag = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestAttribution_tag, 0);
-            if (attributionTag == null) {
-                return input.error("<attribution> does not specify android:tag");
-            }
-            if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) {
-                return input.error("android:tag is too long. Max length is "
-                        + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN);
-            }
-
-            label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0);
-            if (label == Resources.ID_NULL) {
-                return input.error("<attribution> does not specify android:label");
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        int type;
-        final int innerDepth = parser.getDepth();
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            if (tagName.equals("inherit-from")) {
-                sa = res.obtainAttributes(parser,
-                        R.styleable.AndroidManifestAttributionInheritFrom);
-                if (sa == null) {
-                    return input.error("<inherit-from> could not be parsed");
-                }
-
-                try {
-                    String inheritFromId = sa.getNonConfigurationString(
-                            R.styleable.AndroidManifestAttributionInheritFrom_tag, 0);
-
-                    if (inheritFrom == null) {
-                        inheritFrom = new ArrayList<>();
-                    }
-                    inheritFrom.add(inheritFromId);
-                } finally {
-                    sa.recycle();
-                }
-            } else {
-                return input.error("Bad element under <attribution>: " + tagName);
-            }
-        }
-
-        if (inheritFrom == null) {
-            inheritFrom = Collections.emptyList();
-        } else {
-            ((ArrayList) inheritFrom).trimToSize();
-        }
-
-        return input.success(new ParsedAttributionImpl(attributionTag, label, inheritFrom));
-    }
-
-    /**
-     * @return Is this set of attributions a valid combination for a single package?
-     */
-    public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
-        if (attributions == null) {
-            return true;
-        }
-
-        ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
-        ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
-
-        int numAttributions = attributions.size();
-        if (numAttributions > ParsedAttributionImpl.MAX_NUM_ATTRIBUTIONS) {
-            return false;
-        }
-
-        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
-            boolean wasAdded = attributionTags.add(attributions.get(attributionNum).getTag());
-            if (!wasAdded) {
-                // feature id is not unique
-                return false;
-            }
-        }
-
-        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
-            ParsedAttribution feature = attributions.get(attributionNum);
-
-            final List<String> inheritFromList = feature.getInheritFrom();
-            int numInheritFrom = inheritFromList.size();
-            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
-                String inheritFrom = inheritFromList.get(inheritFromNum);
-
-                if (attributionTags.contains(inheritFrom)) {
-                    // Cannot inherit from a attribution that is still defined
-                    return false;
-                }
-
-                boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
-                if (!wasAdded) {
-                    // inheritFrom is not unique
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
deleted file mode 100644
index c1372f6..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.pm.PackageManager.Property;
-import android.os.Bundle;
-import android.os.Parcelable;
-
-import java.util.List;
-import java.util.Map;
-
-/** @hide */
-public interface ParsedComponent extends Parcelable {
-
-    int getBanner();
-
-    @NonNull
-    ComponentName getComponentName();
-
-    int getDescriptionRes();
-
-    int getFlags();
-
-    int getIcon();
-
-    @NonNull
-    List<ParsedIntentInfo> getIntents();
-
-    int getLabelRes();
-
-    int getLogo();
-
-    @Nullable
-    Bundle getMetaData();
-
-    @NonNull
-    String getName();
-
-    @Nullable
-    CharSequence getNonLocalizedLabel();
-
-    @NonNull
-    String getPackageName();
-
-    @NonNull
-    Map<String, Property> getProperties();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java b/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
deleted file mode 100644
index 1c46a10..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import static java.util.Collections.emptyMap;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.parsing.ParsingUtils;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genConstructor = false, genBuilder = false)
[email protected]({"setComponentName", "setProperties", "setIntents"})
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public abstract class ParsedComponentImpl implements ParsedComponent {
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String name;
-    private int icon;
-    private int labelRes;
-    @Nullable
-    private CharSequence nonLocalizedLabel;
-    private int logo;
-    private int banner;
-    private int descriptionRes;
-
-    // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
-    private int flags;
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String packageName;
-
-    @NonNull
-    @DataClass.PluralOf("intent")
-    private List<ParsedIntentInfo> intents = Collections.emptyList();
-
-    @Nullable
-    private ComponentName componentName;
-
-    @Nullable
-    private Bundle metaData;
-
-    @NonNull
-    private Map<String, Property> mProperties = emptyMap();
-
-    public ParsedComponentImpl() {
-
-    }
-
-    protected ParsedComponentImpl(ParsedComponent other) {
-        this.metaData = other.getMetaData();
-        this.name = other.getName();
-        this.icon = other.getIcon();
-        this.labelRes = other.getLabelRes();
-        this.nonLocalizedLabel = other.getNonLocalizedLabel();
-        this.logo = other.getLogo();
-        this.banner = other.getBanner();
-        this.descriptionRes = other.getDescriptionRes();
-        this.flags = other.getFlags();
-        this.packageName = other.getPackageName();
-        this.componentName = other.getComponentName();
-        this.intents = new ArrayList<>(other.getIntents());
-        this.mProperties = new ArrayMap<>();
-        this.mProperties.putAll(other.getProperties());
-    }
-
-    public void addIntent(ParsedIntentInfo intent) {
-        this.intents = CollectionUtils.add(this.intents, intent);
-    }
-
-    /** Add a property to the component */
-    public void addProperty(@NonNull Property property) {
-        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
-    }
-
-    public ParsedComponentImpl setName(String name) {
-        this.name = TextUtils.safeIntern(name);
-        return this;
-    }
-
-    @CallSuper
-    public void setPackageName(@NonNull String packageName) {
-        this.packageName = TextUtils.safeIntern(packageName);
-        //noinspection ConstantConditions
-        this.componentName = null;
-
-        // Note: this method does not edit name (which can point to a class), because this package
-        // name change is not changing the package in code, but the identifier used by the system.
-    }
-
-    @Override
-    @NonNull
-    public ComponentName getComponentName() {
-        if (componentName == null) {
-            componentName = new ComponentName(getPackageName(), getName());
-        }
-        return componentName;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(this.name);
-        dest.writeInt(this.getIcon());
-        dest.writeInt(this.getLabelRes());
-        dest.writeCharSequence(this.getNonLocalizedLabel());
-        dest.writeInt(this.getLogo());
-        dest.writeInt(this.getBanner());
-        dest.writeInt(this.getDescriptionRes());
-        dest.writeInt(this.getFlags());
-        sForInternedString.parcel(this.packageName, dest, flags);
-        dest.writeTypedList(this.getIntents());
-        dest.writeBundle(this.metaData);
-        dest.writeMap(this.mProperties);
-    }
-
-    protected ParsedComponentImpl(Parcel in) {
-        // We use the boot classloader for all classes that we load.
-        final ClassLoader boot = Object.class.getClassLoader();
-        //noinspection ConstantConditions
-        this.name = in.readString();
-        this.icon = in.readInt();
-        this.labelRes = in.readInt();
-        this.nonLocalizedLabel = in.readCharSequence();
-        this.logo = in.readInt();
-        this.banner = in.readInt();
-        this.descriptionRes = in.readInt();
-        this.flags = in.readInt();
-        //noinspection ConstantConditions
-        this.packageName = sForInternedString.unparcel(in);
-        this.intents = ParsingUtils.createTypedInterfaceList(in, ParsedIntentInfoImpl.CREATOR);
-        this.metaData = in.readBundle(boot);
-        this.mProperties = in.readHashMap(boot);
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return name;
-    }
-
-    @DataClass.Generated.Member
-    public int getIcon() {
-        return icon;
-    }
-
-    @DataClass.Generated.Member
-    public int getLabelRes() {
-        return labelRes;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable CharSequence getNonLocalizedLabel() {
-        return nonLocalizedLabel;
-    }
-
-    @DataClass.Generated.Member
-    public int getLogo() {
-        return logo;
-    }
-
-    @DataClass.Generated.Member
-    public int getBanner() {
-        return banner;
-    }
-
-    @DataClass.Generated.Member
-    public int getDescriptionRes() {
-        return descriptionRes;
-    }
-
-    @DataClass.Generated.Member
-    public int getFlags() {
-        return flags;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getPackageName() {
-        return packageName;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull List<ParsedIntentInfo> getIntents() {
-        return intents;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable Bundle getMetaData() {
-        return metaData;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull Map<String,Property> getProperties() {
-        return mProperties;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setIcon( int value) {
-        icon = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setLabelRes( int value) {
-        labelRes = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setNonLocalizedLabel(@NonNull CharSequence value) {
-        nonLocalizedLabel = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setLogo( int value) {
-        logo = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setBanner( int value) {
-        banner = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setDescriptionRes( int value) {
-        descriptionRes = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setFlags( int value) {
-        flags = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedComponentImpl setMetaData(@NonNull Bundle value) {
-        metaData = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1627680195484L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java",
-            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic  void addIntent(android.content.pm.parsing.component.ParsedIntentInfo)\npublic  void addProperty(android.content.pm.PackageManager.Property)\npublic  android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent]\[email protected](genGetters=true, genSetters=true, genConstructor=false, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
deleted file mode 100644
index 5c33cfd..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingUtils.NOT_SET;
-
-import android.annotation.NonNull;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.TypedValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/** @hide */
-class ParsedComponentUtils {
-
-    @NonNull
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    static <Component extends ParsedComponentImpl> ParseResult<Component> parseComponent(
-            Component component, String tag, ParsingPackage pkg, TypedArray array,
-            boolean useRoundIcon, ParseInput input, int bannerAttr, int descriptionAttr,
-            int iconAttr, int labelAttr, int logoAttr, int nameAttr, int roundIconAttr) {
-        String name = array.getNonConfigurationString(nameAttr, 0);
-        if (TextUtils.isEmpty(name)) {
-            return input.error(tag + " does not specify android:name");
-        }
-
-        String packageName = pkg.getPackageName();
-        String className = ParsingUtils.buildClassName(packageName, name);
-        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
-            return input.error(tag + " invalid android:name");
-        }
-
-        //noinspection ConstantConditions; null check done above with isEmpty
-        component.setName(className)
-                .setPackageName(packageName);
-
-        int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
-        if (roundIconVal != 0) {
-            component.setIcon(roundIconVal)
-                    .setNonLocalizedLabel(null);
-        } else {
-            int iconVal = array.getResourceId(iconAttr, 0);
-            if (iconVal != 0) {
-                component.setIcon(iconVal);
-                component.setNonLocalizedLabel(null);
-            }
-        }
-
-        int logoVal = array.getResourceId(logoAttr, 0);
-        if (logoVal != 0) {
-            component.setLogo(logoVal);
-        }
-
-        int bannerVal = array.getResourceId(bannerAttr, 0);
-        if (bannerVal != 0) {
-            component.setBanner(bannerVal);
-        }
-
-        if (descriptionAttr != NOT_SET) {
-            component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
-        }
-
-        TypedValue v = array.peekValue(labelAttr);
-        if (v != null) {
-            component.setLabelRes(v.resourceId);
-            if (v.resourceId == 0) {
-                component.setNonLocalizedLabel(v.coerceToString());
-            }
-        }
-
-        return input.success(component);
-    }
-
-    static ParseResult<Bundle> addMetaData(ParsedComponentImpl component, ParsingPackage pkg,
-            Resources resources, XmlResourceParser parser, ParseInput input) {
-        ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
-                resources, parser, "<meta-data>", input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        final Property property = result.getResult();
-        if (property != null) {
-            component.setMetaData(property.toBundle(component.getMetaData()));
-        }
-        return input.success(component.getMetaData());
-    }
-
-    static ParseResult<Property> addProperty(ParsedComponentImpl component, ParsingPackage pkg,
-            Resources resources, XmlResourceParser parser, ParseInput input) {
-        ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
-                resources, parser, "<property>", input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-        final Property property = result.getResult();
-        if (property != null) {
-            component.addProperty(property);
-        }
-        return input.success(property);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
deleted file mode 100644
index e8fcc00..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.Nullable;
-
-/** @hide */
-public interface ParsedInstrumentation extends ParsedComponent {
-
-    @Nullable
-    String getTargetPackage();
-
-    @Nullable
-    String getTargetProcesses();
-
-    boolean isFunctionalTest();
-
-    boolean isHandleProfiling();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
deleted file mode 100644
index d2b5368..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedInstrumentationImpl extends ParsedComponentImpl implements
-        ParsedInstrumentation {
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String targetPackage;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String targetProcesses;
-    private boolean handleProfiling;
-    private boolean functionalTest;
-
-    public ParsedInstrumentationImpl() {
-    }
-
-    public ParsedInstrumentationImpl setTargetPackage(@Nullable String targetPackage) {
-        this.targetPackage = TextUtils.safeIntern(targetPackage);
-        return this;
-    }
-
-    public ParsedInstrumentationImpl setTargetProcesses(@Nullable String targetProcesses) {
-        this.targetProcesses = TextUtils.safeIntern(targetProcesses);
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Instrumentation{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        sForInternedString.parcel(this.targetPackage, dest, flags);
-        sForInternedString.parcel(this.targetProcesses, dest, flags);
-        dest.writeBoolean(this.handleProfiling);
-        dest.writeBoolean(this.functionalTest);
-    }
-
-    protected ParsedInstrumentationImpl(Parcel in) {
-        super(in);
-        this.targetPackage = sForInternedString.unparcel(in);
-        this.targetProcesses = sForInternedString.unparcel(in);
-        this.handleProfiling = in.readByte() != 0;
-        this.functionalTest = in.readByte() != 0;
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedInstrumentationImpl> CREATOR =
-            new Parcelable.Creator<ParsedInstrumentationImpl>() {
-                @Override
-                public ParsedInstrumentationImpl createFromParcel(Parcel source) {
-                    return new ParsedInstrumentationImpl(source);
-                }
-
-                @Override
-                public ParsedInstrumentationImpl[] newArray(int size) {
-                    return new ParsedInstrumentationImpl[size];
-                }
-            };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedInstrumentationImpl(
-            @Nullable String targetPackage,
-            @Nullable String targetProcesses,
-            boolean handleProfiling,
-            boolean functionalTest) {
-        this.targetPackage = targetPackage;
-        this.targetProcesses = targetProcesses;
-        this.handleProfiling = handleProfiling;
-        this.functionalTest = functionalTest;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getTargetPackage() {
-        return targetPackage;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getTargetProcesses() {
-        return targetProcesses;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isHandleProfiling() {
-        return handleProfiling;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isFunctionalTest() {
-        return functionalTest;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedInstrumentationImpl setHandleProfiling( boolean value) {
-        handleProfiling = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedInstrumentationImpl setFunctionalTest( boolean value) {
-        functionalTest = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1627595809880L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
deleted file mode 100644
index df5e73e..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingUtils.NOT_SET;
-
-import android.annotation.NonNull;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/** @hide */
-public class ParsedInstrumentationUtils {
-
-    @NonNull
-    public static ParseResult<ParsedInstrumentation> parseInstrumentation(ParsingPackage pkg,
-            Resources res, XmlResourceParser parser, boolean useRoundIcon,
-            ParseInput input) throws IOException, XmlPullParserException {
-        ParsedInstrumentationImpl
-                instrumentation = new ParsedInstrumentationImpl();
-        String tag = "<" + parser.getName() + ">";
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
-        try {
-            ParseResult<ParsedInstrumentationImpl> result = ParsedComponentUtils.parseComponent(
-                    instrumentation, tag, pkg, sa, useRoundIcon, input,
-                    R.styleable.AndroidManifestInstrumentation_banner,
-                    NOT_SET /*descriptionAttr*/,
-                    R.styleable.AndroidManifestInstrumentation_icon,
-                    R.styleable.AndroidManifestInstrumentation_label,
-                    R.styleable.AndroidManifestInstrumentation_logo,
-                    R.styleable.AndroidManifestInstrumentation_name,
-                    R.styleable.AndroidManifestInstrumentation_roundIcon);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            // @formatter:off
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage))
-                    .setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses))
-                    .setHandleProfiling(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false))
-                    .setFunctionalTest(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false));
-            // @formatter:on
-        } finally {
-            sa.recycle();
-        }
-
-        ParseResult<ParsedInstrumentationImpl> result =
-                ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, instrumentation, input);
-
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        return input.success(result.getResult());
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
deleted file mode 100644
index 1e36ccca..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Pair;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** @hide **/
-public interface ParsedIntentInfo extends Parcelable {
-
-    boolean isHasDefault();
-
-    int getLabelRes();
-
-    @Nullable CharSequence getNonLocalizedLabel();
-
-    int getIcon();
-
-    @NonNull IntentFilter getIntentFilter();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
deleted file mode 100644
index 9ff7a16..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Pair;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** @hide **/
-@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
-        genBuilder = false, genConstructor = false)
[email protected]({"setIntentFilter"})
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedIntentInfoImpl implements ParsedIntentInfo {
-
-    private boolean mHasDefault;
-    private int mLabelRes;
-    @Nullable
-    private CharSequence mNonLocalizedLabel;
-    private int mIcon;
-
-    @NonNull
-    private IntentFilter mIntentFilter = new IntentFilter();
-
-    public ParsedIntentInfoImpl() {}
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public boolean isHasDefault() {
-        return mHasDefault;
-    }
-
-    @DataClass.Generated.Member
-    public int getLabelRes() {
-        return mLabelRes;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable CharSequence getNonLocalizedLabel() {
-        return mNonLocalizedLabel;
-    }
-
-    @DataClass.Generated.Member
-    public int getIcon() {
-        return mIcon;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull IntentFilter getIntentFilter() {
-        return mIntentFilter;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedIntentInfoImpl setHasDefault( boolean value) {
-        mHasDefault = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedIntentInfoImpl setLabelRes( int value) {
-        mLabelRes = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedIntentInfoImpl setNonLocalizedLabel(@NonNull CharSequence value) {
-        mNonLocalizedLabel = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedIntentInfoImpl setIcon( int value) {
-        mIcon = value;
-        return this;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        byte flg = 0;
-        if (mHasDefault) flg |= 0x1;
-        if (mNonLocalizedLabel != null) flg |= 0x4;
-        dest.writeByte(flg);
-        dest.writeInt(mLabelRes);
-        if (mNonLocalizedLabel != null) dest.writeCharSequence(mNonLocalizedLabel);
-        dest.writeInt(mIcon);
-        dest.writeTypedObject(mIntentFilter, flags);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedIntentInfoImpl(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        byte flg = in.readByte();
-        boolean hasDefault = (flg & 0x1) != 0;
-        int labelRes = in.readInt();
-        CharSequence nonLocalizedLabel = (flg & 0x4) == 0 ? null : (CharSequence) in.readCharSequence();
-        int icon = in.readInt();
-        IntentFilter intentFilter = (IntentFilter) in.readTypedObject(IntentFilter.CREATOR);
-
-        this.mHasDefault = hasDefault;
-        this.mLabelRes = labelRes;
-        this.mNonLocalizedLabel = nonLocalizedLabel;
-        this.mIcon = icon;
-        this.mIntentFilter = intentFilter;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mIntentFilter);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedIntentInfoImpl> CREATOR
-            = new Parcelable.Creator<ParsedIntentInfoImpl>() {
-        @Override
-        public ParsedIntentInfoImpl[] newArray(int size) {
-            return new ParsedIntentInfoImpl[size];
-        }
-
-        @Override
-        public ParsedIntentInfoImpl createFromParcel(@NonNull Parcel in) {
-            return new ParsedIntentInfoImpl(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1627691925408L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java",
-            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo]\[email protected](genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
deleted file mode 100644
index cb72c2b..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
-
-import android.annotation.NonNull;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.PatternMatcher;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Iterator;
-
-/** @hide */
-public class ParsedIntentInfoUtils {
-
-    private static final String TAG = ParsingUtils.TAG;
-
-    public static final boolean DEBUG = false;
-
-    @NonNull
-    public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
-            boolean allowAutoVerify, ParseInput input)
-            throws XmlPullParserException, IOException {
-        ParsedIntentInfoImpl intentInfo = new ParsedIntentInfoImpl();
-        IntentFilter intentFilter = intentInfo.getIntentFilter();
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
-        try {
-            intentFilter.setPriority(
-                    sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
-            intentFilter.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
-
-            TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
-            if (v != null) {
-                intentInfo.setLabelRes(v.resourceId);
-                if (v.resourceId == 0) {
-                    intentInfo.setNonLocalizedLabel(v.coerceToString());
-                }
-            }
-
-            if (ParsingPackageUtils.sUseRoundIcon) {
-                intentInfo.setIcon(sa.getResourceId(
-                        R.styleable.AndroidManifestIntentFilter_roundIcon, 0));
-            }
-
-            if (intentInfo.getIcon() == 0) {
-                intentInfo.setIcon(
-                        sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0));
-            }
-
-            if (allowAutoVerify) {
-                intentFilter.setAutoVerify(sa.getBoolean(
-                        R.styleable.AndroidManifestIntentFilter_autoVerify,
-                        false));
-            }
-        } finally {
-            sa.recycle();
-        }
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final ParseResult result;
-            String nodeName = parser.getName();
-            switch (nodeName) {
-                case "action": {
-                    String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
-                    if (value == null) {
-                        result = input.error("No value supplied for <android:name>");
-                    } else if (value.isEmpty()) {
-                        intentFilter.addAction(value);
-                        // Prior to R, this was not a failure
-                        result = input.deferError("No value supplied for <android:name>",
-                                ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
-                    } else {
-                        intentFilter.addAction(value);
-                        result = input.success(null);
-                    }
-                    break;
-                }
-                case "category": {
-                    String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
-                    if (value == null) {
-                        result = input.error("No value supplied for <android:name>");
-                    } else if (value.isEmpty()) {
-                        intentFilter.addCategory(value);
-                        // Prior to R, this was not a failure
-                        result = input.deferError("No value supplied for <android:name>",
-                                ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
-                    } else {
-                        intentFilter.addCategory(value);
-                        result = input.success(null);
-                    }
-                    break;
-                }
-                case "data":
-                    result = parseData(intentInfo, res, parser, allowGlobs, input);
-                    break;
-                default:
-                    result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
-                    break;
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        intentInfo.setHasDefault(intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
-
-        if (DEBUG) {
-            final StringBuilder cats = new StringBuilder("Intent d=");
-            cats.append(intentInfo.isHasDefault());
-            cats.append(", cat=");
-
-            final Iterator<String> it = intentFilter.categoriesIterator();
-            if (it != null) {
-                while (it.hasNext()) {
-                    cats.append(' ');
-                    cats.append(it.next());
-                }
-            }
-            Slog.d(TAG, cats.toString());
-        }
-
-        return input.success(intentInfo);
-    }
-
-    @NonNull
-    private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
-            Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
-        IntentFilter intentFilter = intentInfo.getIntentFilter();
-        TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
-        try {
-            String str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_mimeType, 0);
-            if (str != null) {
-                try {
-                    intentFilter.addDataType(str);
-                } catch (IntentFilter.MalformedMimeTypeException e) {
-                    return input.error(e.toString());
-                }
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_mimeGroup, 0);
-            if (str != null) {
-                intentFilter.addMimeGroup(str);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_scheme, 0);
-            if (str != null) {
-                intentFilter.addDataScheme(str);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_ssp, 0);
-            if (str != null) {
-                intentFilter.addDataSchemeSpecificPart(str,
-                        PatternMatcher.PATTERN_LITERAL);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_sspPrefix, 0);
-            if (str != null) {
-                intentFilter.addDataSchemeSpecificPart(str,
-                        PatternMatcher.PATTERN_PREFIX);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_sspPattern, 0);
-            if (str != null) {
-                if (!allowGlobs) {
-                    return input.error(
-                            "sspPattern not allowed here; ssp must be literal");
-                }
-                intentFilter.addDataSchemeSpecificPart(str,
-                        PatternMatcher.PATTERN_SIMPLE_GLOB);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_sspAdvancedPattern, 0);
-            if (str != null) {
-                if (!allowGlobs) {
-                    return input.error(
-                            "sspAdvancedPattern not allowed here; ssp must be literal");
-                }
-                intentFilter.addDataSchemeSpecificPart(str,
-                        PatternMatcher.PATTERN_ADVANCED_GLOB);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_sspSuffix, 0);
-            if (str != null) {
-                intentFilter.addDataSchemeSpecificPart(str,
-                        PatternMatcher.PATTERN_SUFFIX);
-            }
-
-
-            String host = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_host, 0);
-            String port = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_port, 0);
-            if (host != null) {
-                intentFilter.addDataAuthority(host, port);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_path, 0);
-            if (str != null) {
-                intentFilter.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_pathPrefix, 0);
-            if (str != null) {
-                intentFilter.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_pathPattern, 0);
-            if (str != null) {
-                if (!allowGlobs) {
-                    return input.error(
-                            "pathPattern not allowed here; path must be literal");
-                }
-                intentFilter.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
-            if (str != null) {
-                if (!allowGlobs) {
-                    return input.error(
-                            "pathAdvancedPattern not allowed here; path must be literal");
-                }
-                intentFilter.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
-            }
-
-            str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestData_pathSuffix, 0);
-            if (str != null) {
-                intentFilter.addDataPath(str, PatternMatcher.PATTERN_SUFFIX);
-            }
-
-
-            return input.success(null);
-        } finally {
-            sa.recycle();
-        }
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
deleted file mode 100644
index 2507205..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.Nullable;
-
-/** @hide */
-public interface ParsedMainComponent extends ParsedComponent {
-
-    @Nullable
-    String[] getAttributionTags();
-
-    /**
-     * A main component's name is a class name. This makes code slightly more readable.
-     */
-    String getClassName();
-
-    boolean isDirectBootAware();
-
-    boolean isEnabled();
-
-    boolean isExported();
-
-    int getOrder();
-
-    @Nullable
-    String getProcessName();
-
-    @Nullable
-    String getSplitName();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
deleted file mode 100644
index 6051435..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedMainComponentImpl extends ParsedComponentImpl implements ParsedMainComponent {
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String processName;
-    private boolean directBootAware;
-    private boolean enabled = true;
-    private boolean exported;
-    private int order;
-
-    @Nullable
-    private String splitName;
-    @Nullable
-    private String[] attributionTags;
-
-    public ParsedMainComponentImpl() {
-    }
-
-    public ParsedMainComponentImpl(ParsedMainComponent other) {
-        super(other);
-        this.processName = other.getProcessName();
-        this.directBootAware = other.isDirectBootAware();
-        this.enabled = other.isEnabled();
-        this.exported = other.isExported();
-        this.order = other.getOrder();
-        this.splitName = other.getSplitName();
-        this.attributionTags = other.getAttributionTags();
-    }
-
-    public ParsedMainComponentImpl setProcessName(String processName) {
-        this.processName = TextUtils.safeIntern(processName);
-        return this;
-    }
-
-    /**
-     * A main component's name is a class name. This makes code slightly more readable.
-     */
-    public String getClassName() {
-        return getName();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        sForInternedString.parcel(this.processName, dest, flags);
-        dest.writeBoolean(this.directBootAware);
-        dest.writeBoolean(this.enabled);
-        dest.writeBoolean(this.exported);
-        dest.writeInt(this.order);
-        dest.writeString(this.splitName);
-        dest.writeString8Array(this.attributionTags);
-    }
-
-    protected ParsedMainComponentImpl(Parcel in) {
-        super(in);
-        this.processName = sForInternedString.unparcel(in);
-        this.directBootAware = in.readBoolean();
-        this.enabled = in.readBoolean();
-        this.exported = in.readBoolean();
-        this.order = in.readInt();
-        this.splitName = in.readString();
-        this.attributionTags = in.createString8Array();
-    }
-
-    public static final Parcelable.Creator<ParsedMainComponentImpl> CREATOR =
-            new Parcelable.Creator<ParsedMainComponentImpl>() {
-                @Override
-                public ParsedMainComponentImpl createFromParcel(Parcel source) {
-                    return new ParsedMainComponentImpl(source);
-                }
-
-                @Override
-                public ParsedMainComponentImpl[] newArray(int size) {
-                    return new ParsedMainComponentImpl[size];
-                }
-            };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedMainComponentImpl(
-            @Nullable String processName,
-            boolean directBootAware,
-            boolean enabled,
-            boolean exported,
-            int order,
-            @Nullable String splitName,
-            @Nullable String[] attributionTags) {
-        this.processName = processName;
-        this.directBootAware = directBootAware;
-        this.enabled = enabled;
-        this.exported = exported;
-        this.order = order;
-        this.splitName = splitName;
-        this.attributionTags = attributionTags;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getProcessName() {
-        return processName;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isDirectBootAware() {
-        return directBootAware;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isEnabled() {
-        return enabled;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isExported() {
-        return exported;
-    }
-
-    @DataClass.Generated.Member
-    public int getOrder() {
-        return order;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getSplitName() {
-        return splitName;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String[] getAttributionTags() {
-        return attributionTags;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setDirectBootAware( boolean value) {
-        directBootAware = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setEnabled( boolean value) {
-        enabled = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setExported( boolean value) {
-        exported = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setOrder( int value) {
-        order = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setSplitName(@android.annotation.NonNull String value) {
-        splitName = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setAttributionTags(@android.annotation.NonNull String... value) {
-        attributionTags = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1627324857874L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
deleted file mode 100644
index 87f75b0..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingUtils.NOT_SET;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/** @hide */
-class ParsedMainComponentUtils {
-
-    private static final String TAG = ParsingUtils.TAG;
-
-    @NonNull
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    static <Component extends ParsedMainComponentImpl> ParseResult<Component> parseMainComponent(
-            Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
-            TypedArray array, int flags, boolean useRoundIcon,  @Nullable String defaultSplitName,
-            @NonNull ParseInput input, int bannerAttr, int descriptionAttr, int directBootAwareAttr,
-            int enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
-            int processAttr, int roundIconAttr, int splitNameAttr, int attributionTagsAttr) {
-        ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
-                array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
-                logoAttr, nameAttr, roundIconAttr);
-        if (result.isError()) {
-            return result;
-        }
-
-        if (directBootAwareAttr != NOT_SET) {
-            component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
-            if (component.isDirectBootAware()) {
-                pkg.setPartiallyDirectBootAware(true);
-            }
-        }
-
-        if (enabledAttr != NOT_SET) {
-            component.setEnabled(array.getBoolean(enabledAttr, true));
-        }
-
-        if (processAttr != NOT_SET) {
-            CharSequence processName;
-            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
-                processName = array.getNonConfigurationString(processAttr,
-                        Configuration.NATIVE_CONFIG_VERSION);
-            } else {
-                // Some older apps have been seen to use a resource reference
-                // here that on older builds was ignored (with a warning).  We
-                // need to continue to do this for them so they don't break.
-                processName = array.getNonResourceString(processAttr);
-            }
-
-            // Backwards-compat, ignore error
-            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
-                    pkg.getPackageName(), pkg.getProcessName(), processName, flags,
-                    separateProcesses, input);
-            if (processNameResult.isError()) {
-                return input.error(processNameResult);
-            }
-
-            component.setProcessName(processNameResult.getResult());
-        }
-
-        if (splitNameAttr != NOT_SET) {
-            component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
-        }
-
-        if (defaultSplitName != null && component.getSplitName() == null) {
-            component.setSplitName(defaultSplitName);
-        }
-
-        if (attributionTagsAttr != NOT_SET) {
-            final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
-            if (attributionTags != null) {
-                component.setAttributionTags(attributionTags.split("\\|"));
-            }
-        }
-
-        return input.success(component);
-    }
-
-    static ParseResult<ParsedIntentInfo> parseIntentFilter(
-            ParsedMainComponent mainComponent,
-            ParsingPackage pkg, Resources resources, XmlResourceParser parser,
-            boolean visibleToEphemeral, boolean allowGlobs, boolean allowAutoVerify,
-            boolean allowImplicitEphemeralVisibility, boolean failOnNoActions,
-            ParseInput input) throws IOException, XmlPullParserException {
-        ParseResult<ParsedIntentInfo> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
-                mainComponent.getName(), pkg, resources, parser, allowGlobs,
-                allowAutoVerify, input);
-        if (intentResult.isError()) {
-            return input.error(intentResult);
-        }
-
-        ParsedIntentInfo intent = intentResult.getResult();
-        IntentFilter intentFilter = intent.getIntentFilter();
-        int actionCount = intentFilter.countActions();
-        if (actionCount == 0 && failOnNoActions) {
-            Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseApkPath() + " "
-                    + parser.getPositionDescription());
-            // Backward-compat, do not actually fail
-            return input.success(null);
-        }
-
-        int intentVisibility;
-        if (visibleToEphemeral) {
-            intentVisibility = IntentFilter.VISIBILITY_EXPLICIT;
-        } else if (allowImplicitEphemeralVisibility
-                && ComponentParseUtils.isImplicitlyExposedIntent(intent)){
-            intentVisibility = IntentFilter.VISIBILITY_IMPLICIT;
-        } else {
-            intentVisibility = IntentFilter.VISIBILITY_NONE;
-        }
-        intentFilter.setVisibilityToInstantApp(intentVisibility);
-
-        return input.success(intentResult.getResult());
-    }
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
deleted file mode 100644
index 6acdb6e..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.Nullable;
-
-import java.util.Set;
-
-/** @hide */
-public interface ParsedPermission extends ParsedComponent {
-
-    @Nullable
-    String getBackgroundPermission();
-
-    @Nullable
-    String getGroup();
-
-    @Nullable
-    Set<String> getKnownCerts();
-
-    @Nullable
-    ParsedPermissionGroup getParsedPermissionGroup();
-
-    int getProtectionLevel();
-
-    int getRequestRes();
-
-    boolean isTree();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
deleted file mode 100644
index 22aa085..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-/** @hide */
-public interface ParsedPermissionGroup extends ParsedComponent {
-
-    int getBackgroundRequestDetailResourceId();
-
-    int getBackgroundRequestResourceId();
-
-    int getPriority();
-
-    int getRequestDetailResourceId();
-
-    int getRequestRes();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
deleted file mode 100644
index 1fa04cf..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
-        genAidl = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements
-        ParsedPermissionGroup {
-
-    private int requestDetailResourceId;
-    private int backgroundRequestResourceId;
-    private int backgroundRequestDetailResourceId;
-    private int requestRes;
-    private int priority;
-
-    public String toString() {
-        return "PermissionGroup{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + getName() + "}";
-    }
-
-    public ParsedPermissionGroupImpl() {
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedPermissionGroupImpl(
-            int requestDetailResourceId,
-            int backgroundRequestResourceId,
-            int backgroundRequestDetailResourceId,
-            int requestRes,
-            int priority) {
-        this.requestDetailResourceId = requestDetailResourceId;
-        this.backgroundRequestResourceId = backgroundRequestResourceId;
-        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
-        this.requestRes = requestRes;
-        this.priority = priority;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public int getRequestDetailResourceId() {
-        return requestDetailResourceId;
-    }
-
-    @DataClass.Generated.Member
-    public int getBackgroundRequestResourceId() {
-        return backgroundRequestResourceId;
-    }
-
-    @DataClass.Generated.Member
-    public int getBackgroundRequestDetailResourceId() {
-        return backgroundRequestDetailResourceId;
-    }
-
-    @DataClass.Generated.Member
-    public int getRequestRes() {
-        return requestRes;
-    }
-
-    @DataClass.Generated.Member
-    public int getPriority() {
-        return priority;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestDetailResourceId( int value) {
-        requestDetailResourceId = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestResourceId( int value) {
-        backgroundRequestResourceId = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestDetailResourceId( int value) {
-        backgroundRequestDetailResourceId = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestRes( int value) {
-        requestRes = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setPriority( int value) {
-        priority = value;
-        return this;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        super.writeToParcel(dest, flags);
-
-        dest.writeInt(requestDetailResourceId);
-        dest.writeInt(backgroundRequestResourceId);
-        dest.writeInt(backgroundRequestDetailResourceId);
-        dest.writeInt(requestRes);
-        dest.writeInt(priority);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedPermissionGroupImpl(@android.annotation.NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        super(in);
-
-        int _requestDetailResourceId = in.readInt();
-        int _backgroundRequestResourceId = in.readInt();
-        int _backgroundRequestDetailResourceId = in.readInt();
-        int _requestRes = in.readInt();
-        int _priority = in.readInt();
-
-        this.requestDetailResourceId = _requestDetailResourceId;
-        this.backgroundRequestResourceId = _backgroundRequestResourceId;
-        this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
-        this.requestRes = _requestRes;
-        this.priority = _priority;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @android.annotation.NonNull android.os.Parcelable.Creator<ParsedPermissionGroupImpl> CREATOR
-            = new android.os.Parcelable.Creator<ParsedPermissionGroupImpl>() {
-        @Override
-        public ParsedPermissionGroupImpl[] newArray(int size) {
-            return new ParsedPermissionGroupImpl[size];
-        }
-
-        @Override
-        public ParsedPermissionGroupImpl createFromParcel(@android.annotation.NonNull Parcel in) {
-            return new ParsedPermissionGroupImpl(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1627602253988L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java",
-            inputSignatures = "private  int requestDetailResourceId\nprivate  int backgroundRequestResourceId\nprivate  int backgroundRequestDetailResourceId\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\nclass ParsedPermissionGroupImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermissionGroup]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
deleted file mode 100644
index 45038cf..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
-
-import java.util.Locale;
-import java.util.Set;
-
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission {
-
-    private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
-
-    @Nullable
-    private String backgroundPermission;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String group;
-    private int requestRes;
-    private int protectionLevel;
-    private boolean tree;
-    @Nullable
-    private ParsedPermissionGroup parsedPermissionGroup;
-    @Nullable
-    private Set<String> knownCerts;
-
-    @VisibleForTesting
-    public ParsedPermissionImpl() {
-    }
-
-    public ParsedPermissionImpl(ParsedPermission other) {
-        super(other);
-        this.backgroundPermission = other.getBackgroundPermission();
-        this.group = other.getGroup();
-        this.requestRes = other.getRequestRes();
-        this.protectionLevel = other.getProtectionLevel();
-        this.tree = other.isTree();
-        this.parsedPermissionGroup = other.getParsedPermissionGroup();
-    }
-
-    public ParsedPermissionImpl setGroup(String group) {
-        this.group = TextUtils.safeIntern(group);
-        return this;
-    }
-
-    protected void setKnownCert(String knownCert) {
-        // Convert the provided digest to upper case for consistent Set membership
-        // checks when verifying the signing certificate digests of requesting apps.
-        this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
-    }
-
-    protected void setKnownCerts(String[] knownCerts) {
-        this.knownCerts = new ArraySet<>();
-        for (String knownCert : knownCerts) {
-            this.knownCerts.add(knownCert.toUpperCase(Locale.US));
-        }
-    }
-
-    public String toString() {
-        return "Permission{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + getName() + "}";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeString(this.backgroundPermission);
-        dest.writeString(this.group);
-        dest.writeInt(this.requestRes);
-        dest.writeInt(this.protectionLevel);
-        dest.writeBoolean(this.tree);
-        dest.writeParcelable(this.parsedPermissionGroup, flags);
-        sForStringSet.parcel(knownCerts, dest, flags);
-    }
-
-    protected ParsedPermissionImpl(Parcel in) {
-        super(in);
-        // We use the boot classloader for all classes that we load.
-        final ClassLoader boot = Object.class.getClassLoader();
-        this.backgroundPermission = in.readString();
-        this.group = TextUtils.safeIntern(in.readString());
-        this.requestRes = in.readInt();
-        this.protectionLevel = in.readInt();
-        this.tree = in.readBoolean();
-        this.parsedPermissionGroup = in.readParcelable(boot, android.content.pm.parsing.component.ParsedPermissionGroup.class);
-        this.knownCerts = sForStringSet.unparcel(in);
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedPermissionImpl> CREATOR =
-            new Parcelable.Creator<ParsedPermissionImpl>() {
-                @Override
-                public ParsedPermissionImpl createFromParcel(Parcel source) {
-                    return new ParsedPermissionImpl(source);
-                }
-
-                @Override
-                public ParsedPermissionImpl[] newArray(int size) {
-                    return new ParsedPermissionImpl[size];
-                }
-            };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedPermissionImpl(
-            @Nullable String backgroundPermission,
-            @Nullable String group,
-            int requestRes,
-            int protectionLevel,
-            boolean tree,
-            @Nullable ParsedPermissionGroup parsedPermissionGroup,
-            @Nullable Set<String> knownCerts) {
-        this.backgroundPermission = backgroundPermission;
-        this.group = group;
-        this.requestRes = requestRes;
-        this.protectionLevel = protectionLevel;
-        this.tree = tree;
-        this.parsedPermissionGroup = parsedPermissionGroup;
-        this.knownCerts = knownCerts;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getBackgroundPermission() {
-        return backgroundPermission;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getGroup() {
-        return group;
-    }
-
-    @DataClass.Generated.Member
-    public int getRequestRes() {
-        return requestRes;
-    }
-
-    @DataClass.Generated.Member
-    public int getProtectionLevel() {
-        return protectionLevel;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isTree() {
-        return tree;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable ParsedPermissionGroup getParsedPermissionGroup() {
-        return parsedPermissionGroup;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable Set<String> getKnownCerts() {
-        return knownCerts;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedPermissionImpl setBackgroundPermission(@NonNull String value) {
-        backgroundPermission = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedPermissionImpl setRequestRes( int value) {
-        requestRes = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedPermissionImpl setProtectionLevel( int value) {
-        protectionLevel = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedPermissionImpl setTree( boolean value) {
-        tree = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedPermissionImpl setParsedPermissionGroup(@NonNull ParsedPermissionGroup value) {
-        parsedPermissionGroup = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedPermissionImpl setKnownCerts(@NonNull Set<String> value) {
-        knownCerts = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1627598236506L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java",
-            inputSignatures = "private static  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
deleted file mode 100644
index 66e9d3d..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingUtils.NOT_SET;
-
-import android.annotation.NonNull;
-import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.util.Slog;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/** @hide */
-public class ParsedPermissionUtils {
-
-    private static final String TAG = ParsingUtils.TAG;
-
-    @NonNull
-    public static ParseResult<ParsedPermission> parsePermission(ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
-            throws IOException, XmlPullParserException {
-        String packageName = pkg.getPackageName();
-        ParsedPermissionImpl permission = new ParsedPermissionImpl();
-        String tag = "<" + parser.getName() + ">";
-        ParseResult<ParsedPermissionImpl> result;
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
-        try {
-            result = ParsedComponentUtils.parseComponent(
-                    permission, tag, pkg, sa, useRoundIcon, input,
-                    R.styleable.AndroidManifestPermission_banner,
-                    R.styleable.AndroidManifestPermission_description,
-                    R.styleable.AndroidManifestPermission_icon,
-                    R.styleable.AndroidManifestPermission_label,
-                    R.styleable.AndroidManifestPermission_logo,
-                    R.styleable.AndroidManifestPermission_name,
-                    R.styleable.AndroidManifestPermission_roundIcon);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            if (sa.hasValue(
-                    R.styleable.AndroidManifestPermission_backgroundPermission)) {
-                if ("android".equals(packageName)) {
-                    permission.setBackgroundPermission(sa.getNonResourceString(
-                            R.styleable.AndroidManifestPermission_backgroundPermission));
-                } else {
-                    Slog.w(TAG, packageName + " defines a background permission. Only the "
-                            + "'android' package can do that.");
-                }
-            }
-
-            // Note: don't allow this value to be a reference to a resource
-            // that may change.
-            permission.setGroup(sa.getNonResourceString(
-                    R.styleable.AndroidManifestPermission_permissionGroup))
-                    .setRequestRes(sa.getResourceId(
-                            R.styleable.AndroidManifestPermission_request, 0))
-                    .setProtectionLevel(sa.getInt(
-                            R.styleable.AndroidManifestPermission_protectionLevel,
-                            PermissionInfo.PROTECTION_NORMAL))
-                    .setFlags(sa.getInt(
-                            R.styleable.AndroidManifestPermission_permissionFlags, 0));
-
-            final int knownCertsResource = sa.getResourceId(
-                    R.styleable.AndroidManifestPermission_knownCerts, 0);
-            if (knownCertsResource != 0) {
-                // The knownCerts attribute supports both a string array resource as well as a
-                // string resource for the case where the permission should only be granted to a
-                // single known signer.
-                final String resourceType = res.getResourceTypeName(knownCertsResource);
-                if (resourceType.equals("array")) {
-                    final String[] knownCerts = res.getStringArray(knownCertsResource);
-                    if (knownCerts != null) {
-                        permission.setKnownCerts(knownCerts);
-                    }
-                } else {
-                    final String knownCert = res.getString(knownCertsResource);
-                    if (knownCert != null) {
-                        permission.setKnownCert(knownCert);
-                    }
-                }
-                if (permission.getKnownCerts() == null) {
-                    Slog.w(TAG, packageName + " defines a knownSigner permission but"
-                            + " the provided knownCerts resource is null");
-                }
-            } else {
-                // If the knownCerts resource ID is null check if the app specified a string
-                // value for the attribute representing a single trusted signer.
-                final String knownCert = sa.getString(
-                        R.styleable.AndroidManifestPermission_knownCerts);
-                if (knownCert != null) {
-                    permission.setKnownCert(knownCert);
-                }
-            }
-
-            // For now only platform runtime permissions can be restricted
-            if (!isRuntime(permission) || !"android".equals(permission.getPackageName())) {
-                permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_HARD_RESTRICTED);
-                permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_SOFT_RESTRICTED);
-            } else {
-                // The platform does not get to specify conflicting permissions
-                if ((permission.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
-                        && (permission.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
-                    throw new IllegalStateException("Permission cannot be both soft and hard"
-                            + " restricted: " + permission.getName());
-                }
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        permission.setProtectionLevel(
-                PermissionInfo.fixProtectionLevel(permission.getProtectionLevel()));
-
-        final int otherProtectionFlags = getProtectionFlags(permission)
-                & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
-                | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
-        if (otherProtectionFlags != 0
-                && getProtection(permission) != PermissionInfo.PROTECTION_SIGNATURE
-                && getProtection(permission) != PermissionInfo.PROTECTION_INTERNAL) {
-            return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
-                    + " non-runtimeOnly flag but is not based on signature or internal type");
-        }
-
-        result = ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        return input.success(result.getResult());
-    }
-
-    @NonNull
-    public static ParseResult<ParsedPermission> parsePermissionTree(ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
-            throws IOException, XmlPullParserException {
-        ParsedPermissionImpl permission = new ParsedPermissionImpl();
-        String tag = "<" + parser.getName() + ">";
-        ParseResult<ParsedPermissionImpl> result;
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
-        try {
-            result = ParsedComponentUtils.parseComponent(
-                    permission, tag, pkg, sa, useRoundIcon, input,
-                    R.styleable.AndroidManifestPermissionTree_banner,
-                    NOT_SET /*descriptionAttr*/,
-                    R.styleable.AndroidManifestPermissionTree_icon,
-                    R.styleable.AndroidManifestPermissionTree_label,
-                    R.styleable.AndroidManifestPermissionTree_logo,
-                    R.styleable.AndroidManifestPermissionTree_name,
-                    R.styleable.AndroidManifestPermissionTree_roundIcon);
-            if (result.isError()) {
-                return input.error(result);
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        int index = permission.getName().indexOf('.');
-        if (index > 0) {
-            index = permission.getName().indexOf('.', index + 1);
-        }
-        if (index < 0) {
-            return input.error("<permission-tree> name has less than three segments: "
-                    + permission.getName());
-        }
-
-        permission.setProtectionLevel(PermissionInfo.PROTECTION_NORMAL)
-                .setTree(true);
-
-        result = ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        return input.success(result.getResult());
-    }
-
-    @NonNull
-    public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(ParsingPackage pkg,
-            Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
-            throws IOException, XmlPullParserException {
-        ParsedPermissionGroupImpl
-                permissionGroup = new ParsedPermissionGroupImpl();
-        String tag = "<" + parser.getName() + ">";
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
-        try {
-            ParseResult<ParsedPermissionGroupImpl> result = ParsedComponentUtils.parseComponent(
-                    permissionGroup, tag, pkg, sa, useRoundIcon, input,
-                    R.styleable.AndroidManifestPermissionGroup_banner,
-                    R.styleable.AndroidManifestPermissionGroup_description,
-                    R.styleable.AndroidManifestPermissionGroup_icon,
-                    R.styleable.AndroidManifestPermissionGroup_label,
-                    R.styleable.AndroidManifestPermissionGroup_logo,
-                    R.styleable.AndroidManifestPermissionGroup_name,
-                    R.styleable.AndroidManifestPermissionGroup_roundIcon);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            // @formatter:off
-            permissionGroup.setRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0))
-                    .setBackgroundRequestResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0))
-                    .setBackgroundRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0))
-                    .setRequestRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0))
-                    .setPriority(sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0))
-                    .setFlags(sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0));
-            // @formatter:on
-        } finally {
-            sa.recycle();
-        }
-
-        ParseResult<ParsedPermissionGroupImpl> result = ComponentParseUtils.parseAllMetaData(pkg,
-                res, parser, tag, permissionGroup, input);
-        if (result.isError()) {
-            return input.error(result);
-        }
-
-        return input.success(result.getResult());
-    }
-
-    public static boolean isRuntime(@NonNull ParsedPermission permission) {
-        return getProtection(permission) == PermissionInfo.PROTECTION_DANGEROUS;
-    }
-
-    public static boolean isAppOp(@NonNull ParsedPermission permission) {
-        return (permission.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
-    }
-
-    @PermissionInfo.Protection
-    public static int getProtection(@NonNull ParsedPermission permission) {
-        return permission.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE;
-    }
-
-    public static int getProtectionFlags(@NonNull ParsedPermission permission) {
-        return permission.getProtectionLevel() & ~PermissionInfo.PROTECTION_MASK_BASE;
-    }
-
-    public static int calculateFootprint(@NonNull ParsedPermission permission) {
-        int size = permission.getName().length();
-        CharSequence nonLocalizedLabel = permission.getNonLocalizedLabel();
-        if (nonLocalizedLabel != null) {
-            size += nonLocalizedLabel.length();
-        }
-        return size;
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
deleted file mode 100644
index 27a540d..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.content.pm.ApplicationInfo;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-
-import java.util.Set;
-
-/** @hide */
-public interface ParsedProcess extends Parcelable {
-
-    @NonNull
-    Set<String> getDeniedPermissions();
-
-    @ApplicationInfo.GwpAsanMode
-    int getGwpAsanMode();
-
-    @ApplicationInfo.MemtagMode
-    int getMemtagMode();
-
-    @NonNull
-    String getName();
-
-    /**
-     * The app class names in this (potentially shared) process, from a package name to
-     * the application class name.
-     * It's a map, because in shared processes, different packages can have different application
-     * classes.
-     */
-    @NonNull
-    ArrayMap<String, String> getAppClassNamesByPackage();
-
-    @ApplicationInfo.NativeHeapZeroInitialized
-    int getNativeHeapZeroInitialized();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
deleted file mode 100644
index d404ecfd..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static java.util.Collections.emptySet;
-
-import android.annotation.NonNull;
-import android.content.pm.ApplicationInfo;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-
-import java.util.Set;
-
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
-        genBuilder = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedProcessImpl implements ParsedProcess {
-
-    @NonNull
-    private String name;
-
-    /** @see ParsedProcess#getAppClassNamesByPackage() */
-    @NonNull
-    private ArrayMap<String, String> appClassNamesByPackage = ArrayMap.EMPTY;
-
-    @NonNull
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
-    private Set<String> deniedPermissions = emptySet();
-
-    @ApplicationInfo.GwpAsanMode
-    private int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
-    @ApplicationInfo.MemtagMode
-    private int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
-    @ApplicationInfo.NativeHeapZeroInitialized
-    private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
-
-    public ParsedProcessImpl() {
-    }
-
-    public ParsedProcessImpl(@NonNull ParsedProcess other) {
-        name = other.getName();
-        appClassNamesByPackage = (other.getAppClassNamesByPackage().size() == 0)
-                ? ArrayMap.EMPTY : new ArrayMap<>(other.getAppClassNamesByPackage());
-        deniedPermissions = new ArraySet<>(other.getDeniedPermissions());
-        gwpAsanMode = other.getGwpAsanMode();
-        memtagMode = other.getMemtagMode();
-        nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
-    }
-
-    public void addStateFrom(@NonNull ParsedProcess other) {
-        deniedPermissions = CollectionUtils.addAll(deniedPermissions, other.getDeniedPermissions());
-        gwpAsanMode = other.getGwpAsanMode();
-        memtagMode = other.getMemtagMode();
-        nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
-
-        final ArrayMap<String, String> oacn = other.getAppClassNamesByPackage();
-        for (int i = 0; i < oacn.size(); i++) {
-            appClassNamesByPackage.put(oacn.keyAt(i), oacn.valueAt(i));
-        }
-    }
-
-    /**
-     * Sets a custom application name used in this process for a given package.
-     */
-    public void putAppClassNameForPackage(String packageName, String className) {
-        if (appClassNamesByPackage.size() == 0) {
-            appClassNamesByPackage = new ArrayMap<>(4);
-        }
-        appClassNamesByPackage.put(packageName, className);
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    /**
-     * Creates a new ParsedProcessImpl.
-     *
-     */
-    @DataClass.Generated.Member
-    public ParsedProcessImpl(
-            @NonNull String name,
-            @NonNull ArrayMap<String,String> appClassNamesByPackage,
-            @NonNull Set<String> deniedPermissions,
-            @ApplicationInfo.GwpAsanMode int gwpAsanMode,
-            @ApplicationInfo.MemtagMode int memtagMode,
-            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
-        this.name = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.appClassNamesByPackage = appClassNamesByPackage;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, appClassNamesByPackage);
-        this.deniedPermissions = deniedPermissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, deniedPermissions);
-        this.gwpAsanMode = gwpAsanMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
-        this.memtagMode = memtagMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.MemtagMode.class, null, memtagMode);
-        this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return name;
-    }
-
-    /**
-     * @see ParsedProcess#getAppClassNamesByPackage()
-     */
-    @DataClass.Generated.Member
-    public @NonNull ArrayMap<String,String> getAppClassNamesByPackage() {
-        return appClassNamesByPackage;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull Set<String> getDeniedPermissions() {
-        return deniedPermissions;
-    }
-
-    @DataClass.Generated.Member
-    public @ApplicationInfo.GwpAsanMode int getGwpAsanMode() {
-        return gwpAsanMode;
-    }
-
-    @DataClass.Generated.Member
-    public @ApplicationInfo.MemtagMode int getMemtagMode() {
-        return memtagMode;
-    }
-
-    @DataClass.Generated.Member
-    public @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized() {
-        return nativeHeapZeroInitialized;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcessImpl setName(@NonNull String value) {
-        name = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        return this;
-    }
-
-    /**
-     * @see ParsedProcess#getAppClassNamesByPackage()
-     */
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcessImpl setAppClassNamesByPackage(@NonNull ArrayMap<String,String> value) {
-        appClassNamesByPackage = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, appClassNamesByPackage);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcessImpl setDeniedPermissions(@NonNull Set<String> value) {
-        deniedPermissions = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, deniedPermissions);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcessImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
-        gwpAsanMode = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcessImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
-        memtagMode = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.MemtagMode.class, null, memtagMode);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcessImpl setNativeHeapZeroInitialized(@ApplicationInfo.NativeHeapZeroInitialized int value) {
-        nativeHeapZeroInitialized = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<Set<String>> sParcellingForDeniedPermissions =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedStringSet.class);
-    static {
-        if (sParcellingForDeniedPermissions == null) {
-            sParcellingForDeniedPermissions = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedStringSet());
-        }
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeString(name);
-        dest.writeMap(appClassNamesByPackage);
-        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
-        dest.writeInt(gwpAsanMode);
-        dest.writeInt(memtagMode);
-        dest.writeInt(nativeHeapZeroInitialized);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedProcessImpl(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _name = in.readString();
-        ArrayMap<String,String> _appClassNamesByPackage = new ArrayMap();
-        in.readMap(_appClassNamesByPackage, String.class.getClassLoader());
-        Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
-        int _gwpAsanMode = in.readInt();
-        int _memtagMode = in.readInt();
-        int _nativeHeapZeroInitialized = in.readInt();
-
-        this.name = _name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.appClassNamesByPackage = _appClassNamesByPackage;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, appClassNamesByPackage);
-        this.deniedPermissions = _deniedPermissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, deniedPermissions);
-        this.gwpAsanMode = _gwpAsanMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
-        this.memtagMode = _memtagMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.MemtagMode.class, null, memtagMode);
-        this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedProcessImpl> CREATOR
-            = new Parcelable.Creator<ParsedProcessImpl>() {
-        @Override
-        public ParsedProcessImpl[] newArray(int size) {
-            return new ParsedProcessImpl[size];
-        }
-
-        @Override
-        public ParsedProcessImpl createFromParcel(@NonNull Parcel in) {
-            return new ParsedProcessImpl(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1639076603310L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\[email protected](genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
deleted file mode 100644
index 5e4cf66..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.internal.R;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Set;
-
-/** @hide */
-public class ParsedProcessUtils {
-
-    @NonNull
-    private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
-            Resources res, XmlResourceParser parser, ParseInput input)
-            throws IOException, XmlPullParserException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
-        try {
-            String perm = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestDenyPermission_name, 0);
-            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
-                perms = CollectionUtils.add(perms, perm);
-            }
-        } finally {
-            sa.recycle();
-        }
-        XmlUtils.skipCurrentTag(parser);
-        return input.success(perms);
-    }
-
-    @NonNull
-    private static ParseResult<Set<String>> parseAllowPermission(Set<String> perms, Resources res,
-            XmlResourceParser parser, ParseInput input)
-            throws IOException, XmlPullParserException {
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
-        try {
-            String perm = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestAllowPermission_name, 0);
-            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
-                perms = CollectionUtils.remove(perms, perm);
-            }
-        } finally {
-            sa.recycle();
-        }
-        XmlUtils.skipCurrentTag(parser);
-        return input.success(perms);
-    }
-
-    @NonNull
-    private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            ParseInput input) throws IOException, XmlPullParserException {
-        ParsedProcessImpl proc = new ParsedProcessImpl();
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
-        try {
-            if (perms != null) {
-                proc.setDeniedPermissions(new ArraySet<>(perms));
-            }
-
-            String processName = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestProcess_process, 0);
-            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
-                    pkg.getPackageName(), pkg.getPackageName(), processName, flags, separateProcesses,
-                    input);
-            if (processNameResult.isError()) {
-                return input.error(processNameResult);
-            }
-
-            String packageName = pkg.getPackageName();
-            String className = ParsingUtils.buildClassName(packageName,
-                    sa.getNonConfigurationString(R.styleable.AndroidManifestProcess_name, 0));
-
-            proc.setName(processNameResult.getResult());
-            proc.putAppClassNameForPackage(packageName, className);
-            proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
-            proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
-            if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
-                final boolean v = sa.getBoolean(
-                        R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
-                proc.setNativeHeapZeroInitialized(
-                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        int type;
-        final int innerDepth = parser.getDepth();
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            ParseResult<?> result;
-
-            String tagName = parser.getName();
-            switch (tagName) {
-                case "deny-permission":
-                    ParseResult<Set<String>> denyResult = parseDenyPermission(
-                            proc.getDeniedPermissions(), res, parser, input);
-                    result = denyResult;
-                    if (denyResult.isSuccess()) {
-                        proc.setDeniedPermissions(denyResult.getResult());
-                    }
-                    break;
-                case "allow-permission":
-                    ParseResult<Set<String>> allowResult = parseAllowPermission(
-                            proc.getDeniedPermissions(), res, parser, input);
-                    result = allowResult;
-                    if (allowResult.isSuccess()) {
-                        proc.setDeniedPermissions(allowResult.getResult());
-                    }
-                    break;
-                default:
-                    result = ParsingUtils.unknownTag("<process>", pkg, parser, input);
-                    break;
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        return input.success(proc);
-    }
-
-    @NonNull
-    public static ParseResult<ArrayMap<String, ParsedProcess>> parseProcesses(
-            String[] separateProcesses, ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, int flags, ParseInput input)
-            throws IOException, XmlPullParserException {
-        Set<String> deniedPerms = null;
-        ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
-
-        int type;
-        final int innerDepth = parser.getDepth();
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            ParseResult<?> result;
-
-            String tagName = parser.getName();
-            switch (tagName) {
-                case "deny-permission":
-                    ParseResult<Set<String>> denyResult = parseDenyPermission(deniedPerms, res,
-                            parser, input);
-                    result = denyResult;
-                    if (denyResult.isSuccess()) {
-                        deniedPerms = denyResult.getResult();
-                    }
-                    break;
-                case "allow-permission":
-                    ParseResult<Set<String>> allowResult = parseAllowPermission(deniedPerms, res,
-                            parser, input);
-                    result = allowResult;
-                    if (allowResult.isSuccess()) {
-                        deniedPerms = allowResult.getResult();
-                    }
-                    break;
-                case "process":
-                    ParseResult<ParsedProcess> processResult = parseProcess(deniedPerms,
-                            separateProcesses, pkg, res, parser, flags, input);
-                    result = processResult;
-                    if (processResult.isSuccess()) {
-                        ParsedProcess process = processResult.getResult();
-                        if (processes.put(process.getName(), process) != null) {
-                            result = input.error(
-                                    "<process> specified existing name '" + process.getName() + "'");
-                        }
-                    }
-                    break;
-                default:
-                    result = ParsingUtils.unknownTag("<processes>", pkg, parser, input);
-                    break;
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-        }
-
-        return input.success(processes);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProvider.java b/core/java/android/content/pm/parsing/component/ParsedProvider.java
deleted file mode 100644
index 1211ce2..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedProvider.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.Nullable;
-import android.content.pm.PathPermission;
-import android.os.PatternMatcher;
-
-/** @hide **/
-public interface ParsedProvider extends ParsedMainComponent {
-
-    @Nullable
-    String getAuthority();
-
-    int getInitOrder();
-
-    boolean isMultiProcess();
-
-    @Nullable PathPermission[] getPathPermissions();
-
-    @Nullable String getReadPermission();
-
-    @Nullable PatternMatcher[] getUriPermissionPatterns();
-
-    @Nullable String getWritePermission();
-
-    boolean isForceUriPermissions();
-
-    boolean isGrantUriPermissions();
-
-    boolean isSyncable();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java b/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
deleted file mode 100644
index 774c3fc..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.pm.PathPermission;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.PatternMatcher;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-/** @hide **/
-@DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedProviderImpl extends ParsedMainComponentImpl implements ParsedProvider {
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String authority;
-    private boolean syncable;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String readPermission;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String writePermission;
-    private boolean grantUriPermissions;
-    private boolean forceUriPermissions;
-    private boolean multiProcess;
-    private int initOrder;
-    @Nullable
-    private PatternMatcher[] uriPermissionPatterns;
-    @Nullable
-    private PathPermission[] pathPermissions;
-
-    public ParsedProviderImpl(ParsedProvider other) {
-        super(other);
-
-        this.authority = other.getAuthority();
-        this.syncable = other.isSyncable();
-        this.readPermission = other.getReadPermission();
-        this.writePermission = other.getWritePermission();
-        this.grantUriPermissions = other.isGrantUriPermissions();
-        this.forceUriPermissions = other.isForceUriPermissions();
-        this.multiProcess = other.isMultiProcess();
-        this.initOrder = other.getInitOrder();
-        this.uriPermissionPatterns = other.getUriPermissionPatterns();
-        this.pathPermissions = other.getPathPermissions();
-    }
-
-    public ParsedProviderImpl setReadPermission(String readPermission) {
-        // Empty string must be converted to null
-        this.readPermission = TextUtils.isEmpty(readPermission)
-                ? null : readPermission.intern();
-        return this;
-    }
-
-    public ParsedProviderImpl setWritePermission(String writePermission) {
-        // Empty string must be converted to null
-        this.writePermission = TextUtils.isEmpty(writePermission)
-                ? null : writePermission.intern();
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Provider{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeString(this.authority);
-        dest.writeBoolean(this.syncable);
-        sForInternedString.parcel(this.readPermission, dest, flags);
-        sForInternedString.parcel(this.writePermission, dest, flags);
-        dest.writeBoolean(this.grantUriPermissions);
-        dest.writeBoolean(this.forceUriPermissions);
-        dest.writeBoolean(this.multiProcess);
-        dest.writeInt(this.initOrder);
-        dest.writeTypedArray(this.uriPermissionPatterns, flags);
-        dest.writeTypedArray(this.pathPermissions, flags);
-    }
-
-    public ParsedProviderImpl() {
-    }
-
-    protected ParsedProviderImpl(Parcel in) {
-        super(in);
-        this.authority = in.readString();
-        this.syncable = in.readBoolean();
-        this.readPermission = sForInternedString.unparcel(in);
-        this.writePermission = sForInternedString.unparcel(in);
-        this.grantUriPermissions = in.readBoolean();
-        this.forceUriPermissions = in.readBoolean();
-        this.multiProcess = in.readBoolean();
-        this.initOrder = in.readInt();
-        this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
-        this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedProviderImpl> CREATOR =
-            new Parcelable.Creator<ParsedProviderImpl>() {
-                @Override
-                public ParsedProviderImpl createFromParcel(Parcel source) {
-                    return new ParsedProviderImpl(source);
-                }
-
-                @Override
-                public ParsedProviderImpl[] newArray(int size) {
-                    return new ParsedProviderImpl[size];
-                }
-            };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedProviderImpl(
-            @Nullable String authority,
-            boolean syncable,
-            @Nullable String readPermission,
-            @Nullable String writePermission,
-            boolean grantUriPermissions,
-            boolean forceUriPermissions,
-            boolean multiProcess,
-            int initOrder,
-            @Nullable PatternMatcher[] uriPermissionPatterns,
-            @Nullable PathPermission[] pathPermissions) {
-        this.authority = authority;
-        this.syncable = syncable;
-        this.readPermission = readPermission;
-        this.writePermission = writePermission;
-        this.grantUriPermissions = grantUriPermissions;
-        this.forceUriPermissions = forceUriPermissions;
-        this.multiProcess = multiProcess;
-        this.initOrder = initOrder;
-        this.uriPermissionPatterns = uriPermissionPatterns;
-        this.pathPermissions = pathPermissions;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getAuthority() {
-        return authority;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isSyncable() {
-        return syncable;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getReadPermission() {
-        return readPermission;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getWritePermission() {
-        return writePermission;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isGrantUriPermissions() {
-        return grantUriPermissions;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isForceUriPermissions() {
-        return forceUriPermissions;
-    }
-
-    @DataClass.Generated.Member
-    public boolean isMultiProcess() {
-        return multiProcess;
-    }
-
-    @DataClass.Generated.Member
-    public int getInitOrder() {
-        return initOrder;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable PatternMatcher[] getUriPermissionPatterns() {
-        return uriPermissionPatterns;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable PathPermission[] getPathPermissions() {
-        return pathPermissions;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setAuthority(@NonNull String value) {
-        authority = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setSyncable( boolean value) {
-        syncable = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setGrantUriPermissions( boolean value) {
-        grantUriPermissions = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setForceUriPermissions( boolean value) {
-        forceUriPermissions = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setMultiProcess( boolean value) {
-        multiProcess = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setInitOrder( int value) {
-        initOrder = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setUriPermissionPatterns(@NonNull PatternMatcher... value) {
-        uriPermissionPatterns = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setPathPermissions(@NonNull PathPermission... value) {
-        pathPermissions = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1627590522169L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.Nullable android.os.PatternMatcher[] uriPermissionPatterns\nprivate @android.annotation.Nullable android.content.pm.PathPermission[] pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedProviderImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedProvider]\[email protected](genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
deleted file mode 100644
index de9dd44..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageUtils.RIGID_PARSER;
-import static android.content.pm.parsing.component.ComponentParseUtils.flag;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-import android.content.pm.PathPermission;
-import android.content.pm.ProviderInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-import android.os.PatternMatcher;
-import android.util.Slog;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/** @hide */
-public class ParsedProviderUtils {
-
-    private static final String TAG = ParsingUtils.TAG;
-
-    @NonNull
-    public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
-            throws IOException, XmlPullParserException {
-        String authority;
-        boolean visibleToEphemeral;
-
-        final int targetSdkVersion = pkg.getTargetSdkVersion();
-        final String packageName = pkg.getPackageName();
-        final ParsedProviderImpl provider = new ParsedProviderImpl();
-        final String tag = parser.getName();
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
-        try {
-            ParseResult<ParsedProviderImpl> result =
-                    ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
-                            pkg, sa, flags, useRoundIcon, defaultSplitName, input,
-                            R.styleable.AndroidManifestProvider_banner,
-                            R.styleable.AndroidManifestProvider_description,
-                            R.styleable.AndroidManifestProvider_directBootAware,
-                            R.styleable.AndroidManifestProvider_enabled,
-                            R.styleable.AndroidManifestProvider_icon,
-                            R.styleable.AndroidManifestProvider_label,
-                            R.styleable.AndroidManifestProvider_logo,
-                            R.styleable.AndroidManifestProvider_name,
-                            R.styleable.AndroidManifestProvider_process,
-                            R.styleable.AndroidManifestProvider_roundIcon,
-                            R.styleable.AndroidManifestProvider_splitName,
-                            R.styleable.AndroidManifestProvider_attributionTags);
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);
-
-            // For compatibility, applications targeting API level 16 or lower
-            // should have their content providers exported by default, unless they
-            // specify otherwise.
-            provider.setSyncable(sa.getBoolean(
-                    R.styleable.AndroidManifestProvider_syncable, false))
-                    .setExported(sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
-                            targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1));
-
-            String permission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestProvider_permission, 0);
-            String readPermission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestProvider_readPermission, 0);
-            if (readPermission == null) {
-                readPermission = permission;
-            }
-            if (readPermission == null) {
-                provider.setReadPermission(pkg.getPermission());
-            } else {
-                provider.setReadPermission(readPermission);
-            }
-            String writePermission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestProvider_writePermission, 0);
-            if (writePermission == null) {
-                writePermission = permission;
-            }
-            if (writePermission == null) {
-                provider.setWritePermission(pkg.getPermission());
-            } else {
-                provider.setWritePermission(writePermission);
-            }
-
-            provider.setGrantUriPermissions(
-                    sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false))
-                    .setForceUriPermissions(
-                            sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions,
-                                    false))
-                    .setMultiProcess(
-                            sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false))
-                    .setInitOrder(sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0))
-                    .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER,
-                            R.styleable.AndroidManifestProvider_singleUser, sa));
-
-            visibleToEphemeral = sa.getBoolean(
-                    R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
-            if (visibleToEphemeral) {
-                provider.setFlags(provider.getFlags() | ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP);
-                pkg.setVisibleToInstantApps(true);
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        if (pkg.isCantSaveState()) {
-            // A heavy-weight application can not have providers in its main process
-            if (Objects.equals(provider.getProcessName(), packageName)) {
-                return input.error("Heavy-weight applications can not have providers"
-                        + " in main process");
-            }
-        }
-
-        if (authority == null) {
-            return input.error("<provider> does not include authorities attribute");
-        }
-        if (authority.length() <= 0) {
-            return input.error("<provider> has empty authorities attribute");
-        }
-        provider.setAuthority(authority);
-
-        return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input);
-    }
-
-    @NonNull
-    private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
-            Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
-            ParsedProviderImpl provider, ParseInput input)
-            throws XmlPullParserException, IOException {
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            String name = parser.getName();
-            final ParseResult result;
-            switch (name) {
-                case "intent-filter":
-                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
-                            .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
-                                    true /*allowGlobs*/, false /*allowAutoVerify*/,
-                                    false /*allowImplicitEphemeralVisibility*/,
-                                    false /*failOnNoActions*/, input);
-                    result = intentResult;
-                    if (intentResult.isSuccess()) {
-                        ParsedIntentInfo intent = intentResult.getResult();
-                        IntentFilter intentFilter = intent.getIntentFilter();
-                        provider.setOrder(Math.max(intentFilter.getOrder(), provider.getOrder()));
-                        provider.addIntent(intent);
-                    }
-                    break;
-                case "meta-data":
-                    result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
-                    break;
-                case "property":
-                    result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input);
-                    break;
-                case "grant-uri-permission": {
-                    result = parseGrantUriPermission(provider, pkg, res, parser, input);
-                    break;
-                }
-                case "path-permission": {
-                    result = parsePathPermission(provider, pkg, res, parser, input);
-                    break;
-                }
-                default:
-                    result = ParsingUtils.unknownTag(tag, pkg, parser, input);
-                    break;
-            }
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-        }
-
-        return input.success(provider);
-    }
-
-    @NonNull
-    private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProviderImpl provider,
-            ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
-        TypedArray sa = resources.obtainAttributes(parser,
-                R.styleable.AndroidManifestGrantUriPermission);
-        try {
-            String name = parser.getName();
-            // Pattern has priority over pre/suffix over literal path
-            PatternMatcher pa = null;
-            String str = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestGrantUriPermission_pathAdvancedPattern, 0);
-            if (str != null) {
-                pa = new PatternMatcher(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
-            } else {
-                str = sa.getNonConfigurationString(
-                        R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
-                if (str != null) {
-                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
-                } else {
-                    str = sa.getNonConfigurationString(
-                            R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
-                    if (str != null) {
-                        pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
-                    } else {
-                        str = sa.getNonConfigurationString(
-                                R.styleable.AndroidManifestGrantUriPermission_pathSuffix, 0);
-                        if (str != null) {
-                            pa = new PatternMatcher(str, PatternMatcher.PATTERN_SUFFIX);
-                        } else {
-                            str = sa.getNonConfigurationString(
-                                    R.styleable.AndroidManifestGrantUriPermission_path, 0);
-                            if (str != null) {
-                                pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (pa != null) {
-                if (provider.getUriPermissionPatterns() == null) {
-                    provider.setUriPermissionPatterns(new PatternMatcher[1]);
-                    provider.getUriPermissionPatterns()[0] = pa;
-                } else {
-                    final int N = provider.getUriPermissionPatterns().length;
-                    PatternMatcher[] newp = new PatternMatcher[N + 1];
-                    System.arraycopy(provider.getUriPermissionPatterns(), 0, newp, 0, N);
-                    newp[N] = pa;
-                    provider.setUriPermissionPatterns(newp);
-                }
-                provider.setGrantUriPermissions(true);
-            } else {
-                if (RIGID_PARSER) {
-                    return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
-                }
-
-                Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at "
-                        + pkg.getBaseApkPath() + " " + parser.getPositionDescription());
-            }
-
-            return input.success(provider);
-        } finally {
-            sa.recycle();
-        }
-    }
-
-    @NonNull
-    private static ParseResult<ParsedProvider> parsePathPermission(ParsedProviderImpl provider,
-            ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
-        TypedArray sa = resources.obtainAttributes(parser,
-                R.styleable.AndroidManifestPathPermission);
-        try {
-            String name = parser.getName();
-
-            String permission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestPathPermission_permission, 0);
-            String readPermission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestPathPermission_readPermission, 0);
-            if (readPermission == null) {
-                readPermission = permission;
-            }
-            String writePermission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestPathPermission_writePermission, 0);
-            if (writePermission == null) {
-                writePermission = permission;
-            }
-
-            boolean havePerm = false;
-            if (readPermission != null) {
-                readPermission = readPermission.intern();
-                havePerm = true;
-            }
-            if (writePermission != null) {
-                writePermission = writePermission.intern();
-                havePerm = true;
-            }
-
-            if (!havePerm) {
-                if (RIGID_PARSER) {
-                    return input.error(
-                            "No readPermission or writePermission for <path-permission>");
-                }
-                Slog.w(TAG, "No readPermission or writePermission for <path-permission>: "
-                        + name + " at " + pkg.getBaseApkPath() + " "
-                        + parser.getPositionDescription());
-                return input.success(provider);
-            }
-
-            // Advanced has priority over simply over prefix over literal
-            PathPermission pa = null;
-            String path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
-            if (path != null) {
-                pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission,
-                        writePermission);
-            } else {
-                path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathPattern, 0);
-                if (path != null) {
-                    pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB,
-                            readPermission, writePermission);
-                } else {
-                    path = sa.getNonConfigurationString(
-                            R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
-                    if (path != null) {
-                        pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission,
-                                writePermission);
-                    } else {
-                        path = sa.getNonConfigurationString(
-                                R.styleable.AndroidManifestPathPermission_pathSuffix, 0);
-                        if (path != null) {
-                            pa = new PathPermission(path, PatternMatcher.PATTERN_SUFFIX,
-                                    readPermission, writePermission);
-                        } else {
-                            path = sa.getNonConfigurationString(
-                                    R.styleable.AndroidManifestPathPermission_path, 0);
-                            if (path != null) {
-                                pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
-                                        readPermission, writePermission);
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (pa != null) {
-                if (provider.getPathPermissions() == null) {
-                    provider.setPathPermissions(new PathPermission[1]);
-                    provider.getPathPermissions()[0] = pa;
-                } else {
-                    final int N = provider.getPathPermissions().length;
-                    PathPermission[] newp = new PathPermission[N + 1];
-                    System.arraycopy(provider.getPathPermissions(), 0, newp, 0, N);
-                    newp[N] = pa;
-                    provider.setPathPermissions(newp);
-                }
-            } else {
-                if (RIGID_PARSER) {
-                    return input.error(
-                            "No path, pathPrefix, or pathPattern for <path-permission>");
-                }
-
-                Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
-                        + name + " at " + pkg.getBaseApkPath()
-                        + " "
-                        + parser.getPositionDescription());
-            }
-
-            return input.success(provider);
-        } finally {
-            sa.recycle();
-        }
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedService.java b/core/java/android/content/pm/parsing/component/ParsedService.java
deleted file mode 100644
index 6736afa..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedService.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.Nullable;
-
-/** @hide **/
-public interface ParsedService extends ParsedMainComponent {
-
-    int getForegroundServiceType();
-
-    @Nullable
-    String getPermission();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java b/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
deleted file mode 100644
index a85fb5c..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-/** @hide **/
-@DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedServiceImpl extends ParsedMainComponentImpl implements ParsedService {
-
-    private int foregroundServiceType;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String permission;
-
-    public ParsedServiceImpl(ParsedServiceImpl other) {
-        super(other);
-        this.foregroundServiceType = other.foregroundServiceType;
-        this.permission = other.permission;
-    }
-
-    public ParsedMainComponent setPermission(String permission) {
-        // Empty string must be converted to null
-        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Service{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(this.foregroundServiceType);
-        sForInternedString.parcel(this.permission, dest, flags);
-    }
-
-    public ParsedServiceImpl() {
-    }
-
-    protected ParsedServiceImpl(Parcel in) {
-        super(in);
-        this.foregroundServiceType = in.readInt();
-        this.permission = sForInternedString.unparcel(in);
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedServiceImpl> CREATOR =
-            new Parcelable.Creator<ParsedServiceImpl>() {
-                @Override
-                public ParsedServiceImpl createFromParcel(Parcel source) {
-                    return new ParsedServiceImpl(source);
-                }
-
-                @Override
-                public ParsedServiceImpl[] newArray(int size) {
-                    return new ParsedServiceImpl[size];
-                }
-            };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedServiceImpl(
-            int foregroundServiceType,
-            @Nullable String permission) {
-        this.foregroundServiceType = foregroundServiceType;
-        this.permission = permission;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public int getForegroundServiceType() {
-        return foregroundServiceType;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable String getPermission() {
-        return permission;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedServiceImpl setForegroundServiceType( int value) {
-        foregroundServiceType = value;
-        return this;
-    }
-
-    @DataClass.Generated(
-            time = 1627592563052L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java",
-            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService]\[email protected](genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
deleted file mode 100644
index d27a0ed..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import static android.content.pm.parsing.component.ComponentParseUtils.flag;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseInput.DeferredError;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/** @hide */
-public class ParsedServiceUtils {
-
-    @NonNull
-    public static ParseResult<ParsedService> parseService(String[] separateProcesses,
-            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
-            throws XmlPullParserException, IOException {
-        boolean visibleToEphemeral;
-        boolean setExported;
-
-        final String packageName = pkg.getPackageName();
-        final ParsedServiceImpl service = new ParsedServiceImpl();
-        String tag = parser.getName();
-
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
-        try {
-            ParseResult<ParsedServiceImpl> result = ParsedMainComponentUtils.parseMainComponent(
-                    service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, defaultSplitName,
-                    input,
-                    R.styleable.AndroidManifestService_banner,
-                    R.styleable.AndroidManifestService_description,
-                    R.styleable.AndroidManifestService_directBootAware,
-                    R.styleable.AndroidManifestService_enabled,
-                    R.styleable.AndroidManifestService_icon,
-                    R.styleable.AndroidManifestService_label,
-                    R.styleable.AndroidManifestService_logo,
-                    R.styleable.AndroidManifestService_name,
-                    R.styleable.AndroidManifestService_process,
-                    R.styleable.AndroidManifestService_roundIcon,
-                    R.styleable.AndroidManifestService_splitName,
-                    R.styleable.AndroidManifestService_attributionTags
-            );
-
-            if (result.isError()) {
-                return input.error(result);
-            }
-
-            setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
-            if (setExported) {
-                service.setExported(sa.getBoolean(R.styleable.AndroidManifestService_exported,
-                        false));
-            }
-
-            String permission = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestService_permission, 0);
-            service.setPermission(permission != null ? permission : pkg.getPermission());
-
-            service.setForegroundServiceType(sa.getInt(
-                    R.styleable.AndroidManifestService_foregroundServiceType,
-                    ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE))
-                    .setFlags(service.getFlags() | (flag(ServiceInfo.FLAG_STOP_WITH_TASK,
-                            R.styleable.AndroidManifestService_stopWithTask, sa)
-                            | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
-                            R.styleable.AndroidManifestService_isolatedProcess, sa)
-                            | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
-                            R.styleable.AndroidManifestService_externalService, sa)
-                            | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
-                            R.styleable.AndroidManifestService_useAppZygote, sa)
-                            | flag(ServiceInfo.FLAG_SINGLE_USER,
-                            R.styleable.AndroidManifestService_singleUser, sa)));
-
-            visibleToEphemeral = sa.getBoolean(
-                    R.styleable.AndroidManifestService_visibleToInstantApps, false);
-            if (visibleToEphemeral) {
-                service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
-                pkg.setVisibleToInstantApps(true);
-            }
-        } finally {
-            sa.recycle();
-        }
-
-        if (pkg.isCantSaveState()) {
-            // A heavy-weight application can not have services in its main process
-            // We can do direct compare because we intern all strings.
-            if (Objects.equals(service.getProcessName(), packageName)) {
-                return input.error("Heavy-weight applications can not have services "
-                        + "in main process");
-            }
-        }
-        final int depth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > depth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final ParseResult parseResult;
-            switch (parser.getName()) {
-                case "intent-filter":
-                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
-                            .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
-                                    true /*allowGlobs*/, false /*allowAutoVerify*/,
-                                    false /*allowImplicitEphemeralVisibility*/,
-                                    false /*failOnNoActions*/, input);
-                    parseResult = intentResult;
-                    if (intentResult.isSuccess()) {
-                        ParsedIntentInfo intent = intentResult.getResult();
-                        IntentFilter intentFilter = intent.getIntentFilter();
-                        service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder()));
-                        service.addIntent(intent);
-                    }
-                    break;
-                case "meta-data":
-                    parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
-                    break;
-                case "property":
-                    parseResult =
-                            ParsedComponentUtils.addProperty(service, pkg, res, parser, input);
-                    break;
-                default:
-                    parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
-                    break;
-            }
-
-            if (parseResult.isError()) {
-                return input.error(parseResult);
-            }
-        }
-
-        if (!setExported) {
-            boolean hasIntentFilters = service.getIntents().size() > 0;
-            if (hasIntentFilters) {
-                final ParseResult exportedCheckResult = input.deferError(
-                        service.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
-                        + " and above) requires that an explicit value for android:exported be"
-                        + " defined when intent filters are present",
-                        DeferredError.MISSING_EXPORTED_FLAG);
-                if (exportedCheckResult.isError()) {
-                    return input.error(exportedCheckResult);
-                }
-            }
-            service.setExported(hasIntentFilters);
-        }
-
-        return input.success(service);
-    }
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
deleted file mode 100644
index e2f5f14..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.content.pm.PackageInfo;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A {@link android.R.styleable#AndroidManifestUsesPermission
- * &lt;uses-permission&gt;} tag parsed from the manifest.
- *
- * @hide
- */
-public interface ParsedUsesPermission extends Parcelable {
-
-    /**
-     * Strong assertion by a developer that they will never use this permission to derive the
-     * physical location of the device, regardless of ACCESS_FINE_LOCATION and/or
-     * ACCESS_COARSE_LOCATION being granted.
-     */
-    int FLAG_NEVER_FOR_LOCATION = PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
-
-    /**
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
-            FLAG_NEVER_FOR_LOCATION
-    })
-    @interface UsesPermissionFlags {}
-
-    @NonNull
-    String getName();
-
-    @UsesPermissionFlags
-    int getUsesPermissionFlags();
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
deleted file mode 100644
index d3c7afb..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-
-/**
- * A {@link android.R.styleable#AndroidManifestUsesPermission
- * &lt;uses-permission&gt;} tag parsed from the manifest.
- *
- * @hide
- */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
-        genAidl = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedUsesPermissionImpl implements ParsedUsesPermission {
-
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
-    @NonNull
-    private String name;
-
-    @ParsedUsesPermission.UsesPermissionFlags
-    private int usesPermissionFlags;
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedUsesPermissionImpl(
-            @NonNull String name,
-            @ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags) {
-        this.name = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.usesPermissionFlags = usesPermissionFlags;
-        com.android.internal.util.AnnotationValidations.validate(
-                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return name;
-    }
-
-    @DataClass.Generated.Member
-    public @ParsedUsesPermission.UsesPermissionFlags int getUsesPermissionFlags() {
-        return usesPermissionFlags;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedUsesPermissionImpl setName(@NonNull String value) {
-        name = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedUsesPermissionImpl setUsesPermissionFlags(@ParsedUsesPermission.UsesPermissionFlags int value) {
-        usesPermissionFlags = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<String> sParcellingForName =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedString.class);
-    static {
-        if (sParcellingForName == null) {
-            sParcellingForName = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedString());
-        }
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        sParcellingForName.parcel(name, dest, flags);
-        dest.writeInt(usesPermissionFlags);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedUsesPermissionImpl(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _name = sParcellingForName.unparcel(in);
-        int _usesPermissionFlags = in.readInt();
-
-        this.name = _name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.usesPermissionFlags = _usesPermissionFlags;
-        com.android.internal.util.AnnotationValidations.validate(
-                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedUsesPermissionImpl> CREATOR
-            = new Parcelable.Creator<ParsedUsesPermissionImpl>() {
-        @Override
-        public ParsedUsesPermissionImpl[] newArray(int size) {
-            return new ParsedUsesPermissionImpl[size];
-        }
-
-        @Override
-        public ParsedUsesPermissionImpl createFromParcel(@NonNull Parcel in) {
-            return new ParsedUsesPermissionImpl(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1627674645598L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java",
-            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 1a3fc85..c323704 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -16,14 +16,11 @@
 
 package android.content.pm.parsing.result;
 
-import static android.content.pm.parsing.ParsingUtils.NOT_SET;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.parsing.ParsingUtils;
 import android.os.ServiceManager;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -35,7 +32,7 @@
 /** @hide */
 public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
 
-    private static final String TAG = ParsingUtils.TAG;
+    private static final String TAG = "ParseTypeImpl";
 
     public static final boolean DEBUG_FILL_STACK_TRACE = false;
 
@@ -64,7 +61,7 @@
     private ArrayMap<Long, String> mDeferredErrors = null;
 
     private String mPackageName;
-    private int mTargetSdkVersion = NOT_SET;
+    private int mTargetSdkVersion = -1;
 
     /**
      * Specifically for {@link PackageManager#getPackageArchiveInfo(String, int)} where
@@ -121,7 +118,7 @@
             // how many APKs they're going through.
             mDeferredErrors.erase();
         }
-        mTargetSdkVersion = NOT_SET;
+        mTargetSdkVersion = -1;
         return this;
     }
 
@@ -141,7 +138,7 @@
         if (DEBUG_THROW_ALL_ERRORS) {
             return error(parseError);
         }
-        if (mTargetSdkVersion != NOT_SET) {
+        if (mTargetSdkVersion != -1) {
             if (mDeferredErrors != null && mDeferredErrors.containsKey(deferredError)) {
                 // If the map already contains the key, that means it's already been checked and
                 // found to be disabled. Otherwise it would've failed when mTargetSdkVersion was
diff --git a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
deleted file mode 100644
index b70353a4..0000000
--- a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.permission;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-
-import com.android.internal.util.DataClass;
-
-/**
- * Implements compatibility support for permissions, and old applications
- * will be automatically granted it.
- *
- * Compatibility permissions are permissions that are automatically granted to
- * packages that target an SDK prior to when the permission was introduced.
- * Sometimes the platform makes breaking behaviour changes and hides the legacy
- * behaviour behind a permission. In these instances, we ensure applications
- * targeting older platform versions are implicitly granted the correct set of
- * permissions.
- *
- * @hide
- */
-@DataClass(genGetters = true, genBuilder = false)
-public class CompatibilityPermissionInfo {
-
-    @NonNull
-    private final String mName;
-    private final int mSdkVersion;
-
-    /**
-     * List of new permissions that have been added since 1.0.
-     *
-     * NOTE: These must be declared in SDK version order, with permissions
-     * added to newer SDKs appearing before those added to older SDKs.
-     *
-     * @hide
-     */
-    public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
-            new CompatibilityPermissionInfo[]{
-                    new CompatibilityPermissionInfo(Manifest.permission.POST_NOTIFICATIONS,
-                            android.os.Build.VERSION_CODES.TIRAMISU),
-                    new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
-                            android.os.Build.VERSION_CODES.DONUT),
-                    new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
-                            android.os.Build.VERSION_CODES.DONUT)
-            };
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public CompatibilityPermissionInfo(
-            @NonNull String name,
-            int sdkVersion) {
-        this.mName = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mName);
-        this.mSdkVersion = sdkVersion;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return mName;
-    }
-
-    @DataClass.Generated.Member
-    public int getSdkVersion() {
-        return mSdkVersion;
-    }
-
-    @DataClass.Generated(
-            time = 1627674427184L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final  int mSdkVersion\npublic static final  android.content.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\[email protected](genGetters=true, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/pkg/FrameworkPackageUserState.java b/core/java/android/content/pm/pkg/FrameworkPackageUserState.java
index 0daf6cf..bac29b4 100644
--- a/core/java/android/content/pm/pkg/FrameworkPackageUserState.java
+++ b/core/java/android/content/pm/pkg/FrameworkPackageUserState.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,8 +31,9 @@
  * See the services variant for method documentation.
  *
  * @hide
- * @deprecated Unless you know exactly what you're doing, you probably want the services variant.
+ * @deprecated Unused by framework.
  */
+@Deprecated
 public interface FrameworkPackageUserState {
 
     FrameworkPackageUserState DEFAULT = new FrameworkPackageUserStateDefault();
diff --git a/core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java b/core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java
index 27255da..3590620 100644
--- a/core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java
+++ b/core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,7 +25,11 @@
 import java.util.Map;
 import java.util.Set;
 
-/** @hide */
+/**
+ * @hide
+ * @deprecated Unused by framework.
+ */
+@Deprecated
 class FrameworkPackageUserStateDefault implements FrameworkPackageUserState {
 
     @Override
diff --git a/core/java/android/content/pm/pkg/PackageUserStateUtils.java b/core/java/android/content/pm/pkg/PackageUserStateUtils.java
deleted file mode 100644
index 468bff1..0000000
--- a/core/java/android/content/pm/pkg/PackageUserStateUtils.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.pkg;
-
-import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
-import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
-
-import android.annotation.NonNull;
-import android.content.pm.ComponentInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.parsing.ParsingPackageRead;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.os.Debug;
-import android.util.DebugUtils;
-import android.util.Slog;
-
-/** @hide */
-public class PackageUserStateUtils {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "PackageUserStateUtils";
-
-    public static boolean isMatch(@NonNull FrameworkPackageUserState state,
-            ComponentInfo componentInfo, long flags) {
-        return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
-                componentInfo.applicationInfo.enabled, componentInfo.enabled,
-                componentInfo.directBootAware, componentInfo.name, flags);
-    }
-
-    public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
-            boolean isPackageEnabled, ParsedMainComponent component, long flags) {
-        return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
-                component.isDirectBootAware(), component.getName(), flags);
-    }
-
-    /**
-     * Test if the given component is considered installed, 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>
-     */
-    public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
-            boolean isPackageEnabled, boolean isComponentEnabled,
-            boolean isComponentDirectBootAware, String componentName, long flags) {
-        final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
-        if (!isAvailable(state, flags) && !(isSystem && matchUninstalled)) {
-            return reportIfDebug(false, flags);
-        }
-
-        if (!isEnabled(state, isPackageEnabled, isComponentEnabled, componentName, flags)) {
-            return reportIfDebug(false, flags);
-        }
-
-        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);
-    }
-
-    public static boolean isAvailable(@NonNull FrameworkPackageUserState state, long flags) {
-        // True if it is installed for this user and it is not hidden. If it is hidden,
-        // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
-        final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
-        final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
-        return matchAnyUser
-                || (state.isInstalled()
-                && (!state.isHidden() || matchUninstalled));
-    }
-
-    public 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;
-    }
-
-    public static boolean isEnabled(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
-            long flags) {
-        return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
-                componentInfo.name, flags);
-    }
-
-    public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
-            ParsedMainComponent parsedComponent, long flags) {
-        return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
-                parsedComponent.getName(), flags);
-    }
-
-    /**
-     * Test if the given component is considered enabled.
-     */
-    public static boolean isEnabled(@NonNull FrameworkPackageUserState state,
-            boolean isPackageEnabled, boolean isComponentEnabled, String componentName,
-            long flags) {
-        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
-            return true;
-        }
-
-        // First check if the overall package is disabled; if the package is
-        // enabled then fall through to check specific component
-        switch (state.getEnabledState()) {
-            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
-            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
-                return false;
-            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
-                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
-                    return false;
-                }
-                // fallthrough
-            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
-                if (!isPackageEnabled) {
-                    return false;
-                }
-                // fallthrough
-            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
-                break;
-        }
-
-        // Check if component has explicit state before falling through to
-        // the manifest default
-        if (state.isComponentEnabled(componentName)) {
-            return true;
-        } else if (state.isComponentDisabled(componentName)) {
-            return false;
-        }
-
-        return isComponentEnabled;
-    }
-
-    public static boolean isPackageEnabled(@NonNull FrameworkPackageUserState state,
-            @NonNull ParsingPackageRead pkg) {
-        switch (state.getEnabledState()) {
-            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
-                return true;
-            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
-            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
-            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
-                return false;
-            default:
-            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
-                return pkg.isEnabled();
-        }
-    }
-}
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
deleted file mode 100644
index 47cf28b..0000000
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm.split;
-
-import android.content.pm.parsing.ApkLiteParseUtils;
-import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
-import android.content.res.ApkAssets;
-import android.content.res.AssetManager;
-import android.os.Build;
-
-import com.android.internal.util.ArrayUtils;
-
-import libcore.io.IoUtils;
-
-import java.io.IOException;
-
-/**
- * Loads the base and split APKs into a single AssetManager.
- * @hide
- */
-public class DefaultSplitAssetLoader implements SplitAssetLoader {
-    private final String mBaseApkPath;
-    private final String[] mSplitApkPaths;
-    private final @ParseFlags int mFlags;
-    private AssetManager mCachedAssetManager;
-
-    private ApkAssets mBaseApkAssets;
-
-    public DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
-        mBaseApkPath = pkg.getBaseApkPath();
-        mSplitApkPaths = pkg.getSplitApkPaths();
-        mFlags = flags;
-    }
-
-    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
-            throws IllegalArgumentException {
-        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
-                && !ApkLiteParseUtils.isApkPath(path)) {
-            throw new IllegalArgumentException("Invalid package file: " + path);
-        }
-
-        try {
-            return ApkAssets.loadFromPath(path);
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
-        }
-    }
-
-    @Override
-    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
-        if (mCachedAssetManager != null) {
-            return mCachedAssetManager;
-        }
-
-        ApkAssets[] apkAssets = new ApkAssets[(mSplitApkPaths != null
-                ? mSplitApkPaths.length : 0) + 1];
-
-        // Load the base.
-        int splitIdx = 0;
-        apkAssets[splitIdx++] = mBaseApkAssets = loadApkAssets(mBaseApkPath, mFlags);
-
-        // Load any splits.
-        if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
-            for (String apkPath : mSplitApkPaths) {
-                apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
-            }
-        }
-
-        AssetManager assets = new AssetManager();
-        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                Build.VERSION.RESOURCES_SDK_INT);
-        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
-
-        mCachedAssetManager = assets;
-        return mCachedAssetManager;
-    }
-
-    @Override
-    public AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException {
-        return getBaseAssetManager();
-    }
-
-    @Override
-    public ApkAssets getBaseApkAssets() {
-        return mBaseApkAssets;
-    }
-
-    @Override
-    public void close() throws Exception {
-        IoUtils.closeQuietly(mCachedAssetManager);
-    }
-}
diff --git a/core/java/android/content/pm/split/OWNERS b/core/java/android/content/pm/split/OWNERS
deleted file mode 100644
index 3d126d2..0000000
--- a/core/java/android/content/pm/split/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 36137
-
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
deleted file mode 100644
index a0c3f75..0000000
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm.split;
-
-import android.annotation.NonNull;
-import android.content.pm.parsing.ApkLiteParseUtils;
-import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
-import android.content.res.ApkAssets;
-import android.content.res.AssetManager;
-import android.os.Build;
-import android.util.SparseArray;
-
-import libcore.io.IoUtils;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
- * is to be used when an application opts-in to isolated split loading.
- * @hide
- */
-public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArgumentException>
-        implements SplitAssetLoader {
-    private final String[] mSplitPaths;
-    private final @ParseFlags int mFlags;
-    private final ApkAssets[][] mCachedSplitApks;
-    private final AssetManager[] mCachedAssetManagers;
-
-    public SplitAssetDependencyLoader(PackageLite pkg,
-            SparseArray<int[]> dependencies, @ParseFlags int flags) {
-        super(dependencies);
-
-        // The base is inserted into index 0, so we need to shift all the splits by 1.
-        mSplitPaths = new String[pkg.getSplitApkPaths().length + 1];
-        mSplitPaths[0] = pkg.getBaseApkPath();
-        System.arraycopy(pkg.getSplitApkPaths(), 0, mSplitPaths, 1, pkg.getSplitApkPaths().length);
-
-        mFlags = flags;
-        mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
-        mCachedAssetManagers = new AssetManager[mSplitPaths.length];
-    }
-
-    @Override
-    protected boolean isSplitCached(int splitIdx) {
-        return mCachedAssetManagers[splitIdx] != null;
-    }
-
-    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
-            throws IllegalArgumentException {
-        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
-                && !ApkLiteParseUtils.isApkPath(path)) {
-            throw new IllegalArgumentException("Invalid package file: " + path);
-        }
-
-        try {
-            return ApkAssets.loadFromPath(path);
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
-        }
-    }
-
-    private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
-        final AssetManager assets = new AssetManager();
-        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                Build.VERSION.RESOURCES_SDK_INT);
-        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
-        return assets;
-    }
-
-    @Override
-    protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
-            int parentSplitIdx) throws IllegalArgumentException {
-        final ArrayList<ApkAssets> assets = new ArrayList<>();
-
-        // Include parent ApkAssets.
-        if (parentSplitIdx >= 0) {
-            Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
-        }
-
-        // Include this ApkAssets.
-        assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
-
-        // Load and include all config splits for this feature.
-        for (int configSplitIdx : configSplitIndices) {
-            assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
-        }
-
-        // Cache the results.
-        mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
-        mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
-    }
-
-    @Override
-    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
-        loadDependenciesForSplit(0);
-        return mCachedAssetManagers[0];
-    }
-
-    @Override
-    public AssetManager getSplitAssetManager(int idx) throws IllegalArgumentException {
-        // Since we insert the base at position 0, and ParsingPackageUtils keeps splits separate
-        // from the base, we need to adjust the index.
-        loadDependenciesForSplit(idx + 1);
-        return mCachedAssetManagers[idx + 1];
-    }
-
-    @Override
-    public ApkAssets getBaseApkAssets() {
-        return mCachedSplitApks[0][0];
-    }
-
-    @Override
-    public void close() throws Exception {
-        for (AssetManager assets : mCachedAssetManagers) {
-            IoUtils.closeQuietly(assets);
-        }
-    }
-}
diff --git a/core/java/android/content/pm/split/SplitAssetLoader.java b/core/java/android/content/pm/split/SplitAssetLoader.java
deleted file mode 100644
index d314e06..0000000
--- a/core/java/android/content/pm/split/SplitAssetLoader.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm.split;
-
-import android.content.res.ApkAssets;
-import android.content.res.AssetManager;
-
-/**
- * Simple interface for loading base Assets and Splits. Used by ParsingPackageUtils when parsing
- * split APKs.
- *
- * @hide
- */
-public interface SplitAssetLoader extends AutoCloseable {
-    AssetManager getBaseAssetManager() throws IllegalArgumentException;
-    AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException;
-
-    ApkAssets getBaseApkAssets();
-}
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
index 535afd36..3703f2e2 100644
--- a/core/java/android/content/res/TEST_MAPPING
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -1,4 +1,12 @@
 {
+  "imports": [
+    {
+      "path": "frameworks/base/core/tests/coretests/src/android/content/res"
+    },
+    {
+      "path": "frameworks/base/core/tests/coretests/src/com/android/internal/content/res"
+    }
+  ],
   "presubmit": [
     {
       "name": "CtsResourcesLoaderTests"
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index 7714dd8..243f801 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -38,6 +38,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
     public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION =
             "com.android.server.adb.WIRELESS_DEBUG_STATUS";
 
@@ -46,6 +47,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
     public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION =
             "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES";
 
@@ -59,6 +61,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
     public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION =
             "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT";
 
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index 41d1e25..ed22de8 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -15,6 +15,7 @@
  */
 package android.hardware;
 
+import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -45,6 +46,7 @@
     private int mHistogramType;
     private float[] mHistogramBins;
     private long[] mHistogramCounts;
+    private int mDynamicRangeProfile;
 
     private static final String TAG = "CameraStreamStats";
 
@@ -60,11 +62,12 @@
         mMaxHalBuffers = 0;
         mMaxAppBuffers = 0;
         mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
+        mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
     }
 
     public CameraStreamStats(int width, int height, int format,
             int dataSpace, long usage, long requestCount, long errorCount,
-            int startLatencyMs, int maxHalBuffers, int maxAppBuffers) {
+            int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile) {
         mWidth = width;
         mHeight = height;
         mFormat = format;
@@ -76,6 +79,7 @@
         mMaxHalBuffers = maxHalBuffers;
         mMaxAppBuffers = maxAppBuffers;
         mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
+        mDynamicRangeProfile = dynamicRangeProfile;
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
@@ -121,6 +125,7 @@
         dest.writeInt(mHistogramType);
         dest.writeFloatArray(mHistogramBins);
         dest.writeLongArray(mHistogramCounts);
+        dest.writeInt(mDynamicRangeProfile);
     }
 
     public void readFromParcel(Parcel in) {
@@ -137,6 +142,7 @@
         mHistogramType = in.readInt();
         mHistogramBins = in.createFloatArray();
         mHistogramCounts = in.createLongArray();
+        mDynamicRangeProfile = in.readInt();
     }
 
     public int getWidth() {
@@ -190,4 +196,8 @@
     public long[] getHistogramCounts() {
         return mHistogramCounts;
     }
+
+    public int getDynamicRangeProfile() {
+        return mDynamicRangeProfile;
+    }
 }
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 4683d25..acceb65 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -110,9 +110,9 @@
     @Retention(RetentionPolicy.SOURCE)
     @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
             USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
-            USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
-            USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP,
-            USAGE_GPU_MIPMAP_COMPLETE})
+            USAGE_GPU_COLOR_OUTPUT, USAGE_COMPOSER_OVERLAY, USAGE_PROTECTED_CONTENT,
+            USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA,
+            USAGE_GPU_CUBE_MAP, USAGE_GPU_MIPMAP_COMPLETE, USAGE_FRONT_BUFFER})
     public @interface Usage {};
 
     @Usage
@@ -151,6 +151,12 @@
     public static final long USAGE_GPU_CUBE_MAP           = 1 << 25;
     /** Usage: The buffer contains a complete mipmap hierarchy */
     public static final long USAGE_GPU_MIPMAP_COMPLETE    = 1 << 26;
+    /** Usage: The buffer is used for front-buffer rendering. When front-buffering rendering is
+     * specified, different usages may adjust their behavior as a result. For example, when
+     * used as USAGE_GPU_COLOR_OUTPUT the buffer will behave similar to a single-buffered window.
+     * When used with USAGE_COMPOSER_OVERLAY, the system will try to prioritize the buffer
+     * receiving an overlay plane & avoid caching it in intermediate composition buffers. */
+    public static final long USAGE_FRONT_BUFFER           = 1 << 32;
 
     /**
      * Creates a new <code>HardwareBuffer</code> instance.
diff --git a/core/java/android/hardware/ISerialManager.aidl b/core/java/android/hardware/ISerialManager.aidl
index 74d30f7..65a0fa4 100644
--- a/core/java/android/hardware/ISerialManager.aidl
+++ b/core/java/android/hardware/ISerialManager.aidl
@@ -22,8 +22,10 @@
 interface ISerialManager
 {
     /* Returns a list of all available serial ports */
+    @EnforcePermission("SERIAL_PORT")
     String[] getSerialPorts();
 
     /* Returns a file descriptor for the serial port. */
+    @EnforcePermission("SERIAL_PORT")
     ParcelFileDescriptor openSerialPort(String name);
 }
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 08f5a8a..37cfb49 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -22,6 +22,8 @@
 import android.hardware.input.InputSensorInfo;
 import android.os.Build;
 
+import java.util.UUID;
+
 /**
  * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
  * the list of available sensors. For more information about Android sensors,
@@ -710,6 +712,20 @@
     public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
 
     /**
+     * A constant describing a head tracker sensor.
+     *
+     * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+     */
+    public static final int TYPE_HEAD_TRACKER = 37;
+
+    /**
+     * A constant string describing a head tracker sensor.
+     *
+     * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+     */
+    public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
+
+    /**
      * A constant describing all sensor types.
      */
 
@@ -829,6 +845,7 @@
             1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT
             6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
             1, // SENSOR_TYPE_HINGE_ANGLE
+            6, // SENSOR_TYPE_HEAD_TRACKER (discontinuity count is excluded)
     };
 
     /**
@@ -925,6 +942,7 @@
     @UnsupportedAppUsage
     private int     mFlags;
     private int     mId;
+    private UUID    mUuid;
 
     Sensor() {
     }
@@ -951,6 +969,8 @@
         this.mMaxDelay = sensorInfo.getMaxDelay();
         this.mFlags = sensorInfo.getFlags();
         this.mId = sensorInfo.getId();
+        // The UUID is never specified when creating a sensor from Input manager
+        this.mUuid = new UUID((long) this.mId, 0);
     }
 
     /**
@@ -1040,11 +1060,9 @@
     }
 
     /**
-     * Do not use.
-     *
-     * This method throws an UnsupportedOperationException.
-     *
-     * Use getId() if you want a unique ID.
+     * Reserved for system and audio servers.
+     * When called from an unauthorized context, the UUID will contain the
+     * sensor ID in the MSB and 0 in the LSB.
      *
      * @see getId
      *
@@ -1052,7 +1070,7 @@
      */
     @SystemApi
     public java.util.UUID getUuid() {
-        throw new UnsupportedOperationException();
+        return mUuid;
     }
 
     /**
@@ -1280,23 +1298,33 @@
             case TYPE_HINGE_ANGLE:
                 mStringType = STRING_TYPE_HINGE_ANGLE;
                 return true;
+            case TYPE_HEAD_TRACKER:
+                mStringType = STRING_TYPE_HEAD_TRACKER;
+                return true;
             default:
                 return false;
         }
     }
 
     /**
-     * Sets the ID associated with the sensor.
+     * Sets the UUID associated with the sensor.
      *
-     * The method name is misleading; while this ID is based on the UUID,
-     * we do not pass in the actual UUID.
+     * NOTE: to be used only by native bindings in SensorManager.
+     *
+     * @see #getUuid
+     */
+    private void setUuid(long msb, long lsb) {
+        mUuid = new UUID(msb, lsb);
+    }
+
+    /**
+     * Sets the ID associated with the sensor.
      *
      * NOTE: to be used only by native bindings in SensorManager.
      *
      * @see #getId
      */
-    private void setUuid(long msb, long lsb) {
-        // TODO(b/29547335): Rename this method to setId.
-        mId = (int) msb;
+    private void setId(int id) {
+        mId = id;
     }
 }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 232f234..c77c8cc 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -641,6 +641,41 @@
      *  <li> values[0]: Measured hinge angle between 0 and 360 degrees inclusive</li>
      * </ul>
      *
+     * <h4>{@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}:</h4>
+     *
+     * A sensor of this type measures the orientation of a user's head relative to an arbitrary
+     * reference frame, as well as the rate of rotation.
+     *
+     * Events produced by this sensor follow a special head-centric coordinate frame, where:
+     * <ul>
+     *  <li> The X axis crosses through the user's ears, with the positive X direction extending
+     *       out of the user's right ear</li>
+     *  <li> The Y axis crosses from the back of the user's head through their nose, with the
+     *       positive direction extending out of the nose, and the X/Y plane being nominally
+     *       parallel to the ground when the user is upright and looking straight ahead</li>
+     *  <li> The Z axis crosses from the neck through the top of the user's head, with the
+     *       positive direction extending out from the top of the head</li>
+     * </ul>
+     *
+     * Data is provided in Euler vector representation, which is a vector whose direction indicates
+     * the axis of rotation and magnitude indicates the angle to rotate around that axis, in
+     * radians.
+     *
+     * The first three elements provide the transform from the (arbitrary, possibly slowly drifting)
+     * reference frame to the head frame. The magnitude of this vector is in range [0, &pi;]
+     * radians, while the value of individual axes is in range [-&pi;, &pi;]. The next three
+     * elements provide the estimated rotational velocity of the user's head relative to itself, in
+     * radians per second.
+     *
+     * <ul>
+     *  <li> values[0] : X component of Euler vector representing rotation</li>
+     *  <li> values[1] : Y component of Euler vector representing rotation</li>
+     *  <li> values[2] : Z component of Euler vector representing rotation</li>
+     *  <li> values[3] : X component of Euler vector representing angular velocity</li>
+     *  <li> values[4] : Y component of Euler vector representing angular velocity</li>
+     *  <li> values[5] : Z component of Euler vector representing angular velocity</li>
+     * </ul>
+     *
      * @see GeomagneticField
      */
     public final float[] values;
diff --git a/core/java/android/hardware/SensorEventCallback.java b/core/java/android/hardware/SensorEventCallback.java
index bac212a..7b0092d 100644
--- a/core/java/android/hardware/SensorEventCallback.java
+++ b/core/java/android/hardware/SensorEventCallback.java
@@ -16,6 +16,8 @@
 
 package android.hardware;
 
+import android.annotation.NonNull;
+
 /**
  * Used for receiving sensor additional information frames.
  */
@@ -52,4 +54,21 @@
      * reported from sensor hardware.
      */
     public void onSensorAdditionalInfo(SensorAdditionalInfo info) {}
+
+    /**
+     * Called when the next {@link android.hardware.SensorEvent SensorEvent} to be delivered via the
+     * {@link #onSensorChanged(SensorEvent) onSensorChanged} method represents the first event after
+     * a discontinuity.
+     *
+     * The exact meaning of discontinuity depends on the sensor type. For {@link
+     * android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that the
+     * reference frame has suddenly and significantly changed.
+     *
+     * Note that this concept is either not relevant to or not supported by most sensor types,
+     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
+     * exception.
+     *
+     * @param sensor The {@link android.hardware.Sensor Sensor} which experienced the discontinuity.
+     */
+    public void onSensorDiscontinuity(@NonNull Sensor sensor) {}
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 3a8513b..32a5ee7 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -676,6 +676,7 @@
         private long mNativeSensorEventQueue;
         private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
         protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
+        protected final SparseIntArray mSensorDiscontinuityCounts = new SparseIntArray();
         private final CloseGuard mCloseGuard = CloseGuard.get();
         protected final SystemSensorManager mManager;
 
@@ -875,10 +876,21 @@
 
             // call onAccuracyChanged() only if the value changes
             final int accuracy = mSensorAccuracies.get(handle);
-            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+            if (t.accuracy >= 0 && accuracy != t.accuracy) {
                 mSensorAccuracies.put(handle, t.accuracy);
                 mListener.onAccuracyChanged(t.sensor, t.accuracy);
             }
+
+            // call onSensorDiscontinuity() if the discontinuity counter changed
+            if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER
+                    && mListener instanceof SensorEventCallback) {
+                final int lastCount = mSensorDiscontinuityCounts.get(handle);
+                final int curCount = Float.floatToIntBits(values[6]);
+                if (lastCount >= 0 && lastCount != curCount) {
+                    ((SensorEventCallback) mListener).onSensorDiscontinuity(t.sensor);
+                }
+            }
+
             mListener.onSensorChanged(t);
         }
 
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 6b5bec9..dc65bef 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.security.keystore.KeyProperties;
 import android.text.TextUtils;
 import android.util.Log;
@@ -666,8 +667,8 @@
     /**
      * A wrapper class for the cryptographic operations supported by BiometricPrompt.
      *
-     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
-     * {@link IdentityCredential}.
+     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+     * {@link IdentityCredential}, and {@link PresentationSession}.
      *
      * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
      * time-based. This is specified during key creation via the timeout parameter of the
@@ -697,10 +698,21 @@
             super(mac);
         }
 
+        /**
+         * Create from a {@link IdentityCredential} object.
+         *
+         * @param credential a {@link IdentityCredential} object.
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+         */
+        @Deprecated
         public CryptoObject(@NonNull IdentityCredential credential) {
             super(credential);
         }
 
+        public CryptoObject(@NonNull PresentationSession session) {
+            super(session);
+        }
+
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -728,10 +740,20 @@
         /**
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
          */
+        @Deprecated
         public @Nullable IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
+
+        /**
+         * Get {@link PresentationSession} object.
+         * @return {@link PresentationSession} object or null if this doesn't contain one.
+         */
+        public @Nullable PresentationSession getPresentationSession() {
+            return super.getPresentationSession();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 7648cf2..d415706 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.security.keystore2.AndroidKeyStoreProvider;
 
 import java.security.Signature;
@@ -27,8 +28,8 @@
 
 /**
  * A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
- * {@link IdentityCredential} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession} objects.
  * @hide
  */
 public class CryptoObject {
@@ -46,10 +47,21 @@
         mCrypto = mac;
     }
 
+    /**
+     * Create from a {@link IdentityCredential} object.
+     *
+     * @param credential a {@link IdentityCredential} object.
+     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+     */
+    @Deprecated
     public CryptoObject(@NonNull IdentityCredential credential) {
         mCrypto = credential;
     }
 
+    public CryptoObject(@NonNull PresentationSession session) {
+        mCrypto = session;
+    }
+
     /**
      * Get {@link Signature} object.
      * @return {@link Signature} object or null if this doesn't contain one.
@@ -77,12 +89,22 @@
     /**
      * Get {@link IdentityCredential} object.
      * @return {@link IdentityCredential} object or null if this doesn't contain one.
+     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
      */
+    @Deprecated
     public IdentityCredential getIdentityCredential() {
         return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
     }
 
     /**
+     * Get {@link PresentationSession} object.
+     * @return {@link PresentationSession} object or null if this doesn't contain one.
+     */
+    public PresentationSession getPresentationSession() {
+        return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
+    }
+
+    /**
      * @hide
      * @return the opId associated with this object or 0 if none
      */
@@ -91,6 +113,8 @@
             return 0;
         } else if (mCrypto instanceof IdentityCredential) {
             return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
+        } else if (mCrypto instanceof PresentationSession) {
+            return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
         }
         return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
     }
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index bd47463..691690c 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -321,6 +321,9 @@
      * can submit reprocess capture requests. Submitting a reprocess request to a regular capture
      * session will result in an {@link IllegalArgumentException}.</p>
      *
+     * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+     * will result in an {@link IllegalArgumentException}.</p>
+     *
      * @param request the settings for this capture
      * @param listener The callback object to notify once this request has been
      * processed. If null, no metadata will be produced for this capture,
@@ -347,13 +350,15 @@
      *                                  a different session; or the capture targets a Surface in
      *                                  the middle of being {@link #prepare prepared}; or the
      *                                  handler is null, the listener is not null, and the calling
-     *                                  thread has no looper.
+     *                                  thread has no looper; or the request targets Surfaces with
+     *                                  an unsupported dynamic range combination
      *
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #abortCaptures
      * @see CameraDevice#createReprocessableCaptureSession
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public abstract int capture(@NonNull CaptureRequest request,
             @Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -389,13 +394,16 @@
      *                                  request was created with a {@link TotalCaptureResult} from
      *                                  a different session; or the capture targets a Surface in
      *                                  the middle of being {@link #prepare prepared}; or the
-     *                                  executor is null, or the listener is not null.
+     *                                  executor is null, or the listener is not null;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination;
      *
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #abortCaptures
      * @see CameraDevice#createReprocessableCaptureSession
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public int captureSingleRequest(@NonNull CaptureRequest request,
             @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
@@ -427,6 +435,9 @@
      * can submit reprocess capture requests. Submitting a reprocess request to a regular
      * capture session will result in an {@link IllegalArgumentException}.</p>
      *
+     * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+     * will result in an {@link IllegalArgumentException}.</p>
+     *
      * @param requests the list of settings for this burst capture
      * @param listener The callback object to notify each time one of the
      * requests in the burst has been processed. If null, no metadata will be
@@ -454,12 +465,15 @@
      *                                  {@link TotalCaptureResult} from a different session; or one
      *                                  of the captures targets a Surface in the middle of being
      *                                  {@link #prepare prepared}; or if the handler is null, the
-     *                                  listener is not null, and the calling thread has no looper.
+     *                                  listener is not null, and the calling thread has no looper;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination.
      *
      * @see #capture
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #abortCaptures
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public abstract int captureBurst(@NonNull List<CaptureRequest> requests,
             @Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -499,12 +513,15 @@
      *                                  {@link TotalCaptureResult} from a different session; or one
      *                                  of the captures targets a Surface in the middle of being
      *                                  {@link #prepare prepared}; or if the executor is null; or if
-     *                                  the listener is null.
+     *                                  the listener is null;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination.
      *
      * @see #capture
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #abortCaptures
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
             @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
@@ -545,6 +562,9 @@
      * single reprocess input image. The request must be capturing images from the camera. If a
      * reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
      *
+     * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+     * will result in an {@link IllegalArgumentException}.</p>
+     *
      * @param request the request to repeat indefinitely
      * @param listener The callback object to notify every time the
      * request finishes processing. If null, no metadata will be
@@ -567,13 +587,16 @@
      *                                  is a reprocess capture request; or the capture targets a
      *                                  Surface in the middle of being {@link #prepare prepared}; or
      *                                  the handler is null, the listener is not null, and the
-     *                                  calling thread has no looper; or no requests were passed in.
+     *                                  calling thread has no looper; or no requests were passed in;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination.
      *
      * @see #capture
      * @see #captureBurst
      * @see #setRepeatingBurst
      * @see #stopRepeating
      * @see #abortCaptures
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
             @Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -604,13 +627,16 @@
      *                                  that are not currently configured as outputs; or the request
      *                                  is a reprocess capture request; or the capture targets a
      *                                  Surface in the middle of being {@link #prepare prepared}; or
-     *                                  the executor is null; or the listener is null.
+     *                                  the executor is null; or the listener is null;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination.
      *
      * @see #capture
      * @see #captureBurst
      * @see #setRepeatingBurst
      * @see #stopRepeating
      * @see #abortCaptures
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
             @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
@@ -655,6 +681,9 @@
      * single reprocess input image. The request must be capturing images from the camera. If a
      * reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
      *
+     * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+     * will result in an {@link IllegalArgumentException}.</p>
+     *
      * @param requests the list of requests to cycle through indefinitely
      * @param listener The callback object to notify each time one of the
      * requests in the repeating bursts has finished processing. If null, no
@@ -678,13 +707,16 @@
      *                                  targets a Surface in the middle of being
      *                                  {@link #prepare prepared}; or the handler is null, the
      *                                  listener is not null, and the calling thread has no looper;
-     *                                  or no requests were passed in.
+     *                                  or no requests were passed in;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination.
      *
      * @see #capture
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #stopRepeating
      * @see #abortCaptures
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public abstract int setRepeatingBurst(@NonNull List<CaptureRequest> requests,
             @Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -717,13 +749,16 @@
      *                                  is a reprocess capture request; or one of the captures
      *                                  targets a Surface in the middle of being
      *                                  {@link #prepare prepared}; or the executor is null; or the
-     *                                  listener is null.
+     *                                  listener is null;
+     *                                  or the request targets Surfaces with an unsupported dynamic
+     *                                  range combination.
      *
      * @see #capture
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #stopRepeating
      * @see #abortCaptures
+     * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
      */
     public int setRepeatingBurstRequests(@NonNull List<CaptureRequest> requests,
             @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 48a9121..d2dc314 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -461,7 +461,7 @@
     public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
             @RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
         if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
-                (usecase <= RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT)) ||
+                (usecase <= RecommendedStreamConfigurationMap.USECASE_10BIT_OUTPUT)) ||
                 ((usecase >= RecommendedStreamConfigurationMap.USECASE_VENDOR_START) &&
                 (usecase < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT))) {
             if (mRecommendedConfigurations == null) {
@@ -2213,6 +2213,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT DYNAMIC_RANGE_TEN_BIT}</li>
      * </ul>
      *
      * <p>This key is available on all devices.</p>
@@ -2236,6 +2237,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
      * @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
      */
     @PublicKey
     @NonNull
@@ -2379,6 +2381,86 @@
             new Key<int[]>("android.request.characteristicKeysNeedingPermission", int[].class);
 
     /**
+     * <p>Devices supporting the 10-bit output capability
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * must list their supported dynamic range profiles along with capture request
+     * constraints for specific profile combinations.</p>
+     * <p>Camera clients can retrieve the list of supported 10-bit dynamic range profiles by calling
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#getSupportedProfiles }.
+     * Any of them can be configured by setting OutputConfiguration dynamic range profile in
+     * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile }.
+     * Clients can also check if there are any constraints that limit the combination
+     * of supported profiles that can be referenced within a single capture request by calling
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.DynamicRangeProfiles> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES =
+            new Key<android.hardware.camera2.params.DynamicRangeProfiles>("android.request.availableDynamicRangeProfiles", android.hardware.camera2.params.DynamicRangeProfiles.class);
+
+    /**
+     * <p>A map of all available 10-bit dynamic range profiles along with their
+     * capture request constraints.</p>
+     * <p>Devices supporting the 10-bit output capability
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * must list their supported dynamic range profiles. In case the camera is not able to
+     * support every possible profile combination within a single capture request, then the
+     * constraints must be listed here as well.</p>
+     * <p><b>Possible values:</b></p>
+     * <ul>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD STANDARD}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10 HLG10}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10 HDR10}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS HDR10_PLUS}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF DOLBY_VISION_10B_HDR_REF}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO DOLBY_VISION_10B_HDR_REF_PO}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM DOLBY_VISION_10B_HDR_OEM}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO DOLBY_VISION_10B_HDR_OEM_PO}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF DOLBY_VISION_8B_HDR_REF}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO DOLBY_VISION_8B_HDR_REF_PO}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM DOLBY_VISION_8B_HDR_OEM}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO DOLBY_VISION_8B_HDR_OEM_PO}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX MAX}</li>
+     * </ul>
+     *
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO
+     * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX
+     * @hide
+     */
+    public static final Key<int[]> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP =
+            new Key<int[]>("android.request.availableDynamicRangeProfilesMap", int[].class);
+
+    /**
+     * <p>Recommended 10-bit dynamic range profile.</p>
+     * <p>Devices supporting the 10-bit output capability
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * must list a 10-bit supported dynamic range profile that is expected to perform
+     * optimally in terms of image quality, power and performance.
+     * The value advertised can be used as a hint by camera clients when configuring the dynamic
+     * range profile when calling
+     * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE =
+            new Key<Integer>("android.request.recommendedTenBitDynamicRangeProfile", int.class);
+
+    /**
      * <p>The list of image formats that are supported by this
      * camera device for output streams.</p>
      * <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
@@ -3340,6 +3422,32 @@
             new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryMaximumResolutionStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
 
     /**
+     * <p>An array of mandatory stream combinations which are applicable when device support the
+     * 10-bit output capability
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * This is an app-readable conversion of the maximum resolution mandatory stream combination
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+     * <p>The array of
+     * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+     * generated according to the documented
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+     * device which has the
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * capability.
+     * Clients can use the array as a quick reference to find an appropriate camera stream
+     * combination.
+     * The mandatory stream combination array will be {@code null} in case the device is not an
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * device.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS =
+            new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryTenBitOutputStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+    /**
      * <p>Whether the camera device supports multi-resolution input or output streams</p>
      * <p>A logical multi-camera or an ultra high resolution camera may support multi-resolution
      * input or output streams. With multi-resolution output streams, the camera device is able
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 3c1ec3e..47eb79d 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -402,7 +402,9 @@
      *                                  registered surfaces do not meet the device-specific
      *                                  extension requirements such as dimensions and/or
      *                                  (output format)/(surface type), or if the extension is not
-     *                                  supported.
+     *                                  supported, or if any of the output configurations select
+     *                                  a dynamic range different from
+     *                                  {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD}
      * @see CameraExtensionCharacteristics#getSupportedExtensions
      * @see CameraExtensionCharacteristics#getExtensionSupportedSizes
      */
@@ -822,7 +824,36 @@
      * be chosen from. {@code DEFAULT} refers to the default sensor pixel mode {@link
      * StreamConfigurationMap} and {@code MAX_RES} refers to the maximum resolution {@link
      * StreamConfigurationMap}. The same capture request must not mix targets from
-     * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes.
+     * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes. </p>
+     *
+     * <p> 10-bit output capable
+     * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT}
+     * devices support at least the following stream combinations: </p>
+     * <table>
+     * <tr><th colspan="7">10-bit output additional guaranteed configurations</th></tr>
+     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr>
+     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>In-application video/image processing.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
+     * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processing.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code RECORD }</td> <td colspan="2" id="rb"></td> <td>High-resolution video recording with preview.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code RECORD }</td> <td>{@code YUV}</td><td id="rb">{@code RECORD }</td> <td>High-resolution recording with in-app snapshot.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV }</td><td id="rb">{@code RECORD }</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD }</td> <td>High-resolution recording with video snapshot.</td> </tr>
+     * </table><br>
+     * <p>Here PRIV can be either 8 or 10-bit {@link android.graphics.ImageFormat#PRIVATE} pixel
+     * format. YUV can be either {@link android.graphics.ImageFormat#YUV_420_888} or
+     * {@link android.graphics.ImageFormat#YCBCR_P010}.
+     * For the maximum size column, PREVIEW refers to the best size match to the device's screen
+     * resolution, or to 1080p (1920x1080), whichever is smaller. RECORD refers to the camera
+     * device's maximum supported recording resolution, as determined by
+     * {@link android.media.CamcorderProfile}. MAXIMUM refers to the camera device's maximum output
+     * resolution for that format or target from {@link StreamConfigurationMap#getOutputSizes(int)}.
+     * Do note that invalid combinations such as having a camera surface configured to use pixel
+     * format {@link android.graphics.ImageFormat#YUV_420_888} with a 10-bit profile
+     * will cause a capture session initialization failure.
+     * </p>
      *
      * <p>Since the capabilities of camera devices vary greatly, a given camera device may support
      * target combinations with sizes outside of these guarantees, but this can only be tested for
@@ -907,6 +938,13 @@
      * guaranteed output targets that can be submitted in a regular or reprocess
      * {@link CaptureRequest} simultaneously.</p>
      *
+     * <p>Reprocessing with 10-bit output targets on 10-bit capable
+     * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} devices is
+     * not supported. Trying to initialize a repreocessable capture session with one ore more
+     * output configurations set {@link OutputConfiguration#setDynamicRangeProfile(int)} to use
+     * a 10-bit dynamic range profile {@link android.hardware.camera2.params.DynamicRangeProfiles}
+     * will trigger {@link IllegalArgumentException}.</p>
+     *
      * <style scoped>
      *  #rb { border-right-width: thick; }
      * </style>
@@ -1083,13 +1121,17 @@
      *
      * @throws IllegalArgumentException In case the session configuration is invalid; or the output
      *                                  configurations are empty; or the session configuration
-     *                                  executor is invalid.
+     *                                  executor is invalid;
+     *                                  or the output dynamic range combination is
+     *                                  invalid/unsupported.
      * @throws CameraAccessException In case the camera device is no longer connected or has
      *                               encountered a fatal error.
      * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
      * @see #createCaptureSessionByOutputConfigurations
      * @see #createReprocessableCaptureSession
      * @see #createConstrainedHighSpeedCaptureSession
+     * @see OutputConfiguration#setDynamicRangeProfile(int)
+     * @see android.hardware.camera2.params.DynamicRangeProfiles
      */
     public void createCaptureSession(
             SessionConfiguration config) throws CameraAccessException {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 639abe9..803684d 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1190,6 +1190,135 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17;
 
+    /**
+     * <p>The device supports one or more 10-bit camera outputs according to the dynamic range
+     * profiles specified in
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#getSupportedProfiles }.
+     * They can be configured as part of the capture session initialization via
+     * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile }.
+     * Cameras that enable this capability must also support the following:
+     * * Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }
+     * * All mandatory stream combinations for this specific capability as per
+     *   documentation {@link android.hardware.camera2.CameraDevice#createCaptureSession }
+     * * In case the device is not able to capture some combination of supported
+     *   standard 8-bit and/or 10-bit dynamic range profiles within the same capture request,
+     *   then those constraints must be listed in
+     *   {@link android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints }
+     * * Recommended dynamic range profile listed in
+     *   {@link android.hardware.camera2.CameraCharacteristics#REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE }.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18;
+
+    //
+    // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+    //
+
+    /**
+     * <p>8-bit SDR profile which is the default for all non 10-bit output capable devices.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD = 0x1;
+
+    /**
+     * <p>10-bit pixel samples encoded using the Hybrid log-gamma transfer function.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10 = 0x2;
+
+    /**
+     * <p>10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+     * This profile utilizes internal static metadata to increase the quality
+     * of the capture.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10 = 0x4;
+
+    /**
+     * <p>10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+     * In contrast to HDR10, this profile uses internal per-frame metadata
+     * to further enhance the quality of the capture.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS = 0x8;
+
+    /**
+     * <p>This is a camera mode for Dolby Vision capture optimized for a more scene
+     * accurate capture. This would typically differ from what a specific device
+     * might want to tune for a consumer optimized Dolby Vision general capture.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF = 0x10;
+
+    /**
+     * <p>This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO = 0x20;
+
+    /**
+     * <p>This is the camera mode for the default Dolby Vision capture mode for the
+     * specific device. This would be tuned by each specific device for consumer
+     * pleasing results that resonate with their particular audience. We expect
+     * that each specific device would have a different look for their default
+     * Dolby Vision capture.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM = 0x40;
+
+    /**
+     * <p>This is the power optimized mode for 10-bit Dolby Vision HDR device specific
+     * capture Mode.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO = 0x80;
+
+    /**
+     * <p>This is the 8-bit version of the Dolby Vision reference capture mode optimized
+     * for scene accuracy.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF = 0x100;
+
+    /**
+     * <p>This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO = 0x200;
+
+    /**
+     * <p>This is the 8-bit version of device specific tuned and optimized Dolby Vision
+     * capture mode.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM = 0x400;
+
+    /**
+     * <p>This is the power optimized mode for 8-bit Dolby Vision HDR device specific
+     * capture Mode.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO = 0x800;
+
+    /**
+     *
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX = 0x1000;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 86ae3a3..5df64e3 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -599,7 +599,8 @@
 
         synchronized (mSurfacesLock) {
             mSurfaceSet.clear();
-            Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
+            Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader(),
+                    Surface.class);
             if (parcelableArray != null) {
                 for (Parcelable p : parcelableArray) {
                     Surface s = (Surface) p;
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index b8443fb..9d2c901 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -35,7 +35,6 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.extension.CameraOutputConfig;
 import android.hardware.camera2.extension.CameraSessionConfig;
-import android.hardware.camera2.extension.CaptureStageImpl;
 import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.ICaptureCallback;
 import android.hardware.camera2.extension.IImageProcessorImpl;
@@ -49,6 +48,7 @@
 import android.hardware.camera2.extension.ParcelImage;
 import android.hardware.camera2.extension.ParcelTotalCaptureResult;
 import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.hardware.camera2.params.ExtensionSessionConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
@@ -130,6 +130,13 @@
                     config.getOutputConfigurations().size() + " expected <= 2");
         }
 
+        for (OutputConfiguration c : config.getOutputConfigurations()) {
+            if (c.getDynamicRangeProfile() != DynamicRangeProfiles.STANDARD) {
+                throw new IllegalArgumentException("Unsupported dynamic range profile: " +
+                        c.getDynamicRangeProfile());
+            }
+        }
+
         int suitableSurfaceCount = 0;
         List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
                 config.getExtension(), SurfaceTexture.class);
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 71047af..c8ecfd0 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -39,6 +39,7 @@
 import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
 import android.hardware.camera2.extension.ParcelImage;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.hardware.camera2.params.ExtensionSessionConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
@@ -145,6 +146,13 @@
                     config.getOutputConfigurations().size() + " expected <= 2");
         }
 
+        for (OutputConfiguration c : config.getOutputConfigurations()) {
+            if (c.getDynamicRangeProfile() != DynamicRangeProfiles.STANDARD) {
+                throw new IllegalArgumentException("Unsupported dynamic range profile: " +
+                        c.getDynamicRangeProfile());
+            }
+        }
+
         Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
                 CameraExtensionCharacteristics.initializeExtension(config.getExtension());
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index e393a66..0f8bdf6 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -51,6 +51,7 @@
 import android.hardware.camera2.marshal.impl.MarshalQueryableString;
 import android.hardware.camera2.params.Capability;
 import android.hardware.camera2.params.DeviceStateSensorOrientationMap;
+import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.hardware.camera2.params.Face;
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
@@ -331,6 +332,7 @@
     private static final int MANDATORY_STREAM_CONFIGURATIONS_DEFAULT = 0;
     private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1;
     private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
+    private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3;
 
     private static String translateLocationProviderToProcess(final String provider) {
         if (provider == null) {
@@ -678,6 +680,16 @@
                 });
 
         sGetCommandMap.put(
+                CameraCharacteristics.SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS.getNativeKey(),
+                new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMandatory10BitStreamCombinations();
+                    }
+                });
+
+        sGetCommandMap.put(
                 CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS.getNativeKey(),
                         new GetCommand() {
                     @Override
@@ -771,6 +783,15 @@
                     }
                 });
         sGetCommandMap.put(
+                CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getDynamicRangeProfiles();
+                    }
+                });
+        sGetCommandMap.put(
                 CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
                         new GetCommand() {
                     @Override
@@ -1015,6 +1036,17 @@
         return map;
     }
 
+    private DynamicRangeProfiles getDynamicRangeProfiles() {
+        int[] profileArray = getBase(
+                CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP);
+
+        if (profileArray == null) {
+            return null;
+        }
+
+        return new DynamicRangeProfiles(profileArray);
+    }
+
     private Location getGpsLocation() {
         String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
         double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
@@ -1378,6 +1410,9 @@
             case MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION:
                 combs = build.getAvailableMandatoryMaximumResolutionStreamCombinations();
                 break;
+            case MANDATORY_STREAM_CONFIGURATIONS_10BIT:
+                combs = build.getAvailableMandatory10BitStreamCombinations();
+                break;
             default:
                 combs = build.getAvailableMandatoryStreamCombinations();
         }
@@ -1389,6 +1424,10 @@
         return null;
     }
 
+    private MandatoryStreamCombination[] getMandatory10BitStreamCombinations() {
+        return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_10BIT);
+    }
+
     private MandatoryStreamCombination[] getMandatoryConcurrentStreamCombinations() {
         if (!mHasMandatoryConcurrentStreams) {
             return null;
diff --git a/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
new file mode 100644
index 0000000..5c1a4aa
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import android.hardware.camera2.CameraMetadata;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Immutable class with information about supported 10-bit dynamic range profiles.
+ *
+ * <p>An instance of this class can be queried by retrieving the value of
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES}.
+ * </p>
+ *
+ * <p>All camera devices supporting the
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT}
+ * capability must advertise the supported 10-bit dynamic range profiles in
+ * {@link #getSupportedProfiles}</p>
+ *
+ * <p>Some devices may not be able to support 8-bit and/or 10-bit output with different dynamic
+ * range profiles within the same capture request. Such device specific constraints can be queried
+ * by calling {@link #getProfileCaptureRequestConstraints(int)}. Do note that unsupported
+ * combinations will result in {@link IllegalArgumentException} when trying to submit a capture
+ * request. Capture requests that only reference outputs configured using the same dynamic range
+ * profile value will never fail due to such constraints.</p>
+ *
+ * @see OutputConfiguration#setDynamicRangeProfile(int)
+ */
+public final class DynamicRangeProfiles {
+    /**
+     * This the default 8-bit standard profile that will be used in case where camera clients do not
+     * explicitly configure a supported dynamic range profile by calling
+     * {@link OutputConfiguration#setDynamicRangeProfile(int)}.
+     */
+    public static final int STANDARD =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD;
+
+    /**
+     * 10-bit pixel samples encoded using the Hybrid log-gamma transfer function
+     *
+     * <p>All 10-bit output capable devices are required to support this profile.</p>
+     */
+    public static final int HLG10  =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10;
+
+    /**
+     * 10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+     *
+     * <p>This profile utilizes internal static metadata to increase the quality
+     * of the capture.</p>
+     */
+    public static final int HDR10  =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10;
+
+    /**
+     * 10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+     *
+     * <p>In contrast to HDR10, this profile uses internal per-frame metadata
+     * to further enhance the quality of the capture.</p>
+     */
+    public static final int HDR10_PLUS =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS;
+
+    /**
+     * <p>This is a camera mode for Dolby Vision capture optimized for a more scene
+     * accurate capture. This would typically differ from what a specific device
+     * might want to tune for a consumer optimized Dolby Vision general capture.</p>
+     */
+    public static final int DOLBY_VISION_10B_HDR_REF =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF;
+
+    /**
+     * <p>This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.</p>
+     */
+    public static final int DOLBY_VISION_10B_HDR_REF_PO =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO;
+
+    /**
+     * <p>This is the camera mode for the default Dolby Vision capture mode for the
+     * specific device. This would be tuned by each specific device for consumer
+     * pleasing results that resonate with their particular audience. We expect
+     * that each specific device would have a different look for their default
+     * Dolby Vision capture.</p>
+     */
+    public static final int DOLBY_VISION_10B_HDR_OEM =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM;
+
+    /**
+     * <p>This is the power optimized mode for 10-bit Dolby Vision HDR device specific capture
+     * Mode.</p>
+     */
+    public static final int DOLBY_VISION_10B_HDR_OEM_PO =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO;
+
+    /**
+     * <p>This is the 8-bit version of the Dolby Vision reference capture mode optimized
+     * for scene accuracy.</p>
+     */
+    public static final int DOLBY_VISION_8B_HDR_REF =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF;
+
+    /**
+     * <p>This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.</p>
+     */
+    public static final int DOLBY_VISION_8B_HDR_REF_PO =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO;
+
+    /**
+     * <p>This is the 8-bit version of device specific tuned and optimized Dolby Vision
+     * capture mode.</p>
+     */
+    public static final int DOLBY_VISION_8B_HDR_OEM =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM;
+
+    /**
+     * <p>This is the power optimized mode for 8-bit Dolby Vision HDR device specific
+     * capture Mode.</p>
+     */
+    public static final int DOLBY_VISION_8B_HDR_OEM_PO =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO;
+
+    /*
+     * @hide
+     */
+    public static final int PUBLIC_MAX =
+            CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX;
+
+     /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PROFILE_"}, value =
+            {STANDARD,
+             HLG10,
+             HDR10,
+             HDR10_PLUS,
+             DOLBY_VISION_10B_HDR_REF,
+             DOLBY_VISION_10B_HDR_REF_PO,
+             DOLBY_VISION_10B_HDR_OEM,
+             DOLBY_VISION_10B_HDR_OEM_PO,
+             DOLBY_VISION_8B_HDR_REF,
+             DOLBY_VISION_8B_HDR_REF_PO,
+             DOLBY_VISION_8B_HDR_OEM,
+             DOLBY_VISION_8B_HDR_OEM_PO})
+    public @interface Profile {
+    }
+
+    private final HashMap<Integer, Set<Integer>> mProfileMap = new HashMap<>();
+
+    /**
+     * Create a new immutable DynamicRangeProfiles instance.
+     *
+     * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+     *
+     * <p>Do note that the constructor is available for testing purposes only!
+     * Camera clients must always retrieve the value of
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES}.
+     * for a given camera id in order to retrieve the device capabilities.</p>
+     *
+     * @param elements
+     *          An array of elements describing the map. It contains two elements per entry which
+     *          describe the supported dynamic range profile value in the first element and in the
+     *          second element a bitmap of concurrently supported dynamic range profiles within the
+     *          same capture request. Bitmap values of 0 indicate that there are no constraints.
+     *
+     * @throws IllegalArgumentException
+     *            if the {@code elements} array length is invalid, not divisible by 2 or contains
+     *            invalid element values
+     * @throws NullPointerException
+     *            if {@code elements} is {@code null}
+     *
+     */
+    public DynamicRangeProfiles(@NonNull final int[] elements) {
+        if ((elements.length % 2) != 0) {
+            throw new IllegalArgumentException("Dynamic range profile map length " +
+                    elements.length + " is not even!");
+        }
+
+        for (int i = 0; i < elements.length; i += 2) {
+            checkProfileValue(elements[i]);
+            // STANDARD is not expected to be included
+            if (elements[i] == STANDARD) {
+                throw new IllegalArgumentException("Dynamic range profile map must not include a"
+                        + " STANDARD profile entry!");
+            }
+            HashSet<Integer> profiles = new HashSet<>();
+
+            if (elements[i+1] != 0) {
+                for (int profile = STANDARD; profile < PUBLIC_MAX; profile <<= 1) {
+                    if ((elements[i+1] & profile) != 0) {
+                        profiles.add(profile);
+                    }
+                }
+            }
+
+            mProfileMap.put(elements[i], profiles);
+        }
+
+        // Build the STANDARD constraints depending on the advertised 10-bit limitations
+        HashSet<Integer> standardConstraints = new HashSet<>();
+        standardConstraints.add(STANDARD);
+        for(Integer profile : mProfileMap.keySet()) {
+            if (mProfileMap.get(profile).isEmpty() || mProfileMap.get(profile).contains(STANDARD)) {
+                standardConstraints.add(profile);
+            }
+        }
+
+        mProfileMap.put(STANDARD, standardConstraints);
+    }
+
+
+    /**
+     * @hide
+     */
+    public static void checkProfileValue(int profile) {
+        switch (profile) {
+            case STANDARD:
+            case HLG10:
+            case HDR10:
+            case HDR10_PLUS:
+            case DOLBY_VISION_10B_HDR_REF:
+            case DOLBY_VISION_10B_HDR_REF_PO:
+            case DOLBY_VISION_10B_HDR_OEM:
+            case DOLBY_VISION_10B_HDR_OEM_PO:
+            case DOLBY_VISION_8B_HDR_REF:
+            case DOLBY_VISION_8B_HDR_REF_PO:
+            case DOLBY_VISION_8B_HDR_OEM:
+            case DOLBY_VISION_8B_HDR_OEM_PO:
+                //No-op
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown profile " + profile);
+        }
+    }
+
+    /**
+     * Return a set of supported dynamic range profiles.
+     *
+     * @return non-modifiable set of dynamic range profiles
+     */
+     public @NonNull Set<Integer> getSupportedProfiles() {
+         return Collections.unmodifiableSet(mProfileMap.keySet());
+     }
+
+    /**
+     * Return a list of supported dynamic range profiles that
+     * can be referenced in a single capture request along with a given
+     * profile.
+     *
+     * <p>For example if assume that a particular 10-bit output capable device
+     * returns ({@link #STANDARD}, {@link #HLG10}, {@link #HDR10}) as result from calling
+     * {@link #getSupportedProfiles()} and {@link #getProfileCaptureRequestConstraints(int)}
+     * returns ({@link #STANDARD}, {@link #HLG10}) when given an argument of {@link #STANDARD}.
+     * This means that the corresponding camera device will only accept and process capture requests
+     * that reference outputs configured using {@link #HDR10} dynamic profile or alternatively
+     * some combination of {@link #STANDARD} and {@link #HLG10}. However trying to
+     * queue capture requests to outputs that reference both {@link #HDR10} and
+     * {@link #STANDARD}/{@link #HLG10} will result in {@link IllegalArgumentException}.</p>
+     *
+     * <p>The list will be empty in case there are no constraints for the given
+     * profile.</p>
+     *
+     * @return non-modifiable set of dynamic range profiles
+     * @throws IllegalArgumentException - If the profile argument is not
+     *                                    within the list returned by
+     *                                    getSupportedProfiles()
+     *
+     * @see OutputConfiguration#setDynamicRangeProfile(int)
+     */
+     public @NonNull Set<Integer> getProfileCaptureRequestConstraints(@Profile int profile) {
+         Set<Integer> ret = mProfileMap.get(profile);
+         if (ret == null) {
+             throw new IllegalArgumentException("Unsupported profile!");
+         }
+
+         return Collections.unmodifiableSet(ret);
+     }
+}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index a678921..32c15da 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -18,8 +18,6 @@
 
 import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
 
-import static com.android.internal.util.Preconditions.*;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.ImageFormat;
@@ -28,7 +26,6 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.media.CamcorderProfile;
 import android.util.Log;
@@ -40,6 +37,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -64,6 +62,7 @@
         private final boolean mIsInput;
         private final boolean mIsUltraHighResolution;
         private final boolean mIsMaximumSize;
+        private final boolean mIs10BitCapable;
 
         /**
          * Create a new {@link MandatoryStreamInformation}.
@@ -119,6 +118,29 @@
          */
         public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
                 boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution) {
+            this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution,
+                    /*is10bitCapable*/ false);
+        }
+
+        /**
+         * Create a new {@link MandatoryStreamInformation}.
+         *
+         * @param availableSizes List of possible stream sizes.
+         * @param format Image format.
+         * @param isMaximumSize Whether this is a maximum size stream.
+         * @param isInput Flag indicating whether this stream is input.
+         * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
+         *                              stream.
+         * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit
+         *
+         * @throws IllegalArgumentException
+         *              if sizes is empty or if the format was not user-defined in
+         *              ImageFormat/PixelFormat.
+         * @hide
+         */
+        public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
+                boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
+                boolean is10BitCapable) {
             if (availableSizes.isEmpty()) {
                 throw new IllegalArgumentException("No available sizes");
             }
@@ -127,6 +149,7 @@
             mIsMaximumSize = isMaximumSize;
             mIsInput = isInput;
             mIsUltraHighResolution = isUltraHighResolution;
+            mIs10BitCapable = is10BitCapable;
         }
 
         /**
@@ -180,6 +203,27 @@
         }
 
         /**
+         * Indicates whether this stream is able to support 10-bit output.
+         *
+         * <p>10-bit capable streams can be configured to output 10-bit sample data via calls to
+         * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile} and
+         * selecting the appropriate output Surface pixel format which can be queried via
+         * {@link #get10BitFormat()} and will be either
+         * {@link ImageFormat#PRIVATE} (the default for Surfaces initialized by
+         * {@link android.view.SurfaceView}, {@link android.view.TextureView},
+         * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec} etc.) or
+         * {@link ImageFormat#YCBCR_P010}.</p>
+         *
+         * @return true if stream is able to output 10-bit pixels
+         *
+         * @see android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
+         * @see OutputConfiguration#setDynamicRangeProfile(int)
+         */
+        public boolean is10BitCapable() {
+            return mIs10BitCapable;
+        }
+
+        /**
          * Return the list of available sizes for this mandatory stream.
          *
          * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
@@ -201,6 +245,29 @@
          * @return integer format.
          */
         public @Format int getFormat() {
+            // P010 YUV streams must be supported along with SDR 8-bit YUV streams
+            if ((mIs10BitCapable)  && (mFormat == ImageFormat.YCBCR_P010)) {
+                return ImageFormat.YUV_420_888;
+            }
+            return mFormat;
+        }
+
+        /**
+         * Retrieve the mandatory stream 10-bit {@code format} for 10-bit capable streams.
+         *
+         * <p>In case {@link #is10BitCapable()} returns {@code true}, then this method
+         * will return the corresponding 10-bit output Surface pixel format. Depending on
+         * the stream type it will be either {@link ImageFormat#PRIVATE} or
+         * {@link ImageFormat#YCBCR_P010}.</p>
+         *
+         * @return integer format.
+         * @throws UnsupportedOperationException in case the stream is not capable of 10-bit output
+         * @see #is10BitCapable()
+         */
+        public @Format int get10BitFormat() {
+            if (!mIs10BitCapable) {
+                throw new UnsupportedOperationException("10-bit output is not supported!");
+            }
             return mFormat;
         }
 
@@ -932,6 +999,41 @@
                 /*reprocessType*/ ReprocessType.PRIVATE),
     };
 
+    private static StreamCombinationTemplate s10BitOutputStreamCombinations[] = {
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM)},
+                    "Simple preview, GPU video processing, or no-preview video recording"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM)},
+                    "In-application video/image processing"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                    "Standard still imaging"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                    "Maximum-resolution in-app processing with preview"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM),
+                    new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.PREVIEW)},
+                    "Maximum-resolution two-input in-app processing"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                    "High-resolution video recording with preview"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.RECORD),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                    "High-resolution recording with in-app snapshot"),
+            new StreamCombinationTemplate(new StreamTemplate [] {
+                    new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                    new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                    "High-resolution recording with video snapshot"),
+    };
+
     /**
      * Helper builder class to generate a list of available mandatory stream combinations.
      * @hide
@@ -971,6 +1073,86 @@
         }
 
         /**
+         * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
+         *
+         * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
+         *         null in case device is not 10-bit output capable.
+         */
+        public @NonNull List<MandatoryStreamCombination>
+        getAvailableMandatory10BitStreamCombinations() {
+            // Since 10-bit streaming support is optional, we mandate these stream
+            // combinations regardless of camera device capabilities.
+
+            StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
+            if (!is10BitOutputSupported()) {
+                Log.v(TAG, "Device is not able to output 10-bit!");
+                return null;
+            }
+
+            HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
+                    enumerateAvailableSizes();
+            if (availableSizes == null) {
+                Log.e(TAG, "Available size enumeration failed!");
+                return null;
+            }
+
+            ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>();
+            availableStreamCombinations.ensureCapacity(chosenStreamCombinations.length);
+            for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
+                ArrayList<MandatoryStreamInformation> streamsInfo = new ArrayList<>();
+                streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+                for (StreamTemplate template : combTemplate.mStreamTemplates) {
+                    List<Size> sizes = null;
+                    Pair<SizeThreshold, Integer> pair;
+                    pair = new Pair<>(template.mSizeThreshold, new Integer(template.mFormat));
+                    sizes = availableSizes.get(pair);
+                    if (template.mFormat == ImageFormat.YCBCR_P010) {
+                        // Make sure that exactly the same 10 and 8-bit YUV streams sizes are
+                        // supported
+                        pair = new Pair<>(template.mSizeThreshold,
+                                new Integer(ImageFormat.YUV_420_888));
+                        HashSet<Size> sdrYuvSizes = new HashSet<>(availableSizes.get(pair));
+                        if (!sdrYuvSizes.equals(new HashSet<>(sizes))) {
+                            Log.e(TAG, "The supported 10-bit YUV sizes are different from the"
+                                    + " supported 8-bit YUV sizes!");
+                            return null;
+                        }
+                    }
+
+                    MandatoryStreamInformation streamInfo;
+                    boolean isMaximumSize =
+                            (template.mSizeThreshold == SizeThreshold.MAXIMUM);
+                    try {
+                        streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
+                                isMaximumSize, /*isInput*/ false,
+                                /*isUltraHighResolution*/ false,
+                                /*is10BitCapable*/ template.mFormat != ImageFormat.JPEG);
+                    } catch (IllegalArgumentException e) {
+                        Log.e(TAG, "No available sizes found for format: " + template.mFormat +
+                                " size threshold: " + template.mSizeThreshold + " combination: " +
+                                combTemplate.mDescription);
+                        return null;
+                    }
+                    streamsInfo.add(streamInfo);
+                }
+
+                MandatoryStreamCombination streamCombination;
+                try {
+                    streamCombination = new MandatoryStreamCombination(streamsInfo,
+                            combTemplate.mDescription, /*isReprocessable*/ false);
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG, "No stream information for mandatory combination: "
+                            + combTemplate.mDescription);
+                    return null;
+                }
+
+                availableStreamCombinations.add(streamCombination);
+            }
+
+            return Collections.unmodifiableList(availableStreamCombinations);
+        }
+
+        /**
           * Retrieve a list of all available mandatory concurrent stream combinations.
           * This method should only be called for devices which are listed in combinations returned
           * by CameraManager.getConcurrentCameraIds.
@@ -1444,7 +1626,8 @@
             final int[] formats = {
                 ImageFormat.PRIVATE,
                 ImageFormat.YUV_420_888,
-                ImageFormat.JPEG
+                ImageFormat.JPEG,
+                ImageFormat.YCBCR_P010
             };
             Size recordingMaxSize = new Size(0, 0);
             Size previewMaxSize = new Size(0, 0);
@@ -1464,7 +1647,11 @@
             HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
             for (int format : formats) {
                 Integer intFormat = new Integer(format);
-                allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
+                Size[] sizes = mStreamConfigMap.getOutputSizes(format);
+                if (sizes == null) {
+                    sizes = new Size[0];
+                }
+                allSizes.put(intFormat, sizes);
             }
 
             List<Size> previewSizes = getSizesWithinBound(
@@ -1646,6 +1833,14 @@
         }
 
         /**
+         * Check whether the current device supports 10-bit output.
+         */
+        private boolean is10BitOutputSupported() {
+            return isCapabilitySupported(
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
+        }
+
+        /**
          * Check whether the current device supports private reprocessing.
          */
         private boolean isPrivateReprocessingSupported() {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 5bb7201..f2b881b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -29,6 +29,8 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.MultiResolutionImageReader;
+import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.hardware.camera2.params.DynamicRangeProfiles.Profile;
 import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.SurfaceUtils;
@@ -258,6 +260,39 @@
     }
 
     /**
+     * Set a specific device supported dynamic range profile.
+     *
+     * <p>Clients can choose from any profile advertised as supported in
+     * CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES
+     * queried using {@link DynamicRangeProfiles#getSupportedProfiles()}.
+     * If this is not explicitly set, then the default profile will be
+     * {@link DynamicRangeProfiles#STANDARD}.</p>
+     *
+     * <p>Do note that invalid combinations between the registered output
+     * surface pixel format and the configured dynamic range profile will
+     * cause capture session initialization failure. Invalid combinations
+     * include any 10-bit dynamic range profile advertised in
+     * {@link DynamicRangeProfiles#getSupportedProfiles()} combined with
+     * an output Surface pixel format different from {@link ImageFormat#PRIVATE}
+     * (the default for Surfaces initialized by {@link android.view.SurfaceView},
+     * {@link android.view.TextureView}, {@link android.media.MediaRecorder},
+     * {@link android.media.MediaCodec} etc.)
+     * or {@link ImageFormat#YCBCR_P010}.</p>
+     */
+    public void setDynamicRangeProfile(@Profile int profile) {
+        mDynamicRangeProfile = profile;
+    }
+
+    /**
+     * Return current dynamic range profile.
+     *
+     * @return the currently set dynamic range profile
+     */
+    public @Profile int getDynamicRangeProfile() {
+        return mDynamicRangeProfile;
+    }
+
+    /**
      * Create a new {@link OutputConfiguration} instance.
      *
      * <p>This constructor takes an argument for desired camera rotation</p>
@@ -319,6 +354,7 @@
         mPhysicalCameraId = null;
         mIsMultiResolution = false;
         mSensorPixelModesUsed = new ArrayList<Integer>();
+        mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
     }
 
     /**
@@ -416,6 +452,7 @@
         mPhysicalCameraId = null;
         mIsMultiResolution = false;
         mSensorPixelModesUsed = new ArrayList<Integer>();
+        mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
     }
 
     /**
@@ -718,6 +755,7 @@
         this.mPhysicalCameraId = other.mPhysicalCameraId;
         this.mIsMultiResolution = other.mIsMultiResolution;
         this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
+        this.mDynamicRangeProfile = other.mDynamicRangeProfile;
     }
 
     /**
@@ -737,6 +775,8 @@
         boolean isMultiResolutionOutput = source.readInt() == 1;
         int[] sensorPixelModesUsed = source.createIntArray();
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+        int dynamicRangeProfile = source.readInt();
+        DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
 
         mSurfaceGroupId = surfaceSetId;
         mRotation = rotation;
@@ -760,6 +800,7 @@
         mPhysicalCameraId = physicalCameraId;
         mIsMultiResolution = isMultiResolutionOutput;
         mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
+        mDynamicRangeProfile = dynamicRangeProfile;
     }
 
     /**
@@ -875,6 +916,7 @@
         dest.writeInt(mIsMultiResolution ? 1 : 0);
         // writeList doesn't seem to work well with Integer list.
         dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
+        dest.writeInt(mDynamicRangeProfile);
     }
 
     /**
@@ -920,6 +962,9 @@
                 if (mSurfaces.get(i) != other.mSurfaces.get(i))
                     return false;
             }
+            if (mDynamicRangeProfile != other.mDynamicRangeProfile) {
+                return false;
+            }
 
             return true;
         }
@@ -939,7 +984,8 @@
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
                     mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
-                    mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
+                    mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
+                    mDynamicRangeProfile);
         }
 
         return HashCodeHelpers.hashCode(
@@ -947,7 +993,8 @@
                 mConfiguredSize.hashCode(), mConfiguredFormat,
                 mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
-                mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
+                mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
+                mDynamicRangeProfile);
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -979,4 +1026,6 @@
     private boolean mIsMultiResolution;
     // The sensor pixel modes that this OutputConfiguration will use
     private ArrayList<Integer> mSensorPixelModesUsed;
+    // Dynamic range profile
+    private int mDynamicRangeProfile;
 }
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 2d72598..80db38f 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -16,6 +16,8 @@
 
 package android.hardware.camera2.params;
 
+import static com.android.internal.R.string.hardware;
+
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -149,6 +151,16 @@
     public static final int USECASE_LOW_LATENCY_SNAPSHOT = 0x6;
 
     /**
+     * If supported, the recommended 10-bit output stream configurations must include
+     * a subset of the advertised {@link android.graphics.ImageFormat#YCBCR_P010} and
+     * {@link android.graphics.ImageFormat#PRIVATE} outputs that are optimized for power
+     * and performance when registered along with a supported 10-bit dynamic range profile.
+     * {@see android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile} for
+     * details.
+     */
+     public static final int USECASE_10BIT_OUTPUT = 0x8;
+
+    /**
      * Device specific use cases.
      * @hide
      */
@@ -163,7 +175,8 @@
         USECASE_SNAPSHOT,
         USECASE_ZSL,
         USECASE_RAW,
-        USECASE_LOW_LATENCY_SNAPSHOT})
+        USECASE_LOW_LATENCY_SNAPSHOT,
+        USECASE_10BIT_OUTPUT})
      public @interface RecommendedUsecase {};
 
     /**
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index fd1a331..6c83057 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -78,7 +78,7 @@
     public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
         checkNotNull(surface);
         long usageFlags = nativeDetectSurfaceUsageFlags(surface);
-        long disallowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | USAGE_HW_COMPOSER
+        long disallowedFlags = USAGE_HW_COMPOSER
                 | USAGE_RENDERSCRIPT | HardwareBuffer.USAGE_CPU_READ_OFTEN;
         long allowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE;
         boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0037464..eefa1d3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -334,6 +334,7 @@
      * @hide
      */
     @TestApi
+    @SystemApi
     public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10;
 
     /**
@@ -1132,41 +1133,48 @@
     }
 
     /**
-     * Sets the default display mode, according to the refresh rate and the resolution chosen by the
-     * user.
+     * Sets the global default {@link Display.Mode}.  The display mode includes preference for
+     * resolution and refresh rate. The mode change is applied globally, i.e. to all the connected
+     * displays. If the mode specified is not supported by a connected display, then no mode change
+     * occurs for that display.
      *
+     * @param mode The {@link Display.Mode} to set, which can include resolution and/or
+     * refresh-rate. It is created using {@link Display.Mode.Builder}.
+     *`
      * @hide
      */
     @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
-    public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) {
+    public void setGlobalUserPreferredDisplayMode(@NonNull Display.Mode mode) {
         // Create a new object containing default values for the unused fields like mode ID and
         // alternative refresh rates.
         Display.Mode preferredMode = new Display.Mode(mode.getPhysicalWidth(),
                 mode.getPhysicalHeight(), mode.getRefreshRate());
-        mGlobal.setUserPreferredDisplayMode(preferredMode);
+        mGlobal.setUserPreferredDisplayMode(Display.INVALID_DISPLAY, preferredMode);
     }
 
     /**
-     * Removes the user preferred display mode.
+     * Removes the global user preferred display mode.
+     * User preferred display mode is cleared for all the connected displays.
      *
      * @hide
      */
     @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
-    public void clearUserPreferredDisplayMode() {
-        mGlobal.setUserPreferredDisplayMode(null);
+    public void clearGlobalUserPreferredDisplayMode() {
+        mGlobal.setUserPreferredDisplayMode(Display.INVALID_DISPLAY, null);
     }
 
     /**
-     * Returns the user preferred display mode.
+     * Returns the global user preferred display mode.
+     * If no user preferred mode has been set, or it has been cleared, this method returns null.
      *
      * @hide
      */
     @TestApi
     @Nullable
-    public Display.Mode getUserPreferredDisplayMode() {
-        return mGlobal.getUserPreferredDisplayMode();
+    public Display.Mode getGlobalUserPreferredDisplayMode() {
+        return mGlobal.getUserPreferredDisplayMode(Display.INVALID_DISPLAY);
     }
 
     /**
@@ -1206,6 +1214,17 @@
     }
 
     /**
+     * Returns whether the specified display supports DISPLAY_DECORATION.
+     *
+     * @param displayId The display to query support.
+     *
+     * @hide
+     */
+    public boolean getDisplayDecorationSupport(int displayId) {
+        return mGlobal.getDisplayDecorationSupport(displayId);
+    }
+
+    /**
      * Returns the user preference for "Match content frame rate".
      * <p>
      * Never: Even if the app requests it, the device will never try to match its output to the
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index e731165..1a7a63ae 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -128,7 +128,7 @@
                 8, // size of display cache
                 CACHE_KEY_DISPLAY_INFO_PROPERTY) {
                 @Override
-                protected DisplayInfo recompute(Integer id) {
+                public DisplayInfo recompute(Integer id) {
                     try {
                         return mDm.getDisplayInfo(id);
                     } catch (RemoteException ex) {
@@ -812,6 +812,21 @@
     }
 
     /**
+     * Report whether the display supports DISPLAY_DECORATION.
+     *
+     * @param displayId The display whose support is being queried.
+     *
+     * @hide
+     */
+    public boolean getDisplayDecorationSupport(int displayId) {
+        try {
+            return mDm.getDisplayDecorationSupport(displayId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the brightness of the display.
      *
      * @param displayId The display from which to get the brightness
@@ -881,9 +896,9 @@
      * Sets the default display mode, according to the refresh rate and the resolution chosen by the
      * user.
      */
-    public void setUserPreferredDisplayMode(Display.Mode mode) {
+    public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) {
         try {
-            mDm.setUserPreferredDisplayMode(mode);
+            mDm.setUserPreferredDisplayMode(displayId, mode);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -892,9 +907,9 @@
     /**
      * Returns the user preferred display mode.
      */
-    public Display.Mode getUserPreferredDisplayMode() {
+    public Display.Mode getUserPreferredDisplayMode(int displayId) {
         try {
-            return mDm.getUserPreferredDisplayMode();
+            return mDm.getUserPreferredDisplayMode(displayId);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 82b31d4..35663af 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -166,8 +166,8 @@
 
     // Sets the user preferred display mode.
     // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission.
-    void setUserPreferredDisplayMode(in Mode mode);
-    Mode getUserPreferredDisplayMode();
+    void setUserPreferredDisplayMode(int displayId, in Mode mode);
+    Mode getUserPreferredDisplayMode(int displayId);
 
     // When enabled the app requested display resolution and refresh rate is always selected
     // in DisplayModeDirector regardless of user settings and policies for low brightness, low
@@ -180,4 +180,7 @@
 
     // Returns the refresh rate switching type.
     int getRefreshRateSwitchingType();
+
+    // Query for DISPLAY_DECORATION support.
+    boolean getDisplayDecorationSupport(int displayId);
 }
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 56f8142..b970559 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -306,22 +306,21 @@
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "enrollment already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnEnrollCancelListener());
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "enrollment already canceled");
+            return;
         }
 
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enroll");
-                mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
-                        mContext.getOpPackageName(), disabledFeatures, previewSurface,
-                        debugConsent);
+                final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
+                        mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
+                        previewSurface, debugConsent);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or
@@ -359,21 +358,20 @@
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "enrollRemotely is already canceled.");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnEnrollCancelListener());
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "enrollRemotely is already canceled.");
+            return;
         }
 
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enrollRemotely");
-                mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
-                        mContext.getOpPackageName(), disabledFeatures);
+                final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken,
+                        mServiceReceiver, mContext.getOpPackageName(), disabledFeatures);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or
@@ -713,10 +711,10 @@
         }
     }
 
-    private void cancelEnrollment() {
+    private void cancelEnrollment(long requestId) {
         if (mService != null) {
             try {
-                mService.cancelEnrollment(mToken);
+                mService.cancelEnrollment(mToken, requestId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -1100,9 +1098,16 @@
     }
 
     private class OnEnrollCancelListener implements OnCancelListener {
+        private final long mAuthRequestId;
+
+        private OnEnrollCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelEnrollment();
+            Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId);
+            cancelEnrollment(mAuthRequestId);
         }
     }
 
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index e919824..989b001 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -76,15 +76,16 @@
     void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
 
     // Start face enrollment
-    void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
-            String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent);
+    long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+            String opPackageName, in int [] disabledFeatures,
+            in Surface previewSurface, boolean debugConsent);
 
     // Start remote face enrollment
-    void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+    long enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
             String opPackageName, in int [] disabledFeatures);
 
     // Cancel enrollment in progress
-    void cancelEnrollment(IBinder token);
+    void cancelEnrollment(IBinder token, long requestId);
 
     // Removes the specified face enrollment for the specified userId.
     void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe04e5d..7e070bc 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -58,6 +58,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.util.Slog;
 import android.view.Surface;
 
@@ -183,9 +184,16 @@
     }
 
     private class OnEnrollCancelListener implements OnCancelListener {
+        private final long mAuthRequestId;
+
+        private OnEnrollCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelEnrollment();
+            Slog.d(TAG, "Cancel fingerprint enrollment requested for: " + mAuthRequestId);
+            cancelEnrollment(mAuthRequestId);
         }
     }
 
@@ -264,10 +272,21 @@
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
          * @hide
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
          */
+        @Deprecated
         public IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
+
+        /**
+         * Get {@link PresentationSession} object.
+         * @return {@link PresentationSession} object or null if this doesn't contain one.
+         * @hide
+         */
+        public PresentationSession getPresentationSession() {
+            return super.getPresentationSession();
+        }
     }
 
     /**
@@ -646,20 +665,19 @@
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "enrollment already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnEnrollCancelListener());
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "enrollment already canceled");
+            return;
         }
 
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
-                mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
-                        mContext.getOpPackageName(), enrollReason);
+                final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
+                        mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or try
@@ -1302,9 +1320,9 @@
         return allSensors.isEmpty() ? null : allSensors.get(0);
     }
 
-    private void cancelEnrollment() {
+    private void cancelEnrollment(long requestId) {
         if (mService != null) try {
-            mService.cancelEnrollment(mToken);
+            mService.cancelEnrollment(mToken, requestId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index ba1dc6d..cbff8b1 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -84,11 +84,11 @@
     void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
 
     // Start fingerprint enrollment
-    void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+    long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
             String opPackageName, int enrollReason);
 
     // Cancel enrollment in progress
-    void cancelEnrollment(IBinder token);
+    void cancelEnrollment(IBinder token, long requestId);
 
     // Any errors resulting from this call will be returned to the listener
     void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/hdmi/DeviceFeatures.java b/core/java/android/hardware/hdmi/DeviceFeatures.java
new file mode 100644
index 0000000..dc530ff
--- /dev/null
+++ b/core/java/android/hardware/hdmi/DeviceFeatures.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Immutable class that stores support status for features in the [Device Features] operand.
+ * Each feature may be supported, be not supported, or have an unknown support status.
+ *
+ * @hide
+ */
+public class DeviceFeatures {
+
+    @IntDef({
+            FEATURE_NOT_SUPPORTED,
+            FEATURE_SUPPORTED,
+            FEATURE_SUPPORT_UNKNOWN
+    })
+    public @interface FeatureSupportStatus {};
+
+    public static final int FEATURE_NOT_SUPPORTED = 0;
+    public static final int FEATURE_SUPPORTED = 1;
+    public static final int FEATURE_SUPPORT_UNKNOWN = 2;
+
+    /**
+     * Instance representing no knowledge of any feature's support.
+     */
+    @NonNull
+    public static final DeviceFeatures ALL_FEATURES_SUPPORT_UNKNOWN =
+            new Builder(FEATURE_SUPPORT_UNKNOWN).build();
+
+    /**
+     * Instance representing no support for any feature.
+     */
+    @NonNull
+    public static final DeviceFeatures NO_FEATURES_SUPPORTED =
+            new Builder(FEATURE_NOT_SUPPORTED).build();
+
+    @FeatureSupportStatus private final int mRecordTvScreenSupport;
+    @FeatureSupportStatus private final int mSetOsdStringSupport;
+    @FeatureSupportStatus private final int mDeckControlSupport;
+    @FeatureSupportStatus private final int mSetAudioRateSupport;
+    @FeatureSupportStatus private final int mArcTxSupport;
+    @FeatureSupportStatus private final int mArcRxSupport;
+    @FeatureSupportStatus private final int mSetAudioVolumeLevelSupport;
+
+    private DeviceFeatures(@NonNull Builder builder) {
+        this.mRecordTvScreenSupport = builder.mRecordTvScreenSupport;
+        this.mSetOsdStringSupport = builder.mOsdStringSupport;
+        this.mDeckControlSupport = builder.mDeckControlSupport;
+        this.mSetAudioRateSupport = builder.mSetAudioRateSupport;
+        this.mArcTxSupport = builder.mArcTxSupport;
+        this.mArcRxSupport = builder.mArcRxSupport;
+        this.mSetAudioVolumeLevelSupport = builder.mSetAudioVolumeLevelSupport;
+    }
+
+    /**
+     * Converts an instance to a builder.
+     */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    /**
+     * Constructs an instance from a [Device Features] operand.
+     *
+     * Bit 7 of [Device Features] is currently ignored. It indicates whether the operand spans more
+     * than one byte, but only the first byte contains information as of CEC 2.0.
+     *
+     * @param deviceFeaturesOperand The [Device Features] operand to parse.
+     * @return Instance representing the [Device Features] operand.
+     */
+    @NonNull
+    public static DeviceFeatures fromOperand(@NonNull byte[] deviceFeaturesOperand) {
+        Builder builder = new Builder(FEATURE_SUPPORT_UNKNOWN);
+
+        // Read feature support status from the bits of [Device Features]
+        if (deviceFeaturesOperand.length >= 1) {
+            byte b = deviceFeaturesOperand[0];
+            builder
+                    .setRecordTvScreenSupport(bitToFeatureSupportStatus(((b >> 6) & 1) == 1))
+                    .setSetOsdStringSupport(bitToFeatureSupportStatus(((b >> 5) & 1) == 1))
+                    .setDeckControlSupport(bitToFeatureSupportStatus(((b >> 4) & 1) == 1))
+                    .setSetAudioRateSupport(bitToFeatureSupportStatus(((b >> 3) & 1) == 1))
+                    .setArcTxSupport(bitToFeatureSupportStatus(((b >> 2) & 1) == 1))
+                    .setArcRxSupport(bitToFeatureSupportStatus(((b >> 1) & 1) == 1))
+                    .setSetAudioVolumeLevelSupport(bitToFeatureSupportStatus((b & 1) == 1));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Returns the input that is not {@link #FEATURE_SUPPORT_UNKNOWN}. If neither is equal to
+     * {@link #FEATURE_SUPPORT_UNKNOWN}, returns the second input.
+     */
+    private static @FeatureSupportStatus int updateFeatureSupportStatus(
+            @FeatureSupportStatus int oldStatus, @FeatureSupportStatus int newStatus) {
+        if (newStatus == FEATURE_SUPPORT_UNKNOWN) {
+            return oldStatus;
+        } else {
+            return newStatus;
+        }
+    }
+
+    /**
+     * Returns the [Device Features] operand corresponding to this instance.
+     * {@link #FEATURE_SUPPORT_UNKNOWN} maps to 0, indicating no support.
+     *
+     * As of CEC 2.0, the returned byte array will always be of length 1.
+     */
+    @NonNull
+    public byte[] toOperand() {
+        byte result = 0;
+
+        if (mRecordTvScreenSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 6);
+        if (mSetOsdStringSupport == FEATURE_SUPPORTED) result = (byte) (1 << 5);
+        if (mDeckControlSupport == FEATURE_SUPPORTED) result = (byte) (1 << 4);
+        if (mSetAudioRateSupport == FEATURE_SUPPORTED) result = (byte) (1 << 3);
+        if (mArcTxSupport == FEATURE_SUPPORTED) result = (byte) (1 << 2);
+        if (mArcRxSupport == FEATURE_SUPPORTED) result = (byte) (1 << 1);
+        if (mSetAudioVolumeLevelSupport == FEATURE_SUPPORTED) result = (byte) 1;
+
+        return new byte[]{ result };
+    }
+
+    @FeatureSupportStatus
+    private static int bitToFeatureSupportStatus(boolean bit) {
+        return bit ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED;
+    }
+
+    /**
+     * Returns whether the device is a TV that supports <Record TV Screen>.
+     */
+    @FeatureSupportStatus
+    public int getRecordTvScreenSupport() {
+        return mRecordTvScreenSupport;
+    }
+
+    /**
+     * Returns whether the device is a TV that supports <Set OSD String>.
+     */
+    @FeatureSupportStatus
+    public int getSetOsdStringSupport() {
+        return mSetOsdStringSupport;
+    }
+
+    /**
+     * Returns whether the device supports being controlled by Deck Control.
+     */
+    @FeatureSupportStatus
+    public int getDeckControlSupport() {
+        return mDeckControlSupport;
+    }
+
+    /**
+     * Returns whether the device is a Source that supports <Set Audio Rate>.
+     */
+    @FeatureSupportStatus
+    public int getSetAudioRateSupport() {
+        return mSetAudioRateSupport;
+    }
+
+    /**
+     * Returns whether the device is a Sink that supports ARC Tx.
+     */
+    @FeatureSupportStatus
+    public int getArcTxSupport() {
+        return mArcTxSupport;
+    }
+
+    /**
+     * Returns whether the device is a Source that supports ARC Rx.
+     */
+    @FeatureSupportStatus
+    public int getArcRxSupport() {
+        return mArcRxSupport;
+    }
+
+    /**
+     * Returns whether the device supports <Set Audio Volume Level>.
+     */
+    @FeatureSupportStatus
+    public int getSetAudioVolumeLevelSupport() {
+        return mSetAudioVolumeLevelSupport;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof DeviceFeatures)) {
+            return false;
+        }
+
+        DeviceFeatures other = (DeviceFeatures) obj;
+        return mRecordTvScreenSupport == other.mRecordTvScreenSupport
+                && mSetOsdStringSupport == other.mSetOsdStringSupport
+                && mDeckControlSupport == other.mDeckControlSupport
+                && mSetAudioRateSupport == other.mSetAudioRateSupport
+                && mArcTxSupport == other.mArcTxSupport
+                && mArcRxSupport == other.mArcRxSupport
+                && mSetAudioVolumeLevelSupport == other.mSetAudioVolumeLevelSupport;
+    }
+
+    @Override
+    public int hashCode() {
+        return java.util.Objects.hash(
+                mRecordTvScreenSupport,
+                mSetOsdStringSupport,
+                mDeckControlSupport,
+                mSetAudioRateSupport,
+                mArcTxSupport,
+                mArcRxSupport,
+                mSetAudioVolumeLevelSupport
+        );
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("Device features: ");
+        s.append("record_tv_screen: ")
+                .append(featureSupportStatusToString(mRecordTvScreenSupport)).append(" ");
+        s.append("set_osd_string: ")
+                .append(featureSupportStatusToString(mSetOsdStringSupport)).append(" ");
+        s.append("deck_control: ")
+                .append(featureSupportStatusToString(mDeckControlSupport)).append(" ");
+        s.append("set_audio_rate: ")
+                .append(featureSupportStatusToString(mSetAudioRateSupport)).append(" ");
+        s.append("arc_tx: ")
+                .append(featureSupportStatusToString(mArcTxSupport)).append(" ");
+        s.append("arc_rx: ")
+                .append(featureSupportStatusToString(mArcRxSupport)).append(" ");
+        s.append("set_audio_volume_level: ")
+                .append(featureSupportStatusToString(mSetAudioVolumeLevelSupport)).append(" ");
+        return s.toString();
+    }
+
+    @NonNull
+    private static String featureSupportStatusToString(@FeatureSupportStatus int status) {
+        switch (status) {
+            case FEATURE_SUPPORTED:
+                return "Y";
+            case FEATURE_NOT_SUPPORTED:
+                return "N";
+            case FEATURE_SUPPORT_UNKNOWN:
+            default:
+                return "?";
+        }
+    }
+
+    /**
+     * Builder for {@link DeviceFeatures} instances.
+     */
+    public static final class Builder {
+        @FeatureSupportStatus private int mRecordTvScreenSupport;
+        @FeatureSupportStatus private int mOsdStringSupport;
+        @FeatureSupportStatus private int mDeckControlSupport;
+        @FeatureSupportStatus private int mSetAudioRateSupport;
+        @FeatureSupportStatus private int mArcTxSupport;
+        @FeatureSupportStatus private int mArcRxSupport;
+        @FeatureSupportStatus private int mSetAudioVolumeLevelSupport;
+
+        private Builder(@FeatureSupportStatus int defaultFeatureSupportStatus) {
+            mRecordTvScreenSupport = defaultFeatureSupportStatus;
+            mOsdStringSupport = defaultFeatureSupportStatus;
+            mDeckControlSupport = defaultFeatureSupportStatus;
+            mSetAudioRateSupport = defaultFeatureSupportStatus;
+            mArcTxSupport = defaultFeatureSupportStatus;
+            mArcRxSupport = defaultFeatureSupportStatus;
+            mSetAudioVolumeLevelSupport = defaultFeatureSupportStatus;
+        }
+
+        private Builder(DeviceFeatures info) {
+            mRecordTvScreenSupport = info.getRecordTvScreenSupport();
+            mOsdStringSupport = info.getSetOsdStringSupport();
+            mDeckControlSupport = info.getDeckControlSupport();
+            mSetAudioRateSupport = info.getSetAudioRateSupport();
+            mArcTxSupport = info.getArcTxSupport();
+            mArcRxSupport = info.getArcRxSupport();
+            mSetAudioVolumeLevelSupport = info.getSetAudioVolumeLevelSupport();
+        }
+
+        /**
+         * Creates a new {@link DeviceFeatures} object.
+         */
+        public DeviceFeatures build() {
+            return new DeviceFeatures(this);
+        }
+
+        /**
+         * Sets the value for {@link #getRecordTvScreenSupport()}.
+         */
+        @NonNull
+        public Builder setRecordTvScreenSupport(@FeatureSupportStatus int recordTvScreenSupport) {
+            mRecordTvScreenSupport = recordTvScreenSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getSetOsdStringSupport()}.
+         */
+        @NonNull
+        public Builder setSetOsdStringSupport(@FeatureSupportStatus int setOsdStringSupport) {
+            mOsdStringSupport = setOsdStringSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeckControlSupport()}.
+         */
+        @NonNull
+        public Builder setDeckControlSupport(@FeatureSupportStatus int deckControlSupport) {
+            mDeckControlSupport = deckControlSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getSetAudioRateSupport()}.
+         */
+        @NonNull
+        public Builder setSetAudioRateSupport(@FeatureSupportStatus int setAudioRateSupport) {
+            mSetAudioRateSupport = setAudioRateSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getArcTxSupport()}.
+         */
+        @NonNull
+        public Builder setArcTxSupport(@FeatureSupportStatus int arcTxSupport) {
+            mArcTxSupport = arcTxSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getArcRxSupport()}.
+         */
+        @NonNull
+        public Builder setArcRxSupport(@FeatureSupportStatus int arcRxSupport) {
+            mArcRxSupport = arcRxSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getSetAudioRateSupport()}.
+         */
+        @NonNull
+        public Builder setSetAudioVolumeLevelSupport(
+                @FeatureSupportStatus int setAudioVolumeLevelSupport) {
+            mSetAudioVolumeLevelSupport = setAudioVolumeLevelSupport;
+            return this;
+        }
+
+        /**
+         * Updates all fields with those in a 'new' instance of {@link DeviceFeatures}.
+         * All fields are replaced with those in the new instance, except when the field is
+         * {@link #FEATURE_SUPPORT_UNKNOWN} in the new instance.
+         */
+        @NonNull
+        public Builder update(DeviceFeatures newDeviceFeatures) {
+            mRecordTvScreenSupport = updateFeatureSupportStatus(mRecordTvScreenSupport,
+                    newDeviceFeatures.getRecordTvScreenSupport());
+            mOsdStringSupport = updateFeatureSupportStatus(mOsdStringSupport,
+                    newDeviceFeatures.getSetOsdStringSupport());
+            mDeckControlSupport = updateFeatureSupportStatus(mDeckControlSupport,
+                    newDeviceFeatures.getDeckControlSupport());
+            mSetAudioRateSupport = updateFeatureSupportStatus(mSetAudioRateSupport,
+                    newDeviceFeatures.getSetAudioRateSupport());
+            mArcTxSupport = updateFeatureSupportStatus(mArcTxSupport,
+                    newDeviceFeatures.getArcTxSupport());
+            mArcRxSupport = updateFeatureSupportStatus(mArcRxSupport,
+                    newDeviceFeatures.getArcRxSupport());
+            mSetAudioVolumeLevelSupport = updateFeatureSupportStatus(mSetAudioVolumeLevelSupport,
+                    newDeviceFeatures.getSetAudioVolumeLevelSupport());
+            return this;
+        }
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
index c0b177d..e3ea4e2 100644
--- a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
@@ -68,6 +68,9 @@
      */
     public static final int ADDR_INTERNAL = 0;
 
+    /** Invalid or uninitialized logical address */
+    public static final int ADDR_INVALID = -1;
+
     /**
      * Physical address used to indicate the source comes from internal device. The physical address
      * of TV(0) is used.
@@ -83,7 +86,15 @@
     /** Invalid device ID */
     public static final int ID_INVALID = 0xFFFF;
 
-    /** Device info used to indicate an inactivated device. */
+    /** Unknown vendor ID */
+    public static final int VENDOR_ID_UNKNOWN = 0xFFFFFF;
+
+    /**
+     * Instance that represents an inactive device.
+     * Can be passed to an input change listener to indicate that the active source
+     * yielded its status, allowing the listener to take an appropriate action such as
+     * switching to another input.
+     */
     public static final HdmiDeviceInfo INACTIVE_DEVICE = new HdmiDeviceInfo();
 
     private static final int HDMI_DEVICE_TYPE_CEC = 0;
@@ -109,10 +120,11 @@
     private final int mLogicalAddress;
     private final int mDeviceType;
     @HdmiCecVersion
-    private final int mHdmiCecVersion;
+    private final int mCecVersion;
     private final int mVendorId;
     private final String mDisplayName;
     private final int mDevicePowerStatus;
+    private final DeviceFeatures mDeviceFeatures;
 
     // MHL only parameters.
     private final int mDeviceId;
@@ -137,14 +149,22 @@
                             int powerStatus = source.readInt();
                             String displayName = source.readString();
                             int cecVersion = source.readInt();
-                            return new HdmiDeviceInfo(logicalAddress, physicalAddress, portId,
-                                    deviceType, vendorId, displayName, powerStatus, cecVersion);
+                            return cecDeviceBuilder()
+                                    .setLogicalAddress(logicalAddress)
+                                    .setPhysicalAddress(physicalAddress)
+                                    .setPortId(portId)
+                                    .setDeviceType(deviceType)
+                                    .setVendorId(vendorId)
+                                    .setDisplayName(displayName)
+                                    .setDevicePowerStatus(powerStatus)
+                                    .setCecVersion(cecVersion)
+                                    .build();
                         case HDMI_DEVICE_TYPE_MHL:
                             int deviceId = source.readInt();
                             int adopterId = source.readInt();
-                            return new HdmiDeviceInfo(physicalAddress, portId, adopterId, deviceId);
+                            return mhlDevice(physicalAddress, portId, adopterId, deviceId);
                         case HDMI_DEVICE_TYPE_HARDWARE:
-                            return new HdmiDeviceInfo(physicalAddress, portId);
+                            return hardwarePort(physicalAddress, portId);
                         case HDMI_DEVICE_TYPE_INACTIVE:
                             return HdmiDeviceInfo.INACTIVE_DEVICE;
                         default:
@@ -159,97 +179,82 @@
             };
 
     /**
-     * Constructor. Used to initialize the instance for CEC device.
+     * Constructor. Initializes the instance representing an inactive device.
+     * Can be passed to an input change listener to indicate that the active source
+     * yielded its status, allowing the listener to take an appropriate action such as
+     * switching to another input.
      *
-     * @param logicalAddress logical address of HDMI-CEC device
-     * @param physicalAddress physical address of HDMI-CEC device
-     * @param portId HDMI port ID (1 for HDMI1)
-     * @param deviceType type of device
-     * @param vendorId vendor id of device. Used for vendor specific command.
-     * @param displayName name of device
-     * @param powerStatus device power status
-     * @hide
+     * @deprecated Use {@link #INACTIVE_DEVICE} instead.
      */
-    public HdmiDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
-            int vendorId, String displayName, int powerStatus, int hdmiCecVersion) {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_CEC;
-        mPhysicalAddress = physicalAddress;
-        mPortId = portId;
+    @Deprecated
+    public HdmiDeviceInfo() {
+        mHdmiDeviceType = HDMI_DEVICE_TYPE_INACTIVE;
+        mPhysicalAddress = PATH_INVALID;
+        mId = ID_INVALID;
 
-        mId = idForCecDevice(logicalAddress);
-        mLogicalAddress = logicalAddress;
-        mDeviceType = deviceType;
-        mHdmiCecVersion = hdmiCecVersion;
-        mVendorId = vendorId;
-        mDevicePowerStatus = powerStatus;
-        mDisplayName = displayName;
-
-        mDeviceId = -1;
-        mAdopterId = -1;
-    }
-
-    /**
-     * Constructor. Used to initialize the instance for CEC device.
-     *
-     * @param logicalAddress logical address of HDMI-CEC device
-     * @param physicalAddress physical address of HDMI-CEC device
-     * @param portId HDMI port ID (1 for HDMI1)
-     * @param deviceType type of device
-     * @param vendorId vendor id of device. Used for vendor specific command.
-     * @param displayName name of device
-     * @param powerStatus device power status
-     * @hide
-     */
-    public HdmiDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
-            int vendorId, String displayName, int powerStatus) {
-        this(logicalAddress, physicalAddress, portId, deviceType,
-                vendorId, displayName, powerStatus, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-    }
-
-    /**
-     * Constructor. Used to initialize the instance for CEC device.
-     *
-     * @param logicalAddress  logical address of HDMI-CEC device
-     * @param physicalAddress physical address of HDMI-CEC device
-     * @param portId          HDMI port ID (1 for HDMI1)
-     * @param deviceType      type of device
-     * @param vendorId        vendor id of device. Used for vendor specific command.
-     * @param displayName     name of device
-     * @hide
-     */
-    public HdmiDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
-            int vendorId, String displayName) {
-        this(logicalAddress, physicalAddress, portId, deviceType,
-                vendorId, displayName, HdmiControlManager.POWER_STATUS_UNKNOWN,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-    }
-
-    /**
-     * Constructor. Used to initialize the instance for device representing hardware port.
-     *
-     * @param physicalAddress physical address of the port
-     * @param portId HDMI port ID (1 for HDMI1)
-     * @hide
-     */
-    public HdmiDeviceInfo(int physicalAddress, int portId) {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_HARDWARE;
-        mPhysicalAddress = physicalAddress;
-        mPortId = portId;
-
-        mId = idForHardware(portId);
-        mLogicalAddress = -1;
-        mDeviceType = DEVICE_RESERVED;
-        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
-        mVendorId = 0;
+        mLogicalAddress = ADDR_INVALID;
+        mDeviceType = DEVICE_INACTIVE;
+        mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+        mPortId = PORT_INVALID;
         mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
-        mDisplayName = "HDMI" + portId;
+        mDisplayName = "Inactive";
+        mVendorId = 0;
+        mDeviceFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN;
 
         mDeviceId = -1;
         mAdopterId = -1;
     }
 
     /**
-     * Constructor. Used to initialize the instance for MHL device.
+     * Converts an instance to a builder.
+     *
+     * @hide
+     */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    private HdmiDeviceInfo(Builder builder) {
+        this.mHdmiDeviceType = builder.mHdmiDeviceType;
+        this.mPhysicalAddress = builder.mPhysicalAddress;
+        this.mPortId = builder.mPortId;
+        this.mLogicalAddress = builder.mLogicalAddress;
+        this.mDeviceType = builder.mDeviceType;
+        this.mCecVersion = builder.mCecVersion;
+        this.mVendorId = builder.mVendorId;
+        this.mDisplayName = builder.mDisplayName;
+        this.mDevicePowerStatus = builder.mDevicePowerStatus;
+        this.mDeviceFeatures = builder.mDeviceFeatures;
+        this.mDeviceId = builder.mDeviceId;
+        this.mAdopterId = builder.mAdopterId;
+
+        switch (mHdmiDeviceType) {
+            case HDMI_DEVICE_TYPE_MHL:
+                this.mId = idForMhlDevice(mPortId);
+                break;
+            case HDMI_DEVICE_TYPE_HARDWARE:
+                this.mId = idForHardware(mPortId);
+                break;
+            case HDMI_DEVICE_TYPE_CEC:
+                this.mId = idForCecDevice(mLogicalAddress);
+                break;
+            case HDMI_DEVICE_TYPE_INACTIVE:
+            default:
+                this.mId = ID_INVALID;
+        }
+    }
+
+    /**
+     * Creates a Builder for an {@link HdmiDeviceInfo} representing a CEC device.
+     *
+     * @hide
+     */
+    public static Builder cecDeviceBuilder() {
+        return new Builder(HDMI_DEVICE_TYPE_CEC);
+    }
+
+    /**
+     * Creates an {@link HdmiDeviceInfo} representing an MHL device.
      *
      * @param physicalAddress physical address of HDMI device
      * @param portId portId HDMI port ID (1 for HDMI1)
@@ -257,44 +262,32 @@
      * @param deviceId device id of MHL
      * @hide
      */
-    public HdmiDeviceInfo(int physicalAddress, int portId, int adopterId, int deviceId) {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_MHL;
-        mPhysicalAddress = physicalAddress;
-        mPortId = portId;
-
-        mId = idForMhlDevice(portId);
-        mLogicalAddress = -1;
-        mDeviceType = DEVICE_RESERVED;
-        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
-        mVendorId = 0;
-        mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
-        mDisplayName = "Mobile";
-
-        mDeviceId = adopterId;
-        mAdopterId = deviceId;
+    public static HdmiDeviceInfo mhlDevice(
+            int physicalAddress, int portId, int adopterId, int deviceId) {
+        return new Builder(HDMI_DEVICE_TYPE_MHL)
+                .setPhysicalAddress(physicalAddress)
+                .setPortId(portId)
+                .setVendorId(0)
+                .setDisplayName("Mobile")
+                .setDeviceId(adopterId)
+                .setAdopterId(deviceId)
+                .build();
     }
 
     /**
-     * Constructor. Used to initialize the instance representing an inactivated device.
-     * Can be passed input change listener to indicate the active source yielded
-     * its status, hence the listener should take an appropriate action such as
-     * switching to other input.
+     * Creates an {@link HdmiDeviceInfo} representing a hardware port.
+     *
+     * @param physicalAddress physical address of the port
+     * @param portId HDMI port ID (1 for HDMI1)
+     * @hide
      */
-    public HdmiDeviceInfo() {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_INACTIVE;
-        mPhysicalAddress = PATH_INVALID;
-        mId = ID_INVALID;
-
-        mLogicalAddress = -1;
-        mDeviceType = DEVICE_INACTIVE;
-        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
-        mPortId = PORT_INVALID;
-        mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
-        mDisplayName = "Inactive";
-        mVendorId = 0;
-
-        mDeviceId = -1;
-        mAdopterId = -1;
+    public static HdmiDeviceInfo hardwarePort(int physicalAddress, int portId) {
+        return new Builder(HDMI_DEVICE_TYPE_HARDWARE)
+                .setPhysicalAddress(physicalAddress)
+                .setPortId(portId)
+                .setVendorId(0)
+                .setDisplayName("HDMI" + portId)
+                .build();
     }
 
     /**
@@ -305,6 +298,15 @@
     }
 
     /**
+     * Returns the CEC features that this device supports.
+     *
+     * @hide
+     */
+    public DeviceFeatures getDeviceFeatures() {
+        return mDeviceFeatures;
+    }
+
+    /**
      * Returns the id to be used for CEC device.
      *
      * @param address logical address of CEC device
@@ -372,7 +374,7 @@
      */
     @HdmiCecVersion
     public int getCecVersion() {
-        return mHdmiCecVersion;
+        return mCecVersion;
     }
 
     /**
@@ -485,7 +487,7 @@
                 dest.writeInt(mVendorId);
                 dest.writeInt(mDevicePowerStatus);
                 dest.writeString(mDisplayName);
-                dest.writeInt(mHdmiCecVersion);
+                dest.writeInt(mCecVersion);
                 break;
             case HDMI_DEVICE_TYPE_MHL:
                 dest.writeInt(mDeviceId);
@@ -508,7 +510,7 @@
                 s.append("logical_address: ").append(String.format("0x%02X", mLogicalAddress));
                 s.append(" ");
                 s.append("device_type: ").append(mDeviceType).append(" ");
-                s.append("cec_version: ").append(mHdmiCecVersion).append(" ");
+                s.append("cec_version: ").append(mCecVersion).append(" ");
                 s.append("vendor_id: ").append(mVendorId).append(" ");
                 s.append("display_name: ").append(mDisplayName).append(" ");
                 s.append("power_status: ").append(mDevicePowerStatus).append(" ");
@@ -531,6 +533,11 @@
         s.append("physical_address: ").append(String.format("0x%04X", mPhysicalAddress));
         s.append(" ");
         s.append("port_id: ").append(mPortId);
+
+        if (mHdmiDeviceType == HDMI_DEVICE_TYPE_CEC) {
+            s.append("\n  ").append(mDeviceFeatures.toString());
+        }
+
         return s.toString();
     }
 
@@ -546,7 +553,7 @@
                 && mPortId == other.mPortId
                 && mLogicalAddress == other.mLogicalAddress
                 && mDeviceType == other.mDeviceType
-                && mHdmiCecVersion == other.mHdmiCecVersion
+                && mCecVersion == other.mCecVersion
                 && mVendorId == other.mVendorId
                 && mDevicePowerStatus == other.mDevicePowerStatus
                 && mDisplayName.equals(other.mDisplayName)
@@ -562,11 +569,180 @@
                 mPortId,
                 mLogicalAddress,
                 mDeviceType,
-                mHdmiCecVersion,
+                mCecVersion,
                 mVendorId,
                 mDevicePowerStatus,
                 mDisplayName,
                 mDeviceId,
                 mAdopterId);
     }
+
+    /**
+     * Builder for {@link HdmiDeviceInfo} instances.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        // Required parameters
+        private final int mHdmiDeviceType;
+
+        // Common parameters
+        private int mPhysicalAddress = PATH_INVALID;
+        private int mPortId = PORT_INVALID;
+
+        // CEC parameters
+        private int mLogicalAddress = ADDR_INVALID;
+        private int mDeviceType = DEVICE_RESERVED;
+        @HdmiCecVersion
+        private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+        private int mVendorId = VENDOR_ID_UNKNOWN;
+        private String mDisplayName = "";
+        private int mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+        private DeviceFeatures mDeviceFeatures;
+
+        // MHL parameters
+        private int mDeviceId = -1;
+        private int mAdopterId = -1;
+
+        private Builder(int hdmiDeviceType) {
+            mHdmiDeviceType = hdmiDeviceType;
+            if (hdmiDeviceType == HDMI_DEVICE_TYPE_CEC) {
+                mDeviceFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN;
+            } else {
+                mDeviceFeatures = DeviceFeatures.NO_FEATURES_SUPPORTED;
+            }
+        }
+
+        private Builder(@NonNull HdmiDeviceInfo hdmiDeviceInfo) {
+            mHdmiDeviceType = hdmiDeviceInfo.mHdmiDeviceType;
+            mPhysicalAddress = hdmiDeviceInfo.mPhysicalAddress;
+            mPortId = hdmiDeviceInfo.mPortId;
+            mLogicalAddress = hdmiDeviceInfo.mLogicalAddress;
+            mDeviceType = hdmiDeviceInfo.mDeviceType;
+            mCecVersion = hdmiDeviceInfo.mCecVersion;
+            mVendorId = hdmiDeviceInfo.mVendorId;
+            mDisplayName = hdmiDeviceInfo.mDisplayName;
+            mDevicePowerStatus = hdmiDeviceInfo.mDevicePowerStatus;
+            mDeviceId = hdmiDeviceInfo.mDeviceId;
+            mAdopterId = hdmiDeviceInfo.mAdopterId;
+            mDeviceFeatures = hdmiDeviceInfo.mDeviceFeatures;
+        }
+
+        /**
+         * Create a new {@link HdmiDeviceInfo} object.
+         */
+        @NonNull
+        public HdmiDeviceInfo build() {
+            return new HdmiDeviceInfo(this);
+        }
+
+        /**
+         * Sets the value for {@link #getPhysicalAddress()}.
+         */
+        @NonNull
+        public Builder setPhysicalAddress(int physicalAddress) {
+            mPhysicalAddress = physicalAddress;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getPortId()}.
+         */
+        @NonNull
+        public Builder setPortId(int portId) {
+            mPortId = portId;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getLogicalAddress()}.
+         */
+        @NonNull
+        public Builder setLogicalAddress(int logicalAddress) {
+            mLogicalAddress = logicalAddress;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeviceType()}.
+         */
+        @NonNull
+        public Builder setDeviceType(int deviceType) {
+            mDeviceType = deviceType;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getCecVersion()}.
+         */
+        @NonNull
+        public Builder setCecVersion(int hdmiCecVersion) {
+            mCecVersion = hdmiCecVersion;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getVendorId()}.
+         */
+        @NonNull
+        public Builder setVendorId(int vendorId) {
+            mVendorId = vendorId;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDisplayName()}.
+         */
+        @NonNull
+        public Builder setDisplayName(@NonNull String displayName) {
+            mDisplayName = displayName;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDevicePowerStatus()}.
+         */
+        @NonNull
+        public Builder setDevicePowerStatus(int devicePowerStatus) {
+            mDevicePowerStatus = devicePowerStatus;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeviceFeatures()}.
+         */
+        @NonNull
+        public Builder setDeviceFeatures(DeviceFeatures deviceFeatures) {
+            this.mDeviceFeatures = deviceFeatures;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeviceId()}.
+         */
+        @NonNull
+        public Builder setDeviceId(int deviceId) {
+            mDeviceId = deviceId;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getAdopterId()}.
+         */
+        @NonNull
+        public Builder setAdopterId(int adopterId) {
+            mAdopterId = adopterId;
+            return this;
+        }
+
+        /**
+         * Updates the value for {@link #getDeviceFeatures()} with a new set of device features.
+         * New information overrides the old, except when feature support was unknown.
+         */
+        @NonNull
+        public Builder updateDeviceFeatures(DeviceFeatures deviceFeatures) {
+            mDeviceFeatures = mDeviceFeatures.toBuilder().update(deviceFeatures).build();
+            return this;
+        }
+    }
 }
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2ac194b..0304815 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -50,6 +50,10 @@
     // Reports whether the hardware supports the given keys; returns true if successful
     boolean hasKeys(int deviceId, int sourceMask, in int[] keyCodes, out boolean[] keyExists);
 
+    // Returns the keyCode produced when pressing the key at the specified location, given the
+    // active keyboard layout.
+    int getKeyCodeForKeyLocation(int deviceId, in int locationKeyCode);
+
     // Temporarily changes the pointer speed.
     void tryPointerSpeed(int speed);
 
diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java
index c673e7a..a5b9a2a 100644
--- a/core/java/android/hardware/input/InputDeviceIdentifier.java
+++ b/core/java/android/hardware/input/InputDeviceIdentifier.java
@@ -16,7 +16,9 @@
 
 package android.hardware.input;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -28,12 +30,13 @@
  *
  * @hide
  */
+@TestApi
 public final class InputDeviceIdentifier implements Parcelable {
     private final String mDescriptor;
     private final int mVendorId;
     private final int mProductId;
 
-    public InputDeviceIdentifier(String descriptor, int vendorId, int productId) {
+    public InputDeviceIdentifier(@NonNull String descriptor, int vendorId, int productId) {
         this.mDescriptor = descriptor;
         this.mVendorId = vendorId;
         this.mProductId = productId;
@@ -51,12 +54,13 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mDescriptor);
         dest.writeInt(mVendorId);
         dest.writeInt(mProductId);
     }
 
+    @NonNull
     public String getDescriptor() {
         return mDescriptor;
     }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6253fb9..cbc8373 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -58,6 +58,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputMonitor;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.VerifiedInputEvent;
@@ -215,14 +216,6 @@
     public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L;
 
     /**
-     * Check whether apps are using FLAG_SLIPPERY for their windows. We expect that this flag is
-     * only used by the system components. If so, we can lock it down.
-     * @hide
-     */
-    @ChangeId
-    public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
-
-    /**
      * Input Event Injection Synchronization Mode: None.
      * Never blocks.  Injection is asynchronous and is assumed always to be successful.
      * @hide
@@ -645,6 +638,30 @@
     }
 
     /**
+     * Returns the descriptors of all supported keyboard layouts appropriate for the specified
+     * input device.
+     * <p>
+     * The input manager consults the built-in keyboard layouts as well as all keyboard layouts
+     * advertised by applications using a {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
+     * </p>
+     *
+     * @param device The input device to query.
+     * @return The ids of all keyboard layouts which are supported by the specified input device.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public List<String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull InputDevice device) {
+        KeyboardLayout[] layouts = getKeyboardLayoutsForInputDevice(device.getIdentifier());
+        List<String> res = new ArrayList<>();
+        for (KeyboardLayout kl : layouts) {
+            res.add(kl.getDescriptor());
+        }
+        return res;
+    }
+
+    /**
      * Gets information about all supported keyboard layouts appropriate
      * for a specific input device.
      * <p>
@@ -658,7 +675,9 @@
      *
      * @hide
      */
-    public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+    @NonNull
+    public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
+            @NonNull InputDeviceIdentifier identifier) {
         try {
             return mIm.getKeyboardLayoutsForInputDevice(identifier);
         } catch (RemoteException ex) {
@@ -688,15 +707,17 @@
     }
 
     /**
-     * Gets the current keyboard layout descriptor for the specified input
-     * device.
+     * Gets the current keyboard layout descriptor for the specified input device.
      *
      * @param identifier Identifier for the input device
-     * @return The keyboard layout descriptor, or null if no keyboard layout has
-     *         been set.
+     * @return The keyboard layout descriptor, or null if no keyboard layout has been set.
+     *
      * @hide
      */
-    public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
+    @TestApi
+    @Nullable
+    public String getCurrentKeyboardLayoutForInputDevice(
+            @NonNull InputDeviceIdentifier identifier) {
         try {
             return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
         } catch (RemoteException ex) {
@@ -705,20 +726,21 @@
     }
 
     /**
-     * Sets the current keyboard layout descriptor for the specified input
-     * device.
+     * Sets the current keyboard layout descriptor for the specified input device.
      * <p>
-     * This method may have the side-effect of causing the input device in
-     * question to be reconfigured.
+     * This method may have the side-effect of causing the input device in question to be
+     * reconfigured.
      * </p>
      *
      * @param identifier The identifier for the input device.
-     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
-     *            must not be null.
+     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
+     *
      * @hide
      */
-    public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
-            String keyboardLayoutDescriptor) {
+    @TestApi
+    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
+    public void setCurrentKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
+            @NonNull String keyboardLayoutDescriptor) {
         if (identifier == null) {
             throw new IllegalArgumentException("identifier must not be null");
         }
@@ -735,11 +757,11 @@
     }
 
     /**
-     * Gets all keyboard layout descriptors that are enabled for the specified
-     * input device.
+     * Gets all keyboard layout descriptors that are enabled for the specified input device.
      *
      * @param identifier The identifier for the input device.
      * @return The keyboard layout descriptors.
+     *
      * @hide
      */
     public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
@@ -757,15 +779,16 @@
     /**
      * Adds the keyboard layout descriptor for the specified input device.
      * <p>
-     * This method may have the side-effect of causing the input device in
-     * question to be reconfigured.
+     * This method may have the side-effect of causing the input device in question to be
+     * reconfigured.
      * </p>
      *
      * @param identifier The identifier for the input device.
-     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
-     *            add.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add.
+     *
      * @hide
      */
+    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
     public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
         if (identifier == null) {
@@ -785,17 +808,19 @@
     /**
      * Removes the keyboard layout descriptor for the specified input device.
      * <p>
-     * This method may have the side-effect of causing the input device in
-     * question to be reconfigured.
+     * This method may have the side-effect of causing the input device in question to be
+     * reconfigured.
      * </p>
      *
      * @param identifier The identifier for the input device.
-     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
-     *            remove.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove.
+     *
      * @hide
      */
-    public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
-            String keyboardLayoutDescriptor) {
+    @TestApi
+    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
+    public void removeKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
+            @NonNull String keyboardLayoutDescriptor) {
         if (identifier == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
@@ -1052,6 +1077,27 @@
         return ret;
     }
 
+    /**
+     * Gets the key code produced by the specified location on a US keyboard layout.
+     * Key code as defined in {@link android.view.KeyEvent}.
+     * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
+     * which can alter their key mapping using country specific keyboard layouts.
+     *
+     * @param deviceId The input device id.
+     * @param locationKeyCode The location of a key on a US keyboard layout.
+     * @return The key code produced when pressing the key at the specified location, given the
+     *         active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
+     *         mapping could not be determined, or if an error occurred.
+     * @hide
+     */
+    public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
+        try {
+            return mIm.getKeyCodeForKeyLocation(deviceId, locationKeyCode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
 
     /**
      * Injects an input event into the event system on behalf of an application.
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 1173c31..f866a2e 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -17,6 +17,7 @@
 package android.hardware.input;
 
 import android.annotation.NonNull;
+import android.graphics.PointF;
 import android.hardware.display.DisplayViewport;
 import android.os.IBinder;
 import android.view.InputEvent;
@@ -79,6 +80,22 @@
     public abstract boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
             @NonNull IBinder toChannelToken);
 
+    /**
+     * Sets the display id that the MouseCursorController will be forced to target. Pass
+     * {@link android.view.Display#INVALID_DISPLAY} to clear the override.
+     */
+    public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId);
+
+    /** Gets the current position of the mouse cursor. */
+    public abstract PointF getCursorPosition();
+
+    /**
+     * Sets the eligibility of windows on a given display for pointer capture. If a display is
+     * marked ineligible, requests to enable pointer capture for windows on that display will be
+     * ignored.
+     */
+    public abstract void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible);
+
     /** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
     public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
 
diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java
index 6599dd2..6e2b56a 100644
--- a/core/java/android/hardware/input/VirtualMouse.java
+++ b/core/java/android/hardware/input/VirtualMouse.java
@@ -20,6 +20,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
+import android.graphics.PointF;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.MotionEvent;
@@ -61,6 +62,8 @@
      * Send a mouse button event to the system.
      *
      * @param event the event
+     * @throws IllegalStateException if the display this mouse is associated with is not currently
+     * targeted
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) {
@@ -76,6 +79,8 @@
      * {@link MotionEvent#AXIS_SCROLL}.
      *
      * @param event the event
+     * @throws IllegalStateException if the display this mouse is associated with is not currently
+     * targeted
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) {
@@ -90,6 +95,8 @@
      * Sends a relative movement event to the system.
      *
      * @param event the event
+     * @throws IllegalStateException if the display this mouse is associated with is not currently
+     * targeted
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) {
@@ -99,4 +106,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Gets the current cursor position.
+     *
+     * @return the position, expressed as x and y coordinates
+     * @throws IllegalStateException if the display this mouse is associated with is not currently
+     * targeted
+     */
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public @NonNull PointF getCursorPosition() {
+        try {
+            return mVirtualDevice.getCursorPosition(mToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 9468ca2..54060cc 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.location;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -118,13 +119,14 @@
      * is newly generated (e.g. any regeneration of a callback client, or generation
      * of a non-equal PendingIntent client), the ID will not be the same.
      *
-     * @return The ID of this ContextHubClient.
+     * @return The ID of this ContextHubClient, in the range [0, 65535].
      */
+    @IntRange(from = 0, to = 65535)
     public int getId() {
         if (mId == null) {
             throw new IllegalStateException("ID was not set");
         }
-        return mId;
+        return (0x0000FFFF & mId);
     }
 
     /**
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 6ea2ac4..4cc001a 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -436,7 +436,8 @@
             mNumAudioSources = in.readInt();
             mIsInitializationRequired = in.readInt() == 1;
             mIsCaptureSupported = in.readInt() == 1;
-            Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
+            Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader(),
+                    BandDescriptor.class);
             mBands = new BandDescriptor[tmp.length];
             for (int i = 0; i < tmp.length; i++) {
                 mBands[i] = (BandDescriptor) tmp[i];
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 7f07af7..459dab1 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.hardware.usb.IUsbOperationInternal;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.ParcelableUsbPort;
@@ -136,7 +137,10 @@
     void resetUsbGadget();
 
     /* Set USB data on or off */
-    boolean enableUsbDataSignal(boolean enable);
+    boolean enableUsbData(in String portId, boolean enable, int operationId, in IUsbOperationInternal callback);
+
+    /* Enable USB data when disabled due to docking event  */
+    void enableUsbDataWhileDocked(in String portId, int operationId, in IUsbOperationInternal callback);
 
     /* Gets the USB Hal Version. */
     int getUsbHalVersion();
@@ -156,9 +160,14 @@
     /* Sets the port's current role. */
     void setPortRoles(in String portId, int powerRole, int dataRole);
 
+    /* Limit power transfer in & out of the port within the allowed limit by the USB
+     * specification.
+     */
+    void enableLimitPowerTransfer(in String portId, boolean limit, int operationId, in IUsbOperationInternal callback);
+
     /* Enable/disable contaminant detection */
     void enableContaminantDetection(in String portId, boolean enable);
 
-   /* Sets USB device connection handler. */
-   void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
+    /* Sets USB device connection handler. */
+    void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
 }
diff --git a/core/java/android/hardware/usb/IUsbOperationInternal.aidl b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
new file mode 100644
index 0000000..3f3bbf6
--- /dev/null
+++ b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+/**
+ * @hide
+ */
+oneway interface IUsbOperationInternal {
+void onOperationComplete(in int status);
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index c29a948..f0e040e 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -36,6 +36,8 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.hardware.usb.gadget.V1_0.GadgetFunction;
 import android.hardware.usb.gadget.V1_2.UsbSpeed;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -48,6 +50,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.StringJoiner;
 
 /**
@@ -517,6 +520,14 @@
     public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024;
 
     /**
+     * Returned when the client has to retry querying the version.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int USB_HAL_RETRY = -2;
+
+    /**
      * The Value for USB hal is not presented.
      *
      * {@hide}
@@ -557,6 +568,14 @@
     public static final int USB_HAL_V1_3 = 13;
 
     /**
+     * Value for USB Hal Version v2.0.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int USB_HAL_V2_0 = 20;
+
+    /**
      * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
@@ -665,6 +684,7 @@
             USB_HAL_V1_1,
             USB_HAL_V1_2,
             USB_HAL_V1_3,
+            USB_HAL_V2_0,
     })
     public @interface UsbHalVersion {}
 
@@ -1169,8 +1189,9 @@
     /**
      * Enable/Disable the USB data signaling.
      * <p>
-     * Enables/Disables USB data path in all the USB ports.
+     * Enables/Disables USB data path of the first port..
      * It will force to stop or restore USB data signaling.
+     * Call UsbPort API if the device has more than one UsbPort.
      * </p>
      *
      * @param enable enable or disable USB data signaling
@@ -1181,11 +1202,11 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_USB)
     public boolean enableUsbDataSignal(boolean enable) {
-        try {
-            return mService.enableUsbDataSignal(enable);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        List<UsbPort> usbPorts = getPorts();
+        if (usbPorts.size() == 1) {
+            return usbPorts.get(0).enableUsbData(enable) == UsbPort.ENABLE_USB_DATA_SUCCESS;
         }
+        return false;
     }
 
     /**
@@ -1271,6 +1292,102 @@
     }
 
     /**
+     * Should only be called by {@link UsbPort#enableLimitPowerTransfer}.
+     * <p>
+     * limits or restores power transfer in and out of USB port.
+     *
+     * @param port USB port for which power transfer has to be limited or restored.
+     * @param limit limit power transfer when true.
+     *              relax power transfer restrictions when false.
+     * @param operationId operationId for the request.
+     * @param callback callback object to be invoked when the operation is complete.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    void enableLimitPowerTransfer(@NonNull UsbPort port, boolean limit, int operationId,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(port, "enableLimitPowerTransfer:port must not be null. opId:"
+                + operationId);
+        try {
+            mService.enableLimitPowerTransfer(port.getId(), limit, operationId, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "enableLimitPowerTransfer failed. opId:" + operationId, e);
+            try {
+                callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+            } catch (RemoteException r) {
+                Log.e(TAG, "enableLimitPowerTransfer failed to call onOperationComplete. opId:"
+                        + operationId, r);
+            }
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Should only be called by {@link UsbPort#enableUsbData}.
+     * <p>
+     * Enables or disables USB data on the specific port.
+     *
+     * @param port USB port for which USB data needs to be enabled or disabled.
+     * @param enable Enable USB data when true.
+     *               Disable USB data when false.
+     * @param operationId operationId for the request.
+     * @param callback callback object to be invoked when the operation is complete.
+     * @return True when the operation is asynchronous. The caller must therefore call
+     *         {@link UsbOperationInternal#waitForOperationComplete} for processing
+     *         the result.
+     *         False when the operation is synchronous. Caller can proceed reading the result
+     *         through {@link UsbOperationInternal#getStatus}
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    boolean enableUsbData(@NonNull UsbPort port, boolean enable, int operationId,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(port, "enableUsbData: port must not be null. opId:" + operationId);
+        try {
+            return mService.enableUsbData(port.getId(), enable, operationId, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "enableUsbData: failed. opId:" + operationId, e);
+            try {
+                callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+            } catch (RemoteException r) {
+                Log.e(TAG, "enableUsbData: failed to call onOperationComplete. opId:"
+                        + operationId, r);
+            }
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Should only be called by {@link UsbPort#enableUsbDataWhileDocked}.
+     * <p>
+     * Enables or disables USB data when disabled due to docking event.
+     *
+     * @param port USB port for which USB data needs to be enabled.
+     * @param operationId operationId for the request.
+     * @param callback callback object to be invoked when the operation is complete.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    void enableUsbDataWhileDocked(@NonNull UsbPort port, int operationId,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(port, "enableUsbDataWhileDocked: port must not be null. opId:"
+                + operationId);
+        try {
+            mService.enableUsbDataWhileDocked(port.getId(), operationId, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "enableUsbDataWhileDocked: failed. opId:" + operationId, e);
+            try {
+                callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+            } catch (RemoteException r) {
+                Log.e(TAG, "enableUsbDataWhileDocked: failed to call onOperationComplete. opId:"
+                        + operationId, r);
+            }
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Sets the component that will handle USB device connection.
      * <p>
      * Setting component allows to specify external USB host manager to handle use cases, where
diff --git a/core/java/android/hardware/usb/UsbOperationInternal.java b/core/java/android/hardware/usb/UsbOperationInternal.java
new file mode 100644
index 0000000..9bc2b38
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbOperationInternal.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.usb;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+/**
+ * UsbOperationInternal allows UsbPort to support both synchronous and
+ * asynchronous function irrespective of whether the underlying hal
+ * method is synchronous or asynchronous.
+ *
+ * @hide
+ */
+public final class UsbOperationInternal extends IUsbOperationInternal.Stub {
+    private static final String TAG = "UsbPortStatus";
+    private final int mOperationID;
+    // Cached portId.
+    private final String mId;
+    // True implies operation did not timeout.
+    private boolean mOperationComplete;
+    private @UsbOperationStatus int mStatus;
+    final ReentrantLock mLock = new ReentrantLock();
+    final Condition mOperationWait  = mLock.newCondition();
+    // Maximum time the caller has to wait for onOperationComplete to be called.
+    private static final int USB_OPERATION_TIMEOUT_MSECS = 5000;
+
+    /**
+     * The requested operation was successfully completed.
+     * Returned in {@link onOperationComplete} and {@link getStatus}.
+     */
+    public static final int USB_OPERATION_SUCCESS = 0;
+
+    /**
+     * The requested operation failed due to internal error.
+     * Returned in {@link onOperationComplete} and {@link getStatus}.
+     */
+    public static final int USB_OPERATION_ERROR_INTERNAL = 1;
+
+    /**
+     * The requested operation failed as it's not supported.
+     * Returned in {@link onOperationComplete} and {@link getStatus}.
+     */
+    public static final int USB_OPERATION_ERROR_NOT_SUPPORTED = 2;
+
+    /**
+     * The requested operation failed as it's not supported.
+     * Returned in {@link onOperationComplete} and {@link getStatus}.
+     */
+    public static final int USB_OPERATION_ERROR_PORT_MISMATCH = 3;
+
+    @IntDef(prefix = { "USB_OPERATION_" }, value = {
+            USB_OPERATION_SUCCESS,
+            USB_OPERATION_ERROR_INTERNAL,
+            USB_OPERATION_ERROR_NOT_SUPPORTED,
+            USB_OPERATION_ERROR_PORT_MISMATCH
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbOperationStatus{}
+
+    UsbOperationInternal(int operationID, String id) {
+        this.mOperationID = operationID;
+        this.mId = id;
+    }
+
+    /**
+     * Hal glue layer would directly call this function when the requested
+     * operation is complete.
+     */
+    @Override
+    public void onOperationComplete(@UsbOperationStatus int status) {
+        mLock.lock();
+        try {
+            mOperationComplete = true;
+            mStatus = status;
+            Log.i(TAG, "Port:" + mId + " opID:" + mOperationID + " status:" + mStatus);
+            mOperationWait.signal();
+        } finally {
+            mLock.unlock();
+        }
+    }
+
+    /**
+     * Caller invokes this function to wait for the operation to be complete.
+     */
+    public void waitForOperationComplete() {
+        mLock.lock();
+        try {
+            long now = System.currentTimeMillis();
+            long deadline = now + USB_OPERATION_TIMEOUT_MSECS;
+            // Wait in loop to overcome spurious wakeups.
+            do {
+                mOperationWait.await(deadline - System.currentTimeMillis(),
+                        TimeUnit.MILLISECONDS);
+            } while (!mOperationComplete && System.currentTimeMillis() < deadline);
+            if (!mOperationComplete) {
+                Log.e(TAG, "Port:" + mId + " opID:" + mOperationID
+                        + " operationComplete not received in " + USB_OPERATION_TIMEOUT_MSECS
+                        + "msecs");
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + " operationComplete interrupted");
+        } finally {
+            mLock.unlock();
+        }
+    }
+
+    public @UsbOperationStatus int getStatus() {
+        return mOperationComplete ? mStatus : USB_OPERATION_ERROR_INTERNAL;
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 274e23f..bef4dea 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,6 +16,10 @@
 
 package android.hardware.usb;
 
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED;
 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED;
 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
@@ -29,20 +33,38 @@
 import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
 import static android.hardware.usb.UsbPortStatus.MODE_NONE;
 import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_DISCONNECTED;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_CONNECTED;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_ENABLED;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_OVERHEAT;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_CONTAMINANT;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DOCK;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DEBUG;
 
 import android.Manifest;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.hardware.usb.UsbOperationInternal;
 import android.hardware.usb.V1_0.Constants;
+import android.os.Binder;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Represents a physical USB port and describes its characteristics.
@@ -51,6 +73,7 @@
  */
 @SystemApi
 public final class UsbPort {
+    private static final String TAG = "UsbPort";
     private final String mId;
     private final int mSupportedModes;
     private final UsbManager mUsbManager;
@@ -64,6 +87,125 @@
      */
     private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
 
+    /**
+     * Counter for tracking UsbOperation operations.
+     */
+    private static final AtomicInteger sUsbOperationCount = new AtomicInteger();
+
+    /**
+     * The {@link #enableUsbData} request was successfully completed.
+     */
+    public static final int ENABLE_USB_DATA_SUCCESS = 0;
+
+    /**
+     * The {@link #enableUsbData} request failed due to internal error.
+     */
+    public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1;
+
+    /**
+     * The {@link #enableUsbData} request failed as it's not supported.
+     */
+    public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2;
+
+    /**
+     * The {@link #enableUsbData} request failed as port id mismatched.
+     */
+    public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3;
+
+    /**
+     * The {@link #enableUsbData} request failed due to other reasons.
+     */
+    public static final int ENABLE_USB_DATA_ERROR_OTHER = 4;
+
+    /** @hide */
+    @IntDef(prefix = { "ENABLE_USB_DATA_" }, value = {
+            ENABLE_USB_DATA_SUCCESS,
+            ENABLE_USB_DATA_ERROR_INTERNAL,
+            ENABLE_USB_DATA_ERROR_NOT_SUPPORTED,
+            ENABLE_USB_DATA_ERROR_PORT_MISMATCH,
+            ENABLE_USB_DATA_ERROR_OTHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface EnableUsbDataStatus{}
+
+    /**
+     * The {@link #enableLimitPowerTransfer} request was successfully completed.
+     */
+    public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0;
+
+    /**
+     * The {@link #enableLimitPowerTransfer} request failed due to internal error.
+     */
+    public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1;
+
+    /**
+     * The {@link #enableLimitPowerTransfer} request failed as it's not supported.
+     */
+    public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2;
+
+    /**
+     * The {@link #enableLimitPowerTransfer} request failed as port id mismatched.
+     */
+    public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3;
+
+    /**
+     * The {@link #enableLimitPowerTransfer} request failed due to other reasons.
+     */
+    public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4;
+
+    /** @hide */
+    @IntDef(prefix = { "ENABLE_LIMIT_POWER_TRANSFER_" }, value = {
+            ENABLE_LIMIT_POWER_TRANSFER_SUCCESS,
+            ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL,
+            ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED,
+            ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH,
+            ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface EnableLimitPowerTransferStatus{}
+
+    /**
+     * The {@link #enableUsbDataWhileDocked} request was successfully completed.
+     */
+    public static final int ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS = 0;
+
+    /**
+     * The {@link #enableUsbDataWhileDocked} request failed due to internal error.
+     */
+    public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL = 1;
+
+    /**
+     * The {@link #enableUsbDataWhileDocked} request failed as it's not supported.
+     */
+    public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED = 2;
+
+    /**
+     * The {@link #enableUsbDataWhileDocked} request failed as port id mismatched.
+     */
+    public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH = 3;
+
+    /**
+     * The {@link #enableUsbDataWhileDocked} request failed as data is still enabled.
+     */
+    public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED = 4;
+
+    /**
+     * The {@link #enableUsbDataWhileDocked} request failed due to other reasons.
+     */
+    public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER = 5;
+
+    /** @hide */
+    @IntDef(prefix = { "ENABLE_USB_DATA_WHILE_DOCKED_" }, value = {
+            ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS,
+            ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL,
+            ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED,
+            ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH,
+            ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED,
+            ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface EnableUsbDataWhileDockedStatus{}
+
     /** @hide */
     public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
             int supportedContaminantProtectionModes,
@@ -157,7 +299,7 @@
      * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
      * </p><p>
      * Note: This function is asynchronous and may fail silently without applying
-     * the requested changes.  If this function does cause a status change to occur then
+     * the operationed changes.  If this function does cause a status change to occur then
      * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
      * </p>
      *
@@ -177,6 +319,133 @@
     }
 
     /**
+     * Enables/Disables Usb data on the port.
+     *
+     * @param enable When true enables USB data if disabled.
+     *               When false disables USB data if enabled.
+     * @return       {@link #ENABLE_USB_DATA_SUCCESS} when request completes successfully or
+     *               {@link #ENABLE_USB_DATA_ERROR_INTERNAL} when request fails due to internal
+     *               error or
+     *               {@link ENABLE_USB_DATA_ERROR_NOT_SUPPORTED} when not supported or
+     *               {@link ENABLE_USB_DATA_ERROR_PORT_MISMATCH} when request fails due to port id
+     *               mismatch or
+     *               {@link ENABLE_USB_DATA_ERROR_OTHER} when fails due to other reasons.
+     */
+    @CheckResult
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @EnableUsbDataStatus int enableUsbData(boolean enable) {
+        // UID is added To minimize operationID overlap between two different packages.
+        int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+        Log.i(TAG, "enableUsbData opId:" + operationId
+                + " callingUid:" + Binder.getCallingUid());
+        UsbOperationInternal opCallback =
+                new UsbOperationInternal(operationId, mId);
+        if (mUsbManager.enableUsbData(this, enable, operationId, opCallback) == true) {
+            opCallback.waitForOperationComplete();
+        }
+
+        int result = opCallback.getStatus();
+        switch (result) {
+            case USB_OPERATION_SUCCESS:
+                return ENABLE_USB_DATA_SUCCESS;
+            case USB_OPERATION_ERROR_INTERNAL:
+                return ENABLE_USB_DATA_ERROR_INTERNAL;
+            case USB_OPERATION_ERROR_NOT_SUPPORTED:
+                return ENABLE_USB_DATA_ERROR_NOT_SUPPORTED;
+            case USB_OPERATION_ERROR_PORT_MISMATCH:
+                return ENABLE_USB_DATA_ERROR_PORT_MISMATCH;
+            default:
+                return ENABLE_USB_DATA_ERROR_OTHER;
+        }
+    }
+
+    /**
+     * Enables Usb data when disabled due to {@link UsbPort#USB_DATA_STATUS_DISABLED_DOCK}
+     *
+     * @return {@link #ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS} when request completes successfully or
+     *         {@link #ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL} when request fails due to
+     *         internal error or
+     *         {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED} when not supported or
+     *         {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH} when request fails due to
+     *         port id mismatch or
+     *         {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED} when request fails as data
+     *         is still enabled or
+     *         {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER} when fails due to other reasons.
+     */
+    @CheckResult
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @EnableUsbDataWhileDockedStatus int enableUsbDataWhileDocked() {
+        // UID is added To minimize operationID overlap between two different packages.
+        int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+        Log.i(TAG, "enableUsbData opId:" + operationId
+                + " callingUid:" + Binder.getCallingUid());
+        UsbPortStatus portStatus = getStatus();
+        if (portStatus != null &&
+                !usbDataStatusToString(portStatus.getUsbDataStatus()).contains("disabled-dock")) {
+            return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED;
+        }
+
+        UsbOperationInternal opCallback =
+                new UsbOperationInternal(operationId, mId);
+        mUsbManager.enableUsbDataWhileDocked(this, operationId, opCallback);
+                opCallback.waitForOperationComplete();
+        int result = opCallback.getStatus();
+        switch (result) {
+            case USB_OPERATION_SUCCESS:
+                return ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS;
+            case USB_OPERATION_ERROR_INTERNAL:
+                return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL;
+            case USB_OPERATION_ERROR_NOT_SUPPORTED:
+                return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED;
+            case USB_OPERATION_ERROR_PORT_MISMATCH:
+                return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH;
+            default:
+                return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER;
+        }
+    }
+
+    /**
+     * Limits power transfer In and out of the port.
+     * <p>
+     * Disables charging and limits sourcing power(when permitted by the USB spec) until
+     * port disconnect event.
+     * </p>
+     * @param enable limits power transfer when true.
+     * @return {@link #ENABLE_LIMIT_POWER_TRANSFER_SUCCESS} when request completes successfully or
+     *         {@link #ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL} when request fails due to
+     *         internal error or
+     *         {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED} when not supported or
+     *         {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH} when request fails due to
+     *         port id mismatch or
+     *         {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER} when fails due to other reasons.
+     */
+    @CheckResult
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @EnableLimitPowerTransferStatus int enableLimitPowerTransfer(boolean enable) {
+        // UID is added To minimize operationID overlap between two different packages.
+        int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+        Log.i(TAG, "enableLimitPowerTransfer opId:" + operationId
+                + " callingUid:" + Binder.getCallingUid());
+        UsbOperationInternal opCallback =
+                new UsbOperationInternal(operationId, mId);
+        mUsbManager.enableLimitPowerTransfer(this, enable, operationId, opCallback);
+        opCallback.waitForOperationComplete();
+        int result = opCallback.getStatus();
+        switch (result) {
+            case USB_OPERATION_SUCCESS:
+                return ENABLE_LIMIT_POWER_TRANSFER_SUCCESS;
+            case USB_OPERATION_ERROR_INTERNAL:
+                return ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL;
+            case USB_OPERATION_ERROR_NOT_SUPPORTED:
+                return ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED;
+            case USB_OPERATION_ERROR_PORT_MISMATCH:
+                return ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH;
+            default:
+                return ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER;
+        }
+    }
+
+    /**
      * @hide
      **/
     public void enableContaminantDetection(boolean enable) {
@@ -274,6 +543,57 @@
     }
 
     /** @hide */
+    public static String usbDataStatusToString(int usbDataStatus) {
+        switch (usbDataStatus) {
+            case USB_DATA_STATUS_UNKNOWN:
+                return "unknown";
+            case USB_DATA_STATUS_ENABLED:
+                return "enabled";
+            case USB_DATA_STATUS_DISABLED_OVERHEAT:
+                return "disabled-overheat";
+            case USB_DATA_STATUS_DISABLED_CONTAMINANT:
+                return "disabled-contaminant";
+            case USB_DATA_STATUS_DISABLED_DOCK:
+                return "disabled-dock";
+            case USB_DATA_STATUS_DISABLED_FORCE:
+                return "disabled-force";
+            case USB_DATA_STATUS_DISABLED_DEBUG:
+                return "disabled-debug";
+            default:
+                return Integer.toString(usbDataStatus);
+        }
+    }
+
+    /** @hide */
+    public static String usbDataStatusToString(int[] usbDataStatus) {
+        StringBuilder modeString = new StringBuilder();
+        if (usbDataStatus == null) {
+            return "unknown";
+        }
+        for (int i = 0; i < usbDataStatus.length; i++) {
+            modeString.append(usbDataStatusToString(usbDataStatus[i]));
+            if (i < usbDataStatus.length - 1) {
+                modeString.append(", ");
+            }
+        }
+        return modeString.toString();
+    }
+
+    /** @hide */
+    public static String powerBrickStatusToString(int powerBrickStatus) {
+        switch (powerBrickStatus) {
+            case POWER_BRICK_STATUS_UNKNOWN:
+                return "unknown";
+            case POWER_BRICK_STATUS_CONNECTED:
+                return "connected";
+            case POWER_BRICK_STATUS_DISCONNECTED:
+                return "disconnected";
+            default:
+                return Integer.toString(powerBrickStatus);
+        }
+    }
+
+    /** @hide */
     public static String roleCombinationsToString(int combo) {
         StringBuilder result = new StringBuilder();
         result.append("[");
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index bb7aff6..d1f4246 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -18,8 +18,8 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.hardware.usb.V1_0.Constants;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -36,27 +36,31 @@
 @Immutable
 @SystemApi
 public final class UsbPortStatus implements Parcelable {
+    private static final String TAG = "UsbPortStatus";
     private final int mCurrentMode;
     private final @UsbPowerRole int mCurrentPowerRole;
     private final @UsbDataRole int mCurrentDataRole;
     private final int mSupportedRoleCombinations;
     private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
     private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
+    private final boolean mPowerTransferLimited;
+    private final @UsbDataStatus int[] mUsbDataStatus;
+    private final @PowerBrickStatus int mPowerBrickStatus;
 
     /**
      * Power role: This USB port does not have a power role.
      */
-    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+    public static final int POWER_ROLE_NONE = 0;
 
     /**
      * Power role: This USB port can act as a source (provide power).
      */
-    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+    public static final int POWER_ROLE_SOURCE = 1;
 
     /**
      * Power role: This USB port can act as a sink (receive power).
      */
-    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+    public static final int POWER_ROLE_SINK = 2;
 
     @IntDef(prefix = { "POWER_ROLE_" }, value = {
             POWER_ROLE_NONE,
@@ -69,17 +73,17 @@
     /**
      * Power role: This USB port does not have a data role.
      */
-    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+    public static final int DATA_ROLE_NONE = 0;
 
     /**
      * Data role: This USB port can act as a host (access data services).
      */
-    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+    public static final int DATA_ROLE_HOST = 1;
 
     /**
      * Data role: This USB port can act as a device (offer data services).
      */
-    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+    public static final int DATA_ROLE_DEVICE = 2;
 
     @IntDef(prefix = { "DATA_ROLE_" }, value = {
             DATA_ROLE_NONE,
@@ -92,15 +96,7 @@
     /**
      * There is currently nothing connected to this USB port.
      */
-    public static final int MODE_NONE = Constants.PortMode.NONE;
-
-    /**
-     * This USB port can act as a downstream facing port (host).
-     *
-     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
-     * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
-     */
-    public static final int MODE_DFP = Constants.PortMode.DFP;
+    public static final int MODE_NONE = 0;
 
     /**
      * This USB port can act as an upstream facing port (device).
@@ -108,7 +104,15 @@
      * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
      * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
      */
-    public static final int MODE_UFP = Constants.PortMode.UFP;
+    public static final int MODE_UFP = 1 << 0;
+
+    /**
+     * This USB port can act as a downstream facing port (host).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_DFP = 1 << 1;
 
     /**
      * This USB port can act either as an downstream facing port (host) or as
@@ -120,87 +124,127 @@
      *
      * @hide
      */
-    public static final int MODE_DUAL = Constants.PortMode.DRP;
+    public static final int MODE_DUAL = MODE_UFP | MODE_DFP;
 
     /**
      * This USB port can support USB Type-C Audio accessory.
      */
-    public static final int MODE_AUDIO_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+    public static final int MODE_AUDIO_ACCESSORY = 1 << 2;
 
     /**
      * This USB port can support USB Type-C debug accessory.
      */
-    public static final int MODE_DEBUG_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+    public static final int MODE_DEBUG_ACCESSORY = 1 << 3;
 
    /**
      * Contaminant presence detection not supported by the device.
      * @hide
      */
-    public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED =
-            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_SUPPORTED;
+    public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED = 0;
 
     /**
      * Contaminant presence detection supported but disabled.
      * @hide
      */
-    public static final int CONTAMINANT_DETECTION_DISABLED =
-            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED;
+    public static final int CONTAMINANT_DETECTION_DISABLED = 1;
 
     /**
      * Contaminant presence enabled but not detected.
      * @hide
      */
-    public static final int CONTAMINANT_DETECTION_NOT_DETECTED =
-            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED;
+    public static final int CONTAMINANT_DETECTION_NOT_DETECTED = 2;
 
     /**
      * Contaminant presence enabled and detected.
      * @hide
      */
-    public static final int CONTAMINANT_DETECTION_DETECTED =
-            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED;
+    public static final int CONTAMINANT_DETECTION_DETECTED = 3;
 
     /**
      * Contaminant protection - No action performed upon detection of
      * contaminant presence.
      * @hide
      */
-    public static final int CONTAMINANT_PROTECTION_NONE =
-            android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.NONE;
+    public static final int CONTAMINANT_PROTECTION_NONE = 0;
 
     /**
      * Contaminant protection - Port is forced to sink upon detection of
      * contaminant presence.
      * @hide
      */
-    public static final int CONTAMINANT_PROTECTION_SINK =
-            android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SINK;
+    public static final int CONTAMINANT_PROTECTION_SINK = 1 << 0;
 
     /**
      * Contaminant protection - Port is forced to source upon detection of
      * contaminant presence.
      * @hide
      */
-    public static final int CONTAMINANT_PROTECTION_SOURCE =
-            android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SOURCE;
+    public static final int CONTAMINANT_PROTECTION_SOURCE = 1 << 1;
 
     /**
      * Contaminant protection - Port is disabled upon detection of
      * contaminant presence.
      * @hide
      */
-    public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE =
-            android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_DISABLE;
+    public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE = 1 << 2;
 
     /**
      * Contaminant protection - Port is disabled upon detection of
      * contaminant presence.
      * @hide
      */
-    public static final int CONTAMINANT_PROTECTION_DISABLED =
-            android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED;
+    public static final int CONTAMINANT_PROTECTION_DISABLED = 1 << 3;
+
+    /**
+     * USB data status is not known.
+     */
+    public static final int USB_DATA_STATUS_UNKNOWN = 0;
+
+    /**
+     * USB data is enabled.
+     */
+    public static final int USB_DATA_STATUS_ENABLED = 1;
+
+    /**
+     * USB data is disabled as the port is too hot.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
+
+    /**
+     * USB data is disabled due to contaminated port.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
+
+    /**
+     * USB data is disabled due to docking event.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_DOCK = 4;
+
+    /**
+     * USB data is disabled by
+     * {@link UsbPort#enableUsbData UsbPort.enableUsbData}.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_FORCE = 5;
+
+    /**
+     * USB data is disabled for debug.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6;
+
+    /**
+     * Unknown whether a power brick is connected.
+     */
+    public static final int POWER_BRICK_STATUS_UNKNOWN = 0;
+
+    /**
+     * The connected device is a power brick.
+     */
+    public static final int POWER_BRICK_STATUS_CONNECTED = 1;
+
+    /**
+     * The connected device is not power brick.
+     */
+    public static final int POWER_BRICK_STATUS_DISCONNECTED = 2;
 
     @IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = {
             CONTAMINANT_DETECTION_NOT_SUPPORTED,
@@ -232,6 +276,44 @@
     @interface UsbPortMode{}
 
     /** @hide */
+    @IntDef(prefix = { "USB_DATA_STATUS_" }, value = {
+            USB_DATA_STATUS_UNKNOWN,
+            USB_DATA_STATUS_ENABLED,
+            USB_DATA_STATUS_DISABLED_OVERHEAT,
+            USB_DATA_STATUS_DISABLED_CONTAMINANT,
+            USB_DATA_STATUS_DISABLED_DOCK,
+            USB_DATA_STATUS_DISABLED_FORCE,
+            USB_DATA_STATUS_DISABLED_DEBUG
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbDataStatus{}
+
+    /** @hide */
+    @IntDef(prefix = { "POWER_BRICK_STATUS_" }, value = {
+            POWER_BRICK_STATUS_UNKNOWN,
+            POWER_BRICK_STATUS_DISCONNECTED,
+            POWER_BRICK_STATUS_CONNECTED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface PowerBrickStatus{}
+
+    /** @hide */
+    public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+            int supportedRoleCombinations, int contaminantProtectionStatus,
+            int contaminantDetectionStatus, @UsbDataStatus int[] usbDataStatus,
+            boolean powerTransferLimited, @PowerBrickStatus int powerBrickStatus) {
+        mCurrentMode = currentMode;
+        mCurrentPowerRole = currentPowerRole;
+        mCurrentDataRole = currentDataRole;
+        mSupportedRoleCombinations = supportedRoleCombinations;
+        mContaminantProtectionStatus = contaminantProtectionStatus;
+        mContaminantDetectionStatus = contaminantDetectionStatus;
+        mUsbDataStatus = usbDataStatus;
+        mPowerTransferLimited = powerTransferLimited;
+        mPowerBrickStatus = powerBrickStatus;
+    }
+
+    /** @hide */
     public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
             int supportedRoleCombinations, int contaminantProtectionStatus,
             int contaminantDetectionStatus) {
@@ -241,6 +323,9 @@
         mSupportedRoleCombinations = supportedRoleCombinations;
         mContaminantProtectionStatus = contaminantProtectionStatus;
         mContaminantDetectionStatus = contaminantDetectionStatus;
+        mUsbDataStatus = new int[]{USB_DATA_STATUS_UNKNOWN};
+        mPowerBrickStatus = POWER_BRICK_STATUS_UNKNOWN;
+        mPowerTransferLimited = false;
     }
 
     /**
@@ -323,6 +408,40 @@
         return mContaminantProtectionStatus;
     }
 
+    /**
+     * Returns UsbData status.
+     *
+     * @return Current USB data status of the port: {@link #USB_DATA_STATUS_UNKNOWN}
+     *         or {@link #USB_DATA_STATUS_ENABLED} or {@link #USB_DATA_STATUS_DIASBLED_OVERHEAT}
+     *         or {@link #USB_DATA_STATUS_DISABLED_CONTAMINANT}
+     *         or {@link #USB_DATA_STATUS_DISABLED_DOCK} or {@link #USB_DATA_STATUS_DISABLED_FORCE}
+     *         or {@link #USB_DATA_STATUS_DISABLED_DEBUG}
+     */
+    public @UsbDataStatus @Nullable int[] getUsbDataStatus() {
+        return mUsbDataStatus;
+    }
+
+    /**
+     * Returns whether power transfer is limited.
+     *
+     * @return true when power transfer is limited.
+     *         false otherwise.
+     */
+    public boolean isPowerTransferLimited() {
+        return mPowerTransferLimited;
+    }
+
+    /**
+     * Let's the caller know if a power brick is connected to the USB port.
+     *
+     * @return {@link #POWER_BRICK_STATUS_UNKNOWN}
+     *         or {@link #POWER_BRICK_STATUS_CONNECTED}
+     *         or {@link #POWER_BRICK_STATUS_DISCONNECTED}
+     */
+    public @PowerBrickStatus int getPowerBrickStatus() {
+        return mPowerBrickStatus;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -336,6 +455,12 @@
                         + getContaminantDetectionStatus()
                 + ", contaminantProtectionStatus="
                         + getContaminantProtectionStatus()
+                + ", usbDataStatus="
+                        + UsbPort.usbDataStatusToString(getUsbDataStatus())
+                + ", isPowerTransferLimited="
+                        + isPowerTransferLimited()
+                +", powerBrickStatus="
+                        + UsbPort.powerBrickStatusToString(getPowerBrickStatus())
                 + "}";
     }
 
@@ -352,6 +477,10 @@
         dest.writeInt(mSupportedRoleCombinations);
         dest.writeInt(mContaminantProtectionStatus);
         dest.writeInt(mContaminantDetectionStatus);
+        dest.writeInt(mUsbDataStatus.length);
+        dest.writeIntArray(mUsbDataStatus);
+        dest.writeBoolean(mPowerTransferLimited);
+        dest.writeInt(mPowerBrickStatus);
     }
 
     public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -364,9 +493,14 @@
             int supportedRoleCombinations = in.readInt();
             int contaminantProtectionStatus = in.readInt();
             int contaminantDetectionStatus = in.readInt();
+            int[] usbDataStatus = new int[in.readInt()];
+            in.readIntArray(usbDataStatus);
+            boolean powerTransferLimited = in.readBoolean();
+            int powerBrickStatus = in.readInt();
             return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
-                    contaminantDetectionStatus);
+                    contaminantDetectionStatus, usbDataStatus, powerTransferLimited,
+                    powerBrickStatus);
         }
 
         @Override
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index e30594f..cc325cd 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -18,6 +18,7 @@
 
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -29,6 +30,7 @@
 import android.os.ResultReceiver;
 import android.util.Log;
 import android.view.InputChannel;
+import android.view.MotionEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
@@ -50,6 +52,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -69,11 +72,12 @@
     private static final int DO_START_INPUT = 32;
     private static final int DO_CREATE_SESSION = 40;
     private static final int DO_SET_SESSION_ENABLED = 45;
-    private static final int DO_REVOKE_SESSION = 50;
     private static final int DO_SHOW_SOFT_INPUT = 60;
     private static final int DO_HIDE_SOFT_INPUT = 70;
     private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
     private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
+    private static final int DO_CAN_START_STYLUS_HANDWRITING = 100;
+    private static final int DO_START_STYLUS_HANDWRITING = 110;
 
     final WeakReference<InputMethodServiceInternal> mTarget;
     final Context mContext;
@@ -169,7 +173,8 @@
                 SomeArgs args = (SomeArgs) msg.obj;
                 try {
                     inputMethod.initializeInternal((IBinder) args.arg1,
-                            (IInputMethodPrivilegedOperations) args.arg2, msg.arg1);
+                            (IInputMethodPrivilegedOperations) args.arg2, msg.arg1,
+                            (boolean) args.arg3);
                 } finally {
                     args.recycle();
                 }
@@ -209,9 +214,6 @@
                 inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
                         msg.arg1 != 0);
                 return;
-            case DO_REVOKE_SESSION:
-                inputMethod.revokeSession((InputMethodSession)msg.obj);
-                return;
             case DO_SHOW_SOFT_INPUT: {
                 final SomeArgs args = (SomeArgs)msg.obj;
                 inputMethod.showSoftInputWithToken(
@@ -229,13 +231,25 @@
             case DO_CHANGE_INPUTMETHOD_SUBTYPE:
                 inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
                 return;
-            case DO_CREATE_INLINE_SUGGESTIONS_REQUEST:
+            case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: {
                 final SomeArgs args = (SomeArgs) msg.obj;
                 inputMethod.onCreateInlineSuggestionsRequest(
                         (InlineSuggestionsRequestInfo) args.arg1,
                         (IInlineSuggestionsRequestCallback) args.arg2);
                 args.recycle();
                 return;
+            }
+            case DO_CAN_START_STYLUS_HANDWRITING: {
+                inputMethod.canStartStylusHandwriting(msg.arg1);
+                return;
+            }
+            case DO_START_STYLUS_HANDWRITING: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                inputMethod.startStylusHandwriting((InputChannel) args.arg1,
+                        (List<MotionEvent>) args.arg2);
+                args.recycle();
+                return;
+            }
 
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
@@ -272,9 +286,10 @@
     @BinderThread
     @Override
     public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
-            int configChanges) {
+            int configChanges, boolean stylusHwSupported) {
         mCaller.executeOrSendMessage(
-                mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, configChanges, token, privOps));
+                mCaller.obtainMessageIOOO(
+                        DO_INITIALIZE_INTERNAL, configChanges, token, privOps, stylusHwSupported));
     }
 
     @BinderThread
@@ -349,22 +364,6 @@
 
     @BinderThread
     @Override
-    public void revokeSession(IInputMethodSession session) {
-        try {
-            InputMethodSession ls = ((IInputMethodSessionWrapper)
-                    session).getInternalInputMethodSession();
-            if (ls == null) {
-                Log.w(TAG, "Session is already finished: " + session);
-                return;
-            }
-            mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
-        } catch (ClassCastException e) {
-            Log.w(TAG, "Incoming session not of correct type: " + session, e);
-        }
-    }
-
-    @BinderThread
-    @Override
     public void showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_SHOW_SOFT_INPUT,
                 flags, showInputToken, resultReceiver));
@@ -383,4 +382,21 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
                 subtype));
     }
+
+    @BinderThread
+    @Override
+    public void canStartStylusHandwriting(int requestId)
+            throws RemoteException {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageI(DO_CAN_START_STYLUS_HANDWRITING, requestId));
+    }
+
+    @BinderThread
+    @Override
+    public void startStylusHandwriting(@NonNull InputChannel channel,
+            @Nullable List<MotionEvent> stylusEvents)
+            throws RemoteException {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_START_STYLUS_HANDWRITING, channel, stylusEvents));
+    }
 }
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
new file mode 100644
index 0000000..e11d635
--- /dev/null
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import static android.view.WindowManager.LayoutParams;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.internal.policy.PhoneWindow;
+
+/**
+ * Window of type {@code LayoutParams.TYPE_INPUT_METHOD_DIALOG} for drawing
+ * Handwriting Ink on screen.
+ * @hide
+ */
+final class InkWindow extends PhoneWindow {
+
+    private final WindowManager mWindowManager;
+
+    public InkWindow(@NonNull Context context) {
+        super(context);
+
+        setType(LayoutParams.TYPE_INPUT_METHOD);
+        final LayoutParams attrs = getAttributes();
+        attrs.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        attrs.setFitInsetsTypes(0);
+        setAttributes(attrs);
+        // Ink window is not touchable with finger.
+        addFlags(FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_TOUCHABLE
+                | FLAG_NOT_FOCUSABLE);
+        setBackgroundDrawableResource(android.R.color.transparent);
+        setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        mWindowManager = context.getSystemService(WindowManager.class);
+    }
+
+    /**
+     * Method to show InkWindow on screen.
+     * Emulates internal behavior similar to Dialog.show().
+     */
+    void show() {
+        if (getDecorView() == null) {
+            Slog.i(InputMethodService.TAG, "DecorView is not set for InkWindow. show() failed.");
+            return;
+        }
+        getDecorView().setVisibility(View.VISIBLE);
+        mWindowManager.addView(getDecorView(), getAttributes());
+    }
+
+    /**
+     * Method to hide InkWindow from screen.
+     * Emulates internal behavior similar to Dialog.hide().
+     * @param remove set {@code true} to remove InkWindow surface completely.
+     */
+    void hide(boolean remove) {
+        if (getDecorView() != null) {
+            getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
+        }
+    }
+
+    void setToken(@NonNull IBinder token) {
+        WindowManager.LayoutParams lp = getAttributes();
+        lp.token = token;
+        setAttributes(lp);
+    }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index afaa085..5d2d8ea 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -54,6 +54,7 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.AnyThread;
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
@@ -83,6 +84,7 @@
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.provider.Settings;
 import android.text.InputType;
@@ -94,6 +96,7 @@
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
+import android.view.InputChannel;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -119,6 +122,7 @@
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceProto;
+import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 import android.widget.FrameLayout;
@@ -142,6 +146,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -302,6 +307,44 @@
     static final boolean DEBUG = false;
 
     /**
+     * Key for a boolean value that tells whether {@link InputMethodService} is responsible for
+     * rendering the back button and the IME switcher button or not when the gestural navigation is
+     * enabled.
+     *
+     * <p>This sysprop is just ignored when the gestural navigation mode is not enabled.</p>
+     *
+     * <p>
+     * To avoid complexity that is not necessary for production, you always need to reboot the
+     * device after modifying this flag as follows:
+     * <pre>
+     * $ adb root
+     * $ adb shell setprop persist.sys.ime.can_render_gestural_nav_buttons true
+     * $ adb reboot
+     * </pre>
+     * </p>
+     */
+    private static final String PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS =
+            "persist.sys.ime.can_render_gestural_nav_buttons";
+
+    /**
+     * Returns whether {@link InputMethodService} is responsible for rendering the back button and
+     * the IME switcher button or not when the gestural navigation is enabled.
+     *
+     * <p>This method is supposed to be used with an assumption that the same value is returned in
+     * other processes. It is developers' responsibility for rebooting the device when the sysprop
+     * is modified.</p>
+     *
+     * @return {@code true} if {@link InputMethodService} is responsible for rendering the back
+     * button and the IME switcher button when the gestural navigation is enabled.
+     *
+     * @hide
+     */
+    @AnyThread
+    public static boolean canImeRenderGesturalNavButtons() {
+        return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false);
+    }
+
+    /**
      * Allows the system to optimize the back button affordance based on the presence of software
      * keyboard.
      *
@@ -426,6 +469,10 @@
     InputMethodManager mImm;
     private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
 
+    @NonNull
+    private final NavigationBarController mNavigationBarController =
+            new NavigationBarController(this);
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mTheme = 0;
 
@@ -520,9 +567,14 @@
 
     private boolean mAutomotiveHideNavBarForKeyboard;
     private boolean mIsAutomotive;
+    private boolean mHandwritingStarted;
     private Handler mHandler;
     private boolean mImeSurfaceScheduledForRemoval;
     private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker();
+    private boolean mDestroyed;
+
+    /** Stylus handwriting Ink window.  */
+    private InkWindow mInkWindow;
 
     /**
      * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
@@ -563,6 +615,7 @@
             info.touchableRegion.set(mTmpInsets.touchableRegion);
             info.setTouchableInsets(mTmpInsets.touchableInsets);
         }
+        mNavigationBarController.updateTouchableInsets(mTmpInsets, info);
 
         if (mInputFrame != null) {
             setImeExclusionRect(mTmpInsets.visibleTopInsets);
@@ -598,11 +651,20 @@
         @MainThread
         @Override
         public final void initializeInternal(@NonNull IBinder token,
-                IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+                IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
+                boolean stylusHwSupported) {
+            if (mDestroyed) {
+                Log.i(TAG, "The InputMethodService has already onDestroyed()."
+                    + "Ignore the initialization.");
+                return;
+            }
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
             mConfigTracker.onInitialize(configChanges);
             mPrivOps.set(privilegedOperations);
             InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
+            if (stylusHwSupported) {
+                mInkWindow = new InkWindow(mWindow.getContext());
+            }
             attachToken(token);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
@@ -635,6 +697,9 @@
             attachToWindowToken(token);
             mToken = token;
             mWindow.setToken(token);
+            if (mInkWindow != null) {
+                mInkWindow.setToken(token);
+            }
         }
 
         /**
@@ -818,6 +883,49 @@
 
         /**
          * {@inheritDoc}
+         * @hide
+         */
+        @Override
+        public void canStartStylusHandwriting(int requestId) {
+            if (DEBUG) Log.v(TAG, "canStartStylusHandwriting()");
+            if (mHandwritingStarted) {
+                Log.d(TAG, "There is an ongoing Handwriting session. ignoring.");
+                return;
+            }
+            if (!mInputStarted) {
+                Log.d(TAG, "Input should have started before starting Stylus handwriting.");
+                return;
+            }
+            if (onStartStylusHandwriting()) {
+                mPrivOps.onStylusHandwritingReady(requestId);
+            } else {
+                Log.i(TAG, "IME is not ready. Can't start Stylus Handwriting");
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * @hide
+         */
+        @MainThread
+        @Override
+        public void startStylusHandwriting(
+                @NonNull InputChannel channel, @Nullable List<MotionEvent> stylusEvents) {
+            if (DEBUG) Log.v(TAG, "startStylusHandwriting()");
+            if (mHandwritingStarted) {
+                return;
+            }
+
+            mHandwritingStarted = true;
+            mShowInputRequested = false;
+
+            mInkWindow.show();
+            // TODO: deliver previous @param stylusEvents
+            // TODO: create spy receiver for @param channel
+        }
+
+        /**
+         * {@inheritDoc}
          */
         @MainThread
         @Override
@@ -1431,14 +1539,17 @@
         mCandidatesVisibility = getCandidatesHiddenVisibility();
         mCandidatesFrame.setVisibility(mCandidatesVisibility);
         mInputFrame.setVisibility(View.GONE);
+        mNavigationBarController.onViewInitialized();
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     @Override public void onDestroy() {
+        mDestroyed = true;
         super.onDestroy();
         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
                 mInsetsComputer);
         doFinishInput();
+        mNavigationBarController.onDestroy();
         mWindow.dismissForDestroyIfNecessary();
         if (mSettingsObserver != null) {
             mSettingsObserver.unregister();
@@ -1811,15 +1922,19 @@
     
     void updateExtractFrameVisibility() {
         final int vis;
+        updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
+
         if (isFullscreenMode()) {
             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
             // "vis" should be applied for the extract frame as well in the fullscreen mode.
             mExtractFrame.setVisibility(vis);
         } else {
-            vis = View.VISIBLE;
+            // mFullscreenArea visibility will according the candidate frame visibility once the
+            // extract frame is gone.
+            vis = mCandidatesVisibility;
             mExtractFrame.setVisibility(View.GONE);
         }
-        updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
+
         if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
             int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
                     ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
@@ -2182,6 +2297,77 @@
     }
     
     /**
+     * Called when an app requests stylus handwriting
+     * {@link InputMethodManager#startStylusHandwriting(View)}.
+     *
+     * This will always be preceded by {@link #onStartInput(EditorInfo, boolean)} for the
+     * {@link EditorInfo} and {@link InputConnection} for which stylus handwriting is being
+     * requested.
+     *
+     * If the IME supports handwriting for the current input, it should return {@code true},
+     * ensure its inking views are attached to the {@link #getStylusHandwritingWindow()}, and handle
+     * stylus input received on the ink window via {@link #getCurrentInputConnection()}.
+     * @return {@code true} if IME can honor the request, {@code false} if IME cannot at this time.
+     */
+    public boolean onStartStylusHandwriting() {
+        // Intentionally empty
+        return false;
+    }
+
+    /**
+     * Called when the current stylus handwriting session was finished (either by the system or
+     * via {@link #finishStylusHandwriting()}.
+     *
+     * When this is called, the ink window has been made invisible, and the IME no longer
+     * intercepts handwriting-related {@code MotionEvent}s.
+     */
+    public void onFinishStylusHandwriting() {
+        // Intentionally empty
+    }
+
+    /**
+     * Returns the stylus handwriting inking window.
+     * IMEs supporting stylus input are expected to attach their inking views to this
+     * window (e.g. with {@link Window#setContentView(View)} )). Handwriting-related
+     * {@link MotionEvent}s are dispatched to the attached view hierarchy.
+     *
+     * Note: This returns {@code null} if IME doesn't support stylus handwriting
+     *   i.e. if {@link InputMethodInfo#supportsStylusHandwriting()} is false.
+     *   This method should be called after {@link #onStartStylusHandwriting()}.
+     * @see #onStartStylusHandwriting()
+     */
+    @Nullable
+    public final Window getStylusHandwritingWindow() {
+        return mInkWindow;
+    }
+
+    /**
+     * Finish the current stylus handwriting session.
+     *
+     * This dismisses the {@link #getStylusHandwritingWindow ink window} and stops intercepting
+     * stylus {@code MotionEvent}s.
+     *
+     * Note for IME developers: Call this method at any time to finish current handwriting session.
+     * Generally, this should be invoked after a short timeout, giving the user enough time
+     * to start the next stylus stroke, if any.
+     *
+     * Handwriting session will be finished by framework on next {@link #onFinishInput()}.
+     */
+    public final void finishStylusHandwriting() {
+        if (DEBUG) Log.v(TAG, "finishStylusHandwriting()");
+        if (mInkWindow == null) {
+            return;
+        }
+        if (!mHandwritingStarted) {
+            return;
+        }
+
+        mHandwritingStarted = false;
+        mInkWindow.hide(false /* remove */);
+        onFinishStylusHandwriting();
+    }
+
+    /**
      * The system has decided that it may be time to show your input method.
      * This is called due to a corresponding call to your
      * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
@@ -2272,6 +2458,7 @@
             setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
         }
 
+        mNavigationBarController.onWindowShown();
         // compute visibility
         onWindowShown();
         mWindowVisible = true;
@@ -2450,6 +2637,9 @@
         mInputStarted = false;
         mStartedInputConnection = null;
         mCurCompletions = null;
+        if (mInkWindow != null) {
+            finishStylusHandwriting();
+        }
     }
 
     void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
@@ -3474,6 +3664,7 @@
                 + " touchableInsets=" + mTmpInsets.touchableInsets
                 + " touchableRegion=" + mTmpInsets.touchableRegion);
         p.println(" mSettingsObserver=" + mSettingsObserver);
+        p.println(" mNavigationBarController=" + mNavigationBarController.toDebugString());
     }
 
     private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
new file mode 100644
index 0000000..7295b72
--- /dev/null
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.inputmethodservice.navigationbar.NavigationBarFrame;
+import android.inputmethodservice.navigationbar.NavigationBarView;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManagerPolicyConstants;
+import android.widget.FrameLayout;
+
+import java.util.Objects;
+
+/**
+ * This class hides details behind {@link InputMethodService#canImeRenderGesturalNavButtons()} from
+ * {@link InputMethodService}.
+ *
+ * <p>All the package-private methods are no-op when
+ * {@link InputMethodService#canImeRenderGesturalNavButtons()} returns {@code false}.</p>
+ */
+final class NavigationBarController {
+
+    private interface Callback {
+        default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
+                @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
+        }
+
+        default void onViewInitialized() {
+        }
+
+        default void onWindowShown() {
+        }
+
+        default void onDestroy() {
+        }
+
+        default String toDebugString() {
+            return "No-op implementation";
+        }
+
+        Callback NOOP = new Callback() {
+        };
+    }
+
+    private final Callback mImpl;
+
+    NavigationBarController(@NonNull InputMethodService inputMethodService) {
+        mImpl = InputMethodService.canImeRenderGesturalNavButtons()
+                ? new Impl(inputMethodService) : Callback.NOOP;
+    }
+
+    void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
+            @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
+        mImpl.updateTouchableInsets(originalInsets, dest);
+    }
+
+    void onViewInitialized() {
+        mImpl.onViewInitialized();
+    }
+
+    void onWindowShown() {
+        mImpl.onWindowShown();
+    }
+
+    void onDestroy() {
+        mImpl.onDestroy();
+    }
+
+    String toDebugString() {
+        return mImpl.toDebugString();
+    }
+
+    private static final class Impl implements Callback {
+        @NonNull
+        private final InputMethodService mService;
+
+        private boolean mDestroyed = false;
+
+        private boolean mRenderGesturalNavButtons;
+
+        @Nullable
+        private NavigationBarFrame mNavigationBarFrame;
+        @Nullable
+        Insets mLastInsets;
+
+        Impl(@NonNull InputMethodService inputMethodService) {
+            mService = inputMethodService;
+        }
+
+        @Nullable
+        private Insets getSystemInsets() {
+            if (mService.mWindow == null) {
+                return null;
+            }
+            final View decorView = mService.mWindow.getWindow().getDecorView();
+            if (decorView == null) {
+                return null;
+            }
+            final WindowInsets windowInsets = decorView.getRootWindowInsets();
+            if (windowInsets == null) {
+                return null;
+            }
+            final Insets stableBarInsets =
+                    windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
+            return Insets.min(windowInsets.getInsets(WindowInsets.Type.systemBars()
+                    | WindowInsets.Type.displayCutout()), stableBarInsets);
+        }
+
+        private void installNavigationBarFrameIfNecessary() {
+            if (!mRenderGesturalNavButtons) {
+                return;
+            }
+            final View rawDecorView = mService.mWindow.getWindow().getDecorView();
+            if (!(rawDecorView instanceof ViewGroup)) {
+                return;
+            }
+            final ViewGroup decorView = (ViewGroup) rawDecorView;
+            mNavigationBarFrame = decorView.findViewByPredicate(
+                    NavigationBarFrame.class::isInstance);
+            final Insets systemInsets = getSystemInsets();
+            if (mNavigationBarFrame == null) {
+                mNavigationBarFrame = new NavigationBarFrame(mService);
+                LayoutInflater.from(mService).inflate(
+                        com.android.internal.R.layout.input_method_navigation_bar,
+                        mNavigationBarFrame);
+                if (systemInsets != null) {
+                    decorView.addView(mNavigationBarFrame, new FrameLayout.LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            systemInsets.bottom, Gravity.BOTTOM));
+                    mLastInsets = systemInsets;
+                } else {
+                    decorView.addView(mNavigationBarFrame);
+                }
+                final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
+                        NavigationBarView.class::isInstance);
+                if (navigationBarView != null) {
+                    // TODO(b/213337792): Support InputMethodService#setBackDisposition().
+                    // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
+                    final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+                            | StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+                    navigationBarView.setNavigationIconHints(hints);
+                }
+            } else {
+                mNavigationBarFrame.setLayoutParams(new FrameLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, systemInsets.bottom, Gravity.BOTTOM));
+                mLastInsets = systemInsets;
+            }
+
+            mNavigationBarFrame.setBackground(null);
+        }
+
+        @Override
+        public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
+                @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
+            if (!mRenderGesturalNavButtons || mNavigationBarFrame == null
+                    || mService.isExtractViewShown()) {
+                return;
+            }
+
+            final Insets systemInsets = getSystemInsets();
+            if (systemInsets != null) {
+                final Window window = mService.mWindow.getWindow();
+                final View decor = window.getDecorView();
+                Region touchableRegion = null;
+                final View inputFrame = mService.mInputFrame;
+                switch (originalInsets.touchableInsets) {
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+                        if (inputFrame.getVisibility() == View.VISIBLE) {
+                            touchableRegion = new Region(inputFrame.getLeft(),
+                                    inputFrame.getTop(), inputFrame.getRight(),
+                                    inputFrame.getBottom());
+                        }
+                        break;
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+                        if (inputFrame.getVisibility() == View.VISIBLE) {
+                            touchableRegion = new Region(inputFrame.getLeft(),
+                                    originalInsets.contentTopInsets, inputFrame.getRight(),
+                                    inputFrame.getBottom());
+                        }
+                        break;
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+                        if (inputFrame.getVisibility() == View.VISIBLE) {
+                            touchableRegion = new Region(inputFrame.getLeft(),
+                                    originalInsets.visibleTopInsets, inputFrame.getRight(),
+                                    inputFrame.getBottom());
+                        }
+                        break;
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION:
+                        touchableRegion = new Region();
+                        touchableRegion.set(originalInsets.touchableRegion);
+                        break;
+                }
+                final Rect navBarRect = new Rect(decor.getLeft(),
+                        decor.getBottom() - systemInsets.bottom,
+                        decor.getRight(), decor.getBottom());
+                if (touchableRegion == null) {
+                    touchableRegion = new Region(navBarRect);
+                } else {
+                    touchableRegion.union(navBarRect);
+                }
+
+                dest.touchableRegion.set(touchableRegion);
+                dest.setTouchableInsets(
+                        ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+
+                // TODO(b/205803355): See if we can use View#OnLayoutChangeListener().
+                // TODO(b/205803355): See if we can replace DecorView#mNavigationColorViewState.view
+                boolean zOrderChanged = false;
+                if (decor instanceof ViewGroup) {
+                    ViewGroup decorGroup = (ViewGroup) decor;
+                    final View navbarBackgroundView = window.getNavigationBarBackgroundView();
+                    zOrderChanged = navbarBackgroundView != null
+                            && decorGroup.indexOfChild(navbarBackgroundView)
+                            > decorGroup.indexOfChild(mNavigationBarFrame);
+                }
+                final boolean insetChanged = !Objects.equals(systemInsets, mLastInsets);
+                if (zOrderChanged || insetChanged) {
+                    final NavigationBarFrame that = mNavigationBarFrame;
+                    that.post(() -> {
+                        if (!that.isAttachedToWindow()) {
+                            return;
+                        }
+                        final Insets currentSystemInsets = getSystemInsets();
+                        if (!Objects.equals(currentSystemInsets, mLastInsets)) {
+                            that.setLayoutParams(
+                                    new FrameLayout.LayoutParams(
+                                            ViewGroup.LayoutParams.MATCH_PARENT,
+                                            currentSystemInsets.bottom, Gravity.BOTTOM));
+                            mLastInsets = currentSystemInsets;
+                        }
+                        if (decor instanceof ViewGroup) {
+                            ViewGroup decorGroup = (ViewGroup) decor;
+                            final View navbarBackgroundView =
+                                    window.getNavigationBarBackgroundView();
+                            if (navbarBackgroundView != null
+                                    && decorGroup.indexOfChild(navbarBackgroundView)
+                                    > decorGroup.indexOfChild(that)) {
+                                decorGroup.bringChildToFront(that);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        private boolean isGesturalNavigationEnabled() {
+            final Resources resources = mService.getResources();
+            if (resources == null) {
+                return false;
+            }
+            return resources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)
+                    == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+        }
+
+        @Override
+        public void onViewInitialized() {
+            if (mDestroyed) {
+                return;
+            }
+            mRenderGesturalNavButtons = isGesturalNavigationEnabled();
+            installNavigationBarFrameIfNecessary();
+        }
+
+        @Override
+        public void onDestroy() {
+            mDestroyed = true;
+        }
+
+        @Override
+        public void onWindowShown() {
+            if (mDestroyed || !mRenderGesturalNavButtons || mNavigationBarFrame == null) {
+                return;
+            }
+            final Insets systemInsets = getSystemInsets();
+            if (systemInsets != null) {
+                if (!Objects.equals(systemInsets, mLastInsets)) {
+                    mNavigationBarFrame.setLayoutParams(new NavigationBarFrame.LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            systemInsets.bottom, Gravity.BOTTOM));
+                    mLastInsets = systemInsets;
+                }
+                final Window window = mService.mWindow.getWindow();
+                View rawDecorView = window.getDecorView();
+                if (rawDecorView instanceof ViewGroup) {
+                    final ViewGroup decor = (ViewGroup) rawDecorView;
+                    final View navbarBackgroundView = window.getNavigationBarBackgroundView();
+                    if (navbarBackgroundView != null
+                            && decor.indexOfChild(navbarBackgroundView)
+                            > decor.indexOfChild(mNavigationBarFrame)) {
+                        decor.bringChildToFront(mNavigationBarFrame);
+                    }
+                }
+                mNavigationBarFrame.setVisibility(View.VISIBLE);
+            }
+        }
+
+        @Override
+        public String toDebugString() {
+            return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons + "}";
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java b/core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java
new file mode 100644
index 0000000..3f26fa4
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * Dispatches common view calls to multiple views.  This is used to handle
+ * multiples of the same nav bar icon appearing.
+ */
+final class ButtonDispatcher {
+    private static final int FADE_DURATION_IN = 150;
+    private static final int FADE_DURATION_OUT = 250;
+    public static final Interpolator LINEAR = new LinearInterpolator();
+
+    private final ArrayList<View> mViews = new ArrayList<>();
+
+    private final int mId;
+
+    private View.OnClickListener mClickListener;
+    private View.OnTouchListener mTouchListener;
+    private View.OnLongClickListener mLongClickListener;
+    private View.OnHoverListener mOnHoverListener;
+    private Boolean mLongClickable;
+    private float mAlpha = 1.0f;
+    private Float mDarkIntensity;
+    private int mVisibility = View.VISIBLE;
+    private Boolean mDelayTouchFeedback;
+    private KeyButtonDrawable mImageDrawable;
+    private View mCurrentView;
+    private ValueAnimator mFadeAnimator;
+    private AccessibilityDelegate mAccessibilityDelegate;
+
+    private final ValueAnimator.AnimatorUpdateListener mAlphaListener = animation ->
+            setAlpha(
+                    (float) animation.getAnimatedValue(),
+                    false /* animate */,
+                    false /* cancelAnimator */);
+
+    private final AnimatorListenerAdapter mFadeListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mFadeAnimator = null;
+            setVisibility(getAlpha() == 1 ? View.VISIBLE : View.INVISIBLE);
+        }
+    };
+
+    public ButtonDispatcher(int id) {
+        mId = id;
+    }
+
+    public void clear() {
+        mViews.clear();
+    }
+
+    public void addView(View view) {
+        mViews.add(view);
+        view.setOnClickListener(mClickListener);
+        view.setOnTouchListener(mTouchListener);
+        view.setOnLongClickListener(mLongClickListener);
+        view.setOnHoverListener(mOnHoverListener);
+        if (mLongClickable != null) {
+            view.setLongClickable(mLongClickable);
+        }
+        view.setAlpha(mAlpha);
+        view.setVisibility(mVisibility);
+        if (mAccessibilityDelegate != null) {
+            view.setAccessibilityDelegate(mAccessibilityDelegate);
+        }
+        if (view instanceof ButtonInterface) {
+            final ButtonInterface button = (ButtonInterface) view;
+            if (mDarkIntensity != null) {
+                button.setDarkIntensity(mDarkIntensity);
+            }
+            if (mImageDrawable != null) {
+                button.setImageDrawable(mImageDrawable);
+            }
+            if (mDelayTouchFeedback != null) {
+                button.setDelayTouchFeedback(mDelayTouchFeedback);
+            }
+        }
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public int getVisibility() {
+        return mVisibility;
+    }
+
+    public boolean isVisible() {
+        return getVisibility() == View.VISIBLE;
+    }
+
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    public KeyButtonDrawable getImageDrawable() {
+        return mImageDrawable;
+    }
+
+    public void setImageDrawable(KeyButtonDrawable drawable) {
+        mImageDrawable = drawable;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            if (mViews.get(i) instanceof ButtonInterface) {
+                ((ButtonInterface) mViews.get(i)).setImageDrawable(mImageDrawable);
+            }
+        }
+        if (mImageDrawable != null) {
+            mImageDrawable.setCallback(mCurrentView);
+        }
+    }
+
+    public void setVisibility(int visibility) {
+        if (mVisibility == visibility) return;
+        if (mFadeAnimator != null) {
+            mFadeAnimator.cancel();
+        }
+
+        mVisibility = visibility;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setVisibility(mVisibility);
+        }
+    }
+
+    public void setAlpha(float alpha) {
+        setAlpha(alpha, false /* animate */);
+    }
+
+    public void setAlpha(float alpha, boolean animate) {
+        setAlpha(alpha, animate, true /* cancelAnimator */);
+    }
+
+    public void setAlpha(float alpha, boolean animate, long duration) {
+        setAlpha(alpha, animate, duration, true /* cancelAnimator */);
+    }
+
+    public void setAlpha(float alpha, boolean animate, boolean cancelAnimator) {
+        setAlpha(
+                alpha,
+                animate,
+                (getAlpha() < alpha) ? FADE_DURATION_IN : FADE_DURATION_OUT,
+                cancelAnimator);
+    }
+
+    public void setAlpha(float alpha, boolean animate, long duration, boolean cancelAnimator) {
+        if (mFadeAnimator != null && (cancelAnimator || animate)) {
+            mFadeAnimator.cancel();
+        }
+        if (animate) {
+            setVisibility(View.VISIBLE);
+            mFadeAnimator = ValueAnimator.ofFloat(getAlpha(), alpha);
+            mFadeAnimator.setDuration(duration);
+            mFadeAnimator.setInterpolator(LINEAR);
+            mFadeAnimator.addListener(mFadeListener);
+            mFadeAnimator.addUpdateListener(mAlphaListener);
+            mFadeAnimator.start();
+        } else {
+            // Discretize the alpha updates to prevent too frequent updates when there is a long
+            // alpha animation
+            int prevAlpha = (int) (getAlpha() * 255);
+            int nextAlpha = (int) (alpha * 255);
+            if (prevAlpha != nextAlpha) {
+                mAlpha = nextAlpha / 255f;
+                final int N = mViews.size();
+                for (int i = 0; i < N; i++) {
+                    mViews.get(i).setAlpha(mAlpha);
+                }
+            }
+        }
+    }
+
+    public void setDarkIntensity(float darkIntensity) {
+        mDarkIntensity = darkIntensity;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            if (mViews.get(i) instanceof ButtonInterface) {
+                ((ButtonInterface) mViews.get(i)).setDarkIntensity(darkIntensity);
+            }
+        }
+    }
+
+    public void setDelayTouchFeedback(boolean delay) {
+        mDelayTouchFeedback = delay;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            if (mViews.get(i) instanceof ButtonInterface) {
+                ((ButtonInterface) mViews.get(i)).setDelayTouchFeedback(delay);
+            }
+        }
+    }
+
+    public void setOnClickListener(View.OnClickListener clickListener) {
+        mClickListener = clickListener;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setOnClickListener(mClickListener);
+        }
+    }
+
+    public void setOnTouchListener(View.OnTouchListener touchListener) {
+        mTouchListener = touchListener;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setOnTouchListener(mTouchListener);
+        }
+    }
+
+    public void setLongClickable(boolean isLongClickable) {
+        mLongClickable = isLongClickable;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setLongClickable(mLongClickable);
+        }
+    }
+
+    public void setOnLongClickListener(View.OnLongClickListener longClickListener) {
+        mLongClickListener = longClickListener;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setOnLongClickListener(mLongClickListener);
+        }
+    }
+
+    public void setOnHoverListener(View.OnHoverListener hoverListener) {
+        mOnHoverListener = hoverListener;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setOnHoverListener(mOnHoverListener);
+        }
+    }
+
+    public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
+        mAccessibilityDelegate = delegate;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setAccessibilityDelegate(delegate);
+        }
+    }
+
+    public void setTranslation(int x, int y, int z) {
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            final View view = mViews.get(i);
+            view.setTranslationX(x);
+            view.setTranslationY(y);
+            view.setTranslationZ(z);
+        }
+    }
+
+    public ArrayList<View> getViews() {
+        return mViews;
+    }
+
+    public View getCurrentView() {
+        return mCurrentView;
+    }
+
+    public void setCurrentView(View currentView) {
+        mCurrentView = currentView.findViewById(mId);
+        if (mImageDrawable != null) {
+            mImageDrawable.setCallback(mCurrentView);
+        }
+        if (mCurrentView != null) {
+            mCurrentView.setTranslationX(0);
+            mCurrentView.setTranslationY(0);
+            mCurrentView.setTranslationZ(0);
+        }
+    }
+
+    /**
+     * Executes when button is detached from window.
+     */
+    public void onDestroy() {
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/ButtonInterface.java b/core/java/android/inputmethodservice/navigationbar/ButtonInterface.java
new file mode 100644
index 0000000..1c9c86d
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/ButtonInterface.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+
+interface ButtonInterface {
+
+    void setImageDrawable(@Nullable Drawable drawable);
+
+    void setDarkIntensity(float intensity);
+
+    void setDelayTouchFeedback(boolean shouldDelay);
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/DeadZone.java b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
new file mode 100644
index 0000000..cd85736
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_DECAY;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_HOLD;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_SIZE;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_SIZE_MAX;
+import static android.inputmethodservice.navigationbar.NavigationBarUtils.dpToPx;
+
+import android.animation.ObjectAnimator;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+/**
+ * The "dead zone" consumes unintentional taps along the top edge of the navigation bar.
+ * When users are typing quickly on an IME they may attempt to hit the space bar, overshoot, and
+ * accidentally hit the home button. The DeadZone expands temporarily after each tap in the UI
+ * outside the navigation bar (since this is when accidental taps are more likely), then contracts
+ * back over time (since a later tap might be intended for the top of the bar).
+ */
+final class DeadZone {
+    public static final String TAG = "DeadZone";
+
+    public static final boolean DEBUG = false;
+    public static final int HORIZONTAL = 0;  // Consume taps along the top edge.
+    public static final int VERTICAL = 1;  // Consume taps along the left edge.
+
+    private static final boolean CHATTY = true; // print to logcat when we eat a click
+    private final NavigationBarView mNavigationBarView;
+
+    private boolean mShouldFlash;
+    private float mFlashFrac = 0f;
+
+    private int mSizeMax;
+    private int mSizeMin;
+    // Upon activity elsewhere in the UI, the dead zone will hold steady for
+    // mHold ms, then move back over the course of mDecay ms
+    private int mHold, mDecay;
+    private boolean mVertical;
+    private long mLastPokeTime;
+    private int mDisplayRotation;
+
+    private final Runnable mDebugFlash = new Runnable() {
+        @Override
+        public void run() {
+            ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+        }
+    };
+
+    public DeadZone(NavigationBarView view) {
+        mNavigationBarView = view;
+        onConfigurationChanged(Surface.ROTATION_0);
+    }
+
+    static float lerp(float a, float b, float f) {
+        return (b - a) * f + a;
+    }
+
+    private float getSize(long now) {
+        if (mSizeMax == 0)
+            return 0;
+        long dt = (now - mLastPokeTime);
+        if (dt > mHold + mDecay)
+            return mSizeMin;
+        if (dt < mHold)
+            return mSizeMax;
+        return (int) lerp(mSizeMax, mSizeMin, (float) (dt - mHold) / mDecay);
+    }
+
+    public void setFlashOnTouchCapture(boolean dbg) {
+        mShouldFlash = dbg;
+        mFlashFrac = 0f;
+        mNavigationBarView.postInvalidate();
+    }
+
+    public void onConfigurationChanged(int rotation) {
+        mDisplayRotation = rotation;
+
+        final Resources res = mNavigationBarView.getResources();
+        mHold = NAVIGATION_BAR_DEADZONE_HOLD;
+        mDecay = NAVIGATION_BAR_DEADZONE_DECAY;
+
+        mSizeMin = dpToPx(NAVIGATION_BAR_DEADZONE_SIZE, res);
+        mSizeMax = dpToPx(NAVIGATION_BAR_DEADZONE_SIZE_MAX, res);
+        mVertical = (res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
+
+        if (DEBUG) {
+            Log.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+                    + (mVertical ? " vertical" : " horizontal"));
+        }
+        setFlashOnTouchCapture(false);   // hard-coded from "bool/config_dead_zone_flash"
+    }
+
+    // I made you a touch event...
+    public boolean onTouchEvent(MotionEvent event) {
+        if (DEBUG) {
+            Log.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
+        }
+
+        // Don't consume events for high precision pointing devices. For this purpose a stylus is
+        // considered low precision (like a finger), so its events may be consumed.
+        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+            return false;
+        }
+
+        final int action = event.getAction();
+        if (action == MotionEvent.ACTION_OUTSIDE) {
+            poke(event);
+            return true;
+        } else if (action == MotionEvent.ACTION_DOWN) {
+            if (DEBUG) {
+                Log.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
+            }
+            //TODO(b/205803355): call mNavBarController.touchAutoDim(mDisplayId); here
+            int size = (int) getSize(event.getEventTime());
+            // In the vertical orientation consume taps along the left edge.
+            // In horizontal orientation consume taps along the top edge.
+            final boolean consumeEvent;
+            if (mVertical) {
+                if (mDisplayRotation == Surface.ROTATION_270) {
+                    consumeEvent = event.getX() > mNavigationBarView.getWidth() - size;
+                } else {
+                    consumeEvent = event.getX() < size;
+                }
+            } else {
+                consumeEvent = event.getY() < size;
+            }
+            if (consumeEvent) {
+                if (CHATTY) {
+                    Log.v(TAG, "consuming errant click: (" + event.getX() + ","
+                            + event.getY() + ")");
+                }
+                if (mShouldFlash) {
+                    mNavigationBarView.post(mDebugFlash);
+                    mNavigationBarView.postInvalidate();
+                }
+                return true; // ...but I eated it
+            }
+        }
+        return false;
+    }
+
+    private void poke(MotionEvent event) {
+        mLastPokeTime = event.getEventTime();
+        if (DEBUG)
+            Log.v(TAG, "poked! size=" + getSize(mLastPokeTime));
+        if (mShouldFlash) mNavigationBarView.postInvalidate();
+    }
+
+    public void setFlash(float f) {
+        mFlashFrac = f;
+        mNavigationBarView.postInvalidate();
+    }
+
+    public float getFlash() {
+        return mFlashFrac;
+    }
+
+    public void onDraw(Canvas can) {
+        if (!mShouldFlash || mFlashFrac <= 0f) {
+            return;
+        }
+
+        final int size = (int) getSize(SystemClock.uptimeMillis());
+        if (mVertical) {
+            if (mDisplayRotation == Surface.ROTATION_270) {
+                can.clipRect(can.getWidth() - size, 0, can.getWidth(), can.getHeight());
+            } else {
+                can.clipRect(0, 0, size, can.getHeight());
+            }
+        } else {
+            can.clipRect(0, 0, can.getWidth(), size);
+        }
+
+        final float frac = DEBUG ? (mFlashFrac - 0.5f) + 0.5f : mFlashFrac;
+        can.drawARGB((int) (frac * 0xFF), 0xDD, 0xEE, 0xAA);
+
+        if (DEBUG && size > mSizeMin) {
+            // Very aggressive redrawing here, for debugging only
+            mNavigationBarView.postInvalidateDelayed(100);
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java
new file mode 100644
index 0000000..25a443d
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_COLOR;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_OFFSET_X;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_OFFSET_Y;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_RADIUS;
+import static android.inputmethodservice.navigationbar.NavigationBarUtils.dpToPx;
+
+import android.animation.ArgbEvaluator;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.BlurMaskFilter.Blur;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.view.View;
+
+
+/**
+ * Drawable for {@link KeyButtonView}s that supports tinting between two colors, rotation and shows
+ * a shadow. AnimatedVectorDrawable will only support tinting from intensities but has no support
+ * for shadows nor rotations.
+ */
+final class KeyButtonDrawable extends Drawable {
+
+    public static final FloatProperty<KeyButtonDrawable> KEY_DRAWABLE_ROTATE =
+        new FloatProperty<KeyButtonDrawable>("KeyButtonRotation") {
+            @Override
+            public void setValue(KeyButtonDrawable drawable, float degree) {
+                drawable.setRotation(degree);
+            }
+
+            @Override
+            public Float get(KeyButtonDrawable drawable) {
+                return drawable.getRotation();
+            }
+        };
+
+    public static final FloatProperty<KeyButtonDrawable> KEY_DRAWABLE_TRANSLATE_Y =
+        new FloatProperty<KeyButtonDrawable>("KeyButtonTranslateY") {
+            @Override
+            public void setValue(KeyButtonDrawable drawable, float y) {
+                drawable.setTranslationY(y);
+            }
+
+            @Override
+            public Float get(KeyButtonDrawable drawable) {
+                return drawable.getTranslationY();
+            }
+        };
+
+    private final Paint mIconPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    private final Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    private final ShadowDrawableState mState;
+    private AnimatedVectorDrawable mAnimatedDrawable;
+    private final Callback mAnimatedDrawableCallback = new Callback() {
+        @Override
+        public void invalidateDrawable(@NonNull Drawable who) {
+            invalidateSelf();
+        }
+
+        @Override
+        public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+            scheduleSelf(what, when);
+        }
+
+        @Override
+        public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+            unscheduleSelf(what);
+        }
+    };
+
+    public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor,
+            boolean horizontalFlip, Color ovalBackgroundColor) {
+        this(d, new ShadowDrawableState(lightColor, darkColor,
+                d instanceof AnimatedVectorDrawable, horizontalFlip, ovalBackgroundColor));
+    }
+
+    private KeyButtonDrawable(Drawable d, ShadowDrawableState state) {
+        mState = state;
+        if (d != null) {
+            mState.mBaseHeight = d.getIntrinsicHeight();
+            mState.mBaseWidth = d.getIntrinsicWidth();
+            mState.mChangingConfigurations = d.getChangingConfigurations();
+            mState.mChildState = d.getConstantState();
+        }
+        if (canAnimate()) {
+            mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
+            mAnimatedDrawable.setCallback(mAnimatedDrawableCallback);
+            setDrawableBounds(mAnimatedDrawable);
+        }
+    }
+
+    public void setDarkIntensity(float intensity) {
+        mState.mDarkIntensity = intensity;
+        final int color = (int) ArgbEvaluator.getInstance()
+                .evaluate(intensity, mState.mLightColor, mState.mDarkColor);
+        updateShadowAlpha();
+        setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_ATOP));
+    }
+
+    public void setRotation(float degrees) {
+        if (canAnimate()) {
+            // AnimatedVectorDrawables will not support rotation
+            return;
+        }
+        if (mState.mRotateDegrees != degrees) {
+            mState.mRotateDegrees = degrees;
+            invalidateSelf();
+        }
+    }
+
+    public void setTranslationX(float x) {
+        setTranslation(x, mState.mTranslationY);
+    }
+
+    public void setTranslationY(float y) {
+        setTranslation(mState.mTranslationX, y);
+    }
+
+    public void setTranslation(float x, float y) {
+        if (mState.mTranslationX != x || mState.mTranslationY != y) {
+            mState.mTranslationX = x;
+            mState.mTranslationY = y;
+            invalidateSelf();
+        }
+    }
+
+    public void setShadowProperties(int x, int y, int size, int color) {
+        if (canAnimate()) {
+            // AnimatedVectorDrawables will not support shadows
+            return;
+        }
+        if (mState.mShadowOffsetX != x || mState.mShadowOffsetY != y
+                || mState.mShadowSize != size || mState.mShadowColor != color) {
+            mState.mShadowOffsetX = x;
+            mState.mShadowOffsetY = y;
+            mState.mShadowSize = size;
+            mState.mShadowColor = color;
+            mShadowPaint.setColorFilter(
+                    new PorterDuffColorFilter(mState.mShadowColor, Mode.SRC_ATOP));
+            updateShadowAlpha();
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        boolean changed = super.setVisible(visible, restart);
+        if (changed) {
+            // End any existing animations when the visibility changes
+            jumpToCurrentState();
+        }
+        return changed;
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        super.jumpToCurrentState();
+        if (mAnimatedDrawable != null) {
+            mAnimatedDrawable.jumpToCurrentState();
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mState.mAlpha = alpha;
+        mIconPaint.setAlpha(alpha);
+        updateShadowAlpha();
+        invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mIconPaint.setColorFilter(colorFilter);
+        if (mAnimatedDrawable != null) {
+            if (hasOvalBg()) {
+                mAnimatedDrawable.setColorFilter(
+                        new PorterDuffColorFilter(mState.mLightColor, PorterDuff.Mode.SRC_IN));
+            } else {
+                mAnimatedDrawable.setColorFilter(colorFilter);
+            }
+        }
+        invalidateSelf();
+    }
+
+    public float getDarkIntensity() {
+        return mState.mDarkIntensity;
+    }
+
+    public float getRotation() {
+        return mState.mRotateDegrees;
+    }
+
+    public float getTranslationX() {
+        return mState.mTranslationX;
+    }
+
+    public float getTranslationY() {
+        return mState.mTranslationY;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mState;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mState.mBaseHeight + (mState.mShadowSize + Math.abs(mState.mShadowOffsetY)) * 2;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mState.mBaseWidth + (mState.mShadowSize + Math.abs(mState.mShadowOffsetX)) * 2;
+    }
+
+    public boolean canAnimate() {
+        return mState.mSupportsAnimation;
+    }
+
+    public void startAnimation() {
+        if (mAnimatedDrawable != null) {
+            mAnimatedDrawable.start();
+        }
+    }
+
+    public void resetAnimation() {
+        if (mAnimatedDrawable != null) {
+            mAnimatedDrawable.reset();
+        }
+    }
+
+    public void clearAnimationCallbacks() {
+        if (mAnimatedDrawable != null) {
+            mAnimatedDrawable.clearAnimationCallbacks();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        Rect bounds = getBounds();
+        if (bounds.isEmpty()) {
+            return;
+        }
+
+        if (mAnimatedDrawable != null) {
+            mAnimatedDrawable.draw(canvas);
+        } else {
+            // If no cache or previous cached bitmap is hardware/software acceleration does not
+            // match the current canvas on draw then regenerate
+            boolean hwBitmapChanged = mState.mIsHardwareBitmap != canvas.isHardwareAccelerated();
+            if (hwBitmapChanged) {
+                mState.mIsHardwareBitmap = canvas.isHardwareAccelerated();
+            }
+            if (mState.mLastDrawnIcon == null || hwBitmapChanged) {
+                regenerateBitmapIconCache();
+            }
+            canvas.save();
+            canvas.translate(mState.mTranslationX, mState.mTranslationY);
+            canvas.rotate(mState.mRotateDegrees, getIntrinsicWidth() / 2, getIntrinsicHeight() / 2);
+
+            if (mState.mShadowSize > 0) {
+                if (mState.mLastDrawnShadow == null || hwBitmapChanged) {
+                    regenerateBitmapShadowCache();
+                }
+
+                // Translate (with rotation offset) before drawing the shadow
+                final float radians = (float) (mState.mRotateDegrees * Math.PI / 180);
+                final float shadowOffsetX = (float) (Math.sin(radians) * mState.mShadowOffsetY
+                        + Math.cos(radians) * mState.mShadowOffsetX) - mState.mTranslationX;
+                final float shadowOffsetY = (float) (Math.cos(radians) * mState.mShadowOffsetY
+                        - Math.sin(radians) * mState.mShadowOffsetX) - mState.mTranslationY;
+                canvas.drawBitmap(mState.mLastDrawnShadow, shadowOffsetX, shadowOffsetY,
+                        mShadowPaint);
+            }
+            canvas.drawBitmap(mState.mLastDrawnIcon, null, bounds, mIconPaint);
+            canvas.restore();
+        }
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return mState.canApplyTheme();
+    }
+
+    @ColorInt int getDrawableBackgroundColor() {
+        return mState.mOvalBackgroundColor.toArgb();
+    }
+
+    boolean hasOvalBg() {
+        return mState.mOvalBackgroundColor != null;
+    }
+
+    private void regenerateBitmapIconCache() {
+        final int width = getIntrinsicWidth();
+        final int height = getIntrinsicHeight();
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bitmap);
+
+        // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
+        final Drawable d = mState.mChildState.newDrawable().mutate();
+        setDrawableBounds(d);
+        canvas.save();
+        if (mState.mHorizontalFlip) {
+            canvas.scale(-1f, 1f, width * 0.5f, height * 0.5f);
+        }
+        d.draw(canvas);
+        canvas.restore();
+
+        if (mState.mIsHardwareBitmap) {
+            bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+        }
+        mState.mLastDrawnIcon = bitmap;
+    }
+
+    private void regenerateBitmapShadowCache() {
+        if (mState.mShadowSize == 0) {
+            // No shadow
+            mState.mLastDrawnIcon = null;
+            return;
+        }
+
+        final int width = getIntrinsicWidth();
+        final int height = getIntrinsicHeight();
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+
+        // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
+        final Drawable d = mState.mChildState.newDrawable().mutate();
+        setDrawableBounds(d);
+        canvas.save();
+        if (mState.mHorizontalFlip) {
+            canvas.scale(-1f, 1f, width * 0.5f, height * 0.5f);
+        }
+        d.draw(canvas);
+        canvas.restore();
+
+        // Draws the shadow from original drawable
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, Blur.NORMAL));
+        int[] offset = new int[2];
+        final Bitmap shadow = bitmap.extractAlpha(paint, offset);
+        paint.setMaskFilter(null);
+        bitmap.eraseColor(Color.TRANSPARENT);
+        canvas.drawBitmap(shadow, offset[0], offset[1], paint);
+
+        if (mState.mIsHardwareBitmap) {
+            bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+        }
+        mState.mLastDrawnShadow = bitmap;
+    }
+
+    /**
+     * Set the alpha of the shadow. As dark intensity increases, drop the alpha of the shadow since
+     * dark color and shadow should not be visible at the same time.
+     */
+    private void updateShadowAlpha() {
+        // Update the color from the original color's alpha as the max
+        int alpha = Color.alpha(mState.mShadowColor);
+        mShadowPaint.setAlpha(
+                Math.round(alpha * (mState.mAlpha / 255f) * (1 - mState.mDarkIntensity)));
+    }
+
+    /**
+     * Prevent shadow clipping by offsetting the drawable bounds by the shadow and its offset
+     * @param d the drawable to set the bounds
+     */
+    private void setDrawableBounds(Drawable d) {
+        final int offsetX = mState.mShadowSize + Math.abs(mState.mShadowOffsetX);
+        final int offsetY = mState.mShadowSize + Math.abs(mState.mShadowOffsetY);
+        d.setBounds(offsetX, offsetY, getIntrinsicWidth() - offsetX,
+                getIntrinsicHeight() - offsetY);
+    }
+
+    private static class ShadowDrawableState extends ConstantState {
+        int mChangingConfigurations;
+        int mBaseWidth;
+        int mBaseHeight;
+        float mRotateDegrees;
+        float mTranslationX;
+        float mTranslationY;
+        int mShadowOffsetX;
+        int mShadowOffsetY;
+        int mShadowSize;
+        int mShadowColor;
+        float mDarkIntensity;
+        int mAlpha;
+        boolean mHorizontalFlip;
+
+        boolean mIsHardwareBitmap;
+        Bitmap mLastDrawnIcon;
+        Bitmap mLastDrawnShadow;
+        ConstantState mChildState;
+
+        final int mLightColor;
+        final int mDarkColor;
+        final boolean mSupportsAnimation;
+        final Color mOvalBackgroundColor;
+
+        public ShadowDrawableState(@ColorInt int lightColor, @ColorInt int darkColor,
+                boolean animated, boolean horizontalFlip, Color ovalBackgroundColor) {
+            mLightColor = lightColor;
+            mDarkColor = darkColor;
+            mSupportsAnimation = animated;
+            mAlpha = 255;
+            mHorizontalFlip = horizontalFlip;
+            mOvalBackgroundColor = ovalBackgroundColor;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new KeyButtonDrawable(null, this);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mChangingConfigurations;
+        }
+
+        @Override
+        public boolean canApplyTheme() {
+            return true;
+        }
+    }
+
+    /**
+     * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
+     * {@link #create(Context, int, boolean, boolean)}.
+     */
+    public static KeyButtonDrawable create(Context context, @ColorInt int lightColor,
+            @ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow,
+            Color ovalBackgroundColor) {
+        final Resources res = context.getResources();
+        boolean isRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        Drawable d = context.getDrawable(iconResId);
+        final KeyButtonDrawable drawable = new KeyButtonDrawable(d, lightColor, darkColor,
+                isRtl && d.isAutoMirrored(), ovalBackgroundColor);
+        if (hasShadow) {
+            int offsetX = dpToPx(NAV_KEY_BUTTON_SHADOW_OFFSET_X, res);
+            int offsetY = dpToPx(NAV_KEY_BUTTON_SHADOW_OFFSET_Y, res);
+            int radius = dpToPx(NAV_KEY_BUTTON_SHADOW_RADIUS, res);
+            int color = NAV_KEY_BUTTON_SHADOW_COLOR;
+            drawable.setShadowProperties(offsetX, offsetY, radius, color);
+        }
+        return drawable;
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java
new file mode 100644
index 0000000..38a63b6
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.DimenRes;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RecordingCanvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Trace;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+final class KeyButtonRipple extends Drawable {
+
+    private static final float GLOW_MAX_SCALE_FACTOR = 1.35f;
+    private static final float GLOW_MAX_ALPHA = 0.2f;
+    private static final float GLOW_MAX_ALPHA_DARK = 0.1f;
+    private static final int ANIMATION_DURATION_SCALE = 350;
+    private static final int ANIMATION_DURATION_FADE = 450;
+    private static final Interpolator ALPHA_OUT_INTERPOLATOR =
+            new PathInterpolator(0f, 0f, 0.8f, 1f);
+
+    @DimenRes
+    private final int mMaxWidthResource;
+
+    private Paint mRipplePaint;
+    private CanvasProperty<Float> mLeftProp;
+    private CanvasProperty<Float> mTopProp;
+    private CanvasProperty<Float> mRightProp;
+    private CanvasProperty<Float> mBottomProp;
+    private CanvasProperty<Float> mRxProp;
+    private CanvasProperty<Float> mRyProp;
+    private CanvasProperty<Paint> mPaintProp;
+    private float mGlowAlpha = 0f;
+    private float mGlowScale = 1f;
+    private boolean mPressed;
+    private boolean mVisible;
+    private boolean mDrawingHardwareGlow;
+    private int mMaxWidth;
+    private boolean mLastDark;
+    private boolean mDark;
+    private boolean mDelayTouchFeedback;
+
+    private final Interpolator mInterpolator = new LogInterpolator();
+    private boolean mSupportHardware;
+    private final View mTargetView;
+    private final Handler mHandler = new Handler();
+
+    private final HashSet<Animator> mRunningAnimations = new HashSet<>();
+    private final ArrayList<Animator> mTmpArray = new ArrayList<>();
+
+    private final TraceAnimatorListener mExitHwTraceAnimator =
+            new TraceAnimatorListener("exitHardware");
+    private final TraceAnimatorListener mEnterHwTraceAnimator =
+            new TraceAnimatorListener("enterHardware");
+
+    public enum Type {
+        OVAL,
+        ROUNDED_RECT
+    }
+
+    private Type mType = Type.ROUNDED_RECT;
+
+    public KeyButtonRipple(Context ctx, View targetView, @DimenRes int maxWidthResource) {
+        mMaxWidthResource = maxWidthResource;
+        mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
+        mTargetView = targetView;
+    }
+
+    public void updateResources() {
+        mMaxWidth = mTargetView.getContext().getResources()
+                .getDimensionPixelSize(mMaxWidthResource);
+        invalidateSelf();
+    }
+
+    public void setDarkIntensity(float darkIntensity) {
+        mDark = darkIntensity >= 0.5f;
+    }
+
+    public void setDelayTouchFeedback(boolean delay) {
+        mDelayTouchFeedback = delay;
+    }
+
+    public void setType(Type type) {
+        mType = type;
+    }
+
+    private Paint getRipplePaint() {
+        if (mRipplePaint == null) {
+            mRipplePaint = new Paint();
+            mRipplePaint.setAntiAlias(true);
+            mRipplePaint.setColor(mLastDark ? 0xff000000 : 0xffffffff);
+        }
+        return mRipplePaint;
+    }
+
+    private void drawSoftware(Canvas canvas) {
+        if (mGlowAlpha > 0f) {
+            final Paint p = getRipplePaint();
+            p.setAlpha((int)(mGlowAlpha * 255f));
+
+            final float w = getBounds().width();
+            final float h = getBounds().height();
+            final boolean horizontal = w > h;
+            final float diameter = getRippleSize() * mGlowScale;
+            final float radius = diameter * .5f;
+            final float cx = w * .5f;
+            final float cy = h * .5f;
+            final float rx = horizontal ? radius : cx;
+            final float ry = horizontal ? cy : radius;
+            final float corner = horizontal ? cy : cx;
+
+            if (mType == Type.ROUNDED_RECT) {
+                canvas.drawRoundRect(cx - rx, cy - ry, cx + rx, cy + ry, corner, corner, p);
+            } else {
+                canvas.save();
+                canvas.translate(cx, cy);
+                float r = Math.min(rx, ry);
+                canvas.drawOval(-r, -r, r, r, p);
+                canvas.restore();
+            }
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        mSupportHardware = canvas.isHardwareAccelerated();
+        if (mSupportHardware) {
+            drawHardware((RecordingCanvas) canvas);
+        } else {
+            drawSoftware(canvas);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        // Not supported.
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        // Not supported.
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    private boolean isHorizontal() {
+        return getBounds().width() > getBounds().height();
+    }
+
+    private void drawHardware(RecordingCanvas c) {
+        if (mDrawingHardwareGlow) {
+            if (mType == Type.ROUNDED_RECT) {
+                c.drawRoundRect(mLeftProp, mTopProp, mRightProp, mBottomProp, mRxProp, mRyProp,
+                        mPaintProp);
+            } else {
+                CanvasProperty<Float> cx = CanvasProperty.createFloat(getBounds().width() / 2);
+                CanvasProperty<Float> cy = CanvasProperty.createFloat(getBounds().height() / 2);
+                int d = Math.min(getBounds().width(), getBounds().height());
+                CanvasProperty<Float> r = CanvasProperty.createFloat(1.0f * d / 2);
+                c.drawCircle(cx, cy, r, mPaintProp);
+            }
+        }
+    }
+
+    /** Gets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */
+    public float getGlowAlpha() {
+        return mGlowAlpha;
+    }
+
+    /** Sets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */
+    public void setGlowAlpha(float x) {
+        mGlowAlpha = x;
+        invalidateSelf();
+    }
+
+    /** Gets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */
+    public float getGlowScale() {
+        return mGlowScale;
+    }
+
+    /** Sets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */
+    public void setGlowScale(float x) {
+        mGlowScale = x;
+        invalidateSelf();
+    }
+
+    private float getMaxGlowAlpha() {
+        return mLastDark ? GLOW_MAX_ALPHA_DARK : GLOW_MAX_ALPHA;
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        boolean pressed = false;
+        for (int i = 0; i < state.length; i++) {
+            if (state[i] == android.R.attr.state_pressed) {
+                pressed = true;
+                break;
+            }
+        }
+        if (pressed != mPressed) {
+            setPressed(pressed);
+            mPressed = pressed;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        boolean changed = super.setVisible(visible, restart);
+        if (changed) {
+            // End any existing animations when the visibility changes
+            jumpToCurrentState();
+        }
+        return changed;
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        endAnimations("jumpToCurrentState", false /* cancel */);
+    }
+
+    @Override
+    public boolean isStateful() {
+        return true;
+    }
+
+    @Override
+    public boolean hasFocusStateSpecified() {
+        return true;
+    }
+
+    public void setPressed(boolean pressed) {
+        if (mDark != mLastDark && pressed) {
+            mRipplePaint = null;
+            mLastDark = mDark;
+        }
+        if (mSupportHardware) {
+            setPressedHardware(pressed);
+        } else {
+            setPressedSoftware(pressed);
+        }
+    }
+
+    /**
+     * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch
+     * is enabled.
+     */
+    public void abortDelayedRipple() {
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    private void endAnimations(String reason, boolean cancel) {
+        Trace.beginSection("KeyButtonRipple.endAnim: reason=" + reason + " cancel=" + cancel);
+        Trace.endSection();
+        mVisible = false;
+        mTmpArray.addAll(mRunningAnimations);
+        int size = mTmpArray.size();
+        for (int i = 0; i < size; i++) {
+            Animator a = mTmpArray.get(i);
+            if (cancel) {
+                a.cancel();
+            } else {
+                a.end();
+            }
+        }
+        mTmpArray.clear();
+        mRunningAnimations.clear();
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    private void setPressedSoftware(boolean pressed) {
+        if (pressed) {
+            if (mDelayTouchFeedback) {
+                if (mRunningAnimations.isEmpty()) {
+                    mHandler.removeCallbacksAndMessages(null);
+                    mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
+                } else if (mVisible) {
+                    enterSoftware();
+                }
+            } else {
+                enterSoftware();
+            }
+        } else {
+            exitSoftware();
+        }
+    }
+
+    private void enterSoftware() {
+        endAnimations("enterSoftware", true /* cancel */);
+        mVisible = true;
+        mGlowAlpha = getMaxGlowAlpha();
+        ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
+                0f, GLOW_MAX_SCALE_FACTOR);
+        scaleAnimator.setInterpolator(mInterpolator);
+        scaleAnimator.setDuration(ANIMATION_DURATION_SCALE);
+        scaleAnimator.addListener(mAnimatorListener);
+        scaleAnimator.start();
+        mRunningAnimations.add(scaleAnimator);
+
+        // With the delay, it could eventually animate the enter animation with no pressed state,
+        // then immediately show the exit animation. If this is skipped there will be no ripple.
+        if (mDelayTouchFeedback && !mPressed) {
+            exitSoftware();
+        }
+    }
+
+    private void exitSoftware() {
+        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
+        alphaAnimator.setInterpolator(ALPHA_OUT_INTERPOLATOR);
+        alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
+        alphaAnimator.addListener(mAnimatorListener);
+        alphaAnimator.start();
+        mRunningAnimations.add(alphaAnimator);
+    }
+
+    private void setPressedHardware(boolean pressed) {
+        if (pressed) {
+            if (mDelayTouchFeedback) {
+                if (mRunningAnimations.isEmpty()) {
+                    mHandler.removeCallbacksAndMessages(null);
+                    mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
+                } else if (mVisible) {
+                    enterHardware();
+                }
+            } else {
+                enterHardware();
+            }
+        } else {
+            exitHardware();
+        }
+    }
+
+    /**
+     * Sets the left/top property for the round rect to {@code prop} depending on whether we are
+     * horizontal or vertical mode.
+     */
+    private void setExtendStart(CanvasProperty<Float> prop) {
+        if (isHorizontal()) {
+            mLeftProp = prop;
+        } else {
+            mTopProp = prop;
+        }
+    }
+
+    private CanvasProperty<Float> getExtendStart() {
+        return isHorizontal() ? mLeftProp : mTopProp;
+    }
+
+    /**
+     * Sets the right/bottom property for the round rect to {@code prop} depending on whether we are
+     * horizontal or vertical mode.
+     */
+    private void setExtendEnd(CanvasProperty<Float> prop) {
+        if (isHorizontal()) {
+            mRightProp = prop;
+        } else {
+            mBottomProp = prop;
+        }
+    }
+
+    private CanvasProperty<Float> getExtendEnd() {
+        return isHorizontal() ? mRightProp : mBottomProp;
+    }
+
+    private int getExtendSize() {
+        return isHorizontal() ? getBounds().width() : getBounds().height();
+    }
+
+    private int getRippleSize() {
+        int size = isHorizontal() ? getBounds().width() : getBounds().height();
+        return Math.min(size, mMaxWidth);
+    }
+
+    private void enterHardware() {
+        endAnimations("enterHardware", true /* cancel */);
+        mVisible = true;
+        mDrawingHardwareGlow = true;
+        setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
+        final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
+                getExtendSize()/2 - GLOW_MAX_SCALE_FACTOR * getRippleSize()/2);
+        startAnim.setDuration(ANIMATION_DURATION_SCALE);
+        startAnim.setInterpolator(mInterpolator);
+        startAnim.addListener(mAnimatorListener);
+        startAnim.setTarget(mTargetView);
+
+        setExtendEnd(CanvasProperty.createFloat(getExtendSize() / 2));
+        final RenderNodeAnimator endAnim = new RenderNodeAnimator(getExtendEnd(),
+                getExtendSize()/2 + GLOW_MAX_SCALE_FACTOR * getRippleSize()/2);
+        endAnim.setDuration(ANIMATION_DURATION_SCALE);
+        endAnim.setInterpolator(mInterpolator);
+        endAnim.addListener(mAnimatorListener);
+        endAnim.addListener(mEnterHwTraceAnimator);
+        endAnim.setTarget(mTargetView);
+
+        if (isHorizontal()) {
+            mTopProp = CanvasProperty.createFloat(0f);
+            mBottomProp = CanvasProperty.createFloat(getBounds().height());
+            mRxProp = CanvasProperty.createFloat(getBounds().height()/2);
+            mRyProp = CanvasProperty.createFloat(getBounds().height()/2);
+        } else {
+            mLeftProp = CanvasProperty.createFloat(0f);
+            mRightProp = CanvasProperty.createFloat(getBounds().width());
+            mRxProp = CanvasProperty.createFloat(getBounds().width()/2);
+            mRyProp = CanvasProperty.createFloat(getBounds().width()/2);
+        }
+
+        mGlowScale = GLOW_MAX_SCALE_FACTOR;
+        mGlowAlpha = getMaxGlowAlpha();
+        mRipplePaint = getRipplePaint();
+        mRipplePaint.setAlpha((int) (mGlowAlpha * 255));
+        mPaintProp = CanvasProperty.createPaint(mRipplePaint);
+
+        startAnim.start();
+        endAnim.start();
+        mRunningAnimations.add(startAnim);
+        mRunningAnimations.add(endAnim);
+
+        invalidateSelf();
+
+        // With the delay, it could eventually animate the enter animation with no pressed state,
+        // then immediately show the exit animation. If this is skipped there will be no ripple.
+        if (mDelayTouchFeedback && !mPressed) {
+            exitHardware();
+        }
+    }
+
+    private void exitHardware() {
+        mPaintProp = CanvasProperty.createPaint(getRipplePaint());
+        final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
+                RenderNodeAnimator.PAINT_ALPHA, 0);
+        opacityAnim.setDuration(ANIMATION_DURATION_FADE);
+        opacityAnim.setInterpolator(ALPHA_OUT_INTERPOLATOR);
+        opacityAnim.addListener(mAnimatorListener);
+        opacityAnim.addListener(mExitHwTraceAnimator);
+        opacityAnim.setTarget(mTargetView);
+
+        opacityAnim.start();
+        mRunningAnimations.add(opacityAnim);
+
+        invalidateSelf();
+    }
+
+    private final AnimatorListenerAdapter mAnimatorListener =
+            new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mRunningAnimations.remove(animation);
+                    if (mRunningAnimations.isEmpty() && !mPressed) {
+                        mVisible = false;
+                        mDrawingHardwareGlow = false;
+                        invalidateSelf();
+                    }
+                }
+            };
+
+    private static final class TraceAnimatorListener extends AnimatorListenerAdapter {
+        private final String mName;
+        TraceAnimatorListener(String name) {
+            mName = name;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            Trace.beginSection("KeyButtonRipple.start." + mName);
+            Trace.endSection();
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            Trace.beginSection("KeyButtonRipple.cancel." + mName);
+            Trace.endSection();
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            Trace.beginSection("KeyButtonRipple.end." + mName);
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Interpolator with a smooth log deceleration
+     */
+    private static final class LogInterpolator implements Interpolator {
+        @Override
+        public float getInterpolation(float input) {
+            return 1 - (float) Math.pow(400, -input * 1.4);
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
new file mode 100644
index 0000000..74d30f8
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_UNKNOWN;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.ImageView;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * @hide
+ */
+public class KeyButtonView extends ImageView implements ButtonInterface {
+    private static final String TAG = KeyButtonView.class.getSimpleName();
+
+    private final boolean mPlaySounds;
+    private long mDownTime;
+    private boolean mTracking;
+    private int mCode;
+    private int mTouchDownX;
+    private int mTouchDownY;
+    private AudioManager mAudioManager;
+    private boolean mGestureAborted;
+    @VisibleForTesting boolean mLongClicked;
+    private OnClickListener mOnClickListener;
+    private final KeyButtonRipple mRipple;
+    private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    private float mDarkIntensity;
+    private boolean mHasOvalBg = false;
+
+    private final Runnable mCheckLongPress = new Runnable() {
+        public void run() {
+            if (isPressed()) {
+                // Log.d("KeyButtonView", "longpressed: " + this);
+                if (isLongClickable()) {
+                    // Just an old-fashioned ImageView
+                    performLongClick();
+                    mLongClicked = true;
+                } else {
+                    if (mCode != KEYCODE_UNKNOWN) {
+                        sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+                    }
+                    mLongClicked = true;
+                }
+            }
+        }
+    };
+
+    public KeyButtonView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        // TODO(b/205803355): Figure out better place to set this.
+        switch (getId()) {
+            case com.android.internal.R.id.input_method_nav_back:
+                mCode = KEYCODE_BACK;
+                break;
+            default:
+                mCode = KEYCODE_UNKNOWN;
+                break;
+        }
+
+        mPlaySounds = true;
+
+        setClickable(true);
+        mAudioManager = context.getSystemService(AudioManager.class);
+
+        mRipple = new KeyButtonRipple(context, this,
+                com.android.internal.R.dimen.input_method_nav_key_button_ripple_max_width);
+        setBackground(mRipple);
+        setWillNotDraw(false);
+        forceHasOverlappingRendering(false);
+    }
+
+    @Override
+    public boolean isClickable() {
+        return mCode != KEYCODE_UNKNOWN || super.isClickable();
+    }
+
+    public void setCode(int code) {
+        mCode = code;
+    }
+
+    @Override
+    public void setOnClickListener(OnClickListener onClickListener) {
+        super.setOnClickListener(onClickListener);
+        mOnClickListener = onClickListener;
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (mCode != KEYCODE_UNKNOWN) {
+            info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
+            if (isLongClickable()) {
+                info.addAction(
+                        new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null));
+            }
+        }
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        if (visibility != View.VISIBLE) {
+            jumpDrawablesToCurrentState();
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (action == ACTION_CLICK && mCode != KEYCODE_UNKNOWN) {
+            sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis());
+            sendEvent(KeyEvent.ACTION_UP, mTracking ? KeyEvent.FLAG_TRACKING : 0);
+            mTracking = false;
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+            playSoundEffect(SoundEffectConstants.CLICK);
+            return true;
+        } else if (action == ACTION_LONG_CLICK && mCode != KEYCODE_UNKNOWN) {
+            sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+            sendEvent(KeyEvent.ACTION_UP, mTracking ? KeyEvent.FLAG_TRACKING : 0);
+            mTracking = false;
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+            return true;
+        }
+        return super.performAccessibilityActionInternal(action, arguments);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        final boolean showSwipeUI = false; // mOverviewProxyService.shouldShowSwipeUpUI();
+        final int action = ev.getAction();
+        int x, y;
+        if (action == MotionEvent.ACTION_DOWN) {
+            mGestureAborted = false;
+        }
+        if (mGestureAborted) {
+            setPressed(false);
+            return false;
+        }
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mDownTime = SystemClock.uptimeMillis();
+                mLongClicked = false;
+                setPressed(true);
+
+                // Use raw X and Y to detect gestures in case a parent changes the x and y values
+                mTouchDownX = (int) ev.getRawX();
+                mTouchDownY = (int) ev.getRawY();
+                if (mCode != KEYCODE_UNKNOWN) {
+                    sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
+                } else {
+                    // Provide the same haptic feedback that the system offers for virtual keys.
+                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                }
+                if (!showSwipeUI) {
+                    playSoundEffect(SoundEffectConstants.CLICK);
+                }
+                removeCallbacks(mCheckLongPress);
+                postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
+                break;
+            case MotionEvent.ACTION_MOVE:
+                x = (int)ev.getRawX();
+                y = (int)ev.getRawY();
+
+                float slop = getQuickStepTouchSlopPx(getContext());
+                if (Math.abs(x - mTouchDownX) > slop || Math.abs(y - mTouchDownY) > slop) {
+                    // When quick step is enabled, prevent animating the ripple triggered by
+                    // setPressed and decide to run it on touch up
+                    setPressed(false);
+                    removeCallbacks(mCheckLongPress);
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                setPressed(false);
+                if (mCode != KEYCODE_UNKNOWN) {
+                    sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
+                }
+                removeCallbacks(mCheckLongPress);
+                break;
+            case MotionEvent.ACTION_UP:
+                final boolean doIt = isPressed() && !mLongClicked;
+                setPressed(false);
+                final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
+                if (showSwipeUI) {
+                    if (doIt) {
+                        // Apply haptic feedback on touch up since there is none on touch down
+                        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                        playSoundEffect(SoundEffectConstants.CLICK);
+                    }
+                } else if (doHapticFeedback && !mLongClicked) {
+                    // Always send a release ourselves because it doesn't seem to be sent elsewhere
+                    // and it feels weird to sometimes get a release haptic and other times not.
+                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
+                }
+                if (mCode != KEYCODE_UNKNOWN) {
+                    if (doIt) {
+                        sendEvent(KeyEvent.ACTION_UP, mTracking ? KeyEvent.FLAG_TRACKING : 0);
+                        mTracking = false;
+                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+                    } else {
+                        sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
+                    }
+                } else {
+                    // no key code, just a regular ImageView
+                    if (doIt && mOnClickListener != null) {
+                        mOnClickListener.onClick(this);
+                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+                    }
+                }
+                removeCallbacks(mCheckLongPress);
+                break;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        super.setImageDrawable(drawable);
+
+        if (drawable == null) {
+            return;
+        }
+        KeyButtonDrawable keyButtonDrawable = (KeyButtonDrawable) drawable;
+        keyButtonDrawable.setDarkIntensity(mDarkIntensity);
+        mHasOvalBg = keyButtonDrawable.hasOvalBg();
+        if (mHasOvalBg) {
+            mOvalBgPaint.setColor(keyButtonDrawable.getDrawableBackgroundColor());
+        }
+        mRipple.setType(keyButtonDrawable.hasOvalBg() ? KeyButtonRipple.Type.OVAL
+                : KeyButtonRipple.Type.ROUNDED_RECT);
+    }
+
+    public void playSoundEffect(int soundConstant) {
+        if (!mPlaySounds) return;
+        mAudioManager.playSoundEffect(soundConstant);
+    }
+
+    public void sendEvent(int action, int flags) {
+        sendEvent(action, flags, SystemClock.uptimeMillis());
+    }
+
+    private void sendEvent(int action, int flags, long when) {
+        if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
+            if (action == MotionEvent.ACTION_UP) {
+                // TODO(b/205803355): Implement notifyBackAction();
+            }
+        }
+
+        // TODO(b/205803355): Consolidate this logic to somewhere else.
+        if (mContext instanceof InputMethodService) {
+            final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
+            final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
+                    0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+                    flags | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+                    InputDevice.SOURCE_KEYBOARD);
+            int displayId = INVALID_DISPLAY;
+
+            // Make KeyEvent work on multi-display environment
+            if (getDisplay() != null) {
+                displayId = getDisplay().getDisplayId();
+            }
+            if (displayId != INVALID_DISPLAY) {
+                ev.setDisplayId(displayId);
+            }
+            final InputMethodService ims = (InputMethodService) mContext;
+            final boolean handled;
+            switch (action) {
+                case KeyEvent.ACTION_DOWN:
+                    handled = ims.onKeyDown(ev.getKeyCode(), ev);
+                    mTracking = handled && ev.getRepeatCount() == 0 &&
+                            (ev.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
+                    break;
+                case KeyEvent.ACTION_UP:
+                    handled = ims.onKeyUp(ev.getKeyCode(), ev);
+                    break;
+                default:
+                    handled = false;
+                    break;
+            }
+            if (!handled) {
+                final InputConnection ic = ims.getCurrentInputConnection();
+                if (ic != null) {
+                    ic.sendKeyEvent(ev);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void setDarkIntensity(float darkIntensity) {
+        mDarkIntensity = darkIntensity;
+
+        Drawable drawable = getDrawable();
+        if (drawable != null) {
+            ((KeyButtonDrawable) drawable).setDarkIntensity(darkIntensity);
+            // Since we reuse the same drawable for multiple views, we need to invalidate the view
+            // manually.
+            invalidate();
+        }
+        mRipple.setDarkIntensity(darkIntensity);
+    }
+
+    @Override
+    public void setDelayTouchFeedback(boolean shouldDelay) {
+        mRipple.setDelayTouchFeedback(shouldDelay);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mHasOvalBg) {
+            int d = Math.min(getWidth(), getHeight());
+            canvas.drawOval(0, 0, d, d, mOvalBgPaint);
+        }
+        super.draw(canvas);
+    }
+
+    /**
+     * Ratio of quickstep touch slop (when system takes over the touch) to view touch slop
+     */
+    public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+
+    /**
+     * Touch slop for quickstep gesture
+     */
+    private static float getQuickStepTouchSlopPx(Context context) {
+        return QUICKSTEP_TOUCH_SLOP_RATIO * ViewConfiguration.get(context).getScaledTouchSlop();
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java
new file mode 100644
index 0000000..93c5439
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.annotation.ColorInt;
+
+final class NavigationBarConstants {
+    private NavigationBarConstants() {
+        // Not intended to be instantiated.
+    }
+
+    // Copied from "navbar_back_button_ime_offset"
+    // TODO(b/215443343): Handle this in the drawable then remove this constant.
+    static final float NAVBAR_BACK_BUTTON_IME_OFFSET = 2.0f;
+
+    // Copied from "light_mode_icon_color_single_tone" at packages/SettingsLib/res/values/colors.xml
+    @ColorInt
+    static final int LIGHT_MODE_ICON_COLOR_SINGLE_TONE = 0xffffffff;
+
+    // Copied from "dark_mode_icon_color_single_tone" at packages/SettingsLib/res/values/colors.xml
+    @ColorInt
+    static final int DARK_MODE_ICON_COLOR_SINGLE_TONE = 0x99000000;
+
+    // Copied from "navigation_bar_deadzone_hold"
+    static final int NAVIGATION_BAR_DEADZONE_HOLD = 333;
+
+    // Copied from "navigation_bar_deadzone_hold"
+    static final int NAVIGATION_BAR_DEADZONE_DECAY = 333;
+
+    // Copied from "navigation_bar_deadzone_size"
+    static final float NAVIGATION_BAR_DEADZONE_SIZE = 12.0f;
+
+    // Copied from "navigation_bar_deadzone_size_max"
+    static final float NAVIGATION_BAR_DEADZONE_SIZE_MAX = 32.0f;
+
+    // Copied from "nav_key_button_shadow_offset_x"
+    static final float NAV_KEY_BUTTON_SHADOW_OFFSET_X = 0.0f;
+
+    // Copied from "nav_key_button_shadow_offset_y"
+    static final float NAV_KEY_BUTTON_SHADOW_OFFSET_Y = 1.0f;
+
+    // Copied from "nav_key_button_shadow_radius"
+    static final float NAV_KEY_BUTTON_SHADOW_RADIUS = 0.5f;
+
+    // Copied from "nav_key_button_shadow_color"
+    @ColorInt
+    static final int NAV_KEY_BUTTON_SHADOW_COLOR = 0x30000000;
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java
new file mode 100644
index 0000000..f01173e
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.view.MotionEvent.ACTION_OUTSIDE;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+/**
+ * @hide
+ */
+public final class NavigationBarFrame extends FrameLayout {
+
+    private DeadZone mDeadZone = null;
+
+    public NavigationBarFrame(@NonNull Context context) {
+        super(context);
+    }
+
+    public NavigationBarFrame(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setDeadZone(@NonNull DeadZone deadZone) {
+        mDeadZone = deadZone;
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (event.getAction() == ACTION_OUTSIDE) {
+            if (mDeadZone != null) {
+                return mDeadZone.onTouchEvent(event);
+            }
+        }
+        return super.dispatchTouchEvent(event);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java
new file mode 100644
index 0000000..d488890
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.inputmethodservice.navigationbar.ReverseLinearLayout.ReverseRelativeLayout;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Space;
+
+/**
+ * @hide
+ */
+public final class NavigationBarInflaterView extends FrameLayout {
+
+    private static final String TAG = "NavBarInflater";
+
+    public static final String NAV_BAR_VIEWS = "sysui_nav_bar";
+    public static final String NAV_BAR_LEFT = "sysui_nav_bar_left";
+    public static final String NAV_BAR_RIGHT = "sysui_nav_bar_right";
+
+    public static final String MENU_IME_ROTATE = "menu_ime";
+    public static final String BACK = "back";
+    public static final String HOME = "home";
+    public static final String RECENT = "recent";
+    public static final String NAVSPACE = "space";
+    public static final String CLIPBOARD = "clipboard";
+    public static final String HOME_HANDLE = "home_handle";
+    public static final String KEY = "key";
+    public static final String LEFT = "left";
+    public static final String RIGHT = "right";
+    public static final String CONTEXTUAL = "contextual";
+    public static final String IME_SWITCHER = "ime_switcher";
+
+    public static final String GRAVITY_SEPARATOR = ";";
+    public static final String BUTTON_SEPARATOR = ",";
+
+    public static final String SIZE_MOD_START = "[";
+    public static final String SIZE_MOD_END = "]";
+
+    public static final String KEY_CODE_START = "(";
+    public static final String KEY_IMAGE_DELIM = ":";
+    public static final String KEY_CODE_END = ")";
+    private static final String WEIGHT_SUFFIX = "W";
+    private static final String WEIGHT_CENTERED_SUFFIX = "WC";
+    private static final String ABSOLUTE_SUFFIX = "A";
+    private static final String ABSOLUTE_VERTICAL_CENTERED_SUFFIX = "C";
+
+    // Copied from "config_navBarLayoutHandle:
+    private static final String CONFIG_NAV_BAR_LAYOUT_HANDLE =
+            "back[70AC];home_handle;ime_switcher[70AC]";
+
+    protected LayoutInflater mLayoutInflater;
+    protected LayoutInflater mLandscapeInflater;
+
+    protected FrameLayout mHorizontal;
+
+    SparseArray<ButtonDispatcher> mButtonDispatchers;
+
+    private View mLastPortrait;
+    private View mLastLandscape;
+
+    private boolean mAlternativeOrder;
+
+    public NavigationBarInflaterView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        createInflaters();
+    }
+
+    void createInflaters() {
+        mLayoutInflater = LayoutInflater.from(mContext);
+        Configuration landscape = new Configuration();
+        landscape.setTo(mContext.getResources().getConfiguration());
+        landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        inflateChildren();
+        clearViews();
+        inflateLayout(getDefaultLayout());
+    }
+
+    private void inflateChildren() {
+        removeAllViews();
+        mHorizontal = (FrameLayout) mLayoutInflater.inflate(
+                com.android.internal.R.layout.input_method_navigation_layout,
+                this /* root */, false /* attachToRoot */);
+        addView(mHorizontal);
+        updateAlternativeOrder();
+    }
+
+    String getDefaultLayout() {
+        return CONFIG_NAV_BAR_LAYOUT_HANDLE;
+    }
+
+    public void setButtonDispatchers(SparseArray<ButtonDispatcher> buttonDispatchers) {
+        mButtonDispatchers = buttonDispatchers;
+        for (int i = 0; i < buttonDispatchers.size(); i++) {
+            initiallyFill(buttonDispatchers.valueAt(i));
+        }
+    }
+
+    void updateButtonDispatchersCurrentView() {
+        if (mButtonDispatchers != null) {
+            View view = mHorizontal;
+            for (int i = 0; i < mButtonDispatchers.size(); i++) {
+                final ButtonDispatcher dispatcher = mButtonDispatchers.valueAt(i);
+                dispatcher.setCurrentView(view);
+            }
+        }
+    }
+
+    void setAlternativeOrder(boolean alternativeOrder) {
+        if (alternativeOrder != mAlternativeOrder) {
+            mAlternativeOrder = alternativeOrder;
+            updateAlternativeOrder();
+        }
+    }
+
+    private void updateAlternativeOrder() {
+        updateAlternativeOrder(mHorizontal.findViewById(
+                com.android.internal.R.id.input_method_nav_ends_group));
+        updateAlternativeOrder(mHorizontal.findViewById(
+                com.android.internal.R.id.input_method_nav_center_group));
+    }
+
+    private void updateAlternativeOrder(View v) {
+        if (v instanceof ReverseLinearLayout) {
+            ((ReverseLinearLayout) v).setAlternativeOrder(mAlternativeOrder);
+        }
+    }
+
+    private void initiallyFill(
+            ButtonDispatcher buttonDispatcher) {
+        addAll(buttonDispatcher, mHorizontal.findViewById(
+                com.android.internal.R.id.input_method_nav_ends_group));
+        addAll(buttonDispatcher, mHorizontal.findViewById(
+                com.android.internal.R.id.input_method_nav_center_group));
+    }
+
+    private void addAll(ButtonDispatcher buttonDispatcher, ViewGroup parent) {
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            // Need to manually search for each id, just in case each group has more than one
+            // of a single id.  It probably mostly a waste of time, but shouldn't take long
+            // and will only happen once.
+            if (parent.getChildAt(i).getId() == buttonDispatcher.getId()) {
+                buttonDispatcher.addView(parent.getChildAt(i));
+            }
+            if (parent.getChildAt(i) instanceof ViewGroup) {
+                addAll(buttonDispatcher, (ViewGroup) parent.getChildAt(i));
+            }
+        }
+    }
+
+    protected void inflateLayout(String newLayout) {
+        if (newLayout == null) {
+            newLayout = getDefaultLayout();
+        }
+        String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+        if (sets.length != 3) {
+            Log.d(TAG, "Invalid layout.");
+            newLayout = getDefaultLayout();
+            sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+        }
+        String[] start = sets[0].split(BUTTON_SEPARATOR);
+        String[] center = sets[1].split(BUTTON_SEPARATOR);
+        String[] end = sets[2].split(BUTTON_SEPARATOR);
+        // Inflate these in start to end order or accessibility traversal will be messed up.
+        inflateButtons(start, mHorizontal.findViewById(
+                        com.android.internal.R.id.input_method_nav_ends_group),
+                false /* landscape */, true /* start */);
+
+        inflateButtons(center, mHorizontal.findViewById(
+                        com.android.internal.R.id.input_method_nav_center_group),
+                false /* landscape */, false /* start */);
+
+        addGravitySpacer(mHorizontal.findViewById(
+                com.android.internal.R.id.input_method_nav_ends_group));
+
+        inflateButtons(end, mHorizontal.findViewById(
+                        com.android.internal.R.id.input_method_nav_ends_group),
+                false /* landscape */, false /* start */);
+
+        updateButtonDispatchersCurrentView();
+    }
+
+    private void addGravitySpacer(LinearLayout layout) {
+        layout.addView(new Space(mContext), new LinearLayout.LayoutParams(0, 0, 1));
+    }
+
+    private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape,
+            boolean start) {
+        for (int i = 0; i < buttons.length; i++) {
+            inflateButton(buttons[i], parent, landscape, start);
+        }
+    }
+
+    private ViewGroup.LayoutParams copy(ViewGroup.LayoutParams layoutParams) {
+        if (layoutParams instanceof LinearLayout.LayoutParams) {
+            return new LinearLayout.LayoutParams(layoutParams.width, layoutParams.height,
+                    ((LinearLayout.LayoutParams) layoutParams).weight);
+        }
+        return new LayoutParams(layoutParams.width, layoutParams.height);
+    }
+
+    @Nullable
+    protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
+            boolean start) {
+        LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
+        View v = createView(buttonSpec, parent, inflater);
+        if (v == null) return null;
+
+        v = applySize(v, buttonSpec, landscape, start);
+        parent.addView(v);
+        addToDispatchers(v);
+        View lastView = landscape ? mLastLandscape : mLastPortrait;
+        View accessibilityView = v;
+        if (v instanceof ReverseRelativeLayout) {
+            accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);
+        }
+        if (lastView != null) {
+            accessibilityView.setAccessibilityTraversalAfter(lastView.getId());
+        }
+        if (landscape) {
+            mLastLandscape = accessibilityView;
+        } else {
+            mLastPortrait = accessibilityView;
+        }
+        return v;
+    }
+
+    private View applySize(View v, String buttonSpec, boolean landscape, boolean start) {
+        String sizeStr = extractSize(buttonSpec);
+        if (sizeStr == null) return v;
+
+        if (sizeStr.contains(WEIGHT_SUFFIX) || sizeStr.contains(ABSOLUTE_SUFFIX)) {
+            // To support gravity, wrap in RelativeLayout and apply gravity to it.
+            // Children wanting to use gravity must be smaller than the frame.
+            ReverseRelativeLayout frame = new ReverseRelativeLayout(mContext);
+            LayoutParams childParams = new LayoutParams(v.getLayoutParams());
+
+            // Compute gravity to apply
+            int gravity = (landscape) ? (start ? Gravity.TOP : Gravity.BOTTOM)
+                    : (start ? Gravity.START : Gravity.END);
+            if (sizeStr.endsWith(WEIGHT_CENTERED_SUFFIX)) {
+                gravity = Gravity.CENTER;
+            } else if (sizeStr.endsWith(ABSOLUTE_VERTICAL_CENTERED_SUFFIX)) {
+                gravity = Gravity.CENTER_VERTICAL;
+            }
+
+            // Set default gravity, flipped if needed in reversed layouts (270 RTL and 90 LTR)
+            frame.setDefaultGravity(gravity);
+            frame.setGravity(gravity); // Apply gravity to root
+
+            frame.addView(v, childParams);
+
+            if (sizeStr.contains(WEIGHT_SUFFIX)) {
+                // Use weighting to set the width of the frame
+                float weight = Float.parseFloat(
+                        sizeStr.substring(0, sizeStr.indexOf(WEIGHT_SUFFIX)));
+                frame.setLayoutParams(new LinearLayout.LayoutParams(0, MATCH_PARENT, weight));
+            } else {
+                int width = (int) convertDpToPx(mContext,
+                        Float.parseFloat(sizeStr.substring(0, sizeStr.indexOf(ABSOLUTE_SUFFIX))));
+                frame.setLayoutParams(new LinearLayout.LayoutParams(width, MATCH_PARENT));
+            }
+
+            // Ensure ripples can be drawn outside bounds
+            frame.setClipChildren(false);
+            frame.setClipToPadding(false);
+
+            return frame;
+        }
+
+        float size = Float.parseFloat(sizeStr);
+        ViewGroup.LayoutParams params = v.getLayoutParams();
+        params.width = (int) (params.width * size);
+        return v;
+    }
+
+    View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
+        View v = null;
+        String button = extractButton(buttonSpec);
+        if (LEFT.equals(button)) {
+            button = extractButton(NAVSPACE);
+        } else if (RIGHT.equals(button)) {
+            button = extractButton(MENU_IME_ROTATE);
+        }
+        if (HOME.equals(button)) {
+            //v = inflater.inflate(R.layout.home, parent, false);
+        } else if (BACK.equals(button)) {
+            v = inflater.inflate(com.android.internal.R.layout.input_method_nav_back, parent,
+                    false);
+        } else if (RECENT.equals(button)) {
+            //v = inflater.inflate(R.layout.recent_apps, parent, false);
+        } else if (MENU_IME_ROTATE.equals(button)) {
+            //v = inflater.inflate(R.layout.menu_ime, parent, false);
+        } else if (NAVSPACE.equals(button)) {
+            //v = inflater.inflate(R.layout.nav_key_space, parent, false);
+        } else if (CLIPBOARD.equals(button)) {
+            //v = inflater.inflate(R.layout.clipboard, parent, false);
+        } else if (CONTEXTUAL.equals(button)) {
+            //v = inflater.inflate(R.layout.contextual, parent, false);
+        } else if (HOME_HANDLE.equals(button)) {
+            v = inflater.inflate(com.android.internal.R.layout.input_method_nav_home_handle,
+                    parent, false);
+        } else if (IME_SWITCHER.equals(button)) {
+            v = inflater.inflate(com.android.internal.R.layout.input_method_nav_ime_switcher,
+                    parent, false);
+        } else if (button.startsWith(KEY)) {
+            /*
+            String uri = extractImage(button);
+            int code = extractKeycode(button);
+            v = inflater.inflate(R.layout.custom_key, parent, false);
+            ((KeyButtonView) v).setCode(code);
+            if (uri != null) {
+                if (uri.contains(":")) {
+                    ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
+                } else if (uri.contains("/")) {
+                    int index = uri.indexOf('/');
+                    String pkg = uri.substring(0, index);
+                    int id = Integer.parseInt(uri.substring(index + 1));
+                    ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
+                }
+            }
+         */
+        }
+        return v;
+    }
+
+    /*
+    public static String extractImage(String buttonSpec) {
+        if (!buttonSpec.contains(KEY_IMAGE_DELIM)) {
+            return null;
+        }
+        final int start = buttonSpec.indexOf(KEY_IMAGE_DELIM);
+        String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_CODE_END));
+        return subStr;
+    }
+
+    public static int extractKeycode(String buttonSpec) {
+        if (!buttonSpec.contains(KEY_CODE_START)) {
+            return 1;
+        }
+        final int start = buttonSpec.indexOf(KEY_CODE_START);
+        String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_IMAGE_DELIM));
+        return Integer.parseInt(subStr);
+    }
+    */
+
+    public static String extractSize(String buttonSpec) {
+        if (!buttonSpec.contains(SIZE_MOD_START)) {
+            return null;
+        }
+        final int sizeStart = buttonSpec.indexOf(SIZE_MOD_START);
+        return buttonSpec.substring(sizeStart + 1, buttonSpec.indexOf(SIZE_MOD_END));
+    }
+
+    public static String extractButton(String buttonSpec) {
+        if (!buttonSpec.contains(SIZE_MOD_START)) {
+            return buttonSpec;
+        }
+        return buttonSpec.substring(0, buttonSpec.indexOf(SIZE_MOD_START));
+    }
+
+    private void addToDispatchers(View v) {
+        if (mButtonDispatchers != null) {
+            final int indexOfKey = mButtonDispatchers.indexOfKey(v.getId());
+            if (indexOfKey >= 0) {
+                mButtonDispatchers.valueAt(indexOfKey).addView(v);
+            }
+            if (v instanceof ViewGroup) {
+                final ViewGroup viewGroup = (ViewGroup)v;
+                final int N = viewGroup.getChildCount();
+                for (int i = 0; i < N; i++) {
+                    addToDispatchers(viewGroup.getChildAt(i));
+                }
+            }
+        }
+    }
+
+    private void clearViews() {
+        if (mButtonDispatchers != null) {
+            for (int i = 0; i < mButtonDispatchers.size(); i++) {
+                mButtonDispatchers.valueAt(i).clear();
+            }
+        }
+        clearAllChildren(mHorizontal.findViewById(
+                com.android.internal.R.id.input_method_nav_buttons));
+    }
+
+    private void clearAllChildren(ViewGroup group) {
+        for (int i = 0; i < group.getChildCount(); i++) {
+            ((ViewGroup) group.getChildAt(i)).removeAllViews();
+        }
+    }
+
+    private static float convertDpToPx(Context context, float dp) {
+        return dp * context.getResources().getDisplayMetrics().density;
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java
new file mode 100644
index 0000000..c6096d7
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import android.content.res.Resources;
+import android.util.TypedValue;
+
+final class NavigationBarUtils {
+    private NavigationBarUtils() {
+        // Not intended to be instantiated.
+    }
+
+    /**
+     * A utility method to convert "dp" to "pixel".
+     *
+     * <p>TODO(b/215443343): Remove this method by migrating DP values from
+     * {@link NavigationBarConstants} to resource files.</p>
+     *
+     * @param dpValue "dp" value to be converted to "pixel"
+     * @param res {@link Resources} to be used when dealing with "dp".
+     * @return the pixels for a given dp value.
+     */
+    static int dpToPx(float dpValue, Resources res) {
+        return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, res.getDisplayMetrics());
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
new file mode 100644
index 0000000..4284778
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.DARK_MODE_ICON_COLOR_SINGLE_TONE;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVBAR_BACK_BUTTON_IME_OFFSET;
+import static android.inputmethodservice.navigationbar.NavigationBarUtils.dpToPx;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.DrawableRes;
+import android.annotation.FloatRange;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
+
+import java.util.function.Consumer;
+
+/**
+ * @hide
+ */
+public final class NavigationBarView extends FrameLayout {
+    final static boolean DEBUG = false;
+    final static String TAG = "NavBarView";
+
+    // Copied from com.android.systemui.animation.Interpolators#FAST_OUT_SLOW_IN
+    private static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+    // The current view is always mHorizontal.
+    View mCurrentView = null;
+    private View mHorizontal;
+
+    private int mCurrentRotation = -1;
+
+    int mDisabledFlags = 0;
+    int mNavigationIconHints = StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+    private final int mNavBarMode = NAV_BAR_MODE_GESTURAL;
+
+    private KeyButtonDrawable mBackIcon;
+    private KeyButtonDrawable mImeSwitcherIcon;
+    private Context mLightContext;
+    private final int mLightIconColor;
+    private final int mDarkIconColor;
+
+    private final android.inputmethodservice.navigationbar.DeadZone mDeadZone;
+    private boolean mDeadZoneConsuming = false;
+
+    private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
+    private Configuration mConfiguration;
+    private Configuration mTmpLastConfiguration;
+
+    private NavigationBarInflaterView mNavigationInflaterView;
+
+    public NavigationBarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mLightContext = context;
+        mLightIconColor = LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
+        mDarkIconColor = DARK_MODE_ICON_COLOR_SINGLE_TONE;
+
+        mConfiguration = new Configuration();
+        mTmpLastConfiguration = new Configuration();
+        mConfiguration.updateFrom(context.getResources().getConfiguration());
+
+        mButtonDispatchers.put(com.android.internal.R.id.input_method_nav_back,
+                new ButtonDispatcher(com.android.internal.R.id.input_method_nav_back));
+        mButtonDispatchers.put(com.android.internal.R.id.input_method_nav_ime_switcher,
+                new ButtonDispatcher(com.android.internal.R.id.input_method_nav_ime_switcher));
+        mButtonDispatchers.put(com.android.internal.R.id.input_method_nav_home_handle,
+                new ButtonDispatcher(com.android.internal.R.id.input_method_nav_home_handle));
+
+        mDeadZone = new android.inputmethodservice.navigationbar.DeadZone(this);
+
+        getImeSwitchButton().setOnClickListener(view -> view.getContext()
+                .getSystemService(InputMethodManager.class).showInputMethodPicker());
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return shouldDeadZoneConsumeTouchEvents(event) || super.onInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        shouldDeadZoneConsumeTouchEvents(event);
+        return super.onTouchEvent(event);
+    }
+
+    private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            mDeadZoneConsuming = false;
+        }
+        if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                    mDeadZoneConsuming = true;
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    mDeadZoneConsuming = false;
+                    break;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public View getCurrentView() {
+        return mCurrentView;
+    }
+
+    /**
+     * Applies {@param consumer} to each of the nav bar views.
+     */
+    public void forEachView(Consumer<View> consumer) {
+        if (mHorizontal != null) {
+            consumer.accept(mHorizontal);
+        }
+    }
+
+    public ButtonDispatcher getBackButton() {
+        return mButtonDispatchers.get(com.android.internal.R.id.input_method_nav_back);
+    }
+
+    public ButtonDispatcher getImeSwitchButton() {
+        return mButtonDispatchers.get(com.android.internal.R.id.input_method_nav_ime_switcher);
+    }
+
+    public ButtonDispatcher getHomeHandle() {
+        return mButtonDispatchers.get(com.android.internal.R.id.input_method_nav_home_handle);
+    }
+
+    public SparseArray<ButtonDispatcher> getButtonDispatchers() {
+        return mButtonDispatchers;
+    }
+
+    private void reloadNavIcons() {
+        updateIcons(Configuration.EMPTY);
+    }
+
+    private void updateIcons(Configuration oldConfig) {
+        final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
+        final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
+        final boolean dirChange =
+                oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
+
+        if (densityChange || dirChange) {
+            mImeSwitcherIcon = getDrawable(com.android.internal.R.drawable.ic_ime_switcher);
+        }
+        if (orientationChange || densityChange || dirChange) {
+            mBackIcon = getBackDrawable();
+        }
+    }
+
+    public KeyButtonDrawable getBackDrawable() {
+        KeyButtonDrawable drawable = getDrawable(com.android.internal.R.drawable.ic_ime_nav_back);
+        orientBackButton(drawable);
+        return drawable;
+    }
+
+    /**
+     * @return whether this nav bar mode is edge to edge
+     */
+    public static boolean isGesturalMode(int mode) {
+        return mode == NAV_BAR_MODE_GESTURAL;
+    }
+
+    private void orientBackButton(KeyButtonDrawable drawable) {
+        final boolean useAltBack =
+                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
+        if (drawable.getRotation() == degrees) {
+            return;
+        }
+
+        if (isGesturalMode(mNavBarMode)) {
+            drawable.setRotation(degrees);
+            return;
+        }
+
+        // Animate the back button's rotation to the new degrees and only in portrait move up the
+        // back button to line up with the other buttons
+        float targetY = useAltBack
+                ? - dpToPx(NAVBAR_BACK_BUTTON_IME_OFFSET, getResources())
+                : 0;
+        ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
+                PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees),
+                PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY));
+        navBarAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+        navBarAnimator.setDuration(200);
+        navBarAnimator.start();
+    }
+
+    private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
+        return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
+                true /* hasShadow */, null /* ovalBackgroundColor */);
+    }
+
+    @Override
+    public void setLayoutDirection(int layoutDirection) {
+        reloadNavIcons();
+
+        super.setLayoutDirection(layoutDirection);
+    }
+
+    public void setNavigationIconHints(int hints) {
+        if (hints == mNavigationIconHints) return;
+        final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        final boolean oldBackAlt =
+                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        if (newBackAlt != oldBackAlt) {
+            //onImeVisibilityChanged(newBackAlt);
+        }
+
+        if (DEBUG) {
+            android.widget.Toast.makeText(getContext(), "Navigation icon hints = " + hints, 500)
+                    .show();
+        }
+        mNavigationIconHints = hints;
+        updateNavButtonIcons();
+    }
+
+    public void setDisabledFlags(int disabledFlags) {
+        if (mDisabledFlags == disabledFlags) return;
+
+        mDisabledFlags = disabledFlags;
+
+        updateNavButtonIcons();
+    }
+
+    public void updateNavButtonIcons() {
+        // We have to replace or restore the back and home button icons when exiting or entering
+        // carmode, respectively. Recents are not available in CarMode in nav bar so change
+        // to recent icon is not required.
+        KeyButtonDrawable backIcon = mBackIcon;
+        orientBackButton(backIcon);
+        getBackButton().setImageDrawable(backIcon);
+
+        getImeSwitchButton().setImageDrawable(mImeSwitcherIcon);
+
+        // Update IME button visibility, a11y and rotate button always overrides the appearance
+        final boolean imeSwitcherVisible =
+                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+        getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE);
+
+        getBackButton().setVisibility(View.VISIBLE);
+        getHomeHandle().setVisibility(View.INVISIBLE);
+
+        // We used to be reporting the touch regions via notifyActiveTouchRegions() here.
+        // TODO(b/215593010): Consider taking care of this in the Launcher side.
+    }
+
+    private Display getContextDisplay() {
+        return getContext().getDisplay();
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mNavigationInflaterView = findViewById(com.android.internal.R.id.input_method_nav_inflater);
+        mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
+
+        updateOrientationViews();
+        reloadNavIcons();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        mDeadZone.onDraw(canvas);
+        super.onDraw(canvas);
+    }
+
+    private void updateOrientationViews() {
+        mHorizontal = findViewById(com.android.internal.R.id.input_method_nav_horizontal);
+
+        updateCurrentView();
+    }
+
+    private void updateCurrentView() {
+        resetViews();
+        mCurrentView = mHorizontal;
+        mCurrentView.setVisibility(View.VISIBLE);
+        mCurrentRotation = getContextDisplay().getRotation();
+        mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90);
+        mNavigationInflaterView.updateButtonDispatchersCurrentView();
+    }
+
+    private void resetViews() {
+        mHorizontal.setVisibility(View.GONE);
+    }
+
+    public void reorient() {
+        updateCurrentView();
+
+        final android.inputmethodservice.navigationbar.NavigationBarFrame frame =
+                getRootView().findViewByPredicate(view -> view instanceof NavigationBarFrame);
+        frame.setDeadZone(mDeadZone);
+        mDeadZone.onConfigurationChanged(mCurrentRotation);
+
+        if (DEBUG) {
+            Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
+        }
+
+        // Resolve layout direction if not resolved since components changing layout direction such
+        // as changing languages will recreate this view and the direction will be resolved later
+        if (!isLayoutDirectionResolved()) {
+            resolveLayoutDirection();
+        }
+        updateNavButtonIcons();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mTmpLastConfiguration.updateFrom(mConfiguration);
+        final int changes = mConfiguration.updateFrom(newConfig);
+
+        updateIcons(mTmpLastConfiguration);
+        if (mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
+                || mTmpLastConfiguration.getLayoutDirection()
+                        != mConfiguration.getLayoutDirection()) {
+            // If car mode or density changes, we need to reset the icons.
+            updateNavButtonIcons();
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        // This needs to happen first as it can changed the enabled state which can affect whether
+        // the back button is visible
+        requestApplyInsets();
+        reorient();
+        updateNavButtonIcons();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        for (int i = 0; i < mButtonDispatchers.size(); ++i) {
+            mButtonDispatchers.valueAt(i).onDestroy();
+        }
+    }
+
+    public void setDarkIntensity(@FloatRange(from = 0.0f, to = 1.0f) float intensity) {
+        for (int i = 0; i < mButtonDispatchers.size(); ++i) {
+            mButtonDispatchers.valueAt(i).setDarkIntensity(intensity);
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationHandle.java b/core/java/android/inputmethodservice/navigationbar/NavigationHandle.java
new file mode 100644
index 0000000..273cafb
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationHandle.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * TODO(b/215443343): Remove this file, as IME actually doesn't use this.
+ *
+ * @hide
+ */
+public class NavigationHandle extends View implements ButtonInterface {
+
+    public NavigationHandle(Context context) {
+        this(context, null);
+    }
+
+    public NavigationHandle(Context context, AttributeSet attr) {
+        super(context, attr);
+        setFocusable(false);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        return false;
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+    }
+
+    @Override
+    public void setDarkIntensity(float intensity) {
+    }
+
+    @Override
+    public void setDelayTouchFeedback(boolean shouldDelay) {
+    }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java b/core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java
new file mode 100644
index 0000000..68163c3
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+
+/**
+ * Automatically reverses the order of children as they are added.
+ * Also reverse the width and height values of layout params
+ * @hide
+ */
+public class ReverseLinearLayout extends LinearLayout {
+
+    /** If true, the layout is reversed vs. a regular linear layout */
+    private boolean mIsLayoutReverse;
+
+    /** If true, the layout is opposite to it's natural reversity from the layout direction */
+    private boolean mIsAlternativeOrder;
+
+    public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        updateOrder();
+    }
+
+    @Override
+    public void addView(View child) {
+        reverseParams(child.getLayoutParams(), child, mIsLayoutReverse);
+        if (mIsLayoutReverse) {
+            super.addView(child, 0);
+        } else {
+            super.addView(child);
+        }
+    }
+
+    @Override
+    public void addView(View child, ViewGroup.LayoutParams params) {
+        reverseParams(params, child, mIsLayoutReverse);
+        if (mIsLayoutReverse) {
+            super.addView(child, 0, params);
+        } else {
+            super.addView(child, params);
+        }
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        updateOrder();
+    }
+
+    public void setAlternativeOrder(boolean alternative) {
+        mIsAlternativeOrder = alternative;
+        updateOrder();
+    }
+
+    /**
+     * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
+     * have to do it manually
+     */
+    private void updateOrder() {
+        boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder;
+
+        if (mIsLayoutReverse != isLayoutReverse) {
+            // reversity changed, swap the order of all views.
+            int childCount = getChildCount();
+            ArrayList<View> childList = new ArrayList<>(childCount);
+            for (int i = 0; i < childCount; i++) {
+                childList.add(getChildAt(i));
+            }
+            removeAllViews();
+            for (int i = childCount - 1; i >= 0; i--) {
+                final View child = childList.get(i);
+                super.addView(child);
+            }
+            mIsLayoutReverse = isLayoutReverse;
+        }
+    }
+
+    private static void reverseParams(ViewGroup.LayoutParams params, View child,
+            boolean isLayoutReverse) {
+        if (child instanceof Reversible) {
+            ((Reversible) child).reverse(isLayoutReverse);
+        }
+        if (child.getPaddingLeft() == child.getPaddingRight()
+                && child.getPaddingTop() == child.getPaddingBottom()) {
+            child.setPadding(child.getPaddingTop(), child.getPaddingLeft(),
+                    child.getPaddingTop(), child.getPaddingLeft());
+        }
+        if (params == null) {
+            return;
+        }
+        int width = params.width;
+        params.width = params.height;
+        params.height = width;
+    }
+
+    interface Reversible {
+        void reverse(boolean isLayoutReverse);
+    }
+
+    public static class ReverseRelativeLayout extends RelativeLayout implements Reversible {
+
+        public ReverseRelativeLayout(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void reverse(boolean isLayoutReverse) {
+            updateGravity(isLayoutReverse);
+            reverseGroup(this, isLayoutReverse);
+        }
+
+        private int mDefaultGravity = Gravity.NO_GRAVITY;
+        public void setDefaultGravity(int gravity) {
+            mDefaultGravity = gravity;
+        }
+
+        public void updateGravity(boolean isLayoutReverse) {
+            // Flip gravity if top of bottom is used
+            if (mDefaultGravity != Gravity.TOP && mDefaultGravity != Gravity.BOTTOM) return;
+
+            // Use the default (intended for 270 LTR and 90 RTL) unless layout is otherwise
+            int gravityToApply = mDefaultGravity;
+            if (isLayoutReverse) {
+                gravityToApply = mDefaultGravity == Gravity.TOP ? Gravity.BOTTOM : Gravity.TOP;
+            }
+
+            if (getGravity() != gravityToApply) setGravity(gravityToApply);
+        }
+    }
+
+    private static void reverseGroup(ViewGroup group, boolean isLayoutReverse) {
+        for (int i = 0; i < group.getChildCount(); i++) {
+            final View child = group.getChildAt(i);
+            reverseParams(child.getLayoutParams(), child, isLayoutReverse);
+
+            // Recursively reverse all children
+            if (child instanceof ViewGroup) {
+                reverseGroup((ViewGroup) child, isLayoutReverse);
+            }
+        }
+    }
+}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f50aa99..6284f56 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -69,6 +69,8 @@
 
     int getMultipathPreference(in Network network);
 
+    SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
+    void notifyStatsProviderWarningOrLimitReached();
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index fab692c..5554137 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -35,6 +35,7 @@
 import android.annotation.RequiresFeature;
 import android.content.pm.PackageManager;
 import android.security.Credentials;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.VpnProfile;
@@ -70,6 +71,7 @@
  *     Exchange, Version 2 (IKEv2)</a>
  */
 public final class Ikev2VpnProfile extends PlatformVpnProfile {
+    private static final String TAG = Ikev2VpnProfile.class.getSimpleName();
     /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
     public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
     /** Prefix for when a Private Key is stored directly in the profile @hide */
@@ -163,6 +165,10 @@
 
         // UnmodifiableList doesn't make a defensive copy by default.
         mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
+        if (excludeLocalRoutes && !isBypassable) {
+            throw new IllegalArgumentException(
+                    "Vpn should be byassable if excludeLocalRoutes is set");
+        }
 
         mIsBypassable = isBypassable;
         mIsMetered = isMetered;
@@ -520,7 +526,10 @@
                 throw new IllegalArgumentException("Invalid auth method set");
         }
 
-        builder.setExcludeLocalRoutes(profile.excludeLocalRoutes);
+        if (profile.excludeLocalRoutes && !profile.isBypassable) {
+            Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
+        }
+        builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable);
 
         return builder.build();
     }
@@ -907,9 +916,23 @@
         }
 
         /**
-         *  Sets whether the local traffic is exempted from the VPN.
+         * Sets whether the local traffic is exempted from the VPN.
          *
-         *  @hide TODO(184750836): unhide once the implementation is completed
+         * When this is set, the system will not use the VPN network when an app
+         * tries to send traffic for an IP address that is on a local network.
+         *
+         * Note that there are important security implications. In particular, the
+         * networks that the device connects to typically decides what IP addresses
+         * are part of the local network. This means that for VPNs setting this
+         * flag, it is possible for anybody to set up a public network in such a
+         * way that traffic to arbitrary IP addresses will bypass the VPN, including
+         * traffic to services like DNS. When using this API, please consider the
+         * security implications for your particular case.
+         *
+         * Note that because the local traffic will always bypass the VPN,
+         * it is not possible to set this flag on a non-bypassable VPN.
+         *
+         * @hide TODO(184750836): unhide once the implementation is completed
          */
         @NonNull
         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/core/java/android/net/InternalNetworkUpdateRequest.java
deleted file mode 100644
index 6f09383..0000000
--- a/core/java/android/net/InternalNetworkUpdateRequest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/** @hide */
-public final class InternalNetworkUpdateRequest implements Parcelable {
-    @NonNull
-    private final StaticIpConfiguration mIpConfig;
-    @Nullable
-    private final NetworkCapabilities mNetworkCapabilities;
-
-    @NonNull
-    public StaticIpConfiguration getIpConfig() {
-        return new StaticIpConfiguration(mIpConfig);
-    }
-
-    @NonNull
-    public NetworkCapabilities getNetworkCapabilities() {
-        return mNetworkCapabilities == null
-                ? null : new NetworkCapabilities(mNetworkCapabilities);
-    }
-
-    /** @hide */
-    public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
-            @Nullable final NetworkCapabilities networkCapabilities) {
-        Objects.requireNonNull(ipConfig);
-        mIpConfig = new StaticIpConfiguration(ipConfig);
-        if (null == networkCapabilities) {
-            mNetworkCapabilities = null;
-        } else {
-            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
-        }
-    }
-
-    private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
-        Objects.requireNonNull(source);
-        mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
-        mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
-    }
-
-    @Override
-    public String toString() {
-        return "InternalNetworkUpdateRequest{"
-                + "mIpConfig=" + mIpConfig
-                + ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o;
-
-        return Objects.equals(that.getIpConfig(), mIpConfig)
-                && Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mIpConfig, mNetworkCapabilities);
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        mIpConfig.writeToParcel(dest, flags);
-        mNetworkCapabilities.writeToParcel(dest, flags);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR =
-            new Parcelable.Creator<InternalNetworkUpdateRequest>() {
-                @Override
-                public InternalNetworkUpdateRequest[] newArray(int size) {
-                    return new InternalNetworkUpdateRequest[size];
-                }
-
-                @Override
-                public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
-                    return new InternalNetworkUpdateRequest(source);
-                }
-            };
-}
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index f4b427959f..596f431 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -339,7 +339,8 @@
 
         out.writeInt(template.getMatchRule());
         BackupUtils.writeString(out, template.getSubscriberIds().iterator().next());
-        BackupUtils.writeString(out, template.getWifiNetworkKey());
+        BackupUtils.writeString(out, template.getWifiNetworkKeys().isEmpty()
+                ? null : template.getWifiNetworkKeys().iterator().next());
         out.writeInt(template.getMeteredness());
 
         return baos.toByteArray();
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7ebb646..c936bfa 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -535,6 +535,46 @@
     }
 
     /**
+     * Get subscription plan for the given networkTemplate.
+     *
+     * @param template the networkTemplate to get the subscription plan for.
+     * @return the active {@link SubscriptionPlan} for the given template, or
+     *         {@code null} if not found.
+     * @hide
+     */
+    @Nullable
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+        try {
+            return mService.getSubscriptionPlan(template);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+     * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void notifyStatsProviderWarningOrLimitReached() {
+        try {
+            mService.notifyStatsProviderWarningOrLimitReached();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Resets network policy settings back to factory defaults.
      *
      * @hide
diff --git a/core/java/android/net/PrivateDnsConnectivityChecker.java b/core/java/android/net/PrivateDnsConnectivityChecker.java
index cfd458c..ac97b36 100644
--- a/core/java/android/net/PrivateDnsConnectivityChecker.java
+++ b/core/java/android/net/PrivateDnsConnectivityChecker.java
@@ -44,7 +44,7 @@
      */
     public static boolean canConnectToPrivateDnsServer(@NonNull String hostname) {
         final SocketFactory factory = SSLSocketFactory.getDefault();
-        TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_APP);
+        TrafficStats.setThreadStatsTagApp();
 
         try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
             socket.setSoTimeout(CONNECTION_TIMEOUT_MS);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 5c28553..3193826 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -96,6 +96,141 @@
      */
     public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
 
+    /**
+     * Action sent in the intent when an error occurred.
+     *
+     * @hide
+     */
+    public static final String ACTION_VPN_MANAGER_ERROR = "android.net.action.VPN_MANAGER_ERROR";
+
+    /**
+     * An IKE protocol error. Codes are the codes from IkeProtocolException, RFC 7296.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_IKE = "android.net.category.ERROR_IKE";
+
+    /**
+     * User deactivated the VPN, either by turning it off or selecting a different VPN provider.
+     * The error code is always 0.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_USER_DEACTIVATED =
+            "android.net.category.ERROR_USER_DEACTIVATED";
+
+    /**
+     * Network error. Error codes are ERROR_CODE_NETWORK_*.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_NETWORK = "android.net.category.ERROR_NETWORK";
+
+    /**
+     * The key of the session that experienced this error, as returned by
+     * startProvisionedVpnProfileSession.
+     *
+     * @hide
+     */
+    public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY";
+
+    /**
+     * Extra for the Network object that was the underlying network at the time of the failure, or
+     * null if none.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
+
+    /**
+     * The NetworkCapabilities of the underlying network.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES =
+            "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
+
+    /**
+     * The LinkProperties of the underlying network.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_LINK_PROPERTIES =
+            "android.net.extra.UNDERLYING_LINK_PROPERTIES";
+
+    /**
+     * A long timestamp with SystemClock.elapsedRealtime base for when the event happened.
+     *
+     * @hide
+     */
+    public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+
+    /**
+     * Extra for the error type. This is ERROR_NOT_RECOVERABLE or ERROR_RECOVERABLE.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR_TYPE = "android.net.extra.ERROR_TYPE";
+
+    /**
+     * Extra for the error code. The value will be 0 for CATEGORY_ERROR_USER_DEACTIVATED, one of
+     * ERROR_CODE_NETWORK_* for ERROR_CATEGORY_NETWORK or one of values defined in
+     * IkeProtocolException#ErrorType for CATEGORY_ERROR_IKE.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE";
+
+    /**
+     * This error is fatal, e.g. the VPN was disabled or configuration error. The stack will not
+     * retry connection.
+     *
+     * @hide
+     */
+    public static final int ERROR_NOT_RECOVERABLE = 1;
+
+    /**
+     * The stack experienced an error but will retry with exponential backoff, e.g. network timeout.
+     *
+     * @hide
+     */
+    public static final int ERROR_RECOVERABLE = 2;
+
+    /**
+     * An error code to indicate that there was an UnknownHostException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_UNKNOWN_HOST = 0;
+
+    /**
+     * An error code to indicate that there is a SocketTimeoutException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_TIMEOUT = 1;
+
+    /**
+     * An error code to indicate that the connection is refused.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_CONNECT = 2;
+
+    /**
+     * An error code to indicate the connection was reset. (e.g. SocketException)
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_CONNECTION_RESET = 3;
+
+    /**
+     * An error code to indicate that there is an IOException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_IO = 4;
+
     /** @hide */
     @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
             TYPE_VPN_OEM})
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2ced056..1ae1b050d 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -41,6 +41,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.NetworkUtilsInternal;
 import com.android.internal.net.VpnConfig;
 
@@ -50,6 +51,7 @@
 import java.net.InetAddress;
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -471,6 +473,13 @@
         }
     }
 
+    private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) {
+        final IpPrefix prefix = new IpPrefix(address, prefixLength);
+        if (!prefix.getAddress().equals(address)) {
+            throw new IllegalArgumentException("Bad address");
+        }
+    }
+
     /**
      * Helper class to create a VPN interface. This class should be always
      * used within the scope of the outer {@link VpnService}.
@@ -481,9 +490,9 @@
 
         private final VpnConfig mConfig = new VpnConfig();
         @UnsupportedAppUsage
-        private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+        private final List<LinkAddress> mAddresses = new ArrayList<>();
         @UnsupportedAppUsage
-        private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+        private final List<RouteInfo> mRoutes = new ArrayList<>();
 
         public Builder() {
             mConfig.user = VpnService.this.getClass().getName();
@@ -555,7 +564,6 @@
                 throw new IllegalArgumentException("Bad address");
             }
             mAddresses.add(new LinkAddress(address, prefixLength));
-            mConfig.updateAllowedFamilies(address);
             return this;
         }
 
@@ -579,28 +587,68 @@
          * Add a network route to the VPN interface. Both IPv4 and IPv6
          * routes are supported.
          *
+         * If a route with the same destination is already present, its type will be updated.
+         *
+         * @throws IllegalArgumentException if the route is invalid.
+         */
+        @NonNull
+        private Builder addRoute(@NonNull IpPrefix prefix, int type) {
+            check(prefix.getAddress(), prefix.getPrefixLength());
+
+            final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */
+                    null, /* interface */ null, type);
+
+            final int index = findRouteIndexByDestination(newRoute);
+
+            if (index == -1) {
+                mRoutes.add(newRoute);
+            } else {
+                mRoutes.set(index, newRoute);
+            }
+
+            return this;
+        }
+
+        /**
+         * Add a network route to the VPN interface. Both IPv4 and IPv6
+         * routes are supported.
+         *
          * Adding a route implicitly allows traffic from that address family
          * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
          *
+         * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
          * @throws IllegalArgumentException if the route is invalid.
          */
         @NonNull
         public Builder addRoute(@NonNull InetAddress address, int prefixLength) {
-            check(address, prefixLength);
+            checkNonPrefixBytes(address, prefixLength);
 
-            int offset = prefixLength / 8;
-            byte[] bytes = address.getAddress();
-            if (offset < bytes.length) {
-                for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
-                    if (bytes[offset] != 0) {
-                        throw new IllegalArgumentException("Bad address");
-                    }
-                }
-            }
-            mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null,
-                RouteInfo.RTN_UNICAST));
-            mConfig.updateAllowedFamilies(address);
-            return this;
+            return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST);
+        }
+
+        /**
+         * Add a network route to the VPN interface. Both IPv4 and IPv6
+         * routes are supported.
+         *
+         * Adding a route implicitly allows traffic from that address family
+         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+         *
+         * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
+         * @throws IllegalArgumentException if the route is invalid.
+         */
+        @NonNull
+        public Builder addRoute(@NonNull IpPrefix prefix) {
+            return addRoute(prefix, RouteInfo.RTN_UNICAST);
         }
 
         /**
@@ -611,6 +659,12 @@
          * Adding a route implicitly allows traffic from that address family
          * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
          *
+         * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
          * @throws IllegalArgumentException if the route is invalid.
          * @see #addRoute(InetAddress, int)
          */
@@ -620,6 +674,23 @@
         }
 
         /**
+         * Exclude a network route from the VPN interface. Both IPv4 and IPv6
+         * routes are supported.
+         *
+         * Calling this method overrides previous calls to {@link #addRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
+         * @throws IllegalArgumentException if the route is invalid.
+         */
+        @NonNull
+        public Builder excludeRoute(@NonNull IpPrefix prefix) {
+            return addRoute(prefix, RouteInfo.RTN_THROW);
+        }
+
+        /**
          * Add a DNS server to the VPN connection. Both IPv4 and IPv6
          * addresses are supported. If none is set, the DNS servers of
          * the default network will be used.
@@ -900,5 +971,23 @@
                 throw new IllegalStateException(e);
             }
         }
+
+        private int findRouteIndexByDestination(RouteInfo route) {
+            for (int i = 0; i < mRoutes.size(); i++) {
+                if (mRoutes.get(i).getDestination().equals(route.getDestination())) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Method for testing, to observe mRoutes while builder is being used.
+         * @hide
+         */
+        @VisibleForTesting
+        public List<RouteInfo> routes() {
+            return Collections.unmodifiableList(mRoutes);
+        }
     }
 }
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
index febd9b4..3f7521a 100644
--- a/core/java/android/net/annotations/PolicyDirection.java
+++ b/core/java/android/net/annotations/PolicyDirection.java
@@ -24,10 +24,6 @@
 
 /**
  * IPsec traffic direction.
- *
- * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
- * to allow others to statically include it.
- *
  * @hide
  */
 @IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
deleted file mode 100644
index b3f7345..0000000
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.vcn;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility;
-import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.PersistableBundle;
-import android.telephony.SubscriptionInfo;
-import android.telephony.TelephonyManager;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.vcn.util.PersistableBundleUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Objects;
-import java.util.Set;
-
-// TODO: Add documents
-/** @hide */
-public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
-    private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
-    @NonNull private final Set<String> mAllowedNetworkPlmnIds;
-    private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
-    @NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
-
-    private static final String ALLOW_ROAMING_KEY = "mAllowRoaming";
-    private final boolean mAllowRoaming;
-
-    private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
-    private final boolean mRequireOpportunistic;
-
-    private VcnCellUnderlyingNetworkPriority(
-            int networkQuality,
-            boolean allowMetered,
-            Set<String> allowedNetworkPlmnIds,
-            Set<Integer> allowedSpecificCarrierIds,
-            boolean allowRoaming,
-            boolean requireOpportunistic) {
-        super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered);
-        mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
-        mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
-        mAllowRoaming = allowRoaming;
-        mRequireOpportunistic = requireOpportunistic;
-
-        validate();
-    }
-
-    /** @hide */
-    @Override
-    protected void validate() {
-        super.validate();
-        validatePlmnIds(mAllowedNetworkPlmnIds);
-        Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null");
-    }
-
-    private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) {
-        Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null");
-
-        // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal
-        // digits.
-        for (String id : allowedNetworkPlmnIds) {
-            if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) {
-                continue;
-            } else {
-                throw new IllegalArgumentException("Found invalid PLMN ID: " + id);
-            }
-        }
-    }
-
-    /** @hide */
-    @NonNull
-    @VisibleForTesting(visibility = Visibility.PROTECTED)
-    public static VcnCellUnderlyingNetworkPriority fromPersistableBundle(
-            @NonNull PersistableBundle in) {
-        Objects.requireNonNull(in, "PersistableBundle is null");
-
-        final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
-        final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
-
-        final PersistableBundle plmnIdsBundle =
-                in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
-        Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null");
-        final Set<String> allowedNetworkPlmnIds =
-                new ArraySet<String>(
-                        PersistableBundleUtils.toList(plmnIdsBundle, STRING_DESERIALIZER));
-
-        final PersistableBundle specificCarrierIdsBundle =
-                in.getPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY);
-        Objects.requireNonNull(specificCarrierIdsBundle, "specificCarrierIdsBundle is null");
-        final Set<Integer> allowedSpecificCarrierIds =
-                new ArraySet<Integer>(
-                        PersistableBundleUtils.toList(
-                                specificCarrierIdsBundle, INTEGER_DESERIALIZER));
-
-        final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
-        final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
-
-        return new VcnCellUnderlyingNetworkPriority(
-                networkQuality,
-                allowMetered,
-                allowedNetworkPlmnIds,
-                allowedSpecificCarrierIds,
-                allowRoaming,
-                requireOpportunistic);
-    }
-
-    /** @hide */
-    @Override
-    @NonNull
-    @VisibleForTesting(visibility = Visibility.PROTECTED)
-    public PersistableBundle toPersistableBundle() {
-        final PersistableBundle result = super.toPersistableBundle();
-
-        final PersistableBundle plmnIdsBundle =
-                PersistableBundleUtils.fromList(
-                        new ArrayList<>(mAllowedNetworkPlmnIds), STRING_SERIALIZER);
-        result.putPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY, plmnIdsBundle);
-
-        final PersistableBundle specificCarrierIdsBundle =
-                PersistableBundleUtils.fromList(
-                        new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
-        result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
-
-        result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming);
-        result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic);
-
-        return result;
-    }
-
-    /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
-    @NonNull
-    public Set<String> getAllowedOperatorPlmnIds() {
-        return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
-    }
-
-    /**
-     * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is
-     * acceptable.
-     */
-    @NonNull
-    public Set<Integer> getAllowedSpecificCarrierIds() {
-        return Collections.unmodifiableSet(mAllowedSpecificCarrierIds);
-    }
-
-    /** Return if roaming is allowed. */
-    public boolean allowRoaming() {
-        return mAllowRoaming;
-    }
-
-    /** Return if requiring an opportunistic network. */
-    public boolean requireOpportunistic() {
-        return mRequireOpportunistic;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(
-                super.hashCode(),
-                mAllowedNetworkPlmnIds,
-                mAllowedSpecificCarrierIds,
-                mAllowRoaming,
-                mRequireOpportunistic);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        if (!super.equals(other)) {
-            return false;
-        }
-
-        if (!(other instanceof VcnCellUnderlyingNetworkPriority)) {
-            return false;
-        }
-
-        final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other;
-        return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
-                && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
-                && mAllowRoaming == rhs.mAllowRoaming
-                && mRequireOpportunistic == rhs.mRequireOpportunistic;
-    }
-
-    /** @hide */
-    @Override
-    void dumpTransportSpecificFields(IndentingPrintWriter pw) {
-        pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
-        pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
-        pw.println("mAllowRoaming: " + mAllowRoaming);
-        pw.println("mRequireOpportunistic: " + mRequireOpportunistic);
-    }
-
-    /** This class is used to incrementally build WifiNetworkPriority objects. */
-    public static final class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
-        @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
-        @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
-
-        private boolean mAllowRoaming = false;
-        private boolean mRequireOpportunistic = false;
-
-        /** Construct a Builder object. */
-        public Builder() {}
-
-        /**
-         * Set allowed operator PLMN IDs.
-         *
-         * <p>This is used to distinguish cases where roaming agreements may dictate a different
-         * priority from a partner's networks.
-         *
-         * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an
-         *     empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and
-         *     thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()}
-         *     and {@link SubscriptionInfo#getMncString()}.
-         */
-        @NonNull
-        public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
-            validatePlmnIds(allowedNetworkPlmnIds);
-
-            mAllowedNetworkPlmnIds.clear();
-            mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds);
-            return this;
-        }
-
-        /**
-         * Set allowed specific carrier IDs.
-         *
-         * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty
-         *     set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}.
-         */
-        @NonNull
-        public Builder setAllowedSpecificCarrierIds(
-                @NonNull Set<Integer> allowedSpecificCarrierIds) {
-            Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null");
-            mAllowedSpecificCarrierIds.clear();
-            mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds);
-            return this;
-        }
-
-        /**
-         * Set if roaming is allowed.
-         *
-         * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code
-         *     false}.
-         */
-        @NonNull
-        public Builder setAllowRoaming(boolean allowRoaming) {
-            mAllowRoaming = allowRoaming;
-            return this;
-        }
-
-        /**
-         * Set if requiring an opportunistic network.
-         *
-         * @param requireOpportunistic the flag to indicate if caller requires an opportunistic
-         *     network. Defaults to {@code false}.
-         */
-        @NonNull
-        public Builder setRequireOpportunistic(boolean requireOpportunistic) {
-            mRequireOpportunistic = requireOpportunistic;
-            return this;
-        }
-
-        /** Build the VcnCellUnderlyingNetworkPriority. */
-        @NonNull
-        public VcnCellUnderlyingNetworkPriority build() {
-            return new VcnCellUnderlyingNetworkPriority(
-                    mNetworkQuality,
-                    mAllowMetered,
-                    mAllowedNetworkPlmnIds,
-                    mAllowedSpecificCarrierIds,
-                    mAllowRoaming,
-                    mRequireOpportunistic);
-        }
-
-        /** @hide */
-        @Override
-        Builder self() {
-            return this;
-        }
-    }
-}
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
new file mode 100644
index 0000000..69e6313
--- /dev/null
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
+import android.os.PersistableBundle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class represents a configuration for a network template class of underlying cellular
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
+public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
+    private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
+    @NonNull private final Set<String> mAllowedNetworkPlmnIds;
+    private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
+    @NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
+
+    private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria";
+    private final int mRoamingMatchCriteria;
+
+    private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria";
+    private final int mOpportunisticMatchCriteria;
+
+    private VcnCellUnderlyingNetworkTemplate(
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps,
+            Set<String> allowedNetworkPlmnIds,
+            Set<Integer> allowedSpecificCarrierIds,
+            int roamingMatchCriteria,
+            int opportunisticMatchCriteria) {
+        super(
+                NETWORK_PRIORITY_TYPE_CELL,
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps);
+        mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
+        mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
+        mRoamingMatchCriteria = roamingMatchCriteria;
+        mOpportunisticMatchCriteria = opportunisticMatchCriteria;
+
+        validate();
+    }
+
+    /** @hide */
+    @Override
+    protected void validate() {
+        super.validate();
+        validatePlmnIds(mAllowedNetworkPlmnIds);
+        Objects.requireNonNull(mAllowedSpecificCarrierIds, "matchingCarrierIds is null");
+        validateMatchCriteria(mRoamingMatchCriteria, "mRoamingMatchCriteria");
+        validateMatchCriteria(mOpportunisticMatchCriteria, "mOpportunisticMatchCriteria");
+    }
+
+    private static void validatePlmnIds(Set<String> matchingOperatorPlmnIds) {
+        Objects.requireNonNull(matchingOperatorPlmnIds, "matchingOperatorPlmnIds is null");
+
+        // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal
+        // digits.
+        for (String id : matchingOperatorPlmnIds) {
+            if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) {
+                continue;
+            } else {
+                throw new IllegalArgumentException("Found invalid PLMN ID: " + id);
+            }
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    public static VcnCellUnderlyingNetworkTemplate fromPersistableBundle(
+            @NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle is null");
+
+        final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+
+        final int minEntryUpstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitUpstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minEntryDownstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitDownstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
+        final PersistableBundle plmnIdsBundle =
+                in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
+        Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null");
+        final Set<String> allowedNetworkPlmnIds =
+                new ArraySet<String>(
+                        PersistableBundleUtils.toList(plmnIdsBundle, STRING_DESERIALIZER));
+
+        final PersistableBundle specificCarrierIdsBundle =
+                in.getPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY);
+        Objects.requireNonNull(specificCarrierIdsBundle, "specificCarrierIdsBundle is null");
+        final Set<Integer> allowedSpecificCarrierIds =
+                new ArraySet<Integer>(
+                        PersistableBundleUtils.toList(
+                                specificCarrierIdsBundle, INTEGER_DESERIALIZER));
+
+        final int roamingMatchCriteria = in.getInt(ROAMING_MATCH_KEY);
+        final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
+
+        return new VcnCellUnderlyingNetworkTemplate(
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps,
+                allowedNetworkPlmnIds,
+                allowedSpecificCarrierIds,
+                roamingMatchCriteria,
+                opportunisticMatchCriteria);
+    }
+
+    /** @hide */
+    @Override
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    public PersistableBundle toPersistableBundle() {
+        final PersistableBundle result = super.toPersistableBundle();
+
+        final PersistableBundle plmnIdsBundle =
+                PersistableBundleUtils.fromList(
+                        new ArrayList<>(mAllowedNetworkPlmnIds), STRING_SERIALIZER);
+        result.putPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY, plmnIdsBundle);
+
+        final PersistableBundle specificCarrierIdsBundle =
+                PersistableBundleUtils.fromList(
+                        new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
+        result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
+
+        result.putInt(ROAMING_MATCH_KEY, mRoamingMatchCriteria);
+        result.putInt(OPPORTUNISTIC_MATCH_KEY, mOpportunisticMatchCriteria);
+
+        return result;
+    }
+
+    /**
+     * Retrieve the matching operator PLMN IDs, or an empty set if any PLMN ID is acceptable.
+     *
+     * @see Builder#setOperatorPlmnIds(Set)
+     */
+    @NonNull
+    public Set<String> getOperatorPlmnIds() {
+        return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
+    }
+
+    /**
+     * Retrieve the matching sim specific carrier IDs, or an empty set if any sim specific carrier
+     * ID is acceptable.
+     *
+     * @see Builder#setSimSpecificCarrierIds(Set)
+     */
+    @NonNull
+    public Set<Integer> getSimSpecificCarrierIds() {
+        return Collections.unmodifiableSet(mAllowedSpecificCarrierIds);
+    }
+
+    /**
+     * Return the matching criteria for roaming networks.
+     *
+     * @see Builder#setRoaming(int)
+     */
+    @MatchCriteria
+    public int getRoaming() {
+        return mRoamingMatchCriteria;
+    }
+
+    /**
+     * Return the matching criteria for opportunistic cellular subscriptions.
+     *
+     * @see Builder#setOpportunistic(int)
+     */
+    @MatchCriteria
+    public int getOpportunistic() {
+        return mOpportunisticMatchCriteria;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                super.hashCode(),
+                mAllowedNetworkPlmnIds,
+                mAllowedSpecificCarrierIds,
+                mRoamingMatchCriteria,
+                mOpportunisticMatchCriteria);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+
+        if (!(other instanceof VcnCellUnderlyingNetworkTemplate)) {
+            return false;
+        }
+
+        final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
+        return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
+                && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
+                && mRoamingMatchCriteria == rhs.mRoamingMatchCriteria
+                && mOpportunisticMatchCriteria == rhs.mOpportunisticMatchCriteria;
+    }
+
+    /** @hide */
+    @Override
+    void dumpTransportSpecificFields(IndentingPrintWriter pw) {
+        pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
+        pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
+        pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
+        pw.println(
+                "mOpportunisticMatchCriteria: "
+                        + getMatchCriteriaString(mOpportunisticMatchCriteria));
+    }
+
+    /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
+    public static final class Builder {
+        private int mMeteredMatchCriteria = MATCH_ANY;
+
+        @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
+        @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
+
+        private int mRoamingMatchCriteria = MATCH_ANY;
+        private int mOpportunisticMatchCriteria = MATCH_ANY;
+
+        private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
+        /** Construct a Builder object. */
+        public Builder() {}
+
+        /**
+         * Set the matching criteria for metered networks.
+         *
+         * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+         * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+         * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+         *
+         * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+         *     #MATCH_ANY}.
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+         */
+        // The matching getter is defined in the super class. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMetered()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setMetered(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setMetered");
+
+            mMeteredMatchCriteria = matchCriteria;
+            return this;
+        }
+
+        /**
+         * Set operator PLMN IDs with which a network can match this template.
+         *
+         * <p>This is used to distinguish cases where roaming agreements may dictate a different
+         * priority from a partner's networks.
+         *
+         * @param operatorPlmnIds the matching operator PLMN IDs in String. Network with one of the
+         *     matching PLMN IDs can match this template. If the set is empty, any PLMN ID will
+         *     match. The default is an empty set. A valid PLMN is a concatenation of MNC and MCC,
+         *     and thus consists of 5 or 6 decimal digits.
+         * @see SubscriptionInfo#getMccString()
+         * @see SubscriptionInfo#getMncString()
+         */
+        @NonNull
+        public Builder setOperatorPlmnIds(@NonNull Set<String> operatorPlmnIds) {
+            validatePlmnIds(operatorPlmnIds);
+
+            mAllowedNetworkPlmnIds.clear();
+            mAllowedNetworkPlmnIds.addAll(operatorPlmnIds);
+            return this;
+        }
+
+        /**
+         * Set sim specific carrier IDs with which a network can match this template.
+         *
+         * @param simSpecificCarrierIds the matching sim specific carrier IDs. Network with one of
+         *     the sim specific carrier IDs can match this template. If the set is empty, any
+         *     carrier ID will match. The default is an empty set.
+         * @see TelephonyManager#getSimSpecificCarrierId()
+         */
+        @NonNull
+        public Builder setSimSpecificCarrierIds(@NonNull Set<Integer> simSpecificCarrierIds) {
+            Objects.requireNonNull(simSpecificCarrierIds, "simSpecificCarrierIds is null");
+
+            mAllowedSpecificCarrierIds.clear();
+            mAllowedSpecificCarrierIds.addAll(simSpecificCarrierIds);
+            return this;
+        }
+
+        /**
+         * Set the matching criteria for roaming networks.
+         *
+         * <p>A template where setRoaming(MATCH_REQUIRED) will only match roaming networks (one
+         * without NET_CAPABILITY_NOT_ROAMING). A template where setRoaming(MATCH_FORBIDDEN) will
+         * only match a network that is not roaming (one with NET_CAPABILITY_NOT_ROAMING).
+         *
+         * @param matchCriteria the matching criteria for roaming networks. Defaults to {@link
+         *     #MATCH_ANY}.
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING
+         */
+        @NonNull
+        public Builder setRoaming(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setRoaming");
+
+            mRoamingMatchCriteria = matchCriteria;
+            return this;
+        }
+
+        /**
+         * Set the matching criteria for opportunistic cellular subscriptions.
+         *
+         * @param matchCriteria the matching criteria for opportunistic cellular subscriptions.
+         *     Defaults to {@link #MATCH_ANY}.
+         * @see SubscriptionManager#setOpportunistic(boolean, int)
+         */
+        @NonNull
+        public Builder setOpportunistic(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setOpportunistic");
+
+            mOpportunisticMatchCriteria = matchCriteria;
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+         *     that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+         *     that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinUpstreamBandwidthKbps(
+                int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+            validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+            mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+            mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+         *     networks that ARE NOT the already-selected underlying network, or {@code 0} to
+         *     disable this requirement. Disabled by default.
+         * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+         *     network that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinDownstreamBandwidthKbps(
+                int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+            validateMinBandwidthKbps(
+                    minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+            mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+            mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /** Build the VcnCellUnderlyingNetworkTemplate. */
+        @NonNull
+        public VcnCellUnderlyingNetworkTemplate build() {
+            return new VcnCellUnderlyingNetworkTemplate(
+                    mMeteredMatchCriteria,
+                    mMinEntryUpstreamBandwidthKbps,
+                    mMinExitUpstreamBandwidthKbps,
+                    mMinEntryDownstreamBandwidthKbps,
+                    mMinExitDownstreamBandwidthKbps,
+                    mAllowedNetworkPlmnIds,
+                    mAllowedSpecificCarrierIds,
+                    mRoamingMatchCriteria,
+                    mOpportunisticMatchCriteria);
+        }
+    }
+}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 55d3ecd..a6830b7 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,7 +16,7 @@
 package android.net.vcn;
 
 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
@@ -42,7 +42,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
@@ -162,30 +162,21 @@
 
     /** @hide */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    public static final LinkedHashSet<VcnUnderlyingNetworkPriority>
-            DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
+    public static final List<VcnUnderlyingNetworkTemplate> DEFAULT_UNDERLYING_NETWORK_TEMPLATES =
+            new ArrayList<>();
 
     static {
-        DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
-                new VcnCellUnderlyingNetworkPriority.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
-                        .setAllowRoaming(true /* allowRoaming */)
-                        .setRequireOpportunistic(true /* requireOpportunistic */)
+        DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
+                new VcnCellUnderlyingNetworkTemplate.Builder()
+                        .setOpportunistic(MATCH_REQUIRED)
                         .build());
 
-        DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
-                new VcnWifiUnderlyingNetworkPriority.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
+        DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
                         .build());
 
-        DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
-                new VcnCellUnderlyingNetworkPriority.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
-                        .setAllowRoaming(true /* allowRoaming */)
-                        .setRequireOpportunistic(false /* requireOpportunistic */)
+        DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
+                new VcnCellUnderlyingNetworkTemplate.Builder()
                         .build());
     }
 
@@ -200,9 +191,9 @@
 
     /** @hide */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+    public static final String UNDERLYING_NETWORK_TEMPLATES_KEY = "mUnderlyingNetworkTemplates";
 
-    @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities;
+    @NonNull private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates;
 
     private static final String MAX_MTU_KEY = "mMaxMtu";
     private final int mMaxMtu;
@@ -215,7 +206,7 @@
             @NonNull String gatewayConnectionName,
             @NonNull IkeTunnelConnectionParams tunnelConnectionParams,
             @NonNull Set<Integer> exposedCapabilities,
-            @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             @NonNull long[] retryIntervalsMs,
             @IntRange(from = MIN_MTU_V6) int maxMtu) {
         mGatewayConnectionName = gatewayConnectionName;
@@ -224,9 +215,9 @@
         mRetryIntervalsMs = retryIntervalsMs;
         mMaxMtu = maxMtu;
 
-        mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities);
-        if (mUnderlyingNetworkPriorities.isEmpty()) {
-            mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+        mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
+        if (mUnderlyingNetworkTemplates.isEmpty()) {
+            mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
         }
 
         validate();
@@ -250,22 +241,19 @@
         mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
                 exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
 
-        final PersistableBundle networkPrioritiesBundle =
-                in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
+        final PersistableBundle networkTemplatesBundle =
+                in.getPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY);
 
-        if (networkPrioritiesBundle == null) {
-            // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus
+        if (networkTemplatesBundle == null) {
+            // UNDERLYING_NETWORK_TEMPLATES_KEY was added in Android T. Thus
             // VcnGatewayConnectionConfig created on old platforms will not have this data and will
             // be assigned with the default value
-            mUnderlyingNetworkPriorities =
-                    new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
-
+            mUnderlyingNetworkTemplates = new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
         } else {
-            mUnderlyingNetworkPriorities =
-                    new LinkedHashSet<>(
-                            PersistableBundleUtils.toList(
-                                    networkPrioritiesBundle,
-                                    VcnUnderlyingNetworkPriority::fromPersistableBundle));
+            mUnderlyingNetworkTemplates =
+                    PersistableBundleUtils.toList(
+                            networkTemplatesBundle,
+                            VcnUnderlyingNetworkTemplate::fromPersistableBundle);
         }
 
         mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
@@ -285,7 +273,7 @@
             checkValidCapability(cap);
         }
 
-        Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+        validateNetworkTemplateList(mUnderlyingNetworkTemplates);
         Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
         validateRetryInterval(mRetryIntervalsMs);
 
@@ -314,6 +302,19 @@
         }
     }
 
+    private static void validateNetworkTemplateList(
+            List<VcnUnderlyingNetworkTemplate> networkPriorityRules) {
+        Objects.requireNonNull(networkPriorityRules, "networkPriorityRules is null");
+
+        Set<VcnUnderlyingNetworkTemplate> existingRules = new ArraySet<>();
+        for (VcnUnderlyingNetworkTemplate rule : networkPriorityRules) {
+            Objects.requireNonNull(rule, "Found null value VcnUnderlyingNetworkTemplate");
+            if (!existingRules.add(rule)) {
+                throw new IllegalArgumentException("Found duplicate VcnUnderlyingNetworkTemplate");
+            }
+        }
+    }
+
     /**
      * Returns the configured Gateway Connection name.
      *
@@ -368,15 +369,13 @@
     }
 
     /**
-     * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not
-     * configured.
+     * Retrieve the VcnUnderlyingNetworkTemplate list, or a default list if it is not configured.
      *
-     * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>)
-     * @hide
+     * @see Builder#setVcnUnderlyingNetworkPriorities(List)
      */
     @NonNull
-    public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() {
-        return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
+    public List<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
+        return new ArrayList<>(mUnderlyingNetworkTemplates);
     }
 
     /**
@@ -415,15 +414,15 @@
                 PersistableBundleUtils.fromList(
                         new ArrayList<>(mExposedCapabilities),
                         PersistableBundleUtils.INTEGER_SERIALIZER);
-        final PersistableBundle networkPrioritiesBundle =
+        final PersistableBundle networkTemplatesBundle =
                 PersistableBundleUtils.fromList(
-                        new ArrayList<>(mUnderlyingNetworkPriorities),
-                        VcnUnderlyingNetworkPriority::toPersistableBundle);
+                        mUnderlyingNetworkTemplates,
+                        VcnUnderlyingNetworkTemplate::toPersistableBundle);
 
         result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
         result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
         result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
-        result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle);
+        result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle);
         result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
         result.putInt(MAX_MTU_KEY, mMaxMtu);
 
@@ -436,7 +435,7 @@
                 mGatewayConnectionName,
                 mTunnelConnectionParams,
                 mExposedCapabilities,
-                mUnderlyingNetworkPriorities,
+                mUnderlyingNetworkTemplates,
                 Arrays.hashCode(mRetryIntervalsMs),
                 mMaxMtu);
     }
@@ -451,7 +450,7 @@
         return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
                 && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
                 && mExposedCapabilities.equals(rhs.mExposedCapabilities)
-                && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities)
+                && mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
                 && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
                 && mMaxMtu == rhs.mMaxMtu;
     }
@@ -465,8 +464,8 @@
         @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
 
         @NonNull
-        private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities =
-                new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+        private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates =
+                new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
 
         @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
         private int mMaxMtu = DEFAULT_MAX_MTU;
@@ -539,27 +538,37 @@
         }
 
         /**
-         * Set the VcnUnderlyingNetworkPriority list.
+         * Set the list of templates to match underlying networks against, in high-to-low priority
+         * order.
          *
-         * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
-         *     are ordered from most to least preferred, or an empty list to use the default
-         *     prioritization. The default network prioritization is Opportunistic cellular, Carrier
-         *     WiFi and Macro cellular
-         * @return
+         * <p>To select the VCN underlying network, the VCN connection will go through all the
+         * network candidates and return a network matching the highest priority rule.
+         *
+         * <p>If multiple networks match the same rule, the VCN will prefer an already-selected
+         * network as opposed to a new/unselected network. However, if both are new/unselected
+         * networks, a network will be chosen arbitrarily amongst the networks matching the highest
+         * priority rule.
+         *
+         * <p>If all networks fail to match the rules provided, an underlying network will still be
+         * selected (at random if necessary).
+         *
+         * @param underlyingNetworkTemplates a list of unique VcnUnderlyingNetworkTemplates that are
+         *     ordered from most to least preferred, or an empty list to use the default
+         *     prioritization. The default network prioritization order is Opportunistic cellular,
+         *     Carrier WiFi and then Macro cellular.
+         * @return this {@link Builder} instance, for chaining
          */
-        /** @hide */
         @NonNull
         public Builder setVcnUnderlyingNetworkPriorities(
-                @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) {
-            Objects.requireNonNull(
-                    mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+                @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+            validateNetworkTemplateList(underlyingNetworkTemplates);
 
-            mUnderlyingNetworkPriorities.clear();
+            mUnderlyingNetworkTemplates.clear();
 
-            if (underlyingNetworkPriorities.isEmpty()) {
-                mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+            if (underlyingNetworkTemplates.isEmpty()) {
+                mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
             } else {
-                mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities);
+                mUnderlyingNetworkTemplates.addAll(underlyingNetworkTemplates);
             }
 
             return this;
@@ -629,7 +638,7 @@
                     mGatewayConnectionName,
                     mTunnelConnectionParams,
                     mExposedCapabilities,
-                    mUnderlyingNetworkPriorities,
+                    mUnderlyingNetworkTemplates,
                     mRetryIntervalsMs,
                     mMaxMtu);
         }
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
deleted file mode 100644
index 551f757..0000000
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.vcn;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.PersistableBundle;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-// TODO: Add documents
-/** @hide */
-public abstract class VcnUnderlyingNetworkPriority {
-    /** @hide */
-    protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
-    /** @hide */
-    protected static final int NETWORK_PRIORITY_TYPE_CELL = 2;
-
-    /** Denotes that any network quality is acceptable */
-    public static final int NETWORK_QUALITY_ANY = 0;
-    /** Denotes that network quality needs to be OK */
-    public static final int NETWORK_QUALITY_OK = 100000;
-
-    private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>();
-
-    static {
-        NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_ANY, "NETWORK_QUALITY_ANY");
-        NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_OK, "NETWORK_QUALITY_OK");
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
-    public @interface NetworkQuality {}
-
-    private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType";
-    private final int mNetworkPriorityType;
-
-    /** @hide */
-    protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
-    private final int mNetworkQuality;
-
-    /** @hide */
-    protected static final String ALLOW_METERED_KEY = "mAllowMetered";
-    private final boolean mAllowMetered;
-
-    /** @hide */
-    protected VcnUnderlyingNetworkPriority(
-            int networkPriorityType, int networkQuality, boolean allowMetered) {
-        mNetworkPriorityType = networkPriorityType;
-        mNetworkQuality = networkQuality;
-        mAllowMetered = allowMetered;
-    }
-
-    private static void validateNetworkQuality(int networkQuality) {
-        Preconditions.checkArgument(
-                networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
-                "Invalid networkQuality:" + networkQuality);
-    }
-
-    /** @hide */
-    protected void validate() {
-        validateNetworkQuality(mNetworkQuality);
-    }
-
-    /** @hide */
-    @NonNull
-    @VisibleForTesting(visibility = Visibility.PROTECTED)
-    public static VcnUnderlyingNetworkPriority fromPersistableBundle(
-            @NonNull PersistableBundle in) {
-        Objects.requireNonNull(in, "PersistableBundle is null");
-
-        final int networkPriorityType = in.getInt(NETWORK_PRIORITY_TYPE_KEY);
-        switch (networkPriorityType) {
-            case NETWORK_PRIORITY_TYPE_WIFI:
-                return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in);
-            case NETWORK_PRIORITY_TYPE_CELL:
-                return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in);
-            default:
-                throw new IllegalArgumentException(
-                        "Invalid networkPriorityType:" + networkPriorityType);
-        }
-    }
-
-    /** @hide */
-    @NonNull
-    PersistableBundle toPersistableBundle() {
-        final PersistableBundle result = new PersistableBundle();
-
-        result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
-        result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
-        result.putBoolean(ALLOW_METERED_KEY, mAllowMetered);
-
-        return result;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        if (!(other instanceof VcnUnderlyingNetworkPriority)) {
-            return false;
-        }
-
-        final VcnUnderlyingNetworkPriority rhs = (VcnUnderlyingNetworkPriority) other;
-        return mNetworkPriorityType == rhs.mNetworkPriorityType
-                && mNetworkQuality == rhs.mNetworkQuality
-                && mAllowMetered == rhs.mAllowMetered;
-    }
-
-    /** @hide */
-    abstract void dumpTransportSpecificFields(IndentingPrintWriter pw);
-
-    /**
-     * Dumps the state of this record for logging and debugging purposes.
-     *
-     * @hide
-     */
-    public void dump(IndentingPrintWriter pw) {
-        pw.println(this.getClass().getSimpleName() + ":");
-        pw.increaseIndent();
-
-        pw.println(
-                "mNetworkQuality: "
-                        + NETWORK_QUALITY_TO_STRING_MAP.get(
-                                mNetworkQuality, "Invalid value " + mNetworkQuality));
-        pw.println("mAllowMetered: " + mAllowMetered);
-        dumpTransportSpecificFields(pw);
-
-        pw.decreaseIndent();
-    }
-
-    /** Retrieve the required network quality. */
-    @NetworkQuality
-    public int getNetworkQuality() {
-        return mNetworkQuality;
-    }
-
-    /** Return if a metered network is allowed. */
-    public boolean allowMetered() {
-        return mAllowMetered;
-    }
-
-    /**
-     * This class is used to incrementally build VcnUnderlyingNetworkPriority objects.
-     *
-     * @param <T> The subclass to be built.
-     */
-    public abstract static class Builder<T extends Builder<T>> {
-        /** @hide */
-        protected int mNetworkQuality = NETWORK_QUALITY_ANY;
-        /** @hide */
-        protected boolean mAllowMetered = false;
-
-        /** @hide */
-        protected Builder() {}
-
-        /**
-         * Set the required network quality.
-         *
-         * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
-         */
-        @NonNull
-        public T setNetworkQuality(@NetworkQuality int networkQuality) {
-            validateNetworkQuality(networkQuality);
-
-            mNetworkQuality = networkQuality;
-            return self();
-        }
-
-        /**
-         * Set if a metered network is allowed.
-         *
-         * @param allowMetered the flag to indicate if a metered network is allowed, defaults to
-         *     {@code false}
-         */
-        @NonNull
-        public T setAllowMetered(boolean allowMetered) {
-            mAllowMetered = allowMetered;
-            return self();
-        }
-
-        /** @hide */
-        abstract T self();
-    }
-}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
new file mode 100644
index 0000000..3a9ca3e
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * This class represents a template containing set of underlying network requirements for doing
+ * route selection.
+ *
+ * <p>Apps provisioning a VCN can configure the underlying network priority for each Gateway
+ * Connection by setting a list (in priority order, most to least preferred) of the appropriate
+ * subclasses in the VcnGatewayConnectionConfig. See {@link
+ * VcnGatewayConnectionConfig.Builder#setVcnUnderlyingNetworkPriorities}
+ */
+public abstract class VcnUnderlyingNetworkTemplate {
+    /** @hide */
+    static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
+    /** @hide */
+    static final int NETWORK_PRIORITY_TYPE_CELL = 2;
+
+    /**
+     * Used to configure the matching criteria of a network characteristic. This may include network
+     * capabilities, or cellular subscription information. Denotes that networks with or without the
+     * characteristic are both acceptable to match this template.
+     */
+    public static final int MATCH_ANY = 0;
+
+    /**
+     * Used to configure the matching criteria of a network characteristic. This may include network
+     * capabilities, or cellular subscription information. Denotes that a network MUST have the
+     * capability in order to match this template.
+     */
+    public static final int MATCH_REQUIRED = 1;
+
+    /**
+     * Used to configure the matching criteria of a network characteristic. This may include network
+     * capabilities, or cellular subscription information. Denotes that a network MUST NOT have the
+     * capability in order to match this template.
+     */
+    public static final int MATCH_FORBIDDEN = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({MATCH_ANY, MATCH_REQUIRED, MATCH_FORBIDDEN})
+    public @interface MatchCriteria {}
+
+    private static final SparseArray<String> MATCH_CRITERIA_TO_STRING_MAP = new SparseArray<>();
+
+    static {
+        MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_ANY, "MATCH_ANY");
+        MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_REQUIRED, "MATCH_REQUIRED");
+        MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_FORBIDDEN, "MATCH_FORBIDDEN");
+    }
+
+    private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType";
+    private final int mNetworkPriorityType;
+
+    /** @hide */
+    static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
+
+    private final int mMeteredMatchCriteria;
+
+    /** @hide */
+    public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0;
+
+    /** @hide */
+    static final String MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinEntryUpstreamBandwidthKbps";
+
+    private final int mMinEntryUpstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitUpstreamBandwidthKbps";
+
+    private final int mMinExitUpstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY =
+            "mMinEntryDownstreamBandwidthKbps";
+
+    private final int mMinEntryDownstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitDownstreamBandwidthKbps";
+
+    private final int mMinExitDownstreamBandwidthKbps;
+
+    /** @hide */
+    VcnUnderlyingNetworkTemplate(
+            int networkPriorityType,
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps) {
+        mNetworkPriorityType = networkPriorityType;
+        mMeteredMatchCriteria = meteredMatchCriteria;
+        mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+        mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+        mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+        mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+    }
+
+    /** @hide */
+    static void validateMatchCriteria(int matchCriteria, String matchingCapability) {
+        Preconditions.checkArgument(
+                MATCH_CRITERIA_TO_STRING_MAP.contains(matchCriteria),
+                "Invalid matching criteria: " + matchCriteria + " for " + matchingCapability);
+    }
+
+    /** @hide */
+    static void validateMinBandwidthKbps(int minEntryBandwidth, int minExitBandwidth) {
+        Preconditions.checkArgument(
+                minEntryBandwidth >= 0, "Invalid minEntryBandwidth, must be >= 0");
+        Preconditions.checkArgument(
+                minExitBandwidth >= 0, "Invalid minExitBandwidth, must be >= 0");
+        Preconditions.checkArgument(
+                minEntryBandwidth >= minExitBandwidth,
+                "Minimum entry bandwidth must be >= exit bandwidth");
+    }
+
+    /** @hide */
+    protected void validate() {
+        validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
+        validateMinBandwidthKbps(mMinEntryUpstreamBandwidthKbps, mMinExitUpstreamBandwidthKbps);
+        validateMinBandwidthKbps(mMinEntryDownstreamBandwidthKbps, mMinExitDownstreamBandwidthKbps);
+    }
+
+    /** @hide */
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    public static VcnUnderlyingNetworkTemplate fromPersistableBundle(
+            @NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle is null");
+
+        final int networkPriorityType = in.getInt(NETWORK_PRIORITY_TYPE_KEY);
+        switch (networkPriorityType) {
+            case NETWORK_PRIORITY_TYPE_WIFI:
+                return VcnWifiUnderlyingNetworkTemplate.fromPersistableBundle(in);
+            case NETWORK_PRIORITY_TYPE_CELL:
+                return VcnCellUnderlyingNetworkTemplate.fromPersistableBundle(in);
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid networkPriorityType:" + networkPriorityType);
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    PersistableBundle toPersistableBundle() {
+        final PersistableBundle result = new PersistableBundle();
+
+        result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
+        result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
+        result.putInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryUpstreamBandwidthKbps);
+        result.putInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinExitUpstreamBandwidthKbps);
+        result.putInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryDownstreamBandwidthKbps);
+        result.putInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinExitDownstreamBandwidthKbps);
+
+        return result;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mNetworkPriorityType,
+                mMeteredMatchCriteria,
+                mMinEntryUpstreamBandwidthKbps,
+                mMinExitUpstreamBandwidthKbps,
+                mMinEntryDownstreamBandwidthKbps,
+                mMinExitDownstreamBandwidthKbps);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (!(other instanceof VcnUnderlyingNetworkTemplate)) {
+            return false;
+        }
+
+        final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
+        return mNetworkPriorityType == rhs.mNetworkPriorityType
+                && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria
+                && mMinEntryUpstreamBandwidthKbps == rhs.mMinEntryUpstreamBandwidthKbps
+                && mMinExitUpstreamBandwidthKbps == rhs.mMinExitUpstreamBandwidthKbps
+                && mMinEntryDownstreamBandwidthKbps == rhs.mMinEntryDownstreamBandwidthKbps
+                && mMinExitDownstreamBandwidthKbps == rhs.mMinExitDownstreamBandwidthKbps;
+    }
+
+    /** @hide */
+    static String getNameString(SparseArray<String> toStringMap, int key) {
+        return toStringMap.get(key, "Invalid value " + key);
+    }
+
+    /** @hide */
+    static String getMatchCriteriaString(int matchCriteria) {
+        return getNameString(MATCH_CRITERIA_TO_STRING_MAP, matchCriteria);
+    }
+
+    /** @hide */
+    abstract void dumpTransportSpecificFields(IndentingPrintWriter pw);
+
+    /**
+     * Dumps the state of this record for logging and debugging purposes.
+     *
+     * @hide
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println(this.getClass().getSimpleName() + ":");
+        pw.increaseIndent();
+
+        pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+        pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+        pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+        pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+        pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
+        dumpTransportSpecificFields(pw);
+
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Return the matching criteria for metered networks.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
+     */
+    public int getMetered() {
+        return mMeteredMatchCriteria;
+    }
+
+    /**
+     * Returns the minimum entry upstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     */
+    public int getMinEntryUpstreamBandwidthKbps() {
+        return mMinEntryUpstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum exit upstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     */
+    public int getMinExitUpstreamBandwidthKbps() {
+        return mMinExitUpstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum entry downstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     */
+    public int getMinEntryDownstreamBandwidthKbps() {
+        return mMinEntryDownstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum exit downstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     */
+    public int getMinExitDownstreamBandwidthKbps() {
+        return mMinExitDownstreamBandwidthKbps;
+    }
+}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
deleted file mode 100644
index 85eb100..0000000
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.vcn;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.PersistableBundle;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.util.Objects;
-
-// TODO: Add documents
-/** @hide */
-public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
-    private static final String SSID_KEY = "mSsid";
-    @Nullable private final String mSsid;
-
-    private VcnWifiUnderlyingNetworkPriority(
-            int networkQuality, boolean allowMetered, String ssid) {
-        super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
-        mSsid = ssid;
-
-        validate();
-    }
-
-    /** @hide */
-    @NonNull
-    @VisibleForTesting(visibility = Visibility.PROTECTED)
-    public static VcnWifiUnderlyingNetworkPriority fromPersistableBundle(
-            @NonNull PersistableBundle in) {
-        Objects.requireNonNull(in, "PersistableBundle is null");
-
-        final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
-        final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
-        final String ssid = in.getString(SSID_KEY);
-        return new VcnWifiUnderlyingNetworkPriority(networkQuality, allowMetered, ssid);
-    }
-
-    /** @hide */
-    @Override
-    @NonNull
-    @VisibleForTesting(visibility = Visibility.PROTECTED)
-    public PersistableBundle toPersistableBundle() {
-        final PersistableBundle result = super.toPersistableBundle();
-        result.putString(SSID_KEY, mSsid);
-        return result;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), mSsid);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        if (!super.equals(other)) {
-            return false;
-        }
-
-        if (!(other instanceof VcnWifiUnderlyingNetworkPriority)) {
-            return false;
-        }
-
-        final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other;
-        return mSsid.equals(rhs.mSsid);
-    }
-
-    /** @hide */
-    @Override
-    void dumpTransportSpecificFields(IndentingPrintWriter pw) {
-        pw.println("mSsid: " + mSsid);
-    }
-
-    /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */
-    @Nullable
-    public String getSsid() {
-        return mSsid;
-    }
-
-    /** This class is used to incrementally build VcnWifiUnderlyingNetworkPriority objects. */
-    public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
-        @Nullable private String mSsid;
-
-        /** Construct a Builder object. */
-        public Builder() {}
-
-        /**
-         * Set the required SSID.
-         *
-         * @param ssid the required SSID, or {@code null} if any SSID is acceptable.
-         */
-        @NonNull
-        public Builder setSsid(@Nullable String ssid) {
-            mSsid = ssid;
-            return this;
-        }
-
-        /** Build the VcnWifiUnderlyingNetworkPriority. */
-        @NonNull
-        public VcnWifiUnderlyingNetworkPriority build() {
-            return new VcnWifiUnderlyingNetworkPriority(mNetworkQuality, mAllowMetered, mSsid);
-        }
-
-        /** @hide */
-        @Override
-        Builder self() {
-            return this;
-        }
-    }
-}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
new file mode 100644
index 0000000..23a07ab
--- /dev/null
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class represents a configuration for a network template class of underlying Carrier WiFi
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
+public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
+    private static final String SSIDS_KEY = "mSsids";
+    @Nullable private final Set<String> mSsids;
+
+    private VcnWifiUnderlyingNetworkTemplate(
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps,
+            Set<String> ssids) {
+        super(
+                NETWORK_PRIORITY_TYPE_WIFI,
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps);
+        mSsids = new ArraySet<>(ssids);
+
+        validate();
+    }
+
+    /** @hide */
+    @Override
+    protected void validate() {
+        super.validate();
+        validateSsids(mSsids);
+    }
+
+    private static void validateSsids(Set<String> ssids) {
+        Objects.requireNonNull(ssids, "ssids is null");
+
+        for (String ssid : ssids) {
+            Objects.requireNonNull(ssid, "found null value ssid");
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle(
+            @NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle is null");
+
+        final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+
+        final int minEntryUpstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitUpstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minEntryDownstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitDownstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
+        final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY);
+        Objects.requireNonNull(ssidsBundle, "ssidsBundle is null");
+        final Set<String> ssids =
+                new ArraySet<String>(
+                        PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER));
+        return new VcnWifiUnderlyingNetworkTemplate(
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps,
+                ssids);
+    }
+
+    /** @hide */
+    @Override
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    public PersistableBundle toPersistableBundle() {
+        final PersistableBundle result = super.toPersistableBundle();
+
+        final PersistableBundle ssidsBundle =
+                PersistableBundleUtils.fromList(new ArrayList<>(mSsids), STRING_SERIALIZER);
+        result.putPersistableBundle(SSIDS_KEY, ssidsBundle);
+
+        return result;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mSsids);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+
+        if (!(other instanceof VcnWifiUnderlyingNetworkTemplate)) {
+            return false;
+        }
+
+        final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other;
+        return mSsids.equals(rhs.mSsids);
+    }
+
+    /** @hide */
+    @Override
+    void dumpTransportSpecificFields(IndentingPrintWriter pw) {
+        pw.println("mSsids: " + mSsids);
+    }
+
+    /**
+     * Retrieve the matching SSIDs, or an empty set if any SSID is acceptable.
+     *
+     * @see Builder#setSsids(Set)
+     */
+    @NonNull
+    public Set<String> getSsids() {
+        return Collections.unmodifiableSet(mSsids);
+    }
+
+    /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
+    public static final class Builder {
+        private int mMeteredMatchCriteria = MATCH_ANY;
+        @NonNull private final Set<String> mSsids = new ArraySet<>();
+
+        private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
+        /** Construct a Builder object. */
+        public Builder() {}
+
+        /**
+         * Set the matching criteria for metered networks.
+         *
+         * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+         * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+         * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+         *
+         * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+         *     #MATCH_ANY}.
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+         */
+        // The matching getter is defined in the super class. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMetered()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setMetered(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setMetered");
+
+            mMeteredMatchCriteria = matchCriteria;
+            return this;
+        }
+
+        /**
+         * Set the SSIDs with which a network can match this priority rule.
+         *
+         * @param ssids the matching SSIDs. Network with one of the matching SSIDs can match this
+         *     priority rule. If the set is empty, any SSID will match. The default is an empty set.
+         */
+        @NonNull
+        public Builder setSsids(@NonNull Set<String> ssids) {
+            validateSsids(ssids);
+
+            mSsids.clear();
+            mSsids.addAll(ssids);
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+         *     that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+         *     that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinUpstreamBandwidthKbps(
+                int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+            validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+            mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+            mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+         *     networks that ARE NOT the already-selected underlying network, or {@code 0} to
+         *     disable this requirement. Disabled by default.
+         * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+         *     network that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinDownstreamBandwidthKbps(
+                int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+            validateMinBandwidthKbps(
+                    minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+            mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+            mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /** Build the VcnWifiUnderlyingNetworkTemplate. */
+        @NonNull
+        public VcnWifiUnderlyingNetworkTemplate build() {
+            return new VcnWifiUnderlyingNetworkTemplate(
+                    mMeteredMatchCriteria,
+                    mMinEntryUpstreamBandwidthKbps,
+                    mMinExitUpstreamBandwidthKbps,
+                    mMinEntryDownstreamBandwidthKbps,
+                    mMinExitDownstreamBandwidthKbps,
+                    mSsids);
+        }
+    }
+}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index c607195..47a272c 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -191,6 +191,8 @@
     private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
             POWER_COMPONENT_CPU,
             POWER_COMPONENT_MOBILE_RADIO,
+            POWER_COMPONENT_WIFI,
+            POWER_COMPONENT_BLUETOOTH,
     };
 
     static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fa209cc..2d33817 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -461,6 +461,12 @@
         public abstract long getCountLocked(int which);
 
         /**
+         * Returns the count accumulated by this Counter for the specified process state.
+         * If the counter does not support per-procstate tracking, returns 0.
+         */
+        public abstract long getCountForProcessState(@BatteryConsumer.ProcessState int procState);
+
+        /**
          * Temporary for debugging.
          */
         public abstract void logState(Printer pw, String prefix);
@@ -1031,6 +1037,16 @@
         public abstract long getBluetoothMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the uid's bluetooth usage
+         * when in the specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getBluetoothMeasuredBatteryConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+        /**
          * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
          * on device power measurement data.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
@@ -1096,6 +1112,17 @@
         public abstract long getWifiMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the uid's wifi usage when in the
+         * specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getWifiMeasuredBatteryConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+
+        /**
          * Returns the battery consumption (in microcoulombs) used by this uid for each
          * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
          * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
@@ -3374,6 +3401,16 @@
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
     /**
+     * Returns aggregated wake lock stats.
+     */
+    public abstract WakeLockStats getWakeLockStats();
+
+    /**
+     * Returns aggregated Bluetooth stats.
+     */
+    public abstract BluetoothBatteryStats getBluetoothBatteryStats();
+
+    /**
      * Returns Timers tracking the total time of each Resource Power Manager state and voter.
      */
     public abstract Map<String, ? extends Timer> getRpmStats();
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 7f526c1..2a609b8 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -157,6 +157,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface WifiSupplState {}
 
+
     private final IBatteryStats mBatteryStats;
 
     /** @hide */
@@ -352,6 +353,36 @@
     }
 
     /**
+     * Retrieves accumulate wake lock stats.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public WakeLockStats getWakeLockStats() {
+        try {
+            return mBatteryStats.getWakeLockStats();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves accumulated bluetooth stats.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public BluetoothBatteryStats getBluetoothBatteryStats() {
+        try {
+            return mBatteryStats.getBluetoothBatteryStats();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Indicates an app acquiring full wifi lock.
      *
      * @param ws worksource (to be used for battery blaming).
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index a23dae8..abe5f81 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -135,7 +135,7 @@
     private final List<UidBatteryConsumer> mUidBatteryConsumers;
     private final List<UserBatteryConsumer> mUserBatteryConsumers;
     private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
-    private final Parcel mHistoryBuffer;
+    private final BatteryStatsHistory mBatteryStatsHistory;
     private CursorWindow mBatteryConsumersCursorWindow;
 
     private BatteryUsageStats(@NonNull Builder builder) {
@@ -146,7 +146,7 @@
         mDischargePercentage = builder.mDischargePercentage;
         mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
         mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
-        mHistoryBuffer = builder.mHistoryBuffer;
+        mBatteryStatsHistory = builder.mBatteryStatsHistory;
         mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
         mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
         mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
@@ -301,11 +301,11 @@
      */
     @NonNull
     public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
-        if (mHistoryBuffer == null) {
+        if (mBatteryStatsHistory == null) {
             throw new IllegalStateException(
                     "Battery history was not requested in the BatteryUsageStatsQuery");
         }
-        return new BatteryStatsHistoryIterator(new BatteryStatsHistory(mHistoryBuffer));
+        return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
     }
 
     @Override
@@ -363,12 +363,9 @@
         }
 
         if (source.readBoolean()) {
-            final byte[] historyBlob = source.readBlob();
-
-            mHistoryBuffer = Parcel.obtain();
-            mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+            mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source);
         } else {
-            mHistoryBuffer = null;
+            mBatteryStatsHistory = null;
         }
     }
 
@@ -389,9 +386,9 @@
 
         mBatteryConsumersCursorWindow.writeToParcel(dest, flags);
 
-        if (mHistoryBuffer != null) {
+        if (mBatteryStatsHistory != null) {
             dest.writeBoolean(true);
-            dest.writeBlob(mHistoryBuffer.marshall());
+            mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest);
         } else {
             dest.writeBoolean(false);
         }
@@ -770,7 +767,7 @@
                 new SparseArray<>();
         private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
                 new SparseArray<>();
-        private Parcel mHistoryBuffer;
+        private BatteryStatsHistory mBatteryStatsHistory;
 
         public Builder(@NonNull String[] customPowerComponentNames) {
             this(customPowerComponentNames, false, false);
@@ -895,8 +892,8 @@
          * Sets the parceled recent history.
          */
         @NonNull
-        public Builder setBatteryHistory(Parcel historyBuffer) {
-            mHistoryBuffer = historyBuffer;
+        public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory) {
+            mBatteryStatsHistory = batteryStatsHistory;
             return this;
         }
 
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 429450c..80cf2f8 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -311,6 +311,7 @@
     private final long mObject;
 
     private IInterface mOwner;
+    @Nullable
     private String mDescriptor;
     private volatile String[] mTransactionTraceNames = null;
     private volatile String mSimpleDescriptor = null;
@@ -930,8 +931,8 @@
                 transactionNames[i] = buf.toString();
                 buf.setLength(0);
             }
-            mTransactionTraceNames = transactionNames;
             mSimpleDescriptor = descriptor;
+            mTransactionTraceNames = transactionNames;
         }
         final int index = transactionCode - FIRST_CALL_TRANSACTION;
         if (index < 0 || index >= mTransactionTraceNames.length) {
@@ -940,13 +941,19 @@
         return mTransactionTraceNames[index];
     }
 
-    private String getSimpleDescriptor() {
-        final int dot = mDescriptor.lastIndexOf(".");
+    private @NonNull String getSimpleDescriptor() {
+        String descriptor = mDescriptor;
+        if (descriptor == null) {
+            // Just "Binder" to avoid null checks in transaction name tracing.
+            return "Binder";
+        }
+
+        final int dot = descriptor.lastIndexOf(".");
         if (dot > 0) {
             // Strip the package name
-            return mDescriptor.substring(dot + 1);
+            return descriptor.substring(dot + 1);
         }
-        return mDescriptor;
+        return descriptor;
     }
 
     /**
diff --git a/core/java/android/os/BluetoothBatteryStats.aidl b/core/java/android/os/BluetoothBatteryStats.aidl
new file mode 100644
index 0000000..d0514b6
--- /dev/null
+++ b/core/java/android/os/BluetoothBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable BluetoothBatteryStats;
diff --git a/core/java/android/os/BluetoothBatteryStats.java b/core/java/android/os/BluetoothBatteryStats.java
new file mode 100644
index 0000000..3d99a08
--- /dev/null
+++ b/core/java/android/os/BluetoothBatteryStats.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of Bluetooth battery stats.
+ *
+ * @hide
+ */
+public class BluetoothBatteryStats implements Parcelable {
+
+    /** @hide */
+    public static class UidStats {
+        public final int uid;
+        public final long scanTimeMs;
+        public final long unoptimizedScanTimeMs;
+        public final int scanResultCount;
+        public final long rxTimeMs;
+        public final long txTimeMs;
+
+        public UidStats(int uid, long scanTimeMs, long unoptimizedScanTimeMs, int scanResultCount,
+                long rxTimeMs, long txTimeMs) {
+            this.uid = uid;
+            this.scanTimeMs = scanTimeMs;
+            this.unoptimizedScanTimeMs = unoptimizedScanTimeMs;
+            this.scanResultCount = scanResultCount;
+            this.rxTimeMs = rxTimeMs;
+            this.txTimeMs = txTimeMs;
+        }
+
+        private UidStats(Parcel in) {
+            uid = in.readInt();
+            scanTimeMs = in.readLong();
+            unoptimizedScanTimeMs = in.readLong();
+            scanResultCount = in.readInt();
+            rxTimeMs = in.readLong();
+            txTimeMs = in.readLong();
+        }
+
+        private void writeToParcel(Parcel out) {
+            out.writeInt(uid);
+            out.writeLong(scanTimeMs);
+            out.writeLong(unoptimizedScanTimeMs);
+            out.writeInt(scanResultCount);
+            out.writeLong(rxTimeMs);
+            out.writeLong(txTimeMs);
+        }
+
+        @Override
+        public String toString() {
+            return "UidStats{"
+                    + "uid=" + uid
+                    + ", scanTimeMs=" + scanTimeMs
+                    + ", unoptimizedScanTimeMs=" + unoptimizedScanTimeMs
+                    + ", scanResultCount=" + scanResultCount
+                    + ", rxTimeMs=" + rxTimeMs
+                    + ", txTimeMs=" + txTimeMs
+                    + '}';
+        }
+    }
+
+    private final List<UidStats> mUidStats;
+
+    public BluetoothBatteryStats(@NonNull List<UidStats> uidStats) {
+        mUidStats = uidStats;
+    }
+
+    @NonNull
+    public List<UidStats> getUidStats() {
+        return mUidStats;
+    }
+
+    protected BluetoothBatteryStats(Parcel in) {
+        final int size = in.readInt();
+        mUidStats = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            mUidStats.add(new UidStats(in));
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        final int size = mUidStats.size();
+        out.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            UidStats stats = mUidStats.get(i);
+            stats.writeToParcel(out);
+        }
+    }
+
+    public static final Creator<BluetoothBatteryStats> CREATOR =
+            new Creator<BluetoothBatteryStats>() {
+                @Override
+                public BluetoothBatteryStats createFromParcel(Parcel in) {
+                    return new BluetoothBatteryStats(in);
+                }
+
+                @Override
+                public BluetoothBatteryStats[] newArray(int size) {
+                    return new BluetoothBatteryStats[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/os/BluetoothServiceManager.java b/core/java/android/os/BluetoothServiceManager.java
new file mode 100644
index 0000000..12f7bc8
--- /dev/null
+++ b/core/java/android/os/BluetoothServiceManager.java
@@ -0,0 +1,121 @@
+/*
+ * 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 android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+import android.os.BluetoothServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the bluetooth
+ * service.
+ *
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+public class BluetoothServiceManager {
+
+    /** @hide */
+    public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+    /**
+     * @hide
+     */
+    public BluetoothServiceManager() {
+    }
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Register a system server binding object for a service.
+         */
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         */
+        @NonNull
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         */
+        @Nullable
+        public IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow}.
+     *
+     */
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor.
+         *
+         * @param name the name of the binder service that cannot be found.
+         *
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "bluetooth" service.
+     */
+    @NonNull
+    public ServiceRegisterer getBluetoothManagerServiceRegisterer() {
+        return new ServiceRegisterer(BLUETOOTH_MANAGER_SERVICE);
+    }
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3f42164..fe86874 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -90,7 +90,7 @@
     Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
-    int removeUserOrSetEphemeral(int userId, boolean evenWhenDisallowed);
+    int removeUserWhenPossible(int userId, boolean overrideDevicePolicy);
     boolean markGuestForDeletion(int userId);
     UserInfo findCurrentGuestUser();
     boolean isQuietModeEnabled(int userId);
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index cfa823c..b74bb33 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.icu.util.ULocale;
 
@@ -312,18 +313,30 @@
         return isPseudoLocale(locale != null ? locale.toLocale() : null);
     }
 
-    @IntRange(from=0, to=1)
-    private static int matchScore(Locale supported, Locale desired) {
+    /**
+     * Determine whether two locales are considered a match, even if they are not exactly equal.
+     * They are considered as a match when both of their languages and scripts
+     * (explicit or inferred) are identical. This means that a user would be able to understand
+     * the content written in the supported locale even if they say they prefer the desired locale.
+     *
+     * E.g. [zh-HK] matches [zh-Hant]; [en-US] matches [en-CA]
+     *
+     * @param supported The supported {@link Locale} to be compared.
+     * @param desired   The desired {@link Locale} to be compared.
+     * @return True if they match, false otherwise.
+     */
+    public static boolean matchesLanguageAndScript(@SuppressLint("UseIcu") @NonNull
+            Locale supported, @SuppressLint("UseIcu") @NonNull Locale desired) {
         if (supported.equals(desired)) {
-            return 1;  // return early so we don't do unnecessary computation
+            return true;  // return early so we don't do unnecessary computation
         }
         if (!supported.getLanguage().equals(desired.getLanguage())) {
-            return 0;
+            return false;
         }
         if (isPseudoLocale(supported) || isPseudoLocale(desired)) {
             // The locales are not the same, but the languages are the same, and one of the locales
             // is a pseudo-locale. So this is not a match.
-            return 0;
+            return false;
         }
         final String supportedScr = getLikelyScript(supported);
         if (supportedScr.isEmpty()) {
@@ -331,20 +344,17 @@
             // if the locales match. So we fall back to old behavior of matching, which considered
             // locales with different regions different.
             final String supportedRegion = supported.getCountry();
-            return (supportedRegion.isEmpty() ||
-                    supportedRegion.equals(desired.getCountry()))
-                    ? 1 : 0;
+            return supportedRegion.isEmpty() || supportedRegion.equals(desired.getCountry());
         }
         final String desiredScr = getLikelyScript(desired);
         // There is no match if the two locales use different scripts. This will most imporantly
         // take care of traditional vs simplified Chinese.
-        return supportedScr.equals(desiredScr) ? 1 : 0;
+        return supportedScr.equals(desiredScr);
     }
 
     private int findFirstMatchIndex(Locale supportedLocale) {
         for (int idx = 0; idx < mList.length; idx++) {
-            final int score = matchScore(supportedLocale, mList[idx]);
-            if (score > 0) {
+            if (matchesLanguageAndScript(supportedLocale, mList[idx])) {
                 return idx;
             }
         }
diff --git a/core/java/android/os/NewUserResponse.java b/core/java/android/os/NewUserResponse.java
index 3869559..c2aef8e 100644
--- a/core/java/android/os/NewUserResponse.java
+++ b/core/java/android/os/NewUserResponse.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 /**
  * Contains the response of the call {@link UserManager#createUser(NewUserRequest)}.
@@ -29,7 +30,11 @@
     private final @Nullable UserHandle mUser;
     private final @UserManager.UserOperationResult int mOperationResult;
 
-    NewUserResponse(@Nullable UserHandle user,
+    /**
+     * @hide
+     */
+    @TestApi
+    public NewUserResponse(@Nullable UserHandle user,
             @UserManager.UserOperationResult int operationResult) {
         mUser = user;
         mOperationResult = operationResult;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3bc3ec8..e8b3ae9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3711,10 +3711,10 @@
         final int m = list.size();
         int i = 0;
         for (; i < m && i < n; i++) {
-            list.set(i, (T) readParcelableInternal(cl, clazz));
+            list.set(i, readParcelableInternal(cl, clazz));
         }
         for (; i < n; i++) {
-            list.add((T) readParcelableInternal(cl, clazz));
+            list.add(readParcelableInternal(cl, clazz));
         }
         for (; i < m; i++) {
             list.remove(n);
@@ -4217,7 +4217,8 @@
      * trying to instantiate an element.
      */
     @Nullable
-    public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+    public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
+            @NonNull Class<? super T> clazz) {
         Objects.requireNonNull(clazz);
         return readParcelableInternal(loader, clazz);
     }
@@ -4227,7 +4228,8 @@
      */
     @SuppressWarnings("unchecked")
     @Nullable
-    private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+    private <T extends Parcelable> T readParcelableInternal(@Nullable ClassLoader loader,
+            @Nullable Class<? super T> clazz) {
         Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
         if (creator == null) {
             return null;
@@ -4463,7 +4465,8 @@
      * deserializing the object.
      */
     @Nullable
-    public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+    public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
+            @NonNull Class<? super T> clazz) {
         Objects.requireNonNull(clazz);
         return readSerializableInternal(
                 loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -4473,8 +4476,8 @@
      * @param clazz The type of the serializable expected or {@code null} for performing no checks
      */
     @Nullable
-    private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
-            @Nullable Class<T> clazz) {
+    private <T extends Serializable> T readSerializableInternal(@Nullable final ClassLoader loader,
+            @Nullable Class<? super T> clazz) {
         String name = readString();
         if (name == null) {
             // For some reason we were unable to read the name of the Serializable (either there
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 74fffd0..d4a338b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -335,6 +335,12 @@
     public static final int USER_ACTIVITY_EVENT_FACE_DOWN = 5;
 
     /**
+     * User activity event type: There is a change in the device state.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_EVENT_DEVICE_STATE = 6;
+
+    /**
      * User activity flag: If already dimmed, extend the dim timeout
      * but do not brighten.  This flag is useful for keeping the screen on
      * a little longer without causing a visible change such as when
@@ -1011,7 +1017,7 @@
             new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
                 CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY) {
                 @Override
-                protected Boolean recompute(Void query) {
+                public Boolean recompute(Void query) {
                     try {
                         return mService.isPowerSaveMode();
                     } catch (RemoteException e) {
@@ -1024,7 +1030,7 @@
             new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
                 CACHE_KEY_IS_INTERACTIVE_PROPERTY) {
                 @Override
-                protected Boolean recompute(Void query) {
+                public Boolean recompute(Void query) {
                     try {
                         return mService.isInteractive();
                     } catch (RemoteException e) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 742a542..2fe0622 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -132,6 +132,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @TestApi
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int NFC_UID = 1027;
 
     /**
@@ -279,6 +280,26 @@
     public static final int LAST_APPLICATION_UID = 19999;
 
     /**
+     * Defines the start of a range of UIDs going from this number to
+     * {@link #LAST_SUPPLEMENTAL_UID} that are reserved for assigning to
+     * supplemental processes. There is a 1-1 mapping between a supplemental
+     * process UID and the app that it belongs to, which can be computed by
+     * subtracting (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID) from the
+     * uid of a supplemental process.
+     *
+     * Note that there are no GIDs associated with these processes; storage
+     * attribution for them will be done using project IDs.
+     * @hide
+     */
+    public static final int FIRST_SUPPLEMENTAL_UID = 20000;
+
+    /**
+     * Last UID that is used for supplemental processes.
+     * @hide
+     */
+    public static final int LAST_SUPPLEMENTAL_UID = 29999;
+
+    /**
      * First uid used for fully isolated sandboxed processes spawned from an app zygote
      * @hide
      */
@@ -880,6 +901,46 @@
     }
 
     /**
+     * Returns whether the provided UID belongs to a supplemental process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final boolean isSupplemental(int uid) {
+        uid = UserHandle.getAppId(uid);
+        return (uid >= FIRST_SUPPLEMENTAL_UID && uid <= LAST_SUPPLEMENTAL_UID);
+    }
+
+    /**
+     *
+     * Returns the app process corresponding to a supplemental process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int toAppUid(int uid) {
+        return uid - (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
+     *
+     * Returns the supplemental process corresponding to an app process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int toSupplementalUid(int uid) {
+        return uid + (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
+     * Returns whether the current process is a supplemental process.
+     */
+    public static final boolean isSupplemental() {
+        return isSupplemental(myUid());
+    }
+
+    /**
      * Returns the UID assigned to a particular user name, or -1 if there is
      * none.  If the given string consists of only numbers, it is converted
      * directly to a uid.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index dbb5a2c..70aaa5e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -15,10 +15,13 @@
  */
 package android.os;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
@@ -2690,6 +2693,12 @@
         ((AndroidBlockGuardPolicy) policy).onCustomSlowCall(name);
     }
 
+    /** @hide */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void noteUntaggedSocket() {
+        if (vmUntaggedSocketEnabled()) onUntaggedSocket();
+    }
+
     /**
      * For code to note that a resource was obtained using a type other than its defined type. This
      * is a no-op unless the current thread's {@link android.os.StrictMode.ThreadPolicy} has {@link
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index d8f6344..b9252d6 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -311,7 +311,6 @@
      * time or throw.
      *
      * @throws DateTimeException when no accurate network time can be provided.
-     * @hide
      */
     public static @NonNull Clock currentNetworkTimeClock() {
         return new SimpleClock(ZoneOffset.UTC) {
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 07f4082..22ddbcc 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -31,15 +31,6 @@
       ]
     },
     {
-      "file_patterns": ["Environment\\.java"],
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest"
-        }
-      ]
-    },
-    {
       "file_patterns": [
         "BatteryStats[^/]*\\.java",
         "BatteryUsageStats[^/]*\\.java",
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index d974e0c..6b869f1 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,7 +16,10 @@
 
 package android.os;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -90,6 +93,7 @@
     /** @hide */
     public static final long TRACE_TAG_DATABASE = 1L << 20;
     /** @hide */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final long TRACE_TAG_NETWORK = 1L << 21;
     /** @hide */
     public static final long TRACE_TAG_ADB = 1L << 22;
@@ -148,6 +152,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
     public static boolean isTagEnabled(long traceTag) {
         long tags = nativeGetEnabledTags();
         return (tags & traceTag) != 0;
@@ -163,7 +168,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static void traceCounter(long traceTag, String counterName, int counterValue) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void traceCounter(long traceTag, @NonNull String counterName, int counterValue) {
         if (isTagEnabled(traceTag)) {
             nativeTraceCounter(traceTag, counterName, counterValue);
         }
@@ -202,7 +208,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static void traceBegin(long traceTag, String methodName) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void traceBegin(long traceTag, @NonNull String methodName) {
         if (isTagEnabled(traceTag)) {
             nativeTraceBegin(traceTag, methodName);
         }
@@ -217,6 +224,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
     public static void traceEnd(long traceTag) {
         if (isTagEnabled(traceTag)) {
             nativeTraceEnd(traceTag);
@@ -237,7 +245,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static void asyncTraceBegin(long traceTag, String methodName, int cookie) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void asyncTraceBegin(long traceTag, @NonNull String methodName, int cookie) {
         if (isTagEnabled(traceTag)) {
             nativeAsyncTraceBegin(traceTag, methodName, cookie);
         }
@@ -255,7 +264,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static void asyncTraceEnd(long traceTag, String methodName, int cookie) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void asyncTraceEnd(long traceTag, @NonNull String methodName, int cookie) {
         if (isTagEnabled(traceTag)) {
             nativeAsyncTraceEnd(traceTag, methodName, cookie);
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b3639e4..f18c9c9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.UNDEFINED;
+
 import android.Manifest;
 import android.accounts.AccountManager;
 import android.annotation.ColorInt;
@@ -820,6 +823,20 @@
     public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
 
     /**
+     * Specifies if a user is disallowed from creating clone profile.
+     * <p>The default value for an unmanaged user is <code>false</code>.
+     * For users with a device owner set, the default is <code>true</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_ADD_CLONE_PROFILE = "no_add_clone_profile";
+
+    /**
      * Specifies if a user is disallowed from disabling application verification. The default
      * value is <code>false</code>.
      *
@@ -1497,6 +1514,7 @@
             DISALLOW_FACTORY_RESET,
             DISALLOW_ADD_USER,
             DISALLOW_ADD_MANAGED_PROFILE,
+            DISALLOW_ADD_CLONE_PROFILE,
             ENSURE_VERIFY_APPS,
             DISALLOW_CONFIG_CELL_BROADCASTS,
             DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -1657,41 +1675,49 @@
     public @interface UserSwitchabilityResult {}
 
     /**
-     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
-     * user has been successfully removed.
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the specified user has been successfully removed.
+     *
      * @hide
      */
+    @SystemApi
     public static final int REMOVE_RESULT_REMOVED = 0;
 
     /**
-     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
-     * user has had its {@link UserInfo#FLAG_EPHEMERAL} flag set to {@code true}, so that it will be
-     * removed when the user is stopped or on boot.
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the specified user is marked so that it will be removed when the user is stopped or on boot.
+     *
      * @hide
      */
-    public static final int REMOVE_RESULT_SET_EPHEMERAL = 1;
+    @SystemApi
+    public static final int REMOVE_RESULT_DEFERRED = 1;
 
     /**
-     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
-     * user is already in the process of being removed.
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the specified user is already in the process of being removed.
+     *
      * @hide
      */
+    @SystemApi
     public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2;
 
     /**
-     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that an error occurred
-     * that prevented the user from being removed or set as ephemeral.
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * an error occurred that prevented the user from being removed or set as ephemeral.
+     *
      * @hide
      */
+    @SystemApi
     public static final int REMOVE_RESULT_ERROR = 3;
 
     /**
-     * Possible response codes from {@link #removeUserOrSetEphemeral(int)}.
+     * Possible response codes from {@link #removeUserWhenPossible(UserHandle, boolean)}.
+     *
      * @hide
      */
     @IntDef(prefix = { "REMOVE_RESULT_" }, value = {
             REMOVE_RESULT_REMOVED,
-            REMOVE_RESULT_SET_EPHEMERAL,
+            REMOVE_RESULT_DEFERRED,
             REMOVE_RESULT_ALREADY_BEING_REMOVED,
             REMOVE_RESULT_ERROR,
     })
@@ -2754,7 +2780,7 @@
             new PropertyInvalidatedCache<Integer, Boolean>(
                 32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
                 @Override
-                protected Boolean recompute(Integer query) {
+                public Boolean recompute(Integer query) {
                     try {
                         return mService.isUserUnlocked(query);
                     } catch (RemoteException re) {
@@ -2762,7 +2788,7 @@
                     }
                 }
                 @Override
-                protected boolean bypass(Integer query) {
+                public boolean bypass(Integer query) {
                     return query < 0;
                 }
             };
@@ -2772,7 +2798,7 @@
             new PropertyInvalidatedCache<Integer, Boolean>(
                 32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
                 @Override
-                protected Boolean recompute(Integer query) {
+                public Boolean recompute(Integer query) {
                     try {
                         return mService.isUserUnlockingOrUnlocked(query);
                     } catch (RemoteException re) {
@@ -2780,7 +2806,7 @@
                     }
                 }
                 @Override
-                protected boolean bypass(Integer query) {
+                public boolean bypass(Integer query) {
                     return query < 0;
                 }
             };
@@ -4637,6 +4663,18 @@
         if (!hasBadge(userId)) {
             return label;
         }
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(
+                getUpdatableUserBadgedLabelId(userId),
+                () -> getDefaultUserBadgedLabel(label, userId),
+                /* formatArgs= */ label);
+    }
+
+    private String getUpdatableUserBadgedLabelId(int userId) {
+        return isManagedProfile(userId) ? WORK_PROFILE_BADGED_LABEL : UNDEFINED;
+    }
+
+    private String getDefaultUserBadgedLabel(CharSequence label, int userId) {
         try {
             final int resourceId = mService.getUserBadgeLabelResId(userId);
             return Resources.getSystem().getString(resourceId, label);
@@ -4728,18 +4766,48 @@
      * the current user, then set the user as ephemeral so that it will be removed when it is
      * stopped.
      *
-     * @param evenWhenDisallowed when {@code true}, user is removed even if the caller user has the
+     * @param overrideDevicePolicy when {@code true}, user is removed even if the caller has
+     * the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
+     *
+     * @return the result code {@link #REMOVE_RESULT_REMOVED}, {@link #REMOVE_RESULT_DEFERRED},
+     * {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, or {@link #REMOVE_RESULT_ERROR}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public int removeUserWhenPossible(@NonNull UserHandle user,
+            boolean overrideDevicePolicy) {
+        try {
+            return mService.removeUserWhenPossible(user.getIdentifier(), overrideDevicePolicy);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Immediately removes the user or, if the user cannot be removed, such as when the user is
+     * the current user, then set the user as ephemeral so that it will be removed when it is
+     * stopped.
+     *
+     * @param evenWhenDisallowed when {@code true}, user is removed even if the caller has the
      * {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
      *
      * @return the {@link RemoveResult} code
+     *
+     * @deprecated  TODO(b/199446770): remove this call after converting all calls to
+     * removeUserWhenPossible(UserHandle, boolean)
+     *
      * @hide
      */
+    @Deprecated
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
     public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
             boolean evenWhenDisallowed) {
         try {
-            return mService.removeUserOrSetEphemeral(userId, evenWhenDisallowed);
+            return mService.removeUserWhenPossible(userId, evenWhenDisallowed);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 5831573..8bc219b 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -32,32 +32,30 @@
 public final class VibrationAttributes implements Parcelable {
     private static final String TAG = "VibrationAttributes";
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @IntDef(prefix = { "USAGE_CLASS_" }, value = {
             USAGE_CLASS_UNKNOWN,
             USAGE_CLASS_ALARM,
             USAGE_CLASS_FEEDBACK,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface UsageClass{}
+    public @interface UsageClass {}
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @IntDef(prefix = { "USAGE_" }, value = {
             USAGE_UNKNOWN,
+            USAGE_ACCESSIBILITY,
             USAGE_ALARM,
-            USAGE_RINGTONE,
-            USAGE_NOTIFICATION,
             USAGE_COMMUNICATION_REQUEST,
-            USAGE_TOUCH,
-            USAGE_PHYSICAL_EMULATION,
             USAGE_HARDWARE_FEEDBACK,
+            USAGE_MEDIA,
+            USAGE_NOTIFICATION,
+            USAGE_PHYSICAL_EMULATION,
+            USAGE_RINGTONE,
+            USAGE_TOUCH,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Usage{}
+    public @interface Usage {}
 
     /**
      * Vibration usage filter value to match all usages.
@@ -138,6 +136,7 @@
      */
     @IntDef(prefix = { "FLAG_" }, flag = true, value = {
             FLAG_BYPASS_INTERRUPTION_POLICY,
+            FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flag{}
@@ -148,10 +147,22 @@
     public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1;
 
     /**
+     * Flag requesting vibration effect to be played even when user settings are disabling it.
+     *
+     * <p>Flag introduced to represent
+     * {@link android.view.HapticFeedbackConstants#FLAG_IGNORE_GLOBAL_SETTING} and
+     * {@link AudioAttributes#FLAG_BYPASS_MUTE}.
+     *
+     * @hide
+     */
+    public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 0x2;
+
+    /**
      * All flags supported by vibrator service, update it when adding new flag.
      * @hide
      */
-    public static final int FLAG_ALL_SUPPORTED = FLAG_BYPASS_INTERRUPTION_POLICY;
+    public static final int FLAG_ALL_SUPPORTED =
+            FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 
     /** Creates a new {@link VibrationAttributes} instance with given usage. */
     public static @NonNull VibrationAttributes createForUsage(int usage) {
@@ -399,6 +410,11 @@
             if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
                 mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
             }
+            if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_MUTE) != 0) {
+                // Muted audio stream translates to vibration usage having the value
+                // Vibrator.VIBRATION_INTENSITY_OFF set in the user setting.
+                mFlags |= FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+            }
         }
 
         /**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 41ab062..ae37a71 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -260,7 +260,7 @@
         for (int i = 0; i < timings.length; i++) {
             float parsedAmplitude = amplitudes[i] == DEFAULT_AMPLITUDE
                     ? DEFAULT_AMPLITUDE : (float) amplitudes[i] / MAX_AMPLITUDE;
-            segments.add(new StepSegment(parsedAmplitude, /* frequency= */ 0, (int) timings[i]));
+            segments.add(new StepSegment(parsedAmplitude, /* frequencyHz= */ 0, (int) timings[i]));
         }
         VibrationEffect effect = new Composed(segments, repeat);
         effect.validate();
@@ -866,7 +866,7 @@
             Preconditions.checkArgumentNonnegative(delay);
             if (delay > 0) {
                 // Created a segment sustaining the zero amplitude to represent the delay.
-                addSegment(new StepSegment(/* amplitude= */ 0, /* frequency= */ 0,
+                addSegment(new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0,
                         /* duration= */ delay));
             }
             return addSegments(effect);
@@ -1033,26 +1033,27 @@
         @NonNull
         public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude,
                 @IntRange(from = 0) int duration) {
-            return addStep(amplitude, getPreviousFrequency(), duration);
+            mSegments.add(new StepSegment(amplitude, getPreviousFrequencyHz(), duration));
+            return this;
         }
 
         /**
-         * Vibrate with given amplitude for the given duration, in millis, keeping the previous
-         * vibration frequency the same.
+         * Vibrate with given amplitude and frequency for the given duration, in millis.
          *
          * <p>If the duration is zero the vibrator will jump to new amplitude.
          *
          * @param amplitude The amplitude for this step
-         * @param frequency The frequency for this step
+         * @param frequencyHz The frequency for this step, in hertz
          * @param duration  The duration of this step in milliseconds
          * @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
          */
         @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
         public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude,
-                @FloatRange(from = -1f, to = 1f) float frequency,
+                @FloatRange(from = 1f) float frequencyHz,
                 @IntRange(from = 0) int duration) {
-            mSegments.add(new StepSegment(amplitude, frequency, duration));
+            Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1");
+            mSegments.add(new StepSegment(amplitude, frequencyHz, duration));
             return this;
         }
 
@@ -1070,7 +1071,9 @@
         @NonNull
         public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude,
                 @IntRange(from = 0) int duration) {
-            return addRamp(amplitude, getPreviousFrequency(), duration);
+            mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude,
+                    getPreviousFrequencyHz(), getPreviousFrequencyHz(), duration));
+            return this;
         }
 
         /**
@@ -1080,22 +1083,23 @@
          * <p>If the duration is zero the vibrator will jump to new amplitude and frequency.
          *
          * @param amplitude The final amplitude this ramp should reach
-         * @param frequency The final frequency this ramp should reach
+         * @param frequencyHz The final frequency this ramp should reach, in hertz
          * @param duration  The duration of this ramp in milliseconds
          * @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
          */
         @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
         public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude,
-                @FloatRange(from = -1f, to = 1f) float frequency,
+                @FloatRange(from = 1f) float frequencyHz,
                 @IntRange(from = 0) int duration) {
-            mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude, getPreviousFrequency(),
-                    frequency, duration));
+            Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1");
+            mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude,
+                    getPreviousFrequencyHz(), frequencyHz, duration));
             return this;
         }
 
         /**
-         * Compose all of the steps together into a single {@link VibrationEffect}.
+         * Compose all the steps together into a single {@link VibrationEffect}.
          *
          * The {@link WaveformBuilder} object is still valid after this call, so you can
          * continue adding more primitives to it and generating more {@link VibrationEffect}s by
@@ -1109,7 +1113,7 @@
         }
 
         /**
-         * Compose all of the steps together into a single {@link VibrationEffect}.
+         * Compose all the steps together into a single {@link VibrationEffect}.
          *
          * <p>To cause the pattern to repeat, pass the index at which to start the repetition
          * (starting at 0), or -1 to disable repeating.
@@ -1131,13 +1135,13 @@
             return effect;
         }
 
-        private float getPreviousFrequency() {
+        private float getPreviousFrequencyHz() {
             if (!mSegments.isEmpty()) {
                 VibrationEffectSegment segment = mSegments.get(mSegments.size() - 1);
                 if (segment instanceof StepSegment) {
-                    return ((StepSegment) segment).getFrequency();
+                    return ((StepSegment) segment).getFrequencyHz();
                 } else if (segment instanceof RampSegment) {
-                    return ((RampSegment) segment).getEndFrequency();
+                    return ((RampSegment) segment).getEndFrequencyHz();
                 }
             }
             return 0;
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index c67c82e..eba9ff1b 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -17,20 +17,21 @@
 package android.os;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.vibrator.IVibrator;
 import android.media.AudioAttributes;
+import android.os.vibrator.VibrationConfig;
 import android.util.Log;
-import android.util.Range;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -51,6 +52,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_OFF = 0;
 
     /**
@@ -58,6 +60,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_LOW = 1;
 
     /**
@@ -65,6 +68,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_MEDIUM = 2;
 
     /**
@@ -72,6 +76,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_HIGH = 3;
 
     /**
@@ -117,16 +122,12 @@
     }
 
     private final String mPackageName;
-    // The default vibration intensity level for haptic feedback.
-    @VibrationIntensity
-    private int mDefaultHapticFeedbackIntensity;
-    // The default vibration intensity level for notifications.
-    @VibrationIntensity
-    private int mDefaultNotificationVibrationIntensity;
-    // The default vibration intensity level for ringtones.
-    @VibrationIntensity
-    private int mDefaultRingVibrationIntensity;
-    private float mHapticChannelMaxVibrationAmplitude;
+    @Nullable
+    private final Resources mResources;
+
+    // This is lazily loaded only for the few clients that need this (e. Settings app).
+    @Nullable
+    private volatile VibrationConfig mVibrationConfig;
 
     /**
      * @hide to prevent subclassing from outside of the framework
@@ -134,8 +135,7 @@
     @UnsupportedAppUsage
     public Vibrator() {
         mPackageName = ActivityThread.currentPackageName();
-        final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
-        loadVibrationConfig(ctx);
+        mResources = null;
     }
 
     /**
@@ -143,26 +143,7 @@
      */
     protected Vibrator(Context context) {
         mPackageName = context.getOpPackageName();
-        loadVibrationConfig(context);
-    }
-
-    private void loadVibrationConfig(Context context) {
-        mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context,
-                com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
-        mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context,
-                com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
-        mDefaultRingVibrationIntensity = loadDefaultIntensity(context,
-                com.android.internal.R.integer.config_defaultRingVibrationIntensity);
-        mHapticChannelMaxVibrationAmplitude = loadFloat(context,
-                com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
-    }
-
-    private int loadDefaultIntensity(Context ctx, int resId) {
-        return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM;
-    }
-
-    private float loadFloat(Context ctx, int resId, float defaultValue) {
-        return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
+        mResources = context.getResources();
     }
 
     /**
@@ -174,31 +155,30 @@
         return VibratorInfo.EMPTY_VIBRATOR_INFO;
     }
 
-    /**
-     * Get the default vibration intensity for haptic feedback.
-     *
-     * @hide
-     */
-    public int getDefaultHapticFeedbackIntensity() {
-        return mDefaultHapticFeedbackIntensity;
+    /** Get the static vibrator configuration from config.xml. */
+    private VibrationConfig getConfig() {
+        if (mVibrationConfig == null) {
+            Resources resources = mResources;
+            if (resources == null) {
+                final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
+                resources = ctx != null ? ctx.getResources() : null;
+            }
+            // This might be constructed more than once, but it only loads static config data from a
+            // xml file, so it would be ok.
+            mVibrationConfig = new VibrationConfig(resources);
+        }
+        return mVibrationConfig;
     }
 
     /**
-     * Get the default vibration intensity for notifications.
+     * Get the default vibration intensity for given usage.
      *
      * @hide
      */
-    public int getDefaultNotificationVibrationIntensity() {
-        return mDefaultNotificationVibrationIntensity;
-    }
-
-    /**
-     * Get the default vibration intensity for ringtones.
-     *
-     * @hide
-     */
-    public int getDefaultRingVibrationIntensity() {
-        return mDefaultRingVibrationIntensity;
+    @TestApi
+    @VibrationIntensity
+    public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
+        return getConfig().getDefaultVibrationIntensity(usage);
     }
 
     /**
@@ -271,43 +251,6 @@
     }
 
     /**
-     * Return a range of relative frequency values supported by the vibrator.
-     *
-     * <p>These values can be used to create waveforms that controls the vibration frequency via
-     * {@link VibrationEffect.WaveformBuilder}.
-     *
-     * @return A range of relative frequency values supported. The range will always contain the
-     * value 0, representing the device resonant frequency. Devices without frequency control will
-     * return the range [0,0]. Devices with frequency control will always return a range containing
-     * the safe range [-1, 1].
-     * @hide
-     */
-    public Range<Float> getRelativeFrequencyRange() {
-        return getInfo().getFrequencyRange();
-    }
-
-    /**
-     * Return the maximum amplitude the vibrator can play at given relative frequency.
-     *
-     * <p>Devices without frequency control will return 1 for the input zero (resonant frequency),
-     * and 0 to any other input.
-     *
-     * <p>Devices with frequency control will return the supported value, for input in
-     * {@link #getRelativeFrequencyRange()}, and 0 for any other input.
-     *
-     * <p>These values can be used to create waveforms that plays vibrations outside the resonant
-     * frequency via {@link VibrationEffect.WaveformBuilder}.
-     *
-     * @return a value in [0,1] representing the maximum amplitude the device can play at given
-     * relative frequency.
-     * @hide
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMaximumAmplitude(float relativeFrequency) {
-        return getInfo().getMaxAmplitude(relativeFrequency);
-    }
-
-    /**
      * Return the maximum amplitude the vibrator can play using the audio haptic channels.
      *
      * <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a
@@ -319,10 +262,7 @@
      * @hide
      */
     public float getHapticChannelMaximumAmplitude() {
-        if (mHapticChannelMaxVibrationAmplitude <= 0) {
-            return Float.NaN;
-        }
-        return mHapticChannelMaxVibrationAmplitude;
+        return getConfig().getHapticChannelMaximumAmplitude();
     }
 
     /**
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 0a0e3c8..5271c4d 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
-import android.util.Log;
 import android.util.MathUtils;
 import android.util.Range;
 import android.util.SparseBooleanArray;
@@ -345,49 +344,31 @@
     }
 
     /**
-     * Return a range of relative frequency values supported by the vibrator.
+     * Return a range of frequency values supported by the vibrator.
      *
-     * @return A range of relative frequency values supported. The range will always contain the
-     * value 0, representing the device resonant frequency. Devices without frequency control will
-     * return the range [0,0]. Devices with frequency control will always return a range containing
-     * the safe range [-1, 1].
+     * @return A range of frequency values supported, in hertz. The range will always contain the
+     * device resonant frequency. Devices without frequency control will return null.
      * @hide
      */
-    public Range<Float> getFrequencyRange() {
-        return mFrequencyMapping.mRelativeFrequencyRange;
+    @Nullable
+    public Range<Float> getFrequencyRangeHz() {
+        return mFrequencyMapping.mFrequencyRangeHz;
     }
 
     /**
-     * Return the maximum amplitude the vibrator can play at given relative frequency.
+     * Return the maximum amplitude the vibrator can play at given frequency.
      *
+     * @param frequencyHz The frequency, in hertz, for query.
+
      * @return a value in [0,1] representing the maximum amplitude the device can play at given
-     * relative frequency. Devices without frequency control will return 1 for the input zero
-     * (resonant frequency), and 0 to any other input. Devices with frequency control will return
-     * the supported value, for input in {@code #getFrequencyRange()}, and 0 for any other input.
+     * frequency. Devices without frequency control will return 0 to any input. Devices with
+     * frequency control will return the supported value, for input in
+     * {@link #getFrequencyRangeHz()}, and 0 for any other input.
      * @hide
      */
     @FloatRange(from = 0, to = 1)
-    public float getMaxAmplitude(float relativeFrequency) {
-        if (mFrequencyMapping.isEmpty()) {
-            // The vibrator has not provided values for frequency mapping.
-            // Return the expected behavior for devices without frequency control.
-            return Float.compare(relativeFrequency, 0) == 0 ? 1 : 0;
-        }
-        return mFrequencyMapping.getMaxAmplitude(relativeFrequency);
-    }
-
-    /**
-     * Return absolute frequency value for this vibrator, in hertz, that corresponds to given
-     * relative frequency.
-     *
-     * @retur a value in hertz that corresponds to given relative frequency. Input values outside
-     * {@link #getFrequencyRange()} will return {@link Float#NaN}. Devices without frequency control
-     * will return {@link Float#NaN} for any input.
-     * @hide
-     */
-    @FloatRange(from = 0)
-    public float getAbsoluteFrequency(float relativeFrequency) {
-        return mFrequencyMapping.toHertz(relativeFrequency);
+    public float getMaxAmplitude(float frequencyHz) {
+        return mFrequencyMapping.getMaxAmplitude(frequencyHz);
     }
 
     protected long getCapabilities() {
@@ -468,134 +449,96 @@
     }
 
     /**
-     * Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
+     * Describes the maximum relative output acceleration that can be achieved for each supported
+     * frequency in a specific vibrator.
      *
      * <p>This mapping is defined by the following parameters:
      *
      * <ol>
-     *     <li>{@code minFrequency}, {@code resonantFrequency} and {@code frequencyResolution}, in
-     *         hertz, provided by the vibrator.
+     *     <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
+     *         provided by the vibrator in hertz.
      *     <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where
      *         {@code maxAmplitudes[i]} represents max supported amplitude at frequency
-     *         {@code minFrequency + frequencyResolution * i}.
-     *     <li>{@code maxFrequency = minFrequency + frequencyResolution * (maxAmplitudes.length-1)}
-     *     <li>{@code suggestedSafeRangeHz} is the suggested frequency range in hertz that should be
-     *         mapped to relative values -1 and 1, where 0 maps to {@code resonantFrequency}.
-     * </ol>
-     *
-     * <p>The mapping is defined linearly by the following points:
-     *
-     * <ol>
-     *     <li>{@code toHertz(relativeMinFrequency) = minFrequency}
-     *     <li>{@code                   toHertz(-1) = resonantFrequency - safeRange / 2}
-     *     <li>{@code                    toHertz(0) = resonantFrequency}
-     *     <li>{@code                    toHertz(1) = resonantFrequency + safeRange / 2}
-     *     <li>{@code toHertz(relativeMaxFrequency) = maxFrequency}
+     *         {@code minFrequencyHz + frequencyResolutionHz * i}.
+     *     <li>{@code maxFrequencyHz = minFrequencyHz
+     *                                     + frequencyResolutionHz * (maxAmplitudes.length-1)}
      * </ol>
      *
      * @hide
      */
     public static final class FrequencyMapping implements Parcelable {
+        @Nullable
+        private final Range<Float> mFrequencyRangeHz;
         private final float mMinFrequencyHz;
         private final float mResonantFrequencyHz;
         private final float mFrequencyResolutionHz;
-        private final float mSuggestedSafeRangeHz;
         private final float[] mMaxAmplitudes;
 
-        // Relative fields calculated from input values:
-        private final Range<Float> mRelativeFrequencyRange;
-
         FrequencyMapping(Parcel in) {
-            this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(),
-                    in.createFloatArray());
+            this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
         }
 
         /**
          * Default constructor.
          *
-         * @param minFrequencyHz        Minimum supported frequency, in hertz.
          * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
+         * @param minFrequencyHz        Minimum supported frequency, in hertz.
          * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
          *                              amplitudes mapping.
-         * @param suggestedSafeRangeHz  The suggested range, in hertz, for the safe relative
-         *                              frequency range represented by [-1, 1].
          * @param maxAmplitudes         The max amplitude supported by each supported frequency,
          *                              starting at minimum frequency with jumps of frequency
          *                              resolution.
          * @hide
          */
-        public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz,
-                float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) {
+        public FrequencyMapping(float resonantFrequencyHz, float minFrequencyHz,
+                float frequencyResolutionHz, float[] maxAmplitudes) {
             mMinFrequencyHz = minFrequencyHz;
             mResonantFrequencyHz = resonantFrequencyHz;
             mFrequencyResolutionHz = frequencyResolutionHz;
-            mSuggestedSafeRangeHz = suggestedSafeRangeHz;
             mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length];
             if (maxAmplitudes != null) {
                 System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
             }
 
-            float maxFrequencyHz =
-                    minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1);
-            if (Float.isNaN(resonantFrequencyHz) || Float.isNaN(minFrequencyHz)
-                    || Float.isNaN(frequencyResolutionHz) || Float.isNaN(suggestedSafeRangeHz)
-                    || resonantFrequencyHz < minFrequencyHz
-                    || resonantFrequencyHz > maxFrequencyHz) {
-                // Some required fields are undefined or have bad values.
-                // Leave this mapping empty.
-                mRelativeFrequencyRange = Range.create(0f, 0f);
-                return;
-            }
+            // If any required field is undefined then leave this mapping empty.
+            boolean isValid = !Float.isNaN(resonantFrequencyHz)
+                    && !Float.isNaN(minFrequencyHz)
+                    && !Float.isNaN(frequencyResolutionHz)
+                    && (mMaxAmplitudes.length > 0);
 
-            // Calculate actual safe range, limiting the suggested one by the device supported range
-            float safeDelta = MathUtils.min(
-                    suggestedSafeRangeHz / 2,
-                    resonantFrequencyHz - minFrequencyHz,
-                    maxFrequencyHz - resonantFrequencyHz);
-            mRelativeFrequencyRange = Range.create(
-                    (minFrequencyHz - resonantFrequencyHz) / safeDelta,
-                    (maxFrequencyHz - resonantFrequencyHz) / safeDelta);
+            float maxFrequencyHz = isValid
+                    ? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1)
+                    : Float.NaN;
+
+            // If the non-empty mapping does not have min < resonant < max frequency respected
+            // then leave this mapping empty.
+            isValid &= !Float.isNaN(maxFrequencyHz)
+                    && (resonantFrequencyHz >= minFrequencyHz)
+                    && (resonantFrequencyHz <= maxFrequencyHz)
+                    && (minFrequencyHz < maxFrequencyHz);
+
+            mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
         }
 
         /**
-         * Returns true if this frequency mapping is empty, i.e. the only supported relative
-         * frequency is 0 (resonant frequency).
+         * Returns true if this frequency mapping is empty, i.e. the only supported is the resonant
+         * frequency.
          */
         public boolean isEmpty() {
-            return Float.compare(mRelativeFrequencyRange.getLower(),
-                    mRelativeFrequencyRange.getUpper()) == 0;
+            return mFrequencyRangeHz == null;
         }
 
         /**
-         * Returns the frequency value in hertz that is mapped to the given relative frequency.
+         * Returns the maximum relative amplitude the vibrator can reach while playing at the
+         * given frequency.
          *
-         * @return The mapped frequency, in hertz, or {@link Float#NaN} is value outside the device
-         * supported range.
+         * @param frequencyHz frequency, in hertz, for query.
+         * @return A value in [0,1] representing the max relative amplitude supported at the given
+         * frequency. This will return 0 if the frequency is outside the supported range, or if the
+         * mapping is empty.
          */
-        public float toHertz(float relativeFrequency) {
-            if (!mRelativeFrequencyRange.contains(relativeFrequency)) {
-                return Float.NaN;
-            }
-            float relativeMinFrequency = mRelativeFrequencyRange.getLower();
-            if (Float.compare(relativeMinFrequency, 0) == 0) {
-                // relative supported range is [0,0], so toHertz(0) should be the resonant frequency
-                return mResonantFrequencyHz;
-            }
-            float shift = (mMinFrequencyHz - mResonantFrequencyHz) / relativeMinFrequency;
-            return mResonantFrequencyHz + relativeFrequency * shift;
-        }
-
-        /**
-         * Returns the maximum amplitude the vibrator can reach while playing at given relative
-         * frequency.
-         *
-         * @return A value in [0,1] representing the max amplitude supported at given relative
-         * frequency. This will return 0 if frequency is outside supported range, or if max
-         * amplitude mapping is empty.
-         */
-        public float getMaxAmplitude(float relativeFrequency) {
-            float frequencyHz = toHertz(relativeFrequency);
-            if (Float.isNaN(frequencyHz)) {
+        public float getMaxAmplitude(float frequencyHz) {
+            if (isEmpty() || Float.isNaN(frequencyHz)) {
                 // Unsupported frequency requested, vibrator cannot play at this frequency.
                 return 0;
             }
@@ -603,13 +546,6 @@
             int floorIndex = (int) Math.floor(position);
             int ceilIndex = (int) Math.ceil(position);
             if (floorIndex < 0 || floorIndex >= mMaxAmplitudes.length) {
-                if (mMaxAmplitudes.length > 0) {
-                    // This should never happen if the setup of relative frequencies was correct.
-                    Log.w(TAG, "Max amplitudes has " + mMaxAmplitudes.length
-                            + " entries and was expected to cover the frequency " + frequencyHz
-                            + " Hz when starting at min frequency of " + mMinFrequencyHz
-                            + " Hz with resolution of " + mFrequencyResolutionHz + " Hz.");
-                }
                 return 0;
             }
             if (floorIndex != ceilIndex && ceilIndex < mMaxAmplitudes.length) {
@@ -621,10 +557,9 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeFloat(mMinFrequencyHz);
             dest.writeFloat(mResonantFrequencyHz);
+            dest.writeFloat(mMinFrequencyHz);
             dest.writeFloat(mFrequencyResolutionHz);
-            dest.writeFloat(mSuggestedSafeRangeHz);
             dest.writeFloatArray(mMaxAmplitudes);
         }
 
@@ -645,14 +580,13 @@
             return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
                     && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
                     && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
-                    && Float.compare(mSuggestedSafeRangeHz, that.mSuggestedSafeRangeHz) == 0
                     && Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes);
         }
 
         @Override
         public int hashCode() {
             int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
-                    mFrequencyResolutionHz, mSuggestedSafeRangeHz);
+                    mFrequencyResolutionHz);
             hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
             return hashCode;
         }
@@ -660,13 +594,10 @@
         @Override
         public String toString() {
             return "FrequencyMapping{"
-                    + "mRelativeFrequencyRange=" + mRelativeFrequencyRange
+                    + "mFrequencyRange=" + mFrequencyRangeHz
                     + ", mMinFrequency=" + mMinFrequencyHz
                     + ", mResonantFrequency=" + mResonantFrequencyHz
-                    + ", mMaxFrequency="
-                    + (mMinFrequencyHz + mFrequencyResolutionHz * (mMaxAmplitudes.length - 1))
                     + ", mFrequencyResolution=" + mFrequencyResolutionHz
-                    + ", mSuggestedSafeRange=" + mSuggestedSafeRangeHz
                     + ", mMaxAmplitudes count=" + mMaxAmplitudes.length
                     + '}';
         }
@@ -699,7 +630,7 @@
         private int mPwleSizeMax;
         private float mQFactor = Float.NaN;
         private FrequencyMapping mFrequencyMapping =
-                new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
+                new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
 
         /** A builder class for a {@link VibratorInfo}. */
         public Builder(int id) {
diff --git a/core/java/android/os/WakeLockStats.aidl b/core/java/android/os/WakeLockStats.aidl
new file mode 100644
index 0000000..be08d78
--- /dev/null
+++ b/core/java/android/os/WakeLockStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable WakeLockStats;
diff --git a/core/java/android/os/WakeLockStats.java b/core/java/android/os/WakeLockStats.java
new file mode 100644
index 0000000..05a7313
--- /dev/null
+++ b/core/java/android/os/WakeLockStats.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of wake lock stats.
+ *  @hide
+ */
+public final class WakeLockStats implements Parcelable {
+
+    /** @hide */
+    public static class WakeLock {
+        public final int uid;
+        @NonNull
+        public final String name;
+        public final int timesAcquired;
+        public final long totalTimeHeldMs;
+
+        /**
+         * Time in milliseconds that the lock has been held or 0 if not currently holding the lock
+         */
+        public final long timeHeldMs;
+
+        public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
+                long timeHeldMs) {
+            this.uid = uid;
+            this.name = name;
+            this.timesAcquired = timesAcquired;
+            this.totalTimeHeldMs = totalTimeHeldMs;
+            this.timeHeldMs = timeHeldMs;
+        }
+
+        private WakeLock(Parcel in) {
+            uid = in.readInt();
+            name = in.readString();
+            timesAcquired = in.readInt();
+            totalTimeHeldMs = in.readLong();
+            timeHeldMs = in.readLong();
+        }
+
+        private void writeToParcel(Parcel out) {
+            out.writeInt(uid);
+            out.writeString(name);
+            out.writeInt(timesAcquired);
+            out.writeLong(totalTimeHeldMs);
+            out.writeLong(timeHeldMs);
+        }
+
+        @Override
+        public String toString() {
+            return "WakeLock{"
+                    + "uid=" + uid
+                    + ", name='" + name + '\''
+                    + ", timesAcquired=" + timesAcquired
+                    + ", totalTimeHeldMs=" + totalTimeHeldMs
+                    + ", timeHeldMs=" + timeHeldMs
+                    + '}';
+        }
+    }
+
+    private final List<WakeLock> mWakeLocks;
+
+    /** @hide **/
+    public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
+        mWakeLocks = wakeLocks;
+    }
+
+    @NonNull
+    public List<WakeLock> getWakeLocks() {
+        return mWakeLocks;
+    }
+
+    private WakeLockStats(Parcel in) {
+        final int size = in.readInt();
+        mWakeLocks = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            mWakeLocks.add(new WakeLock(in));
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        final int size = mWakeLocks.size();
+        out.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            WakeLock stats = mWakeLocks.get(i);
+            stats.writeToParcel(out);
+        }
+    }
+
+    @NonNull
+    public static final Creator<WakeLockStats> CREATOR =
+            new Creator<WakeLockStats>() {
+                public WakeLockStats createFromParcel(Parcel in) {
+                    return new WakeLockStats(in);
+                }
+
+                public WakeLockStats[] newArray(int size) {
+                    return new WakeLockStats[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "WakeLockStats " + mWakeLocks;
+    }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5e4057b..8df659d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -80,6 +80,7 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.sysprop.VoldProperties;
@@ -1443,28 +1444,39 @@
      *
      * @hide
      */
-    public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+    public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+    /** {@hide} */
+    @TestApi
+    public static final String
+            STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
     /**
      * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
-     * in low free space category.
+     * in low free space category and can be configured via
+     * Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE.
      *
      * @hide
      */
-    public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5;
+    public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5;
     /**
      * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
      * allocated for cache.
      *
      * @hide
      */
-    public static final int CACHE_RESERVE_PERCENT_HIGH = 10;
+    public static final int DEFAULT_CACHE_RESERVE_PERCENT_HIGH = 10;
+    /** {@hide} */
+    @TestApi
+    public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
     /**
      * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
      * allocated for cache.
      *
      * @hide
      */
-    public static final int CACHE_RESERVE_PERCENT_LOW = 2;
+    public static final int DEFAULT_CACHE_RESERVE_PERCENT_LOW = 2;
+    /** {@hide} */
+    @TestApi
+    public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
 
     private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
 
@@ -1490,7 +1502,8 @@
     @UnsupportedAppUsage
     public long getStorageLowBytes(File path) {
         final long lowPercent = Settings.Global.getInt(mResolver,
-                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW);
+                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+                DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW);
         final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
 
         final long maxLowBytes = Settings.Global.getLong(mResolver,
@@ -1510,25 +1523,35 @@
     @TestApi
     @SuppressLint("StreamFiles")
     public long computeStorageCacheBytes(@NonNull File path) {
+        final int storageThresholdPercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                STORAGE_THRESHOLD_PERCENT_HIGH_KEY, DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+        final int cacheReservePercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                CACHE_RESERVE_PERCENT_HIGH_KEY, DEFAULT_CACHE_RESERVE_PERCENT_HIGH);
+        final int cacheReservePercentLow = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                CACHE_RESERVE_PERCENT_LOW_KEY, DEFAULT_CACHE_RESERVE_PERCENT_LOW);
         final long totalBytes = path.getTotalSpace();
         final long usableBytes = path.getUsableSpace();
-        final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100;
+        final long storageThresholdHighBytes = totalBytes * storageThresholdPercentHigh / 100;
         final long storageThresholdLowBytes = getStorageLowBytes(path);
         long result;
         if (usableBytes > storageThresholdHighBytes) {
-            // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space,
-            // reserve CACHE_RESERVE_PERCENT_HIGH of total space
-            result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100;
+            // If free space is >storageThresholdPercentHigh of total space,
+            // reserve cacheReservePercentHigh of total space
+            result = totalBytes * cacheReservePercentHigh / 100;
         } else if (usableBytes < storageThresholdLowBytes) {
-            // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB),
-            // reserve CACHE_RESERVE_PERCENT_LOW of total space
-            result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
+            // If free space is <min(storageThresholdPercentLow of total space, 500MB),
+            // reserve cacheReservePercentLow of total space
+            result = totalBytes * cacheReservePercentLow / 100;
         } else {
             // Else, linearly interpolate the amount of space to reserve
-            result = ((CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW)
-                      * (usableBytes - storageThresholdHighBytes) + CACHE_RESERVE_PERCENT_HIGH
-                      * (storageThresholdHighBytes - storageThresholdLowBytes)) * totalBytes
-                      / (100 * (storageThresholdHighBytes - storageThresholdLowBytes));
+            double slope = (cacheReservePercentHigh - cacheReservePercentLow) * totalBytes
+                    / (100.0 * (storageThresholdHighBytes - storageThresholdLowBytes));
+            double intercept = totalBytes * cacheReservePercentLow / 100.0
+                    - storageThresholdLowBytes * slope;
+            result = Math.round(slope * usableBytes + intercept);
         }
         return result;
     }
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 19e04e6..8ee52c2 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -99,7 +99,7 @@
     @UnsupportedAppUsage
     private final boolean mRemovable;
     private final boolean mEmulated;
-    private final boolean mStub;
+    private final boolean mExternallyManaged;
     private final boolean mAllowMassStorage;
     private final long mMaxFileSize;
     private final UserHandle mOwner;
@@ -138,7 +138,7 @@
 
     /** {@hide} */
     public StorageVolume(String id, File path, File internalPath, String description,
-            boolean primary, boolean removable, boolean emulated, boolean stub,
+            boolean primary, boolean removable, boolean emulated, boolean externallyManaged,
             boolean allowMassStorage, long maxFileSize, UserHandle owner, UUID uuid, String fsUuid,
             String state) {
         mId = Preconditions.checkNotNull(id);
@@ -148,7 +148,7 @@
         mPrimary = primary;
         mRemovable = removable;
         mEmulated = emulated;
-        mStub = stub;
+        mExternallyManaged = externallyManaged;
         mAllowMassStorage = allowMassStorage;
         mMaxFileSize = maxFileSize;
         mOwner = Preconditions.checkNotNull(owner);
@@ -165,7 +165,7 @@
         mPrimary = in.readInt() != 0;
         mRemovable = in.readInt() != 0;
         mEmulated = in.readInt() != 0;
-        mStub = in.readInt() != 0;
+        mExternallyManaged = in.readInt() != 0;
         mAllowMassStorage = in.readInt() != 0;
         mMaxFileSize = in.readLong();
         mOwner = in.readParcelable(null, android.os.UserHandle.class);
@@ -275,13 +275,13 @@
     }
 
     /**
-     * Returns true if the volume is a stub volume (a volume managed from outside Android).
+     * Returns true if the volume is managed from outside Android.
      *
      * @hide
      */
     @SystemApi
-    public boolean isStub() {
-        return mStub;
+    public boolean isExternallyManaged() {
+        return mExternallyManaged;
     }
 
     /**
@@ -520,7 +520,7 @@
         pw.printPair("mPrimary", mPrimary);
         pw.printPair("mRemovable", mRemovable);
         pw.printPair("mEmulated", mEmulated);
-        pw.printPair("mStub", mStub);
+        pw.printPair("mExternallyManaged", mExternallyManaged);
         pw.printPair("mAllowMassStorage", mAllowMassStorage);
         pw.printPair("mMaxFileSize", mMaxFileSize);
         pw.printPair("mOwner", mOwner);
@@ -555,7 +555,7 @@
         parcel.writeInt(mPrimary ? 1 : 0);
         parcel.writeInt(mRemovable ? 1 : 0);
         parcel.writeInt(mEmulated ? 1 : 0);
-        parcel.writeInt(mStub ? 1 : 0);
+        parcel.writeInt(mExternallyManaged ? 1 : 0);
         parcel.writeInt(mAllowMassStorage ? 1 : 0);
         parcel.writeLong(mMaxFileSize);
         parcel.writeParcelable(mOwner, flags);
@@ -637,7 +637,7 @@
                     mPrimary,
                     mRemovable,
                     mEmulated,
-                    /* stub= */ false,
+                    /* externallyManaged= */ false,
                     /* allowMassStorage= */ false,
                     /* maxFileSize= */ 0,
                     mOwner,
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index ebd143c..84b6b5e 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -404,7 +404,7 @@
 
         final boolean removable;
         final boolean emulated;
-        final boolean stub = type == TYPE_STUB;
+        final boolean externallyManaged = type == TYPE_STUB;
         final boolean allowMassStorage = false;
         final String envState = reportUnmounted
                 ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
@@ -460,8 +460,8 @@
         }
 
         return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable,
-                emulated, stub, allowMassStorage, maxFileSize, new UserHandle(userId), uuid,
-                derivedFsUuid, envState);
+                emulated, externallyManaged, allowMassStorage, maxFileSize, new UserHandle(userId),
+                uuid, derivedFsUuid, envState);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index eac09aa..1e5cdd7 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -105,7 +105,7 @@
         final boolean primary = false;
         final boolean removable = true;
         final boolean emulated = false;
-        final boolean stub = false;
+        final boolean externallyManaged = false;
         final boolean allowMassStorage = false;
         final long maxFileSize = 0;
         final UserHandle user = new UserHandle(UserHandle.USER_NULL);
@@ -117,8 +117,8 @@
         }
 
         return new StorageVolume(id, userPath, internalPath, description, primary, removable,
-                emulated, stub, allowMassStorage, maxFileSize, user, null /* uuid */, fsUuid,
-                envState);
+                emulated, externallyManaged, allowMassStorage, maxFileSize, user, null /* uuid */,
+                fsUuid, envState);
     }
 
     public void dump(IndentingPrintWriter pw) {
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index 3ec5636..9e1f636 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -29,14 +29,20 @@
  * Representation of {@link VibrationEffectSegment} that ramps vibration amplitude and/or frequency
  * for a specified duration.
  *
+ * <p>The amplitudes are expressed by float values in the range [0, 1], representing the relative
+ * output acceleration for the vibrator. The frequencies are expressed in hertz by positive finite
+ * float values. The special value zero is used here for an unspecified frequency, and will be
+ * automatically mapped to the device's default vibration frequency (usually the resonant
+ * frequency).
+ *
  * @hide
  */
 @TestApi
 public final class RampSegment extends VibrationEffectSegment {
     private final float mStartAmplitude;
-    private final float mStartFrequency;
+    private final float mStartFrequencyHz;
     private final float mEndAmplitude;
-    private final float mEndFrequency;
+    private final float mEndFrequencyHz;
     private final int mDuration;
 
     RampSegment(@NonNull Parcel in) {
@@ -44,12 +50,12 @@
     }
 
     /** @hide */
-    public RampSegment(float startAmplitude, float endAmplitude, float startFrequency,
-            float endFrequency, int duration) {
+    public RampSegment(float startAmplitude, float endAmplitude, float startFrequencyHz,
+            float endFrequencyHz, int duration) {
         mStartAmplitude = startAmplitude;
         mEndAmplitude = endAmplitude;
-        mStartFrequency = startFrequency;
-        mEndFrequency = endFrequency;
+        mStartFrequencyHz = startFrequencyHz;
+        mEndFrequencyHz = endFrequencyHz;
         mDuration = duration;
     }
 
@@ -61,8 +67,8 @@
         RampSegment other = (RampSegment) o;
         return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0
                 && Float.compare(mEndAmplitude, other.mEndAmplitude) == 0
-                && Float.compare(mStartFrequency, other.mStartFrequency) == 0
-                && Float.compare(mEndFrequency, other.mEndFrequency) == 0
+                && Float.compare(mStartFrequencyHz, other.mStartFrequencyHz) == 0
+                && Float.compare(mEndFrequencyHz, other.mEndFrequencyHz) == 0
                 && mDuration == other.mDuration;
     }
 
@@ -74,12 +80,12 @@
         return mEndAmplitude;
     }
 
-    public float getStartFrequency() {
-        return mStartFrequency;
+    public float getStartFrequencyHz() {
+        return mStartFrequencyHz;
     }
 
-    public float getEndFrequency() {
-        return mEndFrequency;
+    public float getEndFrequencyHz() {
+        return mEndFrequencyHz;
     }
 
     @Override
@@ -102,6 +108,12 @@
     /** @hide */
     @Override
     public void validate() {
+        Preconditions.checkArgumentNonNegative(mStartFrequencyHz,
+                "Frequencies must all be >= 0, got start frequency of " + mStartFrequencyHz);
+        Preconditions.checkArgumentFinite(mStartFrequencyHz, "startFrequencyHz");
+        Preconditions.checkArgumentNonNegative(mEndFrequencyHz,
+                "Frequencies must all be >= 0, got end frequency of " + mEndFrequencyHz);
+        Preconditions.checkArgumentFinite(mEndFrequencyHz, "endFrequencyHz");
         Preconditions.checkArgumentNonnegative(mDuration,
                 "Durations must all be >= 0, got " + mDuration);
         Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
@@ -126,7 +138,8 @@
                 && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
             return this;
         }
-        return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequency, mEndFrequency,
+        return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+                mEndFrequencyHz,
                 mDuration);
     }
 
@@ -139,7 +152,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequency, mEndFrequency,
+        return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz,
                 mDuration);
     }
 
@@ -147,8 +160,8 @@
     public String toString() {
         return "Ramp{startAmplitude=" + mStartAmplitude
                 + ", endAmplitude=" + mEndAmplitude
-                + ", startFrequency=" + mStartFrequency
-                + ", endFrequency=" + mEndFrequency
+                + ", startFrequencyHz=" + mStartFrequencyHz
+                + ", endFrequencyHz=" + mEndFrequencyHz
                 + ", duration=" + mDuration
                 + "}";
     }
@@ -163,8 +176,8 @@
         out.writeInt(PARCEL_TOKEN_RAMP);
         out.writeFloat(mStartAmplitude);
         out.writeFloat(mEndAmplitude);
-        out.writeFloat(mStartFrequency);
-        out.writeFloat(mEndFrequency);
+        out.writeFloat(mStartFrequencyHz);
+        out.writeFloat(mEndFrequencyHz);
         out.writeInt(mDuration);
     }
 
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 69a381f..c679511 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -30,12 +30,18 @@
  * Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude and
  * frequency for a specified duration.
  *
+ * <p>The amplitude is expressed by a float value in the range [0, 1], representing the relative
+ * output acceleration for the vibrator. The frequency is expressed in hertz by a positive finite
+ * float value. The special value zero is used here for an unspecified frequency, and will be
+ * automatically mapped to the device's default vibration frequency (usually the resonant
+ * frequency).
+ *
  * @hide
  */
 @TestApi
 public final class StepSegment extends VibrationEffectSegment {
     private final float mAmplitude;
-    private final float mFrequency;
+    private final float mFrequencyHz;
     private final int mDuration;
 
     StepSegment(@NonNull Parcel in) {
@@ -43,9 +49,9 @@
     }
 
     /** @hide */
-    public StepSegment(float amplitude, float frequency, int duration) {
+    public StepSegment(float amplitude, float frequencyHz, int duration) {
         mAmplitude = amplitude;
-        mFrequency = frequency;
+        mFrequencyHz = frequencyHz;
         mDuration = duration;
     }
 
@@ -56,7 +62,7 @@
         }
         StepSegment other = (StepSegment) o;
         return Float.compare(mAmplitude, other.mAmplitude) == 0
-                && Float.compare(mFrequency, other.mFrequency) == 0
+                && Float.compare(mFrequencyHz, other.mFrequencyHz) == 0
                 && mDuration == other.mDuration;
     }
 
@@ -64,8 +70,8 @@
         return mAmplitude;
     }
 
-    public float getFrequency() {
-        return mFrequency;
+    public float getFrequencyHz() {
+        return mFrequencyHz;
     }
 
     @Override
@@ -89,6 +95,9 @@
     /** @hide */
     @Override
     public void validate() {
+        Preconditions.checkArgumentNonNegative(mFrequencyHz,
+                "Frequencies must all be >= 0, got " + mFrequencyHz);
+        Preconditions.checkArgumentFinite(mFrequencyHz, "frequencyHz");
         Preconditions.checkArgumentNonnegative(mDuration,
                 "Durations must all be >= 0, got " + mDuration);
         if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
@@ -108,7 +117,8 @@
         if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
             return this;
         }
-        return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, mFrequency,
+        return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE,
+                mFrequencyHz,
                 mDuration);
     }
 
@@ -119,7 +129,7 @@
         if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
             return this;
         }
-        return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequency,
+        return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz,
                 mDuration);
     }
 
@@ -132,13 +142,13 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mAmplitude, mFrequency, mDuration);
+        return Objects.hash(mAmplitude, mFrequencyHz, mDuration);
     }
 
     @Override
     public String toString() {
         return "Step{amplitude=" + mAmplitude
-                + ", frequency=" + mFrequency
+                + ", frequencyHz=" + mFrequencyHz
                 + ", duration=" + mDuration
                 + "}";
     }
@@ -152,7 +162,7 @@
     public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeInt(PARCEL_TOKEN_STEP);
         out.writeFloat(mAmplitude);
-        out.writeFloat(mFrequency);
+        out.writeFloat(mFrequencyHz);
         out.writeInt(mDuration);
     }
 
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
new file mode 100644
index 0000000..4a61472
--- /dev/null
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
+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_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.Vibrator;
+import android.os.Vibrator.VibrationIntensity;
+
+/**
+ * List of device-specific internal vibration configuration loaded from platform config.xml.
+ *
+ * <p>This should not be public, but some individual values are exposed by {@link Vibrator} by
+ * hidden methods, made available to Settings, SysUI and other platform client code. They can also
+ * be individually exposed with the necessary permissions by the {@link Vibrator} service.
+ *
+ * @hide
+ */
+public class VibrationConfig {
+
+    // TODO(b/191150049): move these to vibrator static config file
+    private final float mHapticChannelMaxVibrationAmplitude;
+    private final int mRampStepDurationMs;
+    private final int mRampDownDurationMs;
+
+    @VibrationIntensity
+    private final int mDefaultAlarmVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultHapticFeedbackIntensity;
+    @VibrationIntensity
+    private final int mDefaultMediaVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultNotificationVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultRingVibrationIntensity;
+
+    /** @hide */
+    public VibrationConfig(@Nullable Resources resources) {
+        mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
+                com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
+        mRampDownDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0);
+        mRampStepDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationWaveformRampStepDuration, 0);
+
+        mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
+        mDefaultHapticFeedbackIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
+        mDefaultMediaVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultMediaVibrationIntensity);
+        mDefaultNotificationVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+        mDefaultRingVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultRingVibrationIntensity);
+    }
+
+    @VibrationIntensity
+    private static int loadDefaultIntensity(@Nullable Resources res, int resId) {
+        int defaultIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        int value = loadInteger(res, resId, defaultIntensity);
+        if (value < Vibrator.VIBRATION_INTENSITY_OFF || value > Vibrator.VIBRATION_INTENSITY_HIGH) {
+            return defaultIntensity;
+        }
+        return value;
+    }
+
+    private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) {
+        return res != null ? res.getFloat(resId) : defaultValue;
+    }
+
+    private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) {
+        return res != null ? res.getInteger(resId) : defaultValue;
+    }
+
+    /**
+     * Return the maximum amplitude the vibrator can play using the audio haptic channels.
+     *
+     * @return a positive value representing the maximum absolute value the device can play signals
+     * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
+     */
+    public float getHapticChannelMaximumAmplitude() {
+        if (mHapticChannelMaxVibrationAmplitude <= 0) {
+            return Float.NaN;
+        }
+        return mHapticChannelMaxVibrationAmplitude;
+    }
+
+    /**
+     * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator
+     * when a vibration is cancelled or finished at non-zero amplitude.
+     */
+    public int getRampDownDurationMs() {
+        if (mRampDownDurationMs < 0) {
+            return 0;
+        }
+        return mRampDownDurationMs;
+    }
+
+    /**
+     * The duration, in milliseconds, that should be applied to convert vibration effect's
+     * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on
+     * devices without PWLE support.
+     */
+    public int getRampStepDurationMs() {
+        if (mRampStepDurationMs < 0) {
+            return 0;
+        }
+        return mRampStepDurationMs;
+    }
+
+    /** Get the default vibration intensity for given usage. */
+    @VibrationIntensity
+    public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
+        switch (usage) {
+            case USAGE_ALARM:
+                return mDefaultAlarmVibrationIntensity;
+            case USAGE_NOTIFICATION:
+            case USAGE_COMMUNICATION_REQUEST:
+                return mDefaultNotificationVibrationIntensity;
+            case USAGE_RINGTONE:
+                return mDefaultRingVibrationIntensity;
+            case USAGE_TOUCH:
+            case USAGE_HARDWARE_FEEDBACK:
+            case USAGE_PHYSICAL_EMULATION:
+            case USAGE_ACCESSIBILITY:
+                return mDefaultHapticFeedbackIntensity;
+            case USAGE_MEDIA:
+            case USAGE_UNKNOWN:
+                // fall through
+            default:
+                return mDefaultMediaVibrationIntensity;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "VibrationConfig{"
+                + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+                + ", mRampStepDurationMs=" + mRampStepDurationMs
+                + ", mRampDownDurationMs=" + mRampDownDurationMs
+                + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity
+                + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity
+                + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
+                + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
+                + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+                + "}";
+    }
+}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 66e1c5a..c9dd06c 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -54,4 +54,11 @@
     void getGroupOfPlatformPermission(
             in String permissionName,
             in AndroidFuture<String> callback);
+    void getUnusedAppCount(
+            in AndroidFuture callback);
+    void getHibernationEligibility(
+                in String packageName,
+                in AndroidFuture callback);
+    void revokeOwnPermissionsOnKill(in String packageName, in List<String> permissions,
+            in AndroidFuture callback);
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 4a94c32..1c0320e 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -67,6 +67,8 @@
     void revokeRuntimePermission(String packageName, String permissionName, int userId,
             String reason);
 
+    void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
+
     boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int userId);
 
@@ -74,6 +76,8 @@
 
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
+    void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions);
+
     void startOneTimePermissionSession(String packageName, int userId, long timeout,
             int importanceToResetTimer, int importanceToKeepSessionAlive);
 
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 00f9e45..0cf06aa 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -69,6 +69,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
+import java.util.function.IntConsumer;
 
 /**
  * Interface for communicating with the permission controller.
@@ -127,6 +128,51 @@
     /** Count and app even if it is a system app. */
     public static final int COUNT_WHEN_SYSTEM = 2;
 
+    /** @hide */
+    @IntDef(prefix = { "HIBERNATION_ELIGIBILITY_"}, value = {
+            HIBERNATION_ELIGIBILITY_UNKNOWN,
+            HIBERNATION_ELIGIBILITY_ELIGIBLE,
+            HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM,
+            HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HibernationEligibilityFlag {}
+
+    /**
+     * Unknown whether package is eligible for hibernation.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int HIBERNATION_ELIGIBILITY_UNKNOWN = -1;
+
+    /**
+     * Package is eligible for app hibernation and may be hibernated when the job runs.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int HIBERNATION_ELIGIBILITY_ELIGIBLE = 0;
+
+    /**
+     * Package is not eligible for app hibernation because it is categorically exempt via the
+     * system.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM = 1;
+
+    /**
+     * Package is not eligible for app hibernation because it has been exempt by the user's
+     * preferences. Note that this should not be set if the package is exempt from hibernation by
+     * the system as the user preference would have no effect.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER = 2;
+
     /**
      * Callback for delivering the result of {@link #revokeRuntimePermissions}.
      */
@@ -786,4 +832,103 @@
             }
         }, executor);
     }
+
+    /**
+     * Get the number of unused, hibernating apps for the user.
+     *
+     * @param executor executor to run callback on
+     * @param callback callback for when result is generated
+     */
+    public void getUnusedAppCount(@NonNull @CallbackExecutor Executor executor,
+            @NonNull IntConsumer callback) {
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        mRemoteService.postAsync(service -> {
+            AndroidFuture<Integer> unusedAppCountResult = new AndroidFuture<>();
+            service.getUnusedAppCount(unusedAppCountResult);
+            return unusedAppCountResult;
+        }).whenCompleteAsync((count, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Error getting unused app count", err);
+                callback.accept(0);
+            } else {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    callback.accept((int) count);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    /**
+     * Get the hibernation eligibility of a package. See {@link HibernationEligibilityFlag}.
+     *
+     * @param packageName package name to check eligibility
+     * @param executor executor to run callback on
+     * @param callback callback for when result is generated
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION)
+    public void getHibernationEligibility(@NonNull String packageName,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull IntConsumer callback) {
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        mRemoteService.postAsync(service -> {
+            AndroidFuture<Integer> eligibilityResult = new AndroidFuture<>();
+            service.getHibernationEligibility(packageName, eligibilityResult);
+            return eligibilityResult;
+        }).whenCompleteAsync((eligibility, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Error getting hibernation eligibility", err);
+                callback.accept(HIBERNATION_ELIGIBILITY_UNKNOWN);
+            } else {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    callback.accept(eligibility);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }, executor);
+    }
+
+    /**
+     * Triggers the revocation of one or more permissions for a package, under the following
+     * conditions:
+     * <ul>
+     * <li>The package {@code packageName} must be under the same UID as the calling process
+     * (typically, the target package is the calling package).
+     * <li>Each permission in {@code permissions} must be granted to the package
+     * {@code packageName}.
+     * <li>Each permission in {@code permissions} must be a runtime permission.
+     * </ul>
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. This revocation happens asynchronously and kills all processes running in the
+     * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     * @param callback Callback called when the revocation request has been completed.
+     *
+     * @see Context#revokeOwnPermissionsOnKill(Collection)
+     *
+     * @hide
+     */
+    public void revokeOwnPermissionsOnKill(@NonNull String packageName,
+            @NonNull List<String> permissions, AndroidFuture<Void> callback) {
+        mRemoteService.postAsync(service -> {
+            service.revokeOwnPermissionsOnKill(packageName, permissions, callback);
+            return callback;
+        }).whenComplete((result, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Failed to self revoke " + String.join(",", permissions)
+                        + " for package " + packageName, err);
+            }
+        });
+    }
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 8854e27..8d9f82b 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -324,6 +324,27 @@
             @NonNull Consumer<String> callback) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
+
+    /**
+     * Triggers the revocation of one or more permissions for a package. This should only be called
+     * at the request of {@code packageName}.
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. This revocation happens asynchronously and kills all processes running in the
+     * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     * @param callback Callback waiting for operation to be complete.
+     *
+     * @see PermissionManager#revokeOwnPermissionsOnKill(java.util.Collection)
+     */
+    @BinderThread
+    public void onRevokeOwnPermissionsOnKill(@NonNull String packageName,
+            @NonNull List<String> permissions, @NonNull Runnable callback) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
     /**
      * Get a user-readable sentence, describing the set of privileges that are to be granted to a
      * companion app managing a device of the given profile.
@@ -340,6 +361,36 @@
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
+    /**
+     * Get the count of unused, hibernating apps on the device.
+     *
+     * @param callback callback after count is retrieved
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION)
+    @NonNull
+    public void onGetUnusedAppCount(@NonNull IntConsumer callback) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
+    /**
+     * Get the hibernation eligibility of the app. See
+     * {@link android.permission.PermissionControllerManager.HibernationEligibilityFlag}.
+     *
+     * @param packageName package to check eligibility
+     * @param callback callback after eligibility is returned
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION)
+    public void onGetHibernationEligibility(@NonNull String packageName,
+            @NonNull IntConsumer callback) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
     @Override
     public final @NonNull IBinder onBind(Intent intent) {
         return new IPermissionController.Stub() {
@@ -618,6 +669,50 @@
                     callback.completeExceptionally(t);
                 }
             }
+
+            @Override
+            public void getUnusedAppCount(@NonNull AndroidFuture callback) {
+                try {
+                    Objects.requireNonNull(callback);
+
+                    enforceSomePermissionsGrantedToCaller(
+                            Manifest.permission.MANAGE_APP_HIBERNATION);
+
+                    PermissionControllerService.this.onGetUnusedAppCount(callback::complete);
+                } catch (Throwable t) {
+                    callback.completeExceptionally(t);
+                }
+            }
+
+            @Override
+            public void getHibernationEligibility(@NonNull String packageName,
+                    @NonNull AndroidFuture callback) {
+                try {
+                    Objects.requireNonNull(callback);
+
+                    enforceSomePermissionsGrantedToCaller(
+                            Manifest.permission.MANAGE_APP_HIBERNATION);
+
+                    PermissionControllerService.this.onGetHibernationEligibility(packageName,
+                            callback::complete);
+                } catch (Throwable t) {
+                    callback.completeExceptionally(t);
+                }
+            }
+
+            @Override
+            public void revokeOwnPermissionsOnKill(@NonNull String packageName,
+                    @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
+                try {
+                    enforceSomePermissionsGrantedToCaller(
+                            Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+                    Objects.requireNonNull(callback);
+                    onRevokeOwnPermissionsOnKill(packageName, permissions,
+                            () -> callback.complete(null));
+                } catch (Throwable t) {
+                    callback.completeExceptionally(t);
+                }
+            }
         };
     }
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 61e48c5..e4aee76 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -68,6 +68,7 @@
 import com.android.internal.util.CollectionUtils;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -561,6 +562,19 @@
     }
 
     /**
+     * @see Context#revokeOwnPermissionsOnKill(Collection)
+     * @hide
+     */
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+        try {
+            mPermissionManager.revokeOwnPermissionsOnKill(mContext.getPackageName(),
+                    new ArrayList<String>(permissions));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the state flags associated with a permission.
      *
      * @param packageName the package name for which to get the flags
@@ -1362,6 +1376,26 @@
         return false;
     }
 
+    /**
+     * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
+     * USED in CTS or local tests.
+     *
+     * @param packageName The package to be revoked
+     * @param userId The user for which to revoke
+     *
+     * @hide
+     */
+    @TestApi
+    public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String packageName,
+            int userId) {
+        try {
+            mPermissionManager.revokePostNotificationPermissionWithoutKillForTest(packageName,
+                    userId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     /* @hide */
     private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
         final IActivityManager am = ActivityManager.getService();
@@ -1447,7 +1481,7 @@
             new PropertyInvalidatedCache<PermissionQuery, Integer>(
                     2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
                 @Override
-                protected Integer recompute(PermissionQuery query) {
+                public Integer recompute(PermissionQuery query) {
                     return checkPermissionUncached(query.permission, query.pid, query.uid);
                 }
             };
@@ -1530,12 +1564,12 @@
             new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
                     16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
                 @Override
-                protected Integer recompute(PackageNamePermissionQuery query) {
+                public Integer recompute(PackageNamePermissionQuery query) {
                     return checkPackageNamePermissionUncached(
                             query.permName, query.pkgName, query.userId);
                 }
                 @Override
-                protected boolean bypass(PackageNamePermissionQuery query) {
+                public boolean bypass(PackageNamePermissionQuery query) {
                     return query.userId < 0;
                 }
             };
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 9bdfd8e..9d0c8d8 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -240,7 +240,7 @@
         mTag = parcel.readString();
         mCreationTime = parcel.readLong();
         mCopies = parcel.readInt();
-        Parcelable[] parcelables = parcel.readParcelableArray(null);
+        Parcelable[] parcelables = parcel.readParcelableArray(null, PageRange.class);
         if (parcelables != null) {
             mPageRanges = new PageRange[parcelables.length];
             for (int i = 0; i < parcelables.length; i++) {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index dd2ea81..5d00b29 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -231,7 +231,7 @@
             prefix = { "STATUS_" },
             value = {STATUS_NOT_BLOCKED, STATUS_BLOCKED_IN_LIST, STATUS_BLOCKED_RESTRICTED,
                     STATUS_BLOCKED_UNKNOWN_NUMBER, STATUS_BLOCKED_PAYPHONE,
-                    STATUS_BLOCKED_NOT_IN_CONTACTS})
+                    STATUS_BLOCKED_NOT_IN_CONTACTS, STATUS_BLOCKED_UNAVAILABLE})
     public @interface BlockStatus {}
 
     /**
@@ -277,6 +277,13 @@
     public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
 
     /**
+     * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
+     * because it is from a number not available.
+     * @hide
+     */
+    public static final int STATUS_BLOCKED_UNAVAILABLE = 6;
+
+    /**
      * Integer reason indicating whether a call was blocked, and if so why.
      * @hide
      */
@@ -441,6 +448,9 @@
         /* Preference key for whether should show an emergency call notification. */
         public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
                 "show_emergency_call_notification";
+        /* Preference key of block unavailable calls setting. */
+        public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE =
+                "block_unavailable_calls_setting";
 
         /**
          * Notifies the provider that emergency services were contacted by the user.
@@ -547,6 +557,7 @@
          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
          *        {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
          * @return {@code true} if the setting is enabled. {@code false} otherwise.
          */
@@ -574,6 +585,7 @@
          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
          *        {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
          * @param value the enabled statue of the setting to set.
          */
@@ -603,6 +615,8 @@
                     return "blocked - payphone";
                 case STATUS_BLOCKED_NOT_IN_CONTACTS:
                     return "blocked - not in contacts";
+                case STATUS_BLOCKED_UNAVAILABLE:
+                    return "blocked - unavailable";
             }
             return "unknown";
         }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0cc5bfd..8dca7ab 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -910,6 +910,7 @@
          * <li>{@link #PRESENTATION_RESTRICTED}</li>
          * <li>{@link #PRESENTATION_UNKNOWN}</li>
          * <li>{@link #PRESENTATION_PAYPHONE}</li>
+         * <li>{@link #PRESENTATION_UNAVAILABLE}</li>
          * </ul>
          * </p>
          *
@@ -925,6 +926,8 @@
         public static final int PRESENTATION_UNKNOWN = 3;
         /** Number is a pay phone. */
         public static final int PRESENTATION_PAYPHONE = 4;
+        /** Number is unavailable. */
+        public static final int PRESENTATION_UNAVAILABLE = 5;
 
         /**
          * The ISO 3166-1 two letters country code of the country where the
@@ -2028,6 +2031,10 @@
                 return presentation;
             }
 
+            if (presentation == TelecomManager.PRESENTATION_UNAVAILABLE) {
+                return PRESENTATION_UNAVAILABLE;
+            }
+
             if (TextUtils.isEmpty(number)
                     || presentation == TelecomManager.PRESENTATION_UNKNOWN) {
                 return PRESENTATION_UNKNOWN;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6349cde..87f4489 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -464,6 +464,13 @@
     public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
 
     /**
+     * Namespace for all Supplemental Api related features.
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_SUPPLEMENTAL_API = "supplemental_api";
+
+    /**
      * Namespace for all SurfaceFlinger features that are used at the native level.
      * These features are applied on boot or after reboot.
      *
@@ -687,6 +694,15 @@
     @SystemApi
     public static final String NAMESPACE_UWB = "uwb";
 
+    /**
+     * Namespace for AmbientContextEventManagerService related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE =
+            "ambient_context_manager_service";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 910fec6..3f54408 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -391,6 +391,21 @@
             "android.settings.REDUCE_BRIGHT_COLORS_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Color correction.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_COLOR_CORRECTION_SETTINGS =
+            "com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of Color inversion.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -4588,6 +4603,43 @@
         public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
 
         /**
+         * The intensity of alarm vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String ALARM_VIBRATION_INTENSITY =
+                "alarm_vibration_intensity";
+
+        /**
+         * The intensity of media vibrations, if configurable.
+         *
+         * This includes any vibration that is part of media, such as music, movie, soundtrack,
+         * game or animations.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String MEDIA_VIBRATION_INTENSITY =
+                "media_vibration_intensity";
+
+        /**
          * The intensity of notification vibrations, if configurable.
          *
          * Not all devices are capable of changing their vibration intensity; on these devices
@@ -4604,6 +4656,7 @@
         @Readable
         public static final String NOTIFICATION_VIBRATION_INTENSITY =
                 "notification_vibration_intensity";
+
         /**
          * The intensity of ringtone vibrations, if configurable.
          *
@@ -4655,7 +4708,6 @@
          * 3 - Strong vibrations
          * @hide
          */
-        @Readable
         public static final String HARDWARE_HAPTIC_FEEDBACK_INTENSITY =
                 "hardware_haptic_feedback_intensity";
 
@@ -5068,7 +5120,12 @@
          * It was about AudioManager's setting and thus affected all the applications which
          * relied on the setting, while this is purely about the vibration setting for incoming
          * calls.
+         *
+         * @deprecated Replaced by using {@link android.os.VibrationAttributes#USAGE_RINGTONE} on
+         * vibrations for incoming calls. User settings are applied automatically by the service and
+         * should not be applied by individual apps.
          */
+        @Deprecated
         @Readable
         public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
 
@@ -5129,7 +5186,12 @@
         /**
          * Whether haptic feedback (Vibrate on tap) is enabled. The value is
          * boolean (1 or 0).
+         *
+         * @deprecated Replaced by using {@link android.os.VibrationAttributes#USAGE_TOUCH} on
+         * vibrations. User settings are applied automatically by the service and should not be
+         * applied by individual apps.
          */
+        @Deprecated
         @Readable
         public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
 
@@ -7106,6 +7168,12 @@
         public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy";
 
         /**
+         * Whether or not to show display system location accesses.
+         * @hide
+         */
+        public static final String LOCATION_SHOW_SYSTEM_OPS = "locationShowSystemOps";
+
+        /**
          * A flag containing settings used for biometric weak
          * @hide
          */
@@ -8922,6 +8990,15 @@
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         /**
+         * The current night mode custom type that has been selected by the user.  Owned
+         * and controlled by UiModeManagerService. Constants are as per UiModeManager.
+         * @hide
+         */
+        @Readable
+        @SuppressLint("NoSettingsProvider")
+        public static final String UI_NIGHT_MODE_CUSTOM_TYPE = "ui_night_mode_custom_type";
+
+        /**
          * The current night mode that has been overridden to turn on by the system.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -8986,6 +9063,16 @@
         public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
 
         /**
+         * The complications that are enabled to be shown over the screensaver by the user. Holds
+         * a comma separated list of
+         * {@link com.android.settingslib.dream.DreamBackend.ComplicationType}.
+         *
+         * @hide
+         */
+        public static final String SCREENSAVER_ENABLED_COMPLICATIONS =
+                "screensaver_enabled_complications";
+
+        /**
          * The default NFC payment component
          * @hide
          */
@@ -9934,6 +10021,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String NOTIFICATION_PERMISSION_ENABLED =
                 "notification_permission_enabled";
 
@@ -10096,6 +10184,29 @@
          * category, formatted as a serialized {@link org.json.JSONObject}. If there is no
          * corresponding package included for a category, then all overlay packages in that
          * category must be disabled.
+         *
+         * A few category keys have special meaning and are used for Material You theming.
+         *
+         * A {@code FabricatedOverlay} containing Material You tonal palettes will be generated
+         * in case {@code android.theme.customization.system_palette} contains a
+         * {@link android.annotation.ColorInt}.
+         *
+         * The strategy used for generating the tonal palettes can be defined with the
+         * {@code android.theme.customization.theme_style} key, with one of the following options:
+         * <ul>
+         *   <li> TONAL_SPOT = Default Material You theme since Android S.</li>
+         *   <li> VIBRANT = Theme where accent 2 and 3 are analogous to accent 1.</li>
+         *   <li> EXPRESSIVE = Highly chromatic theme.</li>
+         *   <li> SPRITZ = Desaturated theme, almost greyscale.</li>
+         * </ul>
+         *
+         * Example of valid fabricated theme specification:
+         * <pre>
+         * {
+         *     "android.theme.customization.system_palette":"B1611C",
+         *     "android.theme.customization.theme_style":"EXPRESSIVE"
+         * }
+         * </pre>
          * @hide
          */
         @SystemApi
@@ -10104,6 +10215,14 @@
                 "theme_customization_overlay_packages";
 
         /**
+         * Indicates whether the device is in kids nav mode.
+         * <p>Type: int (0 for false, 1 for true)
+         *
+         * @hide
+         */
+        public static final String NAV_BAR_KIDS_MODE = "nav_bar_kids_mode";
+
+        /**
          * Navigation bar mode.
          *  0 = 3 button
          *  1 = 2 button
@@ -10225,6 +10344,13 @@
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3;
 
         /**
+         * Whether the following typing focus feature for magnification is enabled.
+         * @hide
+         */
+        public static final String ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED =
+                "accessibility_magnification_follow_typing_enabled";
+
+        /**
          * Controls magnification capability. Accessibility magnification is capable of at least one
          * of the magnification modes.
          *
@@ -10434,7 +10560,7 @@
                 DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
         })
         @Retention(RetentionPolicy.SOURCE)
-        @interface DeviceStateRotationLockSetting {
+        public @interface DeviceStateRotationLockSetting {
         }
 
         /**
@@ -10467,13 +10593,6 @@
         public static final String COMMUNAL_MODE_ENABLED = "communal_mode_enabled";
 
         /**
-         * An array of all the packages which have been enabled for hub mode by the user.
-         *
-         * @hide
-         */
-        public static final String COMMUNAL_MODE_PACKAGES = "communal_mode_packages";
-
-        /**
          * An array of SSIDs of Wi-Fi networks that, when connected, are considered safe to enable
          * the communal mode.
          *
@@ -10483,6 +10602,14 @@
                 "communal_mode_trusted_networks";
 
         /**
+         * Setting to allow Fast Pair scans to be enabled.
+         * @hide
+         */
+        @SystemApi
+        @Readable
+        public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
@@ -12714,16 +12841,6 @@
                 SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
 
         /**
-         * Maximum bytes of storage on the device that is reserved for cached
-         * data.
-         *
-         * @hide
-         */
-        @Readable
-        public static final String
-                SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
-
-        /**
          * The maximum reconnect delay for short network outages or when the
          * network is suspended due to phone use.
          *
@@ -15201,7 +15318,10 @@
          * {@code p1[url_bar]:p2:p3[url_foo,url_bas]}
          *
          * @hide
+         * @deprecated Use {@link android.view.autofill.AutofillManager
+         * #DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES} instead.
          */
+        @Deprecated
         @SystemApi
         @Readable
         public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
new file mode 100644
index 0000000..dccfe36
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.util.Slog;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for {@link AmbientContextEvent} detection service.
+ *
+ * <p> A service that provides requested ambient context events to the system.
+ * The system's default AmbientContextDetectionService implementation is configured in
+ * {@code config_defaultAmbientContextDetectionService}. If this config has no value, a stub is
+ * returned.
+ *
+ * See: {@code AmbientContextManagerService}.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourAmbientContextDetectionService"
+ *          android:permission="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AmbientContextDetectionService extends Service {
+    private static final String TAG = AmbientContextDetectionService.class.getSimpleName();
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the
+     * {@link android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.ambientcontext.AmbientContextDetectionService";
+
+    /**
+     * The key for the bundle the parameter of {@code RemoteCallback#sendResult}. Implementation
+     * should set bundle result with this key.
+     *
+     * @hide
+     */
+    public static final String RESPONSE_BUNDLE_KEY =
+            "android.service.ambientcontext.EventResponseKey";
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return new IAmbientContextDetectionService.Stub() {
+                /** {@inheritDoc} */
+                @Override
+                public void startDetection(
+                        @NonNull AmbientContextEventRequest request, String packageName,
+                        RemoteCallback callback) {
+                    Objects.requireNonNull(request);
+                    Objects.requireNonNull(callback);
+                    Consumer<AmbientContextEventResponse> consumer =
+                            response -> {
+                                Bundle bundle = new Bundle();
+                                bundle.putParcelable(
+                                        AmbientContextDetectionService.RESPONSE_BUNDLE_KEY,
+                                        response);
+                                callback.sendResult(bundle);
+                            };
+                    AmbientContextDetectionService.this.onStartDetection(
+                            request, packageName, consumer);
+                    Slog.d(TAG, "startDetection " + request);
+                }
+
+                /** {@inheritDoc} */
+                @Override
+                public void stopDetection(String packageName) {
+                    Objects.requireNonNull(packageName);
+                    AmbientContextDetectionService.this.onStopDetection(packageName);
+                }
+            };
+        }
+        return null;
+    }
+
+    /**
+     * Starts detection and provides detected events to the consumer. The ongoing detection will
+     * keep running, until onStopDetection is called. If there were previously requested
+     * detection from the same package, the previous request will be replaced with the new request.
+     * The implementation should keep track of whether the user consented each requested
+     * AmbientContextEvent for the app. If not consented, the response should set status
+     * STATUS_ACCESS_DENIED and include an action PendingIntent for the app to redirect the user
+     * to the consent screen.
+     *
+     * @param request The request with events to detect, optional detection window and other
+     *                options.
+     * @param packageName the requesting app's package name
+     * @param consumer the consumer for the detected event
+     */
+    public abstract void onStartDetection(
+            @NonNull AmbientContextEventRequest request,
+            @NonNull String packageName,
+            @NonNull Consumer<AmbientContextEventResponse> consumer);
+
+    /**
+     * Stops detection of the events. Events that are not being detected will be ignored.
+     *
+     * @param packageName stops detection for the given package.
+     */
+    public abstract void onStopDetection(@NonNull String packageName);
+}
diff --git a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
new file mode 100644
index 0000000..1c6e25e
--- /dev/null
+++ b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ambientcontext;
+
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.os.RemoteCallback;
+
+/**
+ * Interface for a concrete implementation to provide AmbientContextEvents to the framework.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextDetectionService {
+    void startDetection(in AmbientContextEventRequest request, in String packageName,
+        in RemoteCallback callback);
+    void stopDetection(in String packageName);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/OWNERS b/core/java/android/service/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/core/java/android/service/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 6411314..50efbac 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -80,6 +80,7 @@
                         colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
                     }
                     wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
+                    contextImage.close();
                 }
             }
 
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 50f9d8a..163d6ed 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -35,6 +35,7 @@
 public abstract class DreamOverlayService extends Service {
     private static final String TAG = "DreamOverlayService";
     private static final boolean DEBUG = false;
+    private boolean mShowComplications;
 
     private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
         @Override
@@ -53,6 +54,8 @@
     @Nullable
     @Override
     public final IBinder onBind(@NonNull Intent intent) {
+        mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
+                DreamService.DEFAULT_SHOW_COMPLICATIONS);
         return mDreamOverlay.asBinder();
     }
 
@@ -74,4 +77,11 @@
             Log.e(TAG, "Could not request exit:" + e);
         }
     }
+
+    /**
+     * Returns whether to show complications on the dream overlay.
+     */
+    public final boolean shouldShowComplications() {
+        return mShowComplications;
+    }
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 4ae3d7d..bb1f393 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.service.dreams;
 
 import android.annotation.IdRes;
@@ -57,7 +58,6 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.DumpUtils.Dump;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -111,7 +111,7 @@
  * <p>If specified with the {@code <meta-data>} element,
  * additional information for the dream is defined using the
  * {@link android.R.styleable#Dream &lt;dream&gt;} element in a separate XML file.
- * Currently, the only addtional
+ * Currently, the only additional
  * information you can provide is for a settings activity that allows the user to configure
  * the dream behavior. For example:</p>
  * <p class="code-caption">res/xml/my_dream.xml</p>
@@ -159,7 +159,8 @@
  * </pre>
  */
 public class DreamService extends Service implements Window.Callback {
-    private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
+    private final String mTag =
+            DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
 
     /**
      * The name of the dream manager service.
@@ -189,6 +190,19 @@
      */
     public static final String DREAM_META_DATA = "android.service.dream";
 
+    /**
+     * Extra containing a boolean for whether to show complications on the overlay.
+     * @hide
+     */
+    public static final String EXTRA_SHOW_COMPLICATIONS =
+            "android.service.dreams.SHOW_COMPLICATIONS";
+
+    /**
+     * The default value for whether to show complications on the overlay.
+     * @hide
+     */
+    public static final boolean DEFAULT_SHOW_COMPLICATIONS = true;
+
     private final IDreamManager mDreamManager;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private IBinder mDreamToken;
@@ -211,13 +225,13 @@
     private DreamServiceWrapper mDreamServiceWrapper;
     private Runnable mDispatchAfterOnAttachedToWindow;
 
-    private OverlayConnection mOverlayConnection;
+    private final OverlayConnection mOverlayConnection;
 
     private static class OverlayConnection implements ServiceConnection {
         // Overlay set during onBind.
         private IDreamOverlay mOverlay;
         // A Queue of pending requests to execute on the overlay.
-        private ArrayDeque<Consumer<IDreamOverlay>> mRequests;
+        private final ArrayDeque<Consumer<IDreamOverlay>> mRequests;
 
         private boolean mBound;
 
@@ -279,7 +293,7 @@
         }
     }
 
-    private IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() {
+    private final IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() {
         @Override
         public void onExitRequested() {
             // Simply finish dream when exit is requested.
@@ -306,11 +320,11 @@
     public boolean dispatchKeyEvent(KeyEvent event) {
         // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK
         if (!mInteractive) {
-            if (mDebug) Slog.v(TAG, "Waking up on keyEvent");
+            if (mDebug) Slog.v(mTag, "Waking up on keyEvent");
             wakeUp();
             return true;
         } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
-            if (mDebug) Slog.v(TAG, "Waking up on back key");
+            if (mDebug) Slog.v(mTag, "Waking up on back key");
             wakeUp();
             return true;
         }
@@ -321,7 +335,7 @@
     @Override
     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
         if (!mInteractive) {
-            if (mDebug) Slog.v(TAG, "Waking up on keyShortcutEvent");
+            if (mDebug) Slog.v(mTag, "Waking up on keyShortcutEvent");
             wakeUp();
             return true;
         }
@@ -334,7 +348,7 @@
         // TODO: create more flexible version of mInteractive that allows clicks
         // but finish()es on any other kind of activity
         if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) {
-            if (mDebug) Slog.v(TAG, "Waking up on touchEvent");
+            if (mDebug) Slog.v(mTag, "Waking up on touchEvent");
             wakeUp();
             return true;
         }
@@ -345,7 +359,7 @@
     @Override
     public boolean dispatchTrackballEvent(MotionEvent event) {
         if (!mInteractive) {
-            if (mDebug) Slog.v(TAG, "Waking up on trackballEvent");
+            if (mDebug) Slog.v(mTag, "Waking up on trackballEvent");
             wakeUp();
             return true;
         }
@@ -356,7 +370,7 @@
     @Override
     public boolean dispatchGenericMotionEvent(MotionEvent event) {
         if (!mInteractive) {
-            if (mDebug) Slog.v(TAG, "Waking up on genericMotionEvent");
+            if (mDebug) Slog.v(mTag, "Waking up on genericMotionEvent");
             wakeUp();
             return true;
         }
@@ -611,7 +625,7 @@
     }
 
     /**
-     * Returns whether or not this dream is interactive.  Defaults to false.
+     * Returns whether this dream is interactive. Defaults to false.
      *
      * @see #setInteractive(boolean)
      */
@@ -635,7 +649,7 @@
     }
 
     /**
-     * Returns whether or not this dream is in fullscreen mode. Defaults to false.
+     * Returns whether this dream is in fullscreen mode. Defaults to false.
      *
      * @see #setFullscreen(boolean)
      */
@@ -657,7 +671,7 @@
     }
 
     /**
-     * Returns whether or not this dream keeps the screen bright while dreaming.
+     * Returns whether this dream keeps the screen bright while dreaming.
      * Defaults to false, allowing the screen to dim if necessary.
      *
      * @see #setScreenBright(boolean)
@@ -667,7 +681,7 @@
     }
 
     /**
-     * Marks this dream as windowless.  Only available to doze dreams.
+     * Marks this dream as windowless. Only available to doze dreams.
      *
      * @hide
      *
@@ -677,7 +691,7 @@
     }
 
     /**
-     * Returns whether or not this dream is windowless.  Only available to doze dreams.
+     * Returns whether this dream is windowless. Only available to doze dreams.
      *
      * @hide
      */
@@ -704,12 +718,12 @@
      * Starts dozing, entering a deep dreamy sleep.
      * <p>
      * Dozing enables the system to conserve power while the user is not actively interacting
-     * with the device.  While dozing, the display will remain on in a low-power state
+     * with the device. While dozing, the display will remain on in a low-power state
      * and will continue to show its previous contents but the application processor and
      * other system components will be allowed to suspend when possible.
      * </p><p>
      * While the application processor is suspended, the dream may stop executing code
-     * for long periods of time.  Prior to being suspended, the dream may schedule periodic
+     * for long periods of time. Prior to being suspended, the dream may schedule periodic
      * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}.
      * The dream may also keep the CPU awake by acquiring a
      * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary.
@@ -718,7 +732,7 @@
      * awake for very long.
      * </p><p>
      * It is a good idea to call this method some time after the dream's entry animation
-     * has completed and the dream is ready to doze.  It is important to completely
+     * has completed and the dream is ready to doze. It is important to completely
      * finish all of the work needed before dozing since the application processor may
      * be suspended at any moment once this method is called unless other wake locks
      * are being held.
@@ -739,7 +753,7 @@
 
     private void updateDoze() {
         if (mDreamToken == null) {
-            Slog.w(TAG, "Updating doze without a dream token.");
+            Slog.w(mTag, "Updating doze without a dream token.");
             return;
         }
 
@@ -755,7 +769,7 @@
     /**
      * Stops dozing, returns to active dreaming.
      * <p>
-     * This method reverses the effect of {@link #startDozing}.  From this moment onward,
+     * This method reverses the effect of {@link #startDozing}. From this moment onward,
      * the application processor will be kept awake as long as the dream is running
      * or until the dream starts dozing again.
      * </p>
@@ -777,12 +791,11 @@
 
     /**
      * Returns true if the dream will allow the system to enter a low-power state while
-     * it is running without actually turning off the screen.  Defaults to false,
+     * it is running without actually turning off the screen. Defaults to false,
      * keeping the application processor awake while the dream is running.
      *
      * @return True if the dream is dozing.
      *
-     * @see #setDozing(boolean)
      * @hide For use by system UI components only.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -809,7 +822,7 @@
      * Sets the screen state to use while dozing.
      * <p>
      * The value of this property determines the power state of the primary display
-     * once {@link #startDozing} has been called.  The default value is
+     * once {@link #startDozing} has been called. The default value is
      * {@link Display#STATE_UNKNOWN} which lets the system decide.
      * The dream may set a different state before starting to doze and may
      * perform transitions between states while dozing to conserve power and
@@ -823,7 +836,7 @@
      * If not using Sidekick, it is recommended that the state be set to
      * {@link Display#STATE_DOZE_SUSPEND} once the dream has completely
      * finished drawing and before it releases its wakelock
-     * to allow the display hardware to be fully suspended.  While suspended,
+     * to allow the display hardware to be fully suspended. While suspended,
      * the display will preserve its on-screen contents.
      * </p><p>
      * If the doze suspend state is used, the dream must make sure to set the mode back
@@ -831,7 +844,7 @@
      * since the display updates may be ignored and not seen by the user otherwise.
      * </p><p>
      * The set of available display power states and their behavior while dozing is
-     * hardware dependent and may vary across devices.  The dream may therefore
+     * hardware dependent and may vary across devices. The dream may therefore
      * need to be modified or configured to correctly support the hardware.
      * </p>
      *
@@ -870,19 +883,19 @@
      * Sets the screen brightness to use while dozing.
      * <p>
      * The value of this property determines the power state of the primary display
-     * once {@link #startDozing} has been called.  The default value is
+     * once {@link #startDozing} has been called. The default value is
      * {@link PowerManager#BRIGHTNESS_DEFAULT} which lets the system decide.
      * The dream may set a different brightness before starting to doze and may adjust
      * the brightness while dozing to conserve power and achieve various effects.
      * </p><p>
      * Note that dream may specify any brightness in the full 0-255 range, including
      * values that are less than the minimum value for manual screen brightness
-     * adjustments by the user.  In particular, the value may be set to 0 which may
+     * adjustments by the user. In particular, the value may be set to 0 which may
      * turn off the backlight entirely while still leaving the screen on although
      * this behavior is device dependent and not guaranteed.
      * </p><p>
      * The available range of display brightness values and their behavior while dozing is
-     * hardware dependent and may vary across devices.  The dream may therefore
+     * hardware dependent and may vary across devices. The dream may therefore
      * need to be modified or configured to correctly support the hardware.
      * </p>
      *
@@ -909,7 +922,7 @@
      */
     @Override
     public void onCreate() {
-        if (mDebug) Slog.v(TAG, "onCreate()");
+        if (mDebug) Slog.v(mTag, "onCreate()");
         super.onCreate();
     }
 
@@ -917,7 +930,7 @@
      * Called when the dream's window has been created and is visible and animation may now begin.
      */
     public void onDreamingStarted() {
-        if (mDebug) Slog.v(TAG, "onDreamingStarted()");
+        if (mDebug) Slog.v(mTag, "onDreamingStarted()");
         // hook for subclasses
     }
 
@@ -926,7 +939,7 @@
      * before the window has been removed.
      */
     public void onDreamingStopped() {
-        if (mDebug) Slog.v(TAG, "onDreamingStopped()");
+        if (mDebug) Slog.v(mTag, "onDreamingStopped()");
         // hook for subclasses
     }
 
@@ -934,11 +947,11 @@
      * Called when the dream is being asked to stop itself and wake.
      * <p>
      * The default implementation simply calls {@link #finish} which ends the dream
-     * immediately.  Subclasses may override this function to perform a smooth exit
+     * immediately. Subclasses may override this function to perform a smooth exit
      * transition then call {@link #finish} afterwards.
      * </p><p>
      * Note that the dream will only be given a short period of time (currently about
-     * five seconds) to wake up.  If the dream does not finish itself in a timely manner
+     * five seconds) to wake up. If the dream does not finish itself in a timely manner
      * then the system will forcibly finish it once the time allowance is up.
      * </p>
      */
@@ -949,7 +962,7 @@
     /** {@inheritDoc} */
     @Override
     public final IBinder onBind(Intent intent) {
-        if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
+        if (mDebug) Slog.v(mTag, "onBind() intent = " + intent);
         mDreamServiceWrapper = new DreamServiceWrapper();
 
         // Connect to the overlay service if present.
@@ -968,7 +981,7 @@
      * </p>
      */
     public final void finish() {
-        if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished);
+        if (mDebug) Slog.v(mTag, "finish(): mFinished=" + mFinished);
 
         Activity activity = mActivity;
         if (activity != null) {
@@ -985,7 +998,7 @@
         mFinished = true;
 
         if (mDreamToken == null) {
-            Slog.w(TAG, "Finish was called before the dream was attached.");
+            Slog.w(mTag, "Finish was called before the dream was attached.");
             stopSelf();
             return;
         }
@@ -1013,8 +1026,10 @@
     }
 
     private void wakeUp(boolean fromSystem) {
-        if (mDebug) Slog.v(TAG, "wakeUp(): fromSystem=" + fromSystem
-                + ", mWaking=" + mWaking + ", mFinished=" + mFinished);
+        if (mDebug) {
+            Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking
+                    + ", mFinished=" + mFinished);
+        }
 
         if (!mWaking && !mFinished) {
             mWaking = true;
@@ -1039,7 +1054,7 @@
             // it we were finishing immediately.
             if (!fromSystem && !mFinished) {
                 if (mActivity == null) {
-                    Slog.w(TAG, "WakeUp was called before the dream was attached.");
+                    Slog.w(mTag, "WakeUp was called before the dream was attached.");
                 } else {
                     try {
                         mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
@@ -1054,7 +1069,7 @@
     /** {@inheritDoc} */
     @Override
     public void onDestroy() {
-        if (mDebug) Slog.v(TAG, "onDestroy()");
+        if (mDebug) Slog.v(mTag, "onDestroy()");
         // hook for subclasses
 
         // Just in case destroy came in before detach, let's take care of that now
@@ -1070,9 +1085,9 @@
      *
      * Must run on mHandler.
      */
-    private final void detach() {
+    private void detach() {
         if (mStarted) {
-            if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()");
+            if (mDebug) Slog.v(mTag, "detach(): Calling onDreamingStopped()");
             mStarted = false;
             onDreamingStopped();
         }
@@ -1097,12 +1112,12 @@
      */
     private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
         if (mDreamToken != null) {
-            Slog.e(TAG, "attach() called when dream with token=" + mDreamToken
+            Slog.e(mTag, "attach() called when dream with token=" + mDreamToken
                     + " already attached");
             return;
         }
         if (mFinished || mWaking) {
-            Slog.w(TAG, "attach() called after dream already finished");
+            Slog.w(mTag, "attach() called after dream already finished");
             try {
                 mDreamManager.finishSelf(dreamToken, true /*immediate*/);
             } catch (RemoteException ex) {
@@ -1145,10 +1160,9 @@
             try {
                 if (!ActivityTaskManager.getService().startDreamActivity(i)) {
                     detach();
-                    return;
                 }
             } catch (RemoteException e) {
-                Log.w(TAG, "Could not connect to activity task manager to start dream activity");
+                Log.w(mTag, "Could not connect to activity task manager to start dream activity");
                 e.rethrowFromSystemServer();
             }
         } else {
@@ -1178,7 +1192,7 @@
         // along well. Dreams usually don't need such bars anyways, so disable them by default.
         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 
-        // Hide all insets  when the dream is showing
+        // Hide all insets when the dream is showing
         mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
         mWindow.setDecorFitsSystemWindows(false);
 
@@ -1194,7 +1208,7 @@
                             try {
                                 overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
                             } catch (RemoteException e) {
-                                Log.e(TAG, "could not send window attributes:" + e);
+                                Log.e(mTag, "could not send window attributes:" + e);
                             }
                         });
                     }
@@ -1230,17 +1244,12 @@
 
     @Override
     protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) {
-        DumpUtils.dumpAsync(mHandler, new Dump() {
-            @Override
-            public void dump(PrintWriter pw, String prefix) {
-                dumpOnHandler(fd, pw, args);
-            }
-        }, pw, "", 1000);
+        DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> dumpOnHandler(fd, pw1, args), pw, "", 1000);
     }
 
     /** @hide */
     protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.print(TAG + ": ");
+        pw.print(mTag + ": ");
         if (mFinished) {
             pw.println("stopped");
         } else {
diff --git a/core/java/android/service/games/CreateGameSessionResult.aidl b/core/java/android/service/games/CreateGameSessionResult.aidl
new file mode 100644
index 0000000..b7c5e16
--- /dev/null
+++ b/core/java/android/service/games/CreateGameSessionResult.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+
+/**
+ * @hide
+ */
+parcelable CreateGameSessionResult;
diff --git a/core/java/android/service/games/CreateGameSessionResult.java b/core/java/android/service/games/CreateGameSessionResult.java
new file mode 100644
index 0000000..8448b0f
--- /dev/null
+++ b/core/java/android/service/games/CreateGameSessionResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControlViewHost;
+
+/**
+ * Internal result object that contains the successful creation of a game session.
+ *
+ * @see IGameSessionService#create(CreateGameSessionRequest, GameSessionViewHostConfiguration,
+ * com.android.internal.infra.AndroidFuture)
+ * @hide
+ */
+@Hide
+public final class CreateGameSessionResult implements Parcelable {
+
+    @NonNull
+    public static final Parcelable.Creator<CreateGameSessionResult> CREATOR =
+            new Parcelable.Creator<CreateGameSessionResult>() {
+                @Override
+                public CreateGameSessionResult createFromParcel(Parcel source) {
+                    return new CreateGameSessionResult(
+                            IGameSession.Stub.asInterface(source.readStrongBinder()),
+                            source.readParcelable(
+                                    SurfaceControlViewHost.SurfacePackage.class.getClassLoader(),
+                                    SurfaceControlViewHost.SurfacePackage.class));
+                }
+
+                @Override
+                public CreateGameSessionResult[] newArray(int size) {
+                    return new CreateGameSessionResult[0];
+                }
+            };
+
+    private final IGameSession mGameSession;
+    private final SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+
+    public CreateGameSessionResult(
+            @NonNull IGameSession gameSession,
+            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+        mGameSession = gameSession;
+        mSurfacePackage = surfacePackage;
+    }
+
+    @NonNull
+    public IGameSession getGameSession() {
+        return mGameSession;
+    }
+
+    @NonNull
+    public SurfaceControlViewHost.SurfacePackage getSurfacePackage() {
+        return mSurfacePackage;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mGameSession.asBinder());
+        dest.writeParcelable(mSurfacePackage, flags);
+    }
+}
diff --git a/core/java/android/service/games/GameScreenshotResult.java b/core/java/android/service/games/GameScreenshotResult.java
new file mode 100644
index 0000000..ae76e08
--- /dev/null
+++ b/core/java/android/service/games/GameScreenshotResult.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Result object for calls to {@link IGameSessionController#takeScreenshot}.
+ *
+ * It includes a status (see {@link #getStatus}) and, if the status is
+ * {@link #GAME_SCREENSHOT_SUCCESS} an {@link android.graphics.Bitmap} result (see {@link
+ * #getBitmap}).
+ *
+ * @hide
+ */
+public final class GameScreenshotResult implements Parcelable {
+
+    /**
+     * The status of a call to {@link IGameSessionController#takeScreenshot} will be represented by
+     * one of these values.
+     *
+     * @hide
+     */
+    @IntDef(flag = false, prefix = {"GAME_SCREENSHOT_"}, value = {
+            GAME_SCREENSHOT_SUCCESS, // 0
+            GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, // 1
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GameScreenshotStatus {
+    }
+
+    /**
+     * Indicates that the result of a call to {@link IGameSessionController#takeScreenshot} was
+     * successful and an {@link android.graphics.Bitmap} result should be available by calling
+     * {@link #getBitmap}.
+     *
+     * @hide
+     */
+    public static final int GAME_SCREENSHOT_SUCCESS = 0;
+
+    /**
+     * Indicates that the result of a call to {@link IGameSessionController#takeScreenshot} failed
+     * due to an internal error.
+     *
+     * This error may occur if the device is not in a suitable state for a screenshot to be taken
+     * (e.g., the screen is off) or if the game task is not in a suitable state for a screenshot
+     * to be taken (e.g., the task is not visible). To make sure that the device and game are
+     * in a suitable state, the caller can monitor the lifecycle methods for the {@link
+     * GameSession} to make sure that the game task is focused. If the conditions are met, then the
+     * caller may try again immediately.
+     *
+     * @hide
+     */
+    public static final int GAME_SCREENSHOT_ERROR_INTERNAL_ERROR = 1;
+
+    @NonNull
+    public static final Parcelable.Creator<GameScreenshotResult> CREATOR =
+            new Parcelable.Creator<GameScreenshotResult>() {
+                @Override
+                public GameScreenshotResult createFromParcel(Parcel source) {
+                    return new GameScreenshotResult(
+                            source.readInt(),
+                            source.readParcelable(null, Bitmap.class));
+                }
+
+                @Override
+                public GameScreenshotResult[] newArray(int size) {
+                    return new GameScreenshotResult[0];
+                }
+            };
+
+    @GameScreenshotStatus
+    private final int mStatus;
+
+    @Nullable
+    private final Bitmap mBitmap;
+
+    /**
+     * Creates a successful {@link GameScreenshotResult} with the provided bitmap.
+     */
+    public static GameScreenshotResult createSuccessResult(@NonNull Bitmap bitmap) {
+        return new GameScreenshotResult(GAME_SCREENSHOT_SUCCESS, bitmap);
+    }
+
+    /**
+     * Creates a failed {@link GameScreenshotResult} with an
+     * {@link #GAME_SCREENSHOT_ERROR_INTERNAL_ERROR} status.
+     */
+    public static GameScreenshotResult createInternalErrorResult() {
+        return new GameScreenshotResult(GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, null);
+    }
+
+    private GameScreenshotResult(@GameScreenshotStatus int status, @Nullable Bitmap bitmap) {
+        this.mStatus = status;
+        this.mBitmap = bitmap;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mStatus);
+        dest.writeParcelable(mBitmap, flags);
+    }
+
+    @GameScreenshotStatus
+    public int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Gets the {@link Bitmap} result from a successful screenshot attempt.
+     *
+     * @return The bitmap.
+     * @throws IllegalStateException if this method is called when {@link #getStatus} does not
+     *                               return {@link #GAME_SCREENSHOT_SUCCESS}.
+     */
+    @NonNull
+    public Bitmap getBitmap() {
+        if (mBitmap == null) {
+            throw new IllegalStateException("Bitmap not available for failed screenshot result");
+        }
+        return mBitmap;
+    }
+
+    @Override
+    public String toString() {
+        return "GameScreenshotResult{"
+                + "mStatus="
+                + mStatus
+                + ", has bitmap='"
+                + mBitmap != null ? "yes" : "no"
+                + "\'}";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof GameScreenshotResult)) {
+            return false;
+        }
+
+        GameScreenshotResult that = (GameScreenshotResult) o;
+        return mStatus == that.mStatus
+                && Objects.equals(mBitmap, that.mBitmap);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStatus, mBitmap);
+    }
+}
diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java
index b79c055..870a7e3 100644
--- a/core/java/android/service/games/GameService.java
+++ b/core/java/android/service/games/GameService.java
@@ -16,6 +16,8 @@
 
 package android.service.games;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
@@ -38,10 +40,29 @@
  * when a game session should begin. It is always kept running by the system.
  * Because of this it should be kept as lightweight as possible.
  *
- * Heavy weight operations (such as showing UI) should be implemented in the
+ * <p> Instead of requiring permissions for sensitive actions (e.g., starting a new game session),
+ * this class is provided with an {@link IGameServiceController} instance which exposes the
+ * sensitive functionality. This controller is provided by the system server when calling the
+ * {@link IGameService#connected(IGameServiceController)} method exposed by this class. The system
+ * server does so only when creating the bound game service.
+ *
+ * <p>Heavyweight operations (such as showing UI) should be implemented in the
  * associated {@link GameSessionService} when a game session is taking place. Its
  * implementation should run in a separate process from the {@link GameService}.
  *
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_GAME_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:
+ * <pre>
+ * &lt;service android:name=".GameService"
+ *          android:label="&#64;string/service_name"
+ *          android:permission="android.permission.BIND_GAME_SERVICE">
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.service.games.GameService" />
+ *     &lt;/intent-filter>
+ * &lt;/service>
+ * </pre>
+ *
  * @hide
  */
 @SystemApi
@@ -66,12 +87,13 @@
      */
     public static final String SERVICE_META_DATA = "android.game_service";
 
+    private IGameServiceController mGameServiceController;
     private IGameManagerService mGameManagerService;
     private final IGameService mInterface = new IGameService.Stub() {
         @Override
-        public void connected() {
+        public void connected(IGameServiceController gameServiceController) {
             Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
-                    GameService::doOnConnected, GameService.this));
+                    GameService::doOnConnected, GameService.this, gameServiceController));
         }
 
         @Override
@@ -79,6 +101,12 @@
             Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
                     GameService::onDisconnected, GameService.this));
         }
+
+        @Override
+        public void gameStarted(GameStartedEvent gameStartedEvent) {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    GameService::onGameStarted, GameService.this, gameStartedEvent));
+        }
     };
 
     private final IBinder.DeathRecipient mGameManagerServiceDeathRecipient = () -> {
@@ -90,7 +118,7 @@
 
     @Override
     @Nullable
-    public IBinder onBind(@Nullable Intent intent) {
+    public final IBinder onBind(@Nullable Intent intent) {
         if (ACTION_GAME_SERVICE.equals(intent.getAction())) {
             return mInterface.asBinder();
         }
@@ -98,7 +126,7 @@
         return null;
     }
 
-    private void doOnConnected() {
+    private void doOnConnected(@NonNull IGameServiceController gameServiceController) {
         mGameManagerService =
                 IGameManagerService.Stub.asInterface(
                         ServiceManager.getService(Context.GAME_SERVICE));
@@ -109,6 +137,7 @@
             Log.w(TAG, "Unable to link to death with system service");
         }
 
+        mGameServiceController = gameServiceController;
         onConnected();
     }
 
@@ -125,4 +154,34 @@
      * The service should clean up any resources that it holds at this point.
      */
     public void onDisconnected() {}
+
+    /**
+     * Called when a game task is started. It is the responsibility of the service to determine what
+     * action to take (e.g., request that a game session be created).
+     *
+     * @param gameStartedEvent Contains information about the game being started.
+     */
+    public void onGameStarted(@NonNull GameStartedEvent gameStartedEvent) {}
+
+    /**
+     * Call to create a new game session be created for a game. This method may be called
+     * by a game service following {@link #onGameStarted}, using the task ID provided by the
+     * provided {@link GameStartedEvent} (using {@link GameStartedEvent#getTaskId}).
+     *
+     * If a game session already exists for the game task, this call will be ignored and the
+     * existing session will continue.
+     *
+     * @param taskId The taskId of the game.
+     */
+    public final void createGameSession(@IntRange(from = 0) int taskId) {
+        if (mGameServiceController == null) {
+            throw new IllegalStateException("Can not call before connected()");
+        }
+
+        try {
+            mGameServiceController.createGameSession(taskId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Request for game session failed", e);
+        }
+    }
 }
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index 0ff08c0..cb5c19b 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -16,11 +16,32 @@
 
 package android.service.games;
 
+import android.annotation.Hide;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.function.pooled.PooledLambda;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
 /**
  * An active game session, providing a facility for the implementation to interact with the game.
  *
@@ -28,25 +49,177 @@
  * which is then returned when a game session is created via
  * {@link GameSessionService#onNewSession(CreateGameSessionRequest)}.
  *
+ * This class exposes various lifecycle methods which are guaranteed to be called in the following
+ * fashion:
+ *
+ * {@link #onCreate()}: Will always be the first lifecycle method to be called, once the game
+ * session is created.
+ *
+ * {@link #onGameTaskFocusChanged(boolean)}: Will be called after {@link #onCreate()} with
+ * focused=true when the game task first comes into focus (if it does). If the game task is focused
+ * when the game session is created, this method will be called immediately after
+ * {@link #onCreate()} with focused=true. After this method is called with focused=true, it will be
+ * called again with focused=false when the task goes out of focus. If this method is ever called
+ * with focused=true, it is guaranteed to be called again with focused=false before
+ * {@link #onDestroy()} is called. If the game task never comes into focus during the session
+ * lifetime, this method will never be called.
+ *
+ * {@link #onDestroy()}: Will always be called after {@link #onCreate()}. If the game task ever
+ * comes into focus before the game session is destroyed, then this method will be called after one
+ * or more pairs of calls to {@link #onGameTaskFocusChanged(boolean)}.
+ *
  * @hide
  */
 @SystemApi
 public abstract class GameSession {
+    private static final String TAG = "GameSession";
+    private static final boolean DEBUG = false;
 
     final IGameSession mInterface = new IGameSession.Stub() {
         @Override
-        public void destroy() {
+        public void onDestroyed() {
             Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
                     GameSession::doDestroy, GameSession.this));
         }
+
+        @Override
+        public void onTaskFocusChanged(boolean focused) {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    GameSession::moveToState, GameSession.this,
+                    focused ? LifecycleState.TASK_FOCUSED : LifecycleState.TASK_UNFOCUSED));
+        }
     };
 
-    void doCreate() {
-        onCreate();
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public enum LifecycleState {
+        // Initial state; may transition to CREATED.
+        INITIALIZED,
+        // May transition to TASK_FOCUSED or DESTROYED.
+        CREATED,
+        // May transition to TASK_UNFOCUSED.
+        TASK_FOCUSED,
+        // May transition to TASK_FOCUSED or DESTROYED.
+        TASK_UNFOCUSED,
+        // May not transition once reached.
+        DESTROYED
     }
 
+    private LifecycleState mLifecycleState = LifecycleState.INITIALIZED;
+    private IGameSessionController mGameSessionController;
+    private int mTaskId;
+    private GameSessionRootView mGameSessionRootView;
+    private SurfaceControlViewHost mSurfaceControlViewHost;
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public void attach(
+            IGameSessionController gameSessionController,
+            int taskId,
+            @NonNull Context context,
+            @NonNull SurfaceControlViewHost surfaceControlViewHost,
+            int widthPx,
+            int heightPx) {
+        mGameSessionController = gameSessionController;
+        mTaskId = taskId;
+        mSurfaceControlViewHost = surfaceControlViewHost;
+        mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost);
+        surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx);
+    }
+
+    @Hide
+    void doCreate() {
+        moveToState(LifecycleState.CREATED);
+    }
+
+    @Hide
     void doDestroy() {
-        onDestroy();
+        mSurfaceControlViewHost.release();
+        moveToState(LifecycleState.DESTROYED);
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    @MainThread
+    public void moveToState(LifecycleState newLifecycleState) {
+        if (DEBUG) {
+            Slog.d(TAG, "moveToState: " + mLifecycleState + " -> " + newLifecycleState);
+        }
+
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            throw new RuntimeException("moveToState should be used only from the main thread");
+        }
+
+        if (mLifecycleState == newLifecycleState) {
+            // Nothing to do.
+            return;
+        }
+
+        switch (mLifecycleState) {
+            case INITIALIZED:
+                if (newLifecycleState == LifecycleState.CREATED) {
+                    onCreate();
+                } else if (newLifecycleState == LifecycleState.DESTROYED) {
+                    onCreate();
+                    onDestroy();
+                } else {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignoring moveToState: INITIALIZED -> " + newLifecycleState);
+                    }
+                    return;
+                }
+                break;
+            case CREATED:
+                if (newLifecycleState == LifecycleState.TASK_FOCUSED) {
+                    onGameTaskFocusChanged(/*focused=*/ true);
+                } else if (newLifecycleState == LifecycleState.DESTROYED) {
+                    onDestroy();
+                } else {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignoring moveToState: CREATED -> " + newLifecycleState);
+                    }
+                    return;
+                }
+                break;
+            case TASK_FOCUSED:
+                if (newLifecycleState == LifecycleState.TASK_UNFOCUSED) {
+                    onGameTaskFocusChanged(/*focused=*/ false);
+                } else if (newLifecycleState == LifecycleState.DESTROYED) {
+                    onGameTaskFocusChanged(/*focused=*/ false);
+                    onDestroy();
+                } else {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignoring moveToState: TASK_FOCUSED -> " + newLifecycleState);
+                    }
+                    return;
+                }
+                break;
+            case TASK_UNFOCUSED:
+                if (newLifecycleState == LifecycleState.TASK_FOCUSED) {
+                    onGameTaskFocusChanged(/*focused=*/ true);
+                } else if (newLifecycleState == LifecycleState.DESTROYED) {
+                    onDestroy();
+                } else {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignoring moveToState: TASK_UNFOCUSED -> " + newLifecycleState);
+                    }
+                    return;
+                }
+                break;
+            case DESTROYED:
+                if (DEBUG) {
+                    Slog.d(TAG, "Ignoring moveToState: DESTROYED -> " + newLifecycleState);
+                }
+                return;
+        }
+
+        mLifecycleState = newLifecycleState;
     }
 
     /**
@@ -54,12 +227,173 @@
      *
      * This should be used perform any setup required now that the game session is created.
      */
-    public void onCreate() {}
+    public void onCreate() {
+    }
 
     /**
-     * Finalizer called when the game session is ending.
+     * Finalizer called when the game session is ending. This method will always be called after a
+     * call to {@link #onCreate()}. If the game task is ever in focus, this method will be called
+     * after one or more pairs of calls to {@link #onGameTaskFocusChanged(boolean)}.
      *
      * This should be used to perform any cleanup before the game session is destroyed.
      */
-    public void onDestroy() {}
+    public void onDestroy() {
+    }
+
+    /**
+     * Called when the game task for this session is or unfocused. The initial call to this method
+     * will always come after a call to {@link #onCreate()} with focused=true (when the game task
+     * first comes into focus after the session is created, or immediately after the session is
+     * created if the game task is already focused).
+     *
+     * This should be used to perform any setup required when the game task comes into focus or any
+     * cleanup that is required when the game task goes out of focus.
+     *
+     * @param focused True if the game task is focused, false if the game task is unfocused.
+     */
+    public void onGameTaskFocusChanged(boolean focused) {}
+
+    /**
+     * Sets the task overlay content to an explicit view. This view is placed directly into the game
+     * session's task overlay view hierarchy. It can itself be a complex view hierarchy. The size
+     * the task overlay view will always match the dimensions of the associated task's window. The
+     * {@code View} may not be cleared once set, but may be replaced by invoking
+     * {@link #setTaskOverlayView(View, ViewGroup.LayoutParams)} again.
+     *
+     * @param view         The desired content to display.
+     * @param layoutParams Layout parameters for the view.
+     */
+    public void setTaskOverlayView(
+            @NonNull View view,
+            @NonNull ViewGroup.LayoutParams layoutParams) {
+        mGameSessionRootView.removeAllViews();
+        mGameSessionRootView.addView(view, layoutParams);
+    }
+
+    /**
+     * Root view of the {@link SurfaceControlViewHost} associated with the {@link GameSession}
+     * instance. It is responsible for observing changes in the size of the window and resizing
+     * itself to match.
+     */
+    private static final class GameSessionRootView extends FrameLayout {
+        private final SurfaceControlViewHost mSurfaceControlViewHost;
+
+        GameSessionRootView(@NonNull Context context,
+                SurfaceControlViewHost surfaceControlViewHost) {
+            super(context);
+            mSurfaceControlViewHost = surfaceControlViewHost;
+        }
+
+        @Override
+        protected void onConfigurationChanged(Configuration newConfig) {
+            super.onConfigurationChanged(newConfig);
+
+            // TODO(b/204504596): Investigate skipping the relayout in cases where the size has
+            // not changed.
+            Rect bounds = newConfig.windowConfiguration.getBounds();
+            mSurfaceControlViewHost.relayout(bounds.width(), bounds.height());
+        }
+    }
+
+    /**
+     * Interface for returning screenshot outcome from calls to {@link #takeScreenshot}.
+     */
+    public interface ScreenshotCallback {
+
+        /**
+         * The status of a failed screenshot attempt provided by {@link #onFailure}.
+         *
+         * @hide
+         */
+        @IntDef(flag = false, prefix = {"ERROR_TAKE_SCREENSHOT_"}, value = {
+                ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, // 0
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface ScreenshotFailureStatus {
+        }
+
+        /**
+         * An error code indicating that an internal error occurred when attempting to take a
+         * screenshot of the game task. If this code is returned, the caller should verify that the
+         * conditions for taking a screenshot are met (device screen is on and the game task is
+         * visible). To do so, the caller can monitor the lifecycle methods for this session to
+         * make sure that the game task is focused. If the conditions are met, then the caller may
+         * try again immediately.
+         */
+        int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0;
+
+        /**
+         * Called when taking the screenshot failed.
+         * @param statusCode Indicates the reason for failure.
+         */
+        void onFailure(@ScreenshotFailureStatus int statusCode);
+
+        /**
+         * Called when taking the screenshot succeeded.
+         * @param bitmap The screenshot.
+         */
+        void onSuccess(@NonNull Bitmap bitmap);
+    }
+
+    /**
+     * Takes a screenshot of the associated game. For this call to succeed, the device screen
+     * must be turned on and the game task must be visible.
+     *
+     * If the callback is called with {@link ScreenshotCallback#onSuccess}, the provided {@link
+     * Bitmap} may be used.
+     *
+     * If the callback is called with {@link ScreenshotCallback#onFailure}, the provided status
+     * code should be checked.
+     *
+     * If the status code is {@link ScreenshotCallback#ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR},
+     * then the caller should verify that the conditions for calling this method are met (device
+     * screen is on and the game task is visible). To do so, the caller can monitor the lifecycle
+     * methods for this session to make sure that the game task is focused. If the conditions are
+     * met, then the caller may try again immediately.
+     *
+     * @param executor Executor on which to run the callback.
+     * @param callback The callback invoked when taking screenshot has succeeded
+     *                 or failed.
+     * @throws IllegalStateException if this method is called prior to {@link #onCreate}.
+     */
+    public void takeScreenshot(@NonNull Executor executor, @NonNull ScreenshotCallback callback) {
+        if (mGameSessionController == null) {
+            throw new IllegalStateException("Can not call before onCreate()");
+        }
+
+        AndroidFuture<GameScreenshotResult> takeScreenshotResult =
+                new AndroidFuture<GameScreenshotResult>().whenCompleteAsync((result, error) -> {
+                    handleScreenshotResult(callback, result, error);
+                }, executor);
+
+        try {
+            mGameSessionController.takeScreenshot(mTaskId, takeScreenshotResult);
+        } catch (RemoteException ex) {
+            takeScreenshotResult.completeExceptionally(ex);
+        }
+    }
+
+    private void handleScreenshotResult(
+            @NonNull ScreenshotCallback callback,
+            @NonNull GameScreenshotResult result,
+            @NonNull Throwable error) {
+        if (error != null) {
+            Slog.w(TAG, error.getMessage(), error.getCause());
+            callback.onFailure(
+                    ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR);
+            return;
+        }
+
+        @GameScreenshotResult.GameScreenshotStatus int status = result.getStatus();
+        switch (status) {
+            case GameScreenshotResult.GAME_SCREENSHOT_SUCCESS:
+                callback.onSuccess(result.getBitmap());
+                break;
+            case GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR:
+                Slog.w(TAG, "Error taking screenshot");
+                callback.onFailure(
+                        ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR);
+                break;
+        }
+    }
 }
diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java
index a2c8870..df5bad5 100644
--- a/core/java/android/service/games/GameSessionService.java
+++ b/core/java/android/service/games/GameSessionService.java
@@ -22,8 +22,12 @@
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.view.Display;
+import android.view.SurfaceControlViewHost;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -48,8 +52,6 @@
  */
 @SystemApi
 public abstract class GameSessionService extends Service {
-    private static final String TAG = "GameSessionService";
-
     /**
      * The {@link Intent} action used when binding to the service.
      * To be supported, the service must require the
@@ -62,18 +64,31 @@
 
     private final IGameSessionService mInterface = new IGameSessionService.Stub() {
         @Override
-        public void create(CreateGameSessionRequest createGameSessionRequest,
+        public void create(
+                IGameSessionController gameSessionController,
+                CreateGameSessionRequest createGameSessionRequest,
+                GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
                 AndroidFuture gameSessionFuture) {
             Handler.getMain().post(PooledLambda.obtainRunnable(
                     GameSessionService::doCreate, GameSessionService.this,
+                    gameSessionController,
                     createGameSessionRequest,
+                    gameSessionViewHostConfiguration,
                     gameSessionFuture));
         }
     };
 
+    private DisplayManager mDisplayManager;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mDisplayManager = this.getSystemService(DisplayManager.class);
+    }
+
     @Override
     @Nullable
-    public IBinder onBind(@Nullable Intent intent) {
+    public final IBinder onBind(@Nullable Intent intent) {
         if (intent == null) {
             return null;
         }
@@ -85,12 +100,39 @@
         return mInterface.asBinder();
     }
 
-    private void doCreate(CreateGameSessionRequest createGameSessionRequest,
-            AndroidFuture<IBinder> gameSessionFuture) {
+    private void doCreate(
+            IGameSessionController gameSessionController,
+            CreateGameSessionRequest createGameSessionRequest,
+            GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
+            AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture) {
         GameSession gameSession = onNewSession(createGameSessionRequest);
         Objects.requireNonNull(gameSession);
 
-        gameSessionFuture.complete(gameSession.mInterface.asBinder());
+        Display display = mDisplayManager.getDisplay(gameSessionViewHostConfiguration.mDisplayId);
+        if (display == null) {
+            createGameSessionResultFuture.completeExceptionally(
+                    new IllegalStateException("No display found for id: "
+                            + gameSessionViewHostConfiguration.mDisplayId));
+            return;
+        }
+
+        IBinder hostToken = new Binder();
+        SurfaceControlViewHost surfaceControlViewHost =
+                new SurfaceControlViewHost(this, display, hostToken);
+
+        gameSession.attach(
+                gameSessionController,
+                createGameSessionRequest.getTaskId(),
+                this,
+                surfaceControlViewHost,
+                gameSessionViewHostConfiguration.mWidthPx,
+                gameSessionViewHostConfiguration.mHeightPx);
+
+        CreateGameSessionResult createGameSessionResult =
+                new CreateGameSessionResult(gameSession.mInterface,
+                        surfaceControlViewHost.getSurfacePackage());
+
+        createGameSessionResultFuture.complete(createGameSessionResult);
 
         gameSession.doCreate();
     }
diff --git a/core/java/android/service/games/GameSessionViewHostConfiguration.aidl b/core/java/android/service/games/GameSessionViewHostConfiguration.aidl
new file mode 100644
index 0000000..b900b9d
--- /dev/null
+++ b/core/java/android/service/games/GameSessionViewHostConfiguration.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+/**
+ * @hide
+ */
+parcelable GameSessionViewHostConfiguration;
diff --git a/core/java/android/service/games/GameSessionViewHostConfiguration.java b/core/java/android/service/games/GameSessionViewHostConfiguration.java
new file mode 100644
index 0000000..53db0df
--- /dev/null
+++ b/core/java/android/service/games/GameSessionViewHostConfiguration.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents the configuration of the {@link android.view.SurfaceControlViewHost} used to render
+ * the overlay for a game session.
+ *
+ * @hide
+ */
+@Hide
+public final class GameSessionViewHostConfiguration implements Parcelable {
+
+    @NonNull
+    public static final Creator<GameSessionViewHostConfiguration> CREATOR =
+            new Creator<GameSessionViewHostConfiguration>() {
+                @Override
+                public GameSessionViewHostConfiguration createFromParcel(Parcel source) {
+                    return new GameSessionViewHostConfiguration(
+                            source.readInt(),
+                            source.readInt(),
+                            source.readInt());
+                }
+
+                @Override
+                public GameSessionViewHostConfiguration[] newArray(int size) {
+                    return new GameSessionViewHostConfiguration[0];
+                }
+            };
+
+    final int mDisplayId;
+    final int mWidthPx;
+    final int mHeightPx;
+
+    public GameSessionViewHostConfiguration(int displayId, int widthPx, int heightPx) {
+        this.mDisplayId = displayId;
+        this.mWidthPx = widthPx;
+        this.mHeightPx = heightPx;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDisplayId);
+        dest.writeInt(mWidthPx);
+        dest.writeInt(mHeightPx);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof GameSessionViewHostConfiguration)) return false;
+        GameSessionViewHostConfiguration that = (GameSessionViewHostConfiguration) o;
+        return mDisplayId == that.mDisplayId && mWidthPx == that.mWidthPx
+                && mHeightPx == that.mHeightPx;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDisplayId, mWidthPx, mHeightPx);
+    }
+
+    @Override
+    public String toString() {
+        return "GameSessionViewHostConfiguration{"
+                + "mDisplayId=" + mDisplayId
+                + ", mWidthPx=" + mWidthPx
+                + ", mHeightPx=" + mHeightPx
+                + '}';
+    }
+}
diff --git a/core/java/android/service/games/GameStartedEvent.aidl b/core/java/android/service/games/GameStartedEvent.aidl
new file mode 100644
index 0000000..8a5a4a1
--- /dev/null
+++ b/core/java/android/service/games/GameStartedEvent.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+
+/**
+ * @hide
+ */
+parcelable GameStartedEvent;
\ No newline at end of file
diff --git a/core/java/android/service/games/GameStartedEvent.java b/core/java/android/service/games/GameStartedEvent.java
new file mode 100644
index 0000000..bf29260
--- /dev/null
+++ b/core/java/android/service/games/GameStartedEvent.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Event object provided when a game task is started.
+ *
+ * This is provided to the Game Service via
+ * {@link GameService#onGameStarted(GameStartedEvent)}. It includes the game's taskId
+ * (see {@link #getTaskId}) that the game's package name (see {@link #getPackageName}).
+ *
+ * @hide
+ */
+@SystemApi
+public final class GameStartedEvent implements Parcelable {
+
+    @NonNull
+    public static final Parcelable.Creator<GameStartedEvent> CREATOR =
+            new Parcelable.Creator<GameStartedEvent>() {
+                @Override
+                public GameStartedEvent createFromParcel(Parcel source) {
+                    return new GameStartedEvent(
+                            source.readInt(),
+                            source.readString());
+                }
+
+                @Override
+                public GameStartedEvent[] newArray(int size) {
+                    return new GameStartedEvent[0];
+                }
+            };
+
+    private final int mTaskId;
+    private final String mPackageName;
+
+    public GameStartedEvent(@IntRange(from = 0) int taskId, @NonNull String packageName) {
+        this.mTaskId = taskId;
+        this.mPackageName = packageName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mTaskId);
+        dest.writeString(mPackageName);
+    }
+
+    /**
+     * Unique identifier for the task associated with the game.
+     */
+    @IntRange(from = 0)
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * The package name for the game.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Override
+    public String toString() {
+        return "GameStartedEvent{"
+                + "mTaskId="
+                + mTaskId
+                + ", mPackageName='"
+                + mPackageName
+                + "\'}";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof GameStartedEvent)) {
+            return false;
+        }
+
+        GameStartedEvent that = (GameStartedEvent) o;
+        return mTaskId == that.mTaskId
+                && Objects.equals(mPackageName, that.mPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTaskId, mPackageName);
+    }
+}
diff --git a/core/java/android/service/games/IGameService.aidl b/core/java/android/service/games/IGameService.aidl
index 8a0d636..38c8416 100644
--- a/core/java/android/service/games/IGameService.aidl
+++ b/core/java/android/service/games/IGameService.aidl
@@ -16,10 +16,14 @@
 
 package android.service.games;
 
+import android.service.games.GameStartedEvent;
+import android.service.games.IGameServiceController;
+
 /**
  * @hide
  */
 oneway interface IGameService {
-    void connected();
+    void connected(in IGameServiceController gameServiceController);
     void disconnected();
+    void gameStarted(in GameStartedEvent gameStartedEvent);
 }
diff --git a/core/java/android/service/games/IGameServiceController.aidl b/core/java/android/service/games/IGameServiceController.aidl
new file mode 100644
index 0000000..886f519
--- /dev/null
+++ b/core/java/android/service/games/IGameServiceController.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+/**
+ * @hide
+ */
+oneway interface IGameServiceController {
+    void createGameSession(int taskId);
+}
\ No newline at end of file
diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl
index b2e9f1d..71da630 100644
--- a/core/java/android/service/games/IGameSession.aidl
+++ b/core/java/android/service/games/IGameSession.aidl
@@ -20,5 +20,6 @@
  * @hide
  */
 oneway interface IGameSession {
-    void destroy();
+    void onDestroyed();
+    void onTaskFocusChanged(boolean focused);
 }
diff --git a/core/java/android/service/games/IGameSessionController.aidl b/core/java/android/service/games/IGameSessionController.aidl
new file mode 100644
index 0000000..fe1d362
--- /dev/null
+++ b/core/java/android/service/games/IGameSessionController.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * @hide
+ */
+oneway interface IGameSessionController {
+    void takeScreenshot(int taskId, in AndroidFuture gameScreenshotResultFuture);
+}
\ No newline at end of file
diff --git a/core/java/android/service/games/IGameSessionService.aidl b/core/java/android/service/games/IGameSessionService.aidl
index 2a53ea7..37cde56 100644
--- a/core/java/android/service/games/IGameSessionService.aidl
+++ b/core/java/android/service/games/IGameSessionService.aidl
@@ -16,8 +16,10 @@
 
 package android.service.games;
 
+import android.service.games.IGameSessionController;
 import android.service.games.IGameSession;
 import android.service.games.CreateGameSessionRequest;
+import android.service.games.GameSessionViewHostConfiguration;
 
 import com.android.internal.infra.AndroidFuture;
 
@@ -27,6 +29,8 @@
  */
 oneway interface IGameSessionService {
     void create(
+            in IGameSessionController gameSessionController,
             in CreateGameSessionRequest createGameSessionRequest,
-            in AndroidFuture /* T=IBinder for IGameSession */ gameSessionFuture);
+            in GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
+            in AndroidFuture /* T=CreateGameSessionResult */ createGameSessionResultFuture);
 }
diff --git a/core/java/android/service/games/OWNERS b/core/java/android/service/games/OWNERS
new file mode 100644
index 0000000..81d94e0
--- /dev/null
+++ b/core/java/android/service/games/OWNERS
@@ -0,0 +1 @@
+include /GAME_MANAGER_OWNERS
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 8242f4e..44a8862 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -17,6 +17,7 @@
 package android.service.persistentdata;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -25,6 +26,8 @@
 import android.os.RemoteException;
 import android.service.oemlock.OemLockManager;
 
+import com.android.internal.R;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -50,6 +53,7 @@
 @SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE)
 public class PersistentDataBlockManager {
     private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
+    private final Context mContext;
     private IPersistentDataBlockService sService;
 
     /**
@@ -74,7 +78,10 @@
     public @interface FlashLockState {}
 
     /** @hide */
-    public PersistentDataBlockManager(IPersistentDataBlockService service) {
+    public PersistentDataBlockManager(
+            Context context,
+            IPersistentDataBlockService service) {
+        mContext = context;
         sService = service;
     }
 
@@ -204,4 +211,15 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Returns the package name which can access the persistent data partition.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public String getPersistentDataPackageName() {
+        return mContext.getString(R.string.config_persistentDataPackageName);
+    }
 }
diff --git a/core/java/android/service/security/attestationverification/OWNERS b/core/java/android/service/security/attestationverification/OWNERS
new file mode 100644
index 0000000..12c9978
--- /dev/null
+++ b/core/java/android/service/security/attestationverification/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/security/attestationverification/OWNERS
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
index 7dd85cc..b903fbe 100644
--- a/core/java/android/service/smartspace/SmartspaceService.java
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -245,7 +245,9 @@
     public abstract void onDestroySmartspaceSession(@NonNull SmartspaceSessionId sessionId);
 
     private void doDestroy(@NonNull SmartspaceSessionId sessionId) {
-        Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG) {
+            Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+        }
         super.onDestroy();
         mSessionCallbacks.remove(sessionId);
         onDestroySmartspaceSession(sessionId);
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index 21661db..ec3b857 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -25,6 +25,7 @@
  */
 interface ITrustAgentService {
     oneway void onUnlockAttempt(boolean successful);
+    oneway void onUserRequestedUnlock();
     oneway void onUnlockLockout(int timeoutMs);
     oneway void onTrustTimeout();
     oneway void onDeviceLocked();
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 61277e2..fba61cf 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -114,15 +114,47 @@
      */
     public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 1 << 1;
 
+    /**
+     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating the platform should
+     * automatically remove trust after some conditions are met (detailed below) with the option for
+     * the agent to renew the trust again later.
+     *
+     * <p>After this is called, the agent will grant trust until the platform thinks an active user
+     * is no longer using that trust. For example, if the user dismisses keyguard, the platform will
+     * remove trust (this does not automatically lock the device).
+     *
+     * <p>When the platform internally removes the agent's trust in this manner, an agent can
+     * re-grant it (via a call to grantTrust) without the user having to unlock the device through
+     * another method (e.g. PIN). This renewable state only persists for a limited time.
+     *
+     * TODO(b/213631675): Remove @hide
+     * @hide
+     */
+    public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 1 << 2;
+
+    /**
+     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that the message should
+     * be displayed to the user.
+     *
+     * Without this flag, the message passed to {@code grantTrust} is only used for debugging
+     * purposes. With the flag, it may be displayed to the user as the reason why the device is
+     * unlocked.
+     *
+     * TODO(b/213911325): Remove @hide
+     * @hide
+     */
+    public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "FLAG_GRANT_TRUST_" }, value = {
             FLAG_GRANT_TRUST_INITIATED_BY_USER,
             FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
+            FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE,
+            FLAG_GRANT_TRUST_DISPLAY_MESSAGE,
     })
     public @interface GrantTrustFlags {}
 
-
     /**
      * Int enum indicating that escrow token is active.
      * See {@link #onEscrowTokenStateReceived(long, int)}
@@ -154,6 +186,7 @@
     private static final int MSG_ESCROW_TOKEN_ADDED = 7;
     private static final int MSG_ESCROW_TOKEN_STATE_RECEIVED = 8;
     private static final int MSG_ESCROW_TOKEN_REMOVED = 9;
+    private static final int MSG_USER_REQUESTED_UNLOCK = 10;
 
     private static final String EXTRA_TOKEN = "token";
     private static final String EXTRA_TOKEN_HANDLE = "token_handle";
@@ -187,6 +220,9 @@
                 case MSG_UNLOCK_ATTEMPT:
                     onUnlockAttempt(msg.arg1 != 0);
                     break;
+                case MSG_USER_REQUESTED_UNLOCK:
+                    onUserRequestedUnlock();
+                    break;
                 case MSG_UNLOCK_LOCKOUT:
                     onDeviceUnlockLockout(msg.arg1);
                     break;
@@ -265,6 +301,22 @@
     }
 
     /**
+     * Called when the user has interacted with the locked device such that they likely want it
+     * to be unlocked. This approximates the timing when, for example, the platform would check for
+     * face authentication to unlock the device.
+     *
+     * To attempt to unlock the device, the agent needs to call
+     * {@link #grantTrust(CharSequence, long, int)}.
+     *
+     * @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
+     *
+     * TODO(b/213631672): Add CTS tests
+     * @hide
+     */
+    public void onUserRequestedUnlock() {
+    }
+
+    /**
      * Called when the timeout provided by the agent expires.  Note that this may be called earlier
      * than requested by the agent if the trust timeout is adjusted by the system or
      * {@link DevicePolicyManager}.  The agent is expected to re-evaluate the trust state and only
@@ -564,6 +616,22 @@
     }
 
     /**
+     * Locks the user.
+     *
+     * This revokes any trust granted by this agent and shows keyguard for the user if it is not
+     * currently shown for them. Other users are not affected. Note that this is in contrast to
+     * {@link #revokeTrust()} which does not show keyguard if it is not already shown.
+     *
+     * If the user has no auth method specified, then keyguard will still be shown but can be
+     * dismissed normally.
+     *
+     * TODO(b/213631675): Implement & make public
+     * @hide
+     */
+    public final void lockUser() {
+    }
+
+    /**
      * Request showing a transient error message on the keyguard.
      * The message will be visible on the lock screen or always on display if possible but can be
      * overridden by other keyguard events of higher priority - eg. fingerprint auth error.
@@ -601,6 +669,11 @@
         }
 
         @Override
+        public void onUserRequestedUnlock() {
+            mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK).sendToTarget();
+        }
+
+        @Override
         public void onUnlockLockout(int timeoutMs) {
             mHandler.obtainMessage(MSG_UNLOCK_LOCKOUT, timeoutMs, 0).sendToTarget();
         }
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index 81af6a2..93d4def 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -46,4 +46,5 @@
     oneway void removeLocalColorsAreas(in List<RectF> regions);
     oneway void addLocalColorsAreas(in List<RectF> regions);
     SurfaceControl mirrorSurfaceControl();
+    oneway void applyDimming(float dimAmount);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 73ffd66..dd4355d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -26,6 +26,7 @@
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import android.animation.ValueAnimator;
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -159,6 +160,7 @@
     private static final int MSG_ZOOM = 10100;
     private static final int MSG_SCALE_PREVIEW = 10110;
     private static final int MSG_REPORT_SHOWN = 10150;
+    private static final int MSG_UPDATE_DIMMING = 10200;
     private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
             Float.NEGATIVE_INFINITY);
 
@@ -167,6 +169,8 @@
     private static final boolean ENABLE_WALLPAPER_DIMMING =
             SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true);
 
+    private static final long DIMMING_ANIMATION_DURATION_MS = 300L;
+
     private final ArrayList<Engine> mActiveEngines
             = new ArrayList<Engine>();
 
@@ -221,6 +225,9 @@
         boolean mOffsetsChanged;
         boolean mFixedSizeAllowed;
         boolean mShouldDim;
+        // Whether the wallpaper should be dimmed by default (when no additional dimming is applied)
+        // based on its color hints
+        boolean mShouldDimByDefault;
         int mWidth;
         int mHeight;
         int mFormat;
@@ -271,7 +278,10 @@
         private Display mDisplay;
         private Context mDisplayContext;
         private int mDisplayState;
+        private @Surface.Rotation int mDisplayInstallOrientation;
         private float mWallpaperDimAmount = 0.05f;
+        private float mPreviousWallpaperDimAmount = mWallpaperDimAmount;
+        private float mDefaultDimAmount = mWallpaperDimAmount;
 
         SurfaceControl mSurfaceControl = new SurfaceControl();
         SurfaceControl mBbqSurfaceControl;
@@ -861,15 +871,34 @@
                 return;
             }
             int colorHints = colors.getColorHints();
-            boolean shouldDim = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
+            mShouldDimByDefault = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
                     && (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) == 0);
-            if (shouldDim != mShouldDim) {
-                mShouldDim = shouldDim;
+
+            // If default dimming value changes and no additional dimming is applied
+            if (mShouldDimByDefault != mShouldDim && mWallpaperDimAmount == 0f) {
+                mShouldDim = mShouldDimByDefault;
                 updateSurfaceDimming();
                 updateSurface(false, false, true);
             }
         }
 
+        /**
+         * Update the dim amount of the wallpaper by updating the surface.
+         *
+         * @param dimAmount Float amount between [0.0, 1.0] to dim the wallpaper.
+         */
+        private void updateWallpaperDimming(float dimAmount) {
+            mPreviousWallpaperDimAmount = mWallpaperDimAmount;
+
+            // Custom dim amount cannot be less than the default dim amount.
+            mWallpaperDimAmount = Math.max(mDefaultDimAmount, dimAmount);
+            // If dim amount is 0f (additional dimming is removed), then the wallpaper should dim
+            // based on its default wallpaper color hints.
+            mShouldDim = dimAmount != 0f || mShouldDimByDefault;
+            updateSurfaceDimming();
+            updateSurface(false, false, true);
+        }
+
         private void updateSurfaceDimming() {
             if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) {
                 return;
@@ -878,9 +907,21 @@
             // preview mode.
             if (!isPreview() && mShouldDim) {
                 Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
-                new SurfaceControl.Transaction()
-                        .setAlpha(mBbqSurfaceControl, 1 - mWallpaperDimAmount)
-                        .apply();
+                SurfaceControl.Transaction surfaceControl = new SurfaceControl.Transaction();
+
+                // Animate dimming to gradually change the wallpaper alpha from the previous
+                // dim amount to the new amount only if the dim amount changed.
+                ValueAnimator animator = ValueAnimator.ofFloat(
+                        mPreviousWallpaperDimAmount, mWallpaperDimAmount);
+                animator.setDuration(mPreviousWallpaperDimAmount == mWallpaperDimAmount
+                        ? 0 : DIMMING_ANIMATION_DURATION_MS);
+                animator.addUpdateListener((ValueAnimator va) -> {
+                    final float dimValue = (float) va.getAnimatedValue();
+                    surfaceControl
+                            .setAlpha(mBbqSurfaceControl, 1 - dimValue)
+                            .apply();
+                });
+                animator.start();
             } else {
                 Log.v(TAG, "Setting wallpaper dimming: " + 0);
                 new SurfaceControl.Transaction()
@@ -1082,6 +1123,11 @@
                             mWindow, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
                             mInsetsState, mTempControls, mSurfaceSize);
+
+                    final int transformHint = SurfaceControl.rotationToBufferTransform(
+                            (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+                    mSurfaceControl.setTransformHint(transformHint);
+
                     if (mSurfaceControl.isValid()) {
                         if (mBbqSurfaceControl == null) {
                             mBbqSurfaceControl = new SurfaceControl.Builder()
@@ -1095,9 +1141,9 @@
                                     .build();
                             updateSurfaceDimming();
                         }
-                        // Propagate transform hint from WM so we can use the right hint for the
+                        // Propagate transform hint from WM, so we can use the right hint for the
                         // first frame.
-                        mBbqSurfaceControl.setTransformHint(mSurfaceControl.getTransformHint());
+                        mBbqSurfaceControl.setTransformHint(transformHint);
                         Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
                                 mSurfaceSize.y, mFormat);
                         // If blastSurface == null that means it hasn't changed since the last
@@ -1332,9 +1378,12 @@
             // Use window context of TYPE_WALLPAPER so client can access UI resources correctly.
             mDisplayContext = createDisplayContext(mDisplay)
                     .createWindowContext(TYPE_WALLPAPER, null /* options */);
-            mWallpaperDimAmount = mDisplayContext.getResources().getFloat(
+            mDefaultDimAmount = mDisplayContext.getResources().getFloat(
                     com.android.internal.R.dimen.config_wallpaperDimAmount);
+            mWallpaperDimAmount = mDefaultDimAmount;
+            mPreviousWallpaperDimAmount = mWallpaperDimAmount;
             mDisplayState = mDisplay.getState();
+            mDisplayInstallOrientation = mDisplay.getInstallOrientation();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
             onCreate(mSurfaceHolder);
@@ -1587,6 +1636,7 @@
                 return;
             }
             Surface surface = mSurfaceHolder.getSurface();
+            if (!surface.isValid()) return;
             boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
             int smaller = widthIsLarger ? mSurfaceSize.x
                     : mSurfaceSize.y;
@@ -1647,7 +1697,7 @@
                     Log.e(TAG, "Error creating page local color bitmap", e);
                     continue;
                 }
-                WallpaperColors color = WallpaperColors.fromBitmap(target);
+                WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
                 target.recycle();
                 WallpaperColors currentColor = page.getColors(area);
 
@@ -2175,6 +2225,12 @@
             mDetached.set(true);
         }
 
+        public void applyDimming(float dimAmount) throws RemoteException {
+            Message msg = mCaller.obtainMessageI(MSG_UPDATE_DIMMING,
+                    Float.floatToIntBits(dimAmount));
+            mCaller.sendMessage(msg);
+        }
+
         public void scalePreview(Rect position) {
             Message msg = mCaller.obtainMessageO(MSG_SCALE_PREVIEW, position);
             mCaller.sendMessage(msg);
@@ -2245,6 +2301,9 @@
                 case MSG_ZOOM:
                     mEngine.setZoom(Float.intBitsToFloat(message.arg1));
                     break;
+                case MSG_UPDATE_DIMMING:
+                    mEngine.updateWallpaperDimming(Float.intBitsToFloat(message.arg1));
+                    break;
                 case MSG_SCALE_PREVIEW:
                     mEngine.scalePreview((Rect) message.obj);
                     break;
diff --git a/core/java/android/service/wallpapereffectsgeneration/OWNERS b/core/java/android/service/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 0000000..d2d3e2c0
--- /dev/null
+++ b/core/java/android/service/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl
index cc349c8..ad3ad7a 100644
--- a/core/java/android/speech/IRecognitionService.aidl
+++ b/core/java/android/speech/IRecognitionService.aidl
@@ -20,6 +20,7 @@
 import android.content.AttributionSource;
 import android.content.Intent;
 import android.speech.IRecognitionListener;
+import android.speech.IRecognitionSupportCallback;
 
 /**
 * A Service interface to speech recognition. Call startListening when
@@ -60,4 +61,18 @@
      * @param listener to receive callbacks, note that this must be non-null
      */
     void cancel(in IRecognitionListener listener, boolean isShutdown);
+
+    /**
+     * Checks whether this RecognitionService could {@link #startListening} successfully on the
+     * given recognizerIntent. For more information see {@link #startListening} and
+     * {@link RecognizerIntent}.
+     */
+    void checkRecognitionSupport(in Intent recognizerIntent, in IRecognitionSupportCallback listener);
+
+    /**
+     * Requests RecognitionService to download the support for the given recognizerIntent. For more
+     * information see {@link #checkRecognitionSupport},  {@link #startListening} and
+     * {@link RecognizerIntent}.
+     */
+    void triggerModelDownload(in Intent recognizerIntent);
 }
diff --git a/core/java/android/speech/IRecognitionSupportCallback.aidl b/core/java/android/speech/IRecognitionSupportCallback.aidl
new file mode 100644
index 0000000..f5a5473
--- /dev/null
+++ b/core/java/android/speech/IRecognitionSupportCallback.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+import android.os.Bundle;
+import android.speech.RecognitionSupport;
+
+/**
+ *  Callback for speech recognition support checks, used with RecognitionService.
+ *  This provides the {@link RecognitionSupport} for a given recognition request, callers can use
+ *  it to check whether RecognitionService can fulfill a given recognition request.
+ *  {@hide}
+ */
+oneway interface IRecognitionSupportCallback {
+    void onSupportResult(in RecognitionSupport recognitionSupport);
+
+    /**
+     * A network or recognition error occurred.
+     *
+     * @param error code is defined in {@link SpeechRecognizer}
+     */
+    void onError(in int error);
+}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 5e647a4..5dbbc04 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -37,6 +37,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.util.function.pooled.PooledLambda;
 
@@ -90,6 +91,10 @@
 
     private static final int MSG_RESET = 4;
 
+    private static final int MSG_CHECK_RECOGNITION_SUPPORT = 5;
+
+    private static final int MSG_TRIGGER_MODEL_DOWNLOAD = 6;
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -107,6 +112,15 @@
                 case MSG_RESET:
                     dispatchClearCallback();
                     break;
+                case MSG_CHECK_RECOGNITION_SUPPORT:
+                    Pair<Intent, IRecognitionSupportCallback> intentAndListener =
+                            (Pair<Intent, IRecognitionSupportCallback>) msg.obj;
+                    dispatchCheckRecognitionSupport(
+                            intentAndListener.first, intentAndListener.second);
+                    break;
+                case MSG_TRIGGER_MODEL_DOWNLOAD:
+                    dispatchTriggerModelDownload((Intent) msg.obj);
+                    break;
             }
         }
     };
@@ -179,6 +193,15 @@
         mStartedDataDelivery = false;
     }
 
+    private void dispatchCheckRecognitionSupport(
+            Intent intent, IRecognitionSupportCallback callback) {
+        RecognitionService.this.onCheckRecognitionSupport(intent, new SupportCallback(callback));
+    }
+
+    private void dispatchTriggerModelDownload(Intent intent) {
+        RecognitionService.this.triggerModelDownload(intent);
+    }
+
     private class StartListeningArgs {
         public final Intent mIntent;
 
@@ -238,6 +261,34 @@
      */
     protected abstract void onStopListening(Callback listener);
 
+    /**
+     * Queries the service on whether it would support a {@link #onStartListening(Intent, Callback)}
+     * for the same {@code recognizerIntent}.
+     *
+     * <p>The service will notify the caller about the level of support or error via
+     * {@link SupportCallback}.
+     *
+     * <p>If the service does not offer the support check it will notify the caller with
+     * {@link SpeechRecognizer#ERROR_CANNOT_CHECK_SUPPORT}.
+     */
+    public void onCheckRecognitionSupport(
+            @NonNull Intent recognizerIntent,
+            @NonNull SupportCallback supportCallback) {
+        if (DBG) {
+            Log.i(TAG, String.format("#onSupports [%s]", recognizerIntent));
+        }
+        supportCallback.onError(SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT);
+    }
+
+    /**
+     * Requests the download of the recognizer support for {@code recognizerIntent}.
+     */
+    public void triggerModelDownload(@NonNull Intent recognizerIntent) {
+        if (DBG) {
+            Log.i(TAG, String.format("#downloadModel [%s]", recognizerIntent));
+        }
+    }
+
     @Override
     @SuppressLint("MissingNullability")
     public Context createContext(@NonNull ContextParams contextParams) {
@@ -410,7 +461,45 @@
         }
     }
 
-    /** Binder of the recognition service */
+    /**
+     * This class receives callbacks from the speech recognition service and forwards them to the
+     * user. An instance of this class is passed to the
+     * {@link RecognitionService#onCheckRecognitionSupport(Intent, SupportCallback)} method. Recognizers may call
+     * these methods on any thread.
+     */
+    public static class SupportCallback {
+
+        private final IRecognitionSupportCallback mCallback;
+
+        private SupportCallback(IRecognitionSupportCallback callback) {
+            this.mCallback = callback;
+        }
+
+        /** The service should call this method to notify the caller about the level of support. */
+        public void onSupportResult(@NonNull RecognitionSupport recognitionSupport) {
+            try {
+                mCallback.onSupportResult(recognitionSupport);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * The service should call this method when an error occurred and can't satisfy the support
+         * request.
+         *
+         * @param errorCode code is defined in {@link SpeechRecognizer}
+         */
+        public void onError(@SpeechRecognizer.RecognitionError int errorCode) {
+            try {
+                mCallback.onError(errorCode);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+/** Binder of the recognition service */
     private static final class RecognitionServiceBinder extends IRecognitionService.Stub {
         private final WeakReference<RecognitionService> mServiceRef;
 
@@ -452,6 +541,27 @@
             }
         }
 
+        @Override
+        public void checkRecognitionSupport(
+                Intent recognizerIntent, IRecognitionSupportCallback callback) {
+            final RecognitionService service = mServiceRef.get();
+            if (service != null) {
+                service.mHandler.sendMessage(
+                        Message.obtain(service.mHandler, MSG_CHECK_RECOGNITION_SUPPORT,
+                                Pair.create(recognizerIntent, callback)));
+            }
+        }
+
+        @Override
+        public void triggerModelDownload(Intent recognizerIntent) {
+            final RecognitionService service = mServiceRef.get();
+            if (service != null) {
+                service.mHandler.sendMessage(
+                        Message.obtain(
+                                service.mHandler, MSG_TRIGGER_MODEL_DOWNLOAD, recognizerIntent));
+            }
+        }
+
         public void clearReference() {
             mServiceRef.clear();
         }
diff --git a/core/java/android/speech/RecognitionSupport.aidl b/core/java/android/speech/RecognitionSupport.aidl
new file mode 100644
index 0000000..20e52a8
--- /dev/null
+++ b/core/java/android/speech/RecognitionSupport.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+parcelable RecognitionSupport;
diff --git a/core/java/android/speech/RecognitionSupport.java b/core/java/android/speech/RecognitionSupport.java
new file mode 100644
index 0000000..3a86d0b
--- /dev/null
+++ b/core/java/android/speech/RecognitionSupport.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.List;
+
+/** Encodes the level of support for a given speech recognition request */
+@DataClass(
+        genConstructor = false,
+        genBuilder = true,
+        genEqualsHashCode = true,
+        genHiddenConstDefs = true,
+        genParcelable = true,
+        genToString = true
+)
+public final class RecognitionSupport implements Parcelable {
+
+    /** Support for this request is ready for use on this device for the returned languages. */
+    @NonNull
+    private List<String> mInstalledLanguages = null;
+
+    /** Support for this request is scheduled for download for the returned languages. */
+    @NonNull private List<String> mPendingLanguages = null;
+
+    /** These languages are supported but need to be downloaded before use. */
+    @NonNull
+    private List<String> mSupportedLanguages = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/speech/RecognitionSupport.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ RecognitionSupport(
+            @NonNull List<String> installedLanguages,
+            @NonNull List<String> pendingLanguages,
+            @NonNull List<String> supportedLanguages) {
+        this.mInstalledLanguages = installedLanguages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInstalledLanguages);
+        this.mPendingLanguages = pendingLanguages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPendingLanguages);
+        this.mSupportedLanguages = supportedLanguages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSupportedLanguages);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Support for this request is ready for use on this device for the returned languages.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getInstalledLanguages() {
+        return mInstalledLanguages;
+    }
+
+    /**
+     * Support for this request is scheduled for download for the returned languages.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getPendingLanguages() {
+        return mPendingLanguages;
+    }
+
+    /**
+     * These languages are supported but need to be downloaded before use.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getSupportedLanguages() {
+        return mSupportedLanguages;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "RecognitionSupport { " +
+                "installedLanguages = " + mInstalledLanguages + ", " +
+                "pendingLanguages = " + mPendingLanguages + ", " +
+                "supportedLanguages = " + mSupportedLanguages +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(RecognitionSupport other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        RecognitionSupport that = (RecognitionSupport) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mInstalledLanguages, that.mInstalledLanguages)
+                && java.util.Objects.equals(mPendingLanguages, that.mPendingLanguages)
+                && java.util.Objects.equals(mSupportedLanguages, that.mSupportedLanguages);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mInstalledLanguages);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPendingLanguages);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLanguages);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeStringList(mInstalledLanguages);
+        dest.writeStringList(mPendingLanguages);
+        dest.writeStringList(mSupportedLanguages);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RecognitionSupport(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        List<String> installedLanguages = new java.util.ArrayList<>();
+        in.readStringList(installedLanguages);
+        List<String> pendingLanguages = new java.util.ArrayList<>();
+        in.readStringList(pendingLanguages);
+        List<String> supportedLanguages = new java.util.ArrayList<>();
+        in.readStringList(supportedLanguages);
+
+        this.mInstalledLanguages = installedLanguages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInstalledLanguages);
+        this.mPendingLanguages = pendingLanguages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPendingLanguages);
+        this.mSupportedLanguages = supportedLanguages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSupportedLanguages);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RecognitionSupport> CREATOR
+            = new Parcelable.Creator<RecognitionSupport>() {
+        @Override
+        public RecognitionSupport[] newArray(int size) {
+            return new RecognitionSupport[size];
+        }
+
+        @Override
+        public RecognitionSupport createFromParcel(@NonNull android.os.Parcel in) {
+            return new RecognitionSupport(in);
+        }
+    };
+
+    /**
+     * A builder for {@link RecognitionSupport}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull List<String> mInstalledLanguages;
+        private @NonNull List<String> mPendingLanguages;
+        private @NonNull List<String> mSupportedLanguages;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Support for this request is ready for use on this device for the returned languages.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setInstalledLanguages(@NonNull List<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mInstalledLanguages = value;
+            return this;
+        }
+
+        /** @see #setInstalledLanguages */
+        @DataClass.Generated.Member
+        public @NonNull Builder addInstalledLanguages(@NonNull String value) {
+            // You can refine this method's name by providing item's singular name, e.g.:
+            // @DataClass.PluralOf("item")) mItems = ...
+
+            if (mInstalledLanguages == null) setInstalledLanguages(new java.util.ArrayList<>());
+            mInstalledLanguages.add(value);
+            return this;
+        }
+
+        /**
+         * Support for this request is scheduled for download for the returned languages.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setPendingLanguages(@NonNull List<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mPendingLanguages = value;
+            return this;
+        }
+
+        /** @see #setPendingLanguages */
+        @DataClass.Generated.Member
+        public @NonNull Builder addPendingLanguages(@NonNull String value) {
+            // You can refine this method's name by providing item's singular name, e.g.:
+            // @DataClass.PluralOf("item")) mItems = ...
+
+            if (mPendingLanguages == null) setPendingLanguages(new java.util.ArrayList<>());
+            mPendingLanguages.add(value);
+            return this;
+        }
+
+        /**
+         * These languages are supported but need to be downloaded before use.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setSupportedLanguages(@NonNull List<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mSupportedLanguages = value;
+            return this;
+        }
+
+        /** @see #setSupportedLanguages */
+        @DataClass.Generated.Member
+        public @NonNull Builder addSupportedLanguages(@NonNull String value) {
+            // You can refine this method's name by providing item's singular name, e.g.:
+            // @DataClass.PluralOf("item")) mItems = ...
+
+            if (mSupportedLanguages == null) setSupportedLanguages(new java.util.ArrayList<>());
+            mSupportedLanguages.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull RecognitionSupport build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mInstalledLanguages = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mPendingLanguages = null;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mSupportedLanguages = null;
+            }
+            RecognitionSupport o = new RecognitionSupport(
+                    mInstalledLanguages,
+                    mPendingLanguages,
+                    mSupportedLanguages);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1639158640137L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/speech/RecognitionSupport.java",
+            inputSignatures = "private @android.annotation.NonNull java.util.List<java.lang.String> mInstalledLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\[email protected](genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/speech/RecognitionSupportCallback.java b/core/java/android/speech/RecognitionSupportCallback.java
new file mode 100644
index 0000000..9278e71
--- /dev/null
+++ b/core/java/android/speech/RecognitionSupportCallback.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+import android.annotation.NonNull;
+
+/**
+ * Used for receiving notifications from the SpeechRecognizer about the device support status for
+ * the given recognition request.
+ */
+public interface RecognitionSupportCallback {
+
+    /** Notifies the caller about the support for the given request. */
+    void onSupportResult(@NonNull RecognitionSupport recognitionSupport);
+
+    /** Notifies the caller about an error during the recognition support request */
+    void onError(@SpeechRecognizer.RecognitionError int error);
+}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 3cdd8b8..71c1e88 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -38,6 +38,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -46,6 +47,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.LinkedBlockingQueue;
 
@@ -112,7 +114,8 @@
             ERROR_TOO_MANY_REQUESTS,
             ERROR_SERVER_DISCONNECTED,
             ERROR_LANGUAGE_NOT_SUPPORTED,
-            ERROR_LANGUAGE_UNAVAILABLE
+            ERROR_LANGUAGE_UNAVAILABLE,
+            ERROR_CANNOT_CHECK_SUPPORT,
     })
     public @interface RecognitionError {}
 
@@ -155,19 +158,24 @@
     /** Requested language is supported, but not available currently (e.g. not downloaded yet). */
     public static final int ERROR_LANGUAGE_UNAVAILABLE = 13;
 
+    /** The service does not allow to check for support. */
+    public static final int ERROR_CANNOT_CHECK_SUPPORT = 14;
+
     /** action codes */
     private static final int MSG_START = 1;
     private static final int MSG_STOP = 2;
     private static final int MSG_CANCEL = 3;
     private static final int MSG_CHANGE_LISTENER = 4;
     private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;
+    private static final int MSG_CHECK_RECOGNITION_SUPPORT = 6;
+    private static final int MSG_TRIGGER_MODEL_DOWNLOAD = 7;
 
     /** The actual RecognitionService endpoint */
     private IRecognitionService mService;
 
     /** Context with which the manager was created */
     private final Context mContext;
-    
+
     /** Component to direct service intent to */
     private final ComponentName mServiceComponent;
 
@@ -197,6 +205,15 @@
                 case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
                     handleSetTemporaryComponent((ComponentName) msg.obj);
                     break;
+                case MSG_CHECK_RECOGNITION_SUPPORT:
+                    Pair<Intent, RecognitionSupportCallback> intentAndListener =
+                            (Pair<Intent, RecognitionSupportCallback>) msg.obj;
+                    handleCheckRecognitionSupport(
+                            intentAndListener.first, intentAndListener.second);
+                    break;
+                case MSG_TRIGGER_MODEL_DOWNLOAD:
+                    handleTriggerModelDownload((Intent) msg.obj);
+                    break;
             }
         }
     };
@@ -208,7 +225,7 @@
     private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>();
 
     /** The Listener that will receive all the callbacks */
-    private final InternalListener mListener = new InternalListener();
+    private final InternalRecognitionListener mListener = new InternalRecognitionListener();
 
     private final IBinder mClientToken = new Binder();
 
@@ -465,6 +482,38 @@
     }
 
     /**
+     * Checks whether {@code recognizerIntent} is supported by
+     * {@link SpeechRecognizer#startListening(Intent)}.
+     *
+     * @param recognizerIntent contains parameters for the recognition to be performed. The intent
+     *        may also contain optional extras. See {@link RecognizerIntent} for the list of
+     *        supported extras, any unlisted extra might be ignored.
+     * @param supportListener the listener on which to receive the support query results.
+     */
+    public void checkRecognitionSupport(
+            @NonNull Intent recognizerIntent,
+            @NonNull RecognitionSupportCallback supportListener) {
+        Objects.requireNonNull(recognizerIntent, "intent must not be null");
+        Objects.requireNonNull(supportListener, "listener must not be null");
+
+        putMessage(Message.obtain(mHandler, MSG_CHECK_RECOGNITION_SUPPORT,
+                Pair.create(recognizerIntent, supportListener)));
+    }
+
+    /**
+     * Attempts to download the support for the given {@code recognizerIntent}. This might trigger
+     * user interaction to approve the download. Callers can verify the status of the request via
+     * {@link #checkRecognitionSupport(Intent, RecognitionSupportCallback)}.
+     *
+     * @param recognizerIntent contains parameters for the recognition to be performed. The intent
+     *        may also contain optional extras, see {@link RecognizerIntent}.
+     */
+    public void triggerModelDownload(@NonNull Intent recognizerIntent) {
+        Objects.requireNonNull(recognizerIntent, "intent must not be null");
+        putMessage(Message.obtain(mHandler, MSG_TRIGGER_MODEL_DOWNLOAD));
+    }
+
+    /**
      * Sets a temporary component to power on-device speech recognizer.
      *
      * <p>This is only expected to be called in tests, system would reject calls from client apps.
@@ -503,7 +552,7 @@
         }
         try {
             mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
-            if (DBG) Log.d(TAG, "service start listening command succeded");
+            if (DBG) Log.d(TAG, "service start listening command succeeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "startListening() failed", e);
             mListener.onError(ERROR_CLIENT);
@@ -517,7 +566,7 @@
         }
         try {
             mService.stopListening(mListener);
-            if (DBG) Log.d(TAG, "service stop listening command succeded");
+            if (DBG) Log.d(TAG, "service stop listening command succeeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "stopListening() failed", e);
             mListener.onError(ERROR_CLIENT);
@@ -531,7 +580,7 @@
         }
         try {
             mService.cancel(mListener, /*isShutdown*/ false);
-            if (DBG) Log.d(TAG, "service cancel command succeded");
+            if (DBG) Log.d(TAG, "service cancel command succeeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "cancel() failed", e);
             mListener.onError(ERROR_CLIENT);
@@ -554,6 +603,35 @@
         }
     }
 
+    private void handleCheckRecognitionSupport(
+            Intent recognizerIntent, RecognitionSupportCallback recognitionSupportCallback) {
+        if (!maybeInitializeManagerService()) {
+            return;
+        }
+        try {
+            mService.checkRecognitionSupport(
+                    recognizerIntent,
+                    new InternalSupportCallback(recognitionSupportCallback));
+            if (DBG) Log.d(TAG, "service support command succeeded");
+        } catch (final RemoteException e) {
+            Log.e(TAG, "checkRecognitionSupport() failed", e);
+            mListener.onError(ERROR_CLIENT);
+        }
+    }
+
+    private void handleTriggerModelDownload(Intent recognizerIntent) {
+        if (!maybeInitializeManagerService()) {
+            return;
+        }
+        try {
+            mService.triggerModelDownload(recognizerIntent);
+            if (DBG) Log.d(TAG, "service download support command succeeded");
+        } catch (final RemoteException e) {
+            Log.e(TAG, "downloadModel() failed", e);
+            mListener.onError(ERROR_CLIENT);
+        }
+    }
+
     private boolean checkOpenConnection() {
         if (mService != null) {
             return true;
@@ -626,7 +704,7 @@
         }
     }
 
-    private boolean maybeInitializeManagerService() {
+    private synchronized boolean maybeInitializeManagerService() {
         if (mManagerService != null) {
             return true;
         }
@@ -678,7 +756,7 @@
      * Internal wrapper of IRecognitionListener which will propagate the results to
      * RecognitionListener
      */
-    private static class InternalListener extends IRecognitionListener.Stub {
+    private static class InternalRecognitionListener extends IRecognitionListener.Stub {
         private RecognitionListener mInternalListener;
 
         private static final int MSG_BEGINNING_OF_SPEECH = 1;
@@ -766,4 +844,42 @@
                     .sendToTarget();
         }
     }
+
+    private static class InternalSupportCallback extends IRecognitionSupportCallback.Stub {
+        private final RecognitionSupportCallback mCallback;
+
+        private static final int MSG_SUPPORT_RESULT = 1;
+        private static final int MSG_ERROR = 2;
+
+        private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                if (mCallback == null) {
+                    return;
+                }
+                switch (msg.what) {
+                    case MSG_SUPPORT_RESULT:
+                        mCallback.onSupportResult((RecognitionSupport) msg.obj);
+                        break;
+                    case MSG_ERROR:
+                        mCallback.onError((Integer) msg.obj);
+                        break;
+                }
+            }
+        };
+
+        private InternalSupportCallback(RecognitionSupportCallback callback) {
+            this.mCallback = callback;
+        }
+
+        @Override
+        public void onSupportResult(RecognitionSupport recognitionSupport) throws RemoteException {
+            Message.obtain(mInternalHandler, MSG_SUPPORT_RESULT, recognitionSupport).sendToTarget();
+        }
+
+        @Override
+        public void onError(int errorCode) throws RemoteException {
+            Message.obtain(mInternalHandler, MSG_ERROR, errorCode).sendToTarget();
+        }
+    }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9eaaa91..542de3f 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -131,6 +131,7 @@
                     mContext.getAttributionTag(), callback);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -152,6 +153,7 @@
             mSubscriptionChangedListenerMap.remove(listener);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -194,6 +196,7 @@
                     mContext.getAttributionTag(), callback);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -216,6 +219,7 @@
             mOpportunisticSubscriptionChangedListenerMap.remove(listener);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -304,6 +308,7 @@
             sRegistry.notifyCarrierNetworkChange(active);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -329,6 +334,7 @@
             sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -347,6 +353,7 @@
             sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -364,6 +371,7 @@
             sRegistry.notifyCallStateForAllSubs(state, incomingNumber);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -376,6 +384,7 @@
             sRegistry.notifySubscriptionInfoChanged();
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -388,6 +397,7 @@
             sRegistry.notifyOpportunisticSubscriptionInfoChanged();
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -404,6 +414,7 @@
             sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -421,6 +432,7 @@
             sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -439,6 +451,7 @@
             sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -454,6 +467,7 @@
             sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -469,6 +483,7 @@
             sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -490,6 +505,7 @@
                     slotIndex, subId, preciseState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -508,6 +524,7 @@
             sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -523,6 +540,7 @@
             sRegistry.notifyEmergencyNumberList(slotIndex, subId);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -538,6 +556,7 @@
             sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -553,6 +572,7 @@
             sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -570,6 +590,7 @@
             sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -583,6 +604,7 @@
             sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -615,6 +637,7 @@
                     SIM_ACTIVATION_TYPE_DATA, activationState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -634,6 +657,7 @@
                     SIM_ACTIVATION_TYPE_VOICE, activationState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -651,6 +675,7 @@
             sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -669,6 +694,7 @@
             sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -683,6 +709,7 @@
             sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -698,6 +725,7 @@
             sRegistry.notifySrvccStateChanged(subId, state);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -721,6 +749,7 @@
                 foregroundCallPreciseState, backgroundCallPreciseState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -741,6 +770,7 @@
             sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -755,6 +785,7 @@
             sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -769,7 +800,7 @@
         try {
             sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
         } catch (RemoteException ex) {
-
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -781,7 +812,7 @@
         try {
             sRegistry.notifyActiveDataSubIdChanged(activeDataSubId);
         } catch (RemoteException ex) {
-
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -814,6 +845,7 @@
             sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
                     chosenPlmn, domain, causeCode, additionalCauseCode);
         } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -830,6 +862,7 @@
             sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -846,6 +879,7 @@
             sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -862,6 +896,7 @@
             sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -880,6 +915,7 @@
                     allowedNetworkType);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -895,6 +931,7 @@
             sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 3ee1a90..fee23f4 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -16,6 +16,9 @@
 
 package android.text;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -85,6 +88,41 @@
     }
 
     /**
+     * Utility function to construct a BoringLayout instance.
+     *
+     * The spacing multiplier and additional amount spacing are not used by BoringLayout.
+     * {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will
+     * return 0.0.
+     *
+     * @param source the text to render
+     * @param paint the default paint for the layout
+     * @param outerWidth the wrapping width for the text
+     * @param align whether to left, right, or center the text
+     * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
+     *                line width
+     * @param includePad set whether to include extra space beyond font ascent and descent which is
+     *                   needed to avoid clipping in some scripts
+     * @param ellipsize whether to ellipsize the text if width of the text is longer than the
+     *                  requested width
+     * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
+     *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
+     *                        not used, {@code outerWidth} is used instead
+     * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts.
+     *                              False for keeping the first font's line height. If some glyphs
+     *                              requires larger vertical spaces, by passing true to this
+     *                              argument, the layout increase the line height to fit all glyphs.
+     */
+    public static @NonNull BoringLayout make(
+            @NonNull CharSequence source, @NonNull TextPaint paint,
+            @IntRange(from = 0) int outerWidth,
+            @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics,
+            boolean includePad, @NonNull TextUtils.TruncateAt ellipsize,
+            @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing) {
+        return new BoringLayout(source, paint, outerWidth, align, 1f, 0f, metrics, includePad,
+                ellipsize, ellipsizedWidth, useFallbackLineSpacing);
+    }
+
+    /**
      * Returns a BoringLayout for the specified text, potentially reusing
      * this one if it is already suitable.  The caller must make sure that
      * no one is still using this Layout.
@@ -109,7 +147,61 @@
         mEllipsizedStart = 0;
         mEllipsizedCount = 0;
 
-        init(source, paint, align, metrics, includePad, true);
+        init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
+        return this;
+    }
+
+    /**
+     * Returns a BoringLayout for the specified text, potentially reusing
+     * this one if it is already suitable.  The caller must make sure that
+     * no one is still using this Layout.
+     *
+     * The spacing multiplier and additional amount spacing are not used by BoringLayout.
+     * {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will
+     * return 0.0.
+     *
+     * @param source the text to render
+     * @param paint the default paint for the layout
+     * @param outerWidth the wrapping width for the text
+     * @param align whether to left, right, or center the text
+     * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
+     *                line width
+     * @param includePad set whether to include extra space beyond font ascent and descent which is
+     *                   needed to avoid clipping in some scripts
+     * @param ellipsize whether to ellipsize the text if width of the text is longer than the
+     *                  requested width
+     * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
+     *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
+     *                        not used, {@code outerWidth} is used instead
+     * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts.
+     *                              False for keeping the first font's line height. If some glyphs
+     *                              requires larger vertical spaces, by passing true to this
+     *                              argument, the layout increase the line height to fit all glyphs.
+     */
+    public @NonNull BoringLayout replaceOrMake(@NonNull CharSequence source,
+            @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth,
+            @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, boolean includePad,
+            @NonNull TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
+            boolean useFallbackLineSpacing) {
+        boolean trust;
+
+        if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
+            replaceWith(source, paint, outerWidth, align, 1f, 0f);
+
+            mEllipsizedWidth = outerWidth;
+            mEllipsizedStart = 0;
+            mEllipsizedCount = 0;
+            trust = true;
+        } else {
+            replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
+                    paint, outerWidth, align, 1f, 0f);
+
+            mEllipsizedWidth = ellipsizedWidth;
+            trust = false;
+        }
+
+        init(getText(), paint, align, metrics, includePad, trust,
+                useFallbackLineSpacing);
         return this;
     }
 
@@ -132,30 +224,13 @@
      *                  requested width
      * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
      *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
-     *                        not used, {@code outerwidth} is used instead
+     *                        not used, {@code outerWidth} is used instead
      */
     public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth,
             Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
             boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
-        boolean trust;
-
-        if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
-            replaceWith(source, paint, outerWidth, align, spacingMult, spacingAdd);
-
-            mEllipsizedWidth = outerWidth;
-            mEllipsizedStart = 0;
-            mEllipsizedCount = 0;
-            trust = true;
-        } else {
-            replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
-                    paint, outerWidth, align, spacingMult, spacingAdd);
-
-            mEllipsizedWidth = ellipsizedWidth;
-            trust = false;
-        }
-
-        init(getText(), paint, align, metrics, includePad, trust);
-        return this;
+        return replaceOrMake(source, paint, outerWidth, align, metrics,
+                includePad, ellipsize, ellipsizedWidth, false /* useFallbackLineSpacing */);
     }
 
     /**
@@ -178,7 +253,7 @@
         mEllipsizedStart = 0;
         mEllipsizedCount = 0;
 
-        init(source, paint, align, metrics, includePad, true);
+        init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
     }
 
     /**
@@ -194,14 +269,46 @@
      * @param includePad set whether to include extra space beyond font ascent and descent which is
      *                   needed to avoid clipping in some scripts
      * @param ellipsize whether to ellipsize the text if width of the text is longer than the
-     *                  requested {@code outerwidth}
+     *                  requested {@code outerWidth}
      * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
      *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
-     *                        not used, {@code outerwidth} is used instead
+     *                        not used, {@code outerWidth} is used instead
      */
     public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align,
             float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad,
             TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+        this(source, paint, outerWidth, align, spacingMult, spacingAdd, metrics, includePad,
+                ellipsize, ellipsizedWidth, false /* fallbackLineSpacing */);
+    }
+
+    /**
+     *
+     * @param source the text to render
+     * @param paint the default paint for the layout
+     * @param outerWidth the wrapping width for the text
+     * @param align whether to left, right, or center the text
+     * @param spacingMult this value is no longer used by BoringLayout
+     * @param spacingAdd this value is no longer used by BoringLayout
+     * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
+     *                line width
+     * @param includePad set whether to include extra space beyond font ascent and descent which is
+     *                   needed to avoid clipping in some scripts
+     * @param ellipsize whether to ellipsize the text if width of the text is longer than the
+     *                  requested {@code outerWidth}
+     * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
+     *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
+     *                        not used, {@code outerWidth} is used instead
+     * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts.
+     *                              False for keeping the first font's line height. If some glyphs
+     *                              requires larger vertical spaces, by passing true to this
+     *                              argument, the layout increase the line height to fit all glyphs.
+     */
+    public BoringLayout(
+            @NonNull CharSequence source, @NonNull TextPaint paint,
+            @IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult,
+            float spacingAdd, @NonNull BoringLayout.Metrics metrics, boolean includePad,
+            @NonNull TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
+            boolean useFallbackLineSpacing) {
         /*
          * It is silly to have to call super() and then replaceWith(),
          * but we can't use "this" for the callback until the call to
@@ -224,11 +331,12 @@
             trust = false;
         }
 
-        init(getText(), paint, align, metrics, includePad, trust);
+        init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing);
     }
 
     /* package */ void init(CharSequence source, TextPaint paint, Alignment align,
-            BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth) {
+            BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth,
+            boolean useFallbackLineSpacing) {
         int spacing;
 
         if (source instanceof String && align == Layout.Alignment.ALIGN_NORMAL) {
@@ -260,7 +368,7 @@
             TextLine line = TextLine.obtain();
             line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
                     Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
-                    mEllipsizedStart, mEllipsizedStart + mEllipsizedCount);
+                    mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
             mMax = (int) Math.ceil(line.metrics(null));
             TextLine.recycle(line);
         }
@@ -336,6 +444,27 @@
     @UnsupportedAppUsage
     public static Metrics isBoring(CharSequence text, TextPaint paint,
             TextDirectionHeuristic textDir, Metrics metrics) {
+        return isBoring(text, paint, textDir, false /* useFallbackLineSpacing */, metrics);
+    }
+
+    /**
+     * Returns null if not boring; the width, ascent, and descent in the
+     * provided Metrics object (or a new one if the provided one was null)
+     * if boring.
+     *
+     * @param text a text to be calculated text layout.
+     * @param paint a paint object used for styling.
+     * @param textDir a text direction.
+     * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts.
+     *                              False for keeping the first font's line height. If some glyphs
+     *                              requires larger vertical spaces, by passing true to this
+     *                              argument, the layout increase the line height to fit all glyphs.
+     * @param metrics the out metrics.
+     * @return metrics on success. null if text cannot be rendered by BoringLayout.
+     */
+    public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
+            @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
+            @Nullable Metrics metrics) {
         final int textLength = text.length();
         if (hasAnyInterestingChars(text, textLength)) {
            return null;  // There are some interesting characters. Not boring.
@@ -362,7 +491,8 @@
         line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
                 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
                 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
-                0 /* ellipsisEnd, 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));
         TextLine.recycle(line);
 
@@ -450,6 +580,11 @@
         return mEllipsizedWidth;
     }
 
+    @Override
+    public boolean isFallbackLineSpacingEnabled() {
+        return mUseFallbackLineSpacing;
+    }
+
     // Override draw so it will be faster.
     @Override
     public void draw(Canvas c, Path highlight, Paint highlightpaint,
@@ -471,6 +606,7 @@
 
     private String mDirect;
     private Paint mPaint;
+    private boolean mUseFallbackLineSpacing;
 
     /* package */ int mBottom, mDesc;   // for Direct
     private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index da3e9b6..95adb77 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -591,7 +591,8 @@
             } else {
                 tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops,
                         getEllipsisStart(lineNum),
-                        getEllipsisStart(lineNum) + getEllipsisCount(lineNum));
+                        getEllipsisStart(lineNum) + getEllipsisCount(lineNum),
+                        isFallbackLineSpacingEnabled());
                 if (justify) {
                     tl.justify(right - left - indentWidth);
                 }
@@ -960,6 +961,15 @@
     }
 
     /**
+     * Return true if the fallback line space is enabled in this Layout.
+     *
+     * @return true if the fallback line space is enabled. Otherwise returns false.
+     */
+    public boolean isFallbackLineSpacingEnabled() {
+        return false;
+    }
+
+    /**
      * Returns true if the character at offset and the preceding character
      * are at different run levels (and thus there's a split caret).
      * @param offset the offset
@@ -1231,7 +1241,8 @@
 
         TextLine tl = TextLine.obtain();
         tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
-                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
         float wid = tl.measure(offset - start, trailing, null);
         TextLine.recycle(tl);
 
@@ -1271,7 +1282,8 @@
 
         TextLine tl = TextLine.obtain();
         tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
-                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
         boolean[] trailings = primaryIsTrailingPreviousAllLineOffsets(line);
         if (!primary) {
             for (int offset = 0; offset < trailings.length; ++offset) {
@@ -1456,7 +1468,8 @@
         paint.setStartHyphenEdit(getStartHyphenEdit(line));
         paint.setEndHyphenEdit(getEndHyphenEdit(line));
         tl.set(paint, mText, start, end, dir, directions, hasTabs, tabStops,
-                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
         if (isJustificationRequired(line)) {
             tl.justify(getJustifyWidth(line));
         }
@@ -1486,7 +1499,8 @@
         paint.setStartHyphenEdit(getStartHyphenEdit(line));
         paint.setEndHyphenEdit(getEndHyphenEdit(line));
         tl.set(paint, mText, start, end, dir, directions, hasTabs, tabStops,
-                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
         if (isJustificationRequired(line)) {
             tl.justify(getJustifyWidth(line));
         }
@@ -1572,7 +1586,8 @@
         // XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here.
         tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs,
                 false, null,
-                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
         final HorizontalMeasurementProvider horizontal =
                 new HorizontalMeasurementProvider(line, primary);
 
@@ -1828,7 +1843,8 @@
         TextLine tl = TextLine.obtain();
         // XXX: we don't care about tabs
         tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null,
-                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
         caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
         TextLine.recycle(tl);
         return caret;
@@ -2202,7 +2218,8 @@
                 }
             }
             tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
-                    0 /* ellipsisStart */, 0 /* ellipsisEnd */);
+                    0 /* ellipsisStart */, 0 /* ellipsisEnd */,
+                    false /* use fallback line spacing. unused */);
             return margin + Math.abs(tl.metrics(null));
         } finally {
             TextLine.recycle(tl);
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 6a3c618..748d551 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.text.LineBreakConfig;
 import android.graphics.text.MeasuredText;
 import android.text.AutoGrowArray.ByteArray;
 import android.text.AutoGrowArray.FloatArray;
@@ -124,7 +125,7 @@
     // The native MeasuredParagraph.
     private @Nullable MeasuredText mMeasuredText;
 
-    // Following two objects are for avoiding object allocation.
+    // Following three objects are for avoiding object allocation.
     private @NonNull TextPaint mCachedPaint = new TextPaint();
     private @Nullable Paint.FontMetricsInt mCachedFm;
 
@@ -350,7 +351,8 @@
         if (mt.mSpanned == null) {
             // No style change by MetricsAffectingSpan. Just measure all text.
             mt.applyMetricsAffectingSpan(
-                    paint, null /* spans */, start, end, null /* native builder ptr */);
+                    paint, null /* lineBreakConfig */, null /* spans */, start, end,
+                    null /* native builder ptr */);
         } else {
             // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
             int spanEnd;
@@ -360,7 +362,8 @@
                         MetricAffectingSpan.class);
                 spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
                 mt.applyMetricsAffectingSpan(
-                        paint, spans, spanStart, spanEnd, null /* native builder ptr */);
+                        paint, null /* line break config */, spans, spanStart, spanEnd,
+                        null /* native builder ptr */);
             }
         }
         return mt;
@@ -373,6 +376,7 @@
      * result to recycle and returns recycle.
      *
      * @param paint the paint to be used for rendering the text.
+     * @param lineBreakConfig the line break configuration for text wrapping.
      * @param text the character sequence to be measured
      * @param start the inclusive start offset of the target region in the text
      * @param end the exclusive end offset of the target region in the text
@@ -386,6 +390,7 @@
      */
     public static @NonNull MeasuredParagraph buildForStaticLayout(
             @NonNull TextPaint paint,
+            @Nullable LineBreakConfig lineBreakConfig,
             @NonNull CharSequence text,
             @IntRange(from = 0) int start,
             @IntRange(from = 0) int end,
@@ -411,7 +416,8 @@
         } else {
             if (mt.mSpanned == null) {
                 // No style change by MetricsAffectingSpan. Just measure all text.
-                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, builder);
+                mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, start, end,
+                        builder);
                 mt.mSpanEndCache.append(end);
             } else {
                 // There may be a MetricsAffectingSpan. Split into span transitions and apply
@@ -424,7 +430,9 @@
                             MetricAffectingSpan.class);
                     spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
                                                        MetricAffectingSpan.class);
-                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, builder);
+                    // TODO: Update line break config with spans.
+                    mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, spanStart, spanEnd,
+                            builder);
                     mt.mSpanEndCache.append(spanEnd);
                 }
             }
@@ -500,12 +508,13 @@
     private void applyReplacementRun(@NonNull ReplacementSpan replacement,
                                      @IntRange(from = 0) int start,  // inclusive, in copied buffer
                                      @IntRange(from = 0) int end,  // exclusive, in copied buffer
+                                     @NonNull TextPaint paint,
                                      @Nullable MeasuredText.Builder builder) {
         // Use original text. Shouldn't matter.
         // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
         //       backward compatibility? or Should we initialize them for getFontMetricsInt?
         final float width = replacement.getSize(
-                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
+                paint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
         if (builder == null) {
             // Assigns all width to the first character. This is the same behavior as minikin.
             mWidths.set(start, width);
@@ -514,22 +523,24 @@
             }
             mWholeWidth += width;
         } else {
-            builder.appendReplacementRun(mCachedPaint, end - start, width);
+            builder.appendReplacementRun(paint, end - start, width);
         }
     }
 
     private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
                                @IntRange(from = 0) int end,  // exclusive, in copied buffer
+                               @NonNull TextPaint paint,
+                               @Nullable LineBreakConfig config,
                                @Nullable MeasuredText.Builder builder) {
 
         if (mLtrWithoutBidi) {
             // If the whole text is LTR direction, just apply whole region.
             if (builder == null) {
-                mWholeWidth += mCachedPaint.getTextRunAdvances(
+                mWholeWidth += paint.getTextRunAdvances(
                         mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
                         mWidths.getRawArray(), start);
             } else {
-                builder.appendStyleRun(mCachedPaint, end - start, false /* isRtl */);
+                builder.appendStyleRun(paint, config, end - start, false /* isRtl */);
             }
         } else {
             // If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -541,11 +552,11 @@
                     final boolean isRtl = (level & 0x1) != 0;
                     if (builder == null) {
                         final int levelLength = levelEnd - levelStart;
-                        mWholeWidth += mCachedPaint.getTextRunAdvances(
+                        mWholeWidth += paint.getTextRunAdvances(
                                 mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
                                 isRtl, mWidths.getRawArray(), levelStart);
                     } else {
-                        builder.appendStyleRun(mCachedPaint, levelEnd - levelStart, isRtl);
+                        builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl);
                     }
                     if (levelEnd == end) {
                         break;
@@ -559,6 +570,7 @@
 
     private void applyMetricsAffectingSpan(
             @NonNull TextPaint paint,
+            @Nullable LineBreakConfig lineBreakConfig,
             @Nullable MetricAffectingSpan[] spans,
             @IntRange(from = 0) int start,  // inclusive, in original text buffer
             @IntRange(from = 0) int end,  // exclusive, in original text buffer
@@ -595,9 +607,11 @@
         }
 
         if (replacement != null) {
-            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, builder);
+            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint,
+                    builder);
         } else {
-            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, builder);
+            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint,
+                    lineBreakConfig, builder);
         }
 
         if (needFontMetrics) {
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 152570f..be66db2 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.graphics.text.LineBreakConfig;
 import android.graphics.text.MeasuredText;
 import android.text.style.MetricAffectingSpan;
 
@@ -96,6 +97,9 @@
         // The hyphenation frequency for this measured text.
         private final @Layout.HyphenationFrequency int mHyphenationFrequency;
 
+        // The line break configuration for calculating text wrapping.
+        private final @Nullable LineBreakConfig mLineBreakConfig;
+
         /**
          * A builder for creating {@link Params}.
          */
@@ -113,6 +117,9 @@
             private @Layout.HyphenationFrequency int mHyphenationFrequency =
                     Layout.HYPHENATION_FREQUENCY_NORMAL;
 
+            // The line break configuration for calculating text wrapping.
+            private @Nullable LineBreakConfig mLineBreakConfig;
+
             /**
              * Builder constructor.
              *
@@ -130,6 +137,7 @@
                 mTextDir = params.mTextDir;
                 mBreakStrategy = params.mBreakStrategy;
                 mHyphenationFrequency = params.mHyphenationFrequency;
+                mLineBreakConfig = params.mLineBreakConfig;
             }
 
             /**
@@ -177,24 +185,41 @@
             }
 
             /**
+             * Set the line break config for the text wrapping.
+             *
+             * @param lineBreakConfig the newly line break configuration.
+             * @return this builder, useful for chaining.
+             * @see StaticLayout.Builder#setLineBreakConfig
+             */
+            public @NonNull Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
+                mLineBreakConfig = lineBreakConfig;
+                return this;
+            }
+
+            /**
              * Build the {@link Params}.
              *
              * @return the layout parameter
              */
             public @NonNull Params build() {
-                return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency);
+                return new Params(mPaint, mLineBreakConfig, mTextDir, mBreakStrategy,
+                        mHyphenationFrequency);
             }
         }
 
         // This is public hidden for internal use.
         // For the external developers, use Builder instead.
         /** @hide */
-        public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir,
-                @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
+        public Params(@NonNull TextPaint paint,
+                @Nullable LineBreakConfig lineBreakConfig,
+                @NonNull TextDirectionHeuristic textDir,
+                @Layout.BreakStrategy int strategy,
+                @Layout.HyphenationFrequency int frequency) {
             mPaint = paint;
             mTextDir = textDir;
             mBreakStrategy = strategy;
             mHyphenationFrequency = frequency;
+            mLineBreakConfig = lineBreakConfig;
         }
 
         /**
@@ -233,6 +258,15 @@
             return mHyphenationFrequency;
         }
 
+        /**
+         * Return the line break configuration for this text.
+         *
+         * @return the current line break configuration, null if no line break configuration is set.
+         */
+        public @Nullable LineBreakConfig getLineBreakConfig() {
+            return mLineBreakConfig;
+        }
+
         /** @hide */
         @IntDef(value = { UNUSABLE, NEED_RECOMPUTE, USABLE })
         @Retention(RetentionPolicy.SOURCE)
@@ -262,8 +296,9 @@
         /** @hide */
         public @CheckResultUsableResult int checkResultUsable(@NonNull TextPaint paint,
                 @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
-                @Layout.HyphenationFrequency int frequency) {
+                @Layout.HyphenationFrequency int frequency, @Nullable LineBreakConfig lbConfig) {
             if (mBreakStrategy == strategy && mHyphenationFrequency == frequency
+                    && isLineBreakEquals(mLineBreakConfig, lbConfig)
                     && mPaint.equalsForTextMeasurement(paint)) {
                 return mTextDir == textDir ? USABLE : NEED_RECOMPUTE;
             } else {
@@ -272,6 +307,29 @@
         }
 
         /**
+         * Check the two LineBreakConfig instances are equal.
+         * This method assumes they are equal if one parameter is null and the other parameter has
+         * a LineBreakStyle value of LineBreakConfig.LINE_BREAK_STYLE_NONE.
+         *
+         * @param o1 the first LineBreakConfig instance.
+         * @param o2 the second LineBreakConfig instance.
+         * @return true if the two LineBreakConfig instances are equal.
+         */
+        private boolean isLineBreakEquals(LineBreakConfig o1, LineBreakConfig o2) {
+            if (Objects.equals(o1, o2)) {
+                return true;
+            }
+            if (o1 == null && (o2 != null
+                    && o2.getLineBreakStyle() == LineBreakConfig.LINE_BREAK_STYLE_NONE)) {
+                return true;
+            } else if (o2 == null && (o1 != null
+                    && o1.getLineBreakStyle() == LineBreakConfig.LINE_BREAK_STYLE_NONE)) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
          * Check if the same text layout.
          *
          * @return true if this and the given param result in the same text layout
@@ -286,21 +344,28 @@
             }
             Params param = (Params) o;
             return checkResultUsable(param.mPaint, param.mTextDir, param.mBreakStrategy,
-                    param.mHyphenationFrequency) == Params.USABLE;
+                    param.mHyphenationFrequency, param.mLineBreakConfig) == Params.USABLE;
         }
 
         @Override
         public int hashCode() {
             // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals.
+            int lineBreakStyle = (mLineBreakConfig != null)
+                    ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE;
             return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(),
                     mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(),
                     mPaint.getTextLocales(), mPaint.getTypeface(),
                     mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir,
-                    mBreakStrategy, mHyphenationFrequency);
+                    mBreakStrategy, mHyphenationFrequency, lineBreakStyle);
         }
 
         @Override
         public String toString() {
+            int lineBreakStyle = (mLineBreakConfig != null)
+                    ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE;
+            int lineBreakWordStyle = (mLineBreakConfig != null)
+                    ? mLineBreakConfig.getLineBreakWordStyle()
+                            : LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
             return "{"
                 + "textSize=" + mPaint.getTextSize()
                 + ", textScaleX=" + mPaint.getTextScaleX()
@@ -313,6 +378,8 @@
                 + ", textDir=" + mTextDir
                 + ", breakStrategy=" + mBreakStrategy
                 + ", hyphenationFrequency=" + mHyphenationFrequency
+                + ", lineBreakStyle=" + lineBreakStyle
+                + ", lineBreakWordStyle=" + lineBreakWordStyle
                 + "}";
         }
     };
@@ -369,7 +436,8 @@
             final PrecomputedText.Params hintParams = hintPct.getParams();
             final @Params.CheckResultUsableResult int checkResult =
                     hintParams.checkResultUsable(params.mPaint, params.mTextDir,
-                            params.mBreakStrategy, params.mHyphenationFrequency);
+                            params.mBreakStrategy, params.mHyphenationFrequency,
+                            params.mLineBreakConfig);
             switch (checkResult) {
                 case Params.USABLE:
                     return hintPct;
@@ -418,9 +486,9 @@
             final int paraStart = pct.getParagraphStart(i);
             final int paraEnd = pct.getParagraphEnd(i);
             result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
-                    params.getTextPaint(), pct, paraStart, paraEnd, params.getTextDirection(),
-                    hyphenationMode, computeLayout, pct.getMeasuredParagraph(i),
-                    null /* no recycle */)));
+                    params.getTextPaint(), params.getLineBreakConfig(), pct, paraStart, paraEnd,
+                    params.getTextDirection(), hyphenationMode, computeLayout,
+                    pct.getMeasuredParagraph(i), null /* no recycle */)));
         }
         return result.toArray(new ParagraphInfo[result.size()]);
     }
@@ -456,8 +524,9 @@
             }
 
             result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
-                    params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
-                    hyphenationMode, computeLayout, null /* no hint */, null /* no recycle */)));
+                    params.getTextPaint(), params.getLineBreakConfig(), text, paraStart, paraEnd,
+                    params.getTextDirection(), hyphenationMode, computeLayout, null /* no hint */,
+                    null /* no recycle */)));
         }
         return result.toArray(new ParagraphInfo[result.size()]);
     }
@@ -544,11 +613,11 @@
     public @Params.CheckResultUsableResult int checkResultUsable(@IntRange(from = 0) int start,
             @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir,
             @NonNull TextPaint paint, @Layout.BreakStrategy int strategy,
-            @Layout.HyphenationFrequency int frequency) {
+            @Layout.HyphenationFrequency int frequency, @NonNull LineBreakConfig lbConfig) {
         if (mStart != start || mEnd != end) {
             return Params.UNUSABLE;
         } else {
-            return mParams.checkResultUsable(paint, textDir, strategy, frequency);
+            return mParams.checkResultUsable(paint, textDir, strategy, frequency, lbConfig);
         }
     }
 
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4789231..b10fc37 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Paint;
+import android.graphics.text.LineBreakConfig;
 import android.graphics.text.LineBreaker;
 import android.os.Build;
 import android.text.style.LeadingMarginSpan;
@@ -403,6 +404,21 @@
         }
 
         /**
+         * Set the line break configuration. The line break will be passed to native used for
+         * calculating the text wrapping. The default value of the line break style is
+         * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE}
+         *
+         * @param lineBreakConfig the line break configuration for text wrapping.
+         * @return this builder, useful for chaining.
+         * @see android.widget.TextView#setLineBreakConfig
+         */
+        @NonNull
+        public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
+            mLineBreakConfig = lineBreakConfig;
+            return this;
+        }
+
+        /**
          * Build the {@link StaticLayout} after options have been set.
          *
          * <p>Note: the builder object must not be reused in any way after calling this
@@ -438,6 +454,7 @@
         @Nullable private int[] mRightIndents;
         private int mJustificationMode;
         private boolean mAddLastLineLineSpacing;
+        private LineBreakConfig mLineBreakConfig;
 
         private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
@@ -612,7 +629,6 @@
         TextPaint paint = b.mPaint;
         int outerWidth = b.mWidth;
         TextDirectionHeuristic textDir = b.mTextDir;
-        final boolean fallbackLineSpacing = b.mFallbackLineSpacing;
         float spacingmult = b.mSpacingMult;
         float spacingadd = b.mSpacingAdd;
         float ellipsizedWidth = b.mEllipsizedWidth;
@@ -630,6 +646,7 @@
         mLineCount = 0;
         mEllipsized = false;
         mMaxLineHeight = mMaximumVisibleLineCount < 1 ? 0 : DEFAULT_MAX_LINE_HEIGHT;
+        mFallbackLineSpacing = b.mFallbackLineSpacing;
 
         int v = 0;
         boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
@@ -670,7 +687,7 @@
             PrecomputedText precomputed = (PrecomputedText) source;
             final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
                     precomputed.checkResultUsable(bufStart, bufEnd, textDir, paint,
-                            b.mBreakStrategy, b.mHyphenationFrequency);
+                            b.mBreakStrategy, b.mHyphenationFrequency, b.mLineBreakConfig);
             switch (checkResult) {
                 case PrecomputedText.Params.UNUSABLE:
                     break;
@@ -680,6 +697,7 @@
                                 .setBreakStrategy(b.mBreakStrategy)
                                 .setHyphenationFrequency(b.mHyphenationFrequency)
                                 .setTextDirection(textDir)
+                                .setLineBreakConfig(b.mLineBreakConfig)
                                 .build();
                     precomputed = PrecomputedText.create(precomputed, newParams);
                     paragraphInfo = precomputed.getParagraphInfo();
@@ -692,8 +710,8 @@
         }
 
         if (paragraphInfo == null) {
-            final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
-                    b.mBreakStrategy, b.mHyphenationFrequency);
+            final PrecomputedText.Params param = new PrecomputedText.Params(paint,
+                    b.mLineBreakConfig, textDir, b.mBreakStrategy, b.mHyphenationFrequency);
             paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart,
                     bufEnd, false /* computeLayout */);
         }
@@ -867,17 +885,17 @@
 
                     boolean moreChars = (endPos < bufEnd);
 
-                    final int ascent = fallbackLineSpacing
+                    final int ascent = mFallbackLineSpacing
                             ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
                             : fmAscent;
-                    final int descent = fallbackLineSpacing
+                    final int descent = mFallbackLineSpacing
                             ? Math.max(fmDescent, Math.round(descents[breakIndex]))
                             : fmDescent;
 
                     // The fallback ascent/descent may be larger than top/bottom of the default font
                     // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
                     // clipping.
-                    if (fallbackLineSpacing) {
+                    if (mFallbackLineSpacing) {
                         if (ascent < fmTop) {
                             fmTop = ascent;
                         }
@@ -1381,6 +1399,11 @@
         return mEllipsizedWidth;
     }
 
+    @Override
+    public boolean isFallbackLineSpacingEnabled() {
+        return mFallbackLineSpacing;
+    }
+
     /**
      * Return the total height of this layout.
      *
@@ -1407,6 +1430,7 @@
     @UnsupportedAppUsage
     private int mColumns;
     private int mEllipsizedWidth;
+    private boolean mFallbackLineSpacing;
 
     /**
      * Keeps track if ellipsize is applied to the text.
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1a7ec7f..49e2111 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -71,6 +71,8 @@
     private Spanned mSpanned;
     private PrecomputedText mComputed;
 
+    private boolean mUseFallbackExtent = false;
+
     // The start and end of a potentially existing ellipsis on this text line.
     // We use them to filter out replacement and metric affecting spans on ellipsized away chars.
     private int mEllipsisStart;
@@ -141,6 +143,7 @@
         tl.mTabs = null;
         tl.mChars = null;
         tl.mComputed = null;
+        tl.mUseFallbackExtent = false;
 
         tl.mMetricAffectingSpanSpanSet.recycle();
         tl.mCharacterStyleSpanSet.recycle();
@@ -171,17 +174,20 @@
      * @param ellipsisStart the start of the ellipsis relative to the line
      * @param ellipsisEnd the end of the ellipsis relative to the line. When there
      *                    is no ellipsis, this should be equal to ellipsisStart.
+     * @param useFallbackLineSpacing true for enabling fallback line spacing. false for disabling
+     *                              fallback line spacing.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
             Directions directions, boolean hasTabs, TabStops tabStops,
-            int ellipsisStart, int ellipsisEnd) {
+            int ellipsisStart, int ellipsisEnd, boolean useFallbackLineSpacing) {
         mPaint = paint;
         mText = text;
         mStart = start;
         mLen = limit - start;
         mDir = dir;
         mDirections = directions;
+        mUseFallbackExtent = useFallbackLineSpacing;
         if (mDirections == null) {
             throw new IllegalArgumentException("Directions cannot be null");
         }
@@ -845,6 +851,30 @@
                 previousLeading);
     }
 
+    private void expandMetricsFromPaint(TextPaint wp, int start, int end,
+            int contextStart, int contextEnd, boolean runIsRtl, FontMetricsInt fmi) {
+
+        final int previousTop     = fmi.top;
+        final int previousAscent  = fmi.ascent;
+        final int previousDescent = fmi.descent;
+        final int previousBottom  = fmi.bottom;
+        final int previousLeading = fmi.leading;
+
+        int count = end - start;
+        int contextCount = contextEnd - contextStart;
+        if (mCharsValid) {
+            wp.getFontMetricsInt(mChars, start, count, contextStart, contextCount, runIsRtl,
+                    fmi);
+        } else {
+            wp.getFontMetricsInt(mText, mStart + start, count, mStart + contextStart, contextCount,
+                    runIsRtl, fmi);
+        }
+
+        updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
+                previousLeading);
+    }
+
+
     static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent,
             int previousDescent, int previousBottom, int previousLeading) {
         fmi.top     = Math.min(fmi.top,     previousTop);
@@ -949,6 +979,10 @@
             shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
         }
 
+        if (mUseFallbackExtent && fmi != null) {
+            expandMetricsFromPaint(wp, start, end, contextStart, contextEnd, runIsRtl, fmi);
+        }
+
         if (c != null) {
             if (wp.bgColor != 0) {
                 int previousColor = wp.getColor();
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
index 02fd7b4..a1d6cc8 100644
--- a/core/java/android/text/TextShaper.java
+++ b/core/java/android/text/TextShaper.java
@@ -222,7 +222,8 @@
                     mp.getDirections(0, count),
                     false /* tabstop is not supported */,
                     null,
-                    -1, -1 // ellipsis is not supported.
+                    -1, -1, // ellipsis is not supported.
+                    false /* fallback line spacing is not used */
             );
             tl.shape(consumer);
         } finally {
diff --git a/core/java/android/util/Dumpable.java b/core/java/android/util/Dumpable.java
new file mode 100644
index 0000000..79c576d
--- /dev/null
+++ b/core/java/android/util/Dumpable.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents an object whose state can be dumped into a {@link PrintWriter}.
+ */
+public interface Dumpable {
+
+    /**
+     * Gets the name of the {@link Dumpable}.
+     *
+     * @return class name, by default.
+     */
+    @NonNull
+    default String getDumpableName() {
+        return getClass().getName();
+    }
+
+    //TODO(b/149254050): decide whether it should take a ParcelFileDescription as well.
+
+    /**
+     * Dumps the internal state into the given {@code writer}.
+     *
+     * @param writer writer to be written to
+     * @param args optional list of arguments
+     */
+    void dump(@NonNull PrintWriter writer, @Nullable String[] args);
+}
diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java
new file mode 100644
index 0000000..04d19dc
--- /dev/null
+++ b/core/java/android/util/DumpableContainer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.NonNull;
+
+/**
+ * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself
+ * is dumped.
+ */
+public interface DumpableContainer {
+
+    /**
+     * Adds the given {@link Dumpable dumpable} to the container.
+     *
+     * <p>If a dumpable with the same {@link Dumpable#getDumpableName() name} was added before, this
+     * call is ignored.
+     *
+     * @param dumpable dumpable to be added.
+     *
+     * @throws IllegalArgumentException if the {@link Dumpable#getDumpableName() dumpable name} is
+     * {@code null}.
+     *
+     * @return {@code true} if the dumpable was added, {@code false} if the call was ignored.
+     */
+    boolean addDumpable(@NonNull Dumpable dumpable);
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 52f1fae..96a1fc6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -81,6 +81,7 @@
         DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
         DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
+        DEFAULT_FLAGS.put("settings_search_always_expand", "false");
         DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
     }
diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java
index dc93a47..e8d96d8 100644
--- a/core/java/android/util/SparseDoubleArray.java
+++ b/core/java/android/util/SparseDoubleArray.java
@@ -81,9 +81,17 @@
      * if no such mapping has been made.
      */
     public double get(int key) {
+        return get(key, 0);
+    }
+
+    /**
+     * Gets the double mapped from the specified key, or the specified value
+     * if no such mapping has been made.
+     */
+    public double get(int key, double valueIfKeyNotFound) {
         final int index = mValues.indexOfKey(key);
         if (index < 0) {
-            return 0.0d;
+            return valueIfKeyNotFound;
         }
         return valueAt(index);
     }
@@ -105,7 +113,7 @@
      * <p>This differs from {@link #put} because instead of replacing any previous value, it adds
      * (in the numerical sense) to it.
      */
-    public void add(int key, double summand) {
+    public void incrementValue(int key, double summand) {
         final double oldValue = get(key);
         put(key, oldValue + summand);
     }
@@ -138,6 +146,13 @@
     }
 
     /**
+     * Removes all key-value mappings from this SparseDoubleArray.
+     */
+    public void clear() {
+        mValues.clear();
+    }
+
+    /**
      * {@inheritDoc}
      *
      * <p>This implementation composes a string by iterating over its mappings.
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index f2bc0c5..7185972 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -164,6 +164,30 @@
     }
 
     /**
+     * Adds a mapping from the specified key to the specified value,
+     * <b>adding</b> its value to the previous mapping from the specified key if there
+     * was one.
+     *
+     * <p>This differs from {@link #put} because instead of replacing any previous value, it adds
+     * (in the numerical sense) to it.
+     *
+     * @hide
+     */
+    public void incrementValue(int key, long summand) {
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+        if (i >= 0) {
+            mValues[i] += summand;
+        } else {
+            i = ~i;
+
+            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = GrowingArrayUtils.insert(mValues, mSize, i, summand);
+            mSize++;
+        }
+    }
+
+    /**
      * Returns the number of key-value mappings that this SparseLongArray
      * currently stores.
      */
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 41b749e..bff5426 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -27,7 +27,7 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningDetails.SignatureSchemeVersion;
-import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.os.Build;
@@ -380,7 +380,7 @@
             // Gather certs from AndroidManifest.xml, which every APK must have, as an optimization
             // to not need to verify the whole APK when verifyFUll == false.
             final ZipEntry manifestEntry = jarFile.findEntry(
-                    ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
+                    ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME);
             if (manifestEntry == null) {
                 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                         "Package " + apkPath + " has no manifest");
@@ -394,7 +394,7 @@
             if (ArrayUtils.isEmpty(lastCerts)) {
                 return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
                         + apkPath + " has no certificates at entry "
-                        + ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
+                        + ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME);
             }
             lastSigs = convertToSignatures(lastCerts);
 
@@ -407,7 +407,7 @@
 
                     final String entryName = entry.getName();
                     if (entryName.startsWith("META-INF/")) continue;
-                    if (entryName.equals(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME)) continue;
+                    if (entryName.equals(ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME)) continue;
 
                     toVerify.add(entry);
                 }
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index af76cb9..e679f29 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -66,14 +66,19 @@
      * @hide
      */
     public void setBatchingEnabled(boolean batchingEnabled) {
+        if (mBatchingEnabled == batchingEnabled) {
+            return;
+        }
+
         mBatchingEnabled = batchingEnabled;
+        mHandler.removeCallbacks(mConsumeBatchedInputEvents);
         if (!batchingEnabled) {
             unscheduleBatchedInput();
             mHandler.post(mConsumeBatchedInputEvents);
         }
     }
 
-    void doConsumeBatchedInput(long frameTimeNanos) {
+    protected void doConsumeBatchedInput(long frameTimeNanos) {
         if (mBatchedInputScheduled) {
             mBatchedInputScheduled = false;
             if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) {
@@ -109,4 +114,38 @@
         }
     }
     private final BatchedInputRunnable mBatchedInputRunnable = new BatchedInputRunnable();
+
+    /**
+     * A {@link BatchedInputEventReceiver} that reports events to an {@link InputEventListener}.
+     * @hide
+     */
+    public static class SimpleBatchedInputEventReceiver extends BatchedInputEventReceiver {
+
+        /** @hide */
+        public interface InputEventListener {
+            /**
+             * Process the input event.
+             * @return handled
+             */
+            boolean onInputEvent(InputEvent event);
+        }
+
+        protected InputEventListener mListener;
+
+        public SimpleBatchedInputEventReceiver(InputChannel inputChannel, Looper looper,
+                Choreographer choreographer, InputEventListener listener) {
+            super(inputChannel, looper, choreographer);
+            mListener = listener;
+        }
+
+        @Override
+        public void onInputEvent(InputEvent event) {
+            boolean handled = false;
+            try {
+                handled = mListener.onInputEvent(event);
+            } finally {
+                finishInputEvent(event, handled);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index be172f7..b8eb602 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -19,6 +19,9 @@
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.FrameInfo;
@@ -151,10 +154,15 @@
     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
 
-    // All frame callbacks posted by applications have this token.
+    // All frame callbacks posted by applications have this token or EXTENDED_FRAME_CALLBACK_TOKEN.
     private static final Object FRAME_CALLBACK_TOKEN = new Object() {
         public String toString() { return "FRAME_CALLBACK_TOKEN"; }
     };
+    private static final Object EXTENDED_FRAME_CALLBACK_TOKEN = new Object() {
+        public String toString() {
+            return "EXTENDED_FRAME_CALLBACK_TOKEN";
+        }
+    };
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final Object mLock = new Object();
@@ -484,6 +492,24 @@
     }
 
     /**
+     * Posts an extended frame callback to run on the next frame.
+     * <p>
+     * The callback runs once then is automatically removed.
+     * </p>
+     *
+     * @param callback The extended frame callback to run during the next frame.
+     *
+     * @see #removeExtendedFrameCallback
+     */
+    public void postExtendedFrameCallback(@NonNull ExtendedFrameCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN, 0);
+    }
+
+    /**
      * Removes callbacks that have the specified action and token.
      *
      * @param callbackType The callback type.
@@ -573,6 +599,21 @@
     }
 
     /**
+     * Removes a previously posted extended frame callback.
+     *
+     * @param callback The extended frame callback to remove.
+     *
+     * @see #postExtendedFrameCallback
+     */
+    public void removeExtendedFrameCallback(@Nullable ExtendedFrameCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        removeCallbacksInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN);
+    }
+
+    /**
      * Gets the time when the current frame started.
      * <p>
      * This method provides the time in milliseconds when the frame started being rendered.
@@ -673,7 +714,7 @@
      * @hide
      */
     public long getVsyncId() {
-        return mLastVsyncEventData.id;
+        return mLastVsyncEventData.preferredFrameTimeline().vsyncId;
     }
 
     /**
@@ -684,7 +725,7 @@
      * @hide
      */
     public long getFrameDeadline() {
-        return mLastVsyncEventData.frameDeadline;
+        return mLastVsyncEventData.preferredFrameTimeline().deadline;
     }
 
     void setFPSDivisor(int divisor) {
@@ -705,8 +746,9 @@
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                 Trace.traceBegin(Trace.TRACE_TAG_VIEW,
-                        "Choreographer#doFrame " + vsyncEventData.id);
+                        "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId);
             }
+            FrameData frameData = new FrameData(frameTimeNanos, vsyncEventData);
             synchronized (mLock) {
                 if (!mFrameScheduled) {
                     traceMessage("Frame not scheduled");
@@ -737,6 +779,7 @@
                                 + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                     }
                     frameTimeNanos = startNanos - lastFrameOffset;
+                    frameData.setFrameTimeNanos(frameTimeNanos);
                 }
 
                 if (frameTimeNanos < mLastFrameTimeNanos) {
@@ -758,8 +801,10 @@
                     }
                 }
 
-                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
-                        vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
+                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos,
+                        vsyncEventData.preferredFrameTimeline().vsyncId,
+                        vsyncEventData.preferredFrameTimeline().deadline, startNanos,
+                        vsyncEventData.frameInterval);
                 mFrameScheduled = false;
                 mLastFrameTimeNanos = frameTimeNanos;
                 mLastFrameIntervalNanos = frameIntervalNanos;
@@ -769,17 +814,17 @@
             AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
 
             mFrameInfo.markInputHandlingStart();
-            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos);
 
             mFrameInfo.markAnimationsStart();
-            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
-            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
+            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData,
                     frameIntervalNanos);
 
             mFrameInfo.markPerformTraversalsStart();
-            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos);
 
-            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, frameIntervalNanos);
         } finally {
             AnimationUtils.unlockAnimationClock();
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -793,8 +838,9 @@
         }
     }
 
-    void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
+    void doCallbacks(int callbackType, FrameData frameData, long frameIntervalNanos) {
         CallbackRecord callbacks;
+        long frameTimeNanos = frameData.mFrameTimeNanos;
         synchronized (mLock) {
             // We use "now" to determine when callbacks become due because it's possible
             // for earlier processing phases in a frame to post callbacks that should run
@@ -831,6 +877,7 @@
                     }
                     frameTimeNanos = now - lastFrameOffset;
                     mLastFrameTimeNanos = frameTimeNanos;
+                    frameData.setFrameTimeNanos(frameTimeNanos);
                 }
             }
         }
@@ -842,7 +889,7 @@
                             + ", action=" + c.action + ", token=" + c.token
                             + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                 }
-                c.run(frameTimeNanos);
+                c.run(frameData);
             }
         } finally {
             synchronized (mLock) {
@@ -942,6 +989,130 @@
         public void doFrame(long frameTimeNanos);
     }
 
+    /** Holds data that describes one possible VSync frame event to render at. */
+    public static class FrameTimeline {
+        static final FrameTimeline INVALID_FRAME_TIMELINE = new FrameTimeline(
+                FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE);
+
+        FrameTimeline(long vsyncId, long expectedPresentTimeNanos, long deadlineNanos) {
+            this.mVsyncId = vsyncId;
+            this.mExpectedPresentTimeNanos = expectedPresentTimeNanos;
+            this.mDeadlineNanos = deadlineNanos;
+        }
+
+        private long mVsyncId;
+        private long mExpectedPresentTimeNanos;
+        private long mDeadlineNanos;
+
+        /**
+         * The id that corresponds to this frame timeline, used to correlate a frame
+         * produced by HWUI with the timeline data stored in Surface Flinger.
+         */
+        public long getVsyncId() {
+            return mVsyncId;
+        }
+
+        /** Sets the vsync ID. */
+        void resetVsyncId() {
+            mVsyncId = FrameInfo.INVALID_VSYNC_ID;
+        }
+
+        /**
+         * The time in {@link System#nanoTime()} timebase which this frame is expected to be
+         * presented.
+         */
+        public long getExpectedPresentTimeNanos() {
+            return mExpectedPresentTimeNanos;
+        }
+
+        /**
+         * The time in  {@link System#nanoTime()} timebase which this frame needs to be ready by.
+         */
+        public long getDeadlineNanos() {
+            return mDeadlineNanos;
+        }
+    }
+
+    /**
+     * The payload for {@link ExtendedFrameCallback} which includes frame information such as when
+     * the frame started being rendered, and multiple possible frame timelines and their
+     * information including deadline and expected present time.
+     */
+    public static class FrameData {
+        static final FrameTimeline[] INVALID_FRAME_TIMELINES = new FrameTimeline[0];
+        FrameData() {
+            this.mFrameTimelines = INVALID_FRAME_TIMELINES;
+            this.mPreferredFrameTimeline = FrameTimeline.INVALID_FRAME_TIMELINE;
+        }
+
+        FrameData(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
+            FrameTimeline[] frameTimelines =
+                    new FrameTimeline[vsyncEventData.frameTimelines.length];
+            for (int i = 0; i <  vsyncEventData.frameTimelines.length; i++) {
+                DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline =
+                        vsyncEventData.frameTimelines[i];
+                frameTimelines[i] = new FrameTimeline(frameTimeline.vsyncId,
+                        frameTimeline.expectedPresentTime, frameTimeline.deadline);
+            }
+            this.mFrameTimeNanos = frameTimeNanos;
+            this.mFrameTimelines = frameTimelines;
+            this.mPreferredFrameTimeline =
+                    frameTimelines[vsyncEventData.preferredFrameTimelineIndex];
+        }
+
+        private long mFrameTimeNanos;
+        private final FrameTimeline[] mFrameTimelines;
+        private final FrameTimeline mPreferredFrameTimeline;
+
+        void setFrameTimeNanos(long frameTimeNanos) {
+            mFrameTimeNanos = frameTimeNanos;
+            for (FrameTimeline ft : mFrameTimelines) {
+                // The ID is no longer valid because the frame time that was registered with the ID
+                // no longer matches.
+                // TODO(b/205721584): Ask SF for valid vsync information.
+                ft.resetVsyncId();
+            }
+        }
+
+        /** The time in nanoseconds when the frame started being rendered. */
+        public long getFrameTimeNanos() {
+            return mFrameTimeNanos;
+        }
+
+        /** The possible frame timelines, sorted chronologically. */
+        @NonNull
+        @SuppressLint("ArrayReturn") // For API consistency and speed.
+        public FrameTimeline[] getFrameTimelines() {
+            return mFrameTimelines;
+        }
+
+        /** The platform-preferred frame timeline. */
+        @NonNull
+        public FrameTimeline getPreferredFrameTimeline() {
+            return mPreferredFrameTimeline;
+        }
+    }
+
+    /**
+     * Implement this interface to receive a callback to start the next frame. The callback is
+     * invoked on the {@link Looper} thread to which the {@link Choreographer} is attached. The
+     * callback payload contains information about multiple possible frames, allowing choice of
+     * the appropriate frame based on latency requirements.
+     *
+     * @see FrameCallback
+     */
+    public interface ExtendedFrameCallback {
+        /**
+         * Called when a new display frame is being rendered.
+         *
+         * @param data The payload which includes frame information. Divide nanosecond values by
+         *             {@code 1000000} to convert it to the {@link SystemClock#uptimeMillis()}
+         *             time base.
+         * @see FrameCallback#doFrame
+         **/
+        void onVsync(@NonNull FrameData data);
+    }
+
     private final class FrameHandler extends Handler {
         public FrameHandler(Looper looper) {
             super(looper);
@@ -983,7 +1154,8 @@
             try {
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                     Trace.traceBegin(Trace.TRACE_TAG_VIEW,
-                            "Choreographer#onVsync " + vsyncEventData.id);
+                            "Choreographer#onVsync "
+                                    + vsyncEventData.preferredFrameTimeline().vsyncId);
                 }
                 // Post the vsync event to the Handler.
                 // The idea is to prevent incoming vsync events from completely starving
@@ -1026,7 +1198,9 @@
     private static final class CallbackRecord {
         public CallbackRecord next;
         public long dueTime;
-        public Object action; // Runnable or FrameCallback
+        /** Runnable or FrameCallback or ExtendedFrameCallback object. */
+        public Object action;
+        /** Denotes the action type. */
         public Object token;
 
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1037,6 +1211,14 @@
                 ((Runnable)action).run();
             }
         }
+
+        void run(FrameData frameData) {
+            if (token == EXTENDED_FRAME_CALLBACK_TOKEN) {
+                ((ExtendedFrameCallback) action).onVsync(frameData);
+            } else {
+                run(frameData.getFrameTimeNanos());
+            }
+        }
     }
 
     private final class CallbackQueue {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3cc51c7..d6e074f 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
 import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -138,6 +139,24 @@
     public static final int INVALID_DISPLAY = -1;
 
     /**
+     * Invalid resolution width.
+     * @hide
+     */
+    public static final int INVALID_DISPLAY_WIDTH = -1;
+
+    /**
+     * Invalid resolution height.
+     * @hide
+     */
+    public static final int INVALID_DISPLAY_HEIGHT = -1;
+
+    /**
+     * Invalid refresh rate.
+     * @hide
+     */
+    public static final float INVALID_DISPLAY_REFRESH_RATE = 0.0f;
+
+    /**
      * The default display group id, which is the display group id of the primary display assuming
      * there is one.
      * @hide
@@ -909,6 +928,18 @@
     }
 
     /**
+     * Returns the install orientation of the display.
+     * @hide
+     */
+    @Surface.Rotation
+    public int getInstallOrientation() {
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.installOrientation;
+        }
+    }
+
+    /**
      * @deprecated use {@link #getRotation}
      * @return orientation of this display.
      */
@@ -1170,6 +1201,49 @@
     }
 
     /**
+     * Sets the default {@link Display.Mode} to use for the display.  The display mode includes
+     * preference for resolution and refresh rate.
+     * If the mode specified is not supported by the display, then no mode change occurs.
+     *
+     * @param mode The {@link Display.Mode} to set, which can include resolution and/or
+     * refresh-rate. It is created using {@link Display.Mode.Builder}.
+     *`
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
+    public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) {
+        // Create a new object containing default values for the unused fields like mode ID and
+        // alternative refresh rates.
+        Display.Mode preferredMode = new Display.Mode(mode.getPhysicalWidth(),
+                mode.getPhysicalHeight(), mode.getRefreshRate());
+        mGlobal.setUserPreferredDisplayMode(mDisplayId, preferredMode);
+    }
+
+    /**
+     * Removes the display's user preferred display mode.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
+    public void clearUserPreferredDisplayMode() {
+        mGlobal.setUserPreferredDisplayMode(mDisplayId, null);
+    }
+
+    /**
+     * Returns the display's user preferred display mode.
+     *
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public Display.Mode getUserPreferredDisplayMode() {
+        return mGlobal.getUserPreferredDisplayMode(mDisplayId);
+    }
+
+
+    /**
      * Returns whether this display can be used to display wide color gamut content.
      * This does not necessarily mean the device itself can render wide color gamut
      * content. To ensure wide color gamut content can be produced, refer to
@@ -1710,6 +1784,30 @@
     }
 
     /**
+     * Returns true if the specified width is valid.
+     * @hide
+     */
+    public static boolean isWidthValid(int width) {
+        return width > 0;
+    }
+
+    /**
+     * Returns true if the specified height is valid.
+     * @hide
+     */
+    public static boolean isHeightValid(int height) {
+        return height > 0;
+    }
+
+    /**
+     * Returns true if the specified refresh-rate is valid.
+     * @hide
+     */
+    public static boolean isRefreshRateValid(float refreshRate) {
+        return refreshRate > 0.0f;
+    }
+
+    /**
      * A mode supported by a given display.
      *
      * @see Display#getSupportedModes()
@@ -1846,6 +1944,30 @@
         }
 
         /**
+         * Returns {@code true} if this mode matches the given parameters, if those parameters are
+         * valid.<p>
+         * If resolution (width and height) is valid and refresh-rate is not, the method matches
+         * only resolution.
+         * If refresh-rate is valid and resolution (width and height) is not, the method matches
+         * only refresh-rate.</p>
+         *
+         * @hide
+         */
+        public boolean matchesIfValid(int width, int height, float refreshRate) {
+            if (!isWidthValid(width) && !isHeightValid(height)
+                    && !isRefreshRateValid(refreshRate)) {
+                return false;
+            }
+            if (isWidthValid(width) != isHeightValid(height)) {
+                return false;
+            }
+            return (!isWidthValid(width) || mWidth == width)
+                    && (!isHeightValid(height) || mHeight == height)
+                    && (!isRefreshRateValid(refreshRate)
+                    || Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate));
+        }
+
+        /**
          * Returns {@code true} if this mode equals to the other mode in all parameters except
          * the refresh rate.
          *
@@ -1855,6 +1977,24 @@
             return mWidth == other.mWidth && mHeight == other.mHeight;
         }
 
+        /**
+         * Returns {@code true} if refresh-rate is set for a display mode
+         *
+         * @hide
+         */
+        public boolean isRefreshRateSet() {
+            return mRefreshRate != INVALID_DISPLAY_REFRESH_RATE;
+        }
+
+        /**
+         * Returns {@code true} if refresh-rate is set for a display mode
+         *
+         * @hide
+         */
+        public boolean isResolutionSet() {
+            return mWidth != INVALID_DISPLAY_WIDTH && mHeight != INVALID_DISPLAY_HEIGHT;
+        }
+
         @Override
         public boolean equals(@Nullable Object other) {
             if (this == other) {
@@ -1923,6 +2063,80 @@
                 return new Mode[size];
             }
         };
+
+        /**
+         * Builder is used to create {@link Display.Mode} objects
+         *
+         * @hide
+         */
+        @TestApi
+        public static final class Builder {
+            private int mWidth;
+            private int mHeight;
+            private float mRefreshRate;
+
+            public Builder() {
+                mWidth = Display.INVALID_DISPLAY_WIDTH;
+                mHeight = Display.INVALID_DISPLAY_HEIGHT;
+                mRefreshRate = Display.INVALID_DISPLAY_REFRESH_RATE;
+            }
+
+            /**
+             * Sets the resolution (width and height) of a {@link Display.Mode}
+             *
+             * @return Instance of {@link Builder}
+             */
+            @NonNull
+            public Builder setResolution(int width, int height) {
+                if (width > 0 && height > 0) {
+                    mWidth = width;
+                    mHeight = height;
+                }
+                return this;
+            }
+
+            /**
+             * Sets the refresh rate of a {@link Display.Mode}
+             *
+             * @return Instance of {@link Builder}
+             */
+            @NonNull
+            public Builder setRefreshRate(float refreshRate) {
+                if (refreshRate > 0.0f) {
+                    mRefreshRate = refreshRate;
+                }
+                return this;
+            }
+
+            /**
+             * Creates the {@link Display.Mode} object.
+             *
+             * <p>
+             * If resolution needs to be set, but refresh-rate doesn't matter, create a mode with
+             * Builder and call setResolution.
+             * {@code
+             * Display.Mode mode =
+             *      new Display.Mode.Builder()
+             *      .setResolution(width, height)
+             *      .build();
+             * }
+             * </p><p>
+             * If refresh-rate needs to be set, but resolution doesn't matter, create a mode with
+             * Builder and call setRefreshRate.
+             * {@code
+             * Display.Mode mode =
+             *      new Display.Mode.Builder()
+             *      .setRefreshRate(refreshRate)
+             *      .build();
+             * }
+             * </p>
+             */
+            @NonNull
+            public Mode build() {
+                Display.Mode mode = new Mode(mWidth, mHeight, mRefreshRate);
+                return mode;
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index ae323226..9889eaa 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -1249,4 +1249,119 @@
             return String.valueOf(mInner);
         }
     }
+
+    /**
+     * A Builder class to construct a DisplayCutout instance.
+     *
+     * <p>Note that this is only for tests purpose. For production code, developers should always
+     * use a {@link DisplayCutout} obtained from the system.</p>
+     */
+    public static final class Builder {
+        private Insets mSafeInsets = Insets.NONE;
+        private Insets mWaterfallInsets = Insets.NONE;
+        private Path mCutoutPath;
+        private final Rect mBoundingRectLeft = new Rect();
+        private final Rect mBoundingRectTop = new Rect();
+        private final Rect mBoundingRectRight = new Rect();
+        private final Rect mBoundingRectBottom = new Rect();
+
+        /**
+         * Begin building a DisplayCutout.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Construct a new {@link DisplayCutout} with the set parameters.
+         */
+        @NonNull
+        public DisplayCutout build() {
+            final CutoutPathParserInfo info;
+            if (mCutoutPath != null) {
+                // Create a fake CutoutPathParserInfo and set it to sCachedCutoutPathParserInfo so
+                // that when getCutoutPath() is called, it will return the cached Path.
+                info = new CutoutPathParserInfo(0, 0, 0, "test", 0, 1f);
+                synchronized (CACHE_LOCK) {
+                    DisplayCutout.sCachedCutoutPathParserInfo = info;
+                    DisplayCutout.sCachedCutoutPath = mCutoutPath;
+                }
+            } else {
+                info = null;
+            }
+            return new DisplayCutout(mSafeInsets.toRect(), mWaterfallInsets, mBoundingRectLeft,
+                    mBoundingRectTop, mBoundingRectRight, mBoundingRectBottom, info, false);
+        }
+
+        /**
+         * Set the safe insets. If not set, the default value is {@link Insets#NONE}.
+         */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setSafeInsets(@NonNull Insets safeInsets) {
+            mSafeInsets = safeInsets;
+            return this;
+        }
+
+        /**
+         * Set the waterfall insets of the DisplayCutout. If not set, the default value is
+         * {@link Insets#NONE}
+         */
+        @NonNull
+        public Builder setWaterfallInsets(@NonNull Insets waterfallInsets) {
+            mWaterfallInsets = waterfallInsets;
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the left of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectLeft(@NonNull Rect boundingRectLeft) {
+            mBoundingRectLeft.set(boundingRectLeft);
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the top of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectTop(@NonNull Rect boundingRectTop) {
+            mBoundingRectTop.set(boundingRectTop);
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the right of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectRight(@NonNull Rect boundingRectRight) {
+            mBoundingRectRight.set(boundingRectRight);
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the bottom of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectBottom(@NonNull Rect boundingRectBottom) {
+            mBoundingRectBottom.set(boundingRectBottom);
+            return this;
+        }
+
+        /**
+         * Set the cutout {@link Path}.
+         *
+         * Note that not support creating/testing multiple display cutouts with setCutoutPath() in
+         * parallel.
+         */
+        @NonNull
+        public Builder setCutoutPath(@NonNull Path cutoutPath) {
+            mCutoutPath = cutoutPath;
+            return this;
+        }
+    }
 }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5c08632..774bab4 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -138,13 +138,28 @@
     }
 
     static final class VsyncEventData {
-        // The frame timeline vsync id, used to correlate a frame
-        // produced by HWUI with the timeline data stored in Surface Flinger.
-        public final long id;
 
-        // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
-        // allotted for the frame to be completed.
-        public final long frameDeadline;
+        static final FrameTimeline[] INVALID_FRAME_TIMELINES =
+                {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
+
+        public static class FrameTimeline {
+            FrameTimeline(long vsyncId, long expectedPresentTime, long deadline) {
+                this.vsyncId = vsyncId;
+                this.expectedPresentTime = expectedPresentTime;
+                this.deadline = deadline;
+            }
+
+            // The frame timeline vsync id, used to correlate a frame
+            // produced by HWUI with the timeline data stored in Surface Flinger.
+            public final long vsyncId;
+
+            // The frame timestamp for when the frame is expected to be presented.
+            public final long expectedPresentTime;
+
+            // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
+            // allotted for the frame to be completed.
+            public final long deadline;
+        }
 
         /**
          * The current interval between frames in ns. This will be used to align
@@ -153,16 +168,27 @@
          */
         public final long frameInterval;
 
-        VsyncEventData(long id, long frameDeadline, long frameInterval) {
-            this.id = id;
-            this.frameDeadline = frameDeadline;
+        public final FrameTimeline[] frameTimelines;
+
+        public final int preferredFrameTimelineIndex;
+
+        // Called from native code.
+        @SuppressWarnings("unused")
+        VsyncEventData(FrameTimeline[] frameTimelines, int preferredFrameTimelineIndex,
+                long frameInterval) {
+            this.frameTimelines = frameTimelines;
+            this.preferredFrameTimelineIndex = preferredFrameTimelineIndex;
             this.frameInterval = frameInterval;
         }
 
         VsyncEventData() {
-            this.id = FrameInfo.INVALID_VSYNC_ID;
-            this.frameDeadline = Long.MAX_VALUE;
             this.frameInterval = -1;
+            this.frameTimelines = INVALID_FRAME_TIMELINES;
+            this.preferredFrameTimelineIndex = 0;
+        }
+
+        public FrameTimeline preferredFrameTimeline() {
+            return frameTimelines[preferredFrameTimelineIndex];
         }
     }
 
@@ -256,9 +282,8 @@
     // Called from native code.
     @SuppressWarnings("unused")
     private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
-            long frameTimelineVsyncId, long frameDeadline, long frameInterval) {
-        onVsync(timestampNanos, physicalDisplayId, frame,
-                new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval));
+            VsyncEventData vsyncEventData) {
+        onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 678c80a..6917d66 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -318,6 +318,12 @@
     @Nullable
     public RoundedCorners roundedCorners;
 
+    /**
+     * Install orientation of the display relative to its natural orientation.
+     */
+    @Surface.Rotation
+    public int installOrientation;
+
     public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
         @Override
         public DisplayInfo createFromParcel(Parcel source) {
@@ -389,7 +395,8 @@
                 && brightnessMaximum == other.brightnessMaximum
                 && brightnessDefault == other.brightnessDefault
                 && Objects.equals(roundedCorners, other.roundedCorners)
-                && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher;
+                && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher
+                && installOrientation == other.installOrientation;
     }
 
     @Override
@@ -441,6 +448,7 @@
         brightnessDefault = other.brightnessDefault;
         roundedCorners = other.roundedCorners;
         shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher;
+        installOrientation = other.installOrientation;
     }
 
     public void readFromParcel(Parcel source) {
@@ -498,6 +506,7 @@
             userDisabledHdrTypes[i] = source.readInt();
         }
         shouldConstrainMetricsForLauncher = source.readBoolean();
+        installOrientation = source.readInt();
     }
 
     @Override
@@ -553,6 +562,7 @@
             dest.writeInt(userDisabledHdrTypes[i]);
         }
         dest.writeBoolean(shouldConstrainMetricsForLauncher);
+        dest.writeInt(installOrientation);
     }
 
     @Override
@@ -809,6 +819,8 @@
         sb.append(brightnessDefault);
         sb.append(", shouldConstrainMetricsForLauncher ");
         sb.append(shouldConstrainMetricsForLauncher);
+        sb.append(", installOrientation ");
+        sb.append(Surface.rotationToString(installOrientation));
         sb.append("}");
         return sb.toString();
     }
diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java
deleted file mode 100644
index fffb323e..0000000
--- a/core/java/android/view/GestureExclusionTracker.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Used by {@link ViewRootImpl} to track system gesture exclusion rects reported by views.
- */
-class GestureExclusionTracker {
-    private boolean mGestureExclusionViewsChanged = false;
-    private boolean mRootGestureExclusionRectsChanged = false;
-    private List<Rect> mRootGestureExclusionRects = Collections.emptyList();
-    private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>();
-    private List<Rect> mGestureExclusionRects = Collections.emptyList();
-
-    public void updateRectsForView(@NonNull View view) {
-        boolean found = false;
-        final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
-        while (i.hasNext()) {
-            final GestureExclusionViewInfo info = i.next();
-            final View v = info.getView();
-            if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
-                mGestureExclusionViewsChanged = true;
-                i.remove();
-                continue;
-            }
-            if (v == view) {
-                found = true;
-                info.mDirty = true;
-                break;
-            }
-        }
-        if (!found && view.isAttachedToWindow()) {
-            mGestureExclusionViewInfos.add(new GestureExclusionViewInfo(view));
-            mGestureExclusionViewsChanged = true;
-        }
-    }
-
-    @Nullable
-    public List<Rect> computeChangedRects() {
-        boolean changed = mRootGestureExclusionRectsChanged;
-        final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
-        final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects);
-        while (i.hasNext()) {
-            final GestureExclusionViewInfo info = i.next();
-            switch (info.update()) {
-                case GestureExclusionViewInfo.CHANGED:
-                    changed = true;
-                    // Deliberate fall-through
-                case GestureExclusionViewInfo.UNCHANGED:
-                    rects.addAll(info.mExclusionRects);
-                    break;
-                case GestureExclusionViewInfo.GONE:
-                    mGestureExclusionViewsChanged = true;
-                    i.remove();
-                    break;
-            }
-        }
-        if (changed || mGestureExclusionViewsChanged) {
-            mGestureExclusionViewsChanged = false;
-            mRootGestureExclusionRectsChanged = false;
-            if (!mGestureExclusionRects.equals(rects)) {
-                mGestureExclusionRects = rects;
-                return rects;
-            }
-        }
-        return null;
-    }
-
-    public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
-        Preconditions.checkNotNull(rects, "rects must not be null");
-        mRootGestureExclusionRects = rects;
-        mRootGestureExclusionRectsChanged = true;
-    }
-
-    @NonNull
-    public List<Rect> getRootSystemGestureExclusionRects() {
-        return mRootGestureExclusionRects;
-    }
-
-    private static class GestureExclusionViewInfo {
-        public static final int CHANGED = 0;
-        public static final int UNCHANGED = 1;
-        public static final int GONE = 2;
-
-        private final WeakReference<View> mView;
-        boolean mDirty = true;
-        List<Rect> mExclusionRects = Collections.emptyList();
-
-        GestureExclusionViewInfo(View view) {
-            mView = new WeakReference<>(view);
-        }
-
-        public View getView() {
-            return mView.get();
-        }
-
-        public int update() {
-            final View excludedView = getView();
-            if (excludedView == null || !excludedView.isAttachedToWindow()
-                    || !excludedView.isAggregatedVisible()) return GONE;
-            final List<Rect> localRects = excludedView.getSystemGestureExclusionRects();
-            final List<Rect> newRects = new ArrayList<>(localRects.size());
-            for (Rect src : localRects) {
-                Rect mappedRect = new Rect(src);
-                ViewParent p = excludedView.getParent();
-                if (p != null && p.getChildVisibleRect(excludedView, mappedRect, null)) {
-                    newRects.add(mappedRect);
-                }
-            }
-
-            if (mExclusionRects.equals(localRects)) return UNCHANGED;
-            mExclusionRects = newRects;
-            return CHANGED;
-        }
-    }
-}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
new file mode 100644
index 0000000..097d1d0
--- /dev/null
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Initiates handwriting mode once it detects stylus movement in handwritable areas.
+ *
+ * It is designed to be used by  {@link ViewRootImpl}. For every stylus related MotionEvent that is
+ * dispatched to view tree, ViewRootImpl should call {@link #onTouchEvent} method of this class.
+ * And it will automatically request to enter the handwriting mode when the conditions meet.
+ *
+ * Notice that ViewRootImpl should still dispatch MotionEvents to view tree as usual.
+ * And if it successfully enters the handwriting mode, the ongoing MotionEvent stream will be
+ * routed to the input method. Input system will fabricate an ACTION_CANCEL and send to
+ * ViewRootImpl.
+ *
+ * This class does nothing if:
+ * a) MotionEvents are not from stylus.
+ * b) The user taps or long-clicks with a stylus etc.
+ * c) Stylus pointer down position is not within a handwritable area.
+ *
+ * Used by InputMethodManager.
+ * @hide
+ */
+public class HandwritingInitiator {
+    /**
+     * The touchSlop from {@link ViewConfiguration} used to decide whether a pointer is considered
+     * moving or stationary.
+     */
+    private final int mTouchSlop;
+    /**
+     * The timeout used to distinguish tap from handwriting. If the stylus doesn't move before this
+     * timeout, it's not considered as handwriting.
+     */
+    private final long mTapTimeoutInMillis;
+
+    private State mState = new State();
+
+    /**
+     * Helper method to reset the internal state of this class.
+     * Calling this method will also prevent the following MotionEvents
+     * triggers handwriting until the next stylus ACTION_DOWN/ACTION_POINTER_DOWN
+     * arrives.
+     */
+    private void reset() {
+        mState = new State();
+    }
+
+    /** The reference to the View that currently has the input connection. */
+    @Nullable
+    @VisibleForTesting
+    public WeakReference<View> mConnectedView = null;
+
+    /**
+     * When InputConnection restarts for a View, View#onInputConnectionCreatedInternal
+     * might be called before View#onInputConnectionClosedInternal, so we need to count the input
+     * connections and only set mConnectedView to null when mConnectionCount is zero.
+     */
+    private int mConnectionCount = 0;
+    private final InputMethodManager mImm;
+
+    @VisibleForTesting
+    public HandwritingInitiator(ViewConfiguration viewConfiguration,
+            InputMethodManager inputMethodManager) {
+        mTouchSlop = viewConfiguration.getScaledTouchSlop();
+        mTapTimeoutInMillis = ViewConfiguration.getTapTimeout();
+        mImm = inputMethodManager;
+    }
+
+    /**
+     * Notify the HandwritingInitiator that a new MotionEvent has arrived.
+     * This method is non-block, and the event passed to this method should be dispatched to the
+     * View tree as usual. If HandwritingInitiator triggers the handwriting mode, an fabricated
+     * ACTION_CANCEL event will be sent to the ViewRootImpl.
+     * @param motionEvent the stylus MotionEvent.
+     */
+    @VisibleForTesting
+    public void onTouchEvent(MotionEvent motionEvent) {
+        final int maskedAction = motionEvent.getActionMasked();
+        switch (maskedAction) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_POINTER_DOWN:
+                final int actionIndex = motionEvent.getActionIndex();
+                final int toolType = motionEvent.getToolType(actionIndex);
+                // TOOL_TYPE_ERASER is also from stylus. This indicates that the user is holding
+                // the eraser button during handwriting.
+                if (toolType != MotionEvent.TOOL_TYPE_STYLUS
+                        && toolType != MotionEvent.TOOL_TYPE_ERASER) {
+                    // The motion event is not from a stylus event, ignore it.
+                    return;
+                }
+                mState.mStylusPointerId = motionEvent.getPointerId(actionIndex);
+                mState.mStylusDownTimeInMillis = motionEvent.getEventTime();
+                mState.mStylusDownX = motionEvent.getX(actionIndex);
+                mState.mStylusDownY = motionEvent.getY(actionIndex);
+                mState.mShouldInitHandwriting = true;
+                mState.mExceedTouchSlop = false;
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int pointerId = motionEvent.getPointerId(motionEvent.getActionIndex());
+                if (pointerId != mState.mStylusPointerId) {
+                    // ACTION_POINTER_UP is from another stylus pointer, ignore the event.
+                    return;
+                }
+                // Deliberately fall through.
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                // If it's ACTION_CANCEL or ACTION_UP, all the pointers go up. There is no need to
+                // check whether the stylus we are tracking goes up.
+                reset();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                // Either we've already tried to initiate handwriting, or the ongoing MotionEvent
+                // sequence is considered to be tap, long-click or other gestures.
+                if (!mState.mShouldInitHandwriting || mState.mExceedTouchSlop) {
+                    return;
+                }
+
+                final long timeElapsed =
+                        motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
+                if (timeElapsed > mTapTimeoutInMillis) {
+                    reset();
+                    return;
+                }
+
+                final int pointerIndex = motionEvent.findPointerIndex(mState.mStylusPointerId);
+                final float x = motionEvent.getX(pointerIndex);
+                final float y = motionEvent.getY(pointerIndex);
+                if (largerThanTouchSlop(x, y, mState.mStylusDownX, mState.mStylusDownY)) {
+                    mState.mExceedTouchSlop = true;
+                    tryStartHandwriting();
+                }
+        }
+    }
+
+    private View getConnectedView() {
+        if (mConnectedView == null) return null;
+        return mConnectedView.get();
+    }
+
+    /**
+     * Notify HandwritingInitiator that a new InputConnection is created.
+     * The caller of this method should guarantee that each onInputConnectionCreated call
+     * is paired with a onInputConnectionClosed call.
+     * @param view the view that created the current InputConnection.
+     * @see  #onInputConnectionClosed(View)
+     */
+    public void onInputConnectionCreated(@NonNull View view) {
+        final View connectedView = getConnectedView();
+        if (connectedView == view) {
+            ++mConnectionCount;
+        } else {
+            mConnectedView = new WeakReference<>(view);
+            mConnectionCount = 1;
+            tryStartHandwriting();
+        }
+    }
+
+    /**
+     * Notify HandwritingInitiator that the InputConnection has closed for the given view.
+     * The caller of this method should guarantee that each onInputConnectionClosed call
+     * is paired with a onInputConnectionCreated call.
+     * @param view the view that closed the InputConnection.
+     */
+    public void onInputConnectionClosed(@NonNull View view) {
+        final View connectedView = getConnectedView();
+        if (connectedView == view) {
+            --mConnectionCount;
+            if (mConnectionCount == 0) {
+                mConnectedView = null;
+            }
+        } else {
+            // Unexpected branch, set mConnectedView to null to avoid further problem.
+            mConnectedView = null;
+            mConnectionCount = 0;
+        }
+    }
+
+    /**
+     * Try to initiate handwriting. For this method to successfully send startHandwriting signal,
+     * the following 3 conditions should meet:
+     *   a) The stylus movement exceeds the touchSlop.
+     *   b) A View has built InputConnection with IME.
+     *   c) The stylus event lands into the connected View's boundary.
+     * This method will immediately fail without any side effect if condition a or b is not met.
+     * However, if both condition a and b are met but the condition c is not met, it will reset the
+     * internal states. And HandwritingInitiator won't attempt to call startHandwriting until the
+     * next ACTION_DOWN.
+     */
+    private void tryStartHandwriting() {
+        if (!mState.mExceedTouchSlop) {
+            return;
+        }
+        final View connectedView = getConnectedView();
+        if (connectedView == null) {
+            return;
+        }
+        final ViewParent viewParent = connectedView.getParent();
+        // Do a final check before startHandwriting.
+        if (viewParent != null && connectedView.isAttachedToWindow()) {
+            final Rect editorBounds =
+                    new Rect(0, 0, connectedView.getWidth(), connectedView.getHeight());
+            if (viewParent.getChildVisibleRect(connectedView, editorBounds, null)) {
+                final int roundedInitX = Math.round(mState.mStylusDownX);
+                final int roundedInitY = Math.round(mState.mStylusDownY);
+                if (editorBounds.contains(roundedInitX, roundedInitY)) {
+                    startHandwriting(connectedView);
+                }
+            }
+        }
+        reset();
+    }
+
+    /** For test only. */
+    @VisibleForTesting
+    public void startHandwriting(View view) {
+        mImm.startStylusHandwriting(view);
+    }
+
+    private boolean largerThanTouchSlop(float x1, float y1, float x2, float y2) {
+        float dx = x1 - x2;
+        float dy = y1 - y2;
+        return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
+    }
+
+    /** Object that keeps the MotionEvent related states for HandwritingInitiator. */
+    private static class State {
+        /**
+         * Whether it should initiate handwriting mode for the current MotionEvent sequence.
+         * (A series of MotionEvents from ACTION_DOWN to ACTION_UP)
+         *
+         * The purpose of this boolean value is:
+         * a) We should only request to start handwriting mode ONCE for each MotionEvent sequence.
+         * If we've already requested to enter handwriting mode for the ongoing MotionEvent
+         * sequence, this boolean is set to false. And it won't request to start handwriting again.
+         *
+         * b) If the MotionEvent sequence is considered to be tap, long-click or other gestures.
+         * This boolean will be set to false, and it won't request to start handwriting.
+         */
+        private boolean mShouldInitHandwriting = false;
+        /**
+         * Whether the current ongoing stylus MotionEvent sequence already exceeds the touchSlop.
+         * It's used for the case where the stylus exceeds touchSlop before the target View built
+         * InputConnection.
+         */
+        private boolean mExceedTouchSlop = false;
+
+        /** The pointer id of the stylus pointer that is being tracked. */
+        private int mStylusPointerId = -1;
+        /** The time stamp when the stylus pointer goes down. */
+        private long mStylusDownTimeInMillis = -1;
+        /** The initial location where the stylus pointer goes down. */
+        private float mStylusDownX = Float.NaN;
+        private float mStylusDownY = Float.NaN;
+    }
+}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index c5bc99d..45b65e5 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -177,6 +177,10 @@
      * Flag for {@link View#performHapticFeedback(int, int)
      * View.performHapticFeedback(int, int)}: Ignore the global setting
      * for whether to perform haptic feedback, do it always.
+     *
+     * @deprecated Starting from {@link android.os.Build.VERSION_CODES#TIRAMISU} only privileged
+     * apps can ignore user settings for touch feedback.
      */
+    @Deprecated
     public static final int FLAG_IGNORE_GLOBAL_SETTING = 0x0002;
 }
diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl
index f95d6b3..449e9b3 100644
--- a/core/java/android/view/IDisplayWindowListener.aidl
+++ b/core/java/android/view/IDisplayWindowListener.aidl
@@ -16,8 +16,11 @@
 
 package android.view;
 
+import android.graphics.Rect;
 import android.content.res.Configuration;
 
+import java.util.List;
+
 /**
  * Interface to listen for changes to display window-containers.
  *
@@ -56,4 +59,9 @@
      * Called when the previous fixed rotation on a display is finished.
      */
     void onFixedRotationFinished(int displayId);
+
+    /**
+     * Called when the keep clear ares on a display have changed.
+     */
+    void onKeepClearAreasChanged(int displayId, in List<Rect> keepClearAreas);
 }
diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl
new file mode 100644
index 0000000..fc9661a
--- /dev/null
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -0,0 +1,28 @@
+/*
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+import android.content.res.Configuration;
+
+/**
+ * API from content embedder back to embedded content in SurfaceControlViewHost
+ * {@hide}
+ */
+oneway interface ISurfaceControlViewHost {
+    void onConfigurationChanged(in Configuration newConfig);
+    void onDispatchDetachedFromWindow();
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2c766bd..c5ccc18 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -25,7 +25,6 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
-import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -66,6 +65,7 @@
 import android.view.SurfaceControl;
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
+import android.window.IOnFpsCallbackListener;
 
 /**
  * System private interface to the window manager.
@@ -484,16 +484,6 @@
     void getStableInsets(int displayId, out Rect outInsets);
 
     /**
-     * Set the forwarded insets on the display.
-     * <p>
-     * This is only used in case a virtual display is displayed on another display that has insets,
-     * and the bounds of the virtual display is overlapping with the insets from the host display.
-     * In that case, the contents on the virtual display won't be placed over the forwarded insets.
-     * Only the owner of the display is permitted to set the forwarded insets on it.
-     */
-    void setForwardedInsets(int displayId, in Insets insets);
-
-    /**
      * Register shortcut key. Shortcut code is packed as:
      * (MetaState << Integer.SIZE) | KeyCode
      * @hide
@@ -551,6 +541,11 @@
     void stopWindowTrace();
 
     /**
+    * If window tracing is active, saves the window trace to file, otherwise does nothing
+    */
+    void saveWindowTraceToFile();
+
+    /**
      * Returns true if window trace is enabled.
      */
     boolean isWindowTraceEnabled();
@@ -922,4 +917,28 @@
      * reverts to using the default task transition with no spec changes.
      */
     void clearTaskTransitionSpec();
+
+    /**
+     * Registers the frame rate per second count callback for one given task ID.
+     * Each callback can only register for receiving FPS callback for one task id until unregister
+     * is called. If there's no task associated with the given task id,
+     * {@link IllegalArgumentException} will be thrown. If a task id destroyed after a callback is
+     * registered, the registered callback will not be unregistered until
+     * {@link unregisterTaskFpsCallback()} is called
+     * @param taskId task id of the task.
+     * @param listener listener to be registered.
+     *
+     * @hide
+     */
+    void registerTaskFpsCallback(in int taskId, in IOnFpsCallbackListener listener);
+
+    /**
+     * Unregisters the frame rate per second count callback which was registered with
+     * {@link #registerTaskFpsCallback(int,TaskFpsCallback)}.
+     *
+     * @param listener listener to be unregistered.
+     *
+     * @hide
+     */
+    void unregisterTaskFpsCallback(in IOnFpsCallbackListener listener);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 921ce53..6226566 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -37,6 +37,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
 
 import java.util.List;
 
@@ -290,6 +291,11 @@
     oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
 
     /**
+     * Called when the keep-clear areas for this window have changed.
+     */
+    oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> keepClearRects);
+
+    /**
     * Request the server to call setInputWindowInfo on a given Surface, and return
     * an input channel where the client can receive input.
     */
@@ -328,4 +334,12 @@
      */
     oneway void generateDisplayHash(IWindow window, in Rect boundsInWindow,
             in String hashAlgorithm, in RemoteCallback callback);
+
+    /**
+     * Sets the {@link IOnBackInvokedCallback} to be invoked for a window when back is triggered.
+     *
+     * @param window The token for the window to set the callback to.
+     * @param callback The {@link IOnBackInvokedCallback} to set.
+     */
+    oneway void setOnBackInvokedCallback(IWindow window, IOnBackInvokedCallback callback);
 }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 4f1354d..188d745 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -572,6 +572,8 @@
      * @return The identifier object for this device
      * @hide
      */
+    @TestApi
+    @NonNull
     public InputDeviceIdentifier getIdentifier() {
         return mIdentifier;
     }
@@ -735,6 +737,21 @@
     }
 
     /**
+     * Gets the key code produced by the specified location on a US keyboard layout.
+     * Key code as defined in {@link android.view.KeyEvent}.
+     * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
+     * which can alter their key mapping using country specific keyboard layouts.
+     *
+     * @param locationKeyCode The location of a key on a US keyboard layout.
+     * @return The key code produced when pressing the key at the specified location, given the
+     *         active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
+     *         mapping could not be determined, or if an error occurred.
+     */
+    public int getKeyCodeForKeyLocation(int locationKeyCode) {
+        return InputManager.getInstance().getKeyCodeForKeyLocation(mId, locationKeyCode);
+    }
+
+    /**
      * Gets information about the range of values for a particular {@link MotionEvent} axis.
      * If the device supports multiple sources, the same axis may have different meanings
      * for each source.  Returns information about the first axis found for any source.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 8db62f6..0ef5854 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -478,10 +478,15 @@
     public static final int FLAG_IS_GENERATED_GESTURE = 0x8;
 
     /**
-     * This flag associated with {@link #ACTION_POINTER_UP}, this indicates that the pointer
-     * has been canceled. Typically this is used for palm event when the user has accidental
-     * touches.
-     * @hide
+     * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
+     * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
+     * is set, the typical actions that occur in response for a pointer going up (such as click
+     * handlers, end of drawing) should be aborted. This flag is typically set when the user was
+     * accidentally touching the screen, such as by gripping the device, or placing the palm on the
+     * screen.
+     *
+     * @see #ACTION_POINTER_UP
+     * @see #ACTION_CANCEL
      */
     public static final int FLAG_CANCELED = 0x20;
 
@@ -1872,7 +1877,7 @@
             float x, float y, float pressure, float size, int metaState,
             float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
         return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
-                xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
+                xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
                 DEFAULT_DISPLAY);
     }
 
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 2b5e286..43df294 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -29,6 +29,13 @@
 per-file MotionEvent.java = file:/services/core/java/com/android/server/input/OWNERS
 per-file PointerIcon.java = file:/services/core/java/com/android/server/input/OWNERS
 per-file SimulatedDpad.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file BatchedInputEventReceiver.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file DragEvent.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file DragEvent.aidl = file:/services/core/java/com/android/server/input/OWNERS
+per-file GestureDetector.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file ScaleGestureDetector.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file KeyboardShortcut*.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file KeyCharacterMap.java = file:/services/core/java/com/android/server/input/OWNERS
 
 # InputWindowHandle
 per-file InputWindowHandle.java  = file:/services/core/java/com/android/server/input/OWNERS
@@ -73,6 +80,7 @@
 per-file IPinnedStackListener.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file IRecents*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file IRemote*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ISurfaceControlViewHost*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file IWindow*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RemoteAnimation*.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RemoteAnimation*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/OnBackInvokedCallback.java b/core/java/android/view/OnBackInvokedCallback.java
new file mode 100644
index 0000000..b5cd89c
--- /dev/null
+++ b/core/java/android/view/OnBackInvokedCallback.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.app.Activity;
+import android.app.Dialog;
+
+/**
+ * Interface for applications to register back invocation callbacks. This allows the client
+ * to customize various back behaviors by overriding the corresponding callback methods.
+ *
+ * Callback instances can be added to and removed from {@link OnBackInvokedDispatcher}, held
+ * by classes that implement {@link OnBackInvokedDispatcherOwner} (such as {@link Activity},
+ * {@link Dialog} and {@link View}).
+ *
+ * Under the hood callbacks are registered at window level. When back is triggered,
+ * callbacks on the in-focus window are invoked in reverse order in which they are added
+ * within the same priority. Between different pirorities, callbacks with higher priority
+ * are invoked first.
+ *
+ * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int)}
+ * for specifying callback priority.
+ */
+public interface OnBackInvokedCallback {
+   /**
+    * Called when a back gesture has been started, or back button has been pressed down.
+    *
+    * @hide
+    */
+    default void onBackStarted() { };
+
+    /**
+     * Called on back gesture progress.
+     *
+     * @param touchX Absolute X location of the touch point.
+     * @param touchY Absolute Y location of the touch point.
+     * @param progress Value between 0 and 1 on how far along the back gesture is.
+     *
+     * @hide
+     */
+    // TODO(b/210539672): combine back progress params into BackEvent.
+    default void onBackProgressed(int touchX, int touchY, float progress) { };
+
+    /**
+     * Called when a back gesture or back button press has been cancelled.
+     *
+     * @hide
+     */
+    default void onBackCancelled() { };
+
+    /**
+     * Called when a back gesture has been completed and committed, or back button pressed
+     * has been released and committed.
+     */
+    default void onBackInvoked() { };
+}
diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java
new file mode 100644
index 0000000..05c312b
--- /dev/null
+++ b/core/java/android/view/OnBackInvokedDispatcher.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Dispatcher to register {@link OnBackInvokedCallback} instances for handling
+ * back invocations.
+ *
+ * It also provides interfaces to update the attributes of {@link OnBackInvokedCallback}.
+ * Attribute updates are proactively pushed to the window manager if they change the dispatch
+ * target (a.k.a. the callback to be invoked next), or its behavior.
+ */
+public abstract class OnBackInvokedDispatcher {
+    /** @hide */
+    @IntDef({
+            PRIORITY_DEFAULT,
+            PRIORITY_OVERLAY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Priority{}
+
+    /**
+     * Priority level of {@link OnBackInvokedCallback}s for overlays such as menus and
+     * navigation drawers that should receive back dispatch before non-overlays.
+     */
+    public static final int PRIORITY_OVERLAY = 1000000;
+
+    /**
+     * Default priority level of {@link OnBackInvokedCallback}s.
+     */
+    public static final int PRIORITY_DEFAULT = 0;
+
+    /**
+     * Registers a {@link OnBackInvokedCallback}.
+     *
+     * Within the same priority level, callbacks are invoked in the reverse order in which
+     * they are registered. Higher priority callbacks are invoked before lower priority ones.
+     *
+     * @param callback The callback to be registered. If the callback instance has been already
+     *                 registered, the existing instance (no matter its priority) will be
+     *                 unregistered and registered again.
+     * @param priority The priority of the callback.
+     */
+    @SuppressLint("SamShouldBeLast")
+    public abstract void registerOnBackInvokedCallback(
+            @NonNull OnBackInvokedCallback callback, @Priority int priority);
+
+    /**
+     * Unregisters a {@link OnBackInvokedCallback}.
+     *
+     * @param callback The callback to be unregistered. Does nothing if the callback has not been
+     *                 registered.
+     */
+    public abstract void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback);
+}
diff --git a/core/java/android/view/OnBackInvokedDispatcherOwner.java b/core/java/android/view/OnBackInvokedDispatcherOwner.java
new file mode 100644
index 0000000..0e14ed4
--- /dev/null
+++ b/core/java/android/view/OnBackInvokedDispatcherOwner.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+
+/**
+ * A class that provides an {@link OnBackInvokedDispatcher} that allows you to register
+ * an {@link OnBackInvokedCallback} for handling the system back invocation behavior.
+ */
+public interface OnBackInvokedDispatcherOwner {
+    /**
+     * Returns the {@link OnBackInvokedDispatcher} that should dispatch the back invocation
+     * to its registered {@link OnBackInvokedCallback}s.
+     * Returns null when the root view is not attached to a window or a view tree with a decor.
+     */
+    @Nullable
+    OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab33fea..e751720b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -231,6 +231,7 @@
             float shadowRadius);
     private static native void nativeSetGlobalShadowSettings(@Size(4) float[] ambientColor,
             @Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius);
+    private static native boolean nativeGetDisplayDecorationSupport(IBinder displayToken);
 
     private static native void nativeSetFrameRate(long transactionObj, long nativeObject,
             float frameRate, int compatibility, int changeFrameRateStrategy);
@@ -1830,13 +1831,15 @@
         public float density;
         public boolean secure;
         public DeviceProductInfo deviceProductInfo;
+        public @Surface.Rotation int installOrientation;
 
         @Override
         public String toString() {
             return "StaticDisplayInfo{isInternal=" + isInternal
                     + ", density=" + density
                     + ", secure=" + secure
-                    + ", deviceProductInfo=" + deviceProductInfo + "}";
+                    + ", deviceProductInfo=" + deviceProductInfo
+                    + ", installOrientation=" + installOrientation + "}";
         }
 
         @Override
@@ -1847,12 +1850,13 @@
             return isInternal == that.isInternal
                     && density == that.density
                     && secure == that.secure
-                    && Objects.equals(deviceProductInfo, that.deviceProductInfo);
+                    && Objects.equals(deviceProductInfo, that.deviceProductInfo)
+                    && installOrientation == that.installOrientation;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(isInternal, density, secure, deviceProductInfo);
+            return Objects.hash(isInternal, density, secure, deviceProductInfo, installOrientation);
         }
     }
 
@@ -2651,6 +2655,20 @@
     }
 
     /**
+     * Returns whether a display supports DISPLAY_DECORATION.
+     *
+     * @param displayToken
+     *      The token for the display.
+     *
+     * @return Whether the display supports DISPLAY_DECORATION.
+     *
+     * @hide
+     */
+    public static boolean getDisplayDecorationSupport(IBinder displayToken) {
+        return nativeGetDisplayDecorationSupport(displayToken);
+    }
+
+    /**
      * Adds a callback to be informed about SF's jank classification for a specific surface.
      * @hide
      */
diff --git a/core/java/android/view/SurfaceControlFpsListener.java b/core/java/android/view/SurfaceControlFpsListener.java
deleted file mode 100644
index 20a511a..0000000
--- a/core/java/android/view/SurfaceControlFpsListener.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-
-/**
- * Listener for sampling the frames per second for a SurfaceControl and its children.
- * This should only be used by a system component that needs to listen to a SurfaceControl's
- * tree's FPS when it is not actively submitting transactions for that SurfaceControl.
- * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
- *
- * @hide
- */
-public abstract class SurfaceControlFpsListener {
-    private long mNativeListener;
-
-    public SurfaceControlFpsListener() {
-        mNativeListener = nativeCreate(this);
-    }
-
-    protected void destroy() {
-        if (mNativeListener == 0) {
-            return;
-        }
-        unregister();
-        nativeDestroy(mNativeListener);
-        mNativeListener = 0;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            destroy();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Reports the fps from the registered SurfaceControl
-     */
-    public abstract void onFpsReported(float fps);
-
-    /**
-     * Registers the sampling listener for a particular task ID
-     */
-    public void register(int taskId) {
-        if (mNativeListener == 0) {
-            return;
-        }
-
-        nativeRegister(mNativeListener, taskId);
-    }
-
-    /**
-     * Unregisters the sampling listener.
-     */
-    public void unregister() {
-        if (mNativeListener == 0) {
-            return;
-        }
-        nativeUnregister(mNativeListener);
-    }
-
-    /**
-     * Dispatch the collected sample.
-     *
-     * Called from native code on a binder thread.
-     */
-    private static void dispatchOnFpsReported(
-            @NonNull SurfaceControlFpsListener listener, float fps) {
-        listener.onFpsReported(fps);
-    }
-
-    private static native long nativeCreate(SurfaceControlFpsListener thiz);
-    private static native void nativeDestroy(long ptr);
-    private static native void nativeRegister(long ptr, int taskId);
-    private static native void nativeUnregister(long ptr);
-}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index a6c5042d..85a9dbd 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -45,6 +46,35 @@
     private SurfaceControl mSurfaceControl;
     private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
+    private final class ISurfaceControlViewHostImpl extends ISurfaceControlViewHost.Stub {
+        @Override
+        public void onConfigurationChanged(Configuration configuration) {
+            if (mViewRoot == null) {
+                return;
+            }
+            mViewRoot.mHandler.post(() -> {
+                if (mWm != null) {
+                    mWm.setConfiguration(configuration);
+                }
+                if (mViewRoot != null) {
+                    mViewRoot.forceWmRelayout();
+                }
+            });
+        }
+
+        @Override
+        public void onDispatchDetachedFromWindow() {
+            if (mViewRoot == null) {
+                return;
+            }
+            mViewRoot.mHandler.post(() -> {
+                release();
+            });
+        }
+    }
+
+    private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
+
     /**
      * Package encapsulating a Surface hierarchy which contains interactive view
      * elements. It's expected to get this object from
@@ -71,12 +101,14 @@
         private SurfaceControl mSurfaceControl;
         private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
         private final IBinder mInputToken;
+        private final ISurfaceControlViewHost mRemoteInterface;
 
         SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection,
-                       IBinder inputToken) {
+               IBinder inputToken, ISurfaceControlViewHost ri) {
             mSurfaceControl = sc;
             mAccessibilityEmbeddedConnection = connection;
             mInputToken = inputToken;
+            mRemoteInterface = ri;
         }
 
         /**
@@ -97,6 +129,7 @@
             }
             mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection;
             mInputToken = other.mInputToken;
+            mRemoteInterface = other.mRemoteInterface;
         }
 
         private SurfacePackage(Parcel in) {
@@ -105,6 +138,8 @@
             mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
                     in.readStrongBinder());
             mInputToken = in.readStrongBinder();
+            mRemoteInterface = ISurfaceControlViewHost.Stub.asInterface(
+                in.readStrongBinder());
         }
 
         /**
@@ -126,6 +161,13 @@
             return mAccessibilityEmbeddedConnection;
         }
 
+        /**
+         * @hide
+         */
+        public ISurfaceControlViewHost getRemoteInterface() {
+            return mRemoteInterface;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -136,6 +178,7 @@
             mSurfaceControl.writeToParcel(out, flags);
             out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
             out.writeStrongBinder(mInputToken);
+            out.writeStrongBinder(mRemoteInterface.asBinder());
         }
 
         /**
@@ -231,7 +274,7 @@
     public @Nullable SurfacePackage getSurfacePackage() {
         if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
             return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
-                    mViewRoot.getInputToken());
+                mViewRoot.getInputToken(), mRemoteInterface);
         } else {
             return null;
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d0a5835..49ece5f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -972,9 +972,9 @@
             mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
 
             if (mViewVisibility) {
-                mTmpTransaction.show(mSurfaceControl);
+                geometryTransaction.show(mSurfaceControl);
             } else {
-                mTmpTransaction.hide(mSurfaceControl);
+                geometryTransaction.hide(mSurfaceControl);
             }
 
             if (mSurfacePackage != null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 258359e..93fdee0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -834,7 +834,7 @@
  */
 @UiThread
 public class View implements Drawable.Callback, KeyEvent.Callback,
-        AccessibilityEventSource {
+        AccessibilityEventSource, OnBackInvokedDispatcherOwner {
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private static final boolean DBG = false;
 
@@ -4734,15 +4734,18 @@
         WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback;
 
         /**
-         * This lives here since it's only valid for interactive views. This list is null until the
-         * first use.
+         * This lives here since it's only valid for interactive views. This list is null
+         * until its first use.
          */
         private List<Rect> mSystemGestureExclusionRects = null;
+        private List<Rect> mKeepClearRects = null;
+        private boolean mPreferKeepClear = false;
 
         /**
-         * Used to track {@link #mSystemGestureExclusionRects}
+         * Used to track {@link #mSystemGestureExclusionRects} and {@link #mKeepClearRects}
          */
         public RenderNode.PositionUpdateListener mPositionUpdateListener;
+        private Runnable mPositionChangedUpdate;
 
         /**
          * Allows the application to implement custom scroll capture support.
@@ -6028,6 +6031,9 @@
                 case R.styleable.View_clipToOutline:
                     setClipToOutline(a.getBoolean(attr, false));
                     break;
+                case R.styleable.View_preferKeepClear:
+                    setPreferKeepClear(a.getBoolean(attr, false));
+                    break;
             }
         }
 
@@ -8663,15 +8669,34 @@
         if (mAttachInfo == null) {
             return;
         }
-
         RectF position = mAttachInfo.mTmpTransformRect;
-        position.set(0, 0, mRight - mLeft, mBottom - mTop);
-        mapRectFromViewToScreenCoords(position, clipToParent);
+        getBoundsToScreenInternal(position, clipToParent);
         outRect.set(Math.round(position.left), Math.round(position.top),
                 Math.round(position.right), Math.round(position.bottom));
     }
 
     /**
+     * Gets the location of this view in screen coordinates.
+     *
+     * @param outRect The output location
+     * @param clipToParent Whether to clip child bounds to the parent ones.
+     * @hide
+     */
+    public void getBoundsOnScreen(RectF outRect, boolean clipToParent) {
+        if (mAttachInfo == null) {
+            return;
+        }
+        RectF position = mAttachInfo.mTmpTransformRect;
+        getBoundsToScreenInternal(position, clipToParent);
+        outRect.set(position.left, position.top, position.right, position.bottom);
+    }
+
+    private void getBoundsToScreenInternal(RectF position, boolean clipToParent) {
+        position.set(0, 0, mRight - mLeft, mBottom - mTop);
+        mapRectFromViewToScreenCoords(position, clipToParent);
+    }
+
+    /**
      * Map a rectangle from view-relative coordinates to screen-relative coordinates
      *
      * @param rect The rectangle to be mapped
@@ -11646,37 +11671,49 @@
         } else {
             info.mSystemGestureExclusionRects = new ArrayList<>(rects);
         }
-        if (rects.isEmpty()) {
+
+        updatePositionUpdateListener();
+        postUpdate(this::updateSystemGestureExclusionRects);
+    }
+
+    private void updatePositionUpdateListener() {
+        final ListenerInfo info = getListenerInfo();
+        if (getSystemGestureExclusionRects().isEmpty()
+                && collectPreferKeepClearRects().isEmpty()) {
             if (info.mPositionUpdateListener != null) {
                 mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
+                info.mPositionChangedUpdate = null;
             }
         } else {
             if (info.mPositionUpdateListener == null) {
+                info.mPositionChangedUpdate = () -> {
+                    updateSystemGestureExclusionRects();
+                    updateKeepClearRects();
+                };
                 info.mPositionUpdateListener = new RenderNode.PositionUpdateListener() {
                     @Override
                     public void positionChanged(long n, int l, int t, int r, int b) {
-                        postUpdateSystemGestureExclusionRects();
+                        postUpdate(info.mPositionChangedUpdate);
                     }
 
                     @Override
                     public void positionLost(long frameNumber) {
-                        postUpdateSystemGestureExclusionRects();
+                        postUpdate(info.mPositionChangedUpdate);
                     }
                 };
                 mRenderNode.addPositionUpdateListener(info.mPositionUpdateListener);
             }
         }
-        postUpdateSystemGestureExclusionRects();
     }
 
     /**
      * WARNING: this can be called by a hwui worker thread, not just the UI thread!
      */
-    void postUpdateSystemGestureExclusionRects() {
+    private void postUpdate(Runnable r) {
         // Potentially racey from a background thread. It's ok if it's not perfect.
         final Handler h = getHandler();
         if (h != null) {
-            h.postAtFrontOfQueue(this::updateSystemGestureExclusionRects);
+            h.postAtFrontOfQueue(r);
         }
     }
 
@@ -11708,6 +11745,106 @@
     }
 
     /**
+     * Set a preference to keep the bounds of this view clear from floating windows above this
+     * view's window. This informs the system that the view is considered a vital area for the
+     * user and that ideally it should not be covered. Setting this is only appropriate for UI
+     * where the user would likely take action to uncover it.
+     * <p>
+     * The system will try to respect this, but when not possible will ignore it.
+     * <p>
+     * @see #setPreferKeepClearRects
+     * @see #isPreferKeepClear
+     * @attr ref android.R.styleable#View_preferKeepClear
+     */
+    public final void setPreferKeepClear(boolean preferKeepClear) {
+        getListenerInfo().mPreferKeepClear = preferKeepClear;
+        updatePositionUpdateListener();
+        postUpdate(this::updateKeepClearRects);
+    }
+
+    /**
+     * Retrieve the preference for this view to be kept clear. This is set either by
+     * {@link #setPreferKeepClear} or via the attribute android.R.styleable#View_preferKeepClear.
+     * <p>
+     * If this is {@code true}, the system will ignore the Rects set by
+     * {@link #setPreferKeepClearRects} and try to keep the whole view clear.
+     * <p>
+     * @see #setPreferKeepClear
+     * @attr ref android.R.styleable#View_preferKeepClear
+     */
+    public final boolean isPreferKeepClear() {
+        return mListenerInfo != null && mListenerInfo.mPreferKeepClear;
+    }
+
+    /**
+     * Set a preference to keep the provided rects clear from floating windows above this
+     * view's window. This informs the system that these rects are considered vital areas for the
+     * user and that ideally they should not be covered. Setting this is only appropriate for UI
+     * where the user would likely take action to uncover it.
+     * <p>
+     * If the whole view is preferred to be clear ({@link #isPreferKeepClear}), the rects set here
+     * will be ignored.
+     * <p>
+     * The system will try to respect this preference, but when not possible will ignore it.
+     * <p>
+     * @see #setPreferKeepClear
+     * @see #getPreferKeepClearRects
+     */
+    public final void setPreferKeepClearRects(@NonNull List<Rect> rects) {
+        final ListenerInfo info = getListenerInfo();
+        if (info.mKeepClearRects != null) {
+            info.mKeepClearRects.clear();
+            info.mKeepClearRects.addAll(rects);
+        } else {
+            info.mKeepClearRects = new ArrayList<>(rects);
+        }
+        updatePositionUpdateListener();
+        postUpdate(this::updateKeepClearRects);
+    }
+
+    /**
+     * @return the list of rects, set by {@link #setPreferKeepClearRects}.
+     *
+     * @see #setPreferKeepClearRects
+     */
+    @NonNull
+    public final List<Rect> getPreferKeepClearRects() {
+        final ListenerInfo info = mListenerInfo;
+        if (info != null && info.mKeepClearRects != null) {
+            return new ArrayList(info.mKeepClearRects);
+        }
+
+        return Collections.emptyList();
+    }
+
+    void updateKeepClearRects() {
+        final AttachInfo ai = mAttachInfo;
+        if (ai != null) {
+            ai.mViewRootImpl.updateKeepClearRectsForView(this);
+        }
+    }
+
+    /**
+     * Retrieve the list of areas within this view's post-layout coordinate space which the
+     * system will try to not cover with other floating elements, like the pip window.
+     */
+    @NonNull
+    List<Rect> collectPreferKeepClearRects() {
+        final ListenerInfo info = mListenerInfo;
+        if (info != null) {
+            final List<Rect> list = new ArrayList();
+            if (info.mPreferKeepClear) {
+                list.add(new Rect(0, 0, getWidth(), getHeight()));
+            } else if (info.mKeepClearRects != null) {
+                list.addAll(info.mKeepClearRects);
+            }
+            return list;
+        }
+
+        return Collections.emptyList();
+    }
+
+    /**
      * Compute the view's coordinate within the surface.
      *
      * <p>Computes the coordinates of this view in its surface. The argument
@@ -14248,34 +14385,34 @@
                 hideTooltip();
                 return true;
             }
-        }
-
-        if (action == R.id.accessibilityActionDragDrop) {
-            if (!canAcceptAccessibilityDrop()) {
-                return false;
-            }
-            try {
-                if (mAttachInfo != null && mAttachInfo.mSession != null) {
-                    final int[] location = new int[2];
-                    getLocationInWindow(location);
-                    final int centerX = location[0] + getWidth() / 2;
-                    final int centerY = location[1] + getHeight() / 2;
-                    return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow,
-                            centerX, centerY);
+            case R.id.accessibilityActionDragDrop: {
+                if (!canAcceptAccessibilityDrop()) {
+                    return false;
                 }
-            } catch (RemoteException e) {
-                Log.e(VIEW_LOG_TAG, "Unable to drop for accessibility", e);
-            }
-            return false;
-        } else if (action == R.id.accessibilityActionDragCancel) {
-            if (!startedSystemDragForAccessibility()) {
+                try {
+                    if (mAttachInfo != null && mAttachInfo.mSession != null) {
+                        final int[] location = new int[2];
+                        getLocationInWindow(location);
+                        final int centerX = location[0] + getWidth() / 2;
+                        final int centerY = location[1] + getHeight() / 2;
+                        return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow,
+                                centerX, centerY);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(VIEW_LOG_TAG, "Unable to drop for accessibility", e);
+                }
                 return false;
             }
-            if (mAttachInfo != null && mAttachInfo.mDragToken != null) {
-                cancelDragAndDrop();
-                return true;
+            case R.id.accessibilityActionDragCancel: {
+                if (!startedSystemDragForAccessibility()) {
+                    return false;
+                }
+                if (mAttachInfo != null && mAttachInfo.mDragToken != null) {
+                    cancelDragAndDrop();
+                    return true;
+                }
+                return false;
             }
-            return false;
         }
         return false;
     }
@@ -15012,8 +15149,10 @@
     /**
      * @return true if this view and all ancestors are visible as of the last
      * {@link #onVisibilityAggregated(boolean)} call.
+     *
+     * @hide
      */
-    boolean isAggregatedVisible() {
+    public boolean isAggregatedVisible() {
         return (mPrivateFlags3 & PFLAG3_AGGREGATED_VISIBLE) != 0;
     }
 
@@ -15099,7 +15238,11 @@
             notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
 
             if (!getSystemGestureExclusionRects().isEmpty()) {
-                postUpdateSystemGestureExclusionRects();
+                postUpdate(this::updateSystemGestureExclusionRects);
+            }
+
+            if (!collectPreferKeepClearRects().isEmpty()) {
+                postUpdate(this::updateKeepClearRects);
             }
         }
     }
@@ -31255,4 +31398,23 @@
         }
         return null;
     }
+
+    /**
+     * Returns the {@link OnBackInvokedDispatcher} instance of the window this view is attached to.
+     *
+     * @return The {@link OnBackInvokedDispatcher} or {@code null} if the view is neither attached
+     *         to a window or a view tree with a decor.
+     */
+    @Nullable
+    public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+        ViewParent parent = getParent();
+        if (parent instanceof View) {
+            return ((View) parent).getOnBackInvokedDispatcher();
+        } else if (parent instanceof ViewRootImpl) {
+            // Get the fallback dispatcher on {@link ViewRootImpl} if the view tree doesn't have
+            // a {@link com.android.internal.policy.DecorView}.
+            return ((ViewRootImpl) parent).getOnBackInvokedDispatcher();
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 07d5fc5..25e0eca 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1896,7 +1896,7 @@
 
         private Canvas mCanvas;
         private Bitmap mBitmap;
-        private boolean mEnabledHwBitmapsInSwMode;
+        private boolean mEnabledHwFeaturesInSwMode;
 
         @Override
         public Canvas getCanvas(View view, int width, int height) {
@@ -1913,7 +1913,7 @@
             if (mCanvas == null) {
                 mCanvas = new Canvas();
             }
-            mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
+            mEnabledHwFeaturesInSwMode = mCanvas.isHwFeaturesInSwModeEnabled();
             mCanvas.setBitmap(mBitmap);
             return mCanvas;
         }
@@ -1921,7 +1921,7 @@
         @Override
         public Bitmap createBitmap() {
             mCanvas.setBitmap(null);
-            mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
+            mCanvas.setHwFeaturesInSwModeEnabled(mEnabledHwFeaturesInSwMode);
             return mBitmap;
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f86abec..97b5a31 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -192,6 +192,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
 import android.window.ClientWindowFrames;
+import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -309,6 +310,12 @@
     private @SurfaceControl.BufferTransform
             int mPreviousTransformHint = SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
     /**
+     * The fallback {@link OnBackInvokedDispatcher} when the window doesn't have a decor view.
+     */
+    private WindowOnBackInvokedDispatcher mFallbackOnBackInvokedDispatcher =
+            new WindowOnBackInvokedDispatcher();
+
+    /**
      * Callback for notifying about global configuration changes.
      */
     public interface ConfigChangedCallback {
@@ -391,6 +398,8 @@
     final DisplayManager mDisplayManager;
     final String mBasePackageName;
 
+    private @Surface.Rotation int mDisplayInstallOrientation;
+
     final int[] mTmpLocation = new int[2];
 
     final TypedValue mTmpValue = new TypedValue();
@@ -742,7 +751,10 @@
         return mImeFocusController;
     }
 
-    private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
+    private final ViewRootRectTracker mGestureExclusionTracker =
+            new ViewRootRectTracker(v -> v.getSystemGestureExclusionRects());
+    private final ViewRootRectTracker mKeepClearRectsTracker =
+            new ViewRootRectTracker(v -> v.collectPreferKeepClearRects());
 
     private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
@@ -752,6 +764,17 @@
         int localChanges;
     }
 
+    private final HandwritingInitiator mHandwritingInitiator;
+
+    /**
+     * Used by InputMethodManager.
+     * @hide
+     */
+    @NonNull
+    public HandwritingInitiator getHandwritingInitiator() {
+        return mHandwritingInitiator;
+    }
+
     /**
      * This is only used on the RenderThread when handling a blast sync. Specifically, it's only
      * used when calling {@link BLASTBufferQueue#setSyncTransaction(Transaction)} and then merged
@@ -826,6 +849,8 @@
                 ? Choreographer.getSfInstance() : Choreographer.getInstance();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
+        mHandwritingInitiator = new HandwritingInitiator(mViewConfiguration,
+                mContext.getSystemService(InputMethodManager.class));
 
         String processorOverrideName = context.getResources().getString(
                                     R.string.config_inputEventCompatProcessorOverrideClassName);
@@ -858,6 +883,7 @@
         mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
 
         mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
+        mFallbackOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -1004,6 +1030,7 @@
                 mView = view;
 
                 mAttachInfo.mDisplayState = mDisplay.getState();
+                mDisplayInstallOrientation = mDisplay.getInstallOrientation();
                 mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
                 mFallbackEventHandler.setView(view);
                 mWindowAttributes.copyFrom(attrs);
@@ -1107,6 +1134,9 @@
                     if (pendingInsetsController != null) {
                         pendingInsetsController.replayAndAttach(mInsetsController);
                     }
+                    ((RootViewSurfaceTaker) mView)
+                            .provideWindowOnBackInvokedDispatcher()
+                            .attachToWindow(mWindowSession, mWindow);
                 }
 
                 try {
@@ -4743,7 +4773,7 @@
      * the root's view hierarchy.
      */
     public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
-        mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects);
+        mGestureExclusionTracker.setRootRects(rects);
         mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED);
     }
 
@@ -4753,7 +4783,26 @@
      */
     @NonNull
     public List<Rect> getRootSystemGestureExclusionRects() {
-        return mGestureExclusionTracker.getRootSystemGestureExclusionRects();
+        return mGestureExclusionTracker.getRootRects();
+    }
+
+    /**
+     * Called from View when the position listener is triggered
+     */
+    void updateKeepClearRectsForView(View view) {
+        mKeepClearRectsTracker.updateRectsForView(view);
+        mHandler.sendEmptyMessage(MSG_KEEP_CLEAR_RECTS_CHANGED);
+    }
+
+    void keepClearRectsChanged() {
+        final List<Rect> rectsForWindowManager = mKeepClearRectsTracker.computeChangedRects();
+        if (rectsForWindowManager != null && mView != null) {
+            try {
+                mWindowSession.reportKeepClearAreasChanged(mWindow, rectsForWindowManager);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
@@ -5249,6 +5298,7 @@
     private static final int MSG_HIDE_INSETS = 35;
     private static final int MSG_REQUEST_SCROLL_CAPTURE = 36;
     private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 37;
+    private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 38;
 
 
     final class ViewRootHandler extends Handler {
@@ -5317,6 +5367,8 @@
                     return "MSG_HIDE_INSETS";
                 case MSG_WINDOW_TOUCH_MODE_CHANGED:
                     return "MSG_WINDOW_TOUCH_MODE_CHANGED";
+                case MSG_KEEP_CLEAR_RECTS_CHANGED:
+                    return "MSG_KEEP_CLEAR_RECTS_CHANGED";
             }
             return super.getMessageName(message);
         }
@@ -5540,7 +5592,10 @@
                 } break;
                 case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
                     systemGestureExclusionChanged();
-                } break;
+                }   break;
+                case MSG_KEEP_CLEAR_RECTS_CHANGED: {
+                    keepClearRectsChanged();
+                }   break;
                 case MSG_REQUEST_SCROLL_CAPTURE:
                     handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
                     break;
@@ -6418,6 +6473,7 @@
 
         private int processPointerEvent(QueuedInputEvent q) {
             final MotionEvent event = (MotionEvent)q.mEvent;
+            mHandwritingInitiator.onTouchEvent(event);
 
             mAttachInfo.mUnbufferedDispatchRequested = false;
             mAttachInfo.mHandlingPointerEvent = true;
@@ -7884,6 +7940,10 @@
                 mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                 mTempControls, mSurfaceSize);
 
+        final int transformHint = SurfaceControl.rotationToBufferTransform(
+                (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+        mSurfaceControl.setTransformHint(transformHint);
+
         if (mAttachInfo.mContentCaptureManager != null) {
             MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
                     .getMainContentCaptureSession();
@@ -7902,7 +7962,6 @@
                 mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
                 mAttachInfo.mThreadedRenderer.setBlastBufferQueue(mBlastBufferQueue);
             }
-            int transformHint = mSurfaceControl.getTransformHint();
             if (mPreviousTransformHint != transformHint) {
                 mPreviousTransformHint = transformHint;
                 dispatchTransformHintChanged(transformHint);
@@ -10620,9 +10679,26 @@
        }
        mBLASTDrawConsumer = consume;
        return true;
-   }
+    }
 
     boolean wasRelayoutRequested() {
         return mRelayoutRequested;
     }
+
+    void forceWmRelayout() {
+       mForceNextWindowRelayout = true;
+       scheduleTraversals();
+    }
+
+    /**
+     * Returns the {@link OnBackInvokedDispatcher} on the decor view if one exists, or the
+     * fallback {@link OnBackInvokedDispatcher} instance.
+     */
+    @Nullable
+    public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+        if (mView instanceof RootViewSurfaceTaker) {
+            return ((RootViewSurfaceTaker) mView).provideWindowOnBackInvokedDispatcher();
+        }
+        return mFallbackOnBackInvokedDispatcher;
+    }
 }
diff --git a/core/java/android/view/ViewRootRectTracker.java b/core/java/android/view/ViewRootRectTracker.java
new file mode 100644
index 0000000..fd9cc19
--- /dev/null
+++ b/core/java/android/view/ViewRootRectTracker.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Abstract class to track a collection of rects reported by the views under the same
+ * {@link ViewRootImpl}.
+ */
+class ViewRootRectTracker {
+    private final Function<View, List<Rect>> mRectCollector;
+    private boolean mViewsChanged = false;
+    private boolean mRootRectsChanged = false;
+    private List<Rect> mRootRects = Collections.emptyList();
+    private List<ViewInfo> mViewInfos = new ArrayList<>();
+    private List<Rect> mRects = Collections.emptyList();
+
+    /**
+     * @param rectCollector given a view returns a list of the rects of interest for this
+     *                      ViewRootRectTracker
+     */
+    ViewRootRectTracker(Function<View, List<Rect>> rectCollector) {
+        mRectCollector = rectCollector;
+    }
+
+    public void updateRectsForView(@NonNull View view) {
+        boolean found = false;
+        final Iterator<ViewInfo> i = mViewInfos.iterator();
+        while (i.hasNext()) {
+            final ViewInfo info = i.next();
+            final View v = info.getView();
+            if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
+                mViewsChanged = true;
+                i.remove();
+                continue;
+            }
+            if (v == view) {
+                found = true;
+                info.mDirty = true;
+                break;
+            }
+        }
+        if (!found && view.isAttachedToWindow()) {
+            mViewInfos.add(new ViewInfo(view));
+            mViewsChanged = true;
+        }
+    }
+
+    /**
+     * @return all visible rects from all views in the global (root) coordinate system
+     */
+    @Nullable
+    public List<Rect> computeChangedRects() {
+        boolean changed = mRootRectsChanged;
+        final Iterator<ViewInfo> i = mViewInfos.iterator();
+        final List<Rect> rects = new ArrayList<>(mRootRects);
+        while (i.hasNext()) {
+            final ViewInfo info = i.next();
+            switch (info.update()) {
+                case ViewInfo.CHANGED:
+                    changed = true;
+                    // Deliberate fall-through
+                case ViewInfo.UNCHANGED:
+                    rects.addAll(info.mRects);
+                    break;
+                case ViewInfo.GONE:
+                    mViewsChanged = true;
+                    i.remove();
+                    break;
+            }
+        }
+        if (changed || mViewsChanged) {
+            mViewsChanged = false;
+            mRootRectsChanged = false;
+            if (!mRects.equals(rects)) {
+                mRects = rects;
+                return rects;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets rects defined in the global (root) coordinate system, i.e. not for a specific view.
+     */
+    public void setRootRects(@NonNull List<Rect> rects) {
+        Preconditions.checkNotNull(rects, "rects must not be null");
+        mRootRects = rects;
+        mRootRectsChanged = true;
+    }
+
+    @NonNull
+    public List<Rect> getRootRects() {
+        return mRootRects;
+    }
+
+    @NonNull
+    private List<Rect> getTrackedRectsForView(@NonNull View v) {
+        final List<Rect> rects = mRectCollector.apply(v);
+        return rects == null ? Collections.emptyList() : rects;
+    }
+
+    private class ViewInfo {
+        public static final int CHANGED = 0;
+        public static final int UNCHANGED = 1;
+        public static final int GONE = 2;
+
+        private final WeakReference<View> mView;
+        boolean mDirty = true;
+        List<Rect> mRects = Collections.emptyList();
+
+        ViewInfo(View view) {
+            mView = new WeakReference<>(view);
+        }
+
+        public View getView() {
+            return mView.get();
+        }
+
+        public int update() {
+            final View view = getView();
+            if (view == null || !view.isAttachedToWindow()
+                    || !view.isAggregatedVisible()) return GONE;
+            final List<Rect> localRects = getTrackedRectsForView(view);
+            final List<Rect> newRects = new ArrayList<>(localRects.size());
+            for (Rect src : localRects) {
+                Rect mappedRect = new Rect(src);
+                ViewParent p = view.getParent();
+                if (p != null && p.getChildVisibleRect(view, mappedRect, null)) {
+                    newRects.add(mappedRect);
+                }
+            }
+
+            if (mRects.equals(localRects)) return UNCHANGED;
+            mRects = newRects;
+            return CHANGED;
+        }
+    }
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cd9f3eb6..ca7f900 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -118,6 +118,7 @@
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.window.TaskFpsCallback;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -3581,7 +3582,8 @@
 
         /**
          * If specified, the insets provided by this window will be our window frame minus the
-         * insets specified by providedInternalInsets.
+         * insets specified by providedInternalInsets. This should not be used together with
+         * {@link WindowState#mGivenContentInsets}. If both of them are set, both will be applied.
          *
          * @hide
          */
@@ -4857,4 +4859,31 @@
     default boolean isTaskSnapshotSupported() {
         return false;
     }
+
+    /**
+     * Registers the frame rate per second count callback for one given task ID.
+     * Each callback can only register for receiving FPS callback for one task id until unregister
+     * is called. If there's no task associated with the given task id,
+     * {@link IllegalArgumentException} will be thrown. If a task id destroyed after a callback is
+     * registered, the registered callback will not be unregistered until
+     * {@link #unregisterTaskFpsCallback(TaskFpsCallback))} is called
+     * @param taskId task id of the task.
+     * @param callback callback to be registered.
+     *
+     * @hide
+     */
+    @SystemApi
+    default void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
+            @NonNull TaskFpsCallback callback) {}
+
+    /**
+     * Unregisters the frame rate per second count callback which was registered with
+     * {@link #registerTaskFpsCallback(int,TaskFpsCallback)}.
+     *
+     * @param callback callback to be unregistered.
+     *
+     * @hide
+     */
+    @SystemApi
+    default void unregisterTaskFpsCallback(@NonNull TaskFpsCallback callback) {}
 }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index dd804168..c16703ef 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -24,6 +24,7 @@
 import static android.window.WindowProviderService.isWindowProviderService;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
@@ -37,6 +38,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.window.TaskFpsCallback;
 import android.window.WindowContext;
 import android.window.WindowProvider;
 
@@ -419,4 +421,22 @@
         }
         return false;
     }
+
+    @Override
+    public void registerTaskFpsCallback(@IntRange(from = 0) int taskId, TaskFpsCallback callback) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().registerTaskFpsCallback(
+                    taskId, callback.getListener());
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void unregisterTaskFpsCallback(TaskFpsCallback callback) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().unregisterTaskFpsCallback(
+                    callback.getListener());
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d699194d..998498b 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
 
 import java.util.HashMap;
 import java.util.Objects;
@@ -87,7 +88,7 @@
         mHostInputToken = hostInputToken;
     }
 
-    protected void setConfiguration(Configuration configuration) {
+    public void setConfiguration(Configuration configuration) {
         mConfiguration.setTo(configuration);
     }
 
@@ -330,6 +331,7 @@
     public void setInsets(android.view.IWindow window, int touchableInsets,
             android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
             android.graphics.Region touchableRegion) {
+        setTouchRegion(window.asBinder(), touchableRegion);
     }
 
     @Override
@@ -458,6 +460,11 @@
     }
 
     @Override
+    public void reportKeepClearAreasChanged(android.view.IWindow window,
+            java.util.List<android.graphics.Rect> exclusionRects) {
+    }
+
+    @Override
     public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
             IBinder hostInputToken, int flags, int privateFlags, int type,
             InputChannel outInputChannel) {
@@ -495,6 +502,10 @@
     }
 
     @Override
+    public void setOnBackInvokedCallback(IWindow iWindow,
+            IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException { }
+
+    @Override
     public boolean dropForAccessibility(IWindow window, int x, int y) {
         return false;
     }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 7a33507..e54ed18 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1766,6 +1766,74 @@
         }
     }
 
+    /**
+     * Sets the system audio caption enabled state.
+     *
+     * @param isEnabled The system audio captioning enabled state.
+     * @param userId The user Id.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
+    public void setSystemAudioCaptioningRequested(boolean isEnabled, int userId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.setSystemAudioCaptioningRequested(isEnabled, userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the system audio caption UI enabled state.
+     *
+     * @param userId The user Id.
+     * @return the system audio caption UI enabled state.
+     * @hide
+     */
+    public boolean isSystemAudioCaptioningUiRequested(int userId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
+        }
+        try {
+            return service.isSystemAudioCaptioningUiRequested(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the system audio caption UI enabled state.
+     *
+     * @param isEnabled The system audio captioning UI enabled state.
+     * @param userId The user Id.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
+    public void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.setSystemAudioCaptioningUiRequested(isEnabled, userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
     private IAccessibilityManager getServiceLocked() {
         if (mService == null) {
             tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index db7c663..0a33d6c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4321,15 +4321,13 @@
                 return "ACTION_PRESS_AND_HOLD";
             case R.id.accessibilityActionImeEnter:
                 return "ACTION_IME_ENTER";
+            case R.id.accessibilityActionDragStart:
+                return "ACTION_DRAG";
+            case R.id.accessibilityActionDragCancel:
+                return "ACTION_CANCEL_DRAG";
+            case R.id.accessibilityActionDragDrop:
+                return "ACTION_DROP";
             default:
-                // TODO(197520937): Use finalized constants in switch
-                if (action == R.id.accessibilityActionDragStart) {
-                    return "ACTION_DRAG";
-                } else if (action == R.id.accessibilityActionDragCancel) {
-                    return "ACTION_CANCEL_DRAG";
-                } else if (action == R.id.accessibilityActionDragDrop) {
-                    return "ACTION_DROP";
-                }
                 return "ACTION_UNKNOWN";
         }
     }
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 36e779a..540f5dc 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -84,6 +84,12 @@
      */
     public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
 
+    /**
+     * Window type: A system window used to show the UI for the interaction with
+     * window-based magnification, which includes the magnified content and the option menu.
+     */
+    public static final int TYPE_MAGNIFICATION_OVERLAY = 6;
+
     /* Special values for window IDs */
     /** @hide */
     public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE;
@@ -801,6 +807,9 @@
             case TYPE_SPLIT_SCREEN_DIVIDER: {
                 return "TYPE_SPLIT_SCREEN_DIVIDER";
             }
+            case TYPE_MAGNIFICATION_OVERLAY: {
+                return "TYPE_MAGNIFICATION_OVERLAY";
+            }
             default:
                 return "<UNKNOWN:" + type + ">";
         }
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 3d68692..4f9781b 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -16,12 +16,16 @@
 
 package android.view.accessibility;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Color;
 import android.graphics.Typeface;
@@ -30,6 +34,8 @@
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 import java.util.Locale;
 
@@ -41,6 +47,7 @@
 public class CaptioningManager {
     /** Default captioning enabled value. */
     private static final int DEFAULT_ENABLED = 0;
+    private static final boolean SYSTEM_AUDIO_CAPTIONING_DEFAULT_ENABLED = false;
 
     /** Default style preset as an index into {@link CaptionStyle#PRESETS}. */
     private static final int DEFAULT_PRESET = 0;
@@ -51,6 +58,9 @@
     private final ArrayList<CaptioningChangeListener> mListeners = new ArrayList<>();
     private final ContentResolver mContentResolver;
     private final ContentObserver mContentObserver;
+    private final Resources mResources;
+    private final Context mContext;
+    private final AccessibilityManager mAccessibilityManager;
 
     /**
      * Creates a new captioning manager for the specified context.
@@ -58,10 +68,13 @@
      * @hide
      */
     public CaptioningManager(Context context) {
+        mContext = context;
         mContentResolver = context.getContentResolver();
+        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
 
         final Handler handler = new Handler(context.getMainLooper());
         mContentObserver = new MyContentObserver(handler);
+        mResources = context.getResources();
     }
 
     /**
@@ -137,6 +150,60 @@
     }
 
     /**
+     * @return the system audio caption enabled state.
+     */
+    public final boolean isSystemAudioCaptioningRequested() {
+        return Secure.getIntForUser(mContentResolver, Secure.ODI_CAPTIONS_ENABLED,
+                SYSTEM_AUDIO_CAPTIONING_DEFAULT_ENABLED ? 1 : 0, mContext.getUserId()) == 1;
+    }
+
+    /**
+     * Sets the system audio caption enabled state.
+     *
+     * @param isEnabled The system audio captioning enabled state.
+     *
+     * @throws SecurityException if the caller does not have permission
+     * {@link Manifest.permission#SET_SYSTEM_AUDIO_CAPTION}
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
+    public final void setSystemAudioCaptioningRequested(boolean isEnabled) {
+        if (mAccessibilityManager != null) {
+            mAccessibilityManager.setSystemAudioCaptioningRequested(isEnabled,
+                    mContext.getUserId());
+        }
+    }
+
+    /**
+     * @return the system audio caption UI enabled state.
+     */
+    public final boolean isSystemAudioCaptioningUiRequested() {
+        return mAccessibilityManager != null
+                && mAccessibilityManager.isSystemAudioCaptioningUiRequested(mContext.getUserId());
+    }
+
+    /**
+     * Sets the system audio caption UI enabled state.
+     *
+     * @param isEnabled The system audio captioning UI enabled state.
+     *
+     * @throws SecurityException if the caller does not have permission
+     * {@link Manifest.permission#SET_SYSTEM_AUDIO_CAPTION}
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
+    public final void setSystemAudioCaptioningUiRequested(boolean isEnabled) {
+        if (mAccessibilityManager != null) {
+            mAccessibilityManager.setSystemAudioCaptioningUiRequested(isEnabled,
+                    mContext.getUserId());
+        }
+    }
+
+    /**
      * Adds a listener for changes in the user's preferred captioning enabled
      * state and visual properties.
      *
@@ -155,6 +222,8 @@
                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE);
                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_LOCALE);
                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_PRESET);
+                registerObserver(Secure.ODI_CAPTIONS_ENABLED);
+                registerObserver(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED);
             }
 
             mListeners.add(listener);
@@ -181,6 +250,19 @@
         }
     }
 
+    /**
+     * Returns true if system wide call captioning is enabled for this device.
+     */
+    public boolean isCallCaptioningEnabled() {
+        try {
+            return mResources.getBoolean(
+                R.bool.config_systemCaptionsServiceCallsEnabled);
+        } catch (Resources.NotFoundException e) {
+            // The resource may not be defined, return false in that case
+            return false;
+        }
+    }
+
     private void notifyEnabledChanged() {
         final boolean enabled = isEnabled();
         synchronized (mListeners) {
@@ -217,6 +299,24 @@
         }
     }
 
+    private void notifySystemAudioCaptionChanged() {
+        final boolean enabled = isSystemAudioCaptioningRequested();
+        synchronized (mListeners) {
+            for (CaptioningChangeListener listener : mListeners) {
+                listener.onSystemAudioCaptioningChanged(enabled);
+            }
+        }
+    }
+
+    private void notifySystemAudioCaptionUiChanged() {
+        final boolean enabled = isSystemAudioCaptioningUiRequested();
+        synchronized (mListeners) {
+            for (CaptioningChangeListener listener : mListeners) {
+                listener.onSystemAudioCaptioningUiChanged(enabled);
+            }
+        }
+    }
+
     private class MyContentObserver extends ContentObserver {
         private final Handler mHandler;
 
@@ -236,6 +336,10 @@
                 notifyLocaleChanged();
             } else if (Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE.equals(name)) {
                 notifyFontScaleChanged();
+            } else if (Secure.ODI_CAPTIONS_ENABLED.equals(name)) {
+                notifySystemAudioCaptionChanged();
+            } else if (Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED.equals(name)) {
+                notifySystemAudioCaptionUiChanged();
             } else {
                 // We only need a single callback when multiple style properties
                 // change in rapid succession.
@@ -553,5 +657,51 @@
          * @see CaptioningManager#getFontScale()
          */
         public void onFontScaleChanged(float fontScale) {}
+
+
+        /**
+         * Called when the system audio caption enabled state changes.
+         *
+         * @param enabled the system audio caption enabled state
+         */
+        public void onSystemAudioCaptioningChanged(boolean enabled) {}
+
+        /**
+         * Called when the system audio caption UI enabled state changes.
+         *
+         * @param enabled the system audio caption UI enabled state
+         */
+        public void onSystemAudioCaptioningUiChanged(boolean enabled) {}
+    }
+
+    /**
+     * Interface for accessing the system audio captioning related secure setting keys.
+     *
+     * @hide
+     */
+    public interface SystemAudioCaptioningAccessing {
+        /**
+         * Sets the system audio caption enabled state.
+         *
+         * @param isEnabled The system audio captioning enabled state.
+         * @param userId The user Id.
+         */
+        void setSystemAudioCaptioningRequested(boolean isEnabled, int userId);
+
+        /**
+         * Gets the system audio caption UI enabled state.
+         *
+         * @param userId The user Id.
+         * @return the system audio caption UI enabled state.
+         */
+        boolean isSystemAudioCaptioningUiRequested(int userId);
+
+        /**
+         * Sets the system audio caption UI enabled state.
+         *
+         * @param isEnabled The system audio captioning UI enabled state.
+         * @param userId The user Id.
+         */
+        void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId);
     }
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 4e8d2da..645ddf5 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -100,4 +100,14 @@
     int getFocusColor();
 
     boolean isAudioDescriptionByDefaultEnabled();
+
+    // Requires Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
+    // System process only
+    void setSystemAudioCaptioningRequested(boolean isEnabled, int userId);
+
+    boolean isSystemAudioCaptioningUiRequested(int userId);
+
+    // Requires Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
+    // System process only
+    void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId);
 }
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
index 1cb6825..722546e 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -67,4 +67,13 @@
      */
     void onAccessibilityActionPerformed(int displayId);
 
+    /**
+     * Called when the user is performing dragging gesture. It is started after the offset
+     * between the down location and the move event location exceed
+     * {@link ViewConfiguration#getScaledTouchSlop()}.
+     *
+     * @param displayId The logical display id.
+     */
+    void onDrag(int displayId);
+
 }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index b1d618e..ab749ee 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -209,6 +209,13 @@
     private boolean mShowWallpaper;
     private boolean mHasRoundedCorners;
 
+    /**
+     * Whether to show a background behind the windows during the animation.
+     * @see #getShowBackground()
+     * @see #setShowBackground(boolean)
+     */
+    private boolean mShowBackground;
+
     private boolean mMore = true;
     private boolean mOneMoreTime = true;
 
@@ -266,6 +273,8 @@
                 a.getBoolean(com.android.internal.R.styleable.Animation_showWallpaper, false));
         setHasRoundedCorners(
                 a.getBoolean(com.android.internal.R.styleable.Animation_hasRoundedCorners, false));
+        setShowBackground(
+                a.getBoolean(com.android.internal.R.styleable.Animation_showBackground, false));
 
         final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
 
@@ -698,6 +707,24 @@
     }
 
     /**
+     * If showBackground is {@code true} and this animation is applied on a window, then the windows
+     * in the animation will animate with the background associated with this window behind them.
+     *
+     * The background comes from the {@link android.R.styleable#Theme_colorBackground} that is
+     * applied to this window through its theme.
+     *
+     * If multiple animating windows have showBackground set to {@code true} during an animation,
+     * the top most window with showBackground set to {@code true} and a valid background color
+     * takes precedence.
+     *
+     * @param showBackground Whether to show a background behind the windows during the animation.
+     * @attr ref android.R.styleable#Animation_showBackground
+     */
+    public void setShowBackground(boolean showBackground) {
+        mShowBackground = showBackground;
+    }
+
+    /**
      * Gets the acceleration curve type for this animation.
      *
      * @return the {@link Interpolator} associated to this animation
@@ -838,6 +865,24 @@
     }
 
     /**
+     * If showBackground is {@code true} and this animation is applied on a window, then the windows
+     * in the animation will animate with the background associated with this window behind them.
+     *
+     * The background comes from the {@link android.R.styleable#Theme_colorBackground} that is
+     * applied to this window through its theme.
+     *
+     * If multiple animating windows have showBackground set to {@code true} during an animation,
+     * the top most window with showBackground set to {@code true} and a valid background color
+     * takes precedence.
+     *
+     * @return if the background of this window should be shown behind the animating windows.
+     * @attr ref android.R.styleable#Animation_showBackground
+     */
+    public boolean getShowBackground() {
+        return mShowBackground;
+    }
+
+    /**
      * <p>Indicates whether or not this animation will affect the transformation
      * matrix. For instance, a fade animation will not affect the matrix whereas
      * a scale animation will.</p>
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1122056..bb13c1e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -472,6 +472,24 @@
     public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
             "augmented_service_request_timeout";
 
+    /**
+     * Sets allowed list for the autofill compatibility mode.
+     *
+     * The list of packages is {@code ":"} colon delimited, and each entry has the name of the
+     * package and an optional list of url bar resource ids (the list is delimited by
+     * brackets&mdash{@code [} and {@code ]}&mdash and is also comma delimited).
+     *
+     * <p>For example, a list with 3 packages {@code p1}, {@code p2}, and {@code p3}, where
+     * package {@code p1} have one id ({@code url_bar}, {@code p2} has none, and {@code p3 }
+     * have 2 ids {@code url_foo} and {@code url_bas}) would be
+     * {@code p1[url_bar]:p2:p3[url_foo,url_bas]}
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
+            "compat_mode_allowed_packages";
+
     /** @hide */
     public static final int RESULT_OK = 0;
     /** @hide */
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index 437e54f..8600f55 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -105,6 +105,13 @@
     private final SparseRectFArray mCharacterBoundsArray;
 
     /**
+     * Container of rectangular position of Editor in the local coordinates that will be transformed
+     * with the transformation matrix when rendered on the screen.
+     * @see {@link EditorBoundsInfo}.
+     */
+    private final EditorBoundsInfo mEditorBoundsInfo;
+
+    /**
      * Transformation matrix that is applied to any positional information of this class to
      * transform local coordinates into screen coordinates.
      */
@@ -141,6 +148,7 @@
         mInsertionMarkerBaseline = source.readFloat();
         mInsertionMarkerBottom = source.readFloat();
         mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader(), android.view.inputmethod.SparseRectFArray.class);
+        mEditorBoundsInfo = source.readTypedObject(EditorBoundsInfo.CREATOR);
         mMatrixValues = source.createFloatArray();
     }
 
@@ -163,6 +171,7 @@
         dest.writeFloat(mInsertionMarkerBaseline);
         dest.writeFloat(mInsertionMarkerBottom);
         dest.writeParcelable(mCharacterBoundsArray, flags);
+        dest.writeTypedObject(mEditorBoundsInfo, flags);
         dest.writeFloatArray(mMatrixValues);
     }
 
@@ -216,6 +225,10 @@
             return false;
         }
 
+        if (!Objects.equals(mEditorBoundsInfo, that.mEditorBoundsInfo)) {
+            return false;
+        }
+
         // Following fields are (partially) covered by hashCode().
 
         if (mComposingTextStart != that.mComposingTextStart
@@ -248,6 +261,7 @@
                 + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
                 + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
                 + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
+                + " mEditorBoundsInfo=" + mEditorBoundsInfo
                 + " mMatrix=" + Arrays.toString(mMatrixValues)
                 + "}";
     }
@@ -266,6 +280,7 @@
         private float mInsertionMarkerBottom = Float.NaN;
         private int mInsertionMarkerFlags = 0;
         private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
+        private EditorBoundsInfo mEditorBoundsInfo = null;
         private float[] mMatrixValues = null;
         private boolean mMatrixInitialized = false;
 
@@ -356,6 +371,17 @@
         }
 
         /**
+         * Sets the current editor related bounds.
+         *
+         * @param bounds {@link EditorBoundsInfo} in local coordinates.
+         */
+        @NonNull
+        public Builder setEditorBoundsInfo(@Nullable EditorBoundsInfo bounds) {
+            mEditorBoundsInfo = bounds;
+            return this;
+        }
+
+        /**
          * Sets the matrix that transforms local coordinates into screen coordinates.
          * @param matrix transformation matrix from local coordinates into screen coordinates. null
          * is interpreted as an identity matrix.
@@ -410,6 +436,7 @@
             if (mCharacterBoundsArrayBuilder != null) {
                 mCharacterBoundsArrayBuilder.reset();
             }
+            mEditorBoundsInfo = null;
         }
     }
 
@@ -425,6 +452,7 @@
         mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
         mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null
                 ? builder.mCharacterBoundsArrayBuilder.build() : null;
+        mEditorBoundsInfo = builder.mEditorBoundsInfo;
         mMatrixValues = new float[9];
         if (builder.mMatrixInitialized) {
             System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
@@ -547,6 +575,14 @@
     }
 
     /**
+     * Returns {@link EditorBoundsInfo} editor related bounds.
+     */
+    @Nullable
+    public EditorBoundsInfo getEditorBoundsInfo() {
+        return mEditorBoundsInfo;
+    }
+
+    /**
      * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
      * matrix that is to be applied other positional data in this class.
      * @return a new instance (copy) of the transformation matrix.
diff --git a/core/java/android/view/inputmethod/EditorBoundsInfo.java b/core/java/android/view/inputmethod/EditorBoundsInfo.java
new file mode 100644
index 0000000..081dc81
--- /dev/null
+++ b/core/java/android/view/inputmethod/EditorBoundsInfo.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Container of rectangular position related info for the Editor.
+ */
+public final class EditorBoundsInfo implements Parcelable {
+
+    /**
+     * Container of rectangular position of Editor in the current window.
+     */
+    private final RectF mEditorBounds;
+
+    /**
+     * Container of rectangular position of Editor with additional padding around for initiating
+     * Stylus Handwriting in the current window.
+     */
+    private final RectF mHandwritingBounds;
+
+    private final int mHashCode;
+
+    private EditorBoundsInfo(@NonNull Parcel source) {
+        mHashCode = source.readInt();
+        mEditorBounds = source.readTypedObject(RectF.CREATOR);
+        mHandwritingBounds = source.readTypedObject(RectF.CREATOR);
+    }
+
+    /**
+     * Returns the bounds of the Editor in local coordinates.
+     *
+     * Screen coordinates can be obtained by transforming with the
+     * {@link CursorAnchorInfo#getMatrix} of the containing {@code CursorAnchorInfo}.
+     */
+    @Nullable
+    public RectF getEditorBounds() {
+        return mEditorBounds;
+    }
+
+    /**
+     * Returns the bounds of the area that should be considered for initiating stylus handwriting
+     * in local coordinates.
+     *
+     * Screen coordinates can be obtained by transforming with the
+     * {@link CursorAnchorInfo#getMatrix} of the containing {@code CursorAnchorInfo}.
+     */
+    @Nullable
+    public RectF getHandwritingBounds() {
+        return mHandwritingBounds;
+    }
+
+    @Override
+    public int hashCode() {
+        return mHashCode;
+    }
+
+    @Override
+    public String toString() {
+        return "EditorBoundsInfo{mEditorBounds=" + mEditorBounds
+                + " mHandwritingBounds=" + mHandwritingBounds
+                + "}";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        EditorBoundsInfo bounds;
+        if (obj instanceof  EditorBoundsInfo) {
+            bounds = (EditorBoundsInfo) obj;
+        } else {
+            return false;
+        }
+        return Objects.equals(bounds.mEditorBounds, mEditorBounds)
+                && Objects.equals(bounds.mHandwritingBounds, mHandwritingBounds);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mHashCode);
+        dest.writeTypedObject(mEditorBounds, flags);
+        dest.writeTypedObject(mHandwritingBounds, flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<EditorBoundsInfo> CREATOR
+            = new Parcelable.Creator<EditorBoundsInfo>() {
+        @Override
+        public EditorBoundsInfo createFromParcel(@NonNull Parcel source) {
+            return new EditorBoundsInfo(source);
+        }
+
+        @Override
+        public EditorBoundsInfo[] newArray(int size) {
+            return new EditorBoundsInfo[size];
+        }
+    };
+
+    /**
+     * Builder for {@link CursorAnchorInfo}.
+     */
+    public static final class Builder {
+        private RectF mEditorBounds = null;
+        private RectF mHandwritingBounds = null;
+
+        /**
+         * Sets the bounding box of the current editor.
+         *
+         * @param bounds {@link RectF} in local coordinates.
+         */
+        @NonNull
+        public EditorBoundsInfo.Builder setEditorBounds(@Nullable RectF bounds) {
+            mEditorBounds = bounds;
+            return this;
+        }
+
+        /**
+         * Sets the current editor's bounds with padding for handwriting.
+         *
+         * @param bounds {@link RectF} in local coordinates.
+         */
+        @NonNull
+        public EditorBoundsInfo.Builder setHandwritingBounds(@Nullable RectF bounds) {
+            mHandwritingBounds = bounds;
+            return this;
+        }
+
+        /**
+         * Returns {@link EditorBoundsInfo} using parameters in this
+         *   {@link EditorBoundsInfo.Builder}.
+         */
+        @NonNull
+        public EditorBoundsInfo build() {
+            return new EditorBoundsInfo(this);
+        }
+    }
+
+    private EditorBoundsInfo(final EditorBoundsInfo.Builder builder) {
+        mEditorBounds = builder.mEditorBounds;
+        mHandwritingBounds = builder.mHandwritingBounds;
+
+        int hash = Objects.hashCode(mEditorBounds);
+        hash *= 31;
+        hash += Objects.hashCode(mHandwritingBounds);
+        mHashCode = hash;
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 5b2068f..fda72d5 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -26,12 +26,16 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.Log;
+import android.view.InputChannel;
+import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.InlineSuggestionsRequestInfo;
 
+import java.util.List;
+
 /**
  * The InputMethod interface represents an input method which can generate key
  * events and text, such as digital, email addresses, CJK characters, other
@@ -100,11 +104,13 @@
      *                             operations that are allowed only to the
      *                             current IME.
      * @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
+     * @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME.
      * @hide
      */
     @MainThread
     default void initializeInternal(IBinder token,
-            IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+            IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
+            boolean stylusHwSupported) {
         attachToken(token);
     }
 
@@ -384,4 +390,23 @@
      */
     public void setCurrentHideInputToken(IBinder hideInputToken);
 
+    /**
+     * Checks if IME is ready to start stylus handwriting session.
+     * If yes, {@link #startStylusHandwriting(InputChannel, List)} is called.
+     * @param requestId
+     * @hide
+     */
+    default void canStartStylusHandwriting(int requestId) {
+        // intentionally empty
+    }
+
+    /**
+     * Start stylus handwriting session.
+     * @hide
+     */
+    default void startStylusHandwriting(
+            @NonNull InputChannel channel, @Nullable List<MotionEvent> events) {
+        // intentionally empty
+    }
+
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0bb3cc2..6a22023 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1761,6 +1761,50 @@
     }
 
     /**
+     * Start stylus handwriting session.
+     *
+     * If supported by the current input method, a stylus handwriting session is started on the
+     * given View, capturing all stylus input and converting it to InputConnection commands.
+     *
+     * If handwriting mode is started successfully by the IME, any currently dispatched stylus
+     * pointers will be {@code android.view.MotionEvent#FLAG_CANCELED} cancelled.
+     *
+     * If Stylus handwriting mode is not supported or cannot be fulfilled for any reason by IME,
+     * request will be ignored and Stylus touch will continue as normal touch input.
+     *
+     * @param view the View for which stylus handwriting is requested. It and
+     * {@link View#hasWindowFocus its window} must be {@link View#hasFocus focused}.
+     */
+    public void startStylusHandwriting(@NonNull View view) {
+        // Re-dispatch if there is a context mismatch.
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.startStylusHandwriting(view);
+        }
+
+        checkFocus();
+        synchronized (mH) {
+            if (view == null || !hasServedByInputMethodLocked(view)) {
+                Log.w(TAG,
+                        "Ignoring startStylusHandwriting() as view=" + view + " is not served.");
+                return;
+            }
+            if (view.getViewRootImpl() != mCurRootView) {
+                Log.w(TAG, "Ignoring startStylusHandwriting: View's window does not have focus.");
+                return;
+            }
+
+            try {
+                mService.startStylusHandwriting(mClient);
+                // TODO(b/210039666): do we need any extra work for supporting non-native
+                //   UI toolkits?
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * This method toggles the input method window display.
      * If the input window is already displayed, it gets hidden.
      * If not the input window will be displayed.
@@ -2093,6 +2137,10 @@
                         + ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
             }
             view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+            final ViewRootImpl viewRoot = view.getViewRootImpl();
+            if (viewRoot != null) {
+                viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
+            }
         }
 
         return true;
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
index 60688ea..ba45b85 100644
--- a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.RemoteException;
+import android.provider.DeviceConfig;
 
 import java.util.Objects;
 
@@ -40,6 +41,11 @@
      */
     public static final String LOG_TAG = "SelectionToolbar";
 
+    /**
+     * Whether system selection toolbar is enabled.
+     */
+    private static final String REMOTE_SELECTION_TOOLBAR_ENABLED =
+            "remote_selection_toolbar_enabled";
 
     @NonNull
     private final Context mContext;
@@ -86,4 +92,21 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private boolean isRemoteSelectionToolbarEnabled() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SELECTION_TOOLBAR,
+                REMOTE_SELECTION_TOOLBAR_ENABLED, false);
+    }
+
+    /**
+     * Returns {@code true} if remote render selection toolbar enabled, otherwise
+     * returns {@code false}.
+     */
+    public static boolean isRemoteSelectionToolbarEnabled(Context context) {
+        SelectionToolbarManager manager = context.getSystemService(SelectionToolbarManager.class);
+        if (manager != null) {
+            return manager.isRemoteSelectionToolbarEnabled();
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 414a7f1..1d8e9a3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2656,6 +2656,27 @@
         }
     }
 
+    /**
+     * Returns whether the selected child view (from the adapter's getView) is enabled.
+     *
+     * @return true if enabled
+     */
+    public boolean isSelectedChildViewEnabled() {
+        return mIsChildViewEnabled;
+    }
+
+    /**
+     * Set whether the selected child view (from the adapter's getView) is enabled.
+     *
+     * When refreshDrawableState is called, AbsListView will control the "enabled" state
+     * of the selector based on this.
+     *
+     * @param selectedChildViewEnabled true if enabled
+     */
+    public void setSelectedChildViewEnabled(boolean selectedChildViewEnabled) {
+        mIsChildViewEnabled = selectedChildViewEnabled;
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         int saveCount = 0;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 60ce651..dd70d69 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
 
 import android.R;
 import android.animation.ValueAnimator;
@@ -84,6 +85,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.ActionMode;
@@ -112,9 +114,11 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorBoundsInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
@@ -448,11 +452,14 @@
     private int mLineChangeSlopMax;
     private int mLineChangeSlopMin;
 
+    private final AccessibilitySmartActions mA11ySmartActions;
+
     Editor(TextView textView) {
         mTextView = textView;
         // Synchronize the filter list, which places the undo input filter at the end.
         mTextView.setFilters(mTextView.getFilters());
         mProcessTextIntentActionsHandler = new ProcessTextIntentActionsHandler(this);
+        mA11ySmartActions = new AccessibilitySmartActions(mTextView);
         mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_enableHapticTextHandle);
 
@@ -1073,7 +1080,7 @@
                 com.android.internal.R.dimen.textview_error_popup_default_width);
         final StaticLayout l = StaticLayout.Builder.obtain(text, 0, text.length(), tv.getPaint(),
                 defaultWidthInPixels)
-                .setUseLineSpacingFromFallbacks(tv.mUseFallbackLineSpacing)
+                .setUseLineSpacingFromFallbacks(tv.isFallbackLineSpacingForStaticLayout())
                 .build();
 
         float max = 0;
@@ -4380,6 +4387,7 @@
             item.setShowAsAction(showAsAction);
             mAssistClickHandlers.put(item,
                     TextClassification.createIntentOnClickListener(action.getActionIntent()));
+            mA11ySmartActions.addAction(action);
             return item;
         }
 
@@ -4393,6 +4401,7 @@
                 }
                 i++;
             }
+            mA11ySmartActions.reset();
         }
 
         private boolean hasLegacyAssistItem(TextClassification classification) {
@@ -4570,6 +4579,12 @@
             mTextView.getLocationOnScreen(mTmpIntOffset);
             mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
             builder.setMatrix(mViewToScreenMatrix);
+            final RectF bounds = new RectF();
+            mTextView.getBoundsOnScreen(bounds, false /* clipToParent */);
+            EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
+            //TODO(b/210039666): add Handwriting bounds once they're available.
+            builder.setEditorBoundsInfo(
+                    boundsBuilder.setEditorBounds(bounds).build());
 
             final float viewportToContentHorizontalOffset =
                     mTextView.viewportToContentHorizontalOffset();
@@ -7649,7 +7664,7 @@
         private final PackageManager mPackageManager;
         private final String mPackageName;
         private final SparseArray<Intent> mAccessibilityIntents = new SparseArray<>();
-        private final SparseArray<AccessibilityNodeInfo.AccessibilityAction> mAccessibilityActions =
+        private final SparseArray<AccessibilityAction> mAccessibilityActions =
                 new SparseArray<>();
         private final List<ResolveInfo> mSupportedActivities = new ArrayList<>();
 
@@ -7699,8 +7714,7 @@
                 int actionId = TextView.ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID + i++;
                 mAccessibilityActions.put(
                         actionId,
-                        new AccessibilityNodeInfo.AccessibilityAction(
-                                actionId, getLabel(resolveInfo)));
+                        new AccessibilityAction(actionId, getLabel(resolveInfo)));
                 mAccessibilityIntents.put(
                         actionId, createProcessTextIntentForResolveInfo(resolveInfo));
             }
@@ -7779,6 +7793,65 @@
         }
     }
 
+    /**
+     * Accessibility helper for "smart" (i.e. textAssist) actions.
+     * Helps ensure that "smart" actions are shown in the accessibility menu.
+     * NOTE that these actions are only available when an action mode is live.
+     *
+     * @hide
+     */
+    private static final class AccessibilitySmartActions {
+
+        private final TextView mTextView;
+        private final SparseArray<Pair<AccessibilityAction, RemoteAction>> mActions =
+                new SparseArray<>();
+
+        private AccessibilitySmartActions(TextView textView) {
+            mTextView = Objects.requireNonNull(textView);
+        }
+
+        private void addAction(RemoteAction action) {
+            final int actionId = ACCESSIBILITY_ACTION_SMART_START_ID + mActions.size();
+            mActions.put(actionId,
+                    new Pair(new AccessibilityAction(actionId, action.getTitle()), action));
+        }
+
+        private void reset() {
+            mActions.clear();
+        }
+
+        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
+            for (int i = 0; i < mActions.size(); i++) {
+                nodeInfo.addAction(mActions.valueAt(i).first);
+            }
+        }
+
+        boolean performAccessibilityAction(int actionId) {
+            final Pair<AccessibilityAction, RemoteAction> pair = mActions.get(actionId);
+            if (pair != null) {
+                TextClassification.createIntentOnClickListener(pair.second.getActionIntent())
+                        .onClick(mTextView);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Initializes the nodeInfo with smart actions.
+     */
+    void onInitializeSmartActionsAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
+        mA11ySmartActions.onInitializeAccessibilityNodeInfo(nodeInfo);
+    }
+
+    /**
+     * Handles the accessibility action if it is an active smart action.
+     * Return false if this method does not hanle the action.
+     */
+    boolean performSmartActionsAccessibilityAction(int actionId) {
+        return mA11ySmartActions.performAccessibilityAction(actionId);
+    }
+
     static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
         if (msgFormat == null) {
             Log.d(TAG, location);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 6d58ee2..b21d08c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6628,6 +6628,7 @@
                 opts = ActivityOptions.makeBasic();
                 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             }
+            opts.setLaunchDisplayId(view.getDisplay().getDisplayId());
             return Pair.create(intent, opts);
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0143401..41c5401 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -48,6 +48,9 @@
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.app.assist.AssistStructure;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -76,6 +79,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
+import android.graphics.text.LineBreakConfig;
 import android.icu.text.DecimalFormatSymbols;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -345,6 +349,8 @@
  * @attr ref android.R.styleable#TextView_fontVariationSettings
  * @attr ref android.R.styleable#TextView_breakStrategy
  * @attr ref android.R.styleable#TextView_hyphenationFrequency
+ * @attr ref android.R.styleable#TextView_lineBreakStyle
+ * @attr ref android.R.styleable#TextView_lineBreakWordStyle
  * @attr ref android.R.styleable#TextView_autoSizeTextType
  * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
  * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
@@ -437,6 +443,9 @@
     // Accessibility action start id for "process text" actions.
     static final int ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID = 0x10000100;
 
+    /** Accessibility action start id for "smart" actions. @hide */
+    static final int ACCESSIBILITY_ACTION_SMART_START_ID = 0x10001000;
+
     /**
      * @hide
      */
@@ -453,6 +462,29 @@
 
     private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
 
+    // The default value of the line break style.
+    private static final int DEFAULT_LINE_BREAK_STYLE = LineBreakConfig.LINE_BREAK_STYLE_NONE;
+
+    // The default value of the line break word style.
+    private static final int DEFAULT_LINE_BREAK_WORD_STYLE =
+            LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+
+    /**
+     * This change ID enables the fallback text line spacing (line height) for BoringLayout.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long BORINGLAYOUT_FALLBACK_LINESPACING = 210923482L; // buganizer id
+
+    /**
+     * This change ID enables the fallback text line spacing (line height) for StaticLayout.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.P)
+    public static final long STATICLAYOUT_FALLBACK_LINESPACING = 37756858; // buganizer id
+
     // System wide time for last cut, copy or text changed action.
     static long sLastCutCopyOrTextChangedTime;
 
@@ -756,6 +788,7 @@
     private Layout mLayout;
     private boolean mLocalesChanged = false;
     private int mTextSizeUnit = -1;
+    private LineBreakConfig mLineBreakConfig = new LineBreakConfig();
 
     // This is used to reflect the current user preference for changing font weight and making text
     // more bold.
@@ -766,8 +799,13 @@
     private boolean mListenerChanged = false;
     // True if internationalized input should be used for numbers and date and time.
     private final boolean mUseInternationalizedInput;
-    // True if fallback fonts that end up getting used should be allowed to affect line spacing.
-    /* package */ boolean mUseFallbackLineSpacing;
+
+    // Fallback fonts that end up getting used should be allowed to affect line spacing.
+    private static final int FALLBACK_LINE_SPACING_NONE = 0;
+    private static final int FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY = 1;
+    private static final int FALLBACK_LINE_SPACING_ALL = 2;
+
+    private int mUseFallbackLineSpacing;
     // True if the view text can be padded for compat reasons, when the view is translated.
     private final boolean mUseTextPaddingForUiTranslation;
 
@@ -1418,6 +1456,16 @@
                     mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE);
                     break;
 
+                case com.android.internal.R.styleable.TextView_lineBreakStyle:
+                    mLineBreakConfig.setLineBreakStyle(
+                            a.getInt(attr, LineBreakConfig.LINE_BREAK_STYLE_NONE));
+                    break;
+
+                case com.android.internal.R.styleable.TextView_lineBreakWordStyle:
+                    mLineBreakConfig.setLineBreakWordStyle(
+                            a.getInt(attr, LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE));
+                    break;
+
                 case com.android.internal.R.styleable.TextView_autoSizeTextType:
                     mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE);
                     break;
@@ -1479,7 +1527,13 @@
 
         final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
         mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
-        mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P;
+        if (CompatChanges.isChangeEnabled(BORINGLAYOUT_FALLBACK_LINESPACING)) {
+            mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_ALL;
+        } else if (CompatChanges.isChangeEnabled(STATICLAYOUT_FALLBACK_LINESPACING)) {
+            mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
+        } else {
+            mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_NONE;
+        }
         // TODO(b/179693024): Use a ChangeId instead.
         mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R;
 
@@ -3944,6 +3998,10 @@
         float mLetterSpacing = 0;
         String mFontFeatureSettings = null;
         String mFontVariationSettings = null;
+        boolean mHasLineBreakStyle = false;
+        boolean mHasLineBreakWordStyle = false;
+        int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE;
+        int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE;
 
         @Override
         public String toString() {
@@ -3974,6 +4032,10 @@
                     + "    mLetterSpacing:" + mLetterSpacing + "\n"
                     + "    mFontFeatureSettings:" + mFontFeatureSettings + "\n"
                     + "    mFontVariationSettings:" + mFontVariationSettings + "\n"
+                    + "    mHasLineBreakStyle:" + mHasLineBreakStyle + "\n"
+                    + "    mHasLineBreakWordStyle:" + mHasLineBreakWordStyle + "\n"
+                    + "    mLineBreakStyle:" + mLineBreakStyle + "\n"
+                    + "    mLineBreakWordStyle:" + mLineBreakWordStyle + "\n"
                     + "}";
         }
     }
@@ -4021,6 +4083,10 @@
                 com.android.internal.R.styleable.TextAppearance_fontFeatureSettings);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontVariationSettings,
                 com.android.internal.R.styleable.TextAppearance_fontVariationSettings);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_lineBreakStyle,
+                com.android.internal.R.styleable.TextAppearance_lineBreakStyle);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_lineBreakWordStyle,
+                com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle);
     }
 
     /**
@@ -4136,6 +4202,16 @@
                 case com.android.internal.R.styleable.TextAppearance_fontVariationSettings:
                     attributes.mFontVariationSettings = appearance.getString(attr);
                     break;
+                case com.android.internal.R.styleable.TextAppearance_lineBreakStyle:
+                    attributes.mHasLineBreakStyle = true;
+                    attributes.mLineBreakStyle =
+                            appearance.getInt(attr, attributes.mLineBreakStyle);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle:
+                    attributes.mHasLineBreakWordStyle = true;
+                    attributes.mLineBreakWordStyle =
+                            appearance.getInt(attr, attributes.mLineBreakWordStyle);
+                    break;
                 default:
             }
         }
@@ -4201,9 +4277,46 @@
         if (attributes.mFontVariationSettings != null) {
             setFontVariationSettings(attributes.mFontVariationSettings);
         }
+
+        if (attributes.mHasLineBreakStyle || attributes.mHasLineBreakWordStyle) {
+            updateLineBreakConfigFromTextAppearance(attributes.mHasLineBreakStyle,
+                    attributes.mHasLineBreakWordStyle, attributes.mLineBreakStyle,
+                    attributes.mLineBreakWordStyle);
+        }
     }
 
     /**
+     * Updates the LineBreakConfig from the TextAppearance.
+     *
+     * This method updates the given line configuration from the TextAppearance. This method will
+     * request new layout if line break config has been changed.
+     *
+     * @param isLineBreakStyleSpecified true if the line break style is specified.
+     * @param isLineBreakWordStyleSpecified true if the line break word style is specified.
+     * @param lineBreakStyle the value of the line break style in the TextAppearance.
+     * @param lineBreakWordStyle the value of the line break word style in the TextAppearance.
+     */
+    private void updateLineBreakConfigFromTextAppearance(boolean isLineBreakStyleSpecified,
+            boolean isLineBreakWordStyleSpecified,
+            @LineBreakConfig.LineBreakStyle int lineBreakStyle,
+            @LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
+        boolean updated = false;
+        if (isLineBreakStyleSpecified && mLineBreakConfig.getLineBreakStyle() != lineBreakStyle) {
+            mLineBreakConfig.setLineBreakStyle(lineBreakStyle);
+            updated = true;
+        }
+        if (isLineBreakWordStyleSpecified
+                && mLineBreakConfig.getLineBreakWordStyle() != lineBreakWordStyle) {
+            mLineBreakConfig.setLineBreakWordStyle(lineBreakWordStyle);
+            updated = true;
+        }
+        if (updated && mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+    /**
      * Get the default primary {@link Locale} of the text in this TextView. This will always be
      * the first member of {@link #getTextLocales()}.
      * @return the default primary {@link Locale} of the text in this TextView.
@@ -4541,8 +4654,18 @@
      * @attr ref android.R.styleable#TextView_fallbackLineSpacing
      */
     public void setFallbackLineSpacing(boolean enabled) {
-        if (mUseFallbackLineSpacing != enabled) {
-            mUseFallbackLineSpacing = enabled;
+        int fallbackStrategy;
+        if (enabled) {
+            if (CompatChanges.isChangeEnabled(BORINGLAYOUT_FALLBACK_LINESPACING)) {
+                fallbackStrategy = FALLBACK_LINE_SPACING_ALL;
+            } else {
+                fallbackStrategy = FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
+            }
+        } else {
+            fallbackStrategy = FALLBACK_LINE_SPACING_NONE;
+        }
+        if (mUseFallbackLineSpacing != fallbackStrategy) {
+            mUseFallbackLineSpacing = fallbackStrategy;
             if (mLayout != null) {
                 nullLayouts();
                 requestLayout();
@@ -4560,7 +4683,17 @@
      */
     @InspectableProperty
     public boolean isFallbackLineSpacing() {
-        return mUseFallbackLineSpacing;
+        return mUseFallbackLineSpacing != FALLBACK_LINE_SPACING_NONE;
+    }
+
+    private boolean isFallbackLineSpacingForBoringLayout() {
+        return mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_ALL;
+    }
+
+    // Package privte for accessing from Editor.java
+    /* package */ boolean isFallbackLineSpacingForStaticLayout() {
+        return mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_ALL
+                || mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
     }
 
     /**
@@ -4738,13 +4871,61 @@
     }
 
     /**
+     * Sets line break configuration indicates which strategy needs to be used when calculating the
+     * text wrapping.
+     * <P>
+     * There are two types of line break rules that can be configured at the same time. One is
+     * line break style(lb) and the other is line break word style(lw). The line break style
+     * affects rule-based breaking. The line break word style affects dictionary-based breaking
+     * and provide phrase-based breaking opportunities. There are several types for the
+     * line break style:
+     * {@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE},
+     * {@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL} and
+     * {@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}.
+     * The type for the line break word style is
+     * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE}.
+     * The default values of the line break style and the line break word style are
+     * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and
+     * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE} respectively, indicating that no line
+     * breaking rules are specified.
+     * See <a href="https://drafts.csswg.org/css-text/#line-break-property">
+     *         the line-break property</a>
+     *
+     * @param lineBreakConfig the line break config for text wrapping.
+     */
+    public void setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
+        Objects.requireNonNull(lineBreakConfig);
+        if (mLineBreakConfig.equals(lineBreakConfig)) {
+            return;
+        }
+        mLineBreakConfig.set(lineBreakConfig);
+        if (mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    /**
+     * Get the current line break configuration for text wrapping.
+     *
+     * @return the current line break configuration to be used for text wrapping.
+     */
+    public @NonNull LineBreakConfig getLineBreakConfig() {
+        LineBreakConfig lbConfig = new LineBreakConfig();
+        lbConfig.set(mLineBreakConfig);
+        return lbConfig;
+    }
+
+    /**
      * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}.
      *
      * @return a current {@link PrecomputedText.Params}
      * @see PrecomputedText
      */
     public @NonNull PrecomputedText.Params getTextMetricsParams() {
-        return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
+        return new PrecomputedText.Params(new TextPaint(mTextPaint), mLineBreakConfig,
+                getTextDirectionHeuristic(),
                 mBreakStrategy, mHyphenationFrequency);
     }
 
@@ -4760,6 +4941,13 @@
         mTextDir = params.getTextDirection();
         mBreakStrategy = params.getBreakStrategy();
         mHyphenationFrequency = params.getHyphenationFrequency();
+        if (params.getLineBreakConfig() != null) {
+            mLineBreakConfig.set(params.getLineBreakConfig());
+        } else {
+            // Set default value if the line break config in the PrecomputedText.Params is null.
+            mLineBreakConfig.setLineBreakStyle(DEFAULT_LINE_BREAK_STYLE);
+            mLineBreakConfig.setLineBreakWordStyle(DEFAULT_LINE_BREAK_WORD_STYLE);
+        }
         if (mLayout != null) {
             nullLayouts();
             requestLayout();
@@ -6298,7 +6486,7 @@
             }
             final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
                     precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
-                            mHyphenationFrequency);
+                            mHyphenationFrequency, mLineBreakConfig);
             switch (checkResult) {
                 case PrecomputedText.Params.UNUSABLE:
                     throw new IllegalArgumentException(
@@ -9148,7 +9336,7 @@
 
             if (hintBoring == UNKNOWN_BORING) {
                 hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
-                                                   mHintBoring);
+                        isFallbackLineSpacingForBoringLayout(), mHintBoring);
                 if (hintBoring != null) {
                     mHintBoring = hintBoring;
                 }
@@ -9190,11 +9378,12 @@
                         .setTextDirection(mTextDir)
                         .setLineSpacing(mSpacingAdd, mSpacingMult)
                         .setIncludePad(mIncludePad)
-                        .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+                        .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
                         .setBreakStrategy(mBreakStrategy)
                         .setHyphenationFrequency(mHyphenationFrequency)
                         .setJustificationMode(mJustificationMode)
-                        .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+                        .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
+                        .setLineBreakConfig(mLineBreakConfig);
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
                             .setEllipsizedWidth(ellipsisWidth);
@@ -9250,7 +9439,7 @@
                     .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
-                    .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+                    .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
                     .setBreakStrategy(mBreakStrategy)
                     .setHyphenationFrequency(mHyphenationFrequency)
                     .setJustificationMode(mJustificationMode)
@@ -9259,7 +9448,8 @@
             result = builder.build();
         } else {
             if (boring == UNKNOWN_BORING) {
-                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
+                        isFallbackLineSpacingForBoringLayout(), mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -9303,11 +9493,12 @@
                     .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
-                    .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+                    .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
                     .setBreakStrategy(mBreakStrategy)
                     .setHyphenationFrequency(mHyphenationFrequency)
                     .setJustificationMode(mJustificationMode)
-                    .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+                    .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
+                    .setLineBreakConfig(mLineBreakConfig);
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
                         .setEllipsizedWidth(ellipsisWidth);
@@ -9430,7 +9621,8 @@
             }
 
             if (des < 0) {
-                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
+                        isFallbackLineSpacingForBoringLayout(), mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -9463,7 +9655,8 @@
                 }
 
                 if (hintDes < 0) {
-                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
+                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+                            isFallbackLineSpacingForBoringLayout(), mHintBoring);
                     if (hintBoring != null) {
                         mHintBoring = hintBoring;
                     }
@@ -9667,12 +9860,13 @@
         layoutBuilder.setAlignment(getLayoutAlignment())
                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
                 .setIncludePad(getIncludeFontPadding())
-                .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+                .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
                 .setBreakStrategy(getBreakStrategy())
                 .setHyphenationFrequency(getHyphenationFrequency())
                 .setJustificationMode(getJustificationMode())
                 .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
-                .setTextDirection(getTextDirectionHeuristic());
+                .setTextDirection(getTextDirectionHeuristic())
+                .setLineBreakConfig(mLineBreakConfig);
 
         final StaticLayout layout = layoutBuilder.build();
 
@@ -10636,8 +10830,8 @@
             return;
         }
 
-        if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected())
-                && getLineCount() == 1 && canMarquee()) {
+        if ((mMarquee == null || mMarquee.isStopped()) && isAggregatedVisible()
+                && (isFocused() || isSelected()) && getLineCount() == 1 && canMarquee()) {
 
             if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
                 mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;
@@ -11096,6 +11290,12 @@
         }
     }
 
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+        startStopMarquee(isVisible);
+    }
+
     /**
      * Use {@link BaseInputConnection#removeComposingSpans
      * BaseInputConnection.removeComposingSpans()} to remove any IME composing
@@ -12115,6 +12315,7 @@
             }
             if (canProcessText()) {  // also implies mEditor is not null.
                 mEditor.mProcessTextIntentActionsHandler.onInitializeAccessibilityNodeInfo(info);
+                mEditor.onInitializeSmartActionsAccessibilityNodeInfo(info);
             }
         }
 
@@ -12318,9 +12519,11 @@
      */
     @Override
     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (mEditor != null
-                && mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)) {
-            return true;
+        if (mEditor != null) {
+            if (mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)
+                    || mEditor.performSmartActionsAccessibilityAction(action)) {
+                return true;
+            }
         }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_CLICK: {
@@ -13749,7 +13952,8 @@
             mChoreographer.removeFrameCallback(mTickCallback);
 
             final TextView textView = mView.get();
-            if (textView != null && (textView.isFocused() || textView.isSelected())) {
+            if (textView != null && textView.isAggregatedVisible()
+                    && (textView.isFocused() || textView.isSelected())) {
                 long currentMs = mChoreographer.getFrameTime();
                 long deltaMs = currentMs - mLastAnimationMs;
                 mLastAnimationMs = currentMs;
diff --git a/core/java/android/window/BackNavigationInfo.aidl b/core/java/android/window/BackNavigationInfo.aidl
new file mode 100644
index 0000000..1529902
--- /dev/null
+++ b/core/java/android/window/BackNavigationInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * @hide
+ */
+parcelable BackNavigationInfo;
\ No newline at end of file
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
new file mode 100644
index 0000000..571714c
--- /dev/null
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteCallback;
+import android.view.SurfaceControl;
+
+/**
+ * Information to be sent to SysUI about a back event.
+ *
+ * @hide
+ */
+public final class BackNavigationInfo implements Parcelable {
+
+    /**
+     * The target of the back navigation is undefined.
+     */
+    public static final int TYPE_UNDEFINED = -1;
+
+    /**
+     * Navigating back will close the currently visible dialog
+     */
+    public static final int TYPE_DIALOG_CLOSE = 0;
+
+    /**
+     * Navigating back will bring the user back to the home screen
+     */
+    public static final int TYPE_RETURN_TO_HOME = 1;
+
+    /**
+     * Navigating back will bring the user to the previous activity in the same Task
+     */
+    public static final int TYPE_CROSS_ACTIVITY = 2;
+
+    /**
+     * Navigating back will bring the user to the previous activity in the previous Task
+     */
+    public static final int TYPE_CROSS_TASK = 3;
+
+    /**
+     * Defines the type of back destinations a back even can lead to. This is used to define the
+     * type of animation that need to be run on SystemUI.
+     */
+    @IntDef(prefix = "TYPE_", value = {
+            TYPE_UNDEFINED,
+            TYPE_DIALOG_CLOSE,
+            TYPE_RETURN_TO_HOME,
+            TYPE_CROSS_ACTIVITY,
+            TYPE_CROSS_TASK})
+    @interface BackTargetType {
+    }
+
+    private final int mType;
+    @Nullable
+    private final SurfaceControl mDepartingWindowContainer;
+    @Nullable
+    private final SurfaceControl mScreenshotSurface;
+    @Nullable
+    private final HardwareBuffer mScreenshotBuffer;
+    @Nullable
+    private final RemoteCallback mRemoteCallback;
+    @Nullable
+    private final WindowConfiguration mTaskWindowConfiguration;
+
+    /**
+     * Create a new {@link BackNavigationInfo} instance.
+     *
+     * @param type  The {@link BackTargetType} of the destination (what will be displayed after
+     *              the back action)
+     * @param topWindowLeash      The leash to animate away the current topWindow. The consumer
+     *                            of the leash is responsible for removing it.
+     * @param screenshotSurface The screenshot of the previous activity to be displayed.
+     * @param screenshotBuffer      A buffer containing a screenshot used to display the activity.
+     *                            See {@link  #getScreenshotHardwareBuffer()} for information
+     *                            about nullity.
+     * @param taskWindowConfiguration The window configuration of the Task being animated
+     *                            beneath.
+     * @param onBackNavigationDone   The callback to be called once the client is done with the back
+     *                           preview.
+     */
+    public BackNavigationInfo(@BackTargetType int type,
+            @Nullable SurfaceControl topWindowLeash,
+            @Nullable SurfaceControl screenshotSurface,
+            @Nullable HardwareBuffer screenshotBuffer,
+            @Nullable WindowConfiguration taskWindowConfiguration,
+            @NonNull RemoteCallback onBackNavigationDone) {
+        mType = type;
+        mDepartingWindowContainer = topWindowLeash;
+        mScreenshotSurface = screenshotSurface;
+        mScreenshotBuffer = screenshotBuffer;
+        mTaskWindowConfiguration = taskWindowConfiguration;
+        mRemoteCallback = onBackNavigationDone;
+    }
+
+    private BackNavigationInfo(@NonNull Parcel in) {
+        mType = in.readInt();
+        mDepartingWindowContainer = in.readTypedObject(SurfaceControl.CREATOR);
+        mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR);
+        mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR);
+        mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
+        mRemoteCallback = requireNonNull(in.readTypedObject(RemoteCallback.CREATOR));
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeTypedObject(mDepartingWindowContainer, flags);
+        dest.writeTypedObject(mScreenshotSurface, flags);
+        dest.writeTypedObject(mScreenshotBuffer, flags);
+        dest.writeTypedObject(mTaskWindowConfiguration, flags);
+        dest.writeTypedObject(mRemoteCallback, flags);
+    }
+
+    /**
+     * Returns the type of back navigation that is about to happen.
+     * @see BackTargetType
+     */
+    public @BackTargetType int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns a leash to the top window container that needs to be animated. This can be null if
+     * the back animation is controlled by the application.
+     */
+    @Nullable
+    public SurfaceControl getDepartingWindowContainer() {
+        return mDepartingWindowContainer;
+    }
+
+    /**
+     *  Returns the {@link SurfaceControl} that should be used to display a screenshot of the
+     *  previous activity.
+     */
+    @Nullable
+    public SurfaceControl getScreenshotSurface() {
+        return mScreenshotSurface;
+    }
+
+    /**
+     * Returns the {@link HardwareBuffer} containing the screenshot the activity about to be
+     * shown. This can be null if one of the following conditions is met:
+     * <ul>
+     *     <li>The screenshot is not available
+     *     <li> The previous activity is the home screen ( {@link  #TYPE_RETURN_TO_HOME}
+     *     <li> The current window is a dialog ({@link  #TYPE_DIALOG_CLOSE}
+     *     <li> The back animation is controlled by the application
+     * </ul>
+     */
+    @Nullable
+    public HardwareBuffer getScreenshotHardwareBuffer() {
+        return mScreenshotBuffer;
+    }
+
+    /**
+     * Returns the {@link WindowConfiguration} of the current task. This is null when the top
+     * application is controlling the back animation.
+     */
+    @Nullable
+    public WindowConfiguration getTaskWindowConfiguration() {
+        return mTaskWindowConfiguration;
+    }
+
+    /**
+     * Callback to be called when the back preview is finished in order to notify the server that
+     * it can clean up the resources created for the animation.
+     */
+    public void onBackNavigationFinished() {
+        mRemoteCallback.sendResult(null);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<BackNavigationInfo> CREATOR = new Creator<BackNavigationInfo>() {
+        @Override
+        public BackNavigationInfo createFromParcel(Parcel in) {
+            return new BackNavigationInfo(in);
+        }
+
+        @Override
+        public BackNavigationInfo[] newArray(int size) {
+            return new BackNavigationInfo[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "BackNavigationInfo{"
+                + "mType=" + typeToString(mType) + " (" + mType + ")"
+                + ", mDepartingWindowContainer=" + mDepartingWindowContainer
+                + ", mScreenshotSurface=" + mScreenshotSurface
+                + ", mTaskWindowConfiguration= " + mTaskWindowConfiguration
+                + ", mScreenshotBuffer=" + mScreenshotBuffer
+                + ", mRemoteCallback=" + mRemoteCallback
+                + '}';
+    }
+
+    /**
+     * Translates the {@link BackNavigationInfo} integer type to its String representation
+     */
+    public static String typeToString(@BackTargetType int type) {
+        switch (type) {
+            case  TYPE_UNDEFINED:
+                return "TYPE_UNDEFINED";
+            case TYPE_DIALOG_CLOSE:
+                return "TYPE_DIALOG_CLOSE";
+            case TYPE_RETURN_TO_HOME:
+                return "TYPE_RETURN_TO_HOME";
+            case TYPE_CROSS_ACTIVITY:
+                return "TYPE_CROSS_ACTIVITY";
+            case TYPE_CROSS_TASK:
+                return "TYPE_CROSS_TASK";
+        }
+        return String.valueOf(type);
+    }
+}
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 974a1dd..88ece5c 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -101,14 +101,6 @@
     public static final int FEATURE_IME_PLACEHOLDER = FEATURE_SYSTEM_FIRST + 7;
 
     /**
-     * Display area for one handed background layer, which preventing when user
-     * turning the Dark theme on, they can not clearly identify the screen has entered
-     * one handed mode.
-     * @hide
-     */
-    public static final int FEATURE_ONE_HANDED_BACKGROUND_PANEL = FEATURE_SYSTEM_FIRST + 8;
-
-    /**
      * Display area hosting IME window tokens (@see ImeContainer). By default, IMEs are parented
      * to FEATURE_IME_PLACEHOLDER but can be reparented under other RootDisplayArea.
      *
@@ -118,7 +110,7 @@
      * app on another screen).
      * @hide
      */
-    public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 9;
+    public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 8;
 
     /**
      * The last boundary of display area for system features
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index c3ef881..3359a41 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
+import android.util.ArraySet;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -81,8 +82,10 @@
 
     /**
      * This is called when the apps that contains running activities on the display has changed.
+     * The running activities refer to the non-finishing activities regardless of they are running
+     * in a process.
      */
-    public void onRunningAppsChanged(int[] runningUids) {}
+    public void onRunningAppsChanged(ArraySet<Integer> runningUids) {}
 
     /** Dump debug data */
     public void dump(String prefix, final PrintWriter pw) {
diff --git a/core/java/android/window/IOnBackInvokedCallback.aidl b/core/java/android/window/IOnBackInvokedCallback.aidl
new file mode 100644
index 0000000..a42863c
--- /dev/null
+++ b/core/java/android/window/IOnBackInvokedCallback.aidl
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+
+ */
+
+package android.window;
+
+/**
+ * Interface that wraps a {@link OnBackInvokedCallback} object, to be stored in window manager
+ * and called from back handling process when back is invoked.
+ *
+ * @hide
+ */
+oneway interface IOnBackInvokedCallback {
+   /**
+    * Called when a back gesture has been started, or back button has been pressed down.
+    * Wraps {@link OnBackInvokedCallback#onBackStarted()}.
+    */
+    void onBackStarted();
+
+    /**
+     * Called on back gesture progress.
+     * Wraps {@link OnBackInvokedCallback#onBackProgressed()}.
+     *
+     * @param touchX Absolute X location of the touch point.
+     * @param touchY Absolute Y location of the touch point.
+     * @param progress Value between 0 and 1 on how far along the back gesture is.
+     */
+    void onBackProgressed(int touchX, int touchY, float progress);
+
+    /**
+     * Called when a back gesture or back button press has been cancelled.
+     * Wraps {@link OnBackInvokedCallback#onBackCancelled()}.
+     */
+    void onBackCancelled();
+
+    /**
+     * Called when a back gesture has been completed and committed, or back button pressed
+     * has been released and committed.
+     * Wraps {@link OnBackInvokedCallback#onBackInvoked()}.
+     */
+    void onBackInvoked();
+}
diff --git a/core/java/android/window/IOnFpsCallbackListener.aidl b/core/java/android/window/IOnFpsCallbackListener.aidl
new file mode 100644
index 0000000..3091df3
--- /dev/null
+++ b/core/java/android/window/IOnFpsCallbackListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * @hide
+ */
+oneway interface IOnFpsCallbackListener {
+
+    /**
+     * Reports the fps from the registered task
+     * @param fps The frame rate per second of the task that has the registered task id
+     *            and its children.
+     */
+    void onFpsReported(in float fps);
+}
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index 090dbff..3f65f47 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -22,6 +22,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.UiThread;
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.content.Context;
@@ -48,13 +49,13 @@
      */
     int SPLASH_SCREEN_STYLE_UNDEFINED = -1;
     /**
-     * Force splash screen to be empty.
-     * @hide
+     * Flag to be used with {@link ActivityOptions#setSplashScreenStyle}, to avoid showing the
+     * splash screen icon of the launched activity
      */
     int SPLASH_SCREEN_STYLE_EMPTY = 0;
     /**
-     * Force splash screen to show icon.
-     * @hide
+     * Flag to be used with {@link ActivityOptions#setSplashScreenStyle}, to show the splash screen
+     * icon of the launched activity.
      */
     int SPLASH_SCREEN_STYLE_ICON = 1;
 
diff --git a/core/java/android/window/TaskFpsCallback.java b/core/java/android/window/TaskFpsCallback.java
new file mode 100644
index 0000000..a8e01b6
--- /dev/null
+++ b/core/java/android/window/TaskFpsCallback.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for sampling the frames per second for a task and its children.
+ * This should only be used by a system component that needs to listen to a task's
+ * tree's FPS when it is not actively submitting transactions for that corresponding SurfaceControl.
+ * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
+ *
+ * Each callback can only register for receiving FPS report for one task id until
+ * {@link WindowManager#unregister()} is called.
+ *
+ * @hide
+ */
+@SystemApi
+public final class TaskFpsCallback {
+
+    /**
+     * Listener interface to receive frame per second of a task.
+     */
+    public interface OnFpsCallbackListener {
+        /**
+         * Reports the fps from the registered task
+         * @param fps The frame per second of the task that has the registered task id
+         *            and its children.
+         */
+        void onFpsReported(float fps);
+    }
+
+    private final IOnFpsCallbackListener mListener;
+
+    public TaskFpsCallback(@NonNull Executor executor, @NonNull OnFpsCallbackListener listener) {
+        mListener = new IOnFpsCallbackListener.Stub() {
+            @Override
+            public void onFpsReported(float fps) {
+                executor.execute(() -> {
+                    listener.onFpsReported(fps);
+                });
+            }
+        };
+    }
+
+    /**
+     * @hide
+     */
+    public IOnFpsCallbackListener getListener() {
+        return mListener;
+    }
+
+    /**
+     * Dispatch the collected sample.
+     *
+     * Called from native code on a binder thread.
+     */
+    @BinderThread
+    private static void dispatchOnFpsReported(
+            @NonNull IOnFpsCallbackListener listener, float fps) {
+        try {
+            listener.onFpsReported(fps);
+        } catch (RemoteException e) {
+            /* ignore */
+        }
+    }
+}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 915c8fb..fd1e848 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -36,6 +36,7 @@
 import static android.view.WindowManager.TransitionType;
 import static android.view.WindowManager.transitTypeToString;
 
+import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -366,6 +367,7 @@
         private int mStartRotation = ROTATION_UNDEFINED;
         private int mEndRotation = ROTATION_UNDEFINED;
         private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        private @ColorInt int mBackgroundColor;
 
         public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
             mContainer = container;
@@ -387,6 +389,7 @@
             mStartRotation = in.readInt();
             mEndRotation = in.readInt();
             mRotationAnimation = in.readInt();
+            mBackgroundColor = in.readInt();
         }
 
         /** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -446,6 +449,11 @@
             mRotationAnimation = anim;
         }
 
+        /** Sets the background color of this change's container. */
+        public void setBackgroundColor(@ColorInt int backgroundColor) {
+            mBackgroundColor = backgroundColor;
+        }
+
         /** @return the container that is changing. May be null if non-remotable (eg. activity) */
         @Nullable
         public WindowContainerToken getContainer() {
@@ -526,6 +534,12 @@
             return mRotationAnimation;
         }
 
+        /** @return get the background color of this change's container. */
+        @ColorInt
+        public int getBackgroundColor() {
+            return mBackgroundColor;
+        }
+
         /** @hide */
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -542,6 +556,7 @@
             dest.writeInt(mStartRotation);
             dest.writeInt(mEndRotation);
             dest.writeInt(mRotationAnimation);
+            dest.writeInt(mBackgroundColor);
         }
 
         @NonNull
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
new file mode 100644
index 0000000..2978604
--- /dev/null
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.OnBackInvokedCallback;
+import android.view.OnBackInvokedDispatcher;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Provides window based implementation of {@link OnBackInvokedDispatcher}.
+ *
+ * Callbacks with higher priorities receive back dispatching first.
+ * Within the same priority, callbacks receive back dispatching in the reverse order
+ * in which they are added.
+ *
+ * When the top priority callback is updated, the new callback is propagated to the Window Manager
+ * if the window the instance is associated with has been attached. It is allowed to register /
+ * unregister {@link OnBackInvokedCallback}s before the window is attached, although callbacks
+ * will not receive dispatches until window attachment.
+ *
+ * @hide
+ */
+public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
+    private IWindowSession mWindowSession;
+    private IWindow mWindow;
+    private static final String TAG = "WindowOnBackDispatcher";
+    private static final boolean DEBUG = false;
+
+    /** The currently most prioritized callback. */
+    @Nullable
+    private OnBackInvokedCallbackWrapper mTopCallback;
+
+    /** Convenience hashmap to quickly decide if a callback has been added. */
+    private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
+    /** Holds all callbacks by priorities. */
+    private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
+            mOnBackInvokedCallbacks = new TreeMap<>();
+
+    /**
+     * Sends the pending top callback (if one exists) to WM when the view root
+     * is attached a window.
+     */
+    public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window) {
+        mWindowSession = windowSession;
+        mWindow = window;
+        if (mTopCallback != null) {
+            setTopOnBackInvokedCallback(mTopCallback);
+        }
+    }
+
+    /** Detaches the dispatcher instance from its window. */
+    public void detachFromWindow() {
+        mWindow = null;
+        mWindowSession = null;
+    }
+
+    // TODO: Take an Executor for the callback to run on.
+    @Override
+    public void registerOnBackInvokedCallback(
+            @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+        if (!mOnBackInvokedCallbacks.containsKey(priority)) {
+            mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
+        }
+        ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+
+        // If callback has already been added, remove it and re-add it.
+        if (mAllCallbacks.containsKey(callback)) {
+            if (DEBUG) {
+                Log.i(TAG, "Callback already added. Removing and re-adding it.");
+            }
+            Integer prevPriority = mAllCallbacks.get(callback);
+            mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
+        }
+
+        callbacks.add(callback);
+        mAllCallbacks.put(callback, priority);
+        if (mTopCallback == null || (mTopCallback.getCallback() != callback
+                && mAllCallbacks.get(mTopCallback.getCallback()) <= priority)) {
+            setTopOnBackInvokedCallback(new OnBackInvokedCallbackWrapper(callback, priority));
+        }
+    }
+
+    @Override
+    public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+        if (!mAllCallbacks.containsKey(callback)) {
+            if (DEBUG) {
+                Log.i(TAG, "Callback not found. returning...");
+            }
+            return;
+        }
+        Integer priority = mAllCallbacks.get(callback);
+        mOnBackInvokedCallbacks.get(priority).remove(callback);
+        mAllCallbacks.remove(callback);
+        if (mTopCallback != null && mTopCallback.getCallback() == callback) {
+            findAndSetTopOnBackInvokedCallback();
+        }
+    }
+
+    /** Clears all registered callbacks on the instance. */
+    public void clear() {
+        mAllCallbacks.clear();
+        mTopCallback = null;
+        mOnBackInvokedCallbacks.clear();
+    }
+
+    /**
+     * Iterates through all callbacks to find the most prioritized one and pushes it to
+     * window manager.
+     */
+    private void findAndSetTopOnBackInvokedCallback() {
+        if (mAllCallbacks.isEmpty()) {
+            setTopOnBackInvokedCallback(null);
+            return;
+        }
+
+        for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
+            ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+            if (!callbacks.isEmpty()) {
+                OnBackInvokedCallbackWrapper callback = new OnBackInvokedCallbackWrapper(
+                        callbacks.get(callbacks.size() - 1), priority);
+                setTopOnBackInvokedCallback(callback);
+                return;
+            }
+        }
+        setTopOnBackInvokedCallback(null);
+    }
+
+    // Pushes the top priority callback to window manager.
+    private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallbackWrapper callback) {
+        mTopCallback = callback;
+        if (mWindowSession == null || mWindow == null) {
+            return;
+        }
+        try {
+            mWindowSession.setOnBackInvokedCallback(mWindow, mTopCallback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e);
+        }
+    }
+
+    private class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+        private final OnBackInvokedCallback mCallback;
+        private final @Priority int mPriority;
+
+        OnBackInvokedCallbackWrapper(
+                @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+            mCallback = callback;
+            mPriority = priority;
+        }
+
+        @NonNull
+        public OnBackInvokedCallback getCallback() {
+            return mCallback;
+        }
+
+        @Override
+        public void onBackStarted() throws RemoteException {
+            Handler.getMain().post(() -> mCallback.onBackStarted());
+        }
+
+        @Override
+        public void onBackProgressed(int touchX, int touchY, float progress)
+                throws RemoteException {
+            Handler.getMain().post(() -> mCallback.onBackProgressed(touchX, touchY, progress));
+        }
+
+        @Override
+        public void onBackCancelled() throws RemoteException {
+            Handler.getMain().post(() -> mCallback.onBackCancelled());
+        }
+
+        @Override
+        public void onBackInvoked() throws RemoteException {
+            Handler.getMain().post(() -> mCallback.onBackInvoked());
+        }
+    }
+}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 4ba7ef2..547535d 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -19,9 +19,10 @@
 import static android.window.ConfigurationHelper.isDifferentDisplay;
 import static android.window.ConfigurationHelper.shouldUpdateResources;
 
+import android.annotation.BinderThread;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityThread;
 import android.app.IWindowToken;
 import android.app.ResourcesManager;
 import android.content.Context;
@@ -30,7 +31,9 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IWindowManager;
@@ -71,6 +74,8 @@
 
     private boolean mAttachToWindowContainer;
 
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
     /**
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
      * can only attach one {@link Context}.
@@ -132,7 +137,8 @@
             if (configuration == null) {
                 return false;
             }
-            onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+            mHandler.post(() -> onConfigurationChanged(configuration, displayId,
+                    false /* shouldReportConfigChange */));
             mAttachToWindowContainer = true;
             return true;
         } catch (RemoteException e) {
@@ -179,9 +185,11 @@
      * @param newConfig the updated {@link Configuration}
      * @param newDisplayId the updated {@link android.view.Display} ID
      */
+    @BinderThread
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
-        onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+        mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId,
+                true /* shouldReportConfigChange */));
     }
 
     // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
@@ -192,6 +200,7 @@
      * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
      * whether to dispatch configuration update or not.
      */
+    @MainThread
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
             boolean shouldReportConfigChange) {
@@ -217,16 +226,14 @@
 
             if (shouldReportConfigChange && context instanceof WindowContext) {
                 final WindowContext windowContext = (WindowContext) context;
-                ActivityThread.currentActivityThread().getHandler().post(
-                        () -> windowContext.dispatchConfigurationChanged(newConfig));
+                windowContext.dispatchConfigurationChanged(newConfig);
             }
 
             final int diff = mConfiguration.diffPublicOnly(newConfig);
             if (shouldReportConfigChange && diff != 0
                     && context instanceof WindowProviderService) {
                 final WindowProviderService windowProviderService = (WindowProviderService) context;
-                ActivityThread.currentActivityThread().getHandler().post(
-                        () -> windowProviderService.onConfigurationChanged(newConfig));
+                windowProviderService.onConfigurationChanged(newConfig);
             }
             freeTextLayoutCachesIfNeeded(diff);
             if (mShouldDumpConfigForIme) {
@@ -248,12 +255,15 @@
         }
     }
 
+    @BinderThread
     @Override
     public void onWindowTokenRemoved() {
-        final Context context = mContextRef.get();
-        if (context != null) {
-            context.destroy();
-            mContextRef.clear();
-        }
+        mHandler.post(() -> {
+            final Context context = mContextRef.get();
+            if (context != null) {
+                context.destroy();
+                mContextRef.clear();
+            }
+        });
     }
 }
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 733c0af..0130ef47 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -190,7 +190,7 @@
         mResInstanceState = source.readInt();
         mPresentityUri = source.readString();
         Parcelable[] tempParcelableArray = source.readParcelableArray(
-                                    PresTupleInfo.class.getClassLoader());
+                                    PresTupleInfo.class.getClassLoader(), PresTupleInfo.class);
         mTupleInfoArray = new PresTupleInfo[] {};
         if(tempParcelableArray != null) {
             mTupleInfoArray = Arrays.copyOf(tempParcelableArray, tempParcelableArray.length,
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 14fd4c2..40ca9fb 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -90,6 +90,14 @@
     public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "AccessibilityButton");
 
+    public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "ColorInversionTile");
+    public static final ComponentName DALTONIZER_TILE_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "ColorCorrectionTile");
+    public static final ComponentName ONE_HANDED_TILE_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "OneHandedModeTile");
+    public static final ComponentName REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "ReduceBrightColorsTile");
 
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index b723db2..4ad232a 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -467,8 +467,21 @@
     }
 
     protected void showEmptyState(ResolverListAdapter activeListAdapter,
+            @DrawableRes int iconRes, String title, String subtitle) {
+        showEmptyState(activeListAdapter, iconRes, title, subtitle, /* buttonOnClick */ null);
+    }
+
+    protected void showEmptyState(ResolverListAdapter activeListAdapter,
             @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
             View.OnClickListener buttonOnClick) {
+        String title = titleRes == 0 ? null : mContext.getString(titleRes);
+        String subtitle = subtitleRes == 0 ? null : mContext.getString(subtitleRes);
+        showEmptyState(activeListAdapter, iconRes, title, subtitle, buttonOnClick);
+    }
+
+    protected void showEmptyState(ResolverListAdapter activeListAdapter,
+            @DrawableRes int iconRes, String title, String subtitle,
+            View.OnClickListener buttonOnClick) {
         ProfileDescriptor descriptor = getItem(
                 userHandleToPageIndex(activeListAdapter.getUserHandle()));
         descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
@@ -479,15 +492,15 @@
         View container = emptyStateView.findViewById(R.id.resolver_empty_state_container);
         setupContainerPadding(container);
 
-        TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
-        title.setText(titleRes);
+        TextView titleView = emptyStateView.findViewById(R.id.resolver_empty_state_title);
+        titleView.setText(title);
 
-        TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
-        if (subtitleRes != 0) {
-            subtitle.setVisibility(View.VISIBLE);
-            subtitle.setText(subtitleRes);
+        TextView subtitleView = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+        if (subtitle != null) {
+            subtitleView.setVisibility(View.VISIBLE);
+            subtitleView.setText(subtitle);
         } else {
-            subtitle.setVisibility(View.GONE);
+            subtitleView.setVisibility(View.GONE);
         }
 
         Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 025f711..be7388b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -759,11 +759,11 @@
         }
 
         try {
-            IBinder permissionToken = ActivityTaskManager.getService()
-                    .requestStartActivityPermissionToken(getActivityToken());
             Intent delegationIntent = new Intent();
             final ComponentName delegateActivity = ComponentName.unflattenFromString(
                     Resources.getSystem().getString(R.string.config_chooserActivity));
+            IBinder permissionToken = ActivityTaskManager.getService()
+                    .requestStartActivityPermissionToken(delegateActivity);
             delegationIntent.setComponent(delegateActivity);
             delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent());
             delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 3b6a877..393bff4 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -16,7 +16,17 @@
 
 package com.android.internal.app;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.os.UserHandle;
 import android.view.LayoutInflater;
@@ -184,8 +194,8 @@
             View.OnClickListener listener) {
         showEmptyState(activeListAdapter,
                 R.drawable.ic_work_apps_off,
-                R.string.resolver_turn_on_work_apps,
-                /* subtitleRes */ 0,
+                getWorkAppPausedTitle(),
+                /* subtitle = */ null,
                 listener);
     }
 
@@ -194,13 +204,13 @@
         if (mIsSendAction) {
             showEmptyState(activeListAdapter,
                     R.drawable.ic_sharing_disabled,
-                    R.string.resolver_cross_profile_blocked,
-                    R.string.resolver_cant_share_with_work_apps_explanation);
+                    getCrossProfileBlockedTitle(),
+                    getCantShareWithWorkMessage());
         } else {
             showEmptyState(activeListAdapter,
                     R.drawable.ic_sharing_disabled,
-                    R.string.resolver_cross_profile_blocked,
-                    R.string.resolver_cant_access_work_apps_explanation);
+                    getCrossProfileBlockedTitle(),
+                    getCantAccessWorkMessage());
         }
     }
 
@@ -209,13 +219,13 @@
         if (mIsSendAction) {
             showEmptyState(activeListAdapter,
                     R.drawable.ic_sharing_disabled,
-                    R.string.resolver_cross_profile_blocked,
-                    R.string.resolver_cant_share_with_personal_apps_explanation);
+                    getCrossProfileBlockedTitle(),
+                    getCantShareWithPersonalMessage());
         } else {
             showEmptyState(activeListAdapter,
                     R.drawable.ic_sharing_disabled,
-                    R.string.resolver_cross_profile_blocked,
-                    R.string.resolver_cant_access_personal_apps_explanation);
+                    getCrossProfileBlockedTitle(),
+                    getCantAccessPersonalMessage());
         }
     }
 
@@ -223,8 +233,8 @@
     protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         showEmptyState(listAdapter,
                 R.drawable.ic_no_apps,
-                R.string.resolver_no_personal_apps_available,
-                /* subtitleRes */ 0);
+                getNoPersonalAppsAvailableMessage(),
+                /* subtitle= */ null);
 
     }
 
@@ -232,10 +242,65 @@
     protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         showEmptyState(listAdapter,
                 R.drawable.ic_no_apps,
-                R.string.resolver_no_work_apps_available,
-                /* subtitleRes */ 0);
+                getNoWorkAppsAvailableMessage(),
+                /* subtitle = */ null);
     }
 
+    private String getWorkAppPausedTitle() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_WORK_PAUSED_TITLE,
+                () -> getContext().getString(R.string.resolver_turn_on_work_apps));
+    }
+
+    private String getCrossProfileBlockedTitle() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+                () -> getContext().getString(R.string.resolver_cross_profile_blocked));
+    }
+
+    private String getCantShareWithWorkMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CANT_SHARE_WITH_WORK,
+                () -> getContext().getString(
+                        R.string.resolver_cant_share_with_work_apps_explanation));
+    }
+
+    private String getCantShareWithPersonalMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CANT_SHARE_WITH_PERSONAL,
+                () -> getContext().getString(
+                        R.string.resolver_cant_share_with_personal_apps_explanation));
+    }
+
+    private String getCantAccessWorkMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CANT_ACCESS_WORK,
+                () -> getContext().getString(
+                        R.string.resolver_cant_access_work_apps_explanation));
+    }
+
+    private String getCantAccessPersonalMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CANT_ACCESS_PERSONAL,
+                () -> getContext().getString(
+                        R.string.resolver_cant_access_personal_apps_explanation));
+    }
+
+    private String getNoWorkAppsAvailableMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_NO_WORK_APPS,
+                () -> getContext().getString(
+                        R.string.resolver_no_work_apps_available));
+    }
+
+    private String getNoPersonalAppsAvailableMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_NO_PERSONAL_APPS,
+                () -> getContext().getString(
+                        R.string.resolver_no_personal_apps_available));
+    }
+
+
     void setEmptyStateBottomOffset(int bottomOffset) {
         mBottomOffset = bottomOffset;
     }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1d13b73f..9648008 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -21,7 +21,9 @@
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
+import android.os.BluetoothBatteryStats;
 import android.os.ParcelFileDescriptor;
+import android.os.WakeLockStats;
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.WifiActivityEnergyInfo;
@@ -157,6 +159,14 @@
     /** {@hide} */
     GpsBatteryStats getGpsBatteryStats();
 
+    /** {@hide} */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
+    WakeLockStats getWakeLockStats();
+
+    /** {@hide} */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
+    BluetoothBatteryStats getBluetoothBatteryStats();
+
     HealthStatsParceler takeUidSnapshot(int uid);
     HealthStatsParceler[] takeUidSnapshots(in int[] uid);
 
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 0f37dc5..25b8dba 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -16,13 +16,14 @@
 
 package com.android.internal.app;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 
 import static com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER;
 import static com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE;
 
 import android.annotation.Nullable;
-import android.annotation.StringRes;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
@@ -101,16 +102,16 @@
         Intent intentReceived = getIntent();
         String className = intentReceived.getComponent().getClassName();
         final int targetUserId;
-        final int userMessageId;
+        final String userMessage;
         if (className.equals(FORWARD_INTENT_TO_PARENT)) {
-            userMessageId = com.android.internal.R.string.forward_intent_to_owner;
+            userMessage = getForwardToPersonalMessage();
             targetUserId = getProfileParent();
 
             getMetricsLogger().write(
                     new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
                     .setSubtype(MetricsEvent.PARENT_PROFILE));
         } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
-            userMessageId = com.android.internal.R.string.forward_intent_to_work;
+            userMessage = getForwardToWorkMessage();
             targetUserId = getManagedProfile();
 
             getMetricsLogger().write(
@@ -118,7 +119,7 @@
                     .setSubtype(MetricsEvent.MANAGED_PROFILE));
         } else {
             Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
-            userMessageId = -1;
+            userMessage = null;
             targetUserId = UserHandle.USER_NULL;
         }
         if (targetUserId == UserHandle.USER_NULL) {
@@ -156,11 +157,23 @@
                     return targetResolveInfo;
                 }, mExecutorService)
                 .thenAcceptAsync(result -> {
-                    maybeShowDisclosure(intentReceived, result, userMessageId);
+                    maybeShowDisclosure(intentReceived, result, userMessage);
                     finish();
                 }, getApplicationContext().getMainExecutor());
     }
 
+    private String getForwardToPersonalMessage() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                FORWARD_INTENT_TO_PERSONAL,
+                () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+    }
+
+    private String getForwardToWorkMessage() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                FORWARD_INTENT_TO_WORK,
+                () -> getString(com.android.internal.R.string.forward_intent_to_work));
+    }
+
     private boolean isIntentForwarderResolveInfo(ResolveInfo resolveInfo) {
         if (resolveInfo == null) {
             return false;
@@ -183,9 +196,9 @@
     }
 
     private void maybeShowDisclosure(
-            Intent intentReceived, ResolveInfo resolveInfo, int messageId) {
-        if (shouldShowDisclosure(resolveInfo, intentReceived)) {
-            mInjector.showToast(messageId, Toast.LENGTH_LONG);
+            Intent intentReceived, ResolveInfo resolveInfo, @Nullable String message) {
+        if (shouldShowDisclosure(resolveInfo, intentReceived) && message != null) {
+            mInjector.showToast(message, Toast.LENGTH_LONG);
         }
     }
 
@@ -405,8 +418,8 @@
         }
 
         @Override
-        public void showToast(int messageId, int duration) {
-            Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
+        public void showToast(String message, int duration) {
+            Toast.makeText(IntentForwarderActivity.this, message, duration).show();
         }
     }
 
@@ -419,6 +432,6 @@
 
         CompletableFuture<ResolveInfo> resolveActivityAsUser(Intent intent, int flags, int userId);
 
-        void showToast(@StringRes int messageId, int duration);
+        void showToast(String message, int duration);
     }
 }
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index d0719ee..b4ae56f 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -159,6 +159,14 @@
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        // In order to make the list view work with CollapsingToolbarLayout,
+        // we have to enable the nested scrolling feature of the list view.
+        getListView().setNestedScrollingEnabled(true);
+    }
+
+    @Override
     public boolean onOptionsItemSelected(MenuItem menuItem) {
         int id = menuItem.getItemId();
         switch (id) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index b273f6d..347153c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -17,6 +17,13 @@
 package com.android.internal.app;
 
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.PermissionChecker.PID_UNKNOWN;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -32,6 +39,7 @@
 import android.app.VoiceInteractor.PickOptionRequest.Option;
 import android.app.VoiceInteractor.Prompt;
 import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -123,7 +131,7 @@
     protected View mProfileView;
     private int mLastSelected = AbsListView.INVALID_POSITION;
     private boolean mResolvingHome = false;
-    private int mProfileSwitchMessageId = -1;
+    private String mProfileSwitchMessage;
     private int mLayoutId;
     @VisibleForTesting
     protected final ArrayList<Intent> mIntents = new ArrayList<>();
@@ -441,7 +449,7 @@
 
         // Determine whether we should show that intent is forwarded
         // from managed profile to owner or other way around.
-        setProfileSwitchMessageId(intent.getContentUserHint());
+        setProfileSwitchMessage(intent.getContentUserHint());
 
         mLaunchedFromUid = getLaunchedFromUid();
         if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
@@ -674,7 +682,7 @@
         }
 
         // Do not show the profile switch message anymore.
-        mProfileSwitchMessageId = -1;
+        mProfileSwitchMessage = null;
 
         onTargetSelected(dri, false);
         if (!mAwaitingDelegateResponse) {
@@ -828,7 +836,7 @@
         }
     }
 
-    private void setProfileSwitchMessageId(int contentUserHint) {
+    private void setProfileSwitchMessage(int contentUserHint) {
         if (contentUserHint != UserHandle.USER_CURRENT &&
                 contentUserHint != UserHandle.myUserId()) {
             UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
@@ -837,13 +845,25 @@
                     : false;
             boolean targetIsManaged = userManager.isManagedProfile();
             if (originIsManaged && !targetIsManaged) {
-                mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
+                mProfileSwitchMessage = getForwardToPersonalMsg();
             } else if (!originIsManaged && targetIsManaged) {
-                mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
+                mProfileSwitchMessage = getForwardToWorkMsg();
             }
         }
     }
 
+    private String getForwardToPersonalMsg() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                FORWARD_INTENT_TO_PERSONAL,
+                () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+    }
+
+    private String getForwardToWorkMsg() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                FORWARD_INTENT_TO_WORK,
+                () -> getString(com.android.internal.R.string.forward_intent_to_work));
+    }
+
     /**
      * Turn on launch mode that is safe to use when forwarding intents received from
      * applications and running in system processes.  This mode uses Activity.startActivityAsCaller
@@ -1095,9 +1115,9 @@
         ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
                 .resolveInfoForPosition(which, hasIndexBeenFiltered);
         if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
-            Toast.makeText(this, String.format(getResources().getString(
-                    com.android.internal.R.string.activity_resolver_work_profiles_support),
-                    ri.activityInfo.loadLabel(getPackageManager()).toString()),
+            Toast.makeText(this,
+                    getWorkProfileNotSupportedMsg(
+                            ri.activityInfo.loadLabel(getPackageManager()).toString()),
                     Toast.LENGTH_LONG).show();
             return;
         }
@@ -1128,6 +1148,15 @@
         }
     }
 
+    private String getWorkProfileNotSupportedMsg(String launcherName) {
+        return getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_WORK_PROFILE_NOT_SUPPORTED,
+                () -> getString(
+                        com.android.internal.R.string.activity_resolver_work_profiles_support,
+                        launcherName),
+                launcherName);
+    }
+
     /**
      * Replace me in subclasses!
      */
@@ -1394,8 +1423,8 @@
         }
         // If needed, show that intent is forwarded
         // from managed profile to owner or other way around.
-        if (mProfileSwitchMessageId != -1) {
-            Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
+        if (mProfileSwitchMessage != null) {
+            Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
         }
         if (!mSafeForwardingMode) {
             if (cti.startAsUser(this, null, user)) {
@@ -1437,11 +1466,11 @@
         try {
             // TODO: Once this is a small springboard activity, it can move off the UI process
             // and we can move the request method to ActivityManagerInternal.
-            IBinder permissionToken = ActivityTaskManager.getService()
-                    .requestStartActivityPermissionToken(getActivityToken());
             final Intent chooserIntent = new Intent();
             final ComponentName delegateActivity = ComponentName.unflattenFromString(
                     Resources.getSystem().getString(R.string.config_chooserActivity));
+            IBinder permissionToken = ActivityTaskManager.getService()
+                    .requestStartActivityPermissionToken(delegateActivity);
             chooserIntent.setClassName(delegateActivity.getPackageName(),
                     delegateActivity.getClassName());
             chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
@@ -1742,12 +1771,12 @@
         viewPager.setSaveEnabled(false);
         TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL)
                 .setContent(R.id.profile_pager)
-                .setIndicator(getString(R.string.resolver_personal_tab));
+                .setIndicator(getPersonalTabLabel());
         tabHost.addTab(tabSpec);
 
         tabSpec = tabHost.newTabSpec(TAB_TAG_WORK)
                 .setContent(R.id.profile_pager)
-                .setIndicator(getString(R.string.resolver_work_tab));
+                .setIndicator(getWorkTabLabel());
         tabHost.addTab(tabSpec);
 
         TabWidget tabWidget = tabHost.getTabWidget();
@@ -1799,6 +1828,16 @@
         findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
     }
 
+    private String getPersonalTabLabel() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_PERSONAL_TAB, () -> getString(R.string.resolver_personal_tab));
+    }
+
+    private String getWorkTabLabel() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_WORK_TAB, () -> getString(R.string.resolver_work_tab));
+    }
+
     void onHorizontalSwipeStateChanged(int state) {}
 
     private void maybeHideDivider() {
@@ -1830,8 +1869,6 @@
     }
 
     private void resetTabsHeaderStyle(TabWidget tabWidget) {
-        String workContentDescription = getString(R.string.resolver_work_tab_accessibility);
-        String personalContentDescription = getString(R.string.resolver_personal_tab_accessibility);
         for (int i = 0; i < tabWidget.getChildCount(); i++) {
             View tabView = tabWidget.getChildAt(i);
             TextView title = tabView.findViewById(android.R.id.title);
@@ -1839,14 +1876,26 @@
             title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
             title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                     getResources().getDimension(R.dimen.resolver_tab_text_size));
-            if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
-                tabView.setContentDescription(personalContentDescription);
-            } else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
-                tabView.setContentDescription(workContentDescription);
+            if (title.getText().equals(getPersonalTabLabel())) {
+                tabView.setContentDescription(getPersonalTabAccessibilityLabel());
+            } else if (title.getText().equals(getWorkTabLabel())) {
+                tabView.setContentDescription(getWorkTabAccessibilityLabel());
             }
         }
     }
 
+    private String getPersonalTabAccessibilityLabel() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_PERSONAL_TAB_ACCESSIBILITY,
+                () -> getString(R.string.resolver_personal_tab_accessibility));
+    }
+
+    private String getWorkTabAccessibilityLabel() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_WORK_TAB_ACCESSIBILITY,
+                () -> getString(R.string.resolver_work_tab_accessibility));
+    }
+
     private static int getAttrColor(Context context, int attr) {
         TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
         int colorAccent = ta.getColor(0, 0);
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 622f166..4da59a3 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -16,7 +16,15 @@
 
 package com.android.internal.app;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.UserHandle;
@@ -196,8 +204,8 @@
             View.OnClickListener listener) {
         showEmptyState(activeListAdapter,
                 R.drawable.ic_work_apps_off,
-                R.string.resolver_turn_on_work_apps,
-                /* subtitleRes */ 0,
+                getWorkAppPausedTitle(),
+                /* subtitle = */ null,
                 listener);
     }
 
@@ -205,32 +213,72 @@
     protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
         showEmptyState(activeListAdapter,
                 R.drawable.ic_sharing_disabled,
-                R.string.resolver_cross_profile_blocked,
-                R.string.resolver_cant_access_work_apps_explanation);
+                getCrossProfileBlockedTitle(),
+                getCantAccessWorkMessage());
     }
 
     @Override
     protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
         showEmptyState(activeListAdapter,
                 R.drawable.ic_sharing_disabled,
-                R.string.resolver_cross_profile_blocked,
-                R.string.resolver_cant_access_personal_apps_explanation);
+                getCrossProfileBlockedTitle(),
+                getCantAccessPersonalMessage());
     }
 
     @Override
     protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         showEmptyState(listAdapter,
                 R.drawable.ic_no_apps,
-                R.string.resolver_no_personal_apps_available,
-                /* subtitleRes */ 0);
+                getNoPersonalAppsAvailableMessage(),
+                /* subtitle = */ null);
     }
 
     @Override
     protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         showEmptyState(listAdapter,
                 R.drawable.ic_no_apps,
-                R.string.resolver_no_work_apps_available,
-                /* subtitleRes */ 0);
+                getNoWorkAppsAvailableMessage(),
+                /* subtitle= */ null);
+    }
+
+    private String getWorkAppPausedTitle() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_WORK_PAUSED_TITLE,
+                () -> getContext().getString(R.string.resolver_turn_on_work_apps));
+    }
+
+    private String getCrossProfileBlockedTitle() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+                () -> getContext().getString(R.string.resolver_cross_profile_blocked));
+    }
+
+    private String getCantAccessWorkMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CANT_ACCESS_WORK,
+                () -> getContext().getString(
+                        R.string.resolver_cant_access_work_apps_explanation));
+    }
+
+    private String getCantAccessPersonalMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_CANT_ACCESS_PERSONAL,
+                () -> getContext().getString(
+                        R.string.resolver_cant_access_personal_apps_explanation));
+    }
+
+    private String getNoWorkAppsAvailableMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_NO_WORK_APPS,
+                () -> getContext().getString(
+                        R.string.resolver_no_work_apps_available));
+    }
+
+    private String getNoPersonalAppsAvailableMessage() {
+        return getContext().getSystemService(DevicePolicyManager.class).getString(
+                RESOLVER_NO_PERSONAL_APPS,
+                () -> getContext().getString(
+                        R.string.resolver_no_personal_apps_available));
     }
 
     void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index ca0856238..3531fad 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.app;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_TITLE;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -70,8 +73,8 @@
         String dialogTitle;
         String dialogMessage = null;
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
-            dialogTitle = getResources().getString(R.string.work_mode_off_title);
-            dialogMessage = getResources().getString(R.string.work_mode_off_message);
+            dialogTitle = getDialogTitle();
+            dialogMessage = getDialogMessage();
         } else {
             Log.wtf(TAG, "Invalid unlaunchable type: " + mReason);
             finish();
@@ -91,6 +94,17 @@
         builder.show();
     }
 
+    private String getDialogTitle() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                UNLAUNCHABLE_APP_WORK_PAUSED_TITLE, () -> getString(R.string.work_mode_off_title));
+    }
+
+    private String getDialogMessage() {
+        return getSystemService(DevicePolicyManager.class).getString(
+                UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE,
+                () -> getString(R.string.work_mode_off_message));
+    }
+
     @Override
     public void onDismiss(DialogInterface dialog) {
         finish();
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 289daee..301de2d 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -239,7 +239,8 @@
         mExtendedInfo = in.readCharSequence();
         mResolvedIntent = in.readParcelable(null /* ClassLoader */, android.content.Intent.class);
         mSourceIntents.addAll(
-                Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */)));
+                Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */,
+                        Intent.class)));
         mIsSuspended = in.readBoolean();
         mPinned = in.readBoolean();
         mResolveInfo = in.readParcelable(null /* ClassLoader */, android.content.pm.ResolveInfo.class);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index f904610..0ada13a7 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -163,6 +163,12 @@
     public static final String PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED =
             "location_indicators_small_enabled";
 
+    /**
+     * Whether to show the location indicator for system apps.
+     */
+    public static final String PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM =
+            "location_indicators_show_system";
+
     // Flags related to Assistant
 
     /**
@@ -523,6 +529,12 @@
      */
     public static final String TASK_MANAGER_ENABLED = "task_manager_enabled";
 
+
+    /**
+     * (boolean) Whether the clipboard overlay is enabled.
+     */
+    public static final String CLIPBOARD_OVERLAY_ENABLED = "clipboard_overlay_enabled";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 29b31d3..b786526 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.PackagePartitions;
-import android.content.pm.parsing.ParsingPackageRead;
 import android.os.Build;
 import android.os.Trace;
 import android.util.ArrayMap;
@@ -82,7 +81,22 @@
     public interface PackageProvider {
 
         /** Performs the given action for each package. */
-        void forEachPackage(TriConsumer<ParsingPackageRead, Boolean, File> p);
+        void forEachPackage(TriConsumer<Package, Boolean, File> p);
+
+        interface Package {
+
+            String getBaseApkPath();
+
+            int getOverlayPriority();
+
+            String getOverlayTarget();
+
+            String getPackageName();
+
+            int getTargetSdkVersion();
+
+            boolean isOverlayIsStatic();
+        }
     }
 
     private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
@@ -304,7 +318,7 @@
     private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos(
             @NonNull PackageProvider packageManager) {
         final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>();
-        packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem,
+        packageManager.forEachPackage((PackageProvider.Package p, Boolean isSystem,
                 @Nullable File preInstalledApexPath) -> {
             if (p.getOverlayTarget() != null && isSystem) {
                 overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(),
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
index e4e0228..0fafd10 100644
--- a/core/java/com/android/internal/content/om/OverlayScanner.java
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -16,15 +16,13 @@
 
 package com.android.internal.content.om;
 
-import static android.content.pm.parsing.ParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY;
-import static android.content.pm.parsing.ParsingPackageUtils.checkRequiredSystemProperties;
-
 import static com.android.internal.content.om.OverlayConfig.TAG;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.text.TextUtils;
@@ -183,7 +181,8 @@
             List<Pair<String, File>> outExcludedOverlayPackages) {
         final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat();
         final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(),
-                overlayApk, PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY);
+                overlayApk,
+                FrameworkParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY);
         if (ret.isError()) {
             Log.w(TAG, "Got exception loading overlay.", ret.getException());
             return null;
@@ -196,7 +195,8 @@
         final String propName = apkLite.getRequiredSystemPropertyName();
         final String propValue = apkLite.getRequiredSystemPropertyValue();
         if ((!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue))
-                && !checkRequiredSystemProperties(propName, propValue)) {
+                && !FrameworkParsingPackageUtils.checkRequiredSystemProperties(propName,
+                propValue)) {
             // The overlay package should be excluded. Adds it into the outExcludedOverlayPackages
             // for overlay configuration parser to ignore it.
             outExcludedOverlayPackages.add(Pair.create(apkLite.getPackageName(), overlayApk));
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
index 537e797..dff9551 100644
--- a/core/java/com/android/internal/graphics/ColorUtils.java
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -62,7 +62,10 @@
         return Color.argb(a, r, g, b);
     }
 
-    private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
+    /**
+     * Returns the composite alpha of the given foreground and background alpha.
+     */
+    public static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
         return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
     }
 
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 84391c1..0443ad0 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -24,7 +24,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.util.EventLog;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -585,6 +584,7 @@
     /**
      * @see #writeThrowable
      */
+    @SuppressWarnings("UnsafeParcelApi")
     private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) {
         final boolean hasThrowable = parcel.readBoolean();
         if (!hasThrowable) {
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 9d0f209..08bc8c7 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -42,4 +42,5 @@
     void shouldOfferSwitchingToNextInputMethod(in AndroidFuture future /* T=Boolean */);
     void notifyUserActionAsync();
     void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
+    void onStylusHandwritingReady(int requestId);
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index d4cc376..7ebcc88 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -394,4 +394,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Calls {@link IInputMethodPrivilegedOperations#onStylusHandwritingReady()}
+     */
+    @AnyThread
+    public void onStylusHandwritingReady(int requestId) {
+        final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+        if (ops == null) {
+            return;
+        }
+        try {
+            ops.onStylusHandwritingReady(requestId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 662ed6b..0470444 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -36,6 +36,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.DumpableInputConnection;
@@ -350,8 +351,19 @@
                     }
                     if (handler.getLooper().isCurrentThread()) {
                         servedView.onInputConnectionClosedInternal();
+                        final ViewRootImpl viewRoot = servedView.getViewRootImpl();
+                        if (viewRoot != null) {
+                            viewRoot.getHandwritingInitiator().onInputConnectionClosed(servedView);
+                        }
                     } else {
                         handler.post(servedView::onInputConnectionClosedInternal);
+                        handler.post(() -> {
+                            final ViewRootImpl viewRoot = servedView.getViewRootImpl();
+                            if (viewRoot != null) {
+                                viewRoot.getHandwritingInitiator()
+                                        .onInputConnectionClosed(servedView);
+                            }
+                        });
                     }
                 }
             }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index d14054d..e1a67d8 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -24,8 +24,6 @@
 import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
 import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
 
-import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
-import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
 
@@ -241,7 +239,6 @@
             if (!mSurfaceOnly) {
                 mRendererWrapper.addObserver(mObserver);
             }
-            notifyCujEvent(ACTION_SESSION_BEGIN);
         }
     }
 
@@ -523,7 +520,6 @@
                     maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
                     missedSfFramesCount,
                     missedAppFramesCount);
-            notifyCujEvent(ACTION_METRICS_LOGGED);
         }
         if (DEBUG) {
             Log.i(TAG, "finish: CUJ=" + mSession.getName()
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 0d4ad38..5a66e9a 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,10 +16,7 @@
 
 package com.android.internal.jank;
 
-import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
-
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
-import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
 import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
@@ -41,6 +38,8 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
@@ -63,17 +62,16 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Build;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
-import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
 import android.util.Log;
@@ -129,14 +127,8 @@
     private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
-    public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
     public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
-    public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
-    public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
-    public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
-    @VisibleForTesting
-    public static final String PROP_NOTIFY_CUJ_EVENT = "debug.jank.notify_cuj_events";
 
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
@@ -181,6 +173,9 @@
     public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
     public static final int CUJ_SCREEN_OFF = 40;
     public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
+    public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
+    public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
+    public static final int CUJ_UNFOLD_ANIM = 44;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -231,6 +226,9 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -293,6 +291,9 @@
             CUJ_SPLASHSCREEN_EXIT_ANIM,
             CUJ_SCREEN_OFF,
             CUJ_SCREEN_OFF_SHOW_AOD,
+            CUJ_ONE_HANDED_ENTER_TRANSITION,
+            CUJ_ONE_HANDED_EXIT_TRANSITION,
+            CUJ_UNFOLD_ANIM,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -366,8 +367,7 @@
                 new ChoreographerWrapper(Choreographer.getInstance());
 
         synchronized (mLock) {
-            FrameTrackerListener eventsListener =
-                    (s, act) -> handleCujEvents(config.getContext(), act, s);
+            FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s);
             return new FrameTracker(session, mWorker.getThreadHandler(),
                     threadedRenderer, viewRoot, surfaceControl, choreographer, mMetrics,
                     mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
@@ -375,24 +375,13 @@
         }
     }
 
-    private void handleCujEvents(Context context, String action, Session session) {
+    private void handleCujEvents(String action, Session session) {
         // Clear the running and timeout tasks if the end / cancel was fired within the tracker.
         // Or we might have memory leaks.
         if (needRemoveTasks(action, session)) {
             removeTimeout(session.getCuj());
             removeTracker(session.getCuj());
         }
-
-        // Notify the receivers if necessary.
-        if (session.shouldNotify()) {
-            if (context != null) {
-                notifyEvents(context, action, session);
-            } else {
-                throw new IllegalArgumentException(
-                        "Can't notify cuj events due to lack of context: cuj="
-                        + session.getName() + ", action=" + action);
-            }
-        }
     }
 
     private boolean needRemoveTasks(String action, Session session) {
@@ -404,22 +393,6 @@
         return badEnd || badCancel;
     }
 
-    /**
-     * Notifies who may interest in some CUJ events.
-     */
-    @VisibleForTesting
-    public void notifyEvents(Context context, String action, Session session) {
-        if (action.equals(ACTION_SESSION_CANCEL)
-                && session.getReason() == REASON_CANCEL_NOT_BEGUN) {
-            return;
-        }
-        Intent intent = new Intent(action);
-        intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
-        intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
-        intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
-        context.sendBroadcast(intent);
-    }
-
     private void removeTimeout(@CujType int cujType) {
         synchronized (mLock) {
             Runnable timeout = mTimeoutActions.get(cujType);
@@ -617,7 +590,17 @@
      */
     public static String getNameOfInteraction(int interactionType) {
         // There is an offset amount of 1 between cujType and interactionType.
-        return getNameOfCuj(interactionType - 1);
+        return getNameOfCuj(getCujTypeFromInteraction(interactionType));
+    }
+
+    /**
+     * A helper method to translate interaction type to CUJ type.
+     *
+     * @param interactionType the interaction type defined in AtomsProto.java
+     * @return the integer in {@link CujType}
+     */
+    private static int getCujTypeFromInteraction(int interactionType) {
+        return interactionType - 1;
     }
 
     /**
@@ -712,6 +695,12 @@
                 return "SCREEN_OFF";
             case CUJ_SCREEN_OFF_SHOW_AOD:
                 return "SCREEN_OFF_SHOW_AOD";
+            case CUJ_ONE_HANDED_ENTER_TRANSITION:
+                return "ONE_HANDED_ENTER_TRANSITION";
+            case CUJ_ONE_HANDED_EXIT_TRANSITION:
+                return "ONE_HANDED_EXIT_TRANSITION";
+            case CUJ_UNFOLD_ANIM:
+                return "UNFOLD_ANIM";
         }
         return "UNKNOWN";
     }
@@ -923,13 +912,11 @@
         private final long mTimeStamp;
         @Reasons
         private int mReason = REASON_END_UNKNOWN;
-        private final boolean mShouldNotify;
         private final String mName;
 
         public Session(@CujType int cujType, @NonNull String postfix) {
             mCujType = cujType;
             mTimeStamp = System.nanoTime();
-            mShouldNotify = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
             mName = TextUtils.isEmpty(postfix)
                     ? String.format("J<%s>", getNameOfCuj(mCujType))
                     : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
@@ -969,10 +956,5 @@
         public @Reasons int getReason() {
             return mReason;
         }
-
-        /** Determines if should notify the receivers of cuj events */
-        public boolean shouldNotify() {
-            return mShouldNotify;
-        }
     }
 }
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 2f4a14f..5378b03 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -58,6 +58,14 @@
     void log(@NonNull UiEventEnum event);
 
     /**
+     * Log a simple event with an instance id, without package information.
+     * Does nothing if event.getId() <= 0.
+     * @param event an enum implementing UiEventEnum interface.
+     * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to log().
+     */
+    void log(@NonNull UiEventEnum event, @Nullable InstanceId instance);
+
+    /**
      * Log an event with package information. Does nothing if event.getId() <= 0.
      * Give both uid and packageName if both are known, but one may be omitted if unknown.
      * @param event an enum implementing UiEventEnum interface.
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index c0f44a5..983e0fe 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -42,16 +42,21 @@
     }
 
     @Override
+    public void log(UiEventEnum event, InstanceId instanceId) {
+        logWithInstanceId(event, 0, null, instanceId);
+    }
+
+    @Override
     public void logWithInstanceId(UiEventEnum event, int uid, String packageName,
             InstanceId instance) {
         final int eventID = event.getId();
-        if ((eventID > 0)  && (instance != null)) {
+        if ((eventID > 0) && (instance != null)) {
             FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED,
                     /* event_id = 1 */ eventID,
                     /* uid = 2 */ uid,
                     /* package_name = 3 */ packageName,
                     /* instance_id = 4 */ instance.getId());
-        } else {
+        } else if (eventID > 0) {
             log(event, uid, packageName);
         }
     }
@@ -78,7 +83,7 @@
                     /* package_name = 2 */ packageName,
                     /* instance_id = 3 */ instance.getId(),
                     /* position_picked = 4 */ position);
-        } else {
+        } else if ((eventID > 0)) {
             logWithPosition(event, uid, packageName, position);
         }
     }
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 2d09434..e303890 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -88,6 +88,11 @@
     }
 
     @Override
+    public void log(UiEventEnum event, InstanceId instance) {
+        logWithInstanceId(event, 0, null, instance);
+    }
+
+    @Override
     public void log(UiEventEnum event, int uid, String packageName) {
         final int eventId = event.getId();
         if (eventId > 0) {
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
index 62517fa..bf23ad1 100644
--- a/core/java/com/android/internal/midi/MidiFramer.java
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -99,6 +99,12 @@
                 }
             } else { // data byte
                 if (!mInSysEx) {
+                    // Hack to avoid crashing if we start parsing in the middle
+                    // of a data stream
+                    if (mNeeded <= 0) {
+                        break;
+                    }
+
                     mBuffer[mCount++] = currentByte;
                     if (--mNeeded == 0) {
                         if (mRunningStatus != 0) {
diff --git a/core/java/com/android/internal/midi/OWNERS b/core/java/com/android/internal/midi/OWNERS
new file mode 100644
index 0000000..af273a6
--- /dev/null
+++ b/core/java/com/android/internal/midi/OWNERS
@@ -0,0 +1 @@
+include /services/midi/OWNERS
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 2a203ac..b579be0 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -34,8 +34,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 
-import java.net.Inet4Address;
-import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -93,8 +91,8 @@
     public String interfaze;
     public String session;
     public int mtu = -1;
-    public List<LinkAddress> addresses = new ArrayList<LinkAddress>();
-    public List<RouteInfo> routes = new ArrayList<RouteInfo>();
+    public List<LinkAddress> addresses = new ArrayList<>();
+    public List<RouteInfo> routes = new ArrayList<>();
     public List<String> dnsServers;
     public List<String> searchDomains;
     public List<String> allowedApplications;
@@ -114,12 +112,32 @@
     public VpnConfig() {
     }
 
-    public void updateAllowedFamilies(InetAddress address) {
-        if (address instanceof Inet4Address) {
-            allowIPv4 = true;
-        } else {
-            allowIPv6 = true;
-        }
+    public VpnConfig(VpnConfig other) {
+        user = other.user;
+        interfaze = other.interfaze;
+        session = other.session;
+        mtu = other.mtu;
+        addresses = copyOf(other.addresses);
+        routes = copyOf(other.routes);
+        dnsServers = copyOf(other.dnsServers);
+        searchDomains = copyOf(other.searchDomains);
+        allowedApplications = copyOf(other.allowedApplications);
+        disallowedApplications = copyOf(other.disallowedApplications);
+        configureIntent = other.configureIntent;
+        startTime = other.startTime;
+        legacy = other.legacy;
+        blocking = other.blocking;
+        allowBypass = other.allowBypass;
+        allowIPv4 = other.allowIPv4;
+        allowIPv6 = other.allowIPv6;
+        isMetered = other.isMetered;
+        underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
+                other.underlyingNetworks, other.underlyingNetworks.length) : null;
+        proxyInfo = other.proxyInfo;
+    }
+
+    private static <T> List<T> copyOf(List<T> list) {
+        return list != null ? new ArrayList<>(list) : null;
     }
 
     public void addLegacyRoutes(String routesStr) {
@@ -131,7 +149,6 @@
             //each route is ip/prefix
             RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST);
             this.routes.add(info);
-            updateAllowedFamilies(info.getDestination().getAddress());
         }
     }
 
@@ -144,7 +161,6 @@
             //each address is ip/prefix
             LinkAddress addr = new LinkAddress(address);
             this.addresses.add(addr);
-            updateAllowedFamilies(addr.getAddress());
         }
     }
 
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 2f40d3b..3b6f8f6 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -14,10 +14,13 @@
 
 package com.android.internal.notification;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_CHANNEL_DEVICE_ADMIN;
+
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
@@ -143,7 +146,7 @@
 
         final NotificationChannel deviceAdmin = new NotificationChannel(
                 DEVICE_ADMIN,
-                context.getString(R.string.notification_channel_device_admin),
+                getDeviceAdminNotificationChannelName(context),
                 NotificationManager.IMPORTANCE_HIGH);
         channelsList.add(deviceAdmin);
 
@@ -209,6 +212,12 @@
         nm.createNotificationChannels(channelsList);
     }
 
+    private static String getDeviceAdminNotificationChannelName(Context context) {
+        DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(NOTIFICATION_CHANNEL_DEVICE_ADMIN,
+                () -> context.getString(R.string.notification_channel_device_admin));
+    }
+
     /** Remove notification channels which are no longer used */
     public static void removeDeprecated(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 879e0a8..b0fce8f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -160,6 +160,11 @@
         mHistoryDir = null;
         mHistoryBuffer = historyBuffer;
     }
+
+    public File getHistoryDirectory() {
+        return mHistoryDir;
+    }
+
     /**
      * Set the active file that mHistoryBuffer is backed up into.
      *
@@ -375,12 +380,26 @@
     }
 
     /**
-     * Read all history files and serialize into a big Parcel. This is to send history files to
-     * Settings app since Settings app can not access /data/system directory.
-     * Checkin file also call this method.
+     * Read all history files and serialize into a big Parcel.
+     * Checkin file calls this method.
+     *
      * @param out the output parcel
      */
     public void writeToParcel(Parcel out) {
+        writeToParcel(out, false /* useBlobs */);
+    }
+
+    /**
+     * This is for Settings app, when Settings app receives big history parcel, it call
+     * this method to parse it into list of parcels.
+     * @param out the output parcel
+     */
+    public void writeToBatteryUsageStatsParcel(Parcel out) {
+        out.writeBlob(mHistoryBuffer.marshall());
+        writeToParcel(out, true /* useBlobs */);
+    }
+
+    private void writeToParcel(Parcel out, boolean useBlobs) {
         final long start = SystemClock.uptimeMillis();
         out.writeInt(mFileNumbers.size() - 1);
         for(int i = 0;  i < mFileNumbers.size() - 1; i++) {
@@ -391,7 +410,12 @@
             } catch(Exception e) {
                 Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
             }
-            out.writeByteArray(raw);
+            if (useBlobs) {
+                out.writeBlob(raw);
+            } else {
+                // Avoiding blobs in the check-in file for compatibility
+                out.writeByteArray(raw);
+            }
         }
         if (DEBUG) {
             Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
@@ -399,18 +423,36 @@
     }
 
     /**
-     * This is for Settings app, when Settings app receives big history parcel, it call
-     * this method to parse it into list of parcels.
-     * Checkin file also call this method.
+     * Reads a BatteryStatsHistory from a parcel written with
+     * the {@link #writeToBatteryUsageStatsParcel} method.
+     */
+    public static BatteryStatsHistory createFromBatteryUsageStatsParcel(Parcel in) {
+        final byte[] historyBlob = in.readBlob();
+
+        Parcel historyBuffer = Parcel.obtain();
+        historyBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+
+        BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer);
+        history.readFromParcel(in, true /* useBlobs */);
+        return history;
+    }
+
+    /**
+     * This is for the check-in file, which has all history files embedded.
+     *
      * @param in the input parcel.
      */
     public void readFromParcel(Parcel in) {
+        readFromParcel(in, false /* useBlobs */);
+    }
+
+    private void readFromParcel(Parcel in, boolean useBlobs) {
         final long start = SystemClock.uptimeMillis();
         mHistoryParcels = new ArrayList<>();
         final int count = in.readInt();
         for(int i = 0; i < count; i++) {
-            byte[] temp = in.createByteArray();
-            if (temp.length == 0) {
+            byte[] temp = useBlobs ? in.readBlob() : in.createByteArray();
+            if (temp == null || temp.length == 0) {
                 continue;
             }
             Parcel p = Parcel.obtain();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 209c64a..8213c86 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -26,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -37,7 +38,6 @@
 import android.database.ContentObserver;
 import android.hardware.usb.UsbManager;
 import android.location.GnssSignalQuality;
-import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
@@ -45,6 +45,7 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Binder;
+import android.os.BluetoothBatteryStats;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBatteryPropertiesRegistrar;
@@ -60,6 +61,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.WakeLockStats;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.os.connectivity.CellularBatteryStats;
@@ -83,7 +85,6 @@
 import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
 import android.util.MutableInt;
-import android.util.Pools;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
@@ -161,7 +162,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 205;
+    static final int VERSION = 206;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -211,6 +212,26 @@
     private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
     private static final long MILLISECONDS_IN_YEAR = 365 * 24 * 3600 * 1000L;
 
+    private static final LongCounter ZERO_LONG_COUNTER = new LongCounter() {
+        @Override
+        public long getCountLocked(int which) {
+            return 0;
+        }
+
+        @Override
+        public long getCountForProcessState(int procState) {
+            return 0;
+        }
+
+        @Override
+        public void logState(Printer pw, String prefix) {
+            pw.println(prefix + "mCount=0");
+        }
+    };
+
+    private static final LongCounter[] ZERO_LONG_COUNTER_ARRAY =
+            new LongCounter[]{ZERO_LONG_COUNTER};
+
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
@@ -241,6 +262,8 @@
     private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
             MeasuredEnergyStats.POWER_BUCKET_CPU,
             MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+            MeasuredEnergyStats.POWER_BUCKET_WIFI,
+            MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
     };
 
     // TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -1143,6 +1166,79 @@
         return mKernelWakelockStats;
     }
 
+    @Override
+    public WakeLockStats getWakeLockStats() {
+        final long realtimeMs = mClock.elapsedRealtime();
+        final long realtimeUs = realtimeMs * 1000;
+        List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
+        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+            final Uid uid = mUidStats.valueAt(i);
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
+                    uid.mWakelockStats.getMap();
+            for (int j = wakelockStats.size() - 1; j >= 0; j--) {
+                final String name = wakelockStats.keyAt(j);
+                final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
+                final DualTimer timer = wakelock.mTimerPartial;
+                if (timer != null) {
+                    final long totalTimeLockHeldMs =
+                            timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
+                    if (totalTimeLockHeldMs != 0) {
+                        uidWakeLockStats.add(
+                                new WakeLockStats.WakeLock(uid.getUid(), name,
+                                        timer.getCountLocked(STATS_SINCE_CHARGED),
+                                        totalTimeLockHeldMs,
+                                        timer.isRunningLocked()
+                                                ? timer.getCurrentDurationMsLocked(realtimeMs)
+                                                : 0));
+                    }
+                }
+            }
+        }
+        return new WakeLockStats(uidWakeLockStats);
+    }
+
+    @Override
+    @GuardedBy("this")
+    public BluetoothBatteryStats getBluetoothBatteryStats() {
+        final long elapsedRealtimeUs = mClock.elapsedRealtime() * 1000;
+        ArrayList<BluetoothBatteryStats.UidStats> uidStats = new ArrayList<>();
+        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+            final Uid uid = mUidStats.valueAt(i);
+            final Timer scanTimer = uid.getBluetoothScanTimer();
+            final long scanTimeMs =
+                    scanTimer != null ? scanTimer.getTotalTimeLocked(
+                            elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
+
+            final Timer unoptimizedScanTimer = uid.getBluetoothUnoptimizedScanTimer();
+            final long unoptimizedScanTimeMs =
+                    unoptimizedScanTimer != null ? unoptimizedScanTimer.getTotalTimeLocked(
+                            elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
+
+            final Counter scanResultCounter = uid.getBluetoothScanResultCounter();
+            final int scanResultCount =
+                    scanResultCounter != null ? scanResultCounter.getCountLocked(
+                            STATS_SINCE_CHARGED) : 0;
+
+            final ControllerActivityCounter counter = uid.getBluetoothControllerActivity();
+            final long rxTimeMs =  counter != null ? counter.getRxTimeCounter().getCountLocked(
+                    STATS_SINCE_CHARGED) : 0;
+            final long txTimeMs =  counter != null ? counter.getTxTimeCounters()[0].getCountLocked(
+                    STATS_SINCE_CHARGED) : 0;
+
+            if (scanTimeMs != 0 || unoptimizedScanTimeMs != 0 || scanResultCount != 0
+                    || rxTimeMs != 0 || txTimeMs != 0) {
+                uidStats.add(new BluetoothBatteryStats.UidStats(uid.getUid(),
+                        scanTimeMs,
+                        unoptimizedScanTimeMs,
+                        scanResultCount,
+                        rxTimeMs,
+                        txTimeMs));
+            }
+        }
+
+        return new BluetoothBatteryStats(uidStats);
+    }
+
     String mLastWakeupReason = null;
     long mLastWakeupUptimeMs = 0;
     private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
@@ -1708,7 +1804,7 @@
         }
     }
 
-    private static class TimeMultiStateCounter implements TimeBaseObs {
+    private static class TimeMultiStateCounter extends LongCounter implements TimeBaseObs {
         private final TimeBase mTimeBase;
         private final LongMultiStateCounter mCounter;
 
@@ -1760,7 +1856,7 @@
         /**
          * Returns accumulated count for the specified state.
          */
-        public long getCountLocked(int procState) {
+        public long getCountForProcessState(@BatteryConsumer.ProcessState int procState) {
             return mCounter.getCount(procState);
         }
 
@@ -1768,6 +1864,12 @@
             return mCounter.getTotalCount();
         }
 
+        @Override
+        public long getCountLocked(int statsType) {
+            return getTotalCountLocked();
+        }
+
+        @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCounter=" + mCounter);
         }
@@ -1913,6 +2015,14 @@
         }
 
         @Override
+        public long getCountForProcessState(int procState) {
+            if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
+                return getCountLocked(STATS_SINCE_CHARGED);
+            }
+            return 0;
+        }
+
+        @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCount=" + mCount);
         }
@@ -3192,57 +3302,50 @@
 
     public static class ControllerActivityCounterImpl extends ControllerActivityCounter
             implements Parcelable {
-        private final LongSamplingCounter mIdleTimeMillis;
+        private final Clock mClock;
+        private final TimeBase mTimeBase;
+        private int mNumTxStates;
+        private int mProcessState;
+        private TimeMultiStateCounter mIdleTimeMillis;
         private final LongSamplingCounter mScanTimeMillis;
         private final LongSamplingCounter mSleepTimeMillis;
-        private final LongSamplingCounter mRxTimeMillis;
-        private final LongSamplingCounter[] mTxTimeMillis;
+        private TimeMultiStateCounter mRxTimeMillis;
+        private TimeMultiStateCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
         private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs;
 
-        public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
-            mIdleTimeMillis = new LongSamplingCounter(timeBase);
+        public ControllerActivityCounterImpl(Clock clock, TimeBase timeBase, int numTxStates) {
+            mClock = clock;
+            mTimeBase = timeBase;
+            mNumTxStates = numTxStates;
             mScanTimeMillis = new LongSamplingCounter(timeBase);
             mSleepTimeMillis = new LongSamplingCounter(timeBase);
-            mRxTimeMillis = new LongSamplingCounter(timeBase);
-            mTxTimeMillis = new LongSamplingCounter[numTxStates];
-            for (int i = 0; i < numTxStates; i++) {
-                mTxTimeMillis[i] = new LongSamplingCounter(timeBase);
-            }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase);
             mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase);
         }
 
-        public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
-            mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
+        public ControllerActivityCounterImpl(Clock clock, TimeBase timeBase, int numTxStates,
+                Parcel in) {
+            mClock = clock;
+            mTimeBase = timeBase;
+            mNumTxStates = numTxStates;
+            mIdleTimeMillis = readTimeMultiStateCounter(in, timeBase);
             mScanTimeMillis = new LongSamplingCounter(timeBase, in);
             mSleepTimeMillis = new LongSamplingCounter(timeBase, in);
-            mRxTimeMillis = new LongSamplingCounter(timeBase, in);
-            final int recordedTxStates = in.readInt();
-            if (recordedTxStates != numTxStates) {
-                throw new ParcelFormatException("inconsistent tx state lengths");
-            }
+            mRxTimeMillis = readTimeMultiStateCounter(in, timeBase);
+            mTxTimeMillis = readTimeMultiStateCounters(in, timeBase, numTxStates);
 
-            mTxTimeMillis = new LongSamplingCounter[numTxStates];
-            for (int i = 0; i < numTxStates; i++) {
-                mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in);
-            }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
             mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in);
         }
 
         public void readSummaryFromParcel(Parcel in) {
-            mIdleTimeMillis.readSummaryFromParcelLocked(in);
+            mIdleTimeMillis = readTimeMultiStateCounter(in, mTimeBase);
             mScanTimeMillis.readSummaryFromParcelLocked(in);
             mSleepTimeMillis.readSummaryFromParcelLocked(in);
-            mRxTimeMillis.readSummaryFromParcelLocked(in);
-            final int recordedTxStates = in.readInt();
-            if (recordedTxStates != mTxTimeMillis.length) {
-                throw new ParcelFormatException("inconsistent tx state lengths");
-            }
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.readSummaryFromParcelLocked(in);
-            }
+            mRxTimeMillis = readTimeMultiStateCounter(in, mTimeBase);
+            mTxTimeMillis = readTimeMultiStateCounters(in, mTimeBase, mNumTxStates);
+
             mPowerDrainMaMs.readSummaryFromParcelLocked(in);
             mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in);
         }
@@ -3253,52 +3356,98 @@
         }
 
         public void writeSummaryToParcel(Parcel dest) {
-            mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+            writeTimeMultiStateCounter(dest, mIdleTimeMillis);
             mScanTimeMillis.writeSummaryFromParcelLocked(dest);
             mSleepTimeMillis.writeSummaryFromParcelLocked(dest);
-            mRxTimeMillis.writeSummaryFromParcelLocked(dest);
-            dest.writeInt(mTxTimeMillis.length);
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.writeSummaryFromParcelLocked(dest);
-            }
+            writeTimeMultiStateCounter(dest, mRxTimeMillis);
+            writeTimeMultiStateCounters(dest, mTxTimeMillis);
             mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
             mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest);
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            mIdleTimeMillis.writeToParcel(dest);
+            writeTimeMultiStateCounter(dest, mIdleTimeMillis);
             mScanTimeMillis.writeToParcel(dest);
             mSleepTimeMillis.writeToParcel(dest);
-            mRxTimeMillis.writeToParcel(dest);
-            dest.writeInt(mTxTimeMillis.length);
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.writeToParcel(dest);
-            }
+            writeTimeMultiStateCounter(dest, mRxTimeMillis);
+            writeTimeMultiStateCounters(dest, mTxTimeMillis);
             mPowerDrainMaMs.writeToParcel(dest);
             mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
         }
 
+        private TimeMultiStateCounter readTimeMultiStateCounter(Parcel in, TimeBase timeBase) {
+            if (in.readBoolean()) {
+                final TimeMultiStateCounter counter =
+                        new TimeMultiStateCounter(timeBase, in, mClock.elapsedRealtime());
+                if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+                    return counter;
+                }
+            }
+            return null;
+        }
+
+        private void writeTimeMultiStateCounter(Parcel dest, TimeMultiStateCounter counter) {
+            if (counter != null) {
+                dest.writeBoolean(true);
+                counter.writeToParcel(dest);
+            } else {
+                dest.writeBoolean(false);
+            }
+        }
+
+        private TimeMultiStateCounter[] readTimeMultiStateCounters(Parcel in, TimeBase timeBase,
+                int expectedNumCounters) {
+            if (in.readBoolean()) {
+                final int numCounters = in.readInt();
+                boolean valid = (numCounters == expectedNumCounters);
+                // Need to read counters out of the Parcel, even if all or some of them are
+                // invalid.
+                TimeMultiStateCounter[] counters = new TimeMultiStateCounter[numCounters];
+                for (int i = 0; i < numCounters; i++) {
+                    final TimeMultiStateCounter counter =
+                            new TimeMultiStateCounter(timeBase, in, mClock.elapsedRealtime());
+                    if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+                        counters[i] = counter;
+                    } else {
+                        valid = false;
+                    }
+                }
+                if (valid) {
+                    return counters;
+                }
+            }
+            return null;
+        }
+
+        private void writeTimeMultiStateCounters(Parcel dest, TimeMultiStateCounter[] counters) {
+            if (counters != null) {
+                dest.writeBoolean(true);
+                dest.writeInt(counters.length);
+                for (TimeMultiStateCounter counter : counters) {
+                    counter.writeToParcel(dest);
+                }
+            } else {
+                dest.writeBoolean(false);
+            }
+        }
+
         public void reset(boolean detachIfReset, long elapsedRealtimeUs) {
-            mIdleTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
+            resetIfNotNull(mIdleTimeMillis, detachIfReset, elapsedRealtimeUs);
             mScanTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
             mSleepTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
-            mRxTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.reset(detachIfReset, elapsedRealtimeUs);
-            }
+            resetIfNotNull(mRxTimeMillis, detachIfReset, elapsedRealtimeUs);
+            resetIfNotNull(mTxTimeMillis, detachIfReset, elapsedRealtimeUs);
             mPowerDrainMaMs.reset(detachIfReset, elapsedRealtimeUs);
             mMonitoredRailChargeConsumedMaMs.reset(detachIfReset, elapsedRealtimeUs);
         }
 
         public void detach() {
-            mIdleTimeMillis.detach();
+            detachIfNotNull(mIdleTimeMillis);
             mScanTimeMillis.detach();
             mSleepTimeMillis.detach();
-            mRxTimeMillis.detach();
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.detach();
-            }
+            detachIfNotNull(mRxTimeMillis);
+            detachIfNotNull(mTxTimeMillis);
             mPowerDrainMaMs.detach();
             mMonitoredRailChargeConsumedMaMs.detach();
         }
@@ -3308,7 +3457,17 @@
          * milliseconds.
          */
         @Override
-        public LongSamplingCounter getIdleTimeCounter() {
+        public LongCounter getIdleTimeCounter() {
+            if (mIdleTimeMillis == null) {
+                return ZERO_LONG_COUNTER;
+            }
+            return mIdleTimeMillis;
+        }
+
+        private TimeMultiStateCounter getOrCreateIdleTimeCounter() {
+            if (mIdleTimeMillis == null) {
+                mIdleTimeMillis = createTimeMultiStateCounter();
+            }
             return mIdleTimeMillis;
         }
 
@@ -3335,7 +3494,17 @@
          * milliseconds.
          */
         @Override
-        public LongSamplingCounter getRxTimeCounter() {
+        public LongCounter getRxTimeCounter() {
+            if (mRxTimeMillis == null) {
+                return ZERO_LONG_COUNTER;
+            }
+            return mRxTimeMillis;
+        }
+
+        private TimeMultiStateCounter getOrCreateRxTimeCounter() {
+            if (mRxTimeMillis == null) {
+                mRxTimeMillis = createTimeMultiStateCounter();
+            }
             return mRxTimeMillis;
         }
 
@@ -3344,10 +3513,33 @@
          * milliseconds.
          */
         @Override
-        public LongSamplingCounter[] getTxTimeCounters() {
+        public LongCounter[] getTxTimeCounters() {
+            if (mTxTimeMillis == null) {
+                return ZERO_LONG_COUNTER_ARRAY;
+            }
             return mTxTimeMillis;
         }
 
+        private TimeMultiStateCounter[] getOrCreateTxTimeCounters() {
+            if (mTxTimeMillis == null) {
+                mTxTimeMillis = new TimeMultiStateCounter[mNumTxStates];
+                for (int i = 0; i < mNumTxStates; i++) {
+                    mTxTimeMillis[i] = createTimeMultiStateCounter();
+                }
+            }
+            return mTxTimeMillis;
+        }
+
+        private TimeMultiStateCounter createTimeMultiStateCounter() {
+            final long timestampMs = mClock.elapsedRealtime();
+            TimeMultiStateCounter counter = new TimeMultiStateCounter(mTimeBase,
+                    BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
+            counter.setState(mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+                    timestampMs);
+            counter.update(0, timestampMs);
+            return counter;
+        }
+
         /**
          * @return a LongSamplingCounter, measuring power use in milli-ampere milliseconds (mAmS).
          */
@@ -3364,6 +3556,21 @@
         public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() {
             return mMonitoredRailChargeConsumedMaMs;
         }
+
+        private void setState(int processState, long elapsedTimeMs) {
+            mProcessState = processState;
+            if (mIdleTimeMillis != null) {
+                mIdleTimeMillis.setState(processState, elapsedTimeMs);
+            }
+            if (mRxTimeMillis != null) {
+                mRxTimeMillis.setState(processState, elapsedTimeMs);
+            }
+            if (mTxTimeMillis != null) {
+                for (int i = 0; i < mTxTimeMillis.length; i++) {
+                    mTxTimeMillis[i].setState(processState, elapsedTimeMs);
+                }
+            }
+        }
     }
 
     /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
@@ -8200,6 +8407,16 @@
                     mapUidProcessStateToBatteryConsumerProcessState(procState);
             getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
             getMobileRadioActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
+            final ControllerActivityCounterImpl wifiControllerActivity =
+                    getWifiControllerActivity();
+            if (wifiControllerActivity != null) {
+                wifiControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
+            }
+            final ControllerActivityCounterImpl bluetoothControllerActivity =
+                    getBluetoothControllerActivity();
+            if (bluetoothControllerActivity != null) {
+                bluetoothControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
+            }
             final MeasuredEnergyStats energyStats =
                     getOrCreateMeasuredEnergyStatsIfSupportedLocked();
             if (energyStats != null) {
@@ -8237,7 +8454,7 @@
 
             long activeTime = 0;
             for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
-                activeTime += mCpuActiveTimeMs.getCountLocked(procState);
+                activeTime += mCpuActiveTimeMs.getCountForProcessState(procState);
             }
             return activeTime;
         }
@@ -8249,7 +8466,7 @@
                 return 0;
             }
 
-            return mCpuActiveTimeMs.getCountLocked(procState);
+            return mCpuActiveTimeMs.getCountForProcessState(procState);
         }
 
         @Override
@@ -8542,12 +8759,12 @@
         }
 
         @Override
-        public ControllerActivityCounter getWifiControllerActivity() {
+        public ControllerActivityCounterImpl getWifiControllerActivity() {
             return mWifiControllerActivity;
         }
 
         @Override
-        public ControllerActivityCounter getBluetoothControllerActivity() {
+        public ControllerActivityCounterImpl getBluetoothControllerActivity() {
             return mBluetoothControllerActivity;
         }
 
@@ -8558,24 +8775,24 @@
 
         public ControllerActivityCounterImpl getOrCreateWifiControllerActivityLocked() {
             if (mWifiControllerActivity == null) {
-                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_BT_TX_LEVELS);
+                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
             }
             return mWifiControllerActivity;
         }
 
         public ControllerActivityCounterImpl getOrCreateBluetoothControllerActivityLocked() {
             if (mBluetoothControllerActivity == null) {
-                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_BT_TX_LEVELS);
+                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_BT_TX_LEVELS);
             }
             return mBluetoothControllerActivity;
         }
 
         public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
             if (mModemControllerActivity == null) {
-                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.getNumTxPowerLevels());
+                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels());
             }
             return mModemControllerActivity;
         }
@@ -8668,6 +8885,14 @@
 
         @GuardedBy("mBsi")
         @Override
+        public long getBluetoothMeasuredBatteryConsumptionUC(
+                @BatteryConsumer.ProcessState int processState) {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+                    processState);
+        }
+
+        @GuardedBy("mBsi")
+        @Override
         public long getCpuMeasuredBatteryConsumptionUC() {
             return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
         }
@@ -8711,6 +8936,13 @@
             return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
         }
 
+        @GuardedBy("mBsi")
+        @Override
+        public long getWifiMeasuredBatteryConsumptionUC(int processState) {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI,
+                    processState);
+        }
+
         /**
          * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
          * since last marked. Also sets the mark time for both these timers.
@@ -9307,7 +9539,7 @@
             if (processState == BatteryConsumer.PROCESS_STATE_ANY) {
                 return mMobileRadioActiveTime.getTotalCountLocked();
             } else {
-                return mMobileRadioActiveTime.getCountLocked(processState);
+                return mMobileRadioActiveTime.getCountForProcessState(processState);
             }
         }
 
@@ -10257,22 +10489,22 @@
             }
 
             if (in.readInt() != 0) {
-                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_WIFI_TX_LEVELS, in);
+                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS, in);
             } else {
                 mWifiControllerActivity = null;
             }
 
             if (in.readInt() != 0) {
-                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_BT_TX_LEVELS, in);
+                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_BT_TX_LEVELS, in);
             } else {
                 mBluetoothControllerActivity = null;
             }
 
             if (in.readInt() != 0) {
-                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.getNumTxPowerLevels(), in);
+                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels(), in);
             } else {
                 mModemControllerActivity = null;
             }
@@ -11239,6 +11471,20 @@
 
                 getMobileRadioActiveTimeCounter()
                         .setState(batteryConsumerProcessState, elapsedRealtimeMs);
+
+                final ControllerActivityCounterImpl wifiControllerActivity =
+                        getWifiControllerActivity();
+                if (wifiControllerActivity != null) {
+                    wifiControllerActivity.setState(batteryConsumerProcessState, elapsedRealtimeMs);
+                }
+
+                final ControllerActivityCounterImpl bluetoothControllerActivity =
+                        getBluetoothControllerActivity();
+                if (bluetoothControllerActivity != null) {
+                    bluetoothControllerActivity.setState(batteryConsumerProcessState,
+                            elapsedRealtimeMs);
+                }
+
                 final MeasuredEnergyStats energyStats =
                         getOrCreateMeasuredEnergyStatsIfSupportedLocked();
                 if (energyStats != null) {
@@ -11647,10 +11893,11 @@
             mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
             mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
         }
-        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
-        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mWifiActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
+                NUM_WIFI_TX_LEVELS);
+        mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS);
-        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 ModemActivityInfo.getNumTxPowerLevels());
         mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
@@ -12483,8 +12730,6 @@
         }
     }
 
-    private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6);
-
     private final Object mWifiNetworkLock = new Object();
 
     @GuardedBy("mWifiNetworkLock")
@@ -12502,21 +12747,15 @@
     private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
 
     @VisibleForTesting
-    protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
-        try {
-            if (!ArrayUtils.isEmpty(ifaces)) {
-                INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
-                        ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-                if (statsService != null) {
-                    return statsService.getDetailedUidStats(ifaces);
-                } else {
-                    Slog.e(TAG, "Failed to get networkStatsService ");
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e);
-        }
-        return null;
+    protected NetworkStats readMobileNetworkStatsLocked(
+            @NonNull NetworkStatsManager networkStatsManager) {
+        return networkStatsManager.getMobileUidStats();
+    }
+
+    @VisibleForTesting
+    protected NetworkStats readWifiNetworkStatsLocked(
+            @NonNull NetworkStatsManager networkStatsManager) {
+        return networkStatsManager.getWifiUidStats();
     }
 
     /**
@@ -12525,7 +12764,8 @@
      */
     @GuardedBy("this")
     public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
-            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+            @NonNull NetworkStatsManager networkStatsManager) {
         if (DEBUG_ENERGY) {
             synchronized (mWifiNetworkLock) {
                 Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
@@ -12535,20 +12775,15 @@
         // Grab a separate lock to acquire the network stats, which may do I/O.
         NetworkStats delta = null;
         synchronized (mWifiNetworkLock) {
-            final NetworkStats latestStats = readNetworkStatsLocked(mWifiIfaces);
+            final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager);
             if (latestStats != null) {
-                delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null,
-                        mNetworkStatsPool.acquire());
-                mNetworkStatsPool.release(mLastWifiNetworkStats);
+                delta = latestStats.subtract(mLastWifiNetworkStats);
                 mLastWifiNetworkStats = latestStats;
             }
         }
 
         synchronized (this) {
             if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
-                if (delta != null) {
-                    mNetworkStatsPool.release(delta);
-                }
                 if (mIgnoreNextExternalStats) {
                     // TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the
                     //  global one) here like we do for display. But I'm not sure it's worth the
@@ -12566,64 +12801,63 @@
 
             SparseLongArray rxPackets = new SparseLongArray();
             SparseLongArray txPackets = new SparseLongArray();
+            SparseLongArray rxTimesMs = new SparseLongArray();
+            SparseLongArray txTimesMs = new SparseLongArray();
             long totalTxPackets = 0;
             long totalRxPackets = 0;
             if (delta != null) {
-                NetworkStats.Entry entry = new NetworkStats.Entry();
-                final int size = delta.size();
-                for (int i = 0; i < size; i++) {
-                    entry = delta.getValues(i, entry);
-
+                for (NetworkStats.Entry entry : delta) {
                     if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
-                                + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
-                                + " txPackets=" + entry.txPackets);
+                        Slog.d(TAG, "Wifi uid " + entry.getUid()
+                                + ": delta rx=" + entry.getRxBytes()
+                                + " tx=" + entry.getTxBytes()
+                                + " rxPackets=" + entry.getRxPackets()
+                                + " txPackets=" + entry.getTxPackets());
                     }
 
-                    if (entry.rxBytes == 0 && entry.txBytes == 0) {
+                    if (entry.getRxBytes() == 0 && entry.getTxBytes() == 0) {
                         // Skip the lookup below since there is no work to do.
                         continue;
                     }
 
-                    final Uid u = getUidStatsLocked(mapUid(entry.uid), elapsedRealtimeMs, uptimeMs);
-                    if (entry.rxBytes != 0) {
-                        u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
-                                entry.rxPackets);
-                        if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
-                            u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.rxBytes,
-                                    entry.rxPackets);
+                    final int uid = mapUid(entry.getUid());
+                    final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
+                    if (entry.getRxBytes() != 0) {
+                        u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.getRxBytes(),
+                                entry.getRxPackets());
+                        if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
+                            u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.getRxBytes(),
+                                    entry.getRxPackets());
                         }
                         mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
-                                entry.rxBytes);
+                                entry.getRxBytes());
                         mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
-                                entry.rxPackets);
+                                entry.getRxPackets());
 
-                        // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
-                        rxPackets.put(u.getUid(), entry.rxPackets);
+                        rxPackets.incrementValue(uid, entry.getRxPackets());
 
                         // Sum the total number of packets so that the Rx Power can
                         // be evenly distributed amongst the apps.
-                        totalRxPackets += entry.rxPackets;
+                        totalRxPackets += entry.getRxPackets();
                     }
 
-                    if (entry.txBytes != 0) {
-                        u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
-                                entry.txPackets);
-                        if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
-                            u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.txBytes,
-                                    entry.txPackets);
+                    if (entry.getTxBytes() != 0) {
+                        u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.getTxBytes(),
+                                entry.getTxPackets());
+                        if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
+                            u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.getTxBytes(),
+                                    entry.getTxPackets());
                         }
                         mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
-                                entry.txBytes);
+                                entry.getTxBytes());
                         mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
-                                entry.txPackets);
+                                entry.getTxPackets());
 
-                        // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
-                        txPackets.put(u.getUid(), entry.txPackets);
+                        txPackets.incrementValue(uid, entry.getTxPackets());
 
                         // Sum the total number of packets so that the Tx Power can
                         // be evenly distributed amongst the apps.
-                        totalTxPackets += entry.txPackets;
+                        totalTxPackets += entry.getTxPackets();
                     }
 
                     // Calculate consumed energy for this uid. Only do so if WifiReporting isn't
@@ -12649,13 +12883,12 @@
                             }
                         }
 
-                        uidEstimatedConsumptionMah.add(u.getUid(),
+                        uidEstimatedConsumptionMah.incrementValue(u.getUid(),
                                 mWifiPowerCalculator.calcPowerWithoutControllerDataMah(
-                                        entry.rxPackets, entry.txPackets,
+                                        entry.getRxPackets(), entry.getTxPackets(),
                                         uidRunningMs, uidScanMs, uidBatchScanMs));
                     }
                 }
-                mNetworkStatsPool.release(delta);
                 delta = null;
             }
 
@@ -12744,11 +12977,9 @@
                                     + scanTxTimeSinceMarkMs + " ms)");
                         }
 
-                        ControllerActivityCounterImpl activityCounter =
-                                uid.getOrCreateWifiControllerActivityLocked();
-                        activityCounter.getRxTimeCounter().addCountLocked(scanRxTimeSinceMarkMs);
-                        activityCounter.getTxTimeCounters()[0].addCountLocked(
-                                scanTxTimeSinceMarkMs);
+                        rxTimesMs.incrementValue(uid.getUid(), scanRxTimeSinceMarkMs);
+                        txTimesMs.incrementValue(uid.getUid(), scanTxTimeSinceMarkMs);
+
                         leftOverRxTimeMs -= scanRxTimeSinceMarkMs;
                         leftOverTxTimeMs -= scanTxTimeSinceMarkMs;
                     }
@@ -12768,14 +12999,14 @@
                             Slog.d(TAG, "  IdleTime for UID " + uid.getUid() + ": "
                                     + myIdleTimeMs + " ms");
                         }
-                        uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter()
-                                .addCountLocked(myIdleTimeMs);
+                        uid.getOrCreateWifiControllerActivityLocked().getOrCreateIdleTimeCounter()
+                                .increment(myIdleTimeMs, elapsedRealtimeMs);
                     }
 
                     if (uidEstimatedConsumptionMah != null) {
                         double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah(
                                 scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs);
-                        uidEstimatedConsumptionMah.add(uid.getUid(), uidEstMah);
+                        uidEstimatedConsumptionMah.incrementValue(uid.getUid(), uidEstMah);
                     }
                 }
 
@@ -12787,36 +13018,51 @@
                 // Distribute the remaining Tx power appropriately between all apps that transmitted
                 // packets.
                 for (int i = 0; i < txPackets.size(); i++) {
-                    final Uid uid = getUidStatsLocked(txPackets.keyAt(i),
-                            elapsedRealtimeMs, uptimeMs);
+                    final int uid = txPackets.keyAt(i);
                     final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
                             / totalTxPackets;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "  TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
-                    }
-                    uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0]
-                            .addCountLocked(myTxTimeMs);
-                    if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.add(uid.getUid(),
-                                mWifiPowerCalculator.calcPowerFromControllerDataMah(
-                                        0, myTxTimeMs, 0));
-                    }
+                    txTimesMs.incrementValue(uid, myTxTimeMs);
                 }
 
                 // Distribute the remaining Rx power appropriately between all apps that received
                 // packets.
                 for (int i = 0; i < rxPackets.size(); i++) {
-                    final Uid uid = getUidStatsLocked(rxPackets.keyAt(i),
-                            elapsedRealtimeMs, uptimeMs);
+                    final int uid = rxPackets.keyAt(i);
                     final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
                             / totalRxPackets;
+                    rxTimesMs.incrementValue(uid, myRxTimeMs);
+                }
+
+                for (int i = 0; i < txTimesMs.size(); i++) {
+                    final int uid = txTimesMs.keyAt(i);
+                    final long myTxTimeMs = txTimesMs.valueAt(i);
                     if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "  RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
+                        Slog.d(TAG, "  TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
                     }
-                    uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter()
-                            .addCountLocked(myRxTimeMs);
+                    getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                            .getOrCreateWifiControllerActivityLocked()
+                            .getOrCreateTxTimeCounters()[0]
+                            .increment(myTxTimeMs, elapsedRealtimeMs);
                     if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.add(uid.getUid(),
+                        uidEstimatedConsumptionMah.incrementValue(uid,
+                                mWifiPowerCalculator.calcPowerFromControllerDataMah(
+                                        0, myTxTimeMs, 0));
+                    }
+                }
+
+                for (int i = 0; i < rxTimesMs.size(); i++) {
+                    final int uid = rxTimesMs.keyAt(i);
+                    final long myRxTimeMs = rxTimesMs.valueAt(i);
+                    if (DEBUG_ENERGY) {
+                        Slog.d(TAG, "  RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
+                    }
+
+                    getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
+                            .getOrCreateWifiControllerActivityLocked()
+                            .getOrCreateRxTimeCounter()
+                            .increment(myRxTimeMs, elapsedRealtimeMs);
+                    if (uidEstimatedConsumptionMah != null) {
+                        uidEstimatedConsumptionMah.incrementValue(uid,
                                 mWifiPowerCalculator.calcPowerFromControllerDataMah(
                                         myRxTimeMs, 0, 0));
                     }
@@ -12824,16 +13070,15 @@
 
                 // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
 
-
                 // Update WiFi controller stats.
-                mWifiActivity.getRxTimeCounter().addCountLocked(
-                        info.getControllerRxDurationMillis());
-                mWifiActivity.getTxTimeCounters()[0].addCountLocked(
-                        info.getControllerTxDurationMillis());
+                mWifiActivity.getOrCreateRxTimeCounter().increment(
+                        info.getControllerRxDurationMillis(), elapsedRealtimeMs);
+                mWifiActivity.getOrCreateTxTimeCounters()[0].increment(
+                        info.getControllerTxDurationMillis(), elapsedRealtimeMs);
                 mWifiActivity.getScanTimeCounter().addCountLocked(
                         info.getControllerScanDurationMillis());
-                mWifiActivity.getIdleTimeCounter().addCountLocked(
-                        info.getControllerIdleDurationMillis());
+                mWifiActivity.getOrCreateIdleTimeCounter().increment(
+                        info.getControllerIdleDurationMillis(), elapsedRealtimeMs);
 
                 // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
                 final double opVolt = mPowerProfile.getAveragePower(
@@ -12888,7 +13133,8 @@
      * Distribute Cell radio energy info and network traffic to apps.
      */
     public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
-            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+            @NonNull NetworkStatsManager networkStatsManager) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
@@ -12902,20 +13148,15 @@
         // Grab a separate lock to acquire the network stats, which may do I/O.
         NetworkStats delta = null;
         synchronized (mModemNetworkLock) {
-            final NetworkStats latestStats = readNetworkStatsLocked(mModemIfaces);
+            final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
             if (latestStats != null) {
-                delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null,
-                        mNetworkStatsPool.acquire());
-                mNetworkStatsPool.release(mLastModemNetworkStats);
+                delta = latestStats.subtract(mLastModemNetworkStats);
                 mLastModemNetworkStats = latestStats;
             }
         }
 
         synchronized (this) {
             if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
-                if (delta != null) {
-                    mNetworkStatsPool.release(delta);
-                }
                 return;
             }
 
@@ -12931,14 +13172,16 @@
 
             if (deltaInfo != null) {
                 mHasModemReporting = true;
-                mModemActivity.getIdleTimeCounter().addCountLocked(
-                        deltaInfo.getIdleTimeMillis());
+                mModemActivity.getOrCreateIdleTimeCounter()
+                        .increment(deltaInfo.getIdleTimeMillis(), elapsedRealtimeMs);
                 mModemActivity.getSleepTimeCounter().addCountLocked(
                         deltaInfo.getSleepTimeMillis());
-                mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
+                mModemActivity.getOrCreateRxTimeCounter()
+                        .increment(deltaInfo.getReceiveTimeMillis(), elapsedRealtimeMs);
                 for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
-                    mModemActivity.getTxTimeCounters()[lvl]
-                        .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl));
+                    mModemActivity.getOrCreateTxTimeCounters()[lvl]
+                            .increment(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl),
+                                    elapsedRealtimeMs);
                 }
 
                 // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -13040,7 +13283,7 @@
                         // Distribute measured mobile radio charge consumption based on app radio
                         // active time
                         if (uidEstimatedConsumptionMah != null) {
-                            uidEstimatedConsumptionMah.add(u.getUid(),
+                            uidEstimatedConsumptionMah.incrementValue(u.getUid(),
                                     mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
                                             appRadioTimeUs / 1000));
                         }
@@ -13056,7 +13299,8 @@
                             if (totalRxPackets > 0 && entry.rxPackets > 0) {
                                 final long rxMs = (entry.rxPackets
                                     * deltaInfo.getReceiveTimeMillis()) / totalRxPackets;
-                                activityCounter.getRxTimeCounter().addCountLocked(rxMs);
+                                activityCounter.getOrCreateRxTimeCounter()
+                                        .increment(rxMs, elapsedRealtimeMs);
                             }
 
                             if (totalTxPackets > 0 && entry.txPackets > 0) {
@@ -13065,7 +13309,8 @@
                                     long txMs = entry.txPackets
                                             * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
                                     txMs /= totalTxPackets;
-                                    activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
+                                    activityCounter.getOrCreateTxTimeCounters()[lvl]
+                                            .increment(txMs, elapsedRealtimeMs);
                                 }
                             }
                         }
@@ -13117,7 +13362,6 @@
                             totalEstimatedConsumptionMah, elapsedRealtimeMs);
                 }
 
-                mNetworkStatsPool.release(delta);
                 delta = null;
             }
         }
@@ -13163,8 +13407,8 @@
             energy = info.getControllerEnergyUsed();
             if (!info.getUidTraffic().isEmpty()) {
                 for (UidTraffic traffic : info.getUidTraffic()) {
-                    uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
-                    uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
+                    uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes());
+                    uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes());
                 }
             }
         }
@@ -13256,6 +13500,9 @@
         long leftOverRxTimeMs = rxTimeMs;
         long leftOverTxTimeMs = txTimeMs;
 
+        final SparseLongArray rxTimesMs = new SparseLongArray(uidCount);
+        final SparseLongArray txTimesMs = new SparseLongArray(uidCount);
+
         for (int i = 0; i < uidCount; i++) {
             final Uid u = mUidStats.valueAt(i);
             if (u.mBluetoothScanTimer == null) {
@@ -13285,13 +13532,11 @@
                     scanTimeTxSinceMarkMs = (txTimeMs * scanTimeTxSinceMarkMs) / totalScanTimeMs;
                 }
 
-                final ControllerActivityCounterImpl counter =
-                        u.getOrCreateBluetoothControllerActivityLocked();
-                counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs);
-                counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs);
+                rxTimesMs.incrementValue(u.getUid(), scanTimeRxSinceMarkMs);
+                txTimesMs.incrementValue(u.getUid(), scanTimeTxSinceMarkMs);
 
                 if (uidEstimatedConsumptionMah != null) {
-                    uidEstimatedConsumptionMah.add(u.getUid(),
+                    uidEstimatedConsumptionMah.incrementValue(u.getUid(),
                             mBluetoothPowerCalculator.calculatePowerMah(
                                     scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0));
                 }
@@ -13352,35 +13597,52 @@
 
                 if (totalRxBytes > 0 && rxBytes > 0) {
                     final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
-                    }
-                    counter.getRxTimeCounter().addCountLocked(timeRxMs);
-
-                    if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.add(u.getUid(),
-                                mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0));
-                    }
+                    rxTimesMs.incrementValue(uid, timeRxMs);
                 }
 
                 if (totalTxBytes > 0 && txBytes > 0) {
                     final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
-                    }
-                    counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
+                    txTimesMs.incrementValue(uid, timeTxMs);
+                }
+            }
 
-                    if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.add(u.getUid(),
-                                mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0));
-                    }
+            for (int i = 0; i < txTimesMs.size(); i++) {
+                final int uid = txTimesMs.keyAt(i);
+                final long myTxTimeMs = txTimesMs.valueAt(i);
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "  TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
+                }
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .getOrCreateBluetoothControllerActivityLocked()
+                        .getOrCreateTxTimeCounters()[0]
+                        .increment(myTxTimeMs, elapsedRealtimeMs);
+                if (uidEstimatedConsumptionMah != null) {
+                    uidEstimatedConsumptionMah.incrementValue(uid,
+                            mBluetoothPowerCalculator.calculatePowerMah(0, myTxTimeMs, 0));
+                }
+            }
+
+            for (int i = 0; i < rxTimesMs.size(); i++) {
+                final int uid = rxTimesMs.keyAt(i);
+                final long myRxTimeMs = rxTimesMs.valueAt(i);
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "  RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
+                }
+
+                getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
+                        .getOrCreateBluetoothControllerActivityLocked()
+                        .getOrCreateRxTimeCounter()
+                        .increment(myRxTimeMs, elapsedRealtimeMs);
+                if (uidEstimatedConsumptionMah != null) {
+                    uidEstimatedConsumptionMah.incrementValue(uid,
+                            mBluetoothPowerCalculator.calculatePowerMah(myRxTimeMs, 0, 0));
                 }
             }
         }
 
-        mBluetoothActivity.getRxTimeCounter().addCountLocked(rxTimeMs);
-        mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(txTimeMs);
-        mBluetoothActivity.getIdleTimeCounter().addCountLocked(idleTimeMs);
+        mBluetoothActivity.getOrCreateRxTimeCounter().increment(rxTimeMs, elapsedRealtimeMs);
+        mBluetoothActivity.getOrCreateTxTimeCounters()[0].increment(txTimeMs, elapsedRealtimeMs);
+        mBluetoothActivity.getOrCreateIdleTimeCounter().increment(idleTimeMs, elapsedRealtimeMs);
 
         // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
         final double opVolt = mPowerProfile.getAveragePower(
@@ -15994,6 +16256,18 @@
         iPw.decreaseIndent();
     }
 
+    /**
+     * Dump Power Profile
+     */
+    @GuardedBy("this")
+    public void dumpPowerProfileLocked(PrintWriter pw) {
+        final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, "    ");
+        iPw.printf("Power Profile: \n");
+        iPw.increaseIndent();
+        mPowerProfile.dump(iPw);
+        iPw.decreaseIndent();
+    }
+
     final ReentrantLock mWriteLock = new ReentrantLock();
 
     @GuardedBy("this")
@@ -16258,6 +16532,7 @@
     @GuardedBy("this")
     public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
         final int version = in.readInt();
+
         if (version != VERSION) {
             Slog.w("BatteryStats", "readFromParcel: version got " + version
                 + ", expected " + VERSION + "; erasing old stats");
@@ -17417,15 +17692,15 @@
         }
         mWifiActiveTimer = new StopwatchTimer(mClock, null, -900, null,
             mOnBatteryTimeBase, in);
-        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mWifiActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_WIFI_TX_LEVELS, in);
         for (int i=0; i<mGpsSignalQualityTimer.length; i++) {
             mGpsSignalQualityTimer[i] = new StopwatchTimer(mClock, null, -1000 - i,
                 null, mOnBatteryTimeBase, in);
         }
-        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS, in);
-        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 ModemActivityInfo.getNumTxPowerLevels(), in);
         mHasWifiReporting = in.readInt() != 0;
         mHasBluetoothReporting = in.readInt() != 0;
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index c1111ec..69b7b4e 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -30,6 +30,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -200,7 +201,13 @@
             Parcel historyBuffer = Parcel.obtain();
             historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
                     batteryStatsImpl.mHistoryBuffer.dataSize());
-            batteryUsageStatsBuilder.setBatteryHistory(historyBuffer);
+
+            final File systemDir =
+                    batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile();
+            final BatteryStatsHistory batteryStatsHistory =
+                    new BatteryStatsHistory(batteryStatsImpl, systemDir, historyBuffer);
+
+            batteryUsageStatsBuilder.setBatteryHistory(batteryStatsHistory);
         }
 
         return batteryUsageStatsBuilder.build();
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
index fd54b32..af82f40 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsStore.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -58,6 +58,7 @@
             new BatteryUsageStatsQuery.Builder()
                     .setMaxStatsAgeMs(0)
                     .includePowerModels()
+                    .includeProcessStateData()
                     .build());
     private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
     private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 20cf102..e9d55db 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -181,7 +180,7 @@
         }
 
         public Handler getHandler() {
-            return new Handler(Looper.getMainLooper());
+            return BackgroundThread.getHandler();
         }
     }
 
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index c322258..20535d2 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -15,6 +15,7 @@
  */
 package com.android.internal.os;
 
+import android.annotation.Nullable;
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
 import android.os.BatteryStats.ControllerActivityCounter;
@@ -26,19 +27,33 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import java.util.Arrays;
 import java.util.List;
 
 public class BluetoothPowerCalculator extends PowerCalculator {
     private static final String TAG = "BluetoothPowerCalc";
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+
+    private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
     private final double mIdleMa;
     private final double mRxMa;
     private final double mTxMa;
     private final boolean mHasBluetoothPowerController;
 
     private static class PowerAndDuration {
+        // Return value of BT duration per app
         public long durationMs;
+        // Return value of BT power per app
         public double powerMah;
+
+        public BatteryConsumer.Key[] keys;
+        public double[] powerPerKeyMah;
+
+        // Aggregated BT duration across all apps
+        public long totalDurationMs;
+        // Aggregated BT power across all apps
+        public double totalPowerMah;
     }
 
     public BluetoothPowerCalculator(PowerProfile profile) {
@@ -55,59 +70,88 @@
             return;
         }
 
-        final PowerAndDuration total = new PowerAndDuration();
+        BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
+        final PowerAndDuration powerAndDuration = new PowerAndDuration();
 
         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
-            calculateApp(app, total, query);
+            if (keys == UNINITIALIZED_KEYS) {
+                if (query.isProcessStateDataNeeded()) {
+                    keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
+                    powerAndDuration.keys = keys;
+                    powerAndDuration.powerPerKeyMah = new double[keys.length];
+                } else {
+                    keys = null;
+                }
+            }
+            calculateApp(app, powerAndDuration, query);
         }
 
         final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
         final int powerModel = getPowerModel(measuredChargeUC, query);
         final ControllerActivityCounter activityCounter =
                 batteryStats.getBluetoothControllerActivity();
-        final long systemDurationMs = calculateDuration(activityCounter);
-        final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC,
-                activityCounter, query.shouldForceUsePowerProfileModel());
+        calculatePowerAndDuration(null, powerModel, measuredChargeUC,
+                activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
 
         // Subtract what the apps used, but clamp to 0.
-        final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
+        final long systemComponentDurationMs = Math.max(0,
+                powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
         if (DEBUG) {
             Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
-                    + " power=" + formatCharge(systemPowerMah));
+                    + " power=" + formatCharge(powerAndDuration.powerMah));
         }
 
         builder.getAggregateBatteryConsumerBuilder(
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
-                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemDurationMs)
+                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                        powerAndDuration.durationMs)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
-                        Math.max(systemPowerMah, total.powerMah), powerModel);
+                        Math.max(powerAndDuration.powerMah, powerAndDuration.totalPowerMah),
+                        powerModel);
 
         builder.getAggregateBatteryConsumerBuilder(
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
-                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.durationMs)
-                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.powerMah,
+                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                        powerAndDuration.totalDurationMs)
+                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                        powerAndDuration.totalPowerMah,
                         powerModel);
     }
 
-    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
+    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration,
             BatteryUsageStatsQuery query) {
         final long measuredChargeUC =
                 app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
         final int powerModel = getPowerModel(measuredChargeUC, query);
         final ControllerActivityCounter activityCounter =
                 app.getBatteryStatsUid().getBluetoothControllerActivity();
-        final long durationMs = calculateDuration(activityCounter);
-        final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
-                query.shouldForceUsePowerProfileModel());
+        calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, measuredChargeUC,
+                activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
 
-        app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs)
-                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel);
+        app.setUsageDurationMillis(
+                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.durationMs)
+                .setConsumedPower(
+                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.powerMah,
+                        powerModel);
 
-        total.durationMs += durationMs;
-        total.powerMah += powerMah;
+        powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
+        powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
+
+        if (query.isProcessStateDataNeeded() && powerAndDuration.keys != null) {
+            for (int j = 0; j < powerAndDuration.keys.length; j++) {
+                BatteryConsumer.Key key = powerAndDuration.keys[j];
+                final int processState = key.processState;
+                if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                    // Already populated with the powerAndDuration across all process states
+                    continue;
+                }
+
+                app.setConsumedPower(key, powerAndDuration.powerPerKeyMah[j], powerModel);
+            }
+        }
     }
 
     @Override
@@ -117,12 +161,12 @@
             return;
         }
 
-        PowerAndDuration total = new PowerAndDuration();
+        PowerAndDuration powerAndDuration = new PowerAndDuration();
 
         for (int i = sippers.size() - 1; i >= 0; i--) {
             final BatterySipper app = sippers.get(i);
             if (app.drainType == BatterySipper.DrainType.APP) {
-                calculateApp(app, app.uidObj, statsType, total);
+                calculateApp(app, app.uidObj, statsType, powerAndDuration);
             }
         }
 
@@ -131,13 +175,14 @@
         final int powerModel = getPowerModel(measuredChargeUC);
         final ControllerActivityCounter activityCounter =
                 batteryStats.getBluetoothControllerActivity();
-        final long systemDurationMs = calculateDuration(activityCounter);
-        final double systemPowerMah =
-                calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false);
+        calculatePowerAndDuration(null, powerModel, measuredChargeUC, activityCounter, false,
+                powerAndDuration);
 
         // Subtract what the apps used, but clamp to 0.
-        final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
-        final long durationMs = Math.max(0, systemDurationMs - total.durationMs);
+        final double powerMah = Math.max(0,
+                powerAndDuration.powerMah - powerAndDuration.totalPowerMah);
+        final long durationMs = Math.max(0,
+                powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
         if (DEBUG && powerMah != 0) {
             Log.d(TAG, "Bluetooth active: time=" + (durationMs)
                     + " power=" + formatCharge(powerMah));
@@ -160,65 +205,102 @@
     }
 
     private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
-            PowerAndDuration total) {
-
+            PowerAndDuration powerAndDuration) {
         final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
         final int powerModel = getPowerModel(measuredChargeUC);
         final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
-        final long durationMs = calculateDuration(activityCounter);
-        final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
-                false);
+        calculatePowerAndDuration(u, powerModel, measuredChargeUC, activityCounter,
+                false, powerAndDuration);
 
-        app.bluetoothRunningTimeMs = durationMs;
-        app.bluetoothPowerMah = powerMah;
+        app.bluetoothRunningTimeMs = powerAndDuration.durationMs;
+        app.bluetoothPowerMah = powerAndDuration.powerMah;
         app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType);
         app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType);
 
-        total.durationMs += durationMs;
-        total.powerMah += powerMah;
-    }
-
-    private long calculateDuration(ControllerActivityCounter counter) {
-        if (counter == null) {
-            return 0;
-        }
-
-        return counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
-                + counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
-                + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+        powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
+        powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
     }
 
     /** Returns bluetooth power usage based on the best data available. */
-    private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel,
-            long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) {
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-            return uCtoMah(measuredChargeUC);
-        }
-
+    private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid,
+            @BatteryConsumer.PowerModel int powerModel,
+            long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower,
+            PowerAndDuration powerAndDuration) {
         if (counter == null) {
-            return 0;
+            powerAndDuration.durationMs = 0;
+            powerAndDuration.powerMah = 0;
+            if (powerAndDuration.powerPerKeyMah != null) {
+                Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
+            }
+            return;
         }
 
-        if (!ignoreReportedPower) {
-            final double powerMah =
-                    counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
-                            / (double) (1000 * 60 * 60);
-            if (powerMah != 0) {
-                return powerMah;
+        final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
+        final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
+        final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
+        final long idleTimeMs = idleTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+        final long rxTimeMs = rxTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+        final long txTimeMs = txTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+
+        powerAndDuration.durationMs = idleTimeMs + rxTimeMs + txTimeMs;
+
+        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+            powerAndDuration.powerMah = uCtoMah(measuredChargeUC);
+            if (uid != null && powerAndDuration.keys != null) {
+                for (int i = 0; i < powerAndDuration.keys.length; i++) {
+                    BatteryConsumer.Key key = powerAndDuration.keys[i];
+                    final int processState = key.processState;
+                    if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                        // Already populated with the powerAndDuration across all process states
+                        continue;
+                    }
+
+                    powerAndDuration.powerPerKeyMah[i] =
+                            uCtoMah(uid.getBluetoothMeasuredBatteryConsumptionUC(processState));
+                }
+            }
+        } else {
+            if (!ignoreReportedPower) {
+                final double powerMah =
+                        counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
+                                / (double) (1000 * 60 * 60);
+                if (powerMah != 0) {
+                    powerAndDuration.powerMah = powerMah;
+                    if (powerAndDuration.powerPerKeyMah != null) {
+                        // Leave this use case unsupported: used energy is reported
+                        // via BluetoothActivityEnergyInfo rather than PowerStats HAL.
+                        Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
+                    }
+                    return;
+                }
+            }
+
+            if (mHasBluetoothPowerController) {
+                powerAndDuration.powerMah = calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
+
+                if (powerAndDuration.keys != null) {
+                    for (int i = 0; i < powerAndDuration.keys.length; i++) {
+                        BatteryConsumer.Key key = powerAndDuration.keys[i];
+                        final int processState = key.processState;
+                        if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                            // Already populated with the powerAndDuration across all process states
+                            continue;
+                        }
+
+                        powerAndDuration.powerPerKeyMah[i] =
+                                calculatePowerMah(
+                                        rxTimeCounter.getCountForProcessState(processState),
+                                        txTimeCounter.getCountForProcessState(processState),
+                                        idleTimeCounter.getCountForProcessState(processState));
+                    }
+                }
+            } else {
+                powerAndDuration.powerMah = 0;
+                if (powerAndDuration.powerPerKeyMah != null) {
+                    Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
+                }
             }
         }
-
-        if (!mHasBluetoothPowerController) {
-            return 0;
-        }
-
-        final long idleTimeMs =
-                counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
-        final long rxTimeMs =
-                counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
-        final long txTimeMs =
-                counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
-        return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
     }
 
     /** Returns estimated bluetooth power usage based on usage times. */
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 7766b77..fd1d86b 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -12,4 +12,5 @@
 per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
 per-file *Kernel* = file:/BATTERY_STATS_OWNERS
 per-file *MultiState* = file:/BATTERY_STATS_OWNERS
+per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS
 
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 4d19b35..54e65e0 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -17,24 +17,31 @@
 package com.android.internal.os;
 
 
+import android.annotation.LongDef;
 import android.annotation.StringDef;
+import android.annotation.XmlRes;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.power.ModemPowerProfile;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 
 /**
@@ -259,6 +266,34 @@
     public @interface PowerGroup {}
 
     /**
+     * Constants for generating a 64bit power constant key.
+     *
+     * The bitfields of a key describes what its corresponding power constant represents:
+     * [63:40] - RESERVED
+     * [39:32] - {@link Subsystem} (max count = 16).
+     * [31:0] - per Subsystem fields, see {@link ModemPowerProfile}.
+     *
+     */
+    private static final long SUBSYSTEM_MASK = 0xF_0000_0000L;
+    /**
+     * Power constant not associated with a subsystem.
+     */
+    public static final long SUBSYSTEM_NONE = 0x0_0000_0000L;
+    /**
+     * Modem power constant.
+     */
+    public static final long SUBSYSTEM_MODEM = 0x1_0000_0000L;
+
+    @LongDef(prefix = { "SUBSYSTEM_" }, value = {
+            SUBSYSTEM_NONE,
+            SUBSYSTEM_MODEM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Subsystem {}
+
+    private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFF_FFFF;
+
+    /**
      * A map from Power Use Item to its power consumption.
      */
     static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
@@ -268,12 +303,16 @@
      */
     static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>();
 
+    static final ModemPowerProfile sModemPowerProfile = new ModemPowerProfile();
+
     private static final String TAG_DEVICE = "device";
     private static final String TAG_ITEM = "item";
     private static final String TAG_ARRAY = "array";
     private static final String TAG_ARRAYITEM = "value";
     private static final String ATTR_NAME = "name";
 
+    private static final String TAG_MODEM = "modem";
+
     private static final Object sLock = new Object();
 
     @VisibleForTesting
@@ -289,19 +328,40 @@
     public PowerProfile(Context context, boolean forTest) {
         // Read the XML file for the given profile (normally only one per device)
         synchronized (sLock) {
-            if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
-                readPowerValuesFromXml(context, forTest);
-            }
-            initCpuClusters();
-            initDisplays();
+            final int xmlId = forTest ? com.android.internal.R.xml.power_profile_test
+                    : com.android.internal.R.xml.power_profile;
+            initLocked(context, xmlId);
         }
     }
 
-    private void readPowerValuesFromXml(Context context, boolean forTest) {
-        final int id = forTest ? com.android.internal.R.xml.power_profile_test :
-                com.android.internal.R.xml.power_profile;
+    /**
+     * Reinitialize the PowerProfile with the provided XML.
+     * WARNING: use only for testing!
+     */
+    @VisibleForTesting
+    public void forceInitForTesting(Context context, @XmlRes int xmlId) {
+        synchronized (sLock) {
+            sPowerItemMap.clear();
+            sPowerArrayMap.clear();
+            sModemPowerProfile.clear();
+            initLocked(context, xmlId);
+        }
+
+    }
+
+    @GuardedBy("sLock")
+    private void initLocked(Context context, @XmlRes int xmlId) {
+        if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
+            readPowerValuesFromXml(context, xmlId);
+        }
+        initCpuClusters();
+        initDisplays();
+        initModem();
+    }
+
+    private void readPowerValuesFromXml(Context context, @XmlRes int xmlId) {
         final Resources resources = context.getResources();
-        XmlResourceParser parser = resources.getXml(id);
+        XmlResourceParser parser = resources.getXml(xmlId);
         boolean parsingArray = false;
         ArrayList<Double> array = new ArrayList<>();
         String arrayName = null;
@@ -340,6 +400,8 @@
                             array.add(value);
                         }
                     }
+                } else if (element.equals(TAG_MODEM)) {
+                    sModemPowerProfile.parseFromXml(parser);
                 }
             }
             if (parsingArray) {
@@ -515,6 +577,39 @@
         return mNumDisplays;
     }
 
+    private void initModem() {
+        handleDeprecatedModemConstant(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
+                POWER_MODEM_CONTROLLER_SLEEP, 0);
+        handleDeprecatedModemConstant(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE,
+                POWER_MODEM_CONTROLLER_IDLE, 0);
+        handleDeprecatedModemConstant(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX,
+                POWER_MODEM_CONTROLLER_RX, 0);
+        handleDeprecatedModemConstant(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0, POWER_MODEM_CONTROLLER_TX, 0);
+        handleDeprecatedModemConstant(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1, POWER_MODEM_CONTROLLER_TX, 1);
+        handleDeprecatedModemConstant(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2, POWER_MODEM_CONTROLLER_TX, 2);
+        handleDeprecatedModemConstant(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3, POWER_MODEM_CONTROLLER_TX, 3);
+        handleDeprecatedModemConstant(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4, POWER_MODEM_CONTROLLER_TX, 4);
+    }
+
+    private void handleDeprecatedModemConstant(int key, String deprecatedKey, int level) {
+        final double drain = sModemPowerProfile.getAverageBatteryDrainMa(key);
+        if (!Double.isNaN(drain)) return; // Value already set, don't overwrite it.
+
+        final double deprecatedDrain = getAveragePower(deprecatedKey, level);
+        sModemPowerProfile.setPowerConstant(key, Double.toString(deprecatedDrain));
+    }
+
     /**
      * Returns the number of memory bandwidth buckets defined in power_profile.xml, or a
      * default value if the subsystem has no recorded value.
@@ -560,6 +655,43 @@
     }
 
     /**
+     * Returns the average current in mA consumed by a subsystem's specified operation, or the given
+     * default value if the subsystem has no recorded value.
+     *
+     * @param key that describes a subsystem's battery draining operation
+     *            The key is built from multiple constant, see {@link Subsystem} and
+     *            {@link ModemPowerProfile}.
+     * @param defaultValue the value to return if the subsystem has no recorded value.
+     * @return the average current in milliAmps.
+     */
+    public double getAverageBatteryDrainOrDefaultMa(long key, double defaultValue) {
+        final long subsystemType = key & SUBSYSTEM_MASK;
+        final int subsystemFields = (int) (key & SUBSYSTEM_FIELDS_MASK);
+
+        final double value;
+        if (subsystemType == SUBSYSTEM_MODEM) {
+            value = sModemPowerProfile.getAverageBatteryDrainMa(subsystemFields);
+        } else {
+            value = Double.NaN;
+        }
+
+        if (Double.isNaN(value)) return defaultValue;
+        return value;
+    }
+
+    /**
+     * Returns the average current in mA consumed by a subsystem's specified operation.
+     *
+     * @param key that describes a subsystem's battery draining operation
+     *            The key is built from multiple constant, see {@link Subsystem} and
+     *            {@link ModemPowerProfile}.
+     * @return the average current in milliAmps.
+     */
+    public double getAverageBatteryDrainMa(long key) {
+        return getAverageBatteryDrainOrDefaultMa(key, 0);
+    }
+
+    /**
      * Returns the average current in mA consumed by the subsystem for the given level.
      *
      * @param type  the subsystem type
@@ -784,6 +916,25 @@
                 PowerProfileProto.BATTERY_CAPACITY);
     }
 
+    /**
+     * Dump the PowerProfile values.
+     */
+    public void dump(PrintWriter pw) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+        sPowerItemMap.forEach((key, value) -> {
+            ipw.print(key, value);
+            ipw.println();
+        });
+        sPowerArrayMap.forEach((key, value) -> {
+            ipw.print(key, Arrays.toString(value));
+            ipw.println();
+        });
+        ipw.println("Modem values:");
+        ipw.increaseIndent();
+        sModemPowerProfile.dump(ipw);
+        ipw.decreaseIndent();
+    }
+
     // Writes items in sPowerItemMap to proto if exists.
     private void writePowerConstantToProto(ProtoOutputStream proto, String key, long fieldId) {
         if (sPowerItemMap.containsKey(key)) {
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 3915b0e..2a71ac6 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -34,6 +35,9 @@
 public class WifiPowerCalculator extends PowerCalculator {
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
     private static final String TAG = "WifiPowerCalculator";
+
+    private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
     private final UsageBasedPowerEstimator mIdlePowerEstimator;
     private final UsageBasedPowerEstimator mTxPowerEstimator;
     private final UsageBasedPowerEstimator mRxPowerEstimator;
@@ -51,6 +55,9 @@
         public long wifiTxPackets;
         public long wifiRxBytes;
         public long wifiTxBytes;
+
+        public BatteryConsumer.Key[] keys;
+        public double[] powerPerKeyMah;
     }
 
     public WifiPowerCalculator(PowerProfile profile) {
@@ -77,7 +84,7 @@
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
-
+        BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
         long totalAppDurationMs = 0;
         double totalAppPowerMah = 0;
         final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
@@ -85,9 +92,20 @@
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+            if (keys == UNINITIALIZED_KEYS) {
+                if (query.isProcessStateDataNeeded()) {
+                    keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_WIFI);
+                    powerDurationAndTraffic.keys = keys;
+                    powerDurationAndTraffic.powerPerKeyMah = new double[keys.length];
+                } else {
+                    keys = null;
+                }
+            }
+
             final long consumptionUC =
                     app.getBatteryStatsUid().getWifiMeasuredBatteryConsumptionUC();
             final int powerModel = getPowerModel(consumptionUC, query);
+
             calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), powerModel,
                     rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED,
                     batteryStats.hasWifiActivityReporting(), consumptionUC);
@@ -99,6 +117,20 @@
                     powerDurationAndTraffic.durationMs);
             app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
                     powerDurationAndTraffic.powerMah, powerModel);
+
+            if (query.isProcessStateDataNeeded() && keys != null) {
+                for (int j = 0; j < keys.length; j++) {
+                    BatteryConsumer.Key key = keys[j];
+                    final int processState = key.processState;
+                    if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                        // Already populated with the total across all process states
+                        continue;
+                    }
+
+                    app.setConsumedPower(key, powerDurationAndTraffic.powerPerKeyMah[j],
+                            powerModel);
+                }
+            }
         }
 
         final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
@@ -193,21 +225,23 @@
                 BatteryStats.NETWORK_WIFI_TX_DATA,
                 statsType);
 
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-            powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
-        }
-
         if (hasWifiActivityReporting && mHasWifiPowerController) {
             final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
             if (counter != null) {
-                final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
-                final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
-                final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
+                final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
+                final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
+                final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
+
+                final long rxTime = rxTimeCounter.getCountLocked(statsType);
+                final long txTime = txTimeCounter.getCountLocked(statsType);
+                final long idleTime = idleTimeCounter.getCountLocked(statsType);
 
                 powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
                 if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
                     powerDurationAndTraffic.powerMah
                             = calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
+                } else {
+                    powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
                 }
 
                 if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
@@ -215,9 +249,32 @@
                             + "ms tx=" + txTime + "ms power=" + formatCharge(
                             powerDurationAndTraffic.powerMah));
                 }
+
+                if (powerDurationAndTraffic.keys != null) {
+                    for (int i = 0; i < powerDurationAndTraffic.keys.length; i++) {
+                        final int processState = powerDurationAndTraffic.keys[i].processState;
+                        if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                            continue;
+                        }
+
+                        if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
+                            powerDurationAndTraffic.powerPerKeyMah[i] =
+                                    calcPowerFromControllerDataMah(
+                                            rxTimeCounter.getCountForProcessState(processState),
+                                            txTimeCounter.getCountForProcessState(processState),
+                                            idleTimeCounter.getCountForProcessState(processState));
+                        } else {
+                            powerDurationAndTraffic.powerPerKeyMah[i] =
+                                    uCtoMah(u.getWifiMeasuredBatteryConsumptionUC(processState));
+                        }
+                    }
+                }
             } else {
                 powerDurationAndTraffic.durationMs = 0;
                 powerDurationAndTraffic.powerMah = 0;
+                if (powerDurationAndTraffic.powerPerKeyMah != null) {
+                    Arrays.fill(powerDurationAndTraffic.powerPerKeyMah, 0);
+                }
             }
         } else {
             final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
@@ -233,6 +290,14 @@
                         powerDurationAndTraffic.wifiRxPackets,
                         powerDurationAndTraffic.wifiTxPackets,
                         wifiRunningTime, wifiScanTimeMs, batchTimeMs);
+            } else {
+                powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
+            }
+
+            if (powerDurationAndTraffic.powerPerKeyMah != null) {
+                // Per-process state attribution is not supported in the absence of WiFi
+                // activity reporting
+                Arrays.fill(powerDurationAndTraffic.powerPerKeyMah, 0);
             }
 
             if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6541b14..d7eeb7b 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -87,6 +87,7 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
 import android.view.PendingInsetsController;
 import android.view.ThreadedRenderer;
 import android.view.View;
@@ -108,6 +109,7 @@
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.PopupWindow;
+import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.internal.R;
 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
@@ -121,7 +123,7 @@
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.BackgroundFallback;
 import com.android.internal.widget.DecorCaptionView;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
 import java.util.List;
 import java.util.function.Consumer;
@@ -294,6 +296,7 @@
         return true;
     };
     private Consumer<Boolean> mCrossWindowBlurEnabledListener;
+    private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
 
     DecorView(Context context, int featureId, PhoneWindow window,
             WindowManager.LayoutParams params) {
@@ -322,6 +325,7 @@
         initResizingPaints();
 
         mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
+        mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher();
     }
 
     void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1869,6 +1873,7 @@
         }
 
         mPendingInsetsController.detach();
+        mOnBackInvokedDispatcher.detachFromWindow();
     }
 
     @Override
@@ -1913,6 +1918,11 @@
         return mPendingInsetsController;
     }
 
+    @Override
+    public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() {
+        return mOnBackInvokedDispatcher;
+    }
+
     private ActionMode createActionMode(
             int type, ActionMode.Callback2 callback, View originatingView) {
         switch (type) {
@@ -2367,6 +2377,7 @@
                 }
             }
         }
+        mOnBackInvokedDispatcher.clear();
     }
 
     @Override
@@ -2648,6 +2659,15 @@
         }
     }
 
+    /**
+     * Returns the {@link OnBackInvokedDispatcher} on the decor view.
+     */
+    @Override
+    @Nullable
+    public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+        return mOnBackInvokedDispatcher;
+    }
+
     @Override
     public String toString() {
         return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java
new file mode 100644
index 0000000..afea69a
--- /dev/null
+++ b/core/java/com/android/internal/power/ModemPowerProfile.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.power;
+
+import android.annotation.IntDef;
+import android.content.res.XmlResourceParser;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseDoubleArray;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * ModemPowerProfile for handling the modem element in the power_profile.xml
+ */
+public class ModemPowerProfile {
+    private static final String TAG = "ModemPowerProfile";
+
+    private static final String TAG_SLEEP = "sleep";
+    private static final String TAG_IDLE = "idle";
+    private static final String TAG_ACTIVE = "active";
+    private static final String TAG_RECEIVE = "receive";
+    private static final String TAG_TRANSMIT = "transmit";
+    private static final String ATTR_RAT = "rat";
+    private static final String ATTR_NR_FREQUENCY = "nrFrequency";
+    private static final String ATTR_LEVEL = "level";
+
+    /**
+     * A flattened list of the modem power constant extracted from the given XML parser.
+     *
+     * The bitfields of a key describes what its corresponding power constant represents:
+     * [31:28] - {@link ModemDrainType} (max count = 16).
+     * [27:24] - {@link ModemTxLevel} (only for {@link MODEM_DRAIN_TYPE_TX}) (max count = 16).
+     * [23:20] - {@link ModemRatType} (max count = 16).
+     * [19:16] - {@link ModemNrFrequencyRange} (only for {@link MODEM_RAT_TYPE_NR})
+     * (max count = 16).
+     * [15:0] - RESERVED
+     */
+    private final SparseDoubleArray mPowerConstants = new SparseDoubleArray();
+
+    private static final int MODEM_DRAIN_TYPE_MASK = 0xF000_0000;
+    private static final int MODEM_TX_LEVEL_MASK = 0x0F00_0000;
+    private static final int MODEM_RAT_TYPE_MASK = 0x00F0_0000;
+    private static final int MODEM_NR_FREQUENCY_RANGE_MASK = 0x000F_0000;
+
+    /**
+     * Corresponds to the overall modem battery drain while asleep.
+     */
+    public static final int MODEM_DRAIN_TYPE_SLEEP = 0x0000_0000;
+
+    /**
+     * Corresponds to the overall modem battery drain while idle.
+     */
+    public static final int MODEM_DRAIN_TYPE_IDLE = 0x1000_0000;
+
+    /**
+     * Corresponds to the modem battery drain while receiving data. A specific Rx battery drain
+     * power constant can be selected using a bitwise OR (|) with {@link ModemRatType} and
+     * {@link ModemNrFrequencyRange} (when applicable).
+     */
+    public static final int MODEM_DRAIN_TYPE_RX = 0x2000_0000;
+
+    /**
+     * Corresponds to the modem battery drain while receiving data.
+     * {@link ModemTxLevel} must be specified with this drain type.
+     * Specific Tx battery drain power constanta can be selected using a bitwise OR (|) with
+     * {@link ModemRatType} and {@link ModemNrFrequencyRange} (when applicable).
+     */
+    public static final int MODEM_DRAIN_TYPE_TX = 0x3000_0000;
+
+    @IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = {
+            MODEM_DRAIN_TYPE_SLEEP,
+            MODEM_DRAIN_TYPE_IDLE,
+            MODEM_DRAIN_TYPE_RX,
+            MODEM_DRAIN_TYPE_TX,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModemDrainType {
+    }
+
+
+    private static final SparseArray<String> MODEM_DRAIN_TYPE_NAMES = new SparseArray<>(4);
+    static {
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_SLEEP, "SLEEP");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_IDLE, "IDLE");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_RX, "RX");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_TX, "TX");
+    }
+
+    /**
+     * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_0}.
+     */
+
+    public static final int MODEM_TX_LEVEL_0 = 0x0000_0000;
+
+    /**
+     * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_1}.
+     */
+
+    public static final int MODEM_TX_LEVEL_1 = 0x0100_0000;
+
+    /**
+     * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_2}.
+     */
+
+    public static final int MODEM_TX_LEVEL_2 = 0x0200_0000;
+
+    /**
+     * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_3}.
+     */
+
+    public static final int MODEM_TX_LEVEL_3 = 0x0300_0000;
+
+    /**
+     * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_4}.
+     */
+
+    public static final int MODEM_TX_LEVEL_4 = 0x0400_0000;
+
+    private static final int MODEM_TX_LEVEL_COUNT = 5;
+
+    @IntDef(prefix = {"MODEM_TX_LEVEL_"}, value = {
+            MODEM_TX_LEVEL_0,
+            MODEM_TX_LEVEL_1,
+            MODEM_TX_LEVEL_2,
+            MODEM_TX_LEVEL_3,
+            MODEM_TX_LEVEL_4,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModemTxLevel {
+    }
+
+    private static final SparseArray<String> MODEM_TX_LEVEL_NAMES = new SparseArray<>(5);
+    static {
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_0, "0");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_1, "1");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_2, "2");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_3, "3");
+        MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_4, "4");
+    }
+
+    private static final int[] MODEM_TX_LEVEL_MAP = new int[]{
+            MODEM_TX_LEVEL_0,
+            MODEM_TX_LEVEL_1,
+            MODEM_TX_LEVEL_2,
+            MODEM_TX_LEVEL_3,
+            MODEM_TX_LEVEL_4};
+
+    /**
+     * Fallback for any active modem usage that does not match specified Radio Access Technology
+     * (RAT) power constants.
+     */
+    public static final int MODEM_RAT_TYPE_DEFAULT = 0x0000_0000;
+
+    /**
+     * Corresponds to active modem usage on 4G {@link TelephonyManager#NETWORK_TYPE_LTE} RAT.
+     */
+    public static final int MODEM_RAT_TYPE_LTE = 0x0010_0000;
+
+    /**
+     * Corresponds to active modem usage on 5G {@link TelephonyManager#NETWORK_TYPE_NR} RAT.
+     */
+    public static final int MODEM_RAT_TYPE_NR = 0x0020_0000;
+
+    @IntDef(prefix = {"MODEM_RAT_TYPE_"}, value = {
+            MODEM_RAT_TYPE_DEFAULT,
+            MODEM_RAT_TYPE_LTE,
+            MODEM_RAT_TYPE_NR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModemRatType {
+    }
+
+    private static final SparseArray<String> MODEM_RAT_TYPE_NAMES = new SparseArray<>(3);
+    static {
+        MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_DEFAULT, "DEFAULT");
+        MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_LTE, "LTE");
+        MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_NR, "NR");
+    }
+
+    /**
+     * Fallback for any active 5G modem usage that does not match specified NR frequency power
+     * constants.
+     */
+    public static final int MODEM_NR_FREQUENCY_RANGE_DEFAULT = 0x0000_0000;
+
+    /**
+     * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_LOW}.
+     */
+    public static final int MODEM_NR_FREQUENCY_RANGE_LOW = 0x0001_0000;
+
+    /**
+     * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MID}.
+     */
+    public static final int MODEM_NR_FREQUENCY_RANGE_MID = 0x0002_0000;
+
+    /**
+     * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_HIGH}.
+     */
+    public static final int MODEM_NR_FREQUENCY_RANGE_HIGH = 0x0003_0000;
+
+    /**
+     * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MMWAVE}.
+     */
+    public static final int MODEM_NR_FREQUENCY_RANGE_MMWAVE = 0x0004_0000;
+
+    @IntDef(prefix = {"MODEM_NR_FREQUENCY_RANGE_"}, value = {
+            MODEM_RAT_TYPE_DEFAULT,
+            MODEM_NR_FREQUENCY_RANGE_LOW,
+            MODEM_NR_FREQUENCY_RANGE_MID,
+            MODEM_NR_FREQUENCY_RANGE_HIGH,
+            MODEM_NR_FREQUENCY_RANGE_MMWAVE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModemNrFrequencyRange {
+    }
+    private static final SparseArray<String> MODEM_NR_FREQUENCY_RANGE_NAMES = new SparseArray<>(5);
+    static {
+        MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_DEFAULT, "DEFAULT");
+        MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_LOW, "LOW");
+        MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MID, "MID");
+        MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_HIGH, "HIGH");
+        MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MMWAVE, "MMWAVE");
+    }
+
+    public ModemPowerProfile() {
+    }
+
+    /**
+     * Generates a ModemPowerProfile object from the <modem /> element of a power_profile.xml
+     */
+    public void parseFromXml(XmlResourceParser parser) throws IOException,
+            XmlPullParserException {
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            final String name = parser.getName();
+            switch (name) {
+                case TAG_SLEEP:
+                    if (parser.next() != XmlPullParser.TEXT) {
+                        continue;
+                    }
+                    final String sleepDrain = parser.getText();
+                    setPowerConstant(MODEM_DRAIN_TYPE_SLEEP, sleepDrain);
+                    break;
+                case TAG_IDLE:
+                    if (parser.next() != XmlPullParser.TEXT) {
+                        continue;
+                    }
+                    final String idleDrain = parser.getText();
+                    setPowerConstant(MODEM_DRAIN_TYPE_IDLE, idleDrain);
+                    break;
+                case TAG_ACTIVE:
+                    parseActivePowerConstantsFromXml(parser);
+                    break;
+                default:
+                    Slog.e(TAG, "Unexpected element parsed: " + name);
+            }
+        }
+    }
+
+    /** Parse the <active /> XML element */
+    private void parseActivePowerConstantsFromXml(XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        // Parse attributes to get the type of active modem usage the power constants are for.
+        final int ratType;
+        final int nrfType;
+        try {
+            ratType = getTypeFromAttribute(parser, ATTR_RAT, MODEM_RAT_TYPE_NAMES);
+            if (ratType == MODEM_RAT_TYPE_NR) {
+                nrfType = getTypeFromAttribute(parser, ATTR_NR_FREQUENCY,
+                        MODEM_NR_FREQUENCY_RANGE_NAMES);
+            } else {
+                nrfType = 0;
+            }
+        } catch (IllegalArgumentException iae) {
+            Slog.e(TAG, "Failed parse to active modem power constants", iae);
+            return;
+        }
+
+        // Parse and populate the active modem use power constants.
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            final String name = parser.getName();
+            switch (name) {
+                case TAG_RECEIVE:
+                    if (parser.next() != XmlPullParser.TEXT) {
+                        continue;
+                    }
+                    final String rxDrain = parser.getText();
+                    final int rxKey = MODEM_DRAIN_TYPE_RX | ratType | nrfType;
+                    setPowerConstant(rxKey, rxDrain);
+                    break;
+                case TAG_TRANSMIT:
+                    final int level = XmlUtils.readIntAttribute(parser, ATTR_LEVEL, -1);
+                    if (parser.next() != XmlPullParser.TEXT) {
+                        continue;
+                    }
+                    final String txDrain = parser.getText();
+                    if (level < 0 || level >= MODEM_TX_LEVEL_COUNT) {
+                        Slog.e(TAG,
+                                "Unexpected tx level: " + level + ". Must be between 0 and " + (
+                                        MODEM_TX_LEVEL_COUNT - 1));
+                        continue;
+                    }
+                    final int modemTxLevel = MODEM_TX_LEVEL_MAP[level];
+                    final int txKey = MODEM_DRAIN_TYPE_TX | modemTxLevel | ratType | nrfType;
+                    setPowerConstant(txKey, txDrain);
+                    break;
+                default:
+                    Slog.e(TAG, "Unexpected element parsed: " + name);
+            }
+        }
+    }
+
+    private static int getTypeFromAttribute(XmlResourceParser parser, String attr,
+            SparseArray<String> names) {
+        final String value = XmlUtils.readStringAttribute(parser, attr);
+        if (value == null) {
+            // Attribute was not specified, just use the default.
+            return 0;
+        }
+        int index = -1;
+        final int size = names.size();
+        // Manual linear search for string. (SparseArray uses == not equals.)
+        for (int i = 0; i < size; i++) {
+            if (value.equals(names.valueAt(i))) {
+                index = i;
+            }
+        }
+        if (index < 0) {
+            final String[] stringNames = new String[size];
+            for (int i = 0; i < size; i++) {
+                stringNames[i] = names.valueAt(i);
+            }
+            throw new IllegalArgumentException(
+                    "Unexpected " + attr + " value : " + value + ". Acceptable values are "
+                            + Arrays.toString(stringNames));
+        }
+        return names.keyAt(index);
+    }
+
+    /**
+     * Set the average battery drain in milli-amps of the modem for a given drain type.
+     *
+     * @param key   a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel},
+     *              {@link ModemRatType}, and {@link ModemNrFrequencyRange}.key
+     * @param value the battery dram in milli-amps for the given key.
+     */
+    public void setPowerConstant(int key, String value) {
+        try {
+            mPowerConstants.put(key, Double.valueOf(value));
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to set power constant 0x" + Integer.toHexString(
+                    key) + "(" + keyToString(key) + ") to " + value, e);
+        }
+    }
+
+    /**
+     * Returns the average battery drain in milli-amps of the modem for a given drain type.
+     * Returns {@link Double.NaN} if a suitable value is not found for the given key.
+     *
+     * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel},
+     *            {@link ModemRatType}, and {@link ModemNrFrequencyRange}.
+     */
+    public double getAverageBatteryDrainMa(int key) {
+        int bestKey = key;
+        double value;
+        value = mPowerConstants.get(bestKey, Double.NaN);
+        if (!Double.isNaN(value)) return value;
+        // The power constant for given key was not explicitly set. Try to fallback to possible
+        // defaults.
+
+        if ((bestKey & MODEM_NR_FREQUENCY_RANGE_MASK) != MODEM_NR_FREQUENCY_RANGE_DEFAULT) {
+            // Fallback to NR Frequency default value
+            bestKey &= ~MODEM_NR_FREQUENCY_RANGE_MASK;
+            bestKey |= MODEM_NR_FREQUENCY_RANGE_DEFAULT;
+            value = mPowerConstants.get(bestKey, Double.NaN);
+            if (!Double.isNaN(value)) return value;
+        }
+
+        if ((bestKey & MODEM_RAT_TYPE_MASK) != MODEM_RAT_TYPE_DEFAULT) {
+            // Fallback to RAT default value
+            bestKey &= ~MODEM_RAT_TYPE_MASK;
+            bestKey |= MODEM_RAT_TYPE_DEFAULT;
+            value = mPowerConstants.get(bestKey, Double.NaN);
+            if (!Double.isNaN(value)) return value;
+        }
+
+        Slog.w(TAG,
+                "getAverageBatteryDrainMaH called with unexpected key: 0x" + Integer.toHexString(
+                        key) + ", " + keyToString(key));
+        return Double.NaN;
+    }
+
+    private static String keyToString(int key) {
+        StringBuilder sb = new StringBuilder();
+        final int drainType = key & MODEM_DRAIN_TYPE_MASK;
+        appendFieldToString(sb, "drain", MODEM_DRAIN_TYPE_NAMES, drainType);
+        sb.append(",");
+
+        if (drainType == MODEM_DRAIN_TYPE_TX) {
+            final int txLevel = key & MODEM_TX_LEVEL_MASK;
+            appendFieldToString(sb, "level", MODEM_TX_LEVEL_NAMES, txLevel);
+        }
+
+        final int ratType = key & MODEM_RAT_TYPE_MASK;
+        appendFieldToString(sb, "RAT", MODEM_RAT_TYPE_NAMES, ratType);
+
+        if (ratType == MODEM_RAT_TYPE_NR) {
+            sb.append(",");
+            final int nrFreq = key & MODEM_NR_FREQUENCY_RANGE_MASK;
+            appendFieldToString(sb, "nrFreq", MODEM_NR_FREQUENCY_RANGE_NAMES, nrFreq);
+        }
+        return sb.toString();
+    }
+    private static void appendFieldToString(StringBuilder sb, String fieldName,
+            SparseArray<String> names, int key) {
+        sb.append(fieldName);
+        sb.append(":");
+        final String name = names.get(key, null);
+        if (name == null) {
+            sb.append("UNKNOWN(0x");
+            sb.append(Integer.toHexString(key));
+            sb.append(")");
+        } else {
+            sb.append(name);
+        }
+    }
+
+    /**
+     * Clear this ModemPowerProfile power constants.
+     */
+    public void clear() {
+        mPowerConstants.clear();
+    }
+
+
+    /**
+     * Dump this ModemPowerProfile power constants.
+     */
+    public void dump(PrintWriter pw) {
+        final int size = mPowerConstants.size();
+        for (int i = 0; i < size; i++) {
+            pw.print(keyToString(mPowerConstants.keyAt(i)));
+            pw.print("=");
+            pw.println(mPowerConstants.valueAt(i));
+        }
+    }
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 5ac4936..def598c 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -85,6 +85,8 @@
     WM_DEBUG_LAYER_MIRRORING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM),
     WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+    WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            "CoreBackPreview"),
     TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
 
     private final boolean mEnabled;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f3cdf82..a5cf7ce 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -207,8 +207,10 @@
      *
      * @param displayId the ID of the display to notify.
      * @param types the internal insets types of the bars are about to show transiently.
+     * @param isGestureOnSystemBar whether the gesture to show the transient bar was a gesture on
+     *        one of the bars itself.
      */
-    void showTransient(int displayId, in int[] types);
+    void showTransient(int displayId, in int[] types, boolean isGestureOnSystemBar);
 
     /**
      * Notifies System UI to abort the transient state of system bars, which prevents the bars being
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 1db7426..3c6b7ff 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -162,4 +162,20 @@
 
     void requestAddTile(in ComponentName componentName, in CharSequence label, in Icon icon, int userId, in IAddTileResultCallback callback);
     void cancelRequestAddTile(in String packageName);
+
+    /**
+    * Overrides the navigation bar mode.
+    *
+    * @param navBarModeOverride the mode of the navigation bar override to be set.
+    *
+    * @hide
+    */
+    void setNavBarModeOverride(int navBarModeOverride);
+
+    /**
+    * Gets the navigation bar mode override.
+    *
+    * @hide
+    */
+    int getNavBarModeOverride();
 }
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 3260136..b32a6b0 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -244,7 +244,12 @@
         writeContaminantPresenceStatus(dump, "contaminant_presence_status",
                 UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
                 status.getContaminantDetectionStatus());
-
+        dump.write("usb_data_status", UsbPortStatusProto.USB_DATA_STATUS,
+                UsbPort.usbDataStatusToString(status.getUsbDataStatus()));
+        dump.write("is_power_transfer_limited", UsbPortStatusProto.IS_POWER_TRANSFER_LIMITED,
+                status.isPowerTransferLimited());
+        dump.write("usb_power_brick_status", UsbPortStatusProto.USB_POWER_BRICK_STATUS,
+                UsbPort.powerBrickStatusToString(status.getPowerBrickStatus()));
         dump.end(token);
     }
 }
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 3ca3320..4b3af15 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -17,7 +17,7 @@
 package com.android.internal.util;
 
 import android.os.FileUtils;
-import android.util.Slog;
+import android.util.Log;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -32,7 +32,6 @@
 import java.util.zip.ZipOutputStream;
 
 import libcore.io.IoUtils;
-import libcore.io.Streams;
 
 /**
  * Utility that rotates files over time, similar to {@code logrotate}. There is
@@ -47,6 +46,8 @@
  * <p>
  * Users must periodically call {@link #maybeRotate(long)} to perform actual
  * rotation. Not inherently thread safe.
+ *
+ * @hide
  */
 public class FileRotator {
     private static final String TAG = "FileRotator";
@@ -110,7 +111,7 @@
             if (!name.startsWith(mPrefix)) continue;
 
             if (name.endsWith(SUFFIX_BACKUP)) {
-                if (LOGD) Slog.d(TAG, "recovering " + name);
+                if (LOGD) Log.d(TAG, "recovering " + name);
 
                 final File backupFile = new File(mBasePath, name);
                 final File file = new File(
@@ -120,7 +121,7 @@
                 backupFile.renameTo(file);
 
             } else if (name.endsWith(SUFFIX_NO_BACKUP)) {
-                if (LOGD) Slog.d(TAG, "recovering " + name);
+                if (LOGD) Log.d(TAG, "recovering " + name);
 
                 final File noBackupFile = new File(mBasePath, name);
                 final File file = new File(
@@ -231,7 +232,7 @@
      * if the write fails.
      */
     private void rewriteSingle(Rewriter rewriter, String name) throws IOException {
-        if (LOGD) Slog.d(TAG, "rewriting " + name);
+        if (LOGD) Log.d(TAG, "rewriting " + name);
 
         final File file = new File(mBasePath, name);
         final File backupFile;
@@ -291,7 +292,7 @@
 
             // read file when it overlaps
             if (info.startMillis <= matchEndMillis && matchStartMillis <= info.endMillis) {
-                if (LOGD) Slog.d(TAG, "reading matching " + name);
+                if (LOGD) Log.d(TAG, "reading matching " + name);
 
                 final File file = new File(mBasePath, name);
                 readFile(file, reader);
@@ -348,7 +349,7 @@
             if (info.isActive()) {
                 if (info.startMillis <= rotateBefore) {
                     // found active file; rotate if old enough
-                    if (LOGD) Slog.d(TAG, "rotating " + name);
+                    if (LOGD) Log.d(TAG, "rotating " + name);
 
                     info.endMillis = currentTimeMillis;
 
@@ -358,7 +359,7 @@
                 }
             } else if (info.endMillis <= deleteBefore) {
                 // found rotated file; delete if old enough
-                if (LOGD) Slog.d(TAG, "deleting " + name);
+                if (LOGD) Log.d(TAG, "deleting " + name);
 
                 final File file = new File(mBasePath, name);
                 file.delete();
@@ -383,7 +384,10 @@
             writer.write(bos);
             bos.flush();
         } finally {
-            FileUtils.sync(fos);
+            try {
+                fos.getFD().sync();
+            } catch (IOException e) {
+            }
             IoUtils.closeQuietly(bos);
         }
     }
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 04e4819..d3c3917 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -178,7 +178,7 @@
     public ScreenshotHelper(Context context) {
         mContext = context;
         IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mBroadcastReceiver, filter);
+        mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
     }
 
     /**
diff --git a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
new file mode 100644
index 0000000..d48b4b1
--- /dev/null
+++ b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util.dump;
+
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Dumpable;
+import android.util.DumpableContainer;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+// TODO(b/149254050): add unit tests
+/**
+ * Helper class for {@link DumpableContainer} implementations - they can "implement it by
+ * association", i.e., by delegating the interface methods to a {@code DumpableContainerImpl}.
+ *
+ * @hide
+ */
+public final class DumpableContainerImpl implements DumpableContainer {
+
+    private static final String TAG = DumpableContainerImpl.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+
+    @Nullable
+    private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
+
+    @Override
+    public boolean addDumpable(Dumpable dumpable) {
+        Objects.requireNonNull(dumpable, "dumpable");
+        String name = dumpable.getDumpableName();
+        Objects.requireNonNull(name, () -> "name of" + dumpable);
+
+        if (mDumpables.containsKey(name)) {
+            Log.e(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+                    + " with that name (" + name + "): " + mDumpables.get(name));
+            return false;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "Adding " + name + " -> " + dumpable);
+        }
+        mDumpables.put(name,  dumpable);
+        return true;
+    }
+
+    /**
+     * Dumps the number of dumpable, without a newline.
+     */
+    private int dumpNumberDumpables(IndentingPrintWriter writer) {
+        int size = mDumpables == null ? 0 : mDumpables.size();
+        if (size == 0) {
+            writer.print("No dumpables");
+        } else {
+            writer.print(size); writer.print(" dumpables");
+        }
+        return size;
+    }
+
+    /**
+     * Lists the name of all dumpables to the given {@code writer}.
+     */
+    public void listDumpables(String prefix, PrintWriter writer) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+
+        int size = dumpNumberDumpables(ipw);
+        if (size == 0) {
+            ipw.println();
+            return;
+        }
+        ipw.print(": ");
+        for (int i = 0; i < size; i++) {
+            ipw.print(mDumpables.keyAt(i));
+            if (i < size - 1) ipw.print(' ');
+        }
+        ipw.println();
+    }
+
+    /**
+     * Dumps the content of all dumpables to the given {@code writer}.
+     */
+    public void dumpAllDumpables(String prefix, PrintWriter writer, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+        int size = dumpNumberDumpables(ipw);
+        if (size == 0) {
+            ipw.println();
+            return;
+        }
+        ipw.println(": ");
+
+        for (int i = 0; i < size; i++) {
+            String dumpableName = mDumpables.keyAt(i);
+            ipw.print('#'); ipw.print(i); ipw.print(": "); ipw.println(dumpableName);
+            Dumpable dumpable = mDumpables.valueAt(i);
+            indentAndDump(ipw, dumpable, args);
+        }
+    }
+
+    private void indentAndDump(IndentingPrintWriter writer, Dumpable dumpable, String[] args) {
+        writer.increaseIndent();
+        try {
+            dumpable.dump(writer, args);
+        } finally {
+            writer.decreaseIndent();
+        }
+    }
+
+    /**
+     * Dumps the content of a specific dumpable to the given {@code writer}.
+     */
+    @SuppressWarnings("resource") // cannot close ipw as it would close writer
+    public void dumpOneDumpable(String prefix, PrintWriter writer, String dumpableName,
+            String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+        Dumpable dumpable = mDumpables.get(dumpableName);
+        if (dumpable == null) {
+            ipw.print("No "); ipw.println(dumpableName);
+            return;
+        }
+        ipw.print(dumpableName); ipw.println(':');
+        indentAndDump(ipw, dumpable, args);
+    }
+}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 36913b7..06e69f2 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -33,7 +33,7 @@
 
 import com.android.internal.R;
 import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
 import java.util.Arrays;
 import java.util.Objects;
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 139660a..6a626ee6 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -19,6 +19,7 @@
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.view.InputChannel;
+import android.view.MotionEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
@@ -36,7 +37,7 @@
  */
 oneway interface IInputMethod {
     void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
-             int configChanges);
+             int configChanges, boolean stylusHwSupported);
 
     void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
             in IInlineSuggestionsRequestCallback cb);
@@ -52,11 +53,13 @@
 
     void setSessionEnabled(IInputMethodSession session, boolean enabled);
 
-    void revokeSession(IInputMethodSession session);
-
     void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver);
 
     void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
 
     void changeInputMethodSubtype(in InputMethodSubtype subtype);
+
+    void canStartStylusHandwriting(int requestId);
+
+    void startStylusHandwriting(in InputChannel channel, in List<MotionEvent> events);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 2dc7c42c..0df3e87 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -81,4 +81,7 @@
     void startImeTrace();
     // Stops an ime trace.
     void stopImeTrace();
+
+    /** Start Stylus handwriting session **/
+    void startStylusHandwriting(in IInputMethodClient client);
 }
diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
index efd5fb2..4b89bf508 100644
--- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -15,11 +15,12 @@
  */
 package com.android.internal.view;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.view.InputQueue;
 import android.view.PendingInsetsController;
 import android.view.SurfaceHolder;
-import android.view.WindowInsetsController;
+import android.window.WindowOnBackInvokedDispatcher;
 
 /** hahahah */
 public interface RootViewSurfaceTaker {
@@ -30,4 +31,6 @@
     InputQueue.Callback willYouTakeTheInputQueue();
     void onRootViewScrollYChanged(int scrollY);
     @Nullable PendingInsetsController providePendingInsetsController();
+    /** @hide */
+    @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher();
 }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
deleted file mode 100644
index a0bf9b5..0000000
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.Window;
-import android.widget.PopupWindow;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A floating toolbar for showing contextual menu items.
- * This view shows as many menu item buttons as can fit in the horizontal toolbar and the
- * the remaining menu items in a vertical overflow view when the overflow button is clicked.
- * The horizontal toolbar morphs into the vertical overflow view.
- */
-public final class FloatingToolbar {
-
-    // This class is responsible for the public API of the floating toolbar.
-    // It delegates rendering operations to the FloatingToolbarPopup.
-
-    public static final String FLOATING_TOOLBAR_TAG = "floating_toolbar";
-
-    private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
-            item -> false;
-
-    private final Window mWindow;
-    private final FloatingToolbarPopup mPopup;
-
-    private final Rect mContentRect = new Rect();
-    private final Rect mPreviousContentRect = new Rect();
-
-    private Menu mMenu;
-    private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
-
-    private int mSuggestedWidth;
-    private boolean mWidthChanged = true;
-
-    private final OnLayoutChangeListener mOrientationChangeHandler = new OnLayoutChangeListener() {
-
-        private final Rect mNewRect = new Rect();
-        private final Rect mOldRect = new Rect();
-
-        @Override
-        public void onLayoutChange(
-                View view,
-                int newLeft, int newRight, int newTop, int newBottom,
-                int oldLeft, int oldRight, int oldTop, int oldBottom) {
-            mNewRect.set(newLeft, newRight, newTop, newBottom);
-            mOldRect.set(oldLeft, oldRight, oldTop, oldBottom);
-            if (mPopup.isShowing() && !mNewRect.equals(mOldRect)) {
-                mWidthChanged = true;
-                updateLayout();
-            }
-        }
-    };
-
-    /**
-     * Sorts the list of menu items to conform to certain requirements.
-     */
-    private final Comparator<MenuItem> mMenuItemComparator = (menuItem1, menuItem2) -> {
-        // Ensure the assist menu item is always the first item:
-        if (menuItem1.getItemId() == android.R.id.textAssist) {
-            return menuItem2.getItemId() == android.R.id.textAssist ? 0 : -1;
-        }
-        if (menuItem2.getItemId() == android.R.id.textAssist) {
-            return 1;
-        }
-
-        // Order by SHOW_AS_ACTION type:
-        if (menuItem1.requiresActionButton()) {
-            return menuItem2.requiresActionButton() ? 0 : -1;
-        }
-        if (menuItem2.requiresActionButton()) {
-            return 1;
-        }
-        if (menuItem1.requiresOverflow()) {
-            return menuItem2.requiresOverflow() ? 0 : 1;
-        }
-        if (menuItem2.requiresOverflow()) {
-            return -1;
-        }
-
-        // Order by order value:
-        return menuItem1.getOrder() - menuItem2.getOrder();
-    };
-
-    /**
-     * Initializes a floating toolbar.
-     */
-    public FloatingToolbar(Window window) {
-        // TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
-        // supports multi-display.
-        mWindow = Objects.requireNonNull(window);
-        mPopup = new FloatingToolbarPopup(window.getContext(), window.getDecorView());
-    }
-
-    /**
-     * Sets the menu to be shown in this floating toolbar.
-     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
-     * toolbar.
-     */
-    public FloatingToolbar setMenu(Menu menu) {
-        mMenu = Objects.requireNonNull(menu);
-        return this;
-    }
-
-    /**
-     * Sets the custom listener for invocation of menu items in this floating toolbar.
-     */
-    public FloatingToolbar setOnMenuItemClickListener(
-            MenuItem.OnMenuItemClickListener menuItemClickListener) {
-        if (menuItemClickListener != null) {
-            mMenuItemClickListener = menuItemClickListener;
-        } else {
-            mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
-        }
-        return this;
-    }
-
-    /**
-     * Sets the content rectangle. This is the area of the interesting content that this toolbar
-     * should avoid obstructing.
-     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
-     * toolbar.
-     */
-    public FloatingToolbar setContentRect(Rect rect) {
-        mContentRect.set(Objects.requireNonNull(rect));
-        return this;
-    }
-
-    /**
-     * Sets the suggested width of this floating toolbar.
-     * The actual width will be about this size but there are no guarantees that it will be exactly
-     * the suggested width.
-     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
-     * toolbar.
-     */
-    public FloatingToolbar setSuggestedWidth(int suggestedWidth) {
-        // Check if there's been a substantial width spec change.
-        int difference = Math.abs(suggestedWidth - mSuggestedWidth);
-        mWidthChanged = difference > (mSuggestedWidth * 0.2);
-
-        mSuggestedWidth = suggestedWidth;
-        return this;
-    }
-
-    /**
-     * Shows this floating toolbar.
-     */
-    public FloatingToolbar show() {
-        registerOrientationHandler();
-        doShow();
-        return this;
-    }
-
-    /**
-     * Updates this floating toolbar to reflect recent position and view updates.
-     * NOTE: This method is a no-op if the toolbar isn't showing.
-     */
-    public FloatingToolbar updateLayout() {
-        if (mPopup.isShowing()) {
-            doShow();
-        }
-        return this;
-    }
-
-    /**
-     * Dismisses this floating toolbar.
-     */
-    public void dismiss() {
-        unregisterOrientationHandler();
-        mPopup.dismiss();
-    }
-
-    /**
-     * Hides this floating toolbar. This is a no-op if the toolbar is not showing.
-     * Use {@link #isHidden()} to distinguish between a hidden and a dismissed toolbar.
-     */
-    public void hide() {
-        mPopup.hide();
-    }
-
-    /**
-     * Returns {@code true} if this toolbar is currently showing. {@code false} otherwise.
-     */
-    public boolean isShowing() {
-        return mPopup.isShowing();
-    }
-
-    /**
-     * Returns {@code true} if this toolbar is currently hidden. {@code false} otherwise.
-     */
-    public boolean isHidden() {
-        return mPopup.isHidden();
-    }
-
-    /**
-     * If this is set to true, the action mode view will dismiss itself on touch events outside of
-     * its window. The setting takes effect immediately.
-     *
-     * @param outsideTouchable whether or not this action mode is "outside touchable"
-     * @param onDismiss optional. Sets a callback for when this action mode popup dismisses itself
-     */
-    public void setOutsideTouchable(
-            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
-        mPopup.setOutsideTouchable(outsideTouchable, onDismiss);
-    }
-
-    private void doShow() {
-        List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
-        menuItems.sort(mMenuItemComparator);
-        if (mPopup.isLayoutRequired(menuItems) || mWidthChanged) {
-            mPopup.dismiss();
-            mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
-        } else {
-            mPopup.updateMenuItems(menuItems, mMenuItemClickListener);
-        }
-        if (!mPopup.isShowing()) {
-            mPopup.show(mContentRect);
-        } else if (!mPreviousContentRect.equals(mContentRect)) {
-            mPopup.updateCoordinates(mContentRect);
-        }
-        mWidthChanged = false;
-        mPreviousContentRect.set(mContentRect);
-    }
-
-    /**
-     * Returns the visible and enabled menu items in the specified menu.
-     * This method is recursive.
-     */
-    private static List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) {
-        List<MenuItem> menuItems = new ArrayList<>();
-        for (int i = 0; (menu != null) && (i < menu.size()); i++) {
-            MenuItem menuItem = menu.getItem(i);
-            if (menuItem.isVisible() && menuItem.isEnabled()) {
-                Menu subMenu = menuItem.getSubMenu();
-                if (subMenu != null) {
-                    menuItems.addAll(getVisibleAndEnabledMenuItems(subMenu));
-                } else {
-                    menuItems.add(menuItem);
-                }
-            }
-        }
-        return menuItems;
-    }
-
-    private void registerOrientationHandler() {
-        unregisterOrientationHandler();
-        mWindow.getDecorView().addOnLayoutChangeListener(mOrientationChangeHandler);
-    }
-
-    private void unregisterOrientationHandler() {
-        mWindow.getDecorView().removeOnLayoutChangeListener(mOrientationChangeHandler);
-    }
-}
diff --git a/core/java/com/android/internal/widget/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/FloatingToolbarPopup.java
deleted file mode 100644
index e0388f6..0000000
--- a/core/java/com/android/internal/widget/FloatingToolbarPopup.java
+++ /dev/null
@@ -1,1610 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Size;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.Transformation;
-import android.widget.ArrayAdapter;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.PopupWindow;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * A popup window used by the floating toolbar.
- *
- * This class is responsible for the rendering/animation of the floating toolbar.
- * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
- * to transition between panels.
- */
-public final class FloatingToolbarPopup {
-
-    /* Minimum and maximum number of items allowed in the overflow. */
-    private static final int MIN_OVERFLOW_SIZE = 2;
-    private static final int MAX_OVERFLOW_SIZE = 4;
-
-    private final Context mContext;
-    private final View mParent;  // Parent for the popup window.
-    private final PopupWindow mPopupWindow;
-
-    /* Margins between the popup window and its content. */
-    private final int mMarginHorizontal;
-    private final int mMarginVertical;
-
-    /* View components */
-    private final ViewGroup mContentContainer;  // holds all contents.
-    private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
-    // holds menu items hidden in the overflow.
-    private final FloatingToolbarPopup.OverflowPanel mOverflowPanel;
-    private final ImageButton mOverflowButton;  // opens/closes the overflow.
-    /* overflow button drawables. */
-    private final Drawable mArrow;
-    private final Drawable mOverflow;
-    private final AnimatedVectorDrawable mToArrow;
-    private final AnimatedVectorDrawable mToOverflow;
-
-    private final FloatingToolbarPopup.OverflowPanelViewHelper
-            mOverflowPanelViewHelper;
-
-    /* Animation interpolators. */
-    private final Interpolator mLogAccelerateInterpolator;
-    private final Interpolator mFastOutSlowInInterpolator;
-    private final Interpolator mLinearOutSlowInInterpolator;
-    private final Interpolator mFastOutLinearInInterpolator;
-
-    /* Animations. */
-    private final AnimatorSet mShowAnimation;
-    private final AnimatorSet mDismissAnimation;
-    private final AnimatorSet mHideAnimation;
-    private final AnimationSet mOpenOverflowAnimation;
-    private final AnimationSet mCloseOverflowAnimation;
-    private final Animation.AnimationListener mOverflowAnimationListener;
-
-    private final Rect mViewPortOnScreen = new Rect();  // portion of screen we can draw in.
-    private final Point mCoordsOnWindow = new Point();  // popup window coordinates.
-    /* Temporary data holders. Reset values before using. */
-    private final int[] mTmpCoords = new int[2];
-
-    private final Region mTouchableRegion = new Region();
-    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
-            info -> {
-                info.contentInsets.setEmpty();
-                info.visibleInsets.setEmpty();
-                info.touchableRegion.set(mTouchableRegion);
-                info.setTouchableInsets(
-                        ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            };
-
-    private final int mLineHeight;
-    private final int mIconTextSpacing;
-
-    /**
-     * @see FloatingToolbarPopup.OverflowPanelViewHelper#preparePopupContent().
-     */
-    private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
-        @Override
-        public void run() {
-            setPanelsStatesAtRestingPosition();
-            setContentAreaAsTouchableSurface();
-            mContentContainer.setAlpha(1);
-        }
-    };
-
-    private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
-    private boolean mHidden; // tracks whether this popup is hidden or hiding.
-
-    /* Calculated sizes for panels and overflow button. */
-    private final Size mOverflowButtonSize;
-    private Size mOverflowPanelSize;  // Should be null when there is no overflow.
-    private Size mMainPanelSize;
-
-    /* Menu items and click listeners */
-    private final Map<MenuItemRepr, MenuItem> mMenuItems = new LinkedHashMap<>();
-    private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
-    private final View.OnClickListener mMenuItemButtonOnClickListener =
-            new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mOnMenuItemClickListener == null) {
-                        return;
-                    }
-                    final Object tag = v.getTag();
-                    if (!(tag instanceof MenuItemRepr)) {
-                        return;
-                    }
-                    final MenuItem menuItem = mMenuItems.get((MenuItemRepr) tag);
-                    if (menuItem == null) {
-                        return;
-                    }
-                    mOnMenuItemClickListener.onMenuItemClick(menuItem);
-                }
-            };
-
-    private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
-    private boolean mIsOverflowOpen;
-
-    private int mTransitionDurationScale;  // Used to scale the toolbar transition duration.
-
-    /**
-     * Initializes a new floating toolbar popup.
-     *
-     * @param parent  A parent view to get the {@link android.view.View#getWindowToken()} token
-     *      from.
-     */
-    public FloatingToolbarPopup(Context context, View parent) {
-        mParent = Objects.requireNonNull(parent);
-        mContext = applyDefaultTheme(context);
-        mContentContainer = createContentContainer(context);
-        mPopupWindow = createPopupWindow(mContentContainer);
-        mMarginHorizontal = parent.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-        mMarginVertical = parent.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
-        mLineHeight = context.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_height);
-        mIconTextSpacing = context.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
-
-        // Interpolators
-        mLogAccelerateInterpolator = new FloatingToolbarPopup.LogAccelerateInterpolator();
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.fast_out_slow_in);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.linear_out_slow_in);
-        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.fast_out_linear_in);
-
-        // Drawables. Needed for views.
-        mArrow = mContext.getResources()
-                .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme());
-        mArrow.setAutoMirrored(true);
-        mOverflow = mContext.getResources()
-                .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme());
-        mOverflow.setAutoMirrored(true);
-        mToArrow = (AnimatedVectorDrawable) mContext.getResources()
-                .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme());
-        mToArrow.setAutoMirrored(true);
-        mToOverflow = (AnimatedVectorDrawable) mContext.getResources()
-                .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme());
-        mToOverflow.setAutoMirrored(true);
-
-        // Views
-        mOverflowButton = createOverflowButton();
-        mOverflowButtonSize = measure(mOverflowButton);
-        mMainPanel = createMainPanel();
-        mOverflowPanelViewHelper =
-                new FloatingToolbarPopup.OverflowPanelViewHelper(mContext, mIconTextSpacing);
-        mOverflowPanel = createOverflowPanel();
-
-        // Animation. Need views.
-        mOverflowAnimationListener = createOverflowAnimationListener();
-        mOpenOverflowAnimation = new AnimationSet(true);
-        mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
-        mCloseOverflowAnimation = new AnimationSet(true);
-        mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
-        mShowAnimation = createEnterAnimation(mContentContainer);
-        mDismissAnimation = createExitAnimation(
-                mContentContainer,
-                150,  // startDelay
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mPopupWindow.dismiss();
-                        mContentContainer.removeAllViews();
-                    }
-                });
-        mHideAnimation = createExitAnimation(
-                mContentContainer,
-                0,  // startDelay
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mPopupWindow.dismiss();
-                    }
-                });
-    }
-
-    /**
-     * Makes this toolbar "outside touchable" and sets the onDismissListener.
-     *
-     * @param outsideTouchable if true, the popup will be made "outside touchable" and
-     *      "non focusable". The reverse will happen if false.
-     * @param onDismiss
-     *
-     * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
-     *
-     * @see PopupWindow#setOutsideTouchable(boolean)
-     * @see PopupWindow#setFocusable(boolean)
-     * @see PopupWindow.OnDismissListener
-     */
-    public boolean setOutsideTouchable(
-            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
-        boolean ret = false;
-        if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
-            mPopupWindow.setOutsideTouchable(outsideTouchable);
-            mPopupWindow.setFocusable(!outsideTouchable);
-            mPopupWindow.update();
-            ret = true;
-        }
-        mPopupWindow.setOnDismissListener(onDismiss);
-        return ret;
-    }
-
-    /**
-     * Lays out buttons for the specified menu items.
-     * Requires a subsequent call to {@link FloatingToolbar#show()} to show the items.
-     */
-    public void layoutMenuItems(
-            List<MenuItem> menuItems,
-            MenuItem.OnMenuItemClickListener menuItemClickListener,
-            int suggestedWidth) {
-        cancelOverflowAnimations();
-        clearPanels();
-        updateMenuItems(menuItems, menuItemClickListener);
-        menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
-        if (!menuItems.isEmpty()) {
-            // Add remaining items to the overflow.
-            layoutOverflowPanelItems(menuItems);
-        }
-        updatePopupSize();
-    }
-
-    /**
-     * Updates the popup's menu items without rebuilding the widget.
-     * Use in place of layoutMenuItems() when the popup's views need not be reconstructed.
-     *
-     * @see #isLayoutRequired(List<MenuItem>)
-     */
-    public void updateMenuItems(
-            List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) {
-        mMenuItems.clear();
-        for (MenuItem menuItem : menuItems) {
-            mMenuItems.put(MenuItemRepr.of(menuItem), menuItem);
-        }
-        mOnMenuItemClickListener = menuItemClickListener;
-    }
-
-    /**
-     * Returns true if this popup needs a relayout to properly render the specified menu items.
-     */
-    public boolean isLayoutRequired(List<MenuItem> menuItems) {
-        return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
-    }
-
-    /**
-     * Shows this popup at the specified coordinates.
-     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
-     */
-    public void show(Rect contentRectOnScreen) {
-        Objects.requireNonNull(contentRectOnScreen);
-
-        if (isShowing()) {
-            return;
-        }
-
-        mHidden = false;
-        mDismissed = false;
-        cancelDismissAndHideAnimations();
-        cancelOverflowAnimations();
-
-        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
-        preparePopupContent();
-        // We need to specify the position in window coordinates.
-        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
-        // specify the popup position in screen coordinates.
-        mPopupWindow.showAtLocation(
-                mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y);
-        setTouchableSurfaceInsetsComputer();
-        runShowAnimation();
-    }
-
-    /**
-     * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
-     */
-    public void dismiss() {
-        if (mDismissed) {
-            return;
-        }
-
-        mHidden = false;
-        mDismissed = true;
-        mHideAnimation.cancel();
-
-        runDismissAnimation();
-        setZeroTouchableSurface();
-    }
-
-    /**
-     * Hides this popup. This is a no-op if this popup is not showing.
-     * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
-     */
-    public void hide() {
-        if (!isShowing()) {
-            return;
-        }
-
-        mHidden = true;
-        runHideAnimation();
-        setZeroTouchableSurface();
-    }
-
-    /**
-     * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
-     */
-    public boolean isShowing() {
-        return !mDismissed && !mHidden;
-    }
-
-    /**
-     * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
-     */
-    public boolean isHidden() {
-        return mHidden;
-    }
-
-    /**
-     * Updates the coordinates of this popup.
-     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
-     * This is a no-op if this popup is not showing.
-     */
-    public void updateCoordinates(Rect contentRectOnScreen) {
-        Objects.requireNonNull(contentRectOnScreen);
-
-        if (!isShowing() || !mPopupWindow.isShowing()) {
-            return;
-        }
-
-        cancelOverflowAnimations();
-        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
-        preparePopupContent();
-        // We need to specify the position in window coordinates.
-        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
-        // specify the popup position in screen coordinates.
-        mPopupWindow.update(
-                mCoordsOnWindow.x, mCoordsOnWindow.y,
-                mPopupWindow.getWidth(), mPopupWindow.getHeight());
-    }
-
-    private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
-        refreshViewPort();
-
-        // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
-        // landscape.
-        final int x = Math.min(
-                contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
-                mViewPortOnScreen.right - mPopupWindow.getWidth());
-
-        final int y;
-
-        final int availableHeightAboveContent =
-                contentRectOnScreen.top - mViewPortOnScreen.top;
-        final int availableHeightBelowContent =
-                mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
-
-        final int margin = 2 * mMarginVertical;
-        final int toolbarHeightWithVerticalMargin = mLineHeight + margin;
-
-        if (!hasOverflow()) {
-            if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) {
-                // There is enough space at the top of the content.
-                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
-            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) {
-                // There is enough space at the bottom of the content.
-                y = contentRectOnScreen.bottom;
-            } else if (availableHeightBelowContent >= mLineHeight) {
-                // Just enough space to fit the toolbar with no vertical margins.
-                y = contentRectOnScreen.bottom - mMarginVertical;
-            } else {
-                // Not enough space. Prefer to position as high as possible.
-                y = Math.max(
-                        mViewPortOnScreen.top,
-                        contentRectOnScreen.top - toolbarHeightWithVerticalMargin);
-            }
-        } else {
-            // Has an overflow.
-            final int minimumOverflowHeightWithMargin =
-                    calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin;
-            final int availableHeightThroughContentDown =
-                    mViewPortOnScreen.bottom - contentRectOnScreen.top
-                            + toolbarHeightWithVerticalMargin;
-            final int availableHeightThroughContentUp =
-                    contentRectOnScreen.bottom - mViewPortOnScreen.top
-                            + toolbarHeightWithVerticalMargin;
-
-            if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
-                // There is enough space at the top of the content rect for the overflow.
-                // Position above and open upwards.
-                updateOverflowHeight(availableHeightAboveContent - margin);
-                y = contentRectOnScreen.top - mPopupWindow.getHeight();
-                mOpenOverflowUpwards = true;
-            } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
-                    && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
-                // There is enough space at the top of the content rect for the main panel
-                // but not the overflow.
-                // Position above but open downwards.
-                updateOverflowHeight(availableHeightThroughContentDown - margin);
-                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
-                mOpenOverflowUpwards = false;
-            } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
-                // There is enough space at the bottom of the content rect for the overflow.
-                // Position below and open downwards.
-                updateOverflowHeight(availableHeightBelowContent - margin);
-                y = contentRectOnScreen.bottom;
-                mOpenOverflowUpwards = false;
-            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin
-                    && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
-                // There is enough space at the bottom of the content rect for the main panel
-                // but not the overflow.
-                // Position below but open upwards.
-                updateOverflowHeight(availableHeightThroughContentUp - margin);
-                y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin
-                        - mPopupWindow.getHeight();
-                mOpenOverflowUpwards = true;
-            } else {
-                // Not enough space.
-                // Position at the top of the view port and open downwards.
-                updateOverflowHeight(mViewPortOnScreen.height() - margin);
-                y = mViewPortOnScreen.top;
-                mOpenOverflowUpwards = false;
-            }
-        }
-
-        // We later specify the location of PopupWindow relative to the attached window.
-        // The idea here is that 1) we can get the location of a View in both window coordinates
-        // and screen coordinates, where the offset between them should be equal to the window
-        // origin, and 2) we can use an arbitrary for this calculation while calculating the
-        // location of the rootview is supposed to be least expensive.
-        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can avoid
-        // the following calculation.
-        mParent.getRootView().getLocationOnScreen(mTmpCoords);
-        int rootViewLeftOnScreen = mTmpCoords[0];
-        int rootViewTopOnScreen = mTmpCoords[1];
-        mParent.getRootView().getLocationInWindow(mTmpCoords);
-        int rootViewLeftOnWindow = mTmpCoords[0];
-        int rootViewTopOnWindow = mTmpCoords[1];
-        int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
-        int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
-        mCoordsOnWindow.set(
-                Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
-    }
-
-    /**
-     * Performs the "show" animation on the floating popup.
-     */
-    private void runShowAnimation() {
-        mShowAnimation.start();
-    }
-
-    /**
-     * Performs the "dismiss" animation on the floating popup.
-     */
-    private void runDismissAnimation() {
-        mDismissAnimation.start();
-    }
-
-    /**
-     * Performs the "hide" animation on the floating popup.
-     */
-    private void runHideAnimation() {
-        mHideAnimation.start();
-    }
-
-    private void cancelDismissAndHideAnimations() {
-        mDismissAnimation.cancel();
-        mHideAnimation.cancel();
-    }
-
-    private void cancelOverflowAnimations() {
-        mContentContainer.clearAnimation();
-        mMainPanel.animate().cancel();
-        mOverflowPanel.animate().cancel();
-        mToArrow.stop();
-        mToOverflow.stop();
-    }
-
-    private void openOverflow() {
-        final int targetWidth = mOverflowPanelSize.getWidth();
-        final int targetHeight = mOverflowPanelSize.getHeight();
-        final int startWidth = mContentContainer.getWidth();
-        final int startHeight = mContentContainer.getHeight();
-        final float startY = mContentContainer.getY();
-        final float left = mContentContainer.getX();
-        final float right = left + mContentContainer.getWidth();
-        Animation widthAnimation = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
-                setWidth(mContentContainer, startWidth + deltaWidth);
-                if (isInRTLMode()) {
-                    mContentContainer.setX(left);
-
-                    // Lock the panels in place.
-                    mMainPanel.setX(0);
-                    mOverflowPanel.setX(0);
-                } else {
-                    mContentContainer.setX(right - mContentContainer.getWidth());
-
-                    // Offset the panels' positions so they look like they're locked in place
-                    // on the screen.
-                    mMainPanel.setX(mContentContainer.getWidth() - startWidth);
-                    mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth);
-                }
-            }
-        };
-        Animation heightAnimation = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
-                setHeight(mContentContainer, startHeight + deltaHeight);
-                if (mOpenOverflowUpwards) {
-                    mContentContainer.setY(
-                            startY - (mContentContainer.getHeight() - startHeight));
-                    positionContentYCoordinatesIfOpeningOverflowUpwards();
-                }
-            }
-        };
-        final float overflowButtonStartX = mOverflowButton.getX();
-        final float overflowButtonTargetX =
-                isInRTLMode() ? overflowButtonStartX + targetWidth - mOverflowButton.getWidth()
-                        : overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
-        Animation overflowButtonAnimation = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                float overflowButtonX = overflowButtonStartX
-                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
-                float deltaContainerWidth =
-                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
-                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
-                mOverflowButton.setX(actualOverflowButtonX);
-            }
-        };
-        widthAnimation.setInterpolator(mLogAccelerateInterpolator);
-        widthAnimation.setDuration(getAdjustedDuration(250));
-        heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        heightAnimation.setDuration(getAdjustedDuration(250));
-        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
-        mOpenOverflowAnimation.getAnimations().clear();
-        mOpenOverflowAnimation.getAnimations().clear();
-        mOpenOverflowAnimation.addAnimation(widthAnimation);
-        mOpenOverflowAnimation.addAnimation(heightAnimation);
-        mOpenOverflowAnimation.addAnimation(overflowButtonAnimation);
-        mContentContainer.startAnimation(mOpenOverflowAnimation);
-        mIsOverflowOpen = true;
-        mMainPanel.animate()
-                .alpha(0).withLayer()
-                .setInterpolator(mLinearOutSlowInInterpolator)
-                .setDuration(250)
-                .start();
-        mOverflowPanel.setAlpha(1); // fadeIn in 0ms.
-    }
-
-    private void closeOverflow() {
-        final int targetWidth = mMainPanelSize.getWidth();
-        final int startWidth = mContentContainer.getWidth();
-        final float left = mContentContainer.getX();
-        final float right = left + mContentContainer.getWidth();
-        Animation widthAnimation = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
-                setWidth(mContentContainer, startWidth + deltaWidth);
-                if (isInRTLMode()) {
-                    mContentContainer.setX(left);
-
-                    // Lock the panels in place.
-                    mMainPanel.setX(0);
-                    mOverflowPanel.setX(0);
-                } else {
-                    mContentContainer.setX(right - mContentContainer.getWidth());
-
-                    // Offset the panels' positions so they look like they're locked in place
-                    // on the screen.
-                    mMainPanel.setX(mContentContainer.getWidth() - targetWidth);
-                    mOverflowPanel.setX(mContentContainer.getWidth() - startWidth);
-                }
-            }
-        };
-        final int targetHeight = mMainPanelSize.getHeight();
-        final int startHeight = mContentContainer.getHeight();
-        final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
-        Animation heightAnimation = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
-                setHeight(mContentContainer, startHeight + deltaHeight);
-                if (mOpenOverflowUpwards) {
-                    mContentContainer.setY(bottom - mContentContainer.getHeight());
-                    positionContentYCoordinatesIfOpeningOverflowUpwards();
-                }
-            }
-        };
-        final float overflowButtonStartX = mOverflowButton.getX();
-        final float overflowButtonTargetX =
-                isInRTLMode() ? overflowButtonStartX - startWidth + mOverflowButton.getWidth()
-                        : overflowButtonStartX + startWidth - mOverflowButton.getWidth();
-        Animation overflowButtonAnimation = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                float overflowButtonX = overflowButtonStartX
-                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
-                float deltaContainerWidth =
-                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
-                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
-                mOverflowButton.setX(actualOverflowButtonX);
-            }
-        };
-        widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        widthAnimation.setDuration(getAdjustedDuration(250));
-        heightAnimation.setInterpolator(mLogAccelerateInterpolator);
-        heightAnimation.setDuration(getAdjustedDuration(250));
-        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
-        mCloseOverflowAnimation.getAnimations().clear();
-        mCloseOverflowAnimation.addAnimation(widthAnimation);
-        mCloseOverflowAnimation.addAnimation(heightAnimation);
-        mCloseOverflowAnimation.addAnimation(overflowButtonAnimation);
-        mContentContainer.startAnimation(mCloseOverflowAnimation);
-        mIsOverflowOpen = false;
-        mMainPanel.animate()
-                .alpha(1).withLayer()
-                .setInterpolator(mFastOutLinearInInterpolator)
-                .setDuration(100)
-                .start();
-        mOverflowPanel.animate()
-                .alpha(0).withLayer()
-                .setInterpolator(mLinearOutSlowInInterpolator)
-                .setDuration(150)
-                .start();
-    }
-
-    /**
-     * Defines the position of the floating toolbar popup panels when transition animation has
-     * stopped.
-     */
-    private void setPanelsStatesAtRestingPosition() {
-        mOverflowButton.setEnabled(true);
-        mOverflowPanel.awakenScrollBars();
-
-        if (mIsOverflowOpen) {
-            // Set open state.
-            final Size containerSize = mOverflowPanelSize;
-            setSize(mContentContainer, containerSize);
-            mMainPanel.setAlpha(0);
-            mMainPanel.setVisibility(View.INVISIBLE);
-            mOverflowPanel.setAlpha(1);
-            mOverflowPanel.setVisibility(View.VISIBLE);
-            mOverflowButton.setImageDrawable(mArrow);
-            mOverflowButton.setContentDescription(mContext.getString(
-                    R.string.floating_toolbar_close_overflow_description));
-
-            // Update x-coordinates depending on RTL state.
-            if (isInRTLMode()) {
-                mContentContainer.setX(mMarginHorizontal);  // align left
-                mMainPanel.setX(0);  // align left
-                mOverflowButton.setX(// align right
-                        containerSize.getWidth() - mOverflowButtonSize.getWidth());
-                mOverflowPanel.setX(0);  // align left
-            } else {
-                mContentContainer.setX(// align right
-                        mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
-                mMainPanel.setX(-mContentContainer.getX());  // align right
-                mOverflowButton.setX(0);  // align left
-                mOverflowPanel.setX(0);  // align left
-            }
-
-            // Update y-coordinates depending on overflow's open direction.
-            if (mOpenOverflowUpwards) {
-                mContentContainer.setY(mMarginVertical);  // align top
-                mMainPanel.setY(// align bottom
-                        containerSize.getHeight() - mContentContainer.getHeight());
-                mOverflowButton.setY(// align bottom
-                        containerSize.getHeight() - mOverflowButtonSize.getHeight());
-                mOverflowPanel.setY(0);  // align top
-            } else {
-                // opens downwards.
-                mContentContainer.setY(mMarginVertical);  // align top
-                mMainPanel.setY(0);  // align top
-                mOverflowButton.setY(0);  // align top
-                mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
-            }
-        } else {
-            // Overflow not open. Set closed state.
-            final Size containerSize = mMainPanelSize;
-            setSize(mContentContainer, containerSize);
-            mMainPanel.setAlpha(1);
-            mMainPanel.setVisibility(View.VISIBLE);
-            mOverflowPanel.setAlpha(0);
-            mOverflowPanel.setVisibility(View.INVISIBLE);
-            mOverflowButton.setImageDrawable(mOverflow);
-            mOverflowButton.setContentDescription(mContext.getString(
-                    R.string.floating_toolbar_open_overflow_description));
-
-            if (hasOverflow()) {
-                // Update x-coordinates depending on RTL state.
-                if (isInRTLMode()) {
-                    mContentContainer.setX(mMarginHorizontal);  // align left
-                    mMainPanel.setX(0);  // align left
-                    mOverflowButton.setX(0);  // align left
-                    mOverflowPanel.setX(0);  // align left
-                } else {
-                    mContentContainer.setX(// align right
-                            mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
-                    mMainPanel.setX(0);  // align left
-                    mOverflowButton.setX(// align right
-                            containerSize.getWidth() - mOverflowButtonSize.getWidth());
-                    mOverflowPanel.setX(// align right
-                            containerSize.getWidth() - mOverflowPanelSize.getWidth());
-                }
-
-                // Update y-coordinates depending on overflow's open direction.
-                if (mOpenOverflowUpwards) {
-                    mContentContainer.setY(// align bottom
-                            mMarginVertical + mOverflowPanelSize.getHeight()
-                                    - containerSize.getHeight());
-                    mMainPanel.setY(0);  // align top
-                    mOverflowButton.setY(0);  // align top
-                    mOverflowPanel.setY(// align bottom
-                            containerSize.getHeight() - mOverflowPanelSize.getHeight());
-                } else {
-                    // opens downwards.
-                    mContentContainer.setY(mMarginVertical);  // align top
-                    mMainPanel.setY(0);  // align top
-                    mOverflowButton.setY(0);  // align top
-                    mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
-                }
-            } else {
-                // No overflow.
-                mContentContainer.setX(mMarginHorizontal);  // align left
-                mContentContainer.setY(mMarginVertical);  // align top
-                mMainPanel.setX(0);  // align left
-                mMainPanel.setY(0);  // align top
-            }
-        }
-    }
-
-    private void updateOverflowHeight(int suggestedHeight) {
-        if (hasOverflow()) {
-            final int maxItemSize =
-                    (suggestedHeight - mOverflowButtonSize.getHeight()) / mLineHeight;
-            final int newHeight = calculateOverflowHeight(maxItemSize);
-            if (mOverflowPanelSize.getHeight() != newHeight) {
-                mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight);
-            }
-            setSize(mOverflowPanel, mOverflowPanelSize);
-            if (mIsOverflowOpen) {
-                setSize(mContentContainer, mOverflowPanelSize);
-                if (mOpenOverflowUpwards) {
-                    final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight;
-                    mContentContainer.setY(mContentContainer.getY() + deltaHeight);
-                    mOverflowButton.setY(mOverflowButton.getY() - deltaHeight);
-                }
-            } else {
-                setSize(mContentContainer, mMainPanelSize);
-            }
-            updatePopupSize();
-        }
-    }
-
-    private void updatePopupSize() {
-        int width = 0;
-        int height = 0;
-        if (mMainPanelSize != null) {
-            width = Math.max(width, mMainPanelSize.getWidth());
-            height = Math.max(height, mMainPanelSize.getHeight());
-        }
-        if (mOverflowPanelSize != null) {
-            width = Math.max(width, mOverflowPanelSize.getWidth());
-            height = Math.max(height, mOverflowPanelSize.getHeight());
-        }
-        mPopupWindow.setWidth(width + mMarginHorizontal * 2);
-        mPopupWindow.setHeight(height + mMarginVertical * 2);
-        maybeComputeTransitionDurationScale();
-    }
-
-    private void refreshViewPort() {
-        mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
-    }
-
-    private int getAdjustedToolbarWidth(int suggestedWidth) {
-        int width = suggestedWidth;
-        refreshViewPort();
-        int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-        if (width <= 0) {
-            width = mParent.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
-        }
-        return Math.min(width, maximumWidth);
-    }
-
-    /**
-     * Sets the touchable region of this popup to be zero. This means that all touch events on
-     * this popup will go through to the surface behind it.
-     */
-    private void setZeroTouchableSurface() {
-        mTouchableRegion.setEmpty();
-    }
-
-    /**
-     * Sets the touchable region of this popup to be the area occupied by its content.
-     */
-    private void setContentAreaAsTouchableSurface() {
-        Objects.requireNonNull(mMainPanelSize);
-        final int width;
-        final int height;
-        if (mIsOverflowOpen) {
-            Objects.requireNonNull(mOverflowPanelSize);
-            width = mOverflowPanelSize.getWidth();
-            height = mOverflowPanelSize.getHeight();
-        } else {
-            width = mMainPanelSize.getWidth();
-            height = mMainPanelSize.getHeight();
-        }
-        mTouchableRegion.set(
-                (int) mContentContainer.getX(),
-                (int) mContentContainer.getY(),
-                (int) mContentContainer.getX() + width,
-                (int) mContentContainer.getY() + height);
-    }
-
-    /**
-     * Make the touchable area of this popup be the area specified by mTouchableRegion.
-     * This should be called after the popup window has been dismissed (dismiss/hide)
-     * and is probably being re-shown with a new content root view.
-     */
-    private void setTouchableSurfaceInsetsComputer() {
-        ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
-                .getRootView()
-                .getViewTreeObserver();
-        viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
-        viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
-    }
-
-    private boolean isInRTLMode() {
-        return mContext.getApplicationInfo().hasRtlSupport()
-                && mContext.getResources().getConfiguration().getLayoutDirection()
-                == View.LAYOUT_DIRECTION_RTL;
-    }
-
-    private boolean hasOverflow() {
-        return mOverflowPanelSize != null;
-    }
-
-    /**
-     * Fits as many menu items in the main panel and returns a list of the menu items that
-     * were not fit in.
-     *
-     * @return The menu items that are not included in this main panel.
-     */
-    public List<MenuItem> layoutMainPanelItems(
-            List<MenuItem> menuItems, final int toolbarWidth) {
-        Objects.requireNonNull(menuItems);
-
-        int availableWidth = toolbarWidth;
-
-        final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>();
-        // add the overflow menu items to the end of the remainingMenuItems list.
-        final LinkedList<MenuItem> overflowMenuItems = new LinkedList();
-        for (MenuItem menuItem : menuItems) {
-            if (menuItem.getItemId() != android.R.id.textAssist
-                    && menuItem.requiresOverflow()) {
-                overflowMenuItems.add(menuItem);
-            } else {
-                remainingMenuItems.add(menuItem);
-            }
-        }
-        remainingMenuItems.addAll(overflowMenuItems);
-
-        mMainPanel.removeAllViews();
-        mMainPanel.setPaddingRelative(0, 0, 0, 0);
-
-        int lastGroupId = -1;
-        boolean isFirstItem = true;
-        while (!remainingMenuItems.isEmpty()) {
-            final MenuItem menuItem = remainingMenuItems.peek();
-
-            // if this is the first item, regardless of requiresOverflow(), it should be
-            // displayed on the main panel. Otherwise all items including this one will be
-            // overflow items, and should be displayed in overflow panel.
-            if (!isFirstItem && menuItem.requiresOverflow()) {
-                break;
-            }
-
-            final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
-            final View menuItemButton = createMenuItemButton(
-                    mContext, menuItem, mIconTextSpacing, showIcon);
-            if (!showIcon && menuItemButton instanceof LinearLayout) {
-                ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
-            }
-
-            // Adding additional start padding for the first button to even out button spacing.
-            if (isFirstItem) {
-                menuItemButton.setPaddingRelative(
-                        (int) (1.5 * menuItemButton.getPaddingStart()),
-                        menuItemButton.getPaddingTop(),
-                        menuItemButton.getPaddingEnd(),
-                        menuItemButton.getPaddingBottom());
-            }
-
-            // Adding additional end padding for the last button to even out button spacing.
-            boolean isLastItem = remainingMenuItems.size() == 1;
-            if (isLastItem) {
-                menuItemButton.setPaddingRelative(
-                        menuItemButton.getPaddingStart(),
-                        menuItemButton.getPaddingTop(),
-                        (int) (1.5 * menuItemButton.getPaddingEnd()),
-                        menuItemButton.getPaddingBottom());
-            }
-
-            menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-            final int menuItemButtonWidth = Math.min(
-                    menuItemButton.getMeasuredWidth(), toolbarWidth);
-
-            // Check if we can fit an item while reserving space for the overflowButton.
-            final boolean canFitWithOverflow =
-                    menuItemButtonWidth <= availableWidth - mOverflowButtonSize.getWidth();
-            final boolean canFitNoOverflow =
-                    isLastItem && menuItemButtonWidth <= availableWidth;
-            if (canFitWithOverflow || canFitNoOverflow) {
-                setButtonTagAndClickListener(menuItemButton, menuItem);
-                // Set tooltips for main panel items, but not overflow items (b/35726766).
-                menuItemButton.setTooltipText(menuItem.getTooltipText());
-                mMainPanel.addView(menuItemButton);
-                final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
-                params.width = menuItemButtonWidth;
-                menuItemButton.setLayoutParams(params);
-                availableWidth -= menuItemButtonWidth;
-                remainingMenuItems.pop();
-            } else {
-                break;
-            }
-            lastGroupId = menuItem.getGroupId();
-            isFirstItem = false;
-        }
-
-        if (!remainingMenuItems.isEmpty()) {
-            // Reserve space for overflowButton.
-            mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0);
-        }
-
-        mMainPanelSize = measure(mMainPanel);
-        return remainingMenuItems;
-    }
-
-    private void layoutOverflowPanelItems(List<MenuItem> menuItems) {
-        ArrayAdapter<MenuItem> overflowPanelAdapter =
-                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
-        overflowPanelAdapter.clear();
-        final int size = menuItems.size();
-        for (int i = 0; i < size; i++) {
-            overflowPanelAdapter.add(menuItems.get(i));
-        }
-        mOverflowPanel.setAdapter(overflowPanelAdapter);
-        if (mOpenOverflowUpwards) {
-            mOverflowPanel.setY(0);
-        } else {
-            mOverflowPanel.setY(mOverflowButtonSize.getHeight());
-        }
-
-        int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
-        int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
-        mOverflowPanelSize = new Size(width, height);
-        setSize(mOverflowPanel, mOverflowPanelSize);
-    }
-
-    /**
-     * Resets the content container and appropriately position it's panels.
-     */
-    private void preparePopupContent() {
-        mContentContainer.removeAllViews();
-
-        // Add views in the specified order so they stack up as expected.
-        // Order: overflowPanel, mainPanel, overflowButton.
-        if (hasOverflow()) {
-            mContentContainer.addView(mOverflowPanel);
-        }
-        mContentContainer.addView(mMainPanel);
-        if (hasOverflow()) {
-            mContentContainer.addView(mOverflowButton);
-        }
-        setPanelsStatesAtRestingPosition();
-        setContentAreaAsTouchableSurface();
-
-        // The positioning of contents in RTL is wrong when the view is first rendered.
-        // Hide the view and post a runnable to recalculate positions and render the view.
-        // TODO: Investigate why this happens and fix.
-        if (isInRTLMode()) {
-            mContentContainer.setAlpha(0);
-            mContentContainer.post(mPreparePopupContentRTLHelper);
-        }
-    }
-
-    /**
-     * Clears out the panels and their container. Resets their calculated sizes.
-     */
-    private void clearPanels() {
-        mOverflowPanelSize = null;
-        mMainPanelSize = null;
-        mIsOverflowOpen = false;
-        mMainPanel.removeAllViews();
-        ArrayAdapter<MenuItem> overflowPanelAdapter =
-                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
-        overflowPanelAdapter.clear();
-        mOverflowPanel.setAdapter(overflowPanelAdapter);
-        mContentContainer.removeAllViews();
-    }
-
-    private void positionContentYCoordinatesIfOpeningOverflowUpwards() {
-        if (mOpenOverflowUpwards) {
-            mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight());
-            mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight());
-            mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight());
-        }
-    }
-
-    private int getOverflowWidth() {
-        int overflowWidth = 0;
-        final int count = mOverflowPanel.getAdapter().getCount();
-        for (int i = 0; i < count; i++) {
-            MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
-            overflowWidth =
-                    Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
-        }
-        return overflowWidth;
-    }
-
-    private int calculateOverflowHeight(int maxItemSize) {
-        // Maximum of 4 items, minimum of 2 if the overflow has to scroll.
-        int actualSize = Math.min(
-                MAX_OVERFLOW_SIZE,
-                Math.min(
-                        Math.max(MIN_OVERFLOW_SIZE, maxItemSize),
-                        mOverflowPanel.getCount()));
-        int extension = 0;
-        if (actualSize < mOverflowPanel.getCount()) {
-            // The overflow will require scrolling to get to all the items.
-            // Extend the height so that part of the hidden items is displayed.
-            extension = (int) (mLineHeight * 0.5f);
-        }
-        return actualSize * mLineHeight
-                + mOverflowButtonSize.getHeight()
-                + extension;
-    }
-
-    private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) {
-        menuItemButton.setTag(MenuItemRepr.of(menuItem));
-        menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
-    }
-
-    /**
-     * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.*
-     * animations. See comment about this in the code.
-     */
-    private int getAdjustedDuration(int originalDuration) {
-        if (mTransitionDurationScale < 150) {
-            // For smaller transition, decrease the time.
-            return Math.max(originalDuration - 50, 0);
-        } else if (mTransitionDurationScale > 300) {
-            // For bigger transition, increase the time.
-            return originalDuration + 50;
-        }
-
-        // Scale the animation duration with getDurationScale(). This allows
-        // android.view.animation.* animations to scale just like android.animation.* animations
-        // when  animator duration scale is adjusted in "Developer Options".
-        // For this reason, do not use this method for android.animation.* animations.
-        return (int) (originalDuration * ValueAnimator.getDurationScale());
-    }
-
-    private void maybeComputeTransitionDurationScale() {
-        if (mMainPanelSize != null && mOverflowPanelSize != null) {
-            int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
-            int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
-            mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h)
-                    / mContentContainer.getContext().getResources().getDisplayMetrics().density);
-        }
-    }
-
-    private ViewGroup createMainPanel() {
-        ViewGroup mainPanel = new LinearLayout(mContext) {
-            @Override
-            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-                if (isOverflowAnimating()) {
-                    // Update widthMeasureSpec to make sure that this view is not clipped
-                    // as we offset its coordinates with respect to its parent.
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            mMainPanelSize.getWidth(),
-                            MeasureSpec.EXACTLY);
-                }
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
-
-            @Override
-            public boolean onInterceptTouchEvent(MotionEvent ev) {
-                // Intercept the touch event while the overflow is animating.
-                return isOverflowAnimating();
-            }
-        };
-        return mainPanel;
-    }
-
-    private ImageButton createOverflowButton() {
-        final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext)
-                .inflate(R.layout.floating_popup_overflow_button, null);
-        overflowButton.setImageDrawable(mOverflow);
-        overflowButton.setOnClickListener(v -> {
-            if (mIsOverflowOpen) {
-                overflowButton.setImageDrawable(mToOverflow);
-                mToOverflow.start();
-                closeOverflow();
-            } else {
-                overflowButton.setImageDrawable(mToArrow);
-                mToArrow.start();
-                openOverflow();
-            }
-        });
-        return overflowButton;
-    }
-
-    private FloatingToolbarPopup.OverflowPanel createOverflowPanel() {
-        final FloatingToolbarPopup.OverflowPanel
-                overflowPanel = new FloatingToolbarPopup.OverflowPanel(this);
-        overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-        overflowPanel.setDivider(null);
-        overflowPanel.setDividerHeight(0);
-
-        final ArrayAdapter adapter =
-                new ArrayAdapter<MenuItem>(mContext, 0) {
-                    @Override
-                    public View getView(int position, View convertView, ViewGroup parent) {
-                        return mOverflowPanelViewHelper.getView(
-                                getItem(position), mOverflowPanelSize.getWidth(), convertView);
-                    }
-                };
-        overflowPanel.setAdapter(adapter);
-
-        overflowPanel.setOnItemClickListener((parent, view, position, id) -> {
-            MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
-            if (mOnMenuItemClickListener != null) {
-                mOnMenuItemClickListener.onMenuItemClick(menuItem);
-            }
-        });
-
-        return overflowPanel;
-    }
-
-    private boolean isOverflowAnimating() {
-        final boolean overflowOpening = mOpenOverflowAnimation.hasStarted()
-                && !mOpenOverflowAnimation.hasEnded();
-        final boolean overflowClosing = mCloseOverflowAnimation.hasStarted()
-                && !mCloseOverflowAnimation.hasEnded();
-        return overflowOpening || overflowClosing;
-    }
-
-    private Animation.AnimationListener createOverflowAnimationListener() {
-        Animation.AnimationListener listener = new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) {
-                // Disable the overflow button while it's animating.
-                // It will be re-enabled when the animation stops.
-                mOverflowButton.setEnabled(false);
-                // Ensure both panels have visibility turned on when the overflow animation
-                // starts.
-                mMainPanel.setVisibility(View.VISIBLE);
-                mOverflowPanel.setVisibility(View.VISIBLE);
-            }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                // Posting this because it seems like this is called before the animation
-                // actually ends.
-                mContentContainer.post(() -> {
-                    setPanelsStatesAtRestingPosition();
-                    setContentAreaAsTouchableSurface();
-                });
-            }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) {
-            }
-        };
-        return listener;
-    }
-
-    private static Size measure(View view) {
-        Preconditions.checkState(view.getParent() == null);
-        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-        return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
-    }
-
-    private static void setSize(View view, int width, int height) {
-        view.setMinimumWidth(width);
-        view.setMinimumHeight(height);
-        ViewGroup.LayoutParams params = view.getLayoutParams();
-        params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params;
-        params.width = width;
-        params.height = height;
-        view.setLayoutParams(params);
-    }
-
-    private static void setSize(View view, Size size) {
-        setSize(view, size.getWidth(), size.getHeight());
-    }
-
-    private static void setWidth(View view, int width) {
-        ViewGroup.LayoutParams params = view.getLayoutParams();
-        setSize(view, width, params.height);
-    }
-
-    private static void setHeight(View view, int height) {
-        ViewGroup.LayoutParams params = view.getLayoutParams();
-        setSize(view, params.width, height);
-    }
-
-    /**
-     * A custom ListView for the overflow panel.
-     */
-    private static final class OverflowPanel extends ListView {
-
-        private final FloatingToolbarPopup mPopup;
-
-        OverflowPanel(FloatingToolbarPopup popup) {
-            super(Objects.requireNonNull(popup).mContext);
-            this.mPopup = popup;
-            setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
-            setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            // Update heightMeasureSpec to make sure that this view is not clipped
-            // as we offset it's coordinates with respect to its parent.
-            int height = mPopup.mOverflowPanelSize.getHeight()
-                    - mPopup.mOverflowButtonSize.getHeight();
-            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-
-        @Override
-        public boolean dispatchTouchEvent(MotionEvent ev) {
-            if (mPopup.isOverflowAnimating()) {
-                // Eat the touch event.
-                return true;
-            }
-            return super.dispatchTouchEvent(ev);
-        }
-
-        @Override
-        protected boolean awakenScrollBars() {
-            return super.awakenScrollBars();
-        }
-    }
-
-    /**
-     * A custom interpolator used for various floating toolbar animations.
-     */
-    private static final class LogAccelerateInterpolator implements Interpolator {
-
-        private static final int BASE = 100;
-        private static final float LOGS_SCALE = 1f / computeLog(1, BASE);
-
-        private static float computeLog(float t, int base) {
-            return (float) (1 - Math.pow(base, -t));
-        }
-
-        @Override
-        public float getInterpolation(float t) {
-            return 1 - computeLog(1 - t, BASE) * LOGS_SCALE;
-        }
-    }
-
-    /**
-     * A helper for generating views for the overflow panel.
-     */
-    private static final class OverflowPanelViewHelper {
-
-        private final View mCalculator;
-        private final int mIconTextSpacing;
-        private final int mSidePadding;
-
-        private final Context mContext;
-
-        OverflowPanelViewHelper(Context context, int iconTextSpacing) {
-            mContext = Objects.requireNonNull(context);
-            mIconTextSpacing = iconTextSpacing;
-            mSidePadding = context.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_side_padding);
-            mCalculator = createMenuButton(null);
-        }
-
-        public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
-            Objects.requireNonNull(menuItem);
-            if (convertView != null) {
-                updateMenuItemButton(
-                        convertView, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
-            } else {
-                convertView = createMenuButton(menuItem);
-            }
-            convertView.setMinimumWidth(minimumWidth);
-            return convertView;
-        }
-
-        public int calculateWidth(MenuItem menuItem) {
-            updateMenuItemButton(
-                    mCalculator, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
-            mCalculator.measure(
-                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-            return mCalculator.getMeasuredWidth();
-        }
-
-        private View createMenuButton(MenuItem menuItem) {
-            View button = createMenuItemButton(
-                    mContext, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
-            button.setPadding(mSidePadding, 0, mSidePadding, 0);
-            return button;
-        }
-
-        private boolean shouldShowIcon(MenuItem menuItem) {
-            if (menuItem != null) {
-                return menuItem.getGroupId() == android.R.id.textAssist;
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Creates and returns a menu button for the specified menu item.
-     */
-    private static View createMenuItemButton(
-            Context context, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
-        final View menuItemButton = LayoutInflater.from(context)
-                .inflate(R.layout.floating_popup_menu_button, null);
-        if (menuItem != null) {
-            updateMenuItemButton(menuItemButton, menuItem, iconTextSpacing, showIcon);
-        }
-        return menuItemButton;
-    }
-
-    /**
-     * Updates the specified menu item button with the specified menu item data.
-     */
-    private static void updateMenuItemButton(
-            View menuItemButton, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
-        final TextView buttonText = menuItemButton.findViewById(
-                R.id.floating_toolbar_menu_item_text);
-        buttonText.setEllipsize(null);
-        if (TextUtils.isEmpty(menuItem.getTitle())) {
-            buttonText.setVisibility(View.GONE);
-        } else {
-            buttonText.setVisibility(View.VISIBLE);
-            buttonText.setText(menuItem.getTitle());
-        }
-        final ImageView buttonIcon = menuItemButton.findViewById(
-                R.id.floating_toolbar_menu_item_image);
-        if (menuItem.getIcon() == null || !showIcon) {
-            buttonIcon.setVisibility(View.GONE);
-            if (buttonText != null) {
-                buttonText.setPaddingRelative(0, 0, 0, 0);
-            }
-        } else {
-            buttonIcon.setVisibility(View.VISIBLE);
-            buttonIcon.setImageDrawable(menuItem.getIcon());
-            if (buttonText != null) {
-                buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
-            }
-        }
-        final CharSequence contentDescription = menuItem.getContentDescription();
-        if (TextUtils.isEmpty(contentDescription)) {
-            menuItemButton.setContentDescription(menuItem.getTitle());
-        } else {
-            menuItemButton.setContentDescription(contentDescription);
-        }
-    }
-
-    private static ViewGroup createContentContainer(Context context) {
-        ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
-                .inflate(R.layout.floating_popup_container, null);
-        contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-        contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
-        contentContainer.setClipToOutline(true);
-        return contentContainer;
-    }
-
-    private static PopupWindow createPopupWindow(ViewGroup content) {
-        ViewGroup popupContentHolder = new LinearLayout(content.getContext());
-        PopupWindow popupWindow = new PopupWindow(popupContentHolder);
-        // TODO: Use .setIsLaidOutInScreen(true) instead of .setClippingEnabled(false)
-        // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
-        popupWindow.setClippingEnabled(false);
-        popupWindow.setWindowLayoutType(
-                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
-        popupWindow.setAnimationStyle(0);
-        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-        content.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-        popupContentHolder.addView(content);
-        return popupWindow;
-    }
-
-    /**
-     * Creates an "appear" animation for the specified view.
-     *
-     * @param view  The view to animate
-     */
-    private static AnimatorSet createEnterAnimation(View view) {
-        AnimatorSet animation = new AnimatorSet();
-        animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
-        return animation;
-    }
-
-    /**
-     * Creates a "disappear" animation for the specified view.
-     *
-     * @param view  The view to animate
-     * @param startDelay  The start delay of the animation
-     * @param listener  The animation listener
-     */
-    private static AnimatorSet createExitAnimation(
-            View view, int startDelay, Animator.AnimatorListener listener) {
-        AnimatorSet animation =  new AnimatorSet();
-        animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
-        animation.setStartDelay(startDelay);
-        animation.addListener(listener);
-        return animation;
-    }
-
-    /**
-     * Returns a re-themed context with controlled look and feel for views.
-     */
-    private static Context applyDefaultTheme(Context originalContext) {
-        TypedArray a = originalContext.obtainStyledAttributes(new int[]{R.attr.isLightTheme});
-        boolean isLightTheme = a.getBoolean(0, true);
-        int themeId =
-                isLightTheme ? R.style.Theme_DeviceDefault_Light : R.style.Theme_DeviceDefault;
-        a.recycle();
-        return new ContextThemeWrapper(originalContext, themeId);
-    }
-
-    /**
-     * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
-     */
-    @VisibleForTesting
-    public static final class MenuItemRepr {
-
-        public final int itemId;
-        public final int groupId;
-        @Nullable public final String title;
-        @Nullable private final Drawable mIcon;
-
-        private MenuItemRepr(
-                int itemId, int groupId, @Nullable CharSequence title, @Nullable Drawable icon) {
-            this.itemId = itemId;
-            this.groupId = groupId;
-            this.title = (title == null) ? null : title.toString();
-            mIcon = icon;
-        }
-
-        /**
-         * Creates an instance of MenuItemRepr for the specified menu item.
-         */
-        public static MenuItemRepr of(MenuItem menuItem) {
-            return new MenuItemRepr(
-                    menuItem.getItemId(),
-                    menuItem.getGroupId(),
-                    menuItem.getTitle(),
-                    menuItem.getIcon());
-        }
-
-        /**
-         * Returns this object's hashcode.
-         */
-        @Override
-        public int hashCode() {
-            return Objects.hash(itemId, groupId, title, mIcon);
-        }
-
-        /**
-         * Returns true if this object is the same as the specified object.
-         */
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (!(o instanceof MenuItemRepr)) {
-                return false;
-            }
-            final MenuItemRepr other = (MenuItemRepr) o;
-            return itemId == other.itemId
-                    && groupId == other.groupId
-                    && TextUtils.equals(title, other.title)
-                    // Many Drawables (icons) do not implement equals(). Using equals() here instead
-                    // of reference comparisons in case a Drawable subclass implements equals().
-                    && Objects.equals(mIcon, other.mIcon);
-        }
-
-        /**
-         * Returns true if the two menu item collections are the same based on MenuItemRepr.
-         */
-        public static boolean reprEquals(
-                Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) {
-            if (menuItems1.size() != menuItems2.size()) {
-                return false;
-            }
-
-            final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator();
-            for (MenuItem menuItem1 : menuItems1) {
-                final MenuItem menuItem2 = menuItems2Iter.next();
-                if (!MenuItemRepr.of(menuItem1).equals(MenuItemRepr.of(menuItem2))) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-    }
-}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index d16d9c6..db4bc2c 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -24,6 +24,8 @@
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.RecoveryCertPath;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 
@@ -96,4 +98,10 @@
     boolean tryUnlockWithCachedUnifiedChallenge(int userId);
     void removeCachedUnifiedChallenge(int userId);
     void updateEncryptionPassword(int type, in byte[] password);
+    boolean registerWeakEscrowTokenRemovedListener(in IWeakEscrowTokenRemovedListener listener);
+    boolean unregisterWeakEscrowTokenRemovedListener(in IWeakEscrowTokenRemovedListener listener);
+    long addWeakEscrowToken(in byte[] token, int userId, in IWeakEscrowTokenActivatedListener callback);
+    boolean removeWeakEscrowToken(long handle, int userId);
+    boolean isWeakEscrowTokenActive(long handle, int userId);
+    boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
 }
diff --git a/core/java/com/android/internal/widget/IWeakEscrowTokenActivatedListener.aidl b/core/java/com/android/internal/widget/IWeakEscrowTokenActivatedListener.aidl
new file mode 100644
index 0000000..9c8d9d6
--- /dev/null
+++ b/core/java/com/android/internal/widget/IWeakEscrowTokenActivatedListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/** @hide */
+oneway interface IWeakEscrowTokenActivatedListener {
+    void onWeakEscrowTokenActivated(long handle, int userId);
+}
diff --git a/core/java/com/android/internal/widget/IWeakEscrowTokenRemovedListener.aidl b/core/java/com/android/internal/widget/IWeakEscrowTokenRemovedListener.aidl
new file mode 100644
index 0000000..7018048
--- /dev/null
+++ b/core/java/com/android/internal/widget/IWeakEscrowTokenRemovedListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/** @hide */
+oneway interface IWeakEscrowTokenRemovedListener {
+    void onWeakEscrowTokenRemoved(long handle, int userId);
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 5a03277..f8ccde4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1028,15 +1028,6 @@
     }
 
     /**
-     * @return Whether tactile feedback for the pattern is enabled.
-     */
-    @UnsupportedAppUsage
-    public boolean isTactileFeedbackEnabled() {
-        return Settings.System.getIntForUser(mContentResolver,
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
-    }
-
-    /**
      * Set and store the lockout deadline, meaning the user can't attempt their unlock
      * pattern until the deadline has passed.
      * @return the chosen deadline.
@@ -1236,6 +1227,28 @@
         }
     }
 
+    /** Register the given WeakEscrowTokenRemovedListener. */
+    public boolean registerWeakEscrowTokenRemovedListener(
+            @NonNull final IWeakEscrowTokenRemovedListener listener) {
+        try {
+            return getLockSettings().registerWeakEscrowTokenRemovedListener(listener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not register WeakEscrowTokenRemovedListener.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Unregister the given WeakEscrowTokenRemovedListener. */
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            @NonNull final IWeakEscrowTokenRemovedListener listener) {
+        try {
+            return getLockSettings().unregisterWeakEscrowTokenRemovedListener(listener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not register WeakEscrowTokenRemovedListener.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
         try {
             getLockSettings().reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
@@ -1355,15 +1368,38 @@
     }
 
     /**
+     * Create a weak escrow token for the current user, which can later be used to unlock FBE
+     * or change user password.
+     *
+     * After adding, if the user currently has lockscreen password, they will need to perform a
+     * confirm credential operation in order to activate the token for future use. If the user
+     * has no secure lockscreen, then the token is activated immediately.
+     *
+     * If the user changes or removes lockscreen password, activated weak escrow tokens will be
+     * removed.
+     *
+     * @return a unique 64-bit token handle which is needed to refer to this token later.
+     */
+    public long addWeakEscrowToken(byte[] token, int userId,
+            @NonNull IWeakEscrowTokenActivatedListener callback) {
+        try {
+            return getLockSettings().addWeakEscrowToken(token, userId, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not add weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Callback interface to notify when an added escrow token has been activated.
      */
     public interface EscrowTokenStateChangeCallback {
         /**
          * The method to be called when the token is activated.
          * @param handle 64 bit handle corresponding to the escrow token
-         * @param userid user for whom the escrow token has been added
+         * @param userId user for whom the escrow token has been added
          */
-        void onEscrowTokenActivated(long handle, int userid);
+        void onEscrowTokenActivated(long handle, int userId);
     }
 
     /**
@@ -1379,6 +1415,21 @@
     }
 
     /**
+     * Remove a weak escrow token.
+     *
+     * @return true if the given handle refers to a valid weak token previously returned from
+     * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+     */
+    public boolean removeWeakEscrowToken(long handle, int userId) {
+        try {
+            return getLockSettings().removeWeakEscrowToken(handle, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not remove the weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check if the given escrow token is active or not. Only active token can be used to call
      * {@link #setLockCredentialWithToken} and {@link #unlockUserWithToken}
      *
@@ -1389,6 +1440,29 @@
     }
 
     /**
+     * Check if the given weak escrow token is active or not. Only active token can be used to call
+     * {@link #setLockCredentialWithToken} and {@link #unlockUserWithToken}
+     */
+    public boolean isWeakEscrowTokenActive(long handle, int userId) {
+        try {
+            return getLockSettings().isWeakEscrowTokenActive(handle, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not check the weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Check if the given weak escrow token is valid. */
+    public boolean isWeakEscrowTokenValid(long handle, byte[] token, int userId) {
+        try {
+            return getLockSettings().isWeakEscrowTokenValid(handle, token, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not validate the weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Change a user's lock credential with a pre-configured escrow token.
      *
      * <p>This method is only available to code running in the system server process itself.
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 3994fbd..2b6b933 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -139,7 +139,6 @@
     private boolean mInputEnabled = true;
     @UnsupportedAppUsage
     private boolean mInStealthMode = false;
-    private boolean mEnableHapticFeedback = true;
     @UnsupportedAppUsage
     private boolean mPatternInProgress = false;
     private boolean mFadePattern = true;
@@ -401,13 +400,6 @@
     }
 
     /**
-     * @return Whether the view has tactile feedback enabled.
-     */
-    public boolean isTactileFeedbackEnabled() {
-        return mEnableHapticFeedback;
-    }
-
-    /**
      * Set whether the view is in stealth mode.  If true, there will be no
      * visible feedback as the user enters the pattern.
      *
@@ -427,17 +419,6 @@
     }
 
     /**
-     * Set whether the view will use tactile feedback.  If true, there will be
-     * tactile feedback as the user enters the pattern.
-     *
-     * @param tactileFeedbackEnabled Whether tactile feedback is enabled
-     */
-    @UnsupportedAppUsage
-    public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) {
-        mEnableHapticFeedback = tactileFeedbackEnabled;
-    }
-
-    /**
      * Set the call back for pattern detection.
      * @param onPatternListener The call back.
      */
@@ -783,11 +764,9 @@
                 addCellToPattern(fillInGapCell);
             }
             addCellToPattern(cell);
-            if (mEnableHapticFeedback) {
-                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
-                        | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
-            }
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
             return cell;
         }
         return null;
@@ -1462,7 +1441,7 @@
         return new SavedState(superState,
                 patternString,
                 mPatternDisplayMode.ordinal(),
-                mInputEnabled, mInStealthMode, mEnableHapticFeedback);
+                mInputEnabled, mInStealthMode);
     }
 
     @Override
@@ -1475,7 +1454,6 @@
         mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
         mInputEnabled = ss.isInputEnabled();
         mInStealthMode = ss.isInStealthMode();
-        mEnableHapticFeedback = ss.isTactileFeedbackEnabled();
     }
 
     /**
@@ -1487,20 +1465,18 @@
         private final int mDisplayMode;
         private final boolean mInputEnabled;
         private final boolean mInStealthMode;
-        private final boolean mTactileFeedbackEnabled;
 
         /**
          * Constructor called from {@link LockPatternView#onSaveInstanceState()}
          */
         @UnsupportedAppUsage
         private SavedState(Parcelable superState, String serializedPattern, int displayMode,
-                boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) {
+                boolean inputEnabled, boolean inStealthMode) {
             super(superState);
             mSerializedPattern = serializedPattern;
             mDisplayMode = displayMode;
             mInputEnabled = inputEnabled;
             mInStealthMode = inStealthMode;
-            mTactileFeedbackEnabled = tactileFeedbackEnabled;
         }
 
         /**
@@ -1513,7 +1489,6 @@
             mDisplayMode = in.readInt();
             mInputEnabled = (Boolean) in.readValue(null);
             mInStealthMode = (Boolean) in.readValue(null);
-            mTactileFeedbackEnabled = (Boolean) in.readValue(null);
         }
 
         public String getSerializedPattern() {
@@ -1532,10 +1507,6 @@
             return mInStealthMode;
         }
 
-        public boolean isTactileFeedbackEnabled(){
-            return mTactileFeedbackEnabled;
-        }
-
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             super.writeToParcel(dest, flags);
@@ -1543,7 +1514,6 @@
             dest.writeInt(mDisplayMode);
             dest.writeValue(mInputEnabled);
             dest.writeValue(mInStealthMode);
-            dest.writeValue(mTactileFeedbackEnabled);
         }
 
         @SuppressWarnings({ "unused", "hiding" }) // Found using reflection
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 627381c..09ff4e0 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -37,6 +37,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
+import android.view.RoundedCorner;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -229,13 +230,29 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (insets.getDisplayCutout() != null) {
-            mHeaderPaddingTop = insets.getDisplayCutout().getSafeInsetTop();
-            mWaterfallInsets = insets.getDisplayCutout().getWaterfallInsets();
-        } else {
-            mHeaderPaddingTop = 0;
-            mWaterfallInsets = Insets.NONE;
+        int headerPaddingTop = 0;
+        Insets waterfallInsets = Insets.NONE;
+
+        final RoundedCorner topLeftRounded =
+                insets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT);
+        if (topLeftRounded != null) {
+            headerPaddingTop = topLeftRounded.getRadius();
         }
+
+        final RoundedCorner topRightRounded =
+                insets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT);
+        if (topRightRounded != null) {
+            headerPaddingTop = Math.max(headerPaddingTop, topRightRounded.getRadius());
+        }
+
+        if (insets.getDisplayCutout() != null) {
+            headerPaddingTop =
+                    Math.max(headerPaddingTop, insets.getDisplayCutout().getSafeInsetTop());
+            waterfallInsets = insets.getDisplayCutout().getWaterfallInsets();
+        }
+
+        mHeaderPaddingTop = headerPaddingTop;
+        mWaterfallInsets = waterfallInsets;
         return super.onApplyWindowInsets(insets);
     }
 
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 5e6f3a4..11566d9 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -22,10 +22,9 @@
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.os.UserHandle;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -68,10 +67,8 @@
     private boolean mHoldLeftOnTransition = true;
     private boolean mHoldRightOnTransition = true;
 
-    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
-            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-            .build();
+    private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
 
     private OnTriggerListener mOnTriggerListener;
     private int mGrabbedState = OnTriggerListener.NO_HANDLE;
@@ -834,16 +831,12 @@
      * Triggers haptic feedback.
      */
     private synchronized void vibrate(long duration) {
-        final boolean hapticEnabled = Settings.System.getIntForUser(
-                mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
-                UserHandle.USER_CURRENT) != 0;
-        if (hapticEnabled) {
-            if (mVibrator == null) {
-                mVibrator = (android.os.Vibrator) getContext()
-                        .getSystemService(Context.VIBRATOR_SERVICE);
-            }
-            mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES);
+        if (mVibrator == null) {
+            mVibrator = getContext().getSystemService(Vibrator.class);
         }
+        mVibrator.vibrate(
+                VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE),
+                TOUCH_VIBRATION_ATTRIBUTES);
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbar.java b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbar.java
new file mode 100644
index 0000000..e75f372
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbar.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.floatingtoolbar;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.Window;
+import android.widget.PopupWindow;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A floating toolbar for showing contextual menu items.
+ * This view shows as many menu item buttons as can fit in the horizontal toolbar and the
+ * the remaining menu items in a vertical overflow view when the overflow button is clicked.
+ * The horizontal toolbar morphs into the vertical overflow view.
+ */
+public final class FloatingToolbar {
+
+    // This class is responsible for the public API of the floating toolbar.
+    // It delegates rendering operations to the FloatingToolbarPopup.
+
+    public static final String FLOATING_TOOLBAR_TAG = "floating_toolbar";
+
+    private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
+            item -> false;
+
+    private final Window mWindow;
+    private final FloatingToolbarPopup mPopup;
+
+    private final Rect mContentRect = new Rect();
+
+    private Menu mMenu;
+    private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+
+    private final OnLayoutChangeListener mOrientationChangeHandler = new OnLayoutChangeListener() {
+
+        private final Rect mNewRect = new Rect();
+        private final Rect mOldRect = new Rect();
+
+        @Override
+        public void onLayoutChange(
+                View view,
+                int newLeft, int newRight, int newTop, int newBottom,
+                int oldLeft, int oldRight, int oldTop, int oldBottom) {
+            mNewRect.set(newLeft, newRight, newTop, newBottom);
+            mOldRect.set(oldLeft, oldRight, oldTop, oldBottom);
+            if (mPopup.isShowing() && !mNewRect.equals(mOldRect)) {
+                mPopup.setWidthChanged(true);
+                updateLayout();
+            }
+        }
+    };
+
+    /**
+     * Sorts the list of menu items to conform to certain requirements.
+     */
+    private final Comparator<MenuItem> mMenuItemComparator = (menuItem1, menuItem2) -> {
+        // Ensure the assist menu item is always the first item:
+        if (menuItem1.getItemId() == android.R.id.textAssist) {
+            return menuItem2.getItemId() == android.R.id.textAssist ? 0 : -1;
+        }
+        if (menuItem2.getItemId() == android.R.id.textAssist) {
+            return 1;
+        }
+
+        // Order by SHOW_AS_ACTION type:
+        if (menuItem1.requiresActionButton()) {
+            return menuItem2.requiresActionButton() ? 0 : -1;
+        }
+        if (menuItem2.requiresActionButton()) {
+            return 1;
+        }
+        if (menuItem1.requiresOverflow()) {
+            return menuItem2.requiresOverflow() ? 0 : 1;
+        }
+        if (menuItem2.requiresOverflow()) {
+            return -1;
+        }
+
+        // Order by order value:
+        return menuItem1.getOrder() - menuItem2.getOrder();
+    };
+
+    /**
+     * Initializes a floating toolbar.
+     */
+    public FloatingToolbar(Window window) {
+        // TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
+        // supports multi-display.
+        mWindow = Objects.requireNonNull(window);
+        mPopup = FloatingToolbarPopup.createInstance(window.getContext(), window.getDecorView());
+    }
+
+    /**
+     * Sets the menu to be shown in this floating toolbar.
+     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+     * toolbar.
+     */
+    public FloatingToolbar setMenu(Menu menu) {
+        mMenu = Objects.requireNonNull(menu);
+        return this;
+    }
+
+    /**
+     * Sets the custom listener for invocation of menu items in this floating toolbar.
+     */
+    public FloatingToolbar setOnMenuItemClickListener(
+            MenuItem.OnMenuItemClickListener menuItemClickListener) {
+        if (menuItemClickListener != null) {
+            mMenuItemClickListener = menuItemClickListener;
+        } else {
+            mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+        }
+        return this;
+    }
+
+    /**
+     * Sets the content rectangle. This is the area of the interesting content that this toolbar
+     * should avoid obstructing.
+     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+     * toolbar.
+     */
+    public FloatingToolbar setContentRect(Rect rect) {
+        mContentRect.set(Objects.requireNonNull(rect));
+        return this;
+    }
+
+    /**
+     * Sets the suggested width of this floating toolbar.
+     * The actual width will be about this size but there are no guarantees that it will be exactly
+     * the suggested width.
+     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+     * toolbar.
+     */
+    public FloatingToolbar setSuggestedWidth(int suggestedWidth) {
+        mPopup.setSuggestedWidth(suggestedWidth);
+        return this;
+    }
+
+    /**
+     * Shows this floating toolbar.
+     */
+    public FloatingToolbar show() {
+        registerOrientationHandler();
+        doShow();
+        return this;
+    }
+
+    /**
+     * Updates this floating toolbar to reflect recent position and view updates.
+     * NOTE: This method is a no-op if the toolbar isn't showing.
+     */
+    public FloatingToolbar updateLayout() {
+        if (mPopup.isShowing()) {
+            doShow();
+        }
+        return this;
+    }
+
+    /**
+     * Dismisses this floating toolbar.
+     */
+    public void dismiss() {
+        unregisterOrientationHandler();
+        mPopup.dismiss();
+    }
+
+    /**
+     * Hides this floating toolbar. This is a no-op if the toolbar is not showing.
+     * Use {@link #isHidden()} to distinguish between a hidden and a dismissed toolbar.
+     */
+    public void hide() {
+        mPopup.hide();
+    }
+
+    /**
+     * Returns {@code true} if this toolbar is currently showing. {@code false} otherwise.
+     */
+    public boolean isShowing() {
+        return mPopup.isShowing();
+    }
+
+    /**
+     * Returns {@code true} if this toolbar is currently hidden. {@code false} otherwise.
+     */
+    public boolean isHidden() {
+        return mPopup.isHidden();
+    }
+
+    /**
+     * If this is set to true, the action mode view will dismiss itself on touch events outside of
+     * its window. The setting takes effect immediately.
+     *
+     * @param outsideTouchable whether or not this action mode is "outside touchable"
+     * @param onDismiss optional. Sets a callback for when this action mode popup dismisses itself
+     */
+    public void setOutsideTouchable(
+            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+        mPopup.setOutsideTouchable(outsideTouchable, onDismiss);
+    }
+
+    private void doShow() {
+        List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
+        menuItems.sort(mMenuItemComparator);
+        mPopup.show(menuItems, mMenuItemClickListener, mContentRect);
+    }
+
+    /**
+     * Returns the visible and enabled menu items in the specified menu.
+     * This method is recursive.
+     */
+    private static List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) {
+        List<MenuItem> menuItems = new ArrayList<>();
+        for (int i = 0; (menu != null) && (i < menu.size()); i++) {
+            MenuItem menuItem = menu.getItem(i);
+            if (menuItem.isVisible() && menuItem.isEnabled()) {
+                Menu subMenu = menuItem.getSubMenu();
+                if (subMenu != null) {
+                    menuItems.addAll(getVisibleAndEnabledMenuItems(subMenu));
+                } else {
+                    menuItems.add(menuItem);
+                }
+            }
+        }
+        return menuItems;
+    }
+
+    private void registerOrientationHandler() {
+        unregisterOrientationHandler();
+        mWindow.getDecorView().addOnLayoutChangeListener(mOrientationChangeHandler);
+    }
+
+    private void unregisterOrientationHandler() {
+        mWindow.getDecorView().removeOnLayoutChangeListener(mOrientationChangeHandler);
+    }
+}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
new file mode 100644
index 0000000..f47700c
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.floatingtoolbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.selectiontoolbar.SelectionToolbarManager;
+import android.widget.PopupWindow;
+
+import java.util.List;
+
+/**
+ * A popup window used by the {@link FloatingToolbar} to render menu items.
+ *
+ */
+public interface FloatingToolbarPopup {
+
+    /**
+     * Sets the suggested dp width of this floating toolbar.
+     * The actual width will be about this size but there are no guarantees that it will be exactly
+     * the suggested width.
+     */
+    void setSuggestedWidth(int suggestedWidth);
+
+    /**
+     * Sets if the floating toolbar width changed.
+     */
+    void setWidthChanged(boolean widthChanged);
+
+    /**
+     * Shows this popup at the specified coordinates.
+     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+     */
+    void show(List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener,
+            Rect contentRect);
+
+    /**
+     * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
+     */
+    void dismiss();
+
+    /**
+     * Hides this popup. This is a no-op if this popup is not showing.
+     * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
+     */
+    void hide();
+
+    /**
+     * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+     */
+    boolean isShowing();
+
+    /**
+     * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
+     */
+    boolean isHidden();
+
+    /**
+     * Makes this toolbar "outside touchable" and sets the onDismissListener.
+     *
+     * @param outsideTouchable if true, the popup will be made "outside touchable" and
+     *      "non focusable". The reverse will happen if false.
+     * @param onDismiss
+     *
+     * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
+     *
+     * @see PopupWindow#setOutsideTouchable(boolean)
+     * @see PopupWindow#setFocusable(boolean)
+     * @see PopupWindow.OnDismissListener
+     */
+    boolean setOutsideTouchable(boolean outsideTouchable, PopupWindow.OnDismissListener onDismiss);
+
+    /**
+     * Returns {@link RemoteFloatingToolbarPopup} implementation if the system selection toolbar
+     * enabled, otherwise returns {@link LocalFloatingToolbarPopup} implementation.
+     */
+    static FloatingToolbarPopup createInstance(Context context, View parent) {
+        boolean enabled = SelectionToolbarManager.isRemoteSelectionToolbarEnabled(context);
+        return enabled
+                ? new RemoteFloatingToolbarPopup(context, parent)
+                : new LocalFloatingToolbarPopup(context, parent);
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
new file mode 100644
index 0000000..80d8bd7
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -0,0 +1,1617 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.floatingtoolbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Size;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.Transformation;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A popup window used by the floating toolbar to render menu items in the local app process.
+ *
+ * This class is responsible for the rendering/animation of the floating toolbar.
+ * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
+ * to transition between panels.
+ */
+public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
+
+    /* Minimum and maximum number of items allowed in the overflow. */
+    private static final int MIN_OVERFLOW_SIZE = 2;
+    private static final int MAX_OVERFLOW_SIZE = 4;
+
+    private final Context mContext;
+    private final View mParent;  // Parent for the popup window.
+    private final PopupWindow mPopupWindow;
+
+    /* Margins between the popup window and its content. */
+    private final int mMarginHorizontal;
+    private final int mMarginVertical;
+
+    /* View components */
+    private final ViewGroup mContentContainer;  // holds all contents.
+    private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
+    // holds menu items hidden in the overflow.
+    private final OverflowPanel mOverflowPanel;
+    private final ImageButton mOverflowButton;  // opens/closes the overflow.
+    /* overflow button drawables. */
+    private final Drawable mArrow;
+    private final Drawable mOverflow;
+    private final AnimatedVectorDrawable mToArrow;
+    private final AnimatedVectorDrawable mToOverflow;
+
+    private final OverflowPanelViewHelper mOverflowPanelViewHelper;
+
+    /* Animation interpolators. */
+    private final Interpolator mLogAccelerateInterpolator;
+    private final Interpolator mFastOutSlowInInterpolator;
+    private final Interpolator mLinearOutSlowInInterpolator;
+    private final Interpolator mFastOutLinearInInterpolator;
+
+    /* Animations. */
+    private final AnimatorSet mShowAnimation;
+    private final AnimatorSet mDismissAnimation;
+    private final AnimatorSet mHideAnimation;
+    private final AnimationSet mOpenOverflowAnimation;
+    private final AnimationSet mCloseOverflowAnimation;
+    private final Animation.AnimationListener mOverflowAnimationListener;
+
+    private final Rect mViewPortOnScreen = new Rect();  // portion of screen we can draw in.
+    private final Point mCoordsOnWindow = new Point();  // popup window coordinates.
+    /* Temporary data holders. Reset values before using. */
+    private final int[] mTmpCoords = new int[2];
+
+    private final Region mTouchableRegion = new Region();
+    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
+            info -> {
+                info.contentInsets.setEmpty();
+                info.visibleInsets.setEmpty();
+                info.touchableRegion.set(mTouchableRegion);
+                info.setTouchableInsets(
+                        ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            };
+
+    private final int mLineHeight;
+    private final int mIconTextSpacing;
+
+    /**
+     * @see OverflowPanelViewHelper#preparePopupContent().
+     */
+    private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
+        @Override
+        public void run() {
+            setPanelsStatesAtRestingPosition();
+            setContentAreaAsTouchableSurface();
+            mContentContainer.setAlpha(1);
+        }
+    };
+
+    private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
+    private boolean mHidden; // tracks whether this popup is hidden or hiding.
+
+    /* Calculated sizes for panels and overflow button. */
+    private final Size mOverflowButtonSize;
+    private Size mOverflowPanelSize;  // Should be null when there is no overflow.
+    private Size mMainPanelSize;
+
+    /* Menu items and click listeners */
+    private final Map<MenuItemRepr, MenuItem> mMenuItems = new LinkedHashMap<>();
+    private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
+    private final View.OnClickListener mMenuItemButtonOnClickListener =
+            new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mOnMenuItemClickListener == null) {
+                        return;
+                    }
+                    final Object tag = v.getTag();
+                    if (!(tag instanceof MenuItemRepr)) {
+                        return;
+                    }
+                    final MenuItem menuItem = mMenuItems.get((MenuItemRepr) tag);
+                    if (menuItem == null) {
+                        return;
+                    }
+                    mOnMenuItemClickListener.onMenuItemClick(menuItem);
+                }
+            };
+
+    private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
+    private boolean mIsOverflowOpen;
+
+    private int mTransitionDurationScale;  // Used to scale the toolbar transition duration.
+
+    private final Rect mPreviousContentRect = new Rect();
+    private int mSuggestedWidth;
+    private boolean mWidthChanged = true;
+
+    /**
+     * Initializes a new floating toolbar popup.
+     *
+     * @param parent  A parent view to get the {@link android.view.View#getWindowToken()} token
+     *      from.
+     */
+    public LocalFloatingToolbarPopup(Context context, View parent) {
+        mParent = Objects.requireNonNull(parent);
+        mContext = applyDefaultTheme(context);
+        mContentContainer = createContentContainer(mContext);
+        mPopupWindow = createPopupWindow(mContentContainer);
+        mMarginHorizontal = parent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+        mMarginVertical = parent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
+        mLineHeight = context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_height);
+        mIconTextSpacing = context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
+
+        // Interpolators
+        mLogAccelerateInterpolator = new LogAccelerateInterpolator();
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_slow_in);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.linear_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_linear_in);
+
+        // Drawables. Needed for views.
+        mArrow = mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme());
+        mArrow.setAutoMirrored(true);
+        mOverflow = mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme());
+        mOverflow.setAutoMirrored(true);
+        mToArrow = (AnimatedVectorDrawable) mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme());
+        mToArrow.setAutoMirrored(true);
+        mToOverflow = (AnimatedVectorDrawable) mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme());
+        mToOverflow.setAutoMirrored(true);
+
+        // Views
+        mOverflowButton = createOverflowButton();
+        mOverflowButtonSize = measure(mOverflowButton);
+        mMainPanel = createMainPanel();
+        mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext, mIconTextSpacing);
+        mOverflowPanel = createOverflowPanel();
+
+        // Animation. Need views.
+        mOverflowAnimationListener = createOverflowAnimationListener();
+        mOpenOverflowAnimation = new AnimationSet(true);
+        mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+        mCloseOverflowAnimation = new AnimationSet(true);
+        mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+        mShowAnimation = createEnterAnimation(mContentContainer);
+        mDismissAnimation = createExitAnimation(
+                mContentContainer,
+                150,  // startDelay
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                        mContentContainer.removeAllViews();
+                    }
+                });
+        mHideAnimation = createExitAnimation(
+                mContentContainer,
+                0,  // startDelay
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                    }
+                });
+    }
+
+    @Override
+    public boolean setOutsideTouchable(
+            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+        boolean ret = false;
+        if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
+            mPopupWindow.setOutsideTouchable(outsideTouchable);
+            mPopupWindow.setFocusable(!outsideTouchable);
+            mPopupWindow.update();
+            ret = true;
+        }
+        mPopupWindow.setOnDismissListener(onDismiss);
+        return ret;
+    }
+
+    /**
+     * Lays out buttons for the specified menu items.
+     * Requires a subsequent call to {@link FloatingToolbar#show()} to show the items.
+     */
+    private void layoutMenuItems(
+            List<MenuItem> menuItems,
+            MenuItem.OnMenuItemClickListener menuItemClickListener,
+            int suggestedWidth) {
+        cancelOverflowAnimations();
+        clearPanels();
+        updateMenuItems(menuItems, menuItemClickListener);
+        menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
+        if (!menuItems.isEmpty()) {
+            // Add remaining items to the overflow.
+            layoutOverflowPanelItems(menuItems);
+        }
+        updatePopupSize();
+    }
+
+    /**
+     * Updates the popup's menu items without rebuilding the widget.
+     * Use in place of layoutMenuItems() when the popup's views need not be reconstructed.
+     *
+     * @see #isLayoutRequired(List<MenuItem>)
+     */
+    private void updateMenuItems(
+            List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) {
+        mMenuItems.clear();
+        for (MenuItem menuItem : menuItems) {
+            mMenuItems.put(MenuItemRepr.of(menuItem), menuItem);
+        }
+        mOnMenuItemClickListener = menuItemClickListener;
+    }
+
+    /**
+     * Returns true if this popup needs a relayout to properly render the specified menu items.
+     */
+    private boolean isLayoutRequired(List<MenuItem> menuItems) {
+        return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
+    }
+
+    @Override
+    public void setWidthChanged(boolean widthChanged) {
+        mWidthChanged = widthChanged;
+    }
+
+    @Override
+    public void setSuggestedWidth(int suggestedWidth) {
+        // Check if there's been a substantial width spec change.
+        int difference = Math.abs(suggestedWidth - mSuggestedWidth);
+        mWidthChanged = difference > (mSuggestedWidth * 0.2);
+        mSuggestedWidth = suggestedWidth;
+    }
+
+    @Override
+    public void show(List<MenuItem> menuItems,
+            MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
+        if (isLayoutRequired(menuItems) || mWidthChanged) {
+            dismiss();
+            layoutMenuItems(menuItems, menuItemClickListener, mSuggestedWidth);
+        } else {
+            updateMenuItems(menuItems, menuItemClickListener);
+        }
+        if (!isShowing()) {
+            show(contentRect);
+        } else if (!mPreviousContentRect.equals(contentRect)) {
+            updateCoordinates(contentRect);
+        }
+        mWidthChanged = false;
+        mPreviousContentRect.set(contentRect);
+    }
+
+    private void show(Rect contentRectOnScreen) {
+        Objects.requireNonNull(contentRectOnScreen);
+
+        if (isShowing()) {
+            return;
+        }
+
+        mHidden = false;
+        mDismissed = false;
+        cancelDismissAndHideAnimations();
+        cancelOverflowAnimations();
+
+        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
+        preparePopupContent();
+        // We need to specify the position in window coordinates.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
+        // specify the popup position in screen coordinates.
+        mPopupWindow.showAtLocation(
+                mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y);
+        setTouchableSurfaceInsetsComputer();
+        runShowAnimation();
+    }
+
+    @Override
+    public void dismiss() {
+        if (mDismissed) {
+            return;
+        }
+
+        mHidden = false;
+        mDismissed = true;
+        mHideAnimation.cancel();
+
+        runDismissAnimation();
+        setZeroTouchableSurface();
+    }
+
+    @Override
+    public void hide() {
+        if (!isShowing()) {
+            return;
+        }
+
+        mHidden = true;
+        runHideAnimation();
+        setZeroTouchableSurface();
+    }
+
+    @Override
+    public boolean isShowing() {
+        return !mDismissed && !mHidden;
+    }
+
+    @Override
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+    /**
+     * Updates the coordinates of this popup.
+     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+     * This is a no-op if this popup is not showing.
+     */
+    private void updateCoordinates(Rect contentRectOnScreen) {
+        Objects.requireNonNull(contentRectOnScreen);
+
+        if (!isShowing() || !mPopupWindow.isShowing()) {
+            return;
+        }
+
+        cancelOverflowAnimations();
+        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
+        preparePopupContent();
+        // We need to specify the position in window coordinates.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
+        // specify the popup position in screen coordinates.
+        mPopupWindow.update(
+                mCoordsOnWindow.x, mCoordsOnWindow.y,
+                mPopupWindow.getWidth(), mPopupWindow.getHeight());
+    }
+
+    private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
+        refreshViewPort();
+
+        // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
+        // landscape.
+        final int x = Math.min(
+                contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+                mViewPortOnScreen.right - mPopupWindow.getWidth());
+
+        final int y;
+
+        final int availableHeightAboveContent =
+                contentRectOnScreen.top - mViewPortOnScreen.top;
+        final int availableHeightBelowContent =
+                mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
+
+        final int margin = 2 * mMarginVertical;
+        final int toolbarHeightWithVerticalMargin = mLineHeight + margin;
+
+        if (!hasOverflow()) {
+            if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) {
+                // There is enough space at the top of the content.
+                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) {
+                // There is enough space at the bottom of the content.
+                y = contentRectOnScreen.bottom;
+            } else if (availableHeightBelowContent >= mLineHeight) {
+                // Just enough space to fit the toolbar with no vertical margins.
+                y = contentRectOnScreen.bottom - mMarginVertical;
+            } else {
+                // Not enough space. Prefer to position as high as possible.
+                y = Math.max(
+                        mViewPortOnScreen.top,
+                        contentRectOnScreen.top - toolbarHeightWithVerticalMargin);
+            }
+        } else {
+            // Has an overflow.
+            final int minimumOverflowHeightWithMargin =
+                    calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin;
+            final int availableHeightThroughContentDown =
+                    mViewPortOnScreen.bottom - contentRectOnScreen.top
+                            + toolbarHeightWithVerticalMargin;
+            final int availableHeightThroughContentUp =
+                    contentRectOnScreen.bottom - mViewPortOnScreen.top
+                            + toolbarHeightWithVerticalMargin;
+
+            if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the top of the content rect for the overflow.
+                // Position above and open upwards.
+                updateOverflowHeight(availableHeightAboveContent - margin);
+                y = contentRectOnScreen.top - mPopupWindow.getHeight();
+                mOpenOverflowUpwards = true;
+            } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
+                    && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the top of the content rect for the main panel
+                // but not the overflow.
+                // Position above but open downwards.
+                updateOverflowHeight(availableHeightThroughContentDown - margin);
+                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+                mOpenOverflowUpwards = false;
+            } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the bottom of the content rect for the overflow.
+                // Position below and open downwards.
+                updateOverflowHeight(availableHeightBelowContent - margin);
+                y = contentRectOnScreen.bottom;
+                mOpenOverflowUpwards = false;
+            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin
+                    && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the bottom of the content rect for the main panel
+                // but not the overflow.
+                // Position below but open upwards.
+                updateOverflowHeight(availableHeightThroughContentUp - margin);
+                y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin
+                        - mPopupWindow.getHeight();
+                mOpenOverflowUpwards = true;
+            } else {
+                // Not enough space.
+                // Position at the top of the view port and open downwards.
+                updateOverflowHeight(mViewPortOnScreen.height() - margin);
+                y = mViewPortOnScreen.top;
+                mOpenOverflowUpwards = false;
+            }
+        }
+
+        // We later specify the location of PopupWindow relative to the attached window.
+        // The idea here is that 1) we can get the location of a View in both window coordinates
+        // and screen coordinates, where the offset between them should be equal to the window
+        // origin, and 2) we can use an arbitrary for this calculation while calculating the
+        // location of the rootview is supposed to be least expensive.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can avoid
+        // the following calculation.
+        mParent.getRootView().getLocationOnScreen(mTmpCoords);
+        int rootViewLeftOnScreen = mTmpCoords[0];
+        int rootViewTopOnScreen = mTmpCoords[1];
+        mParent.getRootView().getLocationInWindow(mTmpCoords);
+        int rootViewLeftOnWindow = mTmpCoords[0];
+        int rootViewTopOnWindow = mTmpCoords[1];
+        int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
+        int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
+        mCoordsOnWindow.set(
+                Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
+    }
+
+    /**
+     * Performs the "show" animation on the floating popup.
+     */
+    private void runShowAnimation() {
+        mShowAnimation.start();
+    }
+
+    /**
+     * Performs the "dismiss" animation on the floating popup.
+     */
+    private void runDismissAnimation() {
+        mDismissAnimation.start();
+    }
+
+    /**
+     * Performs the "hide" animation on the floating popup.
+     */
+    private void runHideAnimation() {
+        mHideAnimation.start();
+    }
+
+    private void cancelDismissAndHideAnimations() {
+        mDismissAnimation.cancel();
+        mHideAnimation.cancel();
+    }
+
+    private void cancelOverflowAnimations() {
+        mContentContainer.clearAnimation();
+        mMainPanel.animate().cancel();
+        mOverflowPanel.animate().cancel();
+        mToArrow.stop();
+        mToOverflow.stop();
+    }
+
+    private void openOverflow() {
+        final int targetWidth = mOverflowPanelSize.getWidth();
+        final int targetHeight = mOverflowPanelSize.getHeight();
+        final int startWidth = mContentContainer.getWidth();
+        final int startHeight = mContentContainer.getHeight();
+        final float startY = mContentContainer.getY();
+        final float left = mContentContainer.getX();
+        final float right = left + mContentContainer.getWidth();
+        Animation widthAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
+                setWidth(mContentContainer, startWidth + deltaWidth);
+                if (isInRTLMode()) {
+                    mContentContainer.setX(left);
+
+                    // Lock the panels in place.
+                    mMainPanel.setX(0);
+                    mOverflowPanel.setX(0);
+                } else {
+                    mContentContainer.setX(right - mContentContainer.getWidth());
+
+                    // Offset the panels' positions so they look like they're locked in place
+                    // on the screen.
+                    mMainPanel.setX(mContentContainer.getWidth() - startWidth);
+                    mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth);
+                }
+            }
+        };
+        Animation heightAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
+                setHeight(mContentContainer, startHeight + deltaHeight);
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(
+                            startY - (mContentContainer.getHeight() - startHeight));
+                    positionContentYCoordinatesIfOpeningOverflowUpwards();
+                }
+            }
+        };
+        final float overflowButtonStartX = mOverflowButton.getX();
+        final float overflowButtonTargetX =
+                isInRTLMode() ? overflowButtonStartX + targetWidth - mOverflowButton.getWidth()
+                        : overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
+        Animation overflowButtonAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                float overflowButtonX = overflowButtonStartX
+                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                float deltaContainerWidth =
+                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
+                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                mOverflowButton.setX(actualOverflowButtonX);
+            }
+        };
+        widthAnimation.setInterpolator(mLogAccelerateInterpolator);
+        widthAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        heightAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        mOpenOverflowAnimation.getAnimations().clear();
+        mOpenOverflowAnimation.getAnimations().clear();
+        mOpenOverflowAnimation.addAnimation(widthAnimation);
+        mOpenOverflowAnimation.addAnimation(heightAnimation);
+        mOpenOverflowAnimation.addAnimation(overflowButtonAnimation);
+        mContentContainer.startAnimation(mOpenOverflowAnimation);
+        mIsOverflowOpen = true;
+        mMainPanel.animate()
+                .alpha(0).withLayer()
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(250)
+                .start();
+        mOverflowPanel.setAlpha(1); // fadeIn in 0ms.
+    }
+
+    private void closeOverflow() {
+        final int targetWidth = mMainPanelSize.getWidth();
+        final int startWidth = mContentContainer.getWidth();
+        final float left = mContentContainer.getX();
+        final float right = left + mContentContainer.getWidth();
+        Animation widthAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
+                setWidth(mContentContainer, startWidth + deltaWidth);
+                if (isInRTLMode()) {
+                    mContentContainer.setX(left);
+
+                    // Lock the panels in place.
+                    mMainPanel.setX(0);
+                    mOverflowPanel.setX(0);
+                } else {
+                    mContentContainer.setX(right - mContentContainer.getWidth());
+
+                    // Offset the panels' positions so they look like they're locked in place
+                    // on the screen.
+                    mMainPanel.setX(mContentContainer.getWidth() - targetWidth);
+                    mOverflowPanel.setX(mContentContainer.getWidth() - startWidth);
+                }
+            }
+        };
+        final int targetHeight = mMainPanelSize.getHeight();
+        final int startHeight = mContentContainer.getHeight();
+        final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
+        Animation heightAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
+                setHeight(mContentContainer, startHeight + deltaHeight);
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(bottom - mContentContainer.getHeight());
+                    positionContentYCoordinatesIfOpeningOverflowUpwards();
+                }
+            }
+        };
+        final float overflowButtonStartX = mOverflowButton.getX();
+        final float overflowButtonTargetX =
+                isInRTLMode() ? overflowButtonStartX - startWidth + mOverflowButton.getWidth()
+                        : overflowButtonStartX + startWidth - mOverflowButton.getWidth();
+        Animation overflowButtonAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                float overflowButtonX = overflowButtonStartX
+                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                float deltaContainerWidth =
+                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
+                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                mOverflowButton.setX(actualOverflowButtonX);
+            }
+        };
+        widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        widthAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setInterpolator(mLogAccelerateInterpolator);
+        heightAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        mCloseOverflowAnimation.getAnimations().clear();
+        mCloseOverflowAnimation.addAnimation(widthAnimation);
+        mCloseOverflowAnimation.addAnimation(heightAnimation);
+        mCloseOverflowAnimation.addAnimation(overflowButtonAnimation);
+        mContentContainer.startAnimation(mCloseOverflowAnimation);
+        mIsOverflowOpen = false;
+        mMainPanel.animate()
+                .alpha(1).withLayer()
+                .setInterpolator(mFastOutLinearInInterpolator)
+                .setDuration(100)
+                .start();
+        mOverflowPanel.animate()
+                .alpha(0).withLayer()
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(150)
+                .start();
+    }
+
+    /**
+     * Defines the position of the floating toolbar popup panels when transition animation has
+     * stopped.
+     */
+    private void setPanelsStatesAtRestingPosition() {
+        mOverflowButton.setEnabled(true);
+        mOverflowPanel.awakenScrollBars();
+
+        if (mIsOverflowOpen) {
+            // Set open state.
+            final Size containerSize = mOverflowPanelSize;
+            setSize(mContentContainer, containerSize);
+            mMainPanel.setAlpha(0);
+            mMainPanel.setVisibility(View.INVISIBLE);
+            mOverflowPanel.setAlpha(1);
+            mOverflowPanel.setVisibility(View.VISIBLE);
+            mOverflowButton.setImageDrawable(mArrow);
+            mOverflowButton.setContentDescription(mContext.getString(
+                    R.string.floating_toolbar_close_overflow_description));
+
+            // Update x-coordinates depending on RTL state.
+            if (isInRTLMode()) {
+                mContentContainer.setX(mMarginHorizontal);  // align left
+                mMainPanel.setX(0);  // align left
+                mOverflowButton.setX(// align right
+                        containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                mOverflowPanel.setX(0);  // align left
+            } else {
+                mContentContainer.setX(// align right
+                        mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                mMainPanel.setX(-mContentContainer.getX());  // align right
+                mOverflowButton.setX(0);  // align left
+                mOverflowPanel.setX(0);  // align left
+            }
+
+            // Update y-coordinates depending on overflow's open direction.
+            if (mOpenOverflowUpwards) {
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setY(// align bottom
+                        containerSize.getHeight() - mContentContainer.getHeight());
+                mOverflowButton.setY(// align bottom
+                        containerSize.getHeight() - mOverflowButtonSize.getHeight());
+                mOverflowPanel.setY(0);  // align top
+            } else {
+                // opens downwards.
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setY(0);  // align top
+                mOverflowButton.setY(0);  // align top
+                mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+            }
+        } else {
+            // Overflow not open. Set closed state.
+            final Size containerSize = mMainPanelSize;
+            setSize(mContentContainer, containerSize);
+            mMainPanel.setAlpha(1);
+            mMainPanel.setVisibility(View.VISIBLE);
+            mOverflowPanel.setAlpha(0);
+            mOverflowPanel.setVisibility(View.INVISIBLE);
+            mOverflowButton.setImageDrawable(mOverflow);
+            mOverflowButton.setContentDescription(mContext.getString(
+                    R.string.floating_toolbar_open_overflow_description));
+
+            if (hasOverflow()) {
+                // Update x-coordinates depending on RTL state.
+                if (isInRTLMode()) {
+                    mContentContainer.setX(mMarginHorizontal);  // align left
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(0);  // align left
+                    mOverflowPanel.setX(0);  // align left
+                } else {
+                    mContentContainer.setX(// align right
+                            mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(// align right
+                            containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                    mOverflowPanel.setX(// align right
+                            containerSize.getWidth() - mOverflowPanelSize.getWidth());
+                }
+
+                // Update y-coordinates depending on overflow's open direction.
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(// align bottom
+                            mMarginVertical + mOverflowPanelSize.getHeight()
+                                    - containerSize.getHeight());
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(// align bottom
+                            containerSize.getHeight() - mOverflowPanelSize.getHeight());
+                } else {
+                    // opens downwards.
+                    mContentContainer.setY(mMarginVertical);  // align top
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+                }
+            } else {
+                // No overflow.
+                mContentContainer.setX(mMarginHorizontal);  // align left
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setX(0);  // align left
+                mMainPanel.setY(0);  // align top
+            }
+        }
+    }
+
+    private void updateOverflowHeight(int suggestedHeight) {
+        if (hasOverflow()) {
+            final int maxItemSize =
+                    (suggestedHeight - mOverflowButtonSize.getHeight()) / mLineHeight;
+            final int newHeight = calculateOverflowHeight(maxItemSize);
+            if (mOverflowPanelSize.getHeight() != newHeight) {
+                mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight);
+            }
+            setSize(mOverflowPanel, mOverflowPanelSize);
+            if (mIsOverflowOpen) {
+                setSize(mContentContainer, mOverflowPanelSize);
+                if (mOpenOverflowUpwards) {
+                    final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight;
+                    mContentContainer.setY(mContentContainer.getY() + deltaHeight);
+                    mOverflowButton.setY(mOverflowButton.getY() - deltaHeight);
+                }
+            } else {
+                setSize(mContentContainer, mMainPanelSize);
+            }
+            updatePopupSize();
+        }
+    }
+
+    private void updatePopupSize() {
+        int width = 0;
+        int height = 0;
+        if (mMainPanelSize != null) {
+            width = Math.max(width, mMainPanelSize.getWidth());
+            height = Math.max(height, mMainPanelSize.getHeight());
+        }
+        if (mOverflowPanelSize != null) {
+            width = Math.max(width, mOverflowPanelSize.getWidth());
+            height = Math.max(height, mOverflowPanelSize.getHeight());
+        }
+        mPopupWindow.setWidth(width + mMarginHorizontal * 2);
+        mPopupWindow.setHeight(height + mMarginVertical * 2);
+        maybeComputeTransitionDurationScale();
+    }
+
+    private void refreshViewPort() {
+        mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
+    }
+
+    private int getAdjustedToolbarWidth(int suggestedWidth) {
+        int width = suggestedWidth;
+        refreshViewPort();
+        int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+        if (width <= 0) {
+            width = mParent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
+        }
+        return Math.min(width, maximumWidth);
+    }
+
+    /**
+     * Sets the touchable region of this popup to be zero. This means that all touch events on
+     * this popup will go through to the surface behind it.
+     */
+    private void setZeroTouchableSurface() {
+        mTouchableRegion.setEmpty();
+    }
+
+    /**
+     * Sets the touchable region of this popup to be the area occupied by its content.
+     */
+    private void setContentAreaAsTouchableSurface() {
+        Objects.requireNonNull(mMainPanelSize);
+        final int width;
+        final int height;
+        if (mIsOverflowOpen) {
+            Objects.requireNonNull(mOverflowPanelSize);
+            width = mOverflowPanelSize.getWidth();
+            height = mOverflowPanelSize.getHeight();
+        } else {
+            width = mMainPanelSize.getWidth();
+            height = mMainPanelSize.getHeight();
+        }
+        mTouchableRegion.set(
+                (int) mContentContainer.getX(),
+                (int) mContentContainer.getY(),
+                (int) mContentContainer.getX() + width,
+                (int) mContentContainer.getY() + height);
+    }
+
+    /**
+     * Make the touchable area of this popup be the area specified by mTouchableRegion.
+     * This should be called after the popup window has been dismissed (dismiss/hide)
+     * and is probably being re-shown with a new content root view.
+     */
+    private void setTouchableSurfaceInsetsComputer() {
+        ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
+                .getRootView()
+                .getViewTreeObserver();
+        viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
+        viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
+    }
+
+    private boolean isInRTLMode() {
+        return mContext.getApplicationInfo().hasRtlSupport()
+                && mContext.getResources().getConfiguration().getLayoutDirection()
+                == View.LAYOUT_DIRECTION_RTL;
+    }
+
+    private boolean hasOverflow() {
+        return mOverflowPanelSize != null;
+    }
+
+    /**
+     * Fits as many menu items in the main panel and returns a list of the menu items that
+     * were not fit in.
+     *
+     * @return The menu items that are not included in this main panel.
+     */
+    public List<MenuItem> layoutMainPanelItems(
+            List<MenuItem> menuItems, final int toolbarWidth) {
+        Objects.requireNonNull(menuItems);
+
+        int availableWidth = toolbarWidth;
+
+        final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>();
+        // add the overflow menu items to the end of the remainingMenuItems list.
+        final LinkedList<MenuItem> overflowMenuItems = new LinkedList();
+        for (MenuItem menuItem : menuItems) {
+            if (menuItem.getItemId() != android.R.id.textAssist
+                    && menuItem.requiresOverflow()) {
+                overflowMenuItems.add(menuItem);
+            } else {
+                remainingMenuItems.add(menuItem);
+            }
+        }
+        remainingMenuItems.addAll(overflowMenuItems);
+
+        mMainPanel.removeAllViews();
+        mMainPanel.setPaddingRelative(0, 0, 0, 0);
+
+        int lastGroupId = -1;
+        boolean isFirstItem = true;
+        while (!remainingMenuItems.isEmpty()) {
+            final MenuItem menuItem = remainingMenuItems.peek();
+
+            // if this is the first item, regardless of requiresOverflow(), it should be
+            // displayed on the main panel. Otherwise all items including this one will be
+            // overflow items, and should be displayed in overflow panel.
+            if (!isFirstItem && menuItem.requiresOverflow()) {
+                break;
+            }
+
+            final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
+            final View menuItemButton = createMenuItemButton(
+                    mContext, menuItem, mIconTextSpacing, showIcon);
+            if (!showIcon && menuItemButton instanceof LinearLayout) {
+                ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
+            }
+
+            // Adding additional start padding for the first button to even out button spacing.
+            if (isFirstItem) {
+                menuItemButton.setPaddingRelative(
+                        (int) (1.5 * menuItemButton.getPaddingStart()),
+                        menuItemButton.getPaddingTop(),
+                        menuItemButton.getPaddingEnd(),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            // Adding additional end padding for the last button to even out button spacing.
+            boolean isLastItem = remainingMenuItems.size() == 1;
+            if (isLastItem) {
+                menuItemButton.setPaddingRelative(
+                        menuItemButton.getPaddingStart(),
+                        menuItemButton.getPaddingTop(),
+                        (int) (1.5 * menuItemButton.getPaddingEnd()),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            final int menuItemButtonWidth = Math.min(
+                    menuItemButton.getMeasuredWidth(), toolbarWidth);
+
+            // Check if we can fit an item while reserving space for the overflowButton.
+            final boolean canFitWithOverflow =
+                    menuItemButtonWidth <= availableWidth - mOverflowButtonSize.getWidth();
+            final boolean canFitNoOverflow =
+                    isLastItem && menuItemButtonWidth <= availableWidth;
+            if (canFitWithOverflow || canFitNoOverflow) {
+                setButtonTagAndClickListener(menuItemButton, menuItem);
+                // Set tooltips for main panel items, but not overflow items (b/35726766).
+                menuItemButton.setTooltipText(menuItem.getTooltipText());
+                mMainPanel.addView(menuItemButton);
+                final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
+                params.width = menuItemButtonWidth;
+                menuItemButton.setLayoutParams(params);
+                availableWidth -= menuItemButtonWidth;
+                remainingMenuItems.pop();
+            } else {
+                break;
+            }
+            lastGroupId = menuItem.getGroupId();
+            isFirstItem = false;
+        }
+
+        if (!remainingMenuItems.isEmpty()) {
+            // Reserve space for overflowButton.
+            mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0);
+        }
+
+        mMainPanelSize = measure(mMainPanel);
+        return remainingMenuItems;
+    }
+
+    private void layoutOverflowPanelItems(List<MenuItem> menuItems) {
+        ArrayAdapter<MenuItem> overflowPanelAdapter =
+                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        overflowPanelAdapter.clear();
+        final int size = menuItems.size();
+        for (int i = 0; i < size; i++) {
+            overflowPanelAdapter.add(menuItems.get(i));
+        }
+        mOverflowPanel.setAdapter(overflowPanelAdapter);
+        if (mOpenOverflowUpwards) {
+            mOverflowPanel.setY(0);
+        } else {
+            mOverflowPanel.setY(mOverflowButtonSize.getHeight());
+        }
+
+        int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
+        int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
+        mOverflowPanelSize = new Size(width, height);
+        setSize(mOverflowPanel, mOverflowPanelSize);
+    }
+
+    /**
+     * Resets the content container and appropriately position it's panels.
+     */
+    private void preparePopupContent() {
+        mContentContainer.removeAllViews();
+
+        // Add views in the specified order so they stack up as expected.
+        // Order: overflowPanel, mainPanel, overflowButton.
+        if (hasOverflow()) {
+            mContentContainer.addView(mOverflowPanel);
+        }
+        mContentContainer.addView(mMainPanel);
+        if (hasOverflow()) {
+            mContentContainer.addView(mOverflowButton);
+        }
+        setPanelsStatesAtRestingPosition();
+        setContentAreaAsTouchableSurface();
+
+        // The positioning of contents in RTL is wrong when the view is first rendered.
+        // Hide the view and post a runnable to recalculate positions and render the view.
+        // TODO: Investigate why this happens and fix.
+        if (isInRTLMode()) {
+            mContentContainer.setAlpha(0);
+            mContentContainer.post(mPreparePopupContentRTLHelper);
+        }
+    }
+
+    /**
+     * Clears out the panels and their container. Resets their calculated sizes.
+     */
+    private void clearPanels() {
+        mOverflowPanelSize = null;
+        mMainPanelSize = null;
+        mIsOverflowOpen = false;
+        mMainPanel.removeAllViews();
+        ArrayAdapter<MenuItem> overflowPanelAdapter =
+                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        overflowPanelAdapter.clear();
+        mOverflowPanel.setAdapter(overflowPanelAdapter);
+        mContentContainer.removeAllViews();
+    }
+
+    private void positionContentYCoordinatesIfOpeningOverflowUpwards() {
+        if (mOpenOverflowUpwards) {
+            mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight());
+            mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight());
+            mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight());
+        }
+    }
+
+    private int getOverflowWidth() {
+        int overflowWidth = 0;
+        final int count = mOverflowPanel.getAdapter().getCount();
+        for (int i = 0; i < count; i++) {
+            MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
+            overflowWidth =
+                    Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
+        }
+        return overflowWidth;
+    }
+
+    private int calculateOverflowHeight(int maxItemSize) {
+        // Maximum of 4 items, minimum of 2 if the overflow has to scroll.
+        int actualSize = Math.min(
+                MAX_OVERFLOW_SIZE,
+                Math.min(
+                        Math.max(MIN_OVERFLOW_SIZE, maxItemSize),
+                        mOverflowPanel.getCount()));
+        int extension = 0;
+        if (actualSize < mOverflowPanel.getCount()) {
+            // The overflow will require scrolling to get to all the items.
+            // Extend the height so that part of the hidden items is displayed.
+            extension = (int) (mLineHeight * 0.5f);
+        }
+        return actualSize * mLineHeight
+                + mOverflowButtonSize.getHeight()
+                + extension;
+    }
+
+    private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) {
+        menuItemButton.setTag(MenuItemRepr.of(menuItem));
+        menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
+    }
+
+    /**
+     * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.*
+     * animations. See comment about this in the code.
+     */
+    private int getAdjustedDuration(int originalDuration) {
+        if (mTransitionDurationScale < 150) {
+            // For smaller transition, decrease the time.
+            return Math.max(originalDuration - 50, 0);
+        } else if (mTransitionDurationScale > 300) {
+            // For bigger transition, increase the time.
+            return originalDuration + 50;
+        }
+
+        // Scale the animation duration with getDurationScale(). This allows
+        // android.view.animation.* animations to scale just like android.animation.* animations
+        // when  animator duration scale is adjusted in "Developer Options".
+        // For this reason, do not use this method for android.animation.* animations.
+        return (int) (originalDuration * ValueAnimator.getDurationScale());
+    }
+
+    private void maybeComputeTransitionDurationScale() {
+        if (mMainPanelSize != null && mOverflowPanelSize != null) {
+            int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
+            int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
+            mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h)
+                    / mContentContainer.getContext().getResources().getDisplayMetrics().density);
+        }
+    }
+
+    private ViewGroup createMainPanel() {
+        ViewGroup mainPanel = new LinearLayout(mContext) {
+            @Override
+            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+                if (isOverflowAnimating()) {
+                    // Update widthMeasureSpec to make sure that this view is not clipped
+                    // as we offset its coordinates with respect to its parent.
+                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            mMainPanelSize.getWidth(),
+                            MeasureSpec.EXACTLY);
+                }
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+
+            @Override
+            public boolean onInterceptTouchEvent(MotionEvent ev) {
+                // Intercept the touch event while the overflow is animating.
+                return isOverflowAnimating();
+            }
+        };
+        return mainPanel;
+    }
+
+    private ImageButton createOverflowButton() {
+        final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext)
+                .inflate(R.layout.floating_popup_overflow_button, null);
+        overflowButton.setImageDrawable(mOverflow);
+        overflowButton.setOnClickListener(v -> {
+            if (mIsOverflowOpen) {
+                overflowButton.setImageDrawable(mToOverflow);
+                mToOverflow.start();
+                closeOverflow();
+            } else {
+                overflowButton.setImageDrawable(mToArrow);
+                mToArrow.start();
+                openOverflow();
+            }
+        });
+        return overflowButton;
+    }
+
+    private OverflowPanel createOverflowPanel() {
+        final OverflowPanel overflowPanel = new OverflowPanel(this);
+        overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+        overflowPanel.setDivider(null);
+        overflowPanel.setDividerHeight(0);
+
+        final ArrayAdapter adapter =
+                new ArrayAdapter<MenuItem>(mContext, 0) {
+                    @Override
+                    public View getView(int position, View convertView, ViewGroup parent) {
+                        return mOverflowPanelViewHelper.getView(
+                                getItem(position), mOverflowPanelSize.getWidth(), convertView);
+                    }
+                };
+        overflowPanel.setAdapter(adapter);
+
+        overflowPanel.setOnItemClickListener((parent, view, position, id) -> {
+            MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
+            if (mOnMenuItemClickListener != null) {
+                mOnMenuItemClickListener.onMenuItemClick(menuItem);
+            }
+        });
+
+        return overflowPanel;
+    }
+
+    private boolean isOverflowAnimating() {
+        final boolean overflowOpening = mOpenOverflowAnimation.hasStarted()
+                && !mOpenOverflowAnimation.hasEnded();
+        final boolean overflowClosing = mCloseOverflowAnimation.hasStarted()
+                && !mCloseOverflowAnimation.hasEnded();
+        return overflowOpening || overflowClosing;
+    }
+
+    private Animation.AnimationListener createOverflowAnimationListener() {
+        Animation.AnimationListener listener = new Animation.AnimationListener() {
+            @Override
+            public void onAnimationStart(Animation animation) {
+                // Disable the overflow button while it's animating.
+                // It will be re-enabled when the animation stops.
+                mOverflowButton.setEnabled(false);
+                // Ensure both panels have visibility turned on when the overflow animation
+                // starts.
+                mMainPanel.setVisibility(View.VISIBLE);
+                mOverflowPanel.setVisibility(View.VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                // Posting this because it seems like this is called before the animation
+                // actually ends.
+                mContentContainer.post(() -> {
+                    setPanelsStatesAtRestingPosition();
+                    setContentAreaAsTouchableSurface();
+                });
+            }
+
+            @Override
+            public void onAnimationRepeat(Animation animation) {
+            }
+        };
+        return listener;
+    }
+
+    private static Size measure(View view) {
+        Preconditions.checkState(view.getParent() == null);
+        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
+    }
+
+    private static void setSize(View view, int width, int height) {
+        view.setMinimumWidth(width);
+        view.setMinimumHeight(height);
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params;
+        params.width = width;
+        params.height = height;
+        view.setLayoutParams(params);
+    }
+
+    private static void setSize(View view, Size size) {
+        setSize(view, size.getWidth(), size.getHeight());
+    }
+
+    private static void setWidth(View view, int width) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        setSize(view, width, params.height);
+    }
+
+    private static void setHeight(View view, int height) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        setSize(view, params.width, height);
+    }
+
+    /**
+     * A custom ListView for the overflow panel.
+     */
+    private static final class OverflowPanel extends ListView {
+
+        private final LocalFloatingToolbarPopup mPopup;
+
+        OverflowPanel(LocalFloatingToolbarPopup popup) {
+            super(Objects.requireNonNull(popup).mContext);
+            this.mPopup = popup;
+            setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
+            setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            // Update heightMeasureSpec to make sure that this view is not clipped
+            // as we offset it's coordinates with respect to its parent.
+            int height = mPopup.mOverflowPanelSize.getHeight()
+                    - mPopup.mOverflowButtonSize.getHeight();
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            if (mPopup.isOverflowAnimating()) {
+                // Eat the touch event.
+                return true;
+            }
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected boolean awakenScrollBars() {
+            return super.awakenScrollBars();
+        }
+    }
+
+    /**
+     * A custom interpolator used for various floating toolbar animations.
+     */
+    private static final class LogAccelerateInterpolator implements Interpolator {
+
+        private static final int BASE = 100;
+        private static final float LOGS_SCALE = 1f / computeLog(1, BASE);
+
+        private static float computeLog(float t, int base) {
+            return (float) (1 - Math.pow(base, -t));
+        }
+
+        @Override
+        public float getInterpolation(float t) {
+            return 1 - computeLog(1 - t, BASE) * LOGS_SCALE;
+        }
+    }
+
+    /**
+     * A helper for generating views for the overflow panel.
+     */
+    private static final class OverflowPanelViewHelper {
+
+        private final View mCalculator;
+        private final int mIconTextSpacing;
+        private final int mSidePadding;
+
+        private final Context mContext;
+
+        OverflowPanelViewHelper(Context context, int iconTextSpacing) {
+            mContext = Objects.requireNonNull(context);
+            mIconTextSpacing = iconTextSpacing;
+            mSidePadding = context.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_side_padding);
+            mCalculator = createMenuButton(null);
+        }
+
+        public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
+            Objects.requireNonNull(menuItem);
+            if (convertView != null) {
+                updateMenuItemButton(
+                        convertView, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            } else {
+                convertView = createMenuButton(menuItem);
+            }
+            convertView.setMinimumWidth(minimumWidth);
+            return convertView;
+        }
+
+        public int calculateWidth(MenuItem menuItem) {
+            updateMenuItemButton(
+                    mCalculator, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            mCalculator.measure(
+                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+            return mCalculator.getMeasuredWidth();
+        }
+
+        private View createMenuButton(MenuItem menuItem) {
+            View button = createMenuItemButton(
+                    mContext, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            button.setPadding(mSidePadding, 0, mSidePadding, 0);
+            return button;
+        }
+
+        private boolean shouldShowIcon(MenuItem menuItem) {
+            if (menuItem != null) {
+                return menuItem.getGroupId() == android.R.id.textAssist;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Creates and returns a menu button for the specified menu item.
+     */
+    private static View createMenuItemButton(
+            Context context, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+        final View menuItemButton = LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_menu_button, null);
+        if (menuItem != null) {
+            updateMenuItemButton(menuItemButton, menuItem, iconTextSpacing, showIcon);
+        }
+        return menuItemButton;
+    }
+
+    /**
+     * Updates the specified menu item button with the specified menu item data.
+     */
+    private static void updateMenuItemButton(
+            View menuItemButton, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+        final TextView buttonText = menuItemButton.findViewById(
+                R.id.floating_toolbar_menu_item_text);
+        buttonText.setEllipsize(null);
+        if (TextUtils.isEmpty(menuItem.getTitle())) {
+            buttonText.setVisibility(View.GONE);
+        } else {
+            buttonText.setVisibility(View.VISIBLE);
+            buttonText.setText(menuItem.getTitle());
+        }
+        final ImageView buttonIcon = menuItemButton.findViewById(
+                R.id.floating_toolbar_menu_item_image);
+        if (menuItem.getIcon() == null || !showIcon) {
+            buttonIcon.setVisibility(View.GONE);
+            if (buttonText != null) {
+                buttonText.setPaddingRelative(0, 0, 0, 0);
+            }
+        } else {
+            buttonIcon.setVisibility(View.VISIBLE);
+            buttonIcon.setImageDrawable(menuItem.getIcon());
+            if (buttonText != null) {
+                buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
+            }
+        }
+        final CharSequence contentDescription = menuItem.getContentDescription();
+        if (TextUtils.isEmpty(contentDescription)) {
+            menuItemButton.setContentDescription(menuItem.getTitle());
+        } else {
+            menuItemButton.setContentDescription(contentDescription);
+        }
+    }
+
+    private static ViewGroup createContentContainer(Context context) {
+        ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_container, null);
+        contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
+        contentContainer.setClipToOutline(true);
+        return contentContainer;
+    }
+
+    private static PopupWindow createPopupWindow(ViewGroup content) {
+        ViewGroup popupContentHolder = new LinearLayout(content.getContext());
+        PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+        // TODO: Use .setIsLaidOutInScreen(true) instead of .setClippingEnabled(false)
+        // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
+        popupWindow.setClippingEnabled(false);
+        popupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+        popupWindow.setAnimationStyle(0);
+        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        content.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        popupContentHolder.addView(content);
+        return popupWindow;
+    }
+
+    /**
+     * Creates an "appear" animation for the specified view.
+     *
+     * @param view  The view to animate
+     */
+    private static AnimatorSet createEnterAnimation(View view) {
+        AnimatorSet animation = new AnimatorSet();
+        animation.playTogether(
+                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
+        return animation;
+    }
+
+    /**
+     * Creates a "disappear" animation for the specified view.
+     *
+     * @param view  The view to animate
+     * @param startDelay  The start delay of the animation
+     * @param listener  The animation listener
+     */
+    private static AnimatorSet createExitAnimation(
+            View view, int startDelay, Animator.AnimatorListener listener) {
+        AnimatorSet animation =  new AnimatorSet();
+        animation.playTogether(
+                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
+        animation.setStartDelay(startDelay);
+        animation.addListener(listener);
+        return animation;
+    }
+
+    /**
+     * Returns a re-themed context with controlled look and feel for views.
+     */
+    private static Context applyDefaultTheme(Context originalContext) {
+        TypedArray a = originalContext.obtainStyledAttributes(new int[]{R.attr.isLightTheme});
+        boolean isLightTheme = a.getBoolean(0, true);
+        int themeId =
+                isLightTheme ? R.style.Theme_DeviceDefault_Light : R.style.Theme_DeviceDefault;
+        a.recycle();
+        return new ContextThemeWrapper(originalContext, themeId);
+    }
+
+    /**
+     * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
+     */
+    @VisibleForTesting
+    public static final class MenuItemRepr {
+
+        public final int itemId;
+        public final int groupId;
+        @Nullable public final String title;
+        @Nullable private final Drawable mIcon;
+
+        private MenuItemRepr(
+                int itemId, int groupId, @Nullable CharSequence title, @Nullable Drawable icon) {
+            this.itemId = itemId;
+            this.groupId = groupId;
+            this.title = (title == null) ? null : title.toString();
+            mIcon = icon;
+        }
+
+        /**
+         * Creates an instance of MenuItemRepr for the specified menu item.
+         */
+        public static MenuItemRepr of(MenuItem menuItem) {
+            return new MenuItemRepr(
+                    menuItem.getItemId(),
+                    menuItem.getGroupId(),
+                    menuItem.getTitle(),
+                    menuItem.getIcon());
+        }
+
+        /**
+         * Returns this object's hashcode.
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hash(itemId, groupId, title, mIcon);
+        }
+
+        /**
+         * Returns true if this object is the same as the specified object.
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (!(o instanceof MenuItemRepr)) {
+                return false;
+            }
+            final MenuItemRepr other = (MenuItemRepr) o;
+            return itemId == other.itemId
+                    && groupId == other.groupId
+                    && TextUtils.equals(title, other.title)
+                    // Many Drawables (icons) do not implement equals(). Using equals() here instead
+                    // of reference comparisons in case a Drawable subclass implements equals().
+                    && Objects.equals(mIcon, other.mIcon);
+        }
+
+        /**
+         * Returns true if the two menu item collections are the same based on MenuItemRepr.
+         */
+        public static boolean reprEquals(
+                Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) {
+            if (menuItems1.size() != menuItems2.size()) {
+                return false;
+            }
+
+            final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator();
+            for (MenuItem menuItem1 : menuItems1) {
+                final MenuItem menuItem2 = menuItems2Iter.next();
+                if (!MenuItemRepr.of(menuItem1).equals(MenuItemRepr.of(menuItem2))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/OWNERS b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
new file mode 100644
index 0000000..ed9425c
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/selectiontoolbar/OWNERS
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
new file mode 100644
index 0000000..b3a8128
--- /dev/null
+++ b/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.floatingtoolbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.selectiontoolbar.SelectionToolbarManager;
+import android.widget.PopupWindow;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A popup window used by the floating toolbar to render menu items in the remote system process.
+ *
+ * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
+ * to transition between panels.
+ */
+public final class RemoteFloatingToolbarPopup implements FloatingToolbarPopup {
+
+    private final SelectionToolbarManager mSelectionToolbarManager;
+    // Parent for the popup window.
+    private final View mParent;
+
+    public RemoteFloatingToolbarPopup(Context context, View parent) {
+        // TODO: implement it
+        mParent = Objects.requireNonNull(parent);
+        mSelectionToolbarManager = context.getSystemService(SelectionToolbarManager.class);
+    }
+
+    @Override
+    public void show(List<MenuItem> menuItems,
+            MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
+        // TODO: implement it
+    }
+
+    @Override
+    public void hide() {
+        // TODO: implement it
+    }
+
+    @Override
+    public void setSuggestedWidth(int suggestedWidth) {
+        // TODO: implement it
+    }
+
+    @Override
+    public void setWidthChanged(boolean widthChanged) {
+        // no-op
+    }
+
+    @Override
+    public void dismiss() {
+        // TODO: implement it
+    }
+
+    @Override
+    public boolean isHidden() {
+        return false;
+    }
+
+    @Override
+    public boolean isShowing() {
+        return false;
+    }
+
+    @Override
+    public boolean setOutsideTouchable(boolean outsideTouchable,
+            PopupWindow.OnDismissListener onDismiss) {
+        return false;
+    }
+}
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
deleted file mode 100644
index 26ff192..0000000
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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 android.os.StrictMode;
-import android.util.Log;
-import android.util.Slog;
-
-import dalvik.system.SocketTagger;
-
-import java.io.FileDescriptor;
-import java.net.SocketException;
-
-/**
- * Assigns tags to sockets for traffic stats.
- */
-public final class NetworkManagementSocketTagger extends SocketTagger {
-    private static final String TAG = "NetworkManagementSocketTagger";
-    private static final boolean LOGD = false;
-
-    private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
-        @Override
-        protected SocketTags initialValue() {
-            return new SocketTags();
-        }
-    };
-
-    public static void install() {
-        SocketTagger.set(new NetworkManagementSocketTagger());
-    }
-
-    public static int setThreadSocketStatsTag(int tag) {
-        final int old = threadSocketTags.get().statsTag;
-        threadSocketTags.get().statsTag = tag;
-        return old;
-    }
-
-    public static int getThreadSocketStatsTag() {
-        return threadSocketTags.get().statsTag;
-    }
-
-    public static int setThreadSocketStatsUid(int uid) {
-        final int old = threadSocketTags.get().statsUid;
-        threadSocketTags.get().statsUid = uid;
-        return old;
-    }
-
-    public static int getThreadSocketStatsUid() {
-        return threadSocketTags.get().statsUid;
-    }
-
-    @Override
-    public void tag(FileDescriptor fd) throws SocketException {
-        final SocketTags options = threadSocketTags.get();
-        if (LOGD) {
-            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
-                    + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
-        }
-        if (options.statsTag == -1 && StrictMode.vmUntaggedSocketEnabled()) {
-            StrictMode.onUntaggedSocket();
-        }
-        // TODO: skip tagging when options would be no-op
-        tagSocketFd(fd, options.statsTag, options.statsUid);
-    }
-
-    private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
-        if (tag == -1 && uid == -1) return;
-
-        final int errno = native_tagSocketFd(fd, tag, uid);
-        if (errno < 0) {
-            Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
-                    + tag + ", "
-                    + uid + ") failed with errno" + errno);
-        }
-    }
-
-    @Override
-    public void untag(FileDescriptor fd) throws SocketException {
-        if (LOGD) {
-            Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
-        }
-        unTagSocketFd(fd);
-    }
-
-    private void unTagSocketFd(FileDescriptor fd) {
-        final SocketTags options = threadSocketTags.get();
-        if (options.statsTag == -1 && options.statsUid == -1) return;
-
-        final int errno = native_untagSocketFd(fd);
-        if (errno < 0) {
-            Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
-        }
-    }
-
-    public static class SocketTags {
-        public int statsTag = -1;
-        public int statsUid = -1;
-    }
-
-    public static void setKernelCounterSet(int uid, int counterSet) {
-        final int errno = native_setCounterSet(counterSet, uid);
-        if (errno < 0) {
-            Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
-                    + errno);
-        }
-    }
-
-    public static void resetKernelUidStats(int uid) {
-        int errno = native_deleteTagData(0, uid);
-        if (errno < 0) {
-            Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
-        }
-    }
-
-    /**
-     * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
-     * format like {@code 0x7fffffff00000000}.
-     */
-    public static int kernelToTag(String string) {
-        int length = string.length();
-        if (length > 10) {
-            return Long.decode(string.substring(0, length - 8)).intValue();
-        } else {
-            return 0;
-        }
-    }
-
-    private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
-    private static native int native_untagSocketFd(FileDescriptor fd);
-    private static native int native_setCounterSet(int uid, int counterSetNum);
-    private static native int native_deleteTagData(int tag, int uid);
-}
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
index 554e278..1c2d19d 100644
--- a/core/java/com/android/server/OWNERS
+++ b/core/java/com/android/server/OWNERS
@@ -1 +1 @@
-per-file SystemConfig.java = [email protected],[email protected]
+per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index d66f461..39f17e51 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -849,8 +849,8 @@
                             XmlUtils.skipCurrentTag(parser);
                         }
                     } break;
-                    case "updatable-library":
-                        // "updatable-library" is meant to behave exactly like "library"
+                    case "apex-library":
+                        // "apex-library" is meant to behave exactly like "library"
                     case "library": {
                         if (allowLibs) {
                             String lname = parser.getAttributeValue(null, "name");
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index da62863..430d84e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -124,7 +124,6 @@
                 "android_view_PointerIcon.cpp",
                 "android_view_Surface.cpp",
                 "android_view_SurfaceControl.cpp",
-                "android_view_SurfaceControlFpsListener.cpp",
                 "android_view_SurfaceControlHdrLayerInfoListener.cpp",
                 "android_graphics_BLASTBufferQueue.cpp",
                 "android_view_SurfaceSession.cpp",
@@ -251,10 +250,10 @@
                 "spatializer-aidl-cpp",
                 "av-types-aidl-cpp",
                 "[email protected]",
+                "libandroid_net",
                 "libandroidicu",
                 "libbattery",
                 "libbpf_android",
-                "libnetdbpf",
                 "libnetdutils",
                 "libmemtrack",
                 "libandroidfw",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 21ec64b..f4296be 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -123,7 +123,6 @@
 extern int register_android_view_InputWindowHandle(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_SurfaceControl(JNIEnv* env);
-extern int register_android_view_SurfaceControlFpsListener(JNIEnv* env);
 extern int register_android_view_SurfaceControlHdrLayerInfoListener(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
 extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
@@ -1546,7 +1545,6 @@
         REG_JNI(register_android_view_InputWindowHandle),
         REG_JNI(register_android_view_Surface),
         REG_JNI(register_android_view_SurfaceControl),
-        REG_JNI(register_android_view_SurfaceControlFpsListener),
         REG_JNI(register_android_view_SurfaceControlHdrLayerInfoListener),
         REG_JNI(register_android_view_SurfaceSession),
         REG_JNI(register_android_view_CompositionSamplingListener),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 4357729..9a460f5 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -76,6 +76,9 @@
 per-file AndroidRuntime.cpp = [email protected], [email protected], [email protected]
 # Although marked "view" this is mostly graphics stuff
 per-file android_view_* = file:/graphics/java/android/graphics/OWNERS
+# File used for Android Studio layoutlib
+per-file LayoutlibLoader.cpp = file:/graphics/java/android/graphics/OWNERS
+per-file LayoutlibLoader.cpp = [email protected], [email protected]
 
 # Verity
 per-file com_android_internal_security_Verity* = [email protected], [email protected]
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index d0504fb..d039bcf 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -66,6 +66,7 @@
     jfieldID    flags;
     //methods
     jmethodID   setType;
+    jmethodID   setId;
     jmethodID   setUuid;
     jmethodID   init;
 } gSensorOffsets;
@@ -112,6 +113,7 @@
     sensorOffsets.flags = GetFieldIDOrDie(_env,sensorClass, "mFlags", "I");
 
     sensorOffsets.setType = GetMethodIDOrDie(_env,sensorClass, "setType", "(I)Z");
+    sensorOffsets.setId = GetMethodIDOrDie(_env,sensorClass, "setId", "(I)V");
     sensorOffsets.setUuid = GetMethodIDOrDie(_env,sensorClass, "setUuid", "(JJ)V");
     sensorOffsets.init = GetMethodIDOrDie(_env,sensorClass, "<init>", "()V");
 
@@ -188,9 +190,10 @@
             env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
         }
 
-        // TODO(b/29547335): Rename "setUuid" method to "setId".
-        int64_t id = nativeSensor.getId();
-        env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, 0);
+        int32_t id = nativeSensor.getId();
+        env->CallVoidMethod(sensor, sensorOffsets.setId, id);
+        Sensor::uuid_t uuid = nativeSensor.getUuid();
+        env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, uuid.i64[0], uuid.i64[1]);
     }
     return sensor;
 }
diff --git a/core/jni/android_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp
index 423ef7c..d7fc1cc 100644
--- a/core/jni/android_media_AudioAttributes.cpp
+++ b/core/jni/android_media_AudioAttributes.cpp
@@ -57,7 +57,7 @@
     jmethodID setUsage;
     jmethodID setSystemUsage;
     jmethodID setInternalCapturePreset;
-    jmethodID setContentType;
+    jmethodID setInternalContentType;
     jmethodID replaceFlags;
     jmethodID addTag;
 } gAudioAttributesBuilderMethods;
@@ -127,7 +127,7 @@
                           gAudioAttributesBuilderMethods.setInternalCapturePreset,
                           attributes.source);
     env->CallObjectMethod(jAttributeBuilder.get(),
-                          gAudioAttributesBuilderMethods.setContentType,
+                          gAudioAttributesBuilderMethods.setInternalContentType,
                           attributes.content_type);
     env->CallObjectMethod(jAttributeBuilder.get(),
                           gAudioAttributesBuilderMethods.replaceFlags,
@@ -202,9 +202,9 @@
     gAudioAttributesBuilderMethods.setInternalCapturePreset = GetMethodIDOrDie(
                 env, audioAttributesBuilderClass, "setInternalCapturePreset",
                 "(I)Landroid/media/AudioAttributes$Builder;");
-    gAudioAttributesBuilderMethods.setContentType = GetMethodIDOrDie(
-                env, audioAttributesBuilderClass, "setContentType",
-                "(I)Landroid/media/AudioAttributes$Builder;");
+    gAudioAttributesBuilderMethods.setInternalContentType =
+            GetMethodIDOrDie(env, audioAttributesBuilderClass, "setInternalContentType",
+                             "(I)Landroid/media/AudioAttributes$Builder;");
     gAudioAttributesBuilderMethods.replaceFlags = GetMethodIDOrDie(
                 env, audioAttributesBuilderClass, "replaceFlags",
                 "(I)Landroid/media/AudioAttributes$Builder;");
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index afad08a..1be1873 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -15,24 +15,23 @@
  */
 
 #define LOG_TAG "NMST_QTagUidNative"
-#include <utils/Log.h>
 
-#include <nativehelper/JNIPlatformHelp.h>
-
-#include "jni.h"
-#include <utils/misc.h>
+#include <android/multinetwork.h>
 #include <cutils/qtaguid.h>
-
 #include <errno.h>
 #include <fcntl.h>
-#include <sys/types.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "jni.h"
 
 namespace android {
 
-static jint QTagUid_tagSocketFd(JNIEnv* env, jclass,
-                                jobject fileDescriptor,
-                                jint tagNum, jint uid) {
+static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor,
+                        jint tagNum, jint uid) {
   int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
   if (env->ExceptionCheck()) {
@@ -40,15 +39,14 @@
     return (jint)-1;
   }
 
-  int res = qtaguid_tagSocket(userFd, tagNum, uid);
+  int res = android_tag_socket_with_uid(userFd, tagNum, uid);
   if (res < 0) {
     return (jint)-errno;
   }
   return (jint)res;
 }
 
-static jint QTagUid_untagSocketFd(JNIEnv* env, jclass,
-                                  jobject fileDescriptor) {
+static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
   int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
   if (env->ExceptionCheck()) {
@@ -56,16 +54,14 @@
     return (jint)-1;
   }
 
-  int res = qtaguid_untagSocket(userFd);
+  int res = android_untag_socket(userFd);
   if (res < 0) {
     return (jint)-errno;
   }
   return (jint)res;
 }
 
-static jint QTagUid_setCounterSet(JNIEnv* env, jclass,
-                                  jint setNum, jint uid) {
-
+static jint setCounterSet(JNIEnv* env, jclass, jint setNum, jint uid) {
   int res = qtaguid_setCounterSet(setNum, uid);
   if (res < 0) {
     return (jint)-errno;
@@ -73,9 +69,7 @@
   return (jint)res;
 }
 
-static jint QTagUid_deleteTagData(JNIEnv* env, jclass,
-                                  jint tagNum, jint uid) {
-
+static jint deleteTagData(JNIEnv* env, jclass, jint tagNum, jint uid) {
   int res = qtaguid_deleteTagData(tagNum, uid);
   if (res < 0) {
     return (jint)-errno;
@@ -84,10 +78,10 @@
 }
 
 static const JNINativeMethod gQTagUidMethods[] = {
-  { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
-  { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
-  { "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
-  { "native_deleteTagData", "(II)I", (void*)QTagUid_deleteTagData},
+  { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd},
+  { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd},
+  { "native_setCounterSet", "(II)I", (void*)setCounterSet},
+  { "native_deleteTagData", "(II)I", (void*)deleteTagData},
 };
 
 int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 0eb8c6a..571a8e2 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -83,17 +83,20 @@
     constexpr int INDIC_MIN_PREFIX = 2;
     constexpr int INDIC_MIN_SUFFIX = 2;
 
+    addHyphenator("af", 1, 1);  // Afrikaans
     addHyphenator("am", 1, 1);  // Amharic
     addHyphenator("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Assamese
     addHyphenator("be", 2, 2);  // Belarusian
     addHyphenator("bg", 2, 2);  // Bulgarian
     addHyphenator("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Bengali
+    addHyphenator("cs", 2, 2);  // Czech
     addHyphenator("cu", 1, 2);  // Church Slavonic
     addHyphenator("cy", 2, 3);  // Welsh
     addHyphenator("da", 2, 2);  // Danish
     addHyphenator("de-1901", 2, 2);  // German 1901 orthography
     addHyphenator("de-1996", 2, 2);  // German 1996 orthography
     addHyphenator("de-CH-1901", 2, 2);  // Swiss High German 1901 orthography
+    addHyphenator("el", 1, 1);  // Greek
     addHyphenator("en-GB", 2, 3);  // British English
     addHyphenator("en-US", 2, 3);  // American English
     addHyphenator("es", 2, 2);  // Spanish
@@ -110,18 +113,25 @@
     // Going with a more conservative value of (2, 2) for now.
     addHyphenator("hy", 2, 2);  // Armenian
     addHyphenator("it", 2, 2);  // Italian
+    addHyphenator("ka", 1, 2);  // Georgian
     addHyphenator("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Kannada
     addHyphenator("la", 2, 2);  // Latin
     addHyphenator("lt", 2, 2);  // Lithuanian
+    addHyphenator("lv", 2, 2);  // Latvian
     addHyphenator("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Malayalam
     addHyphenator("mn-Cyrl", 2, 2);  // Mongolian in Cyrillic script
     addHyphenator("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Marathi
     addHyphenator("nb", 2, 2);  // Norwegian Bokmål
+    addHyphenator("nl", 2, 2);  // Dutch
     addHyphenator("nn", 2, 2);  // Norwegian Nynorsk
     addHyphenator("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Oriya
     addHyphenator("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Punjabi
     addHyphenator("pt", 2, 3);  // Portuguese
+    addHyphenator("ru", 2, 2);  // Russian
+    addHyphenator("sk", 2, 2);  // Slovak
     addHyphenator("sl", 2, 2);  // Slovenian
+    addHyphenator("sq", 2, 2);  // Albanian
+    addHyphenator("sv", 1, 2);  // Swedish
     addHyphenator("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Tamil
     addHyphenator("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX);  // Telugu
     addHyphenator("tk", 2, 2);  // Turkmen
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index ce772cf..d91d526 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -48,6 +48,16 @@
         jmethodID init;
     } frameRateOverrideClassInfo;
 
+    struct {
+        jclass clazz;
+        jmethodID init;
+    } frameTimelineClassInfo;
+
+    struct {
+        jclass clazz;
+        jmethodID init;
+    } vsyncEventDataClassInfo;
+
 } gDisplayEventReceiverClassInfo;
 
 
@@ -105,9 +115,38 @@
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
+
+        ScopedLocalRef<jobjectArray>
+                frameTimelineObjs(env,
+                                  env->NewObjectArray(vsyncEventData.frameTimelines.size(),
+                                                      gDisplayEventReceiverClassInfo
+                                                              .frameTimelineClassInfo.clazz,
+                                                      /*initial element*/ NULL));
+        for (int i = 0; i < vsyncEventData.frameTimelines.size(); i++) {
+            VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
+            ScopedLocalRef<jobject>
+                    frameTimelineObj(env,
+                                     env->NewObject(gDisplayEventReceiverClassInfo
+                                                            .frameTimelineClassInfo.clazz,
+                                                    gDisplayEventReceiverClassInfo
+                                                            .frameTimelineClassInfo.init,
+                                                    frameTimeline.id,
+                                                    frameTimeline.expectedPresentTime,
+                                                    frameTimeline.deadlineTimestamp));
+            env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get());
+        }
+        ScopedLocalRef<jobject>
+                vsyncEventDataJava(env,
+                                   env->NewObject(gDisplayEventReceiverClassInfo
+                                                          .vsyncEventDataClassInfo.clazz,
+                                                  gDisplayEventReceiverClassInfo
+                                                          .vsyncEventDataClassInfo.init,
+                                                  frameTimelineObjs.get(),
+                                                  vsyncEventData.preferredFrameTimelineIndex,
+                                                  vsyncEventData.frameInterval));
+
         env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
-                            timestamp, displayId.value, count, vsyncEventData.id,
-                            vsyncEventData.deadlineTimestamp, vsyncEventData.frameInterval);
+                            timestamp, displayId.value, count, vsyncEventDataJava.get());
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -239,7 +278,7 @@
 
     gDisplayEventReceiverClassInfo.dispatchVsync =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
-                             "(JJIJJJ)V");
+                             "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
     gDisplayEventReceiverClassInfo.dispatchModeChanged =
@@ -258,6 +297,24 @@
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz,
                              "<init>", "(IF)V");
 
+    jclass frameTimelineClazz =
+            FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData$FrameTimeline");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz =
+            MakeGlobalRefOrDie(env, frameTimelineClazz);
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                             "<init>", "(JJJ)V");
+
+    jclass vsyncEventDataClazz =
+            FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData");
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz =
+            MakeGlobalRefOrDie(env, vsyncEventDataClazz);
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.init =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                             "<init>",
+                             "([Landroid/view/"
+                             "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V");
+
     return res;
 }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 67d0c52..d5470cc 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -91,6 +91,7 @@
     jfieldID density;
     jfieldID secure;
     jfieldID deviceProductInfo;
+    jfieldID installOrientation;
 } gStaticDisplayInfoClassInfo;
 
 static struct {
@@ -1210,6 +1211,8 @@
     env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure);
     env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo,
                         convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
+    env->SetIntField(object, gStaticDisplayInfoClassInfo.installOrientation,
+                     static_cast<uint32_t>(info.installOrientation));
     return object;
 }
 
@@ -1768,6 +1771,15 @@
     client->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
 }
 
+static jboolean nativeGetDisplayDecorationSupport(JNIEnv* env, jclass clazz,
+        jobject displayTokenObject) {
+    sp<IBinder> displayToken(ibinderForJavaObject(env, displayTokenObject));
+    if (displayToken == nullptr) {
+        return JNI_FALSE;
+    }
+    return static_cast<jboolean>(SurfaceComposerClient::getDisplayDecorationSupport(displayToken));
+}
+
 static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
     SurfaceControl *surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
     return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
@@ -2092,6 +2104,8 @@
             (void*)nativeMirrorSurface },
     {"nativeSetGlobalShadowSettings", "([F[FFFF)V",
             (void*)nativeSetGlobalShadowSettings },
+    {"nativeGetDisplayDecorationSupport", "(Landroid/os/IBinder;)Z",
+            (void*)nativeGetDisplayDecorationSupport},
     {"nativeGetHandle", "(J)J",
             (void*)nativeGetHandle },
     {"nativeSetFixedTransformHint", "(JJI)V",
@@ -2141,6 +2155,8 @@
     gStaticDisplayInfoClassInfo.deviceProductInfo =
             GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
                             "Landroid/hardware/display/DeviceProductInfo;");
+    gStaticDisplayInfoClassInfo.installOrientation =
+            GetFieldIDOrDie(env, infoClazz, "installOrientation", "I");
 
     jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo");
     gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz);
diff --git a/core/jni/android_view_SurfaceControlFpsListener.cpp b/core/jni/android_view_SurfaceControlFpsListener.cpp
deleted file mode 100644
index 0b15acd..0000000
--- a/core/jni/android_view_SurfaceControlFpsListener.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SurfaceControlFpsListener"
-
-#include <android/gui/BnFpsListener.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/Log.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <nativehelper/JNIHelp.h>
-#include <utils/Log.h>
-#include <utils/RefBase.h>
-
-#include "android_util_Binder.h"
-#include "core_jni_helpers.h"
-
-namespace android {
-
-namespace {
-
-struct {
-    jclass mClass;
-    jmethodID mDispatchOnFpsReported;
-} gListenerClassInfo;
-
-struct SurfaceControlFpsListener : public gui::BnFpsListener {
-    SurfaceControlFpsListener(JNIEnv* env, jobject listener)
-          : mListener(env->NewWeakGlobalRef(listener)) {}
-
-    binder::Status onFpsReported(float fps) override {
-        JNIEnv* env = AndroidRuntime::getJNIEnv();
-        LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onFpsReported.");
-
-        jobject listener = env->NewGlobalRef(mListener);
-        if (listener == NULL) {
-            // Weak reference went out of scope
-            return binder::Status::ok();
-        }
-        env->CallStaticVoidMethod(gListenerClassInfo.mClass,
-                                  gListenerClassInfo.mDispatchOnFpsReported, listener,
-                                  static_cast<jfloat>(fps));
-        env->DeleteGlobalRef(listener);
-
-        if (env->ExceptionCheck()) {
-            ALOGE("SurfaceControlFpsListener.onFpsReported() failed.");
-            LOGE_EX(env);
-            env->ExceptionClear();
-        }
-        return binder::Status::ok();
-    }
-
-protected:
-    virtual ~SurfaceControlFpsListener() {
-        JNIEnv* env = AndroidRuntime::getJNIEnv();
-        env->DeleteWeakGlobalRef(mListener);
-    }
-
-private:
-    jweak mListener;
-};
-
-jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
-    SurfaceControlFpsListener* listener = new SurfaceControlFpsListener(env, obj);
-    listener->incStrong((void*)nativeCreate);
-    return reinterpret_cast<jlong>(listener);
-}
-
-void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
-    SurfaceControlFpsListener* listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
-    listener->decStrong((void*)nativeCreate);
-}
-
-void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr, jint taskId) {
-    sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
-    if (SurfaceComposerClient::addFpsListener(taskId, listener) != OK) {
-        constexpr auto error_msg = "Couldn't addFpsListener";
-        ALOGE(error_msg);
-        jniThrowRuntimeException(env, error_msg);
-    }
-}
-
-void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
-    sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
-
-    if (SurfaceComposerClient::removeFpsListener(listener) != OK) {
-        constexpr auto error_msg = "Couldn't removeFpsListener";
-        ALOGE(error_msg);
-        jniThrowRuntimeException(env, error_msg);
-    }
-}
-
-const JNINativeMethod gMethods[] = {
-        /* name, signature, funcPtr */
-        {"nativeCreate", "(Landroid/view/SurfaceControlFpsListener;)J", (void*)nativeCreate},
-        {"nativeDestroy", "(J)V", (void*)nativeDestroy},
-        {"nativeRegister", "(JI)V", (void*)nativeRegister},
-        {"nativeUnregister", "(J)V", (void*)nativeUnregister}};
-
-} // namespace
-
-int register_android_view_SurfaceControlFpsListener(JNIEnv* env) {
-    int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlFpsListener", gMethods,
-                                       NELEM(gMethods));
-    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
-
-    jclass clazz = env->FindClass("android/view/SurfaceControlFpsListener");
-    gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
-    gListenerClassInfo.mDispatchOnFpsReported =
-            env->GetStaticMethodID(clazz, "dispatchOnFpsReported",
-                                   "(Landroid/view/SurfaceControlFpsListener;F)V");
-    return 0;
-}
-
-} // namespace android
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index 1381de5..08c9f20 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -72,25 +72,29 @@
             return;
         }
 
-        jobjectArray jWindowHandlesArray =
-                env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
+        ScopedLocalRef<jobjectArray>
+                jWindowHandlesArray(env,
+                                    env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass,
+                                                        nullptr));
         for (int i = 0; i < windowInfos.size(); i++) {
             ScopedLocalRef<jobject>
                     jWindowHandle(env,
                                   android_view_InputWindowHandle_fromWindowInfo(env,
                                                                                 windowInfos[i]));
-            env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get());
+            env->SetObjectArrayElement(jWindowHandlesArray.get(), i, jWindowHandle.get());
         }
 
-        jobjectArray jDisplayInfoArray =
-                env->NewObjectArray(displayInfos.size(), gDisplayInfoClassInfo.clazz, nullptr);
+        ScopedLocalRef<jobjectArray>
+                jDisplayInfoArray(env,
+                                  env->NewObjectArray(displayInfos.size(),
+                                                      gDisplayInfoClassInfo.clazz, nullptr));
         for (int i = 0; i < displayInfos.size(); i++) {
             ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i]));
-            env->SetObjectArrayElement(jDisplayInfoArray, i, jDisplayInfo.get());
+            env->SetObjectArrayElement(jDisplayInfoArray.get(), i, jDisplayInfo.get());
         }
 
-        env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray,
-                            jDisplayInfoArray);
+        env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged,
+                            jWindowHandlesArray.get(), jDisplayInfoArray.get());
         env->DeleteGlobalRef(listener);
 
         if (env->ExceptionCheck()) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 78650ed..a4463e4 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -18,7 +18,8 @@
 per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 
 # Biometrics
[email protected]
[email protected]
[email protected]
 
 # Launcher
 [email protected]
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 5090d8c..4c0ba8c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -86,6 +86,8 @@
         optional SettingProto accessibility_floating_menu_opacity = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_floating_menu_fade_enabled = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Setting for accessibility magnification for following typing.
+        optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Accessibility accessibility = 2;
 
@@ -383,6 +385,7 @@
 
     optional SettingProto multi_press_timeout = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    optional SettingProto nav_bar_kids_mode = 91 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto navigation_mode = 76 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message NfcPayment {
@@ -665,5 +668,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 91;
+    // Next tag = 92;
 }
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 73d6a17..e56d55e 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -212,6 +212,12 @@
         // DatabaseHelper.
         optional SettingProto in_silent = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto when_ringing = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+        optional SettingProto alarm_intensity = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto media_intensity = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto ring_intensity = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // notification_intensity is already logged at Notification.vibration_intensity
+        // haptic_feedback_intensity is already logged at HapticFeedback.intensity
     }
     optional Vibrate vibrate = 32;
 
diff --git a/core/proto/android/server/vibrator/OWNERS b/core/proto/android/server/vibrator/OWNERS
new file mode 100644
index 0000000..b54d6bf
--- /dev/null
+++ b/core/proto/android/server/vibrator/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 7b97524..2f2158d 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -97,7 +97,7 @@
     optional int32 status = 6;
 }
 
-// Next id: 18
+// Next id: 25
 message VibratorManagerServiceDumpProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     repeated int32 vibrator_ids = 1;
@@ -106,8 +106,15 @@
     optional VibrationProto current_external_vibration = 4;
     optional bool vibrator_under_external_control = 5;
     optional bool low_power_mode = 6;
+    optional bool vibrate_on = 24;
+    optional int32 alarm_intensity = 18;
+    optional int32 alarm_default_intensity = 19;
     optional int32 haptic_feedback_intensity = 7;
     optional int32 haptic_feedback_default_intensity = 8;
+    optional int32 hardware_feedback_intensity = 22;
+    optional int32 hardware_feedback_default_intensity = 23;
+    optional int32 media_intensity = 20;
+    optional int32 media_default_intensity = 21;
     optional int32 notification_intensity = 9;
     optional int32 notification_default_intensity = 10;
     optional int32 ring_intensity = 11;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 23453876..11560a5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -225,6 +225,7 @@
     repeated InsetsSourceProviderProto insets_source_providers = 35;
     optional bool is_sleeping = 36;
     repeated string sleep_tokens = 37;
+    repeated .android.graphics.RectProto keep_clear_areas = 38;
 
 }
 
@@ -443,6 +444,7 @@
     optional bool force_seamless_rotation = 42;
     optional bool has_compat_scale = 43;
     optional float global_scale = 44;
+    repeated .android.graphics.RectProto keep_clear_areas = 45;
 }
 
 message IdentifierProto {
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index c8cdfdd..ba2b6d6b 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -17,15 +17,11 @@
 syntax = "proto2";
 package android.service;
 
-import "frameworks/base/core/proto/android/privacy.proto";
-
 option java_multiple_files = true;
 option java_outer_classname = "NetworkStatsServiceProto";
 
 // Represents dumpsys from NetworkStatsService (netstats).
 message NetworkStatsServiceDumpProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     repeated NetworkInterfaceProto active_interfaces = 1;
 
     repeated NetworkInterfaceProto active_uid_interfaces = 2;
@@ -45,8 +41,6 @@
 
 // Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces.
 message NetworkInterfaceProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     // Name of the network interface (eg: wlan).
     optional string interface = 1;
 
@@ -55,26 +49,14 @@
 
 // Corresponds to NetworkIdentitySet.
 message NetworkIdentitySetProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     repeated NetworkIdentityProto identities = 1;
 }
 
 // Corresponds to NetworkIdentity.
 message NetworkIdentityProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     // Constants from ConnectivityManager.TYPE_*.
     optional int32 type = 1;
 
-    // Full subscriber ID on eng builds. The IMSI is scrubbed on user & userdebug
-    // builds to only include the info about the GSM network operator (the info
-    // that uniquely identifies the subscriber is removed).
-    optional string subscriber_id = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
-
-    // Name of the network (eg: MyWifi).
-    optional string network_id = 3 [ (android.privacy).dest = DEST_EXPLICIT ];
-
     optional bool roaming = 4;
 
     optional bool metered = 5;
@@ -86,8 +68,6 @@
 
 // Corresponds to NetworkStatsRecorder.
 message NetworkStatsRecorderProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     optional int64 pending_total_bytes = 1;
 
     optional NetworkStatsCollectionProto complete_history = 2;
@@ -95,15 +75,11 @@
 
 // Corresponds to NetworkStatsCollection.
 message NetworkStatsCollectionProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     repeated NetworkStatsCollectionStatsProto stats = 1;
 }
 
 // Corresponds to NetworkStatsCollection.mStats.
 message NetworkStatsCollectionStatsProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     optional NetworkStatsCollectionKeyProto key = 1;
 
     optional NetworkStatsHistoryProto history = 2;
@@ -111,8 +87,6 @@
 
 // Corresponds to NetworkStatsCollection.Key.
 message NetworkStatsCollectionKeyProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     optional NetworkIdentitySetProto identity = 1;
 
     optional int32 uid = 2;
@@ -124,8 +98,6 @@
 
 // Corresponds to NetworkStatsHistory.
 message NetworkStatsHistoryProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     // Duration for this bucket in milliseconds.
     optional int64 bucket_duration_ms = 1;
 
@@ -134,8 +106,6 @@
 
 // Corresponds to each bucket in NetworkStatsHistory.
 message NetworkStatsHistoryBucketProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
     // Bucket start time in milliseconds since epoch.
     optional int64 bucket_start_ms = 1;
 
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 97097ff..c5eaf42 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -196,9 +196,19 @@
 message UsbPortManagerProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
+    enum HalVersion {
+        V_UNKNOWN = 0;
+        V1_0 = 10;
+        V1_1 = 11;
+        V1_2 = 12;
+        V1_3 = 13;
+        V2 = 20;
+    }
+
     optional bool is_simulation_active = 1;
     repeated UsbPortInfoProto usb_ports = 2;
     optional bool enable_usb_data_signaling = 3;
+    optional HalVersion hal_version = 4;
 }
 
 message UsbPortInfoProto {
@@ -254,6 +264,9 @@
     optional DataRole data_role = 4;
     repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
     optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6;
+    optional string usb_data_status = 7;
+    optional bool is_power_transfer_limited = 8;
+    optional string usb_power_brick_status = 9;
 }
 
 message UsbPortStatusRoleCombinationProto {
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 6063062..c42517d 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -37,7 +37,6 @@
     visibility: [":__subpackages__"],
     license_kinds: [
         "SPDX-license-identifier-Apache-2.0",
-        "SPDX-license-identifier-GPL",
     ],
     license_text: [
         "NOTICE",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 31ef2b3..3a842ee 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -716,6 +716,7 @@
 
     <!-- Added in T -->
     <protected-broadcast android:name="android.intent.action.REFRESH_SAFETY_SOURCES" />
+    <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -1502,7 +1503,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record whitelists the permission. For more details see
+         the installer on record allowlists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.BODY_SENSORS_BACKGROUND"
@@ -2182,8 +2183,9 @@
     <permission android:name="android.permission.NFC_HANDOVER_STATUS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
-         <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Allows internal management of Bluetooth state when on wireless consent mode.
+         <p>Not for use by third-party applications.
+         @hide -->
     <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
@@ -2791,6 +2793,10 @@
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
         android:protectionLevel="signature|appop" />
 
+    <!-- @SystemApi @hide Allows starting activities across profiles in the same profile group. -->
+    <permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"
+                android:protectionLevel="signature|role" />
+
     <!-- @SystemApi Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that
          they can interact across profiles in the same profile group.
          @hide -->
@@ -3590,6 +3596,18 @@
     <permission android:name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"
         android:protectionLevel="signature|privileged" />
 
+    <!-- ========================================= -->
+    <!-- Permissions for SupplementalApi -->
+    <!-- ========================================= -->
+    <eat-comment />
+
+    <!-- TODO(b/213488783): Update with correct names. -->
+    <!-- Allows an application to access SupplementalApis. -->
+    <permission android:name="android.permission.ACCESS_SUPPLEMENTAL_APIS"
+        android:label="@string/permlab_accessSupplementalApi"
+        android:description="@string/permdesc_accessSupplementalApi"
+        android:protectionLevel="normal" />
+
     <!-- ==================================== -->
     <!-- Private permissions                  -->
     <!-- ==================================== -->
@@ -3813,6 +3831,13 @@
     <permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
         android:protectionLevel="signature|recents" />
 
+    <!-- @SystemApi Allows an application to set the system audio caption and its UI
+     enabled state.
+     <p>Not for use by third-party applications.
+     @hide -->
+    <permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION"
+                android:protectionLevel="signature|role" />
+
     <!-- Allows an application to retrieve the current state of keys and
          switches.
          <p>Not for use by third-party applications.
@@ -4226,7 +4251,8 @@
 
     <!-- Allows low-level access to setting the keyboard layout.
          <p>Not for use by third-party applications.
-         @hide -->
+         @hide
+         @TestApi -->
     <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
         android:protectionLevel="signature" />
 
@@ -4438,6 +4464,12 @@
     <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
          android:protectionLevel="signature|installer|verifier" />
 
+   <!-- @TestApi Allows an application to revoke the POST_NOTIFICATIONS permission from an app
+        without killing the app. Only granted to the shell.
+        @hide -->
+    <permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows the system to read runtime permission state.
         @hide -->
     <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
@@ -4714,6 +4746,12 @@
     <permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
         android:protectionLevel="signature|privileged|role" />
 
+    <!-- @SystemApi Allows an application to access the ultrasound content.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.ACCESS_ULTRASOUND"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Puts an application in the chain of trust for sound trigger
          operations. Being in the chain of trust allows an application to
          delegate an identity of a separate entity to the sound trigger system
@@ -5091,6 +5129,11 @@
     <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows applications to set the wallpaper dim amount.
+         @hide. -->
+    <permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows applications to read dream settings and dream state.
          @hide -->
     <permission android:name="android.permission.READ_DREAM_STATE"
@@ -5323,6 +5366,12 @@
     <permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to manage weak escrow token on the device. This permission
+         is not available to third party applications.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_WEAK_ESCROW_TOKEN"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to listen to trust changes.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.TRUST_LISTENER"
@@ -5573,20 +5622,6 @@
     <permission android:name="android.permission.VIEW_INSTANT_APPS"
                 android:protectionLevel="signature|preinstalled" />
 
-    <!-- Allows an application to interact with the currently active
-        {@link com.android.server.communal.CommunalManagerService}.
-        @hide
-        @TestApi -->
-    <permission android:name="android.permission.WRITE_COMMUNAL_STATE"
-                android:protectionLevel="signature" />
-
-    <!-- Allows an application to view information from the currently active
-         {@link com.android.server.communal.CommunalManagerService}.
-         @hide
-         @SystemApi -->
-    <permission android:name="android.permission.READ_COMMUNAL_STATE"
-                android:protectionLevel="signature|privileged"/>
-
     <!-- Allows the holder to manage whether the system can bind to services
          provided by instant apps. This permission is intended to protect
          test/development fucntionality and should be used only in such cases.
@@ -6009,10 +6044,15 @@
     <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
                 android:protectionLevel="signature" />
 
-    <!-- Allows managing the Game Mode
-     @hide Used internally. -->
+    <!-- @SystemApi Allows managing the Game Mode
+     @hide -->
     <permission android:name="android.permission.MANAGE_GAME_MODE"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows accessing the frame rate per second of a given application
+     @hide -->
+    <permission android:name="android.permission.ACCESS_FPS_COUNTER"
+                android:protectionLevel="signature|privileged" />
 
     <!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager
          when they are performing reboot-blocking work.
@@ -6071,11 +6111,21 @@
     <permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"
         android:protectionLevel="signature" />
 
-      <!-- @SystemApi Allows an application to query over global data in AppSearch.
+    <!-- @SystemApi Allows an application to query over global data in AppSearch.
            @hide -->
     <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
                 android:protectionLevel="internal|role" />
 
+    <!-- Allows an application to query over global data in AppSearch that's visible to the
+         ASSISTANT role.  -->
+    <permission android:name="android.permission.READ_ASSISTANT_APP_SEARCH_DATA"
+        android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to query over global data in AppSearch that's visible to the
+         HOME role.  -->
+    <permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA"
+        android:protectionLevel="internal|role" />
+
     <!-- @SystemApi Allows an application to create virtual devices in VirtualDeviceManager.
          @hide -->
     <permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
@@ -6089,7 +6139,7 @@
     <permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE"
                 android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to launch device manager setup screens.
+    <!-- @SystemApi Allows an application to launch device manager setup screens.
          <p>Not for use by third-party applications.
          @hide
     -->
@@ -6101,7 +6151,7 @@
          @hide -->
     <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES"
                 android:protectionLevel="signature|role" />
-    
+
     <!-- @SystemApi Allows an app to read whether SafetyCenter is enabled/disabled.
              <p>Protection level: signature|privileged
              @hide
@@ -6109,6 +6159,27 @@
     <permission android:name="android.permission.READ_SAFETY_CENTER_STATUS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Required to access the safety center internal APIs using the
+             {@link android.safetycenter.SafetyCenterManager}.
+             <p>Protection level: internal|installer|role
+             @hide
+        -->
+    <permission android:name="android.permission.MANAGE_SAFETY_CENTER"
+                android:protectionLevel="internal|installer|role" />
+
+    <!-- @SystemApi Allows an application to access the AmbientContextEvent service.
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"
+                android:protectionLevel="internal|role"/>
+
+    <!-- @SystemApi Required by a AmbientContextEventDetectionService
+         to ensure that only the service with this permission can bind to it.
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
@@ -6514,6 +6585,11 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.SmartStorageMaintIdler"
+                android:exported="true"
+                android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.ZramWriteback"
                  android:exported="false"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
@@ -6603,6 +6679,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service android:name="com.android.server.companion.AssociationCleanUpService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
         <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
             android:exported="false">
             <intent-filter>
diff --git a/core/res/OWNERS b/core/res/OWNERS
index b18a989..4bea4d5 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -34,3 +34,7 @@
 
 # Wear
 per-file res/*-watch/* = file:/platform/frameworks/opt/wear:/OWNERS
+
+# PowerProfile
+per-file res/xml/power_profile.xml = file:/BATTERY_STATS_OWNERS
+per-file res/xml/power_profile_test.xml = file:/BATTERY_STATS_OWNERS
diff --git a/core/res/res/drawable/default_dream_preview.xml b/core/res/res/drawable/default_dream_preview.xml
new file mode 100644
index 0000000..bf4a04b
--- /dev/null
+++ b/core/res/res/drawable/default_dream_preview.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@android:color/white"/>
+</shape>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_ime_nav_back.xml b/core/res/res/drawable/ic_ime_nav_back.xml
new file mode 100644
index 0000000..ca329aa
--- /dev/null
+++ b/core/res/res/drawable/ic_ime_nav_back.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:autoMirrored="true"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+    <path
+        android:pathData="M16.78,10.03l-3.97,3.97l3.97,3.97l-1.06,1.06l-5.03,-5.03l5.03,-5.03z"
+        android:fillColor="#FFFFFFFF" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_ime_switcher.xml b/core/res/res/drawable/ic_ime_switcher.xml
new file mode 100644
index 0000000..6c3b766
--- /dev/null
+++ b/core/res/res/drawable/ic_ime_switcher.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="20dp"
+        android:height="20dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M19,7h2v2h-2V7zM15,7h2v2h-2V7zM3,7h2v2H3V7zM7,7h2v2H7V7zM11,7h2v2h-2V7zM19,11h2v2h-2V11zM15,11h2v2h-2V11zM3,11h2v2H3V11zM7,11h2v2H7V11zM11,11h2v2h-2V11zM7,15h10v2H7V15z"
+        android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/core/res/res/layout/input_method_nav_back.xml b/core/res/res/layout/input_method_nav_back.xml
new file mode 100644
index 0000000..671766a
--- /dev/null
+++ b/core/res/res/layout/input_method_nav_back.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<android.inputmethodservice.navigationbar.KeyButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/input_method_nav_back"
+    android:layout_width="@dimen/input_method_navigation_key_width"
+    android:layout_height="match_parent"
+    android:layout_weight="0"
+    android:scaleType="center"
+    android:contentDescription="@string/input_method_nav_back_button_desc"
+    android:paddingStart="@dimen/input_method_navigation_key_padding"
+    android:paddingEnd="@dimen/input_method_navigation_key_padding"
+    />
diff --git a/core/res/res/layout/input_method_nav_home_handle.xml b/core/res/res/layout/input_method_nav_home_handle.xml
new file mode 100644
index 0000000..501f512
--- /dev/null
+++ b/core/res/res/layout/input_method_nav_home_handle.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<android.inputmethodservice.navigationbar.NavigationHandle
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/input_method_nav_home_handle"
+    android:layout_width="72dp"
+    android:layout_height="match_parent"
+    android:layout_weight="0"
+    android:paddingStart="@dimen/input_method_navigation_key_padding"
+    android:paddingEnd="@dimen/input_method_navigation_key_padding"
+    />
diff --git a/core/res/res/layout/input_method_nav_ime_switcher.xml b/core/res/res/layout/input_method_nav_ime_switcher.xml
new file mode 100644
index 0000000..b571ba9
--- /dev/null
+++ b/core/res/res/layout/input_method_nav_ime_switcher.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<android.inputmethodservice.navigationbar.KeyButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/input_method_nav_ime_switcher"
+    android:layout_width="@dimen/input_method_navigation_key_width"
+    android:layout_height="match_parent"
+    android:layout_weight="0"
+    android:contentDescription="@string/input_method_ime_switch_button_desc"
+    android:scaleType="center"
+    android:paddingStart="@dimen/input_method_navigation_key_padding"
+    android:paddingEnd="@dimen/input_method_navigation_key_padding"
+    />
diff --git a/core/res/res/layout/input_method_navigation_bar.xml b/core/res/res/layout/input_method_navigation_bar.xml
new file mode 100644
index 0000000..ce402fb
--- /dev/null
+++ b/core/res/res/layout/input_method_navigation_bar.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<android.inputmethodservice.navigationbar.NavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/input_method_navigation_bar_view"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false">
+
+    <android.inputmethodservice.navigationbar.NavigationBarInflaterView
+        android:id="@+id/input_method_nav_inflater"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false" />
+
+</android.inputmethodservice.navigationbar.NavigationBarView>
diff --git a/core/res/res/layout/input_method_navigation_layout.xml b/core/res/res/layout/input_method_navigation_layout.xml
new file mode 100644
index 0000000..05e750a
--- /dev/null
+++ b/core/res/res/layout/input_method_navigation_layout.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="@dimen/input_method_rounded_corner_content_padding"
+    android:layout_marginEnd="@dimen/input_method_rounded_corner_content_padding"
+    android:paddingStart="@dimen/input_method_nav_content_padding"
+    android:paddingEnd="@dimen/input_method_nav_content_padding"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:id="@+id/input_method_nav_horizontal">
+
+    <FrameLayout
+        android:id="@+id/input_method_nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+
+        <LinearLayout
+            android:id="@+id/input_method_nav_ends_group"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:clipChildren="false" />
+
+        <LinearLayout
+            android:id="@+id/input_method_nav_center_group"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:clipChildren="false" />
+
+    </FrameLayout>
+
+</FrameLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index ddaff68..3bc0283 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die program toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Laat die program toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"verkry toegang tot liggaamsensors (soos hartklopmonitors)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Laat die program toe om toegang te verkry tot data van sensors af wat jou fisieke toestand, soos jou polsslag, monitor."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Toegang tot data vanaf liggaamsensors, soos polsslag, temperatuur, bloedsuurstofpersentasie, ens."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"gaan by liggaamsensors in (soos polsslagmonitors) terwyl dit op die agtergrond is"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Toegang tot data vanaf liggaamsensors, soos polsslag, temperatuur, bloedsuurstofpersentasie, ens. terwyl dit op die agtergrond is."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lees kalendergebeurtenisse en -besonderhede"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Hierdie program kan alle kalendergebeurtenisse lees wat op jou tablet geberg is of jou kalenderdata stoor."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Hierdie program kan alle kalendergeleenthede wat op jou Android TV-toestel geberg is, lees of jou kalenderdata stoor."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"begin Bekyk Toestemmingbesluite"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Laat die houer toe om skerm te begin om toestemmingbesluite na te gaan. Behoort nooit vir normale programme nodig te wees nie."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"begin Bekyk Programkenmerke"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Laat die houer toe om die kenmerke-inligting vir \'n program te begin bekyk."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"kry toegang tot sensordata teen \'n hoë monsternemingkoers"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index cee4da6..fb52e6a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"መተግበሪያው ስለገቢ እና ወጪ ጥሪዎች ያለ ውሂብም ጨምሮ የእርስዎ Android TV መሣሪያ ምዝግብ ማስታወሻ እንዲቀይር ያስችለዋል። ተንኮል-አዘል መተግበሪያዎች ይህን ተጠቅመው የስልክዎን ምዝግብ ማስታወሻ ሊደመስሱ ወይም ሊቀይሩ ይችላሉ።"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የስልክህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"የሰውነት ዳሳሾችን መድረስ (እንደ የልብ ምት መከታተያዎች ያሉ)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"እንደ የእርስዎ የልብ ምት የመሳሰሉ ያሉበትን አካላዊ ሁኔታ ከሚቆጣጠሩ ሰውነት ዳሳሾች ውሂብ ላይ እንዲደርስ ለመተግበሪያው ይፈቅደለታል።"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"እንደ የልብ ምት፣ የሙቀት መጠን፣ የደም ኦክሲጅን መቶኛ፣ ወዘተ ያሉ የሰውነት ዳሳሾች ውሂብን መድረስ።"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ከበስተጀርባ እያለ የሰውነት ዳሳሾችን (እንደ የልብ ምት መቆጣጠሪያዎች) መድረስ"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ከበስተጀርባ እያለ እንደ የልብ ምት፣ የሙቀት መጠን፣ የደም ኦክሲጅን መቶኛ፣ ወዘተ ያሉ የሰውነት ዳሳሾች ውሂብን መድረስ።"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"የቀን መቁጠሪያ ክስተቶችን እና ዝርዝሮችን አንብብ"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ይህ መተግበሪያ ሁሉንም በእርስዎ ጡባዊ ላይ የተከማቹ የቀን መቁጠሪያ ክስተቶችን ማንበብ ወይም የእርስዎን የቀን መቁጠሪያ ውሂብ ማስቀመጥ ይችላል።"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ይህ መተግበሪያ ሁሉንም በእርስዎ Android TV መሣሪያ ላይ የተከማቹ የቀን መቁጠሪያ ክስተቶችን ማንበብ ወይም የእርስዎን የቀን መቁጠሪያ ውሂብ ማስቀመጥ ይችላል።"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"መተግበሪያው የአትረብሽ ውቅረትን እንዲያነብብ እና እንዲጸፍ ይፈቅዳል።"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"የእይታ ፈቃድ መጠቀምን መጀመር"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ያዢው ለአንድ መተግበሪያ የፈቃድ አጠቃቀሙን እንዲያስጀምር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"የእይታ ፈቃድ ውሳኔዎችን ይጀምሩ"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ያዢው የፈቃድ ውሳኔዎችን ለመገምገም ማያ ገጽ እንዲጀምሩ ያስችላቸዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"የመተግበሪያ ባህሪያትን ማየት መጀመር"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ያዢው የአንድ መተግበሪያ የባህሪያት መረጃን ማየት እንዲጀምር ያስችለዋል።"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"የዳሳሽ ውሂቡን በከፍተኛ የናሙና ብዛት ላይ ይድረሱበት"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"ብጁ የመተግበሪያ ማሳወቂያ"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g> በ<xliff:g id="ACCOUNT">%2$s</xliff:g> አዲስ ተጠቃሚ እንዲፈጥር ይፈቀድለት (ይህ መለያ ያለው ተጠቃሚ አስቀድሞ አለ)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g> አዲስ ተጠቃሚ ከ <xliff:g id="ACCOUNT">%2$s</xliff:g> ጋር መፍጠር እንዲችል ይፍቀዱ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"ክትትል የሚደረግበት ተጠቃሚን አክል"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ቋንቋ ያክሉ"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"የክልል ምርጫ"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"የቋንቋ ስም ይተይቡ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 4927fa9..12d1b83 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -319,7 +319,7 @@
     <string name="permgrouplab_location" msgid="1858277002233964394">"الموقع الجغرافي"</string>
     <string name="permgroupdesc_location" msgid="1995955142118450685">"الوصول إلى موقع هذا الجهاز"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"التقويم"</string>
-    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"الوصول تقويمك"</string>
+    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"الوصول إلى تقويمك"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"‏إرسال رسائل قصيرة SMS وعرضها"</string>
     <string name="permgrouplab_storage" msgid="1938416135375282333">"الملفات والوسائط"</string>
@@ -340,17 +340,17 @@
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"الوصول إلى بيانات المستشعر حول علاماتك الحيوية"</string>
     <string name="permgrouplab_notifications" msgid="5472972361980668884">"الإشعارات"</string>
     <string name="permgroupdesc_notifications" msgid="4608679556801506580">"عرض الإشعارات"</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"استرداد محتوى النافذة"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"فحص محتوى نافذة يتم التفاعل معها"</string>
-    <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"تفعيل الاستكشاف باللمس"</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"استرداد محتوى النافذة:"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"فحص محتوى نافذة يتم التفاعل معها."</string>
+    <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"تفعيل الاستكشاف باللمس:"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عالٍ ويمكن استكشاف الشاشة باستخدام الإيماءات."</string>
-    <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"ملاحظة النص الذي تكتبه"</string>
+    <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_title_canControlMagnification" msgid="7701572187333415795">"التحكم في تكبير الشاشة:"</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>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"إيماءات بصمات الإصبع:"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"يمكن أن تلتقط الإيماءات التي تم تنفيذها على جهاز استشعار بصمة الإصبع في الجهاز."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"أخذ لقطة شاشة"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"يمكن أخذ لقطة شاشة."</string>
@@ -439,7 +439,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"‏للسماح للتطبيق بتعديل سجلّ مكالمات جهاز Android TV، بما في ذلك البيانات عن المكالمات الواردة والصادرة. وقد تستخدم التطبيقات الضارة هذا الإعداد لمحو سجلّ المكالمات أو تعديله."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"للسماح للتطبيق بتعديل سجل مكالمات الهاتف، بما في ذلك البيانات عن المكالمات الواردة والصادرة. وربما تستخدم التطبيقات الضارة هذا لمحو سجل المكالمات أو تعديله."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"الوصول إلى أجهزة استشعار الجسم (مثل شاشات معدل ضربات القلب)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"للسماح للتطبيق بالدخول إلى البيانات من أجهزة الاستشعار التي تراقب الحالة البدنية، مثل معدل نبضات القلب."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"الوصول إلى البيانات من أجهزة استشعار الجسم، مثلاً معدّل نبضات القلب ودرجة الحرارة ونسبة الأكسجين في الدم وما إلى ذلك"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"الوصول إلى أجهزة استشعار الجسم (مثلاً شاشات معدّل ضربات القلب في الخلفية)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"الوصول إلى البيانات من أجهزة استشعار الجسم، مثلاً معدّل نبضات القلب ودرجة الحرارة ونسبة الأكسجين في الدم وما إلى ذلك في الخلفية"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"قراءة أحداث التقويم والتفاصيل"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"يمكن لهذا التطبيق قراءة جميع أحداث التقويم المخزَّنة على الجهاز اللوحي ومشاركة بيانات التقويم أو حفظها."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"‏يمكن لهذا التطبيق قراءة جميع أحداث التقويم المخزَّنة على جهاز Android TV ومشاركة بيانات التقويم أو حفظها."</string>
@@ -743,6 +745,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"بدء اتخاذ القرارات المتعلقة بالإذن بعرض البيانات"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"يمكنك السماح للمالك ببدء الشاشة لمراجعة القرارات المتعلقة بالأذونات. لا حاجة لذلك مع التطبيقات العادية."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"بدء عرض ميزات التطبيق"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"للسماح للمالك ببدء عرض معلومات عن ميزات التطبيق."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"الوصول إلى بيانات جهاز الاستشعار بمعدّل مرتفع للبيانات في الملف الصوتي"</string>
@@ -2117,8 +2121,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"إشعار تطبيق مخصّص"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"هل تسمح لتطبيق <xliff:g id="APP">%1$s</xliff:g> بإنشاء مستخدم جديد باستخدام <xliff:g id="ACCOUNT">%2$s</xliff:g> (يوجد مستخدم بهذا الحساب مسبقًا)؟"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"هل تسمح لتطبيق <xliff:g id="APP">%1$s</xliff:g> بإنشاء مستخدم جديد باستخدام <xliff:g id="ACCOUNT">%2$s</xliff:g> ؟"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"إضافة مستخدم تحت الإشراف"</string>
     <string name="language_selection_title" msgid="52674936078683285">"إضافة لغة"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"تفضيل المنطقة"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"اكتب اسم اللغة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 0c09239..f57e42b 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"এপ্‌টোৱে অন্তৰ্গামী আৰু বহিৰ্গামী কলৰ ডেটাকে ধৰি আপোনাৰ Android TV ডিভাইচৰ কল লগ সংশোধন কৰিবলৈ অনুমতি দিয়ে। ক্ষতিকাৰক এপ্‌সমূহে আপোনাৰ কল লগ মচিবলৈ বা সংশোধন কৰিবলৈ এইটো ব্যৱহাৰ কৰিব পাৰে।"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"অন্তৰ্গামী আৰু বহিৰ্গামী কলৰ ডেটাকে ধৰি আপোনাৰ ফ\'নৰ কল লগ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। ক্ষতিকাৰক এপবোৰে আপোনাৰ কল লগ মচিবলৈ বা সংশোধন কৰিবলৈ ইয়াক ব্যৱহাৰ কৰিব পাৰে।"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"শৰীৰৰ ছেন্সৰসমূহ (যেনে হৃদপিণ্ডৰ গতিৰ হাৰ নিৰীক্ষক) ব্যৱহাৰ কৰিব পাৰে"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"আপোনাৰ হৃদস্পন্দনৰ দৰে শাৰীৰিক অৱস্থাক নিৰীক্ষণ কৰা ছেন্সৰৰ পৰা ডেটা লাভ কৰিবলৈ এপক অনুমতি দিয়ে।"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"শৰীৰৰ ছেন্সৰৰ পৰা হৃদস্পন্দনৰ হাৰ, উষ্ণতা, তেজত অক্সিজেনৰ শতকৰা হাৰ ইত্যাদিৰ দৰে ডেটাৰ এক্সেছ পাওক।"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"নেপথ্যত থকাৰ সময়ত শৰীৰৰ ছেন্সৰ (উদাহৰণস্বৰূপে, হৃদস্পন্দনৰ হাৰ নিৰীক্ষক)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"নেপথ্যত থকাৰ সময়ত শৰীৰৰ ছেন্সৰৰ পৰা হৃদস্পন্দনৰ হাৰ, উষ্ণতা, তেজত অক্সিজেনৰ শতকৰা হাৰ ইত্যাদিৰ দৰে ডেটাৰ এক্সেছ পাওক।"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"কেলেণ্ডাৰৰ কাৰ্যক্ৰম আৰু সবিশেষ পঢ়িব পাৰে"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"এই এপ্‌টোৱে আপোনাৰ টেবলেটটোত সংৰক্ষিত আটাইবোৰ কেলেণ্ডাৰ কাৰ্যক্ৰম পঢ়িব পাৰে আৰু আপোনাৰ কেলেণ্ডাৰৰ ডেটা শ্বেয়াৰ বা ছেভ কৰিব পাৰে।"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"এই এপ্‌টোৱে আপোনাৰ Android TV ডিভাইচটোত ষ্ট’ৰ কৰি ৰখা আটাইবোৰ কেলেণ্ডাৰৰ অনুষ্ঠান পঢ়িব পাৰে আৰু আপোনাৰ কেলেণ্ডাৰৰ ডেটা শ্বেয়াৰ অথবা ছেভ কৰিব পাৰে।"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অসুবিধা নিদিবৰ কনফিগাৰেশ্বনক পঢ়িবলৈ আৰু সালসলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"চোৱাৰ অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰক"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ধাৰকক কোনো এপৰ বাবে অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰিবলৈ দিয়ে। সাধাৰণ এপ্‌সমূহৰ বাবে কেতিয়াও প্ৰয়োজন হ’ব নালাগে।"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"অনুমতিৰ সিদ্ধান্তসমূহ চোৱা আৰম্ভ কৰক"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ধাৰকক অনুমতিৰ সিদ্ধান্তসমূহ পৰ্যালোচনা কৰিবলৈ স্ক্ৰীন আৰম্ভ কৰিবলৈ দিয়ে। সাধাৰণ এপ্‌সমূহৰ বাবে কেতিয়াও প্ৰয়োজন হ’ব নালাগে।"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"এপৰ সুবিধাসমূহৰ সম্পর্কীয় তথ্য চোৱাটো আৰম্ভ কৰক"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ধাৰকক কোনো এপৰ সুবিধাসমূহৰ সম্পর্কীয় তথ্য চোৱাটো আৰম্ভ কৰিবলৈ দিয়ে।"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"এটা উচ্চ ছেম্পলিঙৰ হাৰত ছেন্সৰৰ ডেটা এক্সেছ কৰে"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a11b68d..76384f9 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Gələn və gedən zənglərlə bağlı məlumatlar daxil olmaqla tətbiqə Android TV cihazının zəng siyahısını dəyişdirmək icazəsi verir. Zərərli proqramlar zəng siyahısını silmək və ya dəyişdirmək üçün bundan istifadə edə bilər."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Tətbiqə sizin daxil olan və gedən zənglər daxil olmaqla telefon zəngi loqlarınızı redaktə etmək icazəsi verir. Zərərli tətbiqlər bundan telefon loqlarınızı silmək və ya redaktə etmək üçün istifadə edə bilər."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"bədən sensorlarına (ürək döyüntüsü monitorları kimi) giriş"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Tətbiqə ürək döyüntüsü kimi fiziki durumunuzu izləməyən sensorların datasına daxil olmağa icazə verir."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Ürək döyüntüsü, temperatur, qanda oksigen faizi kimi bədən sensorlarından əldə edilən məlumatlara giriş."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"arxa fonda işləyərkən bədən sensorlarına (ürək döyüntüsü nəzarətləri kimi) giriş"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Arxa fonda işləyərkən ürək döyüntüsü, temperatur, qanda oksigen faizi kimi bədən sensorlarından əldə edilən məlumatlara giriş."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Təqvim təqdirləri və detallarını oxuyun"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Bu tətbiq planşetdə yerləşdirilmiş və təqvim datasında yadda saxlanmış bütün təqvim tədbirlərini oxuya bilər."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Bu tətbiq Android TV cihazında saxlanılan bütün təqvim tədbirlərini oxuya, həmçinin təqvim datasını paylaşa və ya yadda saxlaya bilər."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Tətbiqə \"Narahat Etməyin\" konfiqurasiyasını oxumağa və yazmağa icazə verin."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Baxış icazəsinin istifadəsinə başlayın"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Sahibinə tətbiqin icazədən istifadəsinə başlamağa imkan verir. Adi tətbiqlər üçün heç vaxt tələb edilmir."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"icazə qərarlarına baxışı başladın"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Sahibin icazə qərarlarını nəzərdən keçirmək üçün ekranı başlatmasına icazə verir. Normal tətbiqlər tərəfindən heç vaxt tələb edilmir."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"tətbiqin funksiyalarını görməyə başlamaq"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"İstifadəçinin tətbiqin funksiyaları barədə məlumatları görməyə başlamasına icazə verir."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensor datasına yüksək ölçmə sürəti ilə giriş etmək"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index b8e0d9a..15e3aa0 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -430,7 +430,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Dozvoljava aplikaciji da menja evidenciju poziva na Android TV uređaju, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamerne aplikacije mogu ovo da koriste za brisanje ili menjanje evidencije poziva."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Dozvoljava aplikaciji da menja evidenciju poziva na telefonu, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamerne aplikacije mogu ovo da koriste da bi brisale ili menjale evidenciju poziva."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"pristup senzorima na telu (poput monitora za praćenje pulsa)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Dozvoljava aplikaciji da pristupa podacima sa senzora koji nadgledaju fizičku kondiciju, kao što je broj otkucaja srca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Pristup podacima senzora za telo, poput pulsa, temperature, procenat kiseonika u krvi itd."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"pristup senzorima na telu (npr. monitori za praćenje pulsa) tokom rada u pozadini"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Pristup podacima senzora za telo, poput pulsa, temperature, procenat kiseonika u krvi itd, tokom rada u pozadini."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Čitanje događaja i podataka iz kalendara"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ova aplikacija može da čita sve događaje iz kalendara koje čuvate na tabletu, kao i da deli ili čuva podatke iz kalendara."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ova aplikacija može da čita sve događaje iz kalendara koje čuvate na Android TV uređaju, kao i da deli ili čuva podatke iz kalendara."</string>
@@ -734,6 +736,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Dozvoljava aplikaciji da čita i upisuje konfiguraciju podešavanja Ne uznemiravaj."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"početak korišćenja dozvole za pregled"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da započne korišćenje dozvole za aplikaciju. Nikada ne bi trebalo da bude potrebna za uobičajene aplikacije."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"pokretanje pregleda odluka o dozvolama"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Dozvoljava vlasniku da pokrene ekran za proveru odluka o dozvolama. Nikada ne bi trebalo da bude potrebno za obične aplikacije."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pokretanje prikaza funkcija aplikacije"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Dozvoljava nosiocu dozvole da započne pregledanje informacija o funkcijama aplikacije."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri velikoj brzini uzorkovanja"</string>
@@ -1546,7 +1550,7 @@
     <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Veza sa uvek uključenim VPN-om je prekinuta"</string>
     <string name="vpn_lockdown_error" msgid="4453048646854247947">"Povezivanje na stalno uključeni VPN nije uspelo"</string>
     <string name="vpn_lockdown_config" msgid="8331697329868252169">"Promenite podešavanja VPN-a"</string>
-    <string name="upload_file" msgid="8651942222301634271">"Odaberi datoteku"</string>
+    <string name="upload_file" msgid="8651942222301634271">"Odaberi fajl"</string>
     <string name="no_file_chosen" msgid="4146295695162318057">"Nije izabrana nijedna datoteka"</string>
     <string name="reset" msgid="3865826612628171429">"Resetuj"</string>
     <string name="submit" msgid="862795280643405865">"Pošalji"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 26d2e73..e182c28 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Дазваляе праграме змяняць журнал выклікаў прылады Android TV, у тым ліку даныя пра ўваходныя і выходныя выклікі. Шкодныя праграмы могуць злоўжыць гэтым і сцерці ці змяніць даныя ў журнале выклікаў."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Дазваляе прыкладанням змяняць гiсторыю выклiкаў тэлефону, у тым лiку дадзеныя пра ўваходныя і зыходныя выклiкi. Шкоднасныя прыкладаннi могуць выкарыстоўваць гэта, каб выдаляць або змяняць гiсторыю выклікаў."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"атрымліваць доступ да датчыкаў цела (напрыклад, пульсометраў)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Дазваляе праграме атрымліваць доступ да даных з датчыкаў, якія назіраюць за вашым фізічным станам, напрыклад, за частатой пульсу."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Доступ да даных з датчыкаў цела, такіх як пульс, тэмпература, працэнт утрымання ў крыві кіслароду і г. д."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"атрымліваць у фонавым рэжыме доступ да датчыкаў цела (напрыклад, пульсометраў)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Доступ у фонавым рэжыме да даных з датчыкаў цела, такіх як пульс, тэмпература, працэнт утрымання ў крыві кіслароду і г. д."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Чытаць падзеі календара і падрабязныя звесткі"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Гэта праграма можа чытаць усе падзеі календара, захаваныя на вашым планшэце, і абагульваць ці захоўваць даныя календара."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Гэта праграма можа счытваць усе падзеі календара, захаваныя на прыладзе Android TV, і абагульваць ці захоўваць яго даныя."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дазваляе праграме чытаць і выконваць запіс у канфігурацыю рэжыму «Не турбаваць»."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"запусціць выкарыстанне дазволаў на прагляд"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дазваляе трымальніку запусціць выкарыстанне дазволаў праграмай. Не патрэбна для звычайных праграм."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"запускаць прагляд рашэнняў наконт дазволаў"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Дазваляе ўладальніку запускаць экран, на якім можна праглядаць рашэнні наконт дазволаў. Ніколі не павінна патрабавацца для звычайных праграм."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"запусціць прагляд функцый праграмы"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дазваляе трымальніку запусціць прагляд інфармацыі пра функцыі для праграмы."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"атрымліваць даныя датчыка з высокай частатой дыскрэтызацыі"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Апавяшчэнне пра карыстальніцкую праграму"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Дазволіць праграме \"<xliff:g id="APP">%1$s</xliff:g>\" стварыць новага Карыстальніка з уліковым запісам <xliff:g id="ACCOUNT">%2$s</xliff:g> (Карыстальнік з гэтым уліковым запісам ужо існуе)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Дазволіць праграме \"<xliff:g id="APP">%1$s</xliff:g>\" стварыць новага Карыстальніка з уліковым запісам <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Дадаць падкантрольнага карыстальніка"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Дадаць мову"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Параметры рэгіёна"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Увядзіце назву мовы"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 5250956..41aa9f7 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Дава възможност на приложението да променя списъка с обажданията на устройството ви с Android TV, включително данните за входящите и изходящите обаждания. Злонамерените приложения може да използват разрешението, за да изтрият или променят този списък."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Разрешава на приложението да променя списъка с обаждания на телефона ви, включително данните за входящите и изходящите обаждания. Злонамерените приложения могат да използват това, за да изтрият или променят този списък."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"достъп до телесните сензори (напр. пулсомери)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Разрешава на приложението да осъществява достъп до данните от сензорите, които следят физическото ви състояние, като например сърдечния ви ритъм."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Достъп до данните от сензорите за тяло, като например сърдечен ритъм, температура, процент на кислорода в кръвта и др."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"достъп до сензорите за тяло (напр. пулсомери), докато е на заден план"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Достъп до данните от сензорите за тяло, като например сърдечен ритъм, температура, процент на кислорода в кръвта и др., докато е на заден план."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Четене на събития и подробности от календара"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Това приложение може да чете всички съхранявани на таблета ви събития в календара и да споделя или запазва данни в календара ви."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Това приложение може да чете всички съхранявани на устройството ви с Android TV събития в календара и да споделя или запазва данни в календара ви."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Предоставя на приложението достъп за четене и запис до конфигурацията на „Не безпокойте“."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"стартиране на прегледа на използваните разрешения"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Разрешава на притежателя да стартира прегледа на използваните разрешения за дадено приложение. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"стартиране на прегледа на решенията за разрешенията"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Дава възможност на притежателя да стартира екрана с цел преглед на решенията за разрешенията. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"стартиране на прегледа на функциите на приложението"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Разрешава на притежателя да стартира прегледа на информацията за функциите на дадено приложение."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"осъществяване на достъп до данните от сензорите при висока скорост на семплиране"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 657596f..7dac240d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"অ্যাপটিকে ইনকামিং ও আউটগোয়িং কল সহ আপনার Android TV ডিভাইসের কল লগে পরিবর্তন করার অনুমতি দেয়। ক্ষতিকারক অ্যাপ এটিকে কাজে লাগিয়ে আপনার কল লগে পরিবর্তন করতে পারে বা সেটি মুছে ফেলতে পারে।"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ইনকামিং ও আউটগোয়িং কলগুলি সম্পর্কিত ডেটা সহ আপনার ফোনের কল লগ পরিবর্তন করতে দেয়৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি এটিকে আপনার কল লগ মুছে দিতে বা পরিবর্তন করতে ব্যবহার করতে পারে৷"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"শরীরের সেন্সর (হার্ট রেট মনিটারের মত) অ্যাক্সেস করুন"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"অ্যাপ্লিকেশানটিকে আপনার শারীরিক অবস্থা যেমন, আপনার হৃৎস্পন্দন পর্যবেক্ষণ করে এমন সেন্সরগুলি অ্যাক্সেস করতে মঞ্জুরি দেয়।"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"বডি সেন্সর ডেটাতে যেমন হার্ট রেট, তাপমাত্রা, রক্তে অক্সিজেনের শতাংশ ইত্যাদিতে অ্যাক্সেস।"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ব্যাকগ্রাউন্ডে থাকার সময়ে বডি সেন্সর ডেটাতে (যেমন হার্ট রেট মনিটর) অ্যাক্সেস"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ব্যাকগ্রাউন্ডে থাকার সময়ে বডি সেন্সর ডেটা যেমন হার্ট রেট, তাপমাত্রা, রক্তে অক্সিজেনের শতাংশ ইত্যাদিতে অ্যাক্সেস।"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"ক্যালেন্ডারের ইভেন্ট এবং বিশদ বিবরণ পড়ুন"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"এই অ্যাপটি আপনার ট্যাবলেটে সংরক্ষিত সমস্ত ক্যালেন্ডার ইভেন্ট পড়তে এবং আপনার ক্যালেন্ডারের ডেটা শেয়ার বা সংরক্ষণ করতে পারে৷"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"এই অ্যাপ আপনার Android TV ডিভাইসে সেভ করা সব ক্যালেন্ডার ইভেন্ট পড়তে পারে এবং আপনার ক্যালেন্ডারের ডেটা শেয়ার বা সেভ করতে পারে।"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অ্যাপটিকে \'বিরক্ত করবে না\' কনফিগারেশন পড়া এবং লেখার অনুমতি দেয়।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"দেখার অনুমতি কাজে লাগানো শুরু করুন"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"কোনও অ্যাপের কোনও নির্দিষ্ট অনুমতির ব্যবহার শুরু করার ক্ষেত্রে হোল্ডারকে সাহায্য করে। সাধারণ অ্যাপের জন্য এটির পরিবর্তন হওয়ার কথা নয়।"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"অনুমতি সংক্রান্ত সিদ্ধান্ত দেখা শুরু করুন"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"অনুমতি সংক্রান্ত সিদ্ধান্ত পর্যালোচনা করার জন্য, হোল্ডারকে স্ক্রিন চালু করতে দেয়। সাধারণ অ্যাপের জন্য কখনই প্রয়োজন হওয়া উচিত নয়।"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"অ্যাপের ফিচার দেখা শুরু করুন"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"কোনও অ্যাপের ফিচার সম্পর্কিত তথ্য দেখা শুরু করতে অনুমতি দেয়।"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"হাই স্যাম্পলিং রেটে সেন্সর ডেটা অ্যাক্সেস করুন"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"কাস্টম অ্যাপ বিজ্ঞপ্তি"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g>-এ (একজন ব্যবহারকারী এই অ্যাকাউন্টে আগে থেকেই রয়েছেন) একজন নতুন ব্যবহারকারী তৈরি করার অনুমতি <xliff:g id="APP">%1$s</xliff:g>-কে দেবেন?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g>-এ একজন নতুন ব্যবহারকারী তৈরি করার অনুমতি <xliff:g id="APP">%1$s</xliff:g>-কে দেবেন?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"তত্ত্বাবধানে থাকা ব্যবহারকারী যোগ করুন"</string>
     <string name="language_selection_title" msgid="52674936078683285">"একটি ভাষা যোগ করুন"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"পছন্দের অঞ্চল"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"ভাষার নাম লিখুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 6e8cd97..28ca86d 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -430,7 +430,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Omogućava aplikaciji izmjenu zapisnika poziva Android TV uređaja, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije to mogu iskoristiti za brisanje ili izmjenu zapisnika poziva."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Omogućava aplikaciji da izmijeni zapisnik poziva sa vašeg telefona, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije mogu to iskoristiti za brisanje ili izmjenu vašeg zapisnika poziva."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"pristup tjelesnim senzorima (poput monitora za puls)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Dozvoljava aplikaciji pristup podacima sa senzora koji prate fizičke pokazatelje kao što je vaš puls."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Pristup podacima s tjelesnih senzora kao što su puls, temperatura, saturacija kisikom, itd."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"pristup tjelesnim senzorima (poput monitora za puls) dok je aplikacija u pozadini"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Pristup podacima s tjelesnih senzora kao što su puls, temperatura, saturacija kisikom, itd. dok je aplikacija u pozadini."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Čitanje događaja kalendara i detalja"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ova aplikacija može čitati sve događaje u kalendaru pohranjene na vašem tabletu i sačuvati podatke kalendara."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ova aplikacija može čitati sve događaje u kalendaru na vašem Android TV uređaju i dijeliti ili sačuvati podatke kalendara."</string>
@@ -734,6 +736,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućava aplikaciji da čita i upisuje konfiguraciju načina rada Ne ometaj."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti korištenje odobrenja za pregled"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da pokrene korištenje odobrenja za aplikaciju. Ne bi trebalo biti potrebno za obične aplikacije."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"prikažite odluke o odobrenjima"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Dozvoljava vlasniku da pokrene ekran radi pregleda odluka o odobrenju. Obično nije potrebno za obične aplikacije."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pokretanje pregleda funkcija aplikacije"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Dozvoljava vlasniku da pokrene pregled informacija o funkcijama za aplikaciju."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora velikom brzinom uzorkovanja"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 41af878..be1b43c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permet que l\'aplicació modifiqui el registre de trucades del dispositiu Android TV, incloses les dades de les trucades entrants i sortints. És possible que les aplicacions malicioses ho utilitzin per suprimir o per modificar el teu registre de trucades."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permet que l\'aplicació modifiqui el registre de trucades del teu telèfon, incloses les dades de les trucades entrants i sortints. És possible que les aplicacions malicioses ho utilitzin per eliminar o per modificar el teu registre de trucades."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"accedir a sensors corporals (p. ex., monitors de freqüència cardíaca)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permet que l\'aplicació accedeixi a les dades dels sensors que supervisen el teu estat físic, com ara la freqüència cardíaca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Accés a les dades dels sensors corporals com la freqüència cardíaca, la temperatura, el percentatge d\'oxigen a la sang, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"accedir a sensors corporals (com els monitors de freqüència cardíaca) en segon pla"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Accés a les dades dels sensors corporals, com ara la freqüència cardíaca, la temperatura o el percentatge d\'oxigen a la sang, en segon pla."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Aquesta aplicació pot llegir els esdeveniments i la informació del calendari"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Aquesta aplicació pot llegir tots els esdeveniments del calendari emmagatzemats a la tauleta i compartir o desar les dades del teu calendari."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Aquesta aplicació pot llegir tots els esdeveniments del calendari emmagatzemats al dispositiu Android TV i compartir o desar les dades del calendari."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet que l\'aplicació llegeixi la configuració No molestis i hi escrigui."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"comença a utilitzar el permís de visualització"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet que un propietari comenci a utilitzar el permís amb una aplicació. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"inicia la visualització de les decisions sobre permisos"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permet que l\'aplicació en qüestió iniciï la pantalla per revisar les decisions sobre permisos. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"iniciar la visualització de les funcions d\'una aplicació"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permet que el propietari vegi la informació de les funcions d\'una aplicació."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accedir a les dades del sensor a una freqüència de mostratge alta"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificació d\'aplicació personalitzada"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Ja hi ha un usuari amb aquest compte.)"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Afegeix un usuari supervisat"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Afegeix un idioma"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferència de regió"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Escriu el nom de l\'idioma"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 72ad45b..d038dfc 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Umožňuje aplikaci upravovat seznam hovorů na zařízení Android TV, včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Umožňuje aplikaci upravovat seznam hovorů vašeho telefonu, včetně dat o příchozích a odchozích hovorech. Škodlivé aplikace to mohou zneužít k vymazání nebo změnám seznamu hovorů."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"přístup k tělesným senzorům (např. snímače tepu)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Umožňuje aplikaci používat data ze senzorů, které sledují vaši fyzickou kondici, například tepovou frekvenci."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Přístup k datům z tělesných senzorů, jako jsou senzory tepové frekvence, teploty, procenta nasycení krve kyslíkem apod."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"přístup k tělesným senzorům (např. k senzorům tepu) při běhu na pozadí"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Přístup k datům z tělesných senzorů, jako jsou senzory tepové frekvence, teploty, procenta nasycení krve kyslíkem apod., při běhu na pozadí."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Čtení událostí v kalendáři včetně podrobností"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Tato aplikace může číst všechny události v kalendáři uložené v tabletu a sdílet či ukládat data kalendáře."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Tato aplikace může číst všechny události v kalendáři uložené v zařízení Android TV a sdílet či ukládat data kalendáře."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikaci číst a zapisovat konfiguraci režimu Nerušit."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"zahájení zobrazení využití oprávnění"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje přístup zahájit využití oprávnění jiné aplikace. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"zobrazit rozhodnutí o oprávnění"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Umožňuje držiteli aktivovat obrazovku a zkontrolovat rozhodnutí o oprávnění. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"zobrazení informací o funkcích aplikace"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Umožňuje držiteli zobrazit informace o funkcích aplikace."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"přístup k datům ze senzorů s vyšší vzorkovací frekvencí"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Vlastní oznámení aplikace"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Povolit aplikaci <xliff:g id="APP">%1$s</xliff:g> vytvořit nového uživatele s účtem <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Uživatel s tímto účtem již existuje.)"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Povolit aplikaci <xliff:g id="APP">%1$s</xliff:g> vytvořit nového uživatele s účtem <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Přidat dozorovaného uživatele"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Přidat jazyk"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferovaná oblast"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Zadejte název jazyka"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8f4039f..afe1175 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Tillader, at appen ændrer din Android TV-enheds opkaldshistorik, bl.a. data om indgående og udgående opkald. Skadelige apps kan bruge dette til at rydde eller ændre din opkaldshistorik."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Tillader, at appen ændrer telefonens opkaldshistorik, f.eks. data om indgående og udgående opkald. Ondsindede apps kan bruge dette til at slette eller ændre din opkaldshistorik."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"få adgang til kropssensorer (f.eks. pulsmålere)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Giver appen adgang til data fra sensorer, der overvåger din fysiske tilstand, f.eks. din puls."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Adgang til data fra kropssensorer såsom puls, temperatur, procentdelen af ilt i blodet m.m."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"tilgå kropssensorer (f.eks. pulsmålere), mens appen kører i baggrunden."</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Adgang til data fra kropssensorer såsom puls, temperatur, procentdelen af ilt i blodet m.m., mens appen kører i baggrunden."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Læs kalenderbegivenheder og -info"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Denne app kan læse alle kalenderbegivenheder, der er gemt på din tablet, og dele eller gemme dine kalenderdata."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Denne app kan læse alle kalenderbegivenheder, der er gemt på din Android TV-enhed, og dele eller gemme dine kalenderdata."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Giver appen tilladelse til at læse og redigere konfigurationen af Forstyr ikke."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start brugen at tilladelsesvisning"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Tillader, at brugeren kan bruge en tilladelse for en app. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"starte visningen af beslutninger om tilladelser"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Giver modtageren mulighed for at åbne skærmen til gennemgang af beslutninger om tilladelser. Dette bør aldrig være nødvendigt for standardapps."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"se appfunktioner"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Giver den app, som har tilladelsen, mulighed for at se oplysninger om en apps funktioner."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"få adgang til sensordata ved høj samplingfrekvens"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Tilpasset appnotifikation"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en ny bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g> (der findes allerede en bruger med denne konto)?"</string>
     <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>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <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="search_language_hint" msgid="7004225294308793583">"Angiv sprog"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 5e5e578..ecaa63c 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Ermöglicht der App, die Anrufliste deines Android TV-Geräts zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Du solltest wissen, dass dies von schädlichen Apps genutzt werden kann, um Einträge in der Anrufliste zu löschen oder zu ändern."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Ermöglicht der App, die Anrufliste deines Telefons zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so die Einträge in der Anrufliste löschen oder sie ändern."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"Auf Körpersensoren wie z. B. Pulsmesser zugreifen"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Ermöglicht der App, auf Daten von Sensoren zuzugreifen, die deine körperliche Verfassung überwachen, beispielsweise deinen Puls"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Zugriff auf Daten von Körpersensoren, wie z. B. Herzfrequenz, Temperatur und Sauerstoffsättigung."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"Auf im Hintergrund aktive Körpersensoren (z. B. Herzfrequenzmesser) zugreifen"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Zugriff auf Daten von Körpersensoren, wie z. B. Herzfrequenz, Temperatur und Sauerstoffsättigung, während sie im Hintergrund aktiv sind."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Kalendertermine und Details lesen"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Diese App kann alle auf deinem Tablet gespeicherten Kalendertermine lesen und deine Kalenderdaten teilen oder speichern."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Diese App kann alle auf deinem Android TV-Gerät gespeicherten Kalendertermine lesen und die Kalenderdaten teilen oder speichern."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ermöglicht der App Lese- und Schreibzugriff auf die „Bitte nicht stören“-Konfiguration"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Mit der Verwendung der Anzeigeberechtigung beginnen"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ermöglicht dem Inhaber, die Berechtigungsnutzung für eine App zu beginnen. Sollte für normale Apps nie benötigt werden."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"Entscheidungen zu Leseberechtigung starten"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Ermöglicht dem Inhaber, das Display zu starten, um Berechtigungsentscheidungen anzusehen. Sollte für normale Apps nie benötigt werden."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"App-Funktionen ansehen"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Ermöglicht der App, die Informationen über die Funktionen einer anderen App anzusehen."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Sensordaten mit hoher Frequenz auslesen"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Benutzerdefinierte App-Benachrichtigung"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Es gibt bereits einen Nutzer mit <xliff:g id="ACCOUNT">%2$s</xliff:g>. Möchtest du zulassen, dass <xliff:g id="APP">%1$s</xliff:g> einen neuen Nutzer mit diesem Konto erstellt?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Möchtest du zulassen, dass <xliff:g id="APP">%1$s</xliff:g> einen neuen Nutzer mit <xliff:g id="ACCOUNT">%2$s</xliff:g> erstellt?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Betreuten Nutzer hinzufügen"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Sprache hinzufügen"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Region auswählen"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Sprache eingeben"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index f184200..44fe426 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων της συσκευής σας Android TV, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Οι κακόβουλες εφαρμογές μπορεί να εκμεταλλευτούν αυτή την άδεια για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων του τηλεφώνου σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"πρόσβαση στους αισθητήρες λειτουργιών (π.χ. παρακολούθηση καρδιακού παλμού)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Επιτρέπει στην εφαρμογή να αποκτήσει πρόσβαση στα δεδομένα των αισθητήρων που παρακολουθούν τη φυσική σας κατάσταση, όπως τον καρδιακό ρυθμό σας."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Πρόσβαση σε δεδομένα από αισθητήρες σώματος όπως καρδιακού ρυθμού, θερμοκρασίας, ποσοστού οξυγόνου στο αίμα κ.λπ."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"πρόσβαση σε αισθητήρες σώματος (π.χ. καρδιακού ρυθμού) στο παρασκήνιο"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Πρόσβαση σε δεδομένα από αισθητήρες σώματος όπως καρδιακού ρυθμού, θερμοκρασίας, ποσοστού οξυγόνου στο αίμα κ.λπ. κατά την εκτέλεση στο παρασκήνιο."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Ανάγνωση συμβάντων ημερολογίου και λεπτομερειών"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Αυτή η εφαρμογή μπορεί να διαβάσει όλα τα συμβάντα ημερολογίου που είναι αποθηκευμένα στο tablet που χρησιμοποιείτε και να μοιραστεί ή να αποθηκεύσει τα δεδομένα ημερολογίου σας."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Αυτή η εφαρμογή μπορεί να διαβάσει όλα τα συμβάντα ημερολογίου που είναι αποθηκευμένα στη συσκευή Android TV και να μοιραστεί ή να αποθηκεύσει τα δεδομένα ημερολογίου σας."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Επιτρέπει στην εφαρμογή την εγγραφή και τη σύνταξη διαμόρφωσης για τη λειτουργία \"Μην ενοχλείτε\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"έναρξη χρήσης άδειας προβολής"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Επιτρέπει στον κάτοχο να ξεκινήσει τη χρήση της άδειας για μια εφαρμογή. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"έναρξη προβολής αποφάσεων για άδειες"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Επιτρέπει στον κάτοχο να ξεκινήσει την προβολή για τον έλεγχο των αποφάσεων για τις άδειες. Δεν χρειάζεται ποτέ για κανονικές εφαρμογές."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"έναρξη προβολής λειτουργιών εφαρμογής"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Επιτρέπει στον κάτοχο να ξεκινήσει την προβολή των πληροφοριών για τις λειτουργίες μιας εφαρμογής."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"πρόσβαση σε δεδομένα αισθητήρα με υψηλό ρυθμό δειγματοληψίας"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Προσαρμοσμένη ειδοποίηση εφαρμογής"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Επιτρέπετε στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g> να δημιουργήσει έναν νέο χρήστη με τον λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g> (υπάρχει ήδη χρήστης με αυτόν τον λογαριασμό);"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Επιτρέπετε στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g> να δημιουργήσει έναν νέο χρήστη με τον λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g>;"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Προσθήκη εποπτευόμενου χρήστη"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Προσθήκη γλώσσας"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Προτίμηση περιοχής"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Εισαγ. όνομα γλώσσας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 2bf964b..a23eda2 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Allows the app to modify your Android TV device\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to delete or modify your call log."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"access body sensors (like heart rate monitors)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"access body sensors (like heart rate monitors) while in the background"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc. while in the background."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Read calendar events and details"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"This app can read all calendar events stored on your tablet and share or save your calendar data."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"This app can read all calendar events stored on your Android TV device and share or save your calendar data."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"start view permission decisions"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Allows the holder to start screen to review permission decisions. Should never be needed for normal apps."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index b4d1c82..281ecb1 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Allows the app to modify your Android TV device\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to delete or modify your call log."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"access body sensors (like heart rate monitors)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"access body sensors (like heart rate monitors) while in the background"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc. while in the background."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Read calendar events and details"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"This app can read all calendar events stored on your tablet and share or save your calendar data."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"This app can read all calendar events stored on your Android TV device and share or save your calendar data."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"start view permission decisions"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Allows the holder to start screen to review permission decisions. Should never be needed for normal apps."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e47a2c2..8d8b33c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Allows the app to modify your Android TV device\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to delete or modify your call log."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"access body sensors (like heart rate monitors)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"access body sensors (like heart rate monitors) while in the background"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc. while in the background."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Read calendar events and details"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"This app can read all calendar events stored on your tablet and share or save your calendar data."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"This app can read all calendar events stored on your Android TV device and share or save your calendar data."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"start view permission decisions"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Allows the holder to start screen to review permission decisions. Should never be needed for normal apps."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 2108367..e411655 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Allows the app to modify your Android TV device\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to delete or modify your call log."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"access body sensors (like heart rate monitors)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Allows the app to access data from sensors that monitor your physical condition, such as your heart rate."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"access body sensors (like heart rate monitors) while in the background"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Access to data from body sensors, such as heart rate, temperature, blood oxygen percentage, etc. while in the background."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Read calendar events and details"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"This app can read all calendar events stored on your tablet and share or save your calendar data."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"This app can read all calendar events stored on your Android TV device and share or save your calendar data."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"start view permission decisions"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Allows the holder to start screen to review permission decisions. Should never be needed for normal apps."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 68b1de3..c2e7472 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎Allows the app to modify your Android TV device\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log.‎‏‎‎‏‎"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‎‎Allows the app to modify your phone\'s call log, including data about incoming and outgoing calls. Malicious apps may use this to erase or modify your call log.‎‏‎‎‏‎"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‎access body sensors (like heart rate monitors)‎‏‎‎‏‎"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‎Allows the app to access data from sensors that monitor your physical condition, such as your heart rate.‎‏‎‎‏‎"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎Access to data from body sensors such as heart rate, temperature, blood oxygen percentage, etc.‎‏‎‎‏‎"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎access body sensors (like heart rate monitors) while in the background‎‏‎‎‏‎"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎Access to data from body sensors such as heart rate, temperature, blood oxygen percentage, etc. while in the background.‎‏‎‎‏‎"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎Read calendar events and details‎‏‎‎‏‎"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎This app can read all calendar events stored on your tablet and share or save your calendar data.‎‏‎‎‏‎"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎This app can read all calendar events stored on your Android TV device and share or save your calendar data.‎‏‎‎‏‎"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎Allows the app to read and write Do Not Disturb configuration.‎‏‎‎‏‎"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‎start view permission usage‎‏‎‎‏‎"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‎Allows the holder to start the permission usage for an app. Should never be needed for normal apps.‎‏‎‎‏‎"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎start view permission decisions‎‏‎‎‏‎"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎Allows the holder to start screen to review permission decisions. Should never be needed for normal apps.‎‏‎‎‏‎"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎start view app features‎‏‎‎‏‎"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎Allows the holder to start viewing the features info for an app.‎‏‎‎‏‎"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎access sensor data at a high sampling rate‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 517ee57..fad71eb7 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que la app modifique el registro de llamadas del dispositivo Android TV, incluidos datos sobre llamadas entrantes y salientes. Las apps maliciosas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite que la aplicación modifique el registro de llamadas del dispositivo, incluidos los datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"acceder a los sensores corporales (como los monitores de frecuencia cardíaca)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite que la app acceda a datos de sensores que monitorean tu estado físico, como la frecuencia cardíaca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acceso a datos de sensores corporales, como el ritmo cardíaco, la temperatura, el porcentaje de oxígeno en sangre, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"acceso a sensores corporales (p. ej., ritmo cardíaco) en segundo plano"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acceso en segundo plano a datos de sensores corporales, como el ritmo cardíaco, la temperatura, el porcentaje de oxígeno en sangre, etc."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Leer eventos y detalles del calendario"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Esta app puede leer todos los eventos del calendario de tu tablet y compartir o guardar los datos correspondientes."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Esta app puede leer todos los eventos del calendario guardados en el dispositivo Android TV, así como compartir o almacenar los datos correspondientes."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de la función No interrumpir."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el propietario inicie el uso de permisos para una app. No debería requerirse para apps normales."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"iniciar vista de las decisiones sobre permisos"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite que el propietario vea la pantalla a fin de revisar las decisiones que tomó sobre los permisos. Las apps normales no deberían necesitar este permiso."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"iniciar vista de funciones de la app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que el propietario vea la información de las funciones de una app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Acceder a los datos de sensores a una tasa de muestreo alta"</string>
@@ -773,7 +777,7 @@
     <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Evita el uso de algunas funciones de bloqueo de pantalla."</string>
   <string-array name="phoneTypes">
     <item msgid="8996339953292723951">"Casa"</item>
-    <item msgid="7740243458912727194">"Móvil"</item>
+    <item msgid="7740243458912727194">"Celular"</item>
     <item msgid="8526146065496663766">"Trabajo"</item>
     <item msgid="8150904584178569699">"Fax laboral"</item>
     <item msgid="4537253139152229577">"Fax residencial"</item>
@@ -816,7 +820,7 @@
   </string-array>
     <string name="phoneTypeCustom" msgid="5120365721260686814">"Personalizado"</string>
     <string name="phoneTypeHome" msgid="3880132427643623588">"Casa"</string>
-    <string name="phoneTypeMobile" msgid="1178852541462086735">"Móvil"</string>
+    <string name="phoneTypeMobile" msgid="1178852541462086735">"Celular"</string>
     <string name="phoneTypeWork" msgid="6604967163358864607">"Trabajo"</string>
     <string name="phoneTypeFaxWork" msgid="6757519896109439123">"Fax laboral"</string>
     <string name="phoneTypeFaxHome" msgid="6678559953115904345">"Fax personal"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de app personalizada"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"¿Quieres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario nuevo con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Ya existe un usuario con esta cuenta)"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"¿Deseas permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario nuevo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Agregar usuario supervisado"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Agregar un idioma"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferencia de región"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Nombre del idioma"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b58d116..669e914 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que la aplicación edite el registro de llamadas de tu dispositivo Android TV, incluidos sus datos sobre llamadas entrantes y salientes. Las aplicaciones maliciosas pueden usar este permiso para borrar o editar el registro de llamadas."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite que la aplicación modifique el registro de llamadas del teléfono, incluidos datos sobre llamadas entrantes y salientes. Las aplicaciones malintencionadas pueden usar este permiso para borrar o modificar el registro de llamadas."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"sensores corp. (como monitores frec. cardíaca)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite que la aplicación acceda a datos de sensores que controlan tu condición física, como la frecuencia cardíaca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acceso a datos de sensores corporales, como la frecuencia cardiaca, la temperatura, el porcentaje de oxígeno en sangre, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"acceso a sensores corporales (como monitores de frec. cardiaca) en segundo plano"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acceso a datos de sensores corporales, como la frecuencia cardiaca, la temperatura, el porcentaje de oxígeno en sangre, etc., en segundo plano."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Leer eventos y detalles del calendario"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Esta aplicación puede leer los eventos de calendario almacenados en tu tablet y compartir o guardar los datos de tu calendario."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Esta aplicación puede ver los eventos de calendario almacenados en tu dispositivo Android TV y compartir o guardar los datos de tu calendario."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de No molestar."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de visualización"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el titular inicie el uso de permisos de una aplicación. Las aplicaciones normales no deberían necesitar nunca este permiso."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"iniciar la revisión de decisiones sobre los permisos"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite que el titular inicie la revisión de las decisiones sobre los permisos. Las aplicaciones normales no deberían necesitar nunca este permiso."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ver funciones de una aplicación"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que el titular vea la información de las funciones de una aplicación."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder a datos de sensores a una frecuencia de muestreo alta"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de aplicación personalizada"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"¿Permitir que <xliff:g id="APP">%1$s</xliff:g> cree otro usuario con la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g>, que ya tiene uno?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"¿Permitir que <xliff:g id="APP">%1$s</xliff:g> cree otro usuario con la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Añadir usuario supervisado"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Añadir un idioma"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferencia de región"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Nombre de idioma"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3afe63dd..e4d33a7 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Lubab rakendusel muuta teie Android TV seadme kõnelogi, sh sissetulevate ja väljaminevate kõnede andmeid. Pahatahtlikud rakendused võivad kasutada seda kõnelogi kustutamiseks või muutmiseks."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Lubab rakendusel muuta telefoni kõnelogi, sh sissetulevate ja väljaminevate kõnede andmeid. Pahatahtlikud rakendused võivad kasutada seda kõnelogi kustutamiseks või muutmiseks."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"juurdepääs kehaanduritele (nt pulsilugeja)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Lubab rakendusel hankida juurdepääsu andmetele anduritest, mis jälgivad teie füüsilist seisundit, nt südame löögisagedust."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Juurdepääs kehaandurite andmetele, nagu pulss, temperatuur, vere hapnikusisalduse protsent jne."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"taustal töötades juurdepääs kehaanduritele (nt pulsilugeja)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Taustal töötades juurdepääs kehaandurite andmetele, nagu pulss, temperatuur, vere hapnikusisalduse protsent jne."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Kalendrisündmuste ja üksikasjade lugemine"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"See rakendus saab kõiki teie tahvelarvutisse salvestatud kalendrisündmusi lugeda ja teie kalendriandmeid jagada või salvestada."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"See rakendus saab kõiki teie Android TV seadmesse salvestatud kalendrisündmusi lugeda ja teie kalendriandmeid jagada või salvestada."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Võimaldab rakendusel lugeda ja kirjutada funktsiooni Mitte segada seadistusi."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"vaatamisloa kasutamise alustamine"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Võimaldab omanikul rakenduse puhul alustada loa kasutamist. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"Alustada lubade otsuste vaatamist."</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Võimaldab omanikul ekraani käivitada, et lubade otsused üle vaadata. Tavaliste rakenduste puhul ei tohiks seda kunagi vaja minna."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"rakenduse funktsioonide vaatamise alustamine"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Võimaldab omanikul alustada rakenduse funktsioonide teabe vaatamist."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"juurdepääs anduri andmetele kõrgel diskreetimissagedusel"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Rakenduse kohandatud märguanne"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Kas lubada rakendusel <xliff:g id="APP">%1$s</xliff:g> luua uus kasutaja kontoga <xliff:g id="ACCOUNT">%2$s</xliff:g> (selle kontoga kasutaja on juba olemas)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Kas lubada rakendusel <xliff:g id="APP">%1$s</xliff:g> luua uus kasutaja kontoga <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Jälgitava kasutaja lisamine"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Keele lisamine"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Piirkonnaeelistus"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Sisestage keele nimi"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index b4b4ec2..a3766b76 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Android TV gailuko deien erregistroa aldatzeko baimena ematen die aplikazioei, jasotako eta egindako deiei buruzko datuak barne. Baliteke asmo txarreko aplikazioek deien erregistroa ezabatzea edo aldatzea."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Telefonoaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Asmo txarreko aplikazioek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"Atzitu gorputzaren sentsoreak (adibidez, bihotz-maiztasunarenak)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Zure egoera fisikoa kontrolatzen duten sentsoreetako datuak (adibidez, bihotz-maiztasuna) atzitzeko baimena ematen die aplikazioei."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Gorputz-sentsoreen datuak atzitzeko baimena; esate baterako, bihotz-maiztasuna, tenperatura, odolean dagoen oxigenoaren ehunekoa, etab."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"atzitu atzeko planoan gorputz-sentsoreak (adibidez, bihotz-maiztasunaren neurgailuak)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Atzeko planoan gorputz-sentsoreen datuak atzitzeko baimena; esate baterako, bihotz-maiztasuna, tenperatura, odolean dagoen oxigenoaren ehunekoa, etab."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"irakurri egutegiko gertaerak eta xehetasunak"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Aplikazioak tabletan gordetako egutegiko gertaerak irakur ditzake eta egutegiko datuak parteka eta gorde ditzake."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Aplikazioak Android TV gailuan gordeta dituzun egutegiko gertaerak irakur ditzake, baita egutegiko datuak partekatu eta gorde ere."</string>
@@ -560,7 +562,7 @@
     <string name="permlab_useBiometric" msgid="6314741124749633786">"erabili hardware biometrikoa"</string>
     <string name="permdesc_useBiometric" msgid="7502858732677143410">"Autentifikatzeko hardware biometrikoa erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_manageFingerprint" msgid="7432667156322821178">"kudeatu hatz-marken hardwarea"</string>
-    <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Erreferentzia-gako digitalen txantiloiak gehitzeko eta ezabatzeko metodoei dei egitea baimentzen die aplikazioei."</string>
+    <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Aztarna digitalaren txantiloiak gehitzeko eta ezabatzeko metodoei dei egitea baimentzen die aplikazioei."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"erabili hatz-marken hardwarea"</string>
     <string name="permdesc_useFingerprint" msgid="412463055059323742">"Autentifikatzeko hatz-marken hardwarea erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_audioWrite" msgid="8501705294265669405">"musika-bilduma aldatu"</string>
@@ -617,7 +619,7 @@
     <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Aurrera egiteko, erabili hatz-marka edo pantailaren blokeoa"</string>
   <string-array name="fingerprint_error_vendor">
   </string-array>
-    <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Arazoren bat izan da. Saiatu berriro."</string>
+    <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Arazo bat izan da. Saiatu berriro."</string>
     <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Hatz-markaren ikonoa"</string>
     <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Aurpegi bidez desblokeatzeko eginbidea"</string>
     <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Arazoak ditugu aurpegi bidez desblokeatzeko eginbidearekin"</string>
@@ -671,7 +673,7 @@
     <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Aurrera egiteko, erabili aurpegia edo pantailaren blokeoa"</string>
   <string-array name="face_error_vendor">
   </string-array>
-    <string name="face_error_vendor_unknown" msgid="7387005932083302070">"Arazoren bat izan da. Saiatu berriro."</string>
+    <string name="face_error_vendor_unknown" msgid="7387005932083302070">"Arazo bat izan da. Saiatu berriro."</string>
     <string name="face_icon_content_description" msgid="465030547475916280">"Aurpegiaren ikonoa"</string>
     <string name="permlab_readSyncSettings" msgid="6250532864893156277">"irakurri sinkronizazio-ezarpenak"</string>
     <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Kontu baten sinkronizazio-ezarpenak irakurtzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa konturen batekin sinkronizatuta dagoen zehatz dezake."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ez molestatzeko moduaren konfigurazioa irakurtzeko eta bertan idazteko baimena ematen die aplikazioei."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"hasi ikusteko baimena erabiltzen"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Aplikazioaren baimena erabiltzen hasteko baimena ematen die titularrei. Aplikazio normalek ez lukete beharko."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"hasi baimenen inguruko erabakiak ikusten"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Baimenen inguruko erabakiak berrikusteko pantaila ikusten hasteko baimena ematen die titularrei. Aplikazio normalek ez lukete beharko."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"hasi aplikazioaren eginbideak ikusten"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Aplikazio baten eginbideei buruzko informazioa ikusten hasteko baimena ematen die titularrei."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"atzitu sentsoreen datuen laginak abiadura handian"</string>
@@ -1629,8 +1633,8 @@
     <string name="expires_on" msgid="1623640879705103121">"Iraungitze-data:"</string>
     <string name="serial_number" msgid="3479576915806623429">"Serie-zenbakia:"</string>
     <string name="fingerprints" msgid="148690767172613723">"Erreferentzia-fitxategiak:"</string>
-    <string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256 erreferentzia-gako digitala:"</string>
-    <string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 erreferentzia-gako digitala:"</string>
+    <string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256 aztarna digitala:"</string>
+    <string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 aztarna digitala:"</string>
     <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Ikusi guztiak"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Aukeratu jarduera"</string>
     <string name="share_action_provider_share_with" msgid="1904096863622941880">"Partekatu hauekin:"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b8f2673..c9dedf9 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"‏به برنامه اجازه می‌دهد گزارش‌های تماس در دستگاه Android TV را تغییر دهد، ازجمله داده‌های مربوط به تماس‎های ورودی و خروجی. برنامه‌های مخرب می‌توانند از این مجوز برای پاک کردن یا تغییر دادن گزارش تماس شما استفاده کنند."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"‏به برنامه اجازه می‌دهد گزارشات تماس تلفنی شما، از جمله داده‌هایی درمورد تماس‎های ورودی و خروجی را تغییر دهد. برنامه‌های مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"دسترسی به حسگرهای بدن (مانند پایشگرهای ضربان قلب)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"به برنامه امکان می‌دهد به اطلاعات حسگرهایی که بر شرایط فیزیکی شما مانند ضربان قلبتان، نظارت دارند، دسترسی داشته باشد."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"دسترسی به داده‌های حسگرهای بدن مثل ضربان قلب، دما، درصد اکسیژن خون، و غیره."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"دسترسی به حسگرهای بدن (مثل نمایشگرهای ضربان قلب) هنگام اجرا شدن در پس‌زمینه"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"دسترسی به داده‌های حسگرهای بدن مثل ضربان قلب، دما، درصد اکسیژن خون، و غیره، هنگام اجرا شدن در پس‌زمینه."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"خواندن رویدادها و جزئیات تقویم"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"این برنامه می‌تواند همه رویدادهای تقویم ذخیره‌شده در رایانه لوحی شما را بخواند و داده‌های تقویم شما را به اشتراک بگذارد یا ذخیره کند."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"‏این برنامه می‌تواند همه رویدادهای تقویم را که در Android TV شما ذخیره‌شده بخواند، و داده‌های تقویم شما را هم‌رسانی یا ذخیره کند."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"به برنامه امکان می‌دهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"شروع مشاهده استفاده از مجوز"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"به دارنده اجازه شروع استفاده از مجوز را برای برنامه می‌دهد. هرگز برای برنامه‌های معمول نیاز نیست."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"شروع مشاهده تصمیم‌های مربوط به اجازه‌ها"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"به دارنده امکان می‌دهد صفحه را برای مرور تصمیم‌های مربوط به اجازه‌ها شروع کند. هرگز نباید برای برنامه‌های عادی لازم باشد."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"مشاهده ویژگی‌های برنامه"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"به دارنده اجازه می‌دهد اطلاعات مربوط به ویژگی‌های برنامه را مشاهده کند."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"دسترسی به داده‌های حسگر با نرخ نمونه‌برداری بالا"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"اعلان برنامه سفارشی"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"به<xliff:g id="APP">%1$s</xliff:g> اجازه می‌دهید با <xliff:g id="ACCOUNT">%2$s</xliff:g> (کاربری با این حساب درحال‌حاضر وجود دارد) کاربری جدید ایجاد کند؟"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"به <xliff:g id="APP">%1$s</xliff:g> اجازه می‌دهید با <xliff:g id="ACCOUNT">%2$s</xliff:g> کاربری جدید ایجاد کند؟"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"افزودن کاربر تحت نظارت"</string>
     <string name="language_selection_title" msgid="52674936078683285">"افزودن زبان"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"اولویت‌های منطقه"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"نام زبان را تایپ کنید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 618cd52..b7234be 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Antaa sovelluksen muokata Android TV ‑laitteen puhelulokia, kuten tietoja vastatuista ja soitetuista puheluista. Haitalliset sovellukset voivat tyhjentää puhelulokisi tai muokata sitä."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Antaa sovelluksen muokata puhelimesi puhelulokia, kuten tietoja vastatuista ja soitetuista puheluista. Haitalliset sovellukset voivat poistaa puhelulokisi tai muokata sitä."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"käyttää kehon antureita (kuten sykemittareita)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Antaa sovelluksen käyttää kehosi tilaa seuraavien anturien tietoja, esimerkiksi sykettä."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Pääsy kehon antureiden dataan, esim. sykkeeseen, ruumiinlämpöön ja veren happiprosenttiin."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"Pääsy kehon antureihin (kuten sykemittareihin) taustalla"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Pääsy kehon antureiden dataan (esim. sykkeeseen, ruumiinlämpöön ja veren happiprosenttiin) taustalla."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lue kalenterin tapahtumia ja tietoja"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Tämä sovellus voi lukea kaikkia tabletille tallennettuja kalenteritapahtumia sekä jakaa tai tallentaa kalenteritietoja."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Tämä sovellus voi lukea kaikkia Android TV ‑laitteeseen tallennettuja kalenteritapahtumia sekä jakaa tai tallentaa kalenteritietoja."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Sallii sovelluksen lukea ja muokata Älä häiritse -tilan asetuksia."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"aloita katseluoikeuksien käyttö"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Antaa luvanhaltijan käynnistää sovelluksen käyttöoikeuksien käytön. Ei tavallisten sovelluksien käyttöön."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"aloita lupapäätösten tarkistaminen"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Antaa luvanhaltijan käynnistää näytön lupapäätösten tarkistamiseksi. Ei tavallisten sovellusten käyttöön."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"aloittaa sovellusominaisuuksien katselun"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Antaa luvanhaltijan aloittaa sovelluksen ominaisuustietojen katselun."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"saada pääsyn anturidataan suuremmalla näytteenottotaajuudella"</string>
@@ -1641,7 +1645,7 @@
     <string name="activity_resolver_use_once" msgid="948462794469672658">"Vain kerran"</string>
     <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s ei tue työprofiilia"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Tabletti"</string>
-    <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televisio"</string>
+    <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
     <string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Puhelin"</string>
     <string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Telineen kaiuttimet"</string>
     <string name="default_audio_route_name_hdmi" msgid="5474470558160717850">"HDMI"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Oma sovellusilmoitus"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Saako <xliff:g id="APP">%1$s</xliff:g> luoda uuden käyttäjän (<xliff:g id="ACCOUNT">%2$s</xliff:g>) – tällä käyttäjällä on jo tili?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Saako <xliff:g id="APP">%1$s</xliff:g> luoda uuden käyttäjän (<xliff:g id="ACCOUNT">%2$s</xliff:g>)?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Lisää valvottu käyttäjä"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Lisää kieli"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Alueasetus"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Anna kielen nimi"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 4cc5c14..6a08237 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permet à l\'application de modifier le journal d\'appels de votre appareil Android TV, y compris les données sur les appels entrants et sortants. Des applications malveillantes pourraient utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permet à l\'application de lire le journal d\'appels de votre téléphone, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"accéder aux capteurs corporels (comme les moniteurs de fréquence cardiaque)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permet à l\'application d\'accéder aux données des capteurs qui surveillent votre condition physique, comme votre rythme cardiaque."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Accès aux données des capteurs corporels comme la fréquence cardiaque, la température, le pourcentage d\'oxygène dans le sang, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"Accès capteurs corporels (comme moniteurs fréquence cardiaque) en arrière-plan"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Accès aux données des capteurs corporels comme la fréquence cardiaque, la température, le pourcentage d\'oxygène dans le sang, etc. en arrière-plan."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lire les événements d\'agenda et leurs détails"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Cette application peut lire tous les événements d\'agenda stockés sur votre tablette et partager ou enregistrer les données de votre agenda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Cette application peut lire tous les événements d\'agenda stockés sur votre appareil Android TV et partager ou enregistrer les données de votre agenda."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"démarrer l\'affichage de l\'usage des autorisations"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet au détenteur de démarrer l\'usage des autorisations pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"démarrer les décisions d\'autorisation de lecture"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permet au détenteur de démarrer l\'écran pour revoir les décisions d\'autorisation. Ne devrait pas être requis pour les applications standards."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"démarrer l\'affichage des fonctionnalités de l\'application"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permet au détenteur de commencer à afficher les renseignements sur les fonctionnalités d\'une application."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d’échantillonnage élevé"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notification d\'application personnalisée"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un utilisateur <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Un utilisateur est déjà associé à ce compte)"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil d\'utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Ajouter un utilisateur supervisé"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Ajouter une langue"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Préférences régionales"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Entrez la langue"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 19adf8a..c606ed3 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permet à l\'application de lire le journal d\'appels de votre appareil Android TV, y compris les données sur les appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permet à l\'application de lire le journal d\'appels de votre téléphone, y compris les données relatives aux appels entrants et sortants. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier votre journal d\'appels."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"accéder capteurs corp. (ex : cardiofréquencemètres)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permet à l\'application d\'accéder aux données des capteurs qui contrôlent votre condition physique, comme votre rythme cardiaque."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Accès aux données des capteurs corporels mesurant la fréquence cardiaque, la température, le pourcentage d\'oxygène dans le sang, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"accéder en arrière-plan aux capteurs corporels (par ex., cardiofréquencemètres)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Accès en arrière-plan aux données des capteurs corporels mesurant la fréquence cardiaque, la température, le pourcentage d\'oxygène dans le sang, etc."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lire les événements d\'agenda et les détails associés"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Cette application peut lire tous les événements d\'agenda enregistrés sur votre tablette et partager ou enregistrer vos données d\'agenda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Cette application peut lire tous les événements de l\'agenda enregistrés sur votre appareil Android TV, et partager ou enregistrer les données de votre agenda."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"activer l\'utilisation de l\'autorisation d\'affichage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet à l\'application autorisée d\'activer l\'utilisation de l\'autorisation pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"activer l\'affichage des décisions liées aux autorisations"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permet à l\'appli autorisée de lancer un écran pour examiner les décisions liées aux autorisations. Ne devrait jamais être nécessaire pour les applis standards."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"commencer à voir les fonctionnalités d\'une appli"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permet à l\'appli autorisée de commencer à voir les infos sur les fonctionnalités d\'une appli."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d\'échantillonnage élevé"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notification d\'application personnalisée"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g> (un utilisateur associé à ce compte existe déjà) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Ajouter un utilisateur supervisé"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Ajouter une langue"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Préférences régionales"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Saisissez la langue"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index a61ef81..24287d9 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que a aplicación modifique o rexistro de chamadas do dispositivo Android TV, incluídos os datos acerca de chamadas entrantes e saíntes. As aplicacións maliciosas poden utilizar este permiso para borrar ou modificar o rexistro de chamadas."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite á aplicación modificar o rexistro de chamadas do teléfono, incluídos os datos acerca de chamadas entrantes e saíntes. É posible que aplicacións maliciosas utilicen esta acción para borrar ou modificar o teu rexistro de chamadas."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"acceder a sensores corporais (como monitores de ritmo cardíaco)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite que a aplicación acceda aos datos dos sensores que controlan o teu estado físico, como o ritmo cardíaco."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acceso a datos de sensores corporais, como o ritmo cardíaco, a temperatura, a porcentaxe de osíxeno en sangue etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"acceso en segundo plano a sensores corporais (como monitores de ritmo cardíaco)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acceso en segundo plano a datos de sensores corporais, como o ritmo cardíaco, a temperatura, a porcentaxe de osíxeno en sangue etc."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Ler os detalles e os eventos do calendario"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Esta aplicación pode ler todos os eventos do calendario almacenados na túa tableta e compartir ou gardar os datos do calendario."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Esta aplicación pode ler todos os eventos do calendario almacenados no dispositivo Android TV e compartir ou gardar os datos do calendario."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite á aplicación ler e escribir a configuración do modo Non molestar."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite ao propietario iniciar o uso de permisos dunha aplicación. As aplicacións normais non deberían precisalo nunca."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"iniciar vista das decisións sobre os permisos"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite que a persoa propietaria acceda a unha pantalla onde poderá examinar as decisións relativas aos permisos. Esta opción nunca debería ser necesaria para as aplicacións normais."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"comezar a ver as funcións da aplicación"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o propietario comece a ver a información das funcións dunha aplicación."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder aos datos dos sensores usando unha taxa de mostraxe alta"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 9971033..5ca2c39 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ઍપને ઇનકમિંગ અને આઉટગોઇંગ કૉલ વિશેના ડેટા સહિત, તમારા Android TV ડિવાઇસના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ ઍપ આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત કરવા માટે કરી શકે છે."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા ફોનના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ ઍપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"બૉડીસેન્સર્સ ઍક્સેસ(જેમકે હ્રદય ગતી મૉનિટર)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"એપ્લિકેશનને તમારી હૃદય ગતિ જેવી તમારી શારીરિક સ્થિતિને મૉનિટર કરતાં સેન્સર્સથી ડેટા ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"બૉડી સેન્સરના ડેટાનો ઍક્સેસ જેમ કે હૃદયના ધબકારા, તાપમાન, લોહીમાં ઑક્સિજનની ટકાવારી વગેરે."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"બૉડી સેન્સર (જેમ કે હૃદયના ધબકારાના નિરીક્ષણો) બૅકગ્રાઉન્ડમાં ઍક્સેસ કરો"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"બૅકગ્રાઉન્ડમાં હોય ત્યારે બૉડી સેન્સરના ડેટાનો ઍક્સેસ જેમ કે હૃદયના ધબકારા, તાપમાન, લોહીમાં ઑક્સિજનની ટકાવારી વગેરે."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"કૅલેન્ડર ઇવેન્ટ્સ અને વિગતો વાંચો"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત બધા કૅલેન્ડર ઇવેન્ટને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"એપ્લિકેશનને ખલેલ પાડશો નહીં ગોઠવણી વાંચવા અને લખવાની મંજૂરી આપે છે."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"પરવાનગી વપરાશ જુઓને શરૂ કરો"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"કોઈ ઍપ માટે પરવાનગી વપરાશ શરૂ કરવાની ધારકને મંજૂરી આપે છે. સામાન્ય ઍપ માટે ક્યારેય જરૂર પડી ન શકે."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"પરવાનગી સંબંધિત નિર્ણયો જોવાનું શરૂ કરો"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ધારકને પરવાનગી સંબંધિત નિર્ણયોનો રિવ્યૂ કરવા માટે સ્ક્રીન શરૂ કરવાની મંજૂરી આપે છે. સામાન્ય ઍપ માટે ક્યારેય જરૂરી હોવું જોઈએ નહીં."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ઍપની સુવિધાઓ જોવા માટેની પરવાનગી ચાલુ કરો"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ધારકને ઍપ માટેની સુવિધાઓની માહિતી જોવાનું શરૂ કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ઉચ્ચ સેમ્પ્લિંગ રેટ પર સેન્સરનો ડેટા ઍક્સેસ કરો"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"ઍપનું કસ્ટમ નોટિફિકેશન"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g>ને <xliff:g id="ACCOUNT">%2$s</xliff:g> માટે એક નવા વપરાશકર્તા બનાવવાની મંજૂરી આપીએ (આ એકાઉન્ટ માટે એક વપરાશકર્તા પહેલાંથી અસ્તિત્વમાં છે) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g>ને <xliff:g id="ACCOUNT">%2$s</xliff:g> માટે એક નવા વપરાશકર્તા બનાવવાની મંજૂરી આપીએ ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"નિરીક્ષિત વપરાશકર્તા ઉમેરો"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ભાષા ઉમેરો"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"પ્રદેશ પસંદગી"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"ભાષાનું નામ ટાઇપ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a30c9a9..b846ff8 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -303,21 +303,21 @@
     <string name="user_owner_label" msgid="8628726904184471211">"प्रोफ़ाइल बदलकर निजी प्रोफ़ाइल पर जाएं"</string>
     <string name="managed_profile_label" msgid="7316778766973512382">"प्रोफ़ाइल बदलकर वर्क प्रोफ़ाइल पर जाएं"</string>
     <string name="permgrouplab_contacts" msgid="4254143639307316920">"संपर्क"</string>
-    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"अपने संपर्कों को ऐक्सेस करने की"</string>
+    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"अपने संपर्कों को ऐक्सेस करें"</string>
     <string name="permgrouplab_location" msgid="1858277002233964394">"जगह की जानकारी"</string>
     <string name="permgroupdesc_location" msgid="1995955142118450685">"इस डिवाइस की जगह तक पहुंचने दें"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"कैलेंडर"</string>
-    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"अपने कैलेंडर को ऐक्सेस करने"</string>
+    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"अपने कैलेंडर को ऐक्सेस करें"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"मैसेज (एसएमएस)"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"मैसेज (एसएमएस) भेजें और देखें"</string>
     <string name="permgrouplab_storage" msgid="1938416135375282333">"फ़ाइलें और मीडिया"</string>
-    <string name="permgroupdesc_storage" msgid="6351503740613026600">"अपने डिवाइस पर मौजूद फ़ोटो, मीडिया और फ़ाइलें ऐक्सेस करने की"</string>
+    <string name="permgroupdesc_storage" msgid="6351503740613026600">"अपने डिवाइस पर मौजूद फ़ोटो, मीडिया, और फ़ाइलें ऐक्सेस करें"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"माइक्रोफ़ोन"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ऑडियो रिकॉर्ड करें"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"शारीरिक गतिविधि"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"शारीरिक गतिविधि की जानकारी पा सकता है"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"कैमरा"</string>
-    <string name="permgroupdesc_camera" msgid="7585150538459320326">"चित्र लेने और वीडियो रिकॉर्ड करने"</string>
+    <string name="permgroupdesc_camera" msgid="7585150538459320326">"फ़ोटो खीचें और वीडियो रिकॉर्ड करें"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"आस-पास मौजूद डिवाइस"</string>
     <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"आस-पास मौजूद डिवाइस खोजें और उनसे कनेक्ट करें"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"कॉल लॉग"</string>
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"यह ऐप्लिकेशन को आपके Android TV डिवाइस के कॉल लॉग में बदलाव करने की अनुमति देता है. इसमें किसी को की गई (आउटगोइंग) कॉल और किसी से मिली (इनकमिंग) कॉल शामिल हैं. हालांकि, नुकसान पहुंचाने वाले ऐप्लिकेशन इसका इस्तेमाल करके कॉल लॉग को मिटा सकते हैं या बदल सकते हैं."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ऐप को आने वाला कॉल (इनकमिंग) और किया जाने वाला कॉल (आउटगोइंग) डेटा सहित, आपके फ़ोन के कॉल लॉग को बदलने की अनुमति देता है. धोखा देने वाले ऐप, इसका इस्तेमाल करके आपके कॉल लॉग को मिटा या बदल सकते हैं."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर के लिए बने सेंसर (जैसे हृदय गति मॉनीटर) को ऐक्सेस करें"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"ऐप को आपकी शारीरिक स्‍थिति, जैसे आपकी हृदय गति पर नज़र रखने वाले सेंसर के डेटा तक पहुंचने देती है."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"बॉडी सेंसर से मिले डेटा का ऐक्सेस, जैसे कि धड़कन की दर, तापमान, खून में मौजूद ऑक्सीजन का प्रतिशत वगैरह."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"बैकग्राउंड में काम करने वाले बॉडी सेंसर (जैसे, धड़कन की दर मापने वाले मॉनिटर) से मिले डेटा का ऐक्सेस"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"बैकग्राउंड में काम करने वाले बॉडी सेंसर से मिले डेटा का ऐक्सेस, जैसे कि धड़कन की दर, तापमान, खून में मौजूद ऑक्सीजन का प्रतिशत वगैरह."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"कैलेंडर इवेंट और विवरण पढ़ें"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"यह ऐप्लिकेशन आपके टैबलेट पर संग्रहित सभी कैलेंडर इवेंट पढ़ सकता है और आपका कैलेंडर डेटा शेयर कर सकता है या सहेज सकता है."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"यह ऐप्लिकेशन आपके टीवी पर सेव किए गए सभी कैलेंडर इवेंट को पढ़ सकता है. इसके अलावा यह आपके कैलेंडर का डेटा शेयर कर सकता है या सेव कर सकता है."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ऐप को परेशान न करें कॉन्फ़िगरेशन पढ़ने और लिखने देती है."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"देखने की अनुमतियां चालू करें"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"इस्तेमाल करने वाले को किसी ऐप्लिकेशन के लिए अनुमतियों का इस्तेमाल शुरू करने देता है. सामान्य ऐप्लिकेशन के लिए इसकी ज़रूरत कभी नहीं पड़ती."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"अनुमतियों को देखना चालू करना"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ऐप्लिकेशन को स्क्रीन शुरू करने की अनुमति मिलती है, ताकि अनुमतियों की समीक्षा की जा सके. सामान्य ऐप्लिकेशन के लिए, इसकी ज़रूरत कभी नहीं पड़ती."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ऐप्लिकेशन की सुविधाओं को देखना शुरू करें"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ऐप्लिकेशन को, किसी ऐप्लिकेशन की सुविधाओं की जानकारी देखने की अनुमति देता है."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"सेंसर डेटा को, नमूने लेने की तेज़ दर पर ऐक्सेस करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 6afc049..e88b331 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -430,7 +430,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Aplikaciji omogućuje izmjenu zapisnika poziva vašeg Android TV uređaja, uključujući podatke o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije to mogu upotrijebiti za brisanje ili izmjenu zapisnika poziva."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Aplikaciji omogućuje izmjenu dnevnika poziva vašeg telefona zajedno s podacima o dolaznim i odlaznim pozivima. Zlonamjerne aplikacije to mogu upotrebljavati za brisanje ili izmjenu vašeg dnevnika poziva."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"pristupati biometrijskim senzorima (kao što su monitori otkucaja srca)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Omogućuje aplikaciji pristup podacima sa senzora koji nadziru vaše fizičko stanje, na primjer, broj otkucaja srca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Pristupite podacima biometrijskih senzora kao što su puls, temperatura, postotak kisika u krvi itd."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"pristup biometrijskim senzorima (kao što su monitori pulsa) u pozadini"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Pristupite podacima biometrijskih senzora kao što su puls, temperatura, postotak kisika u krvi itd. u pozadini."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Čitanje događaja i pojedinosti kalendara"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Aplikacija može čitati sve kalendarske događaje pohranjene na tabletu i dijeliti ili spremati podatke iz vašeg kalendara."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Aplikacija može čitati sve kalendarske događaje pohranjene na Android TV uređaju i dijeliti ili spremati podatke iz vašeg kalendara."</string>
@@ -734,6 +736,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućuje aplikaciji čitanje i pisanje konfiguracije opcije Ne uznemiravaj."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti upotrebu dopuštenja za pregled"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dopušta nositelju pokretanje upotrebe dopuštenja za aplikaciju. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"pokrenuti odluke o dopuštenju za pregled"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Dopušta nositelju pokretanje zaslona za pregled odluka o dopuštenjima. Ne bi trebalo biti potrebno za uobičajene aplikacije."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pokretanje prikaza značajki aplikacije"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Dopušta nositelju pokretanje prikaza informacija o značajkama aplikacije."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri višoj brzini uzorkovanja"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index c736246..46fcc2a 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Lehetővé teszi, hogy az alkalmazás módosítsa az Android TV eszköz híváslistáját, beleértve a bejövő és kimenő hívások adatait is. A rosszindulatú alkalmazások ezt felhasználhatják a híváslista törlésére vagy módosítására."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Lehetővé teszi, hogy az alkalmazás módosítsa a telefon híváslistáját, beleértve a bejövő és kimenő hívások adatait is. A rosszindulatú alkalmazások ezt arra használhatják, hogy híváslistáját töröljék vagy módosítsák."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"hozzáférés a testérzékelőkhöz (például pulzusmérők)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Engedélyezi az alkalmazásnak, hogy hozzáférjen az Ön fizikai állapotát – például a pulzusszámát – figyelő érzékelők adataihoz."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Hozzáférhet olyan, testérzékelőktől származó adatokhoz, mint a pulzusszám, a testhőmérséklet, a véroxigénszint stb."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"hozzáférés a háttérben futó testérzékelőkhöz (például pulzusmérők)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Hozzáférhet olyan, a háttérben futó testérzékelőktől származó adatokhoz, mint a pulzusszám, a testhőmérséklet, a véroxigénszint stb."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Naptáresemények és a naptári adatok olvasása"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Az alkalmazás olvashatja a táblagépen tárolt összes naptáreseményt, és megoszthatja vagy mentheti a naptáradatokat."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Az alkalmazás olvashatja az Android TV eszközön tárolt összes naptáreseményt, és megoszthatja vagy mentheti a naptáradatokat."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Az alkalmazás olvashatja és szerkesztheti a „Ne zavarjanak” funkció beállításait."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"engedélyhasználat megtekintésének elindítása"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lehetővé teszi a felhasználó számára, hogy elindítsa az alkalmazás engedélyhasználatát. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"engedélyezési döntések megtekintésének elindítása"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Lehetővé teszi az alkalmazás számára, hogy elindítsa a képernyőt az engedélyezési döntések felülvizsgálata érdekében. A normál alkalmazások esetében erre nincs szükség."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"alkalmazásfunkciók megtekintésének megkezdése"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Engedélyezi az alkalmazás számára, hogy megkezdje az alkalmazások funkcióira vonatkozó adatok megtekintését."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"hozzáférés a szenzoradatokhoz nagy mintavételezési gyakorisággal"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4f8f8e8..b60f08b 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Թույլ է տալիս հավելվածին փոփոխել Android TV սարքի զանգերի մատյանը, այդ թվում՝ մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել՝ ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Թույլ է տալիս հավելվածին փոփոխել ձեր հեռախոսի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ՝ սրտի կծկումների հաճախականության չափիչ)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Հավելվածին թույլ է տալիս մուտք ունենալ սենսորների տվյալներին, որոնք վերահսկում են ձեր ֆիզիկական վիճակը, օրինակ՝ ձեր սրտի զարկերը:"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Մարմնի սենսորների տվյալների (օրինակ՝ սրտի զարկերի հաճախականության, ջերմաստիճանի, արյան մեջ թթվածնի տոկոսային պարունակության ցուցանիշների) օգտագործման թույլտվություն"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ֆոնային ռեժիմում մարմնի սենսորների տվյալների օգտագործման թույլտվություն"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Ֆոնային ռեժիմում մարմնի սենսորների տվյալների (օրինակ՝ սրտի զարկերի հաճախականության, ջերմաստիճանի, արյան մեջ թթվածնի տոկոսային պարունակության ցուցանիշների) օգտագործման թույլտվություն"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Կարդալ օրացույցի միջոցառումները և տվյալները"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Այս հավելվածը կարող է կարդալ օրացույցի՝ ձեր պլանշետում պահված բոլոր միջոցառումները, ինչպես նաև հրապարակել կամ պահել ձեր օրացույցի տվյալները:"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Այս հավելվածը կարող է կարդալ օրացույցի՝ ձեր Android TV սարքում պահված բոլոր միջոցառումները, ինչպես նաև հրապարակել կամ պահել ձեր օրացույցի տվյալները:"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Թույլ է տալիս հավելվածին փոփոխել «Չանհանգստացնել» գործառույթի կազմաձևումը:"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"թույլտվությունների մասին տվյալների հասանելիություն"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Հավելվածին հասանելի կդառնան թույլտվությունների մասին տվյալները։ Այս թույլտվությունն անհրաժեշտ չէ սովորական հավելվածներին։"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"սկսել թույլտվությունների հետ գործողությունների դիտումը"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Թույլ է տալիս դիտել թույլտվությունների հետ գործողությունները։ Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի։"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"հավելվածի գործառույթների դիտում"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Թույլ է տալիս դիտել հավելվածի գործառույթների մասին տեղեկությունները։"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"օգտագործել սենսորների տվյալները բարձր հաճախականության վրա"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1457450..7925b3f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -226,9 +226,9 @@
     <string name="reboot_to_update_title" msgid="2125818841916373708">"Pemutakhiran sistem Android"</string>
     <string name="reboot_to_update_prepare" msgid="6978842143587422365">"Bersiap untuk memperbarui..."</string>
     <string name="reboot_to_update_package" msgid="4644104795527534811">"Memproses pembaruan paket…"</string>
-    <string name="reboot_to_update_reboot" msgid="4474726009984452312">"Memulai kembali…"</string>
+    <string name="reboot_to_update_reboot" msgid="4474726009984452312">"Memulai ulang…"</string>
     <string name="reboot_to_reset_title" msgid="2226229680017882787">"Kembalikan ke setelan pabrik"</string>
-    <string name="reboot_to_reset_message" msgid="3347690497972074356">"Memulai kembali…"</string>
+    <string name="reboot_to_reset_message" msgid="3347690497972074356">"Memulai ulang…"</string>
     <string name="shutdown_progress" msgid="5017145516412657345">"Sedang mematikan..."</string>
     <string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Tablet Anda akan dimatikan."</string>
     <string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"Perangkat Android TV akan dimatikan."</string>
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Mengizinkan aplikasi mengubah log panggilan perangkat Android TV, termasuk data tentang panggilan masuk dan keluar. Aplikasi berbahaya dapat menggunakan izin ini untuk menghapus atau mengubah log panggilan Anda."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Memungkinkan apl memodifikasi log panggilan ponsel Anda, termasuk data tentang panggilan masuk dan keluar. Apl berbahaya dapat menggunakan ini untuk menghapus atau memodifikasi log panggilan Anda."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"akses sensor tubuh (misalnya, monitor detak jantung)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Mengizinkan aplikasi untuk mengakses data dari sensor yang memantau kondisi fisik Anda, seperti denyut jantung."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Akses ke data dari sensor tubuh seperti detak jantung, suhu, persentase oksigen dalam darah, dan lainnya."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"akses sensor tubuh (misalnya, monitor detak jantung) saat di latar belakang"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Akses ke data dari sensor tubuh seperti detak jantung, suhu, persentase oksigen dalam darah, dan lainnya saat di latar belakang."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Baca acara kalender dan detailnya"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Aplikasi ini dapat membaca semua acara kalender yang tersimpan di tablet dan membagikan atau menyimpan data kalender."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Aplikasi ini dapat membaca semua acara kalender yang tersimpan di perangkat Android TV dan membagikan atau menyimpan data kalender."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Mengizinkan aplikasi membaca dan menulis konfigurasi status Jangan Ganggu."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulai melihat penggunaan izin"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Memungkinkan pemegang memulai penggunaan izin untuk aplikasi. Tidak diperlukan untuk aplikasi normal."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"mulai melihat keputusan izin"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Mengizinkan pemegang memulai layar untuk meninjau keputusan izin. Tidak pernah dibutuhkan untuk aplikasi normal."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"mulai lihat fitur aplikasi"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Memungkinkan pemegang mulai melihat info fitur untuk aplikasi."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mengakses data sensor pada frekuensi pengambilan sampel yang tinggi"</string>
@@ -1366,7 +1370,7 @@
     <string name="sim_done_button" msgid="6464250841528410598">"Selesai"</string>
     <string name="sim_added_title" msgid="7930779986759414595">"Kartu SIM ditambahkan"</string>
     <string name="sim_added_message" msgid="6602906609509958680">"Mulai ulang perangkat Anda untuk mengakses jaringan selular."</string>
-    <string name="sim_restart_button" msgid="8481803851341190038">"Nyalakan Ulang"</string>
+    <string name="sim_restart_button" msgid="8481803851341190038">"Mulai Ulang"</string>
     <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Aktifkan layanan seluler"</string>
     <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Download aplikasi operator untuk mengaktifkan SIM baru"</string>
     <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Download aplikasi <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengaktifkan SIM baru"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notifikasi aplikasi kustom"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Izinkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baru dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> (Pengguna dengan akun ini sudah ada) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Izinkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baru dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Tambahkan pengguna yang diawasi"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Tambahkan bahasa"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferensi wilayah"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Ketik nama bahasa"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index cd28dc9..c758de5 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Leyfir forritinu að breyta símtalaskrá Android TV tækisins, þ. á m. gögnum um hringd og móttekin símtöl. Spilliforrit geta notað þetta til að eyða eða breyta símtalaskránni."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Leyfir forriti að breyta símtalaskrá símans, þ. á m. gögnum um hringd og móttekin símtöl. Spilliforrit geta notað þetta til að eyða eða breyta símtalaskránni."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"fá aðgang að líkamsskynjurum (s.s. hjartsláttarmælum)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Veitir forritinu aðgang að gögnum frá skynjurum sem fylgjast með líkamsstarfsemi þinni, svo sem hjartslætti."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Aðgangur að gögnum frá líkamsskynjurum, svo sem um hjartslátt, hitastig, súrefnismettun í blóði o.s.frv."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"aðgangur að líkamsskynjurum (svo sem púlsmælum) í bakgrunni"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Aðgangur að gögnum frá líkamsskynjurum, svo sem um hjartslátt, hitastig, súrefnismettun í blóði o.s.frv. í bakgrunni."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lesa dagatalsviðburði og upplýsingar"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Þetta forrit getur lesið alla dagatalsviðburði sem eru vistaðir í spjaldtölvunni og deilt eða vistað dagatalsgögnin þín."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Þetta forrit getur lesið alla dagatalsviðburði sem eru vistaðir í Android TV og deilt eða vistað dagatalsgögnin þín."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leyfir forriti að lesa og skrifa í grunnstillingu „Ónáðið ekki“."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"heimildanotkun upphafsyfirlits"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leyfir handhafa að byrja heimildanotkun fyrir forrit. Ætti aldrei að þurfa fyrir venjuleg forrit."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"opna ákvarðanir um skoðunarheimildir"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Gerir notandanum kleift að opna skjá til að fara yfir ákvarðanir um heimildir. Þetta ætti aldrei að þurfa fyrir venjuleg forrit."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"byrja að skoða eiginleika forrits"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Leyfir handhafa að skoða upplýsingar um eiginleika tiltekins forrits."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aðgangur að skynjaragögnum með hárri upptökutíðni"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Sérsniðin forritatilkynning"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Viltu leyfa <xliff:g id="APP">%1$s</xliff:g> að stofna nýjan notanda með <xliff:g id="ACCOUNT">%2$s</xliff:g> (notandi með þennan reikning er þegar fyrir hendi)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Leyfa <xliff:g id="APP">%1$s</xliff:g> að stofna nýjan notanda með <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Bæta við stýrðum notanda"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Bæta við tungumáli"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Svæðisval"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Sláðu inn heiti tungumáls"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6571f23..c5683fa 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -303,29 +303,29 @@
     <string name="user_owner_label" msgid="8628726904184471211">"Passa al profilo personale"</string>
     <string name="managed_profile_label" msgid="7316778766973512382">"Passa a profilo di lavoro"</string>
     <string name="permgrouplab_contacts" msgid="4254143639307316920">"Contatti"</string>
-    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"accedere ai contatti"</string>
+    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"Possono accedere ai contatti"</string>
     <string name="permgrouplab_location" msgid="1858277002233964394">"Posizione"</string>
-    <string name="permgroupdesc_location" msgid="1995955142118450685">"accedere alla posizione di questo dispositivo"</string>
+    <string name="permgroupdesc_location" msgid="1995955142118450685">"Possono accedere alla posizione di questo dispositivo"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendario"</string>
-    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"accedere al calendario"</string>
+    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"Possono accedere al calendario"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="5726462398070064542">"inviare e visualizzare SMS"</string>
-    <string name="permgrouplab_storage" msgid="1938416135375282333">"File e contenuti multimediali"</string>
-    <string name="permgroupdesc_storage" msgid="6351503740613026600">"accedere a foto, contenuti multimediali e file sul dispositivo"</string>
+    <string name="permgroupdesc_sms" msgid="5726462398070064542">"Possono inviare e visualizzare SMS"</string>
+    <string name="permgrouplab_storage" msgid="1938416135375282333">"File/contenuti multimediali"</string>
+    <string name="permgroupdesc_storage" msgid="6351503740613026600">"Possono accedere a foto, contenuti multimediali e file sul dispositivo"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"Microfono"</string>
-    <string name="permgroupdesc_microphone" msgid="1047786732792487722">"registrare audio"</string>
+    <string name="permgroupdesc_microphone" msgid="1047786732792487722">"Possono registrare audio"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Attività fisica"</string>
-    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"Consente di accedere all\'attività fisica"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"Possono accedere all\'attività fisica"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"Fotocamera"</string>
-    <string name="permgroupdesc_camera" msgid="7585150538459320326">"scattare foto e registrare video"</string>
+    <string name="permgroupdesc_camera" msgid="7585150538459320326">"Possono scattare foto e registrare video"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Dispositivi nelle vicinanze"</string>
     <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"Consente di rilevare dispositivi nelle vicinanze e di connettersi a tali dispositivi"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"Registri chiamate"</string>
-    <string name="permgroupdesc_calllog" msgid="2026996642917801803">"leggere e modificare il registro chiamate del telefono"</string>
+    <string name="permgroupdesc_calllog" msgid="2026996642917801803">"Possono leggere e modificare il registro chiamate del telefono"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"Telefono"</string>
-    <string name="permgroupdesc_phone" msgid="270048070781478204">"eseguire e gestire le telefonate"</string>
+    <string name="permgroupdesc_phone" msgid="270048070781478204">"Possono eseguire e gestire le telefonate"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensori del corpo"</string>
-    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
+    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"Possono accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
     <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifiche"</string>
     <string name="permgroupdesc_notifications" msgid="4608679556801506580">"Visualizzazione di notifiche"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperare contenuti della finestra"</string>
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Consente all\'app di modificare il registro chiamate del dispositivo Android TV, inclusi i dati relativi alle chiamate in arrivo e in uscita. Le app dannose potrebbero farne uso per cancellare o modificare il registro chiamate."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Consente all\'applicazione di modificare il registro chiamate del telefono, inclusi i dati sulle chiamate in arrivo e in uscita. Le applicazioni dannose potrebbero farne uso per cancellare o modificare il registro chiamate."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"accesso ai sensori (come il cardiofrequenzimetro)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Consente all\'app di accedere ai dati relativi ai sensori che monitorano le tue condizioni fisiche, ad esempio la frequenza cardiaca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Accedere ai dati dei sensori del corpo, ad esempio a frequenza cardiaca, temperatura, percentuale di ossigeno nel sangue e così via."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"Accesso ai sensori del corpo (come i cardiofrequenzimetri) in background"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Accedere ai dati dei sensori del corpo, ad esempio a frequenza cardiaca, temperatura, percentuale di ossigeno nel sangue e così via in background."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"lettura di eventi di calendario e dettagli"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Questa app può leggere tutti gli eventi di calendario memorizzati sul tablet e condividere o salvare i dati di calendario."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Questa app può leggere tutti gli eventi di calendario memorizzati sul dispositivo Android TV e condividere o salvare i dati di calendario."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Consente all\'app di leggere e modificare la configurazione della funzione Non disturbare."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"avvio dell\'uso dell\'autorizzazione di visualizzazione"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Consente al titolare di avviare l\'uso delle autorizzazioni per un\'app. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"Inizio della visualizzazione delle decisioni relative all\'autorizzazione"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Consente all\'app che ha questa autorizzazione di avviare lo schermo per esaminare le decisioni relative all\'autorizzazione. Non dovrebbe mai essere necessaria per le normali app."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"Inizio della visualizzazione di funzionalità delle app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Consente all\'app che ha questa autorizzazione di iniziare a visualizzare le informazioni relative alle funzionalità di un\'app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Accesso ai dati dei sensori a una frequenza di campionamento elevata"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 4214bc2..9da03ea1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"‏מאפשרת לאפליקציה לשנות את יומן השיחות של מכשיר ה-Android TV, כולל נתונים על שיחות נכנסות ויוצאות. אפליקציות זדוניות עלולות להשתמש בכך כדי למחוק או לשנות את יומן השיחות."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"מאפשרת לאפליקציה לשנות את יומן השיחות של הטלפון, כולל נתונים על שיחות נכנסות ויוצאות. אפליקציות זדוניות עלולות לעשות בכך שימוש כדי למחוק או לשנות את יומן השיחות שלך."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"גישה אל חיישני גוף (כמו מוניטורים לקצב לב)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"מאפשרת לאפליקציה לגשת אל נתוני חיישנים העוקבים אחר מצבך הגופני, כמו קצב הלב."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"גישה לנתונים מחיישנים גופניים כמו דופק, חום גוף, שיעור החמצן בדם ועוד."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"גישה לחיישנים גופניים (כמו מוניטורים למדידת דופק) תוך כדי פעילות ברקע"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"גישה לנתונים מחיישנים גופניים, כמו דופק, חום גוף, שיעור החמצן בדם, תוך כדי פעילות ברקע."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"קריאה של אירועי יומן והפרטים שלהם"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"האפליקציה הזו יכולה לקרוא את כל אירועי היומן המאוחסנים בטאבלט, ולשתף או לשמור את נתוני היומן."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"‏האפליקציה הזו יכולה לקרוא את כל אירועי היומן המאוחסנים במכשיר ה-Android TV, ולשתף או לשמור את נתוני היומן."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"מאפשרת לאפליקציה לקרוא ולכתוב את התצורה של התכונה \'נא לא להפריע\'."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"התחלת צפייה בהרשאות השימוש"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"מאפשרת לבעלים להפעיל את השימוש בהרשאות עבור אפליקציה מסוימת. הרשאה זו אף פעם לא נדרשת עבור אפליקציות רגילות."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ההחלטות לגבי ההרשאות להפעלת התצוגה"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"בעלי ההרשאה יוכלו להפעיל את המסך כדי לעיין בהחלטות לגבי הרשאות. ההרשאה לא נחוצה לאפליקציות רגילות בדרך כלל."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"התחלת צפייה בהרשאות של אפליקציות"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"בעלי ההרשאה יוכלו להתחיל לצפות בפרטי התכונות של אפליקציות."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"גישה לנתוני חיישנים בתדירות דגימה גבוהה"</string>
@@ -1348,7 +1352,7 @@
     <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"הוגדר רינגטון שקט"</string>
     <string name="volume_call" msgid="7625321655265747433">"עוצמת קול בשיחה"</string>
     <string name="volume_bluetooth_call" msgid="2930204618610115061">"‏עוצמת הקול בשיחה ב-Bluetooth"</string>
-    <string name="volume_alarm" msgid="4486241060751798448">"עוצמת קול של התראה"</string>
+    <string name="volume_alarm" msgid="4486241060751798448">"עוצמת הקול של השעון המעורר"</string>
     <string name="volume_notification" msgid="6864412249031660057">"עוצמת הקול של ההתראות"</string>
     <string name="volume_unknown" msgid="4041914008166576293">"עוצמת קול"</string>
     <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"‏עוצמת הקול של Bluetooth"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"התראות אפליקציה בהתאמה אישית"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"האם לאפשר לאפליקציה <xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש באמצעות <xliff:g id="ACCOUNT">%2$s</xliff:g> (כבר קיים משתמש לחשבון הזה)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"לאפשר לאפליקציה <xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש באמצעות <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"הוספת משתמש בפיקוח"</string>
     <string name="language_selection_title" msgid="52674936078683285">"הוספת שפה"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"העדפת אזור"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"יש להקליד את שם השפה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 7a6b566..76d248c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Android TV デバイスの通話履歴(着信や発信のデータなど)の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、通話履歴が消去または変更される恐れがあります。"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"モバイル デバイスの通話履歴(着信や発信のデータなど)の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、通話履歴が消去または変更される恐れがあります。"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ボディーセンサー(心拍数モニターなど)へのアクセス"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"心拍数など、身体状態を監視するセンサーからのデータにアクセスすることをアプリに許可します。"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"心拍数、体温、血液中の酸素の割合などのボディセンサー データにアクセスする権限です。"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ボディセンサー(心拍数モニター)のデータへのバックグラウンドでのアクセス権限"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"心拍数、体温、血液中の酸素の割合などのボディセンサー データにバックグラウンドでアクセスする権限です。"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"カレンダーの予定と詳細を読み取り"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"このアプリは、お使いのタブレットに保存されたカレンダーの予定をすべて読み取り、カレンダーのデータを共有したり、保存したりできます。"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"このアプリは、Android TV デバイスに保存されているカレンダーの予定をすべて読み取り、カレンダーのデータを共有したり、保存したりできます。"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"サイレント モード設定の読み取りと書き込みをアプリに許可します。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"表示権限の使用の開始"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"アプリの権限使用の開始を所有者に許可します。通常のアプリでは不要です。"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"閲覧権限の許可 / 拒否の開始"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"権限の許可 / 拒否を確認するための画面の開始を所有者に許可します。通常のアプリでは不要です。"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"アプリ機能の表示の開始"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"アプリの機能情報の表示の開始を所有者に許可します。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"高サンプリング レートでセンサーデータにアクセスする"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6ab9312..91144c6 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ნებას რთავს აპს, შეცვალოს თქვენს Android TV მოწყობილობაზე ზარების ჟურნალი, შემომავალი და გამავალი ზარების მონაცემთა ჩათვლით. მავნე აპებს შეუძლიათ, ამის მეშვეობით, ამოშალონ ან შეცვალონ თქვენი ზარების ჟურნალი."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"აპს შეეძლება, შეცვალოს თქვენი ტელეფონის ზარების ჟურნალი, მათ შორის შემომავალი და გამავალი ზარების მონაცემები. მავნე აპებმა შეიძლება გამოიყენონ ეს თქვენი ზარების ჟურნალის წასაშლელად ან შესაცვლელად."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"სხეულის სენსორებზე წვდომა (მაგ., გულისცემის მონიტორები)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"აპისთვის ნების დართვა, რათა მას ჰქონდეს წვდომა თქვენი ფიზიკური მდგომარეობის მონიტორინგის სენსორების მონაცემებზე."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"წვდომა სხეულის ისეთ სენსორებზე, როგორიცაა გულისცემის სიხშირე, ტემპერატურა, სისხლში ჟანგბადის პროცენტული შემცველობა და ა.შ."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"სხეულის სენსორებზე წვდომა (მაგ., გულისცემის სიხშ. მონ.) ფონურ რეჟიმში მუშაობისას"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"წვდომა სხეულის ისეთ სენსორებზე, როგორიცაა გულისცემის სიხშირე, ტემპერატურა, სისხლში ჟანგბადის პროცენტული შემცველობა და ა.შ. ფონურ რეჟიმში მუშაობისას."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"კალენდრის მოვლენებისა და დეტალების წაკითხვა"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ამ აპს შეუძლია თქვენს ტაბლეტში შენახული კალენდრის ყველა მოვლენის წაკითხვა და თქვენი კალენდრის მონაცემების გაზიარება ან შენახვა."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ამ აპს შეუძლია თქვენს Android TV მოწყობილობაზე შენახული კალენდრის ყველა მოვლენის წაკითხვა და თქვენი კალენდრის მონაცემების გაზიარება ან შენახვა."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"საშუალებას აძლევს აპს, წაიკითხოს და დაწეროს კონფიგურაცია „არ შემაწუხოთ“."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ნახვის ნებართვის გამოყენების დაწყება"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"მფლობელს საშუალებას აძლევს, დაიწყოს აპის ნებართვის გამოყენება. ჩვეულებრივი აპებისთვის არასოდეს უნდა იყოს საჭირო."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ნებართვის შესახებ გადაწყვეტილებების ნახვის დაწყება"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"მფლობელს საშუალებას აძლევს, გაუშვას ეკრანი ნებართვის შესახებ გადაწყვეტილებების სანახავად. ჩვეულებრივი აპებისთვის არასოდეს უნდა იყოს საჭირო."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"აპის ფუნქციების ნახვის დაწყება"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"მფლობელს საშუალებას აძლევს, დაიწყოს აპის ფუნქციების ინფორმაციის ნახვა."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"სენსორის მონაცემებზე წვდომა სემპლინგის მაღალი სიხშირით"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index b231be1..3f754959 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Қолданба Android TV құрылғысының қоңыраулар журналын, сонымен қатар кіріс және шығыс қоңыраулар туралы деректерді өзгерте алатын болады. Қоңыраулар журналын деректерден тазарту немесе оны өзгерту үшін зиянды қолданбалар осы рұқсатты пайдалануы мүмкін."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Қолданбаға сіздің телефоныңыздың қоңырау тіркеуін, келетін немесе шығатын қоңыраулар туралы деректерді қоса, өзгерту мүмкіндігін береді. Залалды қолданбалар бұны сіздің қоңырау тіркеуіңізді өшіру үшін қолдануы мүмкін."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"дене датчиктеріне (мысалы, жүрек соғу жиілігінің мониторларына) қатынасу"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Қолданбаға жүрек соғу жиілігіңіз сияқты дене күйіңізді бақылайтын сенсорлардың деректеріне қатынасуға рұқсат етеді."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Жүрек қағысы, температура, қандағы оттегі пайызы және т.б. сияқты дене датчиктерінен алынған деректерді пайдалану."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"дене датчиктерін (мысалы, жүрек қағысын өлшегіштерді) фондық режимде пайдалану"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Жүрек қағысы, температура, қандағы оттегі пайызы және т.б. сияқты дене датчиктерінен алынған деректерді фондық режимде пайдалану."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Күнтізбе оқиғалары мен мәліметтерін оқу"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Бұл қолданба планшетте сақталған барлық күнтізбе оқиғаларын оқи алады және күнтізбе деректерін бөлісе не сақтай алады."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Бұл қолданба Android TV құрылғыңызда сақталған барлық күнтізбе оқиғаларын оқи алады және күнтізбе деректерін бөлісе не сақтай алады."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Қолданбаға «Мазаламау» конфигурациясын оқу және жазу мүмкіндігін береді."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"рұқсаттарды пайдалану туралы деректерді көру"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Пайдаланушы қолданбаға берілетін рұқсаттарды басқара алады. Ондай рұқсаттар әдеттегі қолданбаларға керек емес."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"Рұқсаттары бар әрекеттерді көру"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Қолданбаға рұқсаттары бар әрекеттерді көруге мүмкіндік береді. Әдеттегі қолданбаларға қажет емес."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"қолданба функцияларын көре бастау"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Қолданбаға функциялар туралы ақпаратты көре бастауды кідіртуге мүмкіндік береді."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"жоғары дискретизация жиілігіндегі датчик деректерін пайдалану"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Арнаулы хабар хабарландыруы"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунты бар жаңа пайдаланушы (мұндай аккаунтқа ие пайдаланушы бұрыннан бар) жасауға рұқсат етілсін бе?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунты бар жаңа пайдаланушы жасауға рұқсат етілсін бе?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Бақыланатын пайдаланушыны қосу"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Тіл қосу"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Аймақ параметрі"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Тіл атауын теріңіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 6d72625..ecae41a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"អនុញ្ញាតឱ្យ​កម្មវិធី​កែកំណត់ហេតុហៅ​ទូរសព្ទនៃឧបករណ៍ Android TV របស់អ្នក រួមទាំងទិន្នន័យអំពី​ការហៅចូល និងការហៅចេញផងដែរ។ កម្មវិធី​គ្រោះថ្នាក់​អាចប្រើការអនុញ្ញាត​នេះ ដើម្បី​លុប ឬកែកំណត់ហេតុ​ហៅទូរសព្ទរបស់អ្នក។"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ឲ្យ​កម្មវិធី​កែ​បញ្ជី​ហៅ​នៃ​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប ឬ​កែ​បញ្ជី​ការ​ហៅ​របស់​អ្នក។"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ចូលដំណើរការឧបករណ៍ចាប់សញ្ញារាងកាយ (ដូចជាម៉ាស៊ីនវាស់ចង្វាក់បេះដូង)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"ឲ្យ​កម្មវិធី​ចូល​ដំណើរការ​ទិន្នន័យ​ពី​ឧបករណ៍​ចាប់​សញ្ញា​ដែល​តាមដាន​លក្ខខណ្ឌ​សុខភាព​របស់​អ្នក ដូច​ជា​ចង្វាក់​បេះដូង។"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"សិទ្ធិចូលប្រើទិន្នន័យ​ពីឧបករណ៍​ចាប់សញ្ញារាងកាយដូចជា ចង្វាក់បេះដូង សីតុណ្ហភាព ភាគរយនៃ​អុកស៊ីសែន​ក្នុងឈាម។ល។"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ចូលប្រើឧបករណ៍ចាប់សញ្ញារាងកាយ (ដូចជាឧបករណ៍វាស់ចង្វាក់បេះដូង) ខណៈពេលនៅផ្ទៃខាងក្រោយ"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"សិទ្ធិចូលប្រើទិន្នន័យ​ពីឧបករណ៍​ចាប់សញ្ញារាងកាយដូចជា ចង្វាក់បេះដូង សីតុណ្ហភាព ភាគរយនៃ​អុកស៊ីសែន​ក្នុងឈាម។ល។ ខណៈពេល​នៅផ្ទៃខាងក្រោយ។"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"អាន​ព្រឹត្តិការណ៍​ប្រតិទិន​ និង​ព័ត៌មាន​លម្អិត"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"កម្មវិធី​នេះ​អាច​ធ្វើការ​អាន​ព្រឹត្តិការណ៍​ប្រតិទិន​ទាំងអស់​ ដែល​ផ្ទុក​នៅលើ​ថេប្លេត​របស់​អ្នក​ និង​ចែករំលែក​ ឬ​រក្សាទុក​ទិន្នន័យ​ប្រតិទិន​របស់​អ្នក​។"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"កម្មវិធីនេះ​អាចអានព្រឹត្តិការណ៍​ក្នុងប្រតិទិន​ទាំងអស់​ដែលបានរក្សាទុក​នៅក្នុងឧបករណ៍ Android TV របស់អ្នក និង​ចែករំលែក ឬរក្សាទុក​ទិន្នន័យ​ប្រតិទិន​របស់អ្នក។"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"អនុញ្ញាតឲ្យកម្មវិធីអាន និងសរសេរការកំណត់រចនាសម្ព័ន្ធមុខងារ កុំរំខាន។"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ចាប់ផ្ដើម​មើល​ការប្រើប្រាស់​ការអនុញ្ញាត"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"អនុញ្ញាត​ឱ្យម្ចាស់​ចាប់ផ្ដើម​ការប្រើប្រាស់​ការអនុញ្ញាត​សម្រាប់កម្មវិធី។ មិនគួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ចាប់ផ្ដើមមើលការសម្រេចលើការអនុញ្ញាត"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"អនុញ្ញាតឱ្យកម្មវិធីចាប់ផ្ដើមត្រួតពិនិត្យ ដើម្បីពិនិត្យមើលការសម្រេចលើការអនុញ្ញាត។ មិនចាំបាច់ទេសម្រាប់កម្មវិធីធម្មតា។"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ចាប់ផ្ដើមមើល​មុខងារកម្មវិធី"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"អនុញ្ញាតឱ្យកម្មវិធី​ចាប់ផ្ដើម​មើលព័ត៌មានមុខងារ​សម្រាប់កម្មវិធី។"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ចូលប្រើទិន្នន័យ​ឧបករណ៍ចាប់សញ្ញា​នៅអត្រាសំណាកខ្ពស់"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 5ec2013..a24166c 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ಒಳಬರುವ ಮತ್ತು ಹೊರಹೋಗುವ ಕರೆಗಳ ಕುರಿತ ಡೇಟಾ ಸೇರಿದಂತೆ ನಿಮ್ಮ Android TV ಸಾಧನದ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್‍‍ಗಳು ನಿಮ್ಮ ಕರೆಯ ಲಾಗ್‍ ಅನ್ನು ಅಳಿಸಲು ಅಥವಾ ಮಾರ್ಪಡಿಸಲು ಇದನ್ನು ಬಳಸಿಕೊಳ್ಳಬಹುದು."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ಒಳಬರುವ ಮತ್ತು ಹೊರಹೋಗುವ ಕರೆಗಳ ಕುರಿತ ಡೇಟಾ ಸೇರಿದಂತೆ, ನಿಮ್ಮ ಫೋನ್‍‍ನ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್‍‍ಗಳು ನಿಮ್ಮ ಕರೆಯ ಲಾಗ್‍ ಅನ್ನು ಅಳಿಸಲು ಅಥವಾ ಮಾರ್ಪಡಿಸಲು ಇದನ್ನು ಬಳಸಿಕೊಳ್ಳಬಹುದು."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ದೇಹ ಸೆನ್ಸರ್‌ಗಳನ್ನು ಪ್ರವೇಶಿಸಿ (ಹೃದಯದ ಬಡಿತ ಮಾನಿಟರ್‌ಗಳಂತಹ)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"ನಿಮ್ಮ ಹೃದಯ ಬಡಿತದಂತಹ ನಿಮ್ಮ ದೈಹಿಕ ಸ್ಥಿತಿಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುವ ಸೆನ್ಸರ್‌‌ಗಳಿಂದ ಡೇಟಾ ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"ಹೃದಯ ಬಡಿತ, ತಾಪಮಾನ, ರಕ್ತದ ಆಮ್ಲಜನಕದ ಶೇಕಡಾವಾರು ಮುಂತಾದ ದೇಹದ ಸೆನ್ಸರ್‌ಗಳಿಂದ ಡೇಟಾಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡುತ್ತದೆ."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ಹಿನ್ನೆಲೆಯಲ್ಲಿರುವ ದೇಹದ ಸೆನ್ಸರ್‌ಗಳಿಗೆ (ಹೃದಯದ ಬಡಿತ ಮಾನಿಟರ್‌ಗಳು) ಪ್ರವೇಶ ನೀಡುತ್ತದೆ"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ಹೃದಯ ಬಡಿತ, ತಾಪಮಾನ, ರಕ್ತದ ಆಮ್ಲಜನಕದ ಶೇಕಡಾವಾರು ಮುಂತಾದ ಹಿನ್ನೆಲೆಯಲ್ಲಿರುವ ದೇಹದ ಸೆನ್ಸರ್‌ಗಳಿಂದ ಡೇಟಾಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡುತ್ತದೆ."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್‌ಗಳು ಮತ್ತು ವಿವರಗಳನ್ನು ಓದಿ"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ನಲ್ಲಿ ಸಂಗ್ರಹವಾಗಿರುವ ಎಲ್ಲಾ ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್‌ಗಳನ್ನು ಓದಬಹುದು ಮತ್ತು ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು ಅಥವಾ ಉಳಿಸಬಹುದು."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ Android TV ಸಾಧನದಲ್ಲಿ ಸಂಗ್ರಹವಾಗಿರುವ ಎಲ್ಲಾ ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್‌ಗಳನ್ನು ಓದಬಹುದು ಮತ್ತು ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು ಅಥವಾ ಉಳಿಸಬಹುದು."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ಓದಲು ಮತ್ತು ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ವೀಕ್ಷಣಾ ಅನುಮತಿಯ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ಆ್ಯಪ್‌ಗಾಗಿ ಅನುಮತಿ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಹೊಂದಿರುವವರಿಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಆ್ಯಪ್‌ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ಅನುಮತಿಯ ನಿರ್ಧಾರಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ಅನುಮತಿಯ ನಿರ್ಧಾರಗಳನ್ನು ಪರಿಶೀಲಿಸುವುದಕ್ಕಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಬಳಕೆದಾರರನ್ನು ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಆ್ಯಪ್‌ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ಆ್ಯಪ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಪ್ರಾರಂಭಿಸಿ"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ಆ್ಯಪ್‌ನ ವೈಶಿಷ್ಟ್ಯಗಳ ಮಾಹಿತಿಯನ್ನು ವೀಕ್ಷಿಸಲು ಬಳಕೆದಾರರನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ಹೆಚ್ಚಿನ ನಮೂನೆ ದರದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾ ಪ್ರವೇಶಿಸಿ"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"ಕಸ್ಟಮ್ ಆ್ಯಪ್ ಅಧಿಸೂಚನೆ"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (ಈ ಖಾತೆಯ ಬಳಕೆದಾರರು ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದ್ದಾರೆ) ಮೂಲಕ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು <xliff:g id="APP">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೆ ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ಮೂಲಕ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು <xliff:g id="APP">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸುವುದೇ ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"ಮೇಲ್ವಿಚಾರಣೆಯ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ಭಾಷೆ ಸೇರಿಸಿ"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"ಪ್ರದೇಶ ಪ್ರಾಶಸ್ತ್ಯ"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"ಭಾಷೆ ಹೆಸರನ್ನು ಟೈಪ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3b58e62..8920be2 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -303,7 +303,7 @@
     <string name="user_owner_label" msgid="8628726904184471211">"개인 프로필로 전환"</string>
     <string name="managed_profile_label" msgid="7316778766973512382">"직장 프로필로 전환"</string>
     <string name="permgrouplab_contacts" msgid="4254143639307316920">"연락처"</string>
-    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"주소록에 액세스"</string>
+    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"연락처에 액세스"</string>
     <string name="permgrouplab_location" msgid="1858277002233964394">"위치"</string>
     <string name="permgroupdesc_location" msgid="1995955142118450685">"이 기기의 위치정보에 액세스"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"캘린더"</string>
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"앱이 수신 및 발신 통화에 관한 데이터를 비롯하여 Android TV 기기의 통화 기록을 수정하도록 허용합니다. 이렇게 하면 악성 앱이 통화 기록을 삭제하거나 수정할 수도 있습니다."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"앱에서 수신 및 발신 통화 데이터를 포함하여 휴대전화의 통화 기록을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 통화 기록을 지우거나 수정할 수 있습니다."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"인체 감지 센서(예: 심박수 모니터)에 액세스"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"앱이 심박수와 같은 신체 상태를 확인하는 센서의 데이터에 접근하도록 허용합니다."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"심박수, 체온, 혈중 산소 농도와 같은 생체 신호 센서의 데이터에 액세스합니다."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"백그라운드에서 생체 신호 센서(심박수 모니터 등)에 액세스"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"백그라운드에서 심박수, 체온, 혈중 산소 농도 등 생체 신호 센서의 데이터에 액세스합니다."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"캘린더 일정 및 세부정보 읽기"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"이 앱은 태블릿에 저장된 모든 캘린더 일정을 읽고 캘린더 데이터를 공유하거나 저장할 수 있습니다."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"앱이 Android TV 기기에 저장된 모든 캘린더 일정을 읽고 캘린더 데이터를 공유하거나 저장할 수 있습니다."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"앱에서 방해 금지 모드 설정을 읽고 작성하도록 허용합니다."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"권한 사용 보기 시작"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"앱의 권한 사용을 시작하려면 보유자를 허용하세요. 일반 앱에는 필요하지 않습니다."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"권한 결정 보기 시작"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"권한 결정 검토를 위해 기기 보유자가 화면을 시작할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"앱 기능 보기"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"권한을 보유한 앱에서 앱의 기능 정보를 보도록 허용합니다."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"더 높은 샘플링 레이트로 센서 데이터 액세스"</string>
@@ -1321,7 +1325,7 @@
     <string name="ringtone_silent" msgid="397111123930141876">"없음"</string>
     <string name="ringtone_picker_title" msgid="667342618626068253">"벨소리"</string>
     <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"알람 소리"</string>
-    <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"알림 사운드"</string>
+    <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"알림음"</string>
     <string name="ringtone_unknown" msgid="5059495249862816475">"알 수 없음"</string>
     <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi 네트워크에 로그인"</string>
     <string name="network_available_sign_in" msgid="1520342291829283114">"네트워크에 로그인"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"맞춤 앱 알림"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g>에서 <xliff:g id="ACCOUNT">%2$s</xliff:g> 계정으로 신규 사용자를 만들도록 허용하시겠습니까? 이 계정으로 등록된 사용자가 이미 존재합니다."</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g>에서 <xliff:g id="ACCOUNT">%2$s</xliff:g> 계정으로 신규 사용자를 만들도록 허용하시겠습니까?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"관리 대상 사용자 추가"</string>
     <string name="language_selection_title" msgid="52674936078683285">"언어 추가"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"지역 환경설정"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"언어 이름 입력"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index ab2ab9b..50c9393 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Колдонмого Android TV түзмөгүңүздүн чалуулар тизмесин, анын ичинде кирүүчү жана чыгуучу чалуулар тууралуу маалыматтарды өзгөртүүгө уруксат берет. Зыянкеч колдонмолор ал уруксатты колдонуп чалуулар тизмеңизди тазалап же өзгөртүп коюшу мүмкүн."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Колдонмого телефонуңуздун чалуулар тизмегин, анын ичинде, чыгыш жана кириш чалууларына тиешелүү берилиштерди өзгөртүү уруксатын берет. Зыяндуу колдонмолор муну колдонуп чалуулар тизмегин өзгөртө же жок кыла алышат."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"дене-бой сенсорлоруна (жүрөктүн кагышын өлчөгүчтөр сыяктуу) уруксат"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Колдонмого жүрөгүңүздүн согушу сыяктуу дене-бой абалыңызды көзөмөлдөгөн сенсорлордогу маалыматтарды көрүп туруу мүмкүнчүлүгүн берет."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Жүрөктүн согушу, температура, кандагы кычкылтектин пайыздык үлүшү ж.б. сыяктуу дене сенсорлорунун маалыматын алуу."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"дене сенсорлорун (жүрөктүн согушун өлчөгүчтөр сыяктуу) фондо алуу"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Жүрөктүн согушу, температура, кандагы кычкылтектин пайыздык үлүшү ж.б. сыяктуу дене сенсорлорунун маалыматын фондо алуу."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Жылнаамадагы иш-чараларды жана алардын чоо-жайын окуу"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Бул колдонмо планшетиңизде сакталган жылнаамадагы иш-чаралардын баарын окуп жана андагы маалыматтарды бөлүшүп же сактай алат."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Бул колдонмо Android TV түзмөгүңүздө сакталган жылнаама иш-чараларынын баарын окуп, ошондой эле жылнаама дайындарын бөлүшүп же сактай алат."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Колдонмого \"Тынчымды алба\" режиминин конфигурациясын окуу жана жазуу мүмкүнчүлүгүн берет."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"уруксаттын колдонулушун көрүп баштоо"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Колдонмонун пайдаланылышына уруксат берүүгө мүмкүнчүлүк берет. Кадимки колдонмолорго эч качан талап кылынбашы керек."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"уруксаттар боюнча кабыл алынган чечимдерди карап чыгуу"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Ээсине уруксаттар боюнча кабыл алынган чечимдерди карап чыгуу үчүн экранды иштетүү мүмкүнчүлүгүн берет. Кадимки колдонмолорго эч качан талап кылынбашы керек."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"колдонмонун функцияларын көрүп баштоо"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Колдонуучуга функциялары тууралуу маалыматты көрүп баштоо мүмкүнчүлүгүн берет."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"үлгүнү жаздыруу ылдамдыгы жогору болгон сенсор дайындарынын үлгүсүнө мүмкүнчүлүк алуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 2018524..398b6b3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ອະນຸຍາດໃຫ້ແອັບແກ້ໄຂບັນທຶກການໂທຂອງອຸປະກອນ Android TV ທ່ານ, ຮວມທັງສາຍໂທເຂົ້າ ແລະ ໂທອອກ. ແອັບທີ່ເປັນອັນຕະລາຍສາມາດໃຊ້ຄຸນສົມບັດນີ້ເພື່ອລຶບ ຫຼື ແກ້ໄຂບັນທຶກການໂທຂອງທ່ານໄດ້."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ອະນຸຍາດໃຫ້ແອັບຯ ແກ້ໄຂລາຍການການໂທໃນໂທລະສັບຂອງທ່ານ, ຮວມທັງຂໍ້ມູນກ່ຽວກັບສາຍໂທເຂົ້າ ແລະການໂທອອກ. ແອັບຯທີ່ເປັນອັນຕະລາຍ ອາດໃຊ້ຄວາມສາມາດນີ້ ເພື່ອລຶບ ຫຼືແກ້ໄຂລາຍການການໂທຂອງທ່ານໄດ້."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ເຂົ້າ​ຫາເຊັນ​ເຊີ​​ກວດຮ່າງ​ກາຍ (ເຊັ່ນ: ​ຈໍຕິດ​ຕາມ​ອັດ​ຕາ​ການ​ເຕັ້ນ​ຂອງຫົວ​ໃຈ)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"​ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບຯ​ເຂົ້າ​ເຖິງ​ຂໍ້​ມູນ​ຈາກ​ເຊັນ​ເຊີ​ທີ່​ຕິດ​ຕາມ​ສະ​ພາບ​ຮ່າງ​ການ​ຂອງ​ທ່ານ, ເຊັ່ນ: ອັດ​ຕາ​ການ​ເຕັ້ນ​ຂອງ​ຫົວ​ໃຈຂອງ​ທ່ານ."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"ເຂົ້າເຖິງຂໍ້ມູນຈາກເຊັນເຊີຮ່າງກາຍ ເຊັ່ນ: ອັດຕາການເຕັ້ນຫົວໃຈ, ອຸນຫະພູມ, ເປີເຊັນອອກຊິເຈນໃນເລືອດ ແລະ ອື່ນໆ."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ເຂົ້າເຖິງເຊັນເຊີຮ່າງກາຍ (ເຊັ່ນ: ເຄື່ອງວັດແທກອັດຕາການເຕັ້ນຫົວໃຈ) ໃນຂະນະທີ່ຢູ່ໃນພື້ນຫຼັງ"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ເຂົ້າເຖິງຂໍ້ມູນຈາກເຊັນເຊີຮ່າງກາຍ ເຊັ່ນ: ອັດຕາການເຕັ້ນຫົວໃຈ, ອຸນຫະພູມ, ເປີເຊັນອອກຊິເຈນໃນເລືອດ ແລະ ອື່ນໆໃນຂະນະທີ່ຢູ່ໃນພື້ນຫຼັງ."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Read calendar events and details"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"This app can read all calendar events stored on your tablet and share or save your calendar data."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ແອັບນີ້ສາມາດອ່ານນັດໝາຍປະຕິທິນທັງໝົດທີ່ບັນທຶກໄວ້ຢູ່ອຸປະກອນ Android TV ຂອງທ່ານ ແລະ ແບ່ງປັນ ຫຼື ບັນທຶກຂໍ້ມູນປະຕິທິນຂອງທ່ານ."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ອະນຸຍາດ​​ໃຫ້​ແອັບ​ອ່ານ​ ​ແລະ​ຂຽນການກນຳ​ດຄ່າ ບໍ່​ລົບ​ກວນ."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ເລີ່ມການໃຊ້ສິດອະນຸຍາດການເບິ່ງ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມການໃຊ້ສິດອະນຸຍາດສຳລັບແອັບໃດໜຶ່ງໄດ້. ແອັບປົກກະຕິບໍ່ຄວນຕ້ອງໃຊ້."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ເລີ່ມເບິ່ງການຕັດສິນໃຈການອະນຸຍາດ"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມໜ້າຈໍເພື່ອກວດສອບການຕັດສິນໃຈການອະນຸຍາດ. ແອັບປົກກະຕິບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ເລີ່ມເບິ່ງຄຸນສົມບັດແອັບ"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມການເບິ່ງຂໍ້ມູນຄຸນສົມບັດສຳລັບແອັບໃດໜຶ່ງ."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີໃນອັດຕາຕົວຢ່າງສູງ"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"ການແຈ້ງເຕືອນແອັບແບບກຳນົດເອງ"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"ອະນຸຍາດໃຫ້ <xliff:g id="APP">%1$s</xliff:g> ສ້າງຜູ້ໃຊ້ໃໝ່ກັບ <xliff:g id="ACCOUNT">%2$s</xliff:g> ໄດ້ບໍ່ (ມີຜູ້ໃຊ້ທີ່ໃຊ້ບັນຊີນີ້ຢູ່ກ່ອນແລ້ວ) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"ອະນຸຍາດໃຫ້ <xliff:g id="APP">%1$s</xliff:g> ສ້າງຜູ້ໃຊ້ໃໝ່ກັບ <xliff:g id="ACCOUNT">%2$s</xliff:g> ໄດ້ບໍ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"ເພີ່ມຜູ້ໃຊ້ທີ່ມີການເບິ່ງແຍງກວດກາ"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ເພີ່ມພາສາ"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"ການຕັ້ງຄ່າພາກພື້ນ"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"ພິມຊື່ພາສາ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index aea67f2..de36cb6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Programai leidžiama keisti „Android TV“ įrenginio skambučių žurnalą, įskaitant gaunamųjų ir siunčiamųjų skambučių duomenis. Kenkėjiškos programos gali ištrinti arba keisti skambučių žurnalą."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Programai leidžiama skaityti telefono skambučių žurnalą, įskaitant duomenis apie gaunamuosius ir siunčiamuosius skambučius. Kenkėjiškos programos tai gali naudoti, kad ištrintų ar keistų jūsų skambučių žurnalą."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"pas. k. jut. (pvz., pul. dažn. st. įr.)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Programai leidžiama pasiekti duomenis, gautus iš jutiklių, stebinčių fizinę būseną, pvz., širdies ritmą."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Pasiekti kūno jutiklių duomenis, pvz., pulso dažnį, temperatūrą, deguonies procentinę dalį kraujyje ir kt."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"pasiekti kūno jutiklių duom. (pvz., pulso dažn. stebėjimo įreng.), veikiant fone"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Pasiekti kūno jutiklių duomenis, pvz., pulso dažnį, temperatūrą, deguonies procentinę dalį kraujyje ir kt., kai veikia fone."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Skaityti kalendoriaus įvykius arba išsamią informaciją"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ši programa gali nuskaityti visus planšetiniame kompiuteryje saugomus kalendoriaus įvykius ir bendrinti arba išsaugoti kalendoriaus duomenis."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ši programa gali nuskaityti visus „Android TV“ įrenginyje saugomus kalendoriaus įvykius ir bendrinti arba išsaugoti kalendoriaus duomenis."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leidžiama programai skaityti ir rašyti „Do Not Disturb“ konfigūraciją."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pradėti peržiūrėti leidimo naudojimą"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leidžia savininkui pradėti naudoti programos leidimą. Įprastoms programoms to neturėtų prireikti."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"pradėti sprendimų dėl leidimų peržiūrą"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Savininkui leidžiama atidaryti ekraną ir peržiūrėti sprendimus dėl leidimų. Neturėtų prireikti naudojant įprastas programas."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pradėti programos funkcijų peržiūrą"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Savininkui leidžiama pradėti programos funkcijų informacijos peržiūrą."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pasiekti jutiklių duomenis dideliu skaitmeninimo dažniu"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 495bc38..93414e0 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -430,7 +430,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Ļauj lietotnei pārveidot Android TV ierīces zvanu žurnālu, tostarp datus par ienākošajiem un izejošajiem zvaniem. Ļaunprātīgas lietotnes var to izmantot, lai dzēstu vai pārveidotu zvanu žurnālu."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Ļauj lietotnei pārveidot tālruņa zvanu žurnālu, tostarp ienākošo un izejošo zvanu datus. Ļaunprātīgas lietotnes var to izmantot, lai dzēstu vai pārveidotu savu zvanu žurnālu."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"Piekļūt ķermeņa sensoriem (piemēram, sirdsdarbības monitoriem)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Ļauj lietotnei piekļūt to sensoru datiem, kuri pārrauga jūsu fizisko stāvokli (piemēram, sirdsdarbības ātrumu)."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Piekļuve ķermeņa sensoru rādītāju datiem (piemēram, sirdsdarbības ātrumam, temperatūrai, procentuālajam skābekļa daudzumam asinīs u.c.)"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"piekļuve ķermeņa sensoru datiem (piemēram, sirdsdarbības ātrumam) fonā"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Piekļuve ķermeņa sensoru rādītāju datiem (piemēram, sirdsdarbības ātrumam, temperatūrai, procentuālajam skābekļa daudzumam asinīs u.c.) fonā."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lasīt kalendāra pasākumus un informāciju"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Šī lietotne var lasīt visus kalendāra pasākumus, kas saglabāti planšetdatorā, un kopīgot vai saglabāt jūsu kalendāra datus."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Šī lietotne var lasīt visus kalendāra pasākumus, kas saglabāti Android TV ierīcē, un kopīgot vai saglabāt jūsu kalendāra datus."</string>
@@ -734,6 +736,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ļauj lietotnei lasīt un rakstīt režīma “Netraucēt” konfigurāciju."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Datu skatīšana par izmantojamajām atļaujām"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ļauj atļaujas īpašniekam sākt lietotnes atļauju izmantošanu. Parastām lietotnēm tas nekad nav nepieciešams."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"Skatīt darbības ar atļaujām"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Lietotne ar šo atļauju var pārskatīt darbības ar atļaujām. Parastām lietotnēm šīs atļauja nekad nav nepieciešama."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"Skatīt lietotnes funkcijas"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Lietotne ar šo atļauju var skatīt informāciju par citas lietotnes funkcijām."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"piekļuve sensoru datiem, izmantojot augstu iztveršanas frekvenci"</string>
@@ -2021,8 +2025,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Pielāgots lietotnes paziņojums"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Vai atļaut lietotnei <xliff:g id="APP">%1$s</xliff:g> izveidot jaunu lietotāju, izmantojot e-pasta adresi <xliff:g id="ACCOUNT">%2$s</xliff:g> (lietotājs ar šādu kontu jau pastāv)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Vai atļaut lietotnei <xliff:g id="APP">%1$s</xliff:g> izveidot jaunu lietotāju, izmantojot e-pasta adresi <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Pievienot uzraudzītu lietotāju"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Pievienot valodu"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Reģiona preference"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Ierakstiet valodas nosaukumu"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 92e1d5f..d4f099f 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Дозволува апликацијата да го менува дневникот на повици на вашиот уред Android TV, вклучувајќи и податоци за дојдовните или појдовните повици. Злонамерните апликации може да го искористат ова за да го избришат или да го менуваат дневникот на повици."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Овозможува апликацијата да го менува дневникот на повици на вашиот телефон, вклучувајќи податоци за дојдовни и појдовни повици. Злонамерните апликации може да го искористат ова да го избришат или да го менуваат вашиот дневник на повици."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"пристап до телесните сензори (како мониторите за пулс)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Дозволува апликацијата да пристапува до податоци од сензори кои ја следат вашата физичка состојба, како на пр. отчукувањата на срцето."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Пристап до податоци од телесни сензори, како пулс, температура, процент на кислород во крвта итн."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"пристап до телесните сензори (како мониторите за пулс) додека се во заднина"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Пристап до податоци од телесни сензори, како пулс, температура, процент на кислород во крвта итн. додека се во заднина."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Чита настани и детали од календарот"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Апликацијава може да ги чита сите настани во календарот складирани во вашиот таблет и да ги споделува или зачувува податоците од календарот."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Апликацијава може да ги чита сите настани во календарот складирани во вашиот уред Android TV и да ги споделува или зачувува податоците од календарот."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозволува апликацијата да чита и пишува конфигурација Не вознемирувај."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"започнете со користење на дозволата за приказ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозволува сопственикот да почне со користење на дозволата за апликација. Не треба да се користи за стандардни апликации."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"да го стартува приказот за одлуки за дозволи"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Му дозволува на сопственикот да го стартува екранот за прегледување одлуки за дозволи. Не треба да се користи за стандардни апликации."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"да почне со прегледување на функциите на апликацијата"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"му дозволува на сопственикот да почне со прегледување на податоците за функциите за некоја апликација"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"пристапува до податоците со висока фреквенција на семпл"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 0d76eae..1c98f25 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ഇൻകമിംഗ്, ഔട്ട്‌ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റ ഉൾപ്പെടെ നിങ്ങളുടെ Android TV-യിലെ കോൾ ചരിത്രം പരിഷ്‌ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ കോൾ ചരിത്രം മായ്‌ക്കാനോ പരിഷ്‌ക്കരിക്കാനോ ദോഷകരമായ ആപ്പുകൾ ഇത് ഉപയോഗിച്ചേക്കാം."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ഇൻകമ്മിംഗ്-ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റയുൾപ്പെടെയുള്ള നിങ്ങളുടെ ഫോണിന്‍റെ കോൾ ചരിത്രം പരിഷ്‌ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.ഇതു വഴി കോൾ ചരിത്ര ഡാറ്റകൾ പരിഷ്‌ക്കരിക്കാനും ഇല്ലാതാക്കാനും ദോഷകരമായ അപ്ലിക്കേഷനുകൾക്ക് കഴിഞ്ഞേയ്ക്കാം."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ശരീര സെൻസറുകൾ (ഹൃദയമിടിപ്പ് നിരക്ക് മോണിറ്ററുകൾ പോലെ) ആക്സസ് ചെയ്യുക"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"നിങ്ങളുടെ ഹൃദയമിടിപ്പ് പോലുള്ള ശാരീരികാവസ്ഥ നിരീക്ഷിക്കാൻ സെൻസറുകളിൽ നിന്ന് വിവരം ആക്‌സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"ഹൃദയമിടിപ്പ്, താപനില, രക്തത്തിലെ ഓക്‌സിജന്റെ ശതമാനം മുതലായവ പോലുള്ള ബോഡി സെൻസറുകളിൽ നിന്നുള്ള ഡാറ്റയിലേക്കുള്ള ആക്‌സസ്."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"പശ്ചാത്തലത്തിൽ ബോഡി സെൻസറുകൾ (ഹൃദയമിടിപ്പ് മോണിറ്ററുകൾ പോലുള്ളവ) ആക്സസ് ചെയ്യുക"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"പശ്ചാത്തലത്തിൽ ആയിരിക്കുമ്പോൾ ഹൃദയമിടിപ്പ്, താപനില, രക്തത്തിലെ ഓക്‌സിജന്റെ ശതമാനം മുതലായവ പോലുള്ള ബോഡി സെൻസറുകളിൽ നിന്നുള്ള ഡാറ്റയിലേക്കുള്ള ആക്‌സസ്."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"കലണ്ടർ ഇവന്റുകളും വിശദാംശങ്ങളും വായിക്കുക"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ഈ ആപ്പിന് നിങ്ങളുടെ ടാബ്‌ലെറ്റിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ കലണ്ടർ ഇവന്റുകളും വായിക്കാനും നിങ്ങളുടെ കലണ്ടർ വിവരങ്ങൾ പങ്കിടാനും അല്ലെങ്കിൽ സംരക്ഷിക്കാനും കഴിയും."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ഈ ആപ്പിന് നിങ്ങളുടെ Android TV-യിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ കലണ്ടർ ഇവന്റുകളും വായിക്കാനും നിങ്ങളുടെ കലണ്ടർ ഡാറ്റ പങ്കിടാനോ സംരക്ഷിക്കാനോ സാധിക്കുകയും ചെയ്യും."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\'ശല്യപ്പെടുത്തരുത്\' കോൺഫിഗറേഷൻ വായിക്കുന്നതിനും എഴുതുന്നതിനും ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"അനുമതി ഉപയോഗം കാണാൻ ആരംഭിക്കുക"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ഒരു ആപ്പിനുള്ള അനുമതി ഉപയോഗം ആരംഭിക്കാൻ ഹോൾഡറിനെ അനുവദിക്കുന്നു. സാധാരണ ആപ്പുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"അനുമതിയുമായി ബന്ധപ്പെട്ട തീരുമാനങ്ങൾ കാണാൻ ആരംഭിക്കുക"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"അനുമതിയുമായി ബന്ധപ്പെട്ട തീരുമാനങ്ങൾ അവലോകനം ചെയ്യുന്നതിന് സ്ക്രീൻ ആരംഭിക്കാൻ ഹോൾഡറിനെ അനുവദിക്കുന്നു. സാധാരണ ആപ്പുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ആപ്പ് ഫീച്ചറുകൾ കാണാൻ ആരംഭിക്കുക"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ആപ്പിനുള്ള ഫീച്ചറുകളുടെ വിവരങ്ങൾ കാണാൻ ആരംഭിക്കാൻ ഹോൾഡറിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ഉയർന്ന സാം‍പ്ലിംഗ് റേറ്റിൽ സെൻസർ ഡാറ്റ ആക്സസ് ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 24cad85..db67e90 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Аппад таны Android TV төхөөрөмжийн ирсэн болон залгасан дуудлага зэрэг өгөгдөл бүхий дуудлагын жагсаалтыг өөрчлөхийг зөвшөөрнө. Хортой аппууд үүнийг ашиглан таны дуудлагын жагсаалтыг устгаж эсвэл өөрчилж болзошгүй."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Апп нь таны утасны ирсэн гарсан дуудлага зэргийг агуулсан дуудлагын логыг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан таны дуудлагын логыг өөрчлөх болон арилгах боломжтой."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"биеийн мэдрэгчид хандах (зүрхний хэмнэл шалгагч г.м)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Апп-т таны зүрхний цохилт гэх мэт биеийн байдлыг хянадаг мэдрэгчдийн датанд хандалт хийх боломж олгоно."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Зүрхний хэм, температур, цусны хүчилтөрөгчийн хувь гэх мэт биеийн мэдрэгчийн өгөгдөлд хандана уу"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"арын дэвсгэрт ажиллах үед биеийн мэдрэгчид (зүрхний хэм хянагч гэх мэт) хандах"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Арын дэвсгэрт ажиллах үед зүрхний хэм, температур, цусны хүчилтөрөгчийн хувь гэх мэт биеийн мэдрэгчийн өгөгдөлд хандана уу."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Календарийн арга хэмжээ, дэлгэрэнгүйг унших"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Энэ апп таны таблетад хадгалсан календарийн бүх арга хэмжээг унших, календарийн өгөгдлийг хуваалцах, хадгалах боломжтой."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Энэ апп таны Android TV төхөөрөмжид хадгалсан календарийн бүх арга хэмжээг унших болон таны календарийн өгөгдлийг хуваалцах эсвэл хадгалах боломжтой."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Апп-д Бүү саад бол тохируулгыг уншиж, бичихийг зөвшөөрөх"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"зөвшөөрлийн ашиглалтыг харж эхлэх"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Эзэмшигчид аппын зөвшөөрлөө ашиглаж эхлэхийг зөвшөөрдөг. Энгийн аппуудад шаардлагагүй."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"зөвшөөрлийн шийдвэрийг хянах дэлгэцийг эхлүүлэх"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Эзэмшигчид зөвшөөрлийн шийдвэрийг хянах дэлгэцийг эхлүүлэх боломжийг олгоно. Энгийн аппуудад хэзээ ч шаардагдахгүй."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"аппын онцлогуудыг үзэж эхлэх"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Аппын онцлогуудын мэдээллийг үзэж эхлэхийг эзэмшигчид зөвшөөрдөг."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"түүврийн өндөр хувиар мэдрэгчийн өгөгдөлд хандах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 162b4ae..1385dcd 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, तुमच्या Android TV डिव्हाइसचा कॉल लॉग सुधारित करण्यासाठी ॲपला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेन्सर (हृदय गती मॉनिटरसारखे) अ‍ॅक्सेस करा"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्‍या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी ॲपला अनुमती देते."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"हार्ट रेट, तापमान, रक्तातील ऑक्सिजनची टक्केवारी इ. सारख्या शरीर सेन्सरवरील डेटाचा अ‍ॅक्सेस आहे."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"बॅकग्राउंडमध्ये असताना शरीर सेन्सर (जसे की हार्ट रेट मॉनिटर) अ‍ॅक्सेस करा"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"बॅकग्राउंडमध्ये असताना हार्ट रेट, तापमान, रक्तातील ऑक्सिजनची टक्केवारी इ. सारखा शरीर सेन्सरवरील डेटा अ‍ॅक्सेस करा."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"हा अ‍ॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"हे अ‍ॅप तुमच्या Android TV डिव्हाइसवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"परवानगीशी संबंधित निर्णय पाहणे सुरू करा"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"होल्डरला परवानगीशी संबंधित निर्णयांचे पुनरावलोकन करण्यासाठी स्क्रीन सुरू करण्याची अनुमती देते. सामान्य ॲप्ससाठी कधीही आवश्यक नसते."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ॲप वैशिष्ट्ये पाहणे सुरू करा"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"होल्डरला ॲपसाठी वैशिष्ट्यांची माहिती पाहण्यास सुरू करण्याची अनुमती देते."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"उच्च नमुना दराने सेन्सर डेटा अ‍ॅक्सेस करते"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 4a50bc9..43847fe 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Membenarkan apl mengubah suai log panggilan peranti Android TV anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakan keupayaan ini untuk memadamkan atau mengubah suai log panggilan anda."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Membenarkan apl untuk mengubah suai panggilan telefon anda, termasuk data tentang panggilan masuk dan keluar. Apl hasad boleh menggunakannya untuk memadam atau mengubah suai log panggilan anda."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"akss pndia bdn (spt pmntau kdr dnyt jntg)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Membenarkan apl mengakses data dari penderia yang memantau keadaan fizikal anda, seperti kadar denyutan jantung anda."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Akses kepada data daripada penderia tubuh seperti kadar denyut jantung, suhu, peratusan oksigen darah, dsb."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"akses penderia tubuh (seperti pemantau kadar denyut jantung) semasa di latar"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Akses kepada data daripada penderia tubuh seperti kadar denyut jantung, suhu, peratusan oksigen darah, dsb. semasa di latar."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Baca acara dan butiran kalendar"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Apl ini boleh membaca semua acara kalendar yang disimpan pada tablet anda dan berkongsi atau menyimpan data kalendar anda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Apl ini boleh membaca semua acara kalendar yang disimpan pada peranti Android TV anda dan berkongsi atau menyimpan data kalendar anda."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Membenarkan apl membaca dan menulis konfigurasi Jangan Ganggu."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulakan lihat penggunaan kebenaran"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Membenarkan pemegang memulakan penggunaan kebenaran untuk apl. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"mula melihat keputusan kebenaran"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Membenarkan pemegang memulakan skrin untuk menyemak keputusan kebenaran. Tidak diperlukan untuk apl biasa."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"mula melihat ciri apl"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Membenarkan pemegang mula melihat maklumat ciri untuk apl."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"akses data penderia pada data pensampelan yang tinggi"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Pemberitahuan apl tersuai"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Benarkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baharu dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> (Pengguna dengan akaun ini sudah wujud) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Benarkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baharu dengan <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Tambahkan pengguna di bawah seliaan"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Tambahkan bahasa"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Pilihan wilayah"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Taipkan nama bahasa"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 169c0fd..f509906 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -317,7 +317,7 @@
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ကိုယ်ခန္ဓာလှုပ်ရှားမှု"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"သင့်ကိုယ်ခန္ဓာလှုပ်ရှားမှုကို ဝင်ကြည့်ရန်"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"ကင်မရာ"</string>
-    <string name="permgroupdesc_camera" msgid="7585150538459320326">"ဓာတ်ပုံ ရိုက်ပြီးနောက် ဗွီဒီယို မှတ်တမ်းတင်ရန်"</string>
+    <string name="permgroupdesc_camera" msgid="7585150538459320326">"ဓာတ်ပုံနှင့် ဗီဒီယို ရိုက်ကူးရန်"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"အနီးတစ်ဝိုက်ရှိ စက်များ"</string>
     <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"အနီးတစ်ဝိုက်ရှိ စက်များကို ရှာဖွေပြီးချိတ်ဆက်မည်"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"ခေါ်ဆိုမှတ်တမ်း"</string>
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"အဝင်နှင့် အထွက်ခေါ်ဆိုမှု အချက်အလက်များ အပါအဝင် သင့် Android TV စက်ပစ္စည်းပေါ်ရှိ ခေါ်ဆိုထားသော မှတ်တမ်းကို အက်ပ်အား မွမ်းမံခွင့်ပြုသည်။ သံသယဖြစ်နိုင်ဖွယ်ရှိသည့် အက်ပ်များသည် ၎င်းကို အသုံးပြုပြီး သင်၏ ခေါ်ဆိုထားသော မှတ်တမ်းကို ဖျက်နိုင်သည်။"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"အပလီကေးရှင်းအား သင့်ဖုန်း၏ ဖုန်းခေါ်ဆိုမှု မှတ်တမ်း (အဝင်အထွက်ခေါ်ဆိုမှု အချက်အလက်များ) ကို ပြင်ဆင်ခွင့် ပေးခြင်း။ အန္တရာယ်ရှိ အပလီကေးရှင်းများမှ ဤအချက်ကို အသုံးပြု၍ သင့် ဖုန်းခေါ်ဆိုမှု မှတ်တမ်းကို ဖျက်ပစ်ခြင်း၊ ပြင်ဆင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ခန္ဓာကိုယ် အာရုံကိရိယာများကို (နှလုံးခုန်နှုန်း မော်နီတာလို)ကို ရယူသုံးရန်"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"သင်၏ နှလုံးခုန်နှုန်းလို ရုပ်ပိုင်း အခြေအနေကို စောင့်ကြပ်သည့် အာရုံခံစက်များထံမှ ဒေတာများကို အက်ပ်အား ရယူသုံးခွင့် ပြုပါ။"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"နှလုံးခုန်နှုန်း၊ အပူချိန်၊ သွေးတွင်း အောက်ဆီဂျင် ရာခိုင်နှုန်း စသည့် ခန္ဓာကိုယ် အာရုံခံကိရိယာများမှ ဒေတာသုံးခွင့်။"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"(နှလုံးခုန်နှုန်း စောင့်ကြည့်ခြင်းကဲ့သို့) ခန္ဓာကိုယ် အာရုံခံကိရိယာများကို နောက်ခံ၌သုံးခွင့်"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"နှလုံးခုန်နှုန်း၊ အပူချိန်၊ သွေးတွင်း အောက်ဆီဂျင် ရာခိုင်နှုန်း စသည့် ခန္ဓာကိုယ် အာရုံခံကိရိယာများမှ ဒေတာကို နောက်ခံ၌သုံးခွင့်။"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"ပြက္ခဒိန်ဖြစ်ရပ်များနှင့် အသေးစိတ်အချက်အလက်များကို ဖတ်ခြင်း"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ဤအက်ပ်သည် သင့်တက်ဘလက်တွင် သိမ်းဆည်းထားသည့် ပြက္ခဒိန်ဖြစ်ရပ်များကို ကြည့်ရှုနိုင်ပြီး သင့်ပြက္ခဒိန်ဒေတာများကို မျှဝေခြင်းနှင့် သိမ်းဆည်းခြင်းတို့ ပြုလုပ်နိုင်ပါသည်။"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ဤအက်ပ်သည် သင့် Android TV စက်ပစ္စည်းတွင် သိမ်းဆည်းထားသည့် ပြက္ခဒိန်ဖြစ်ရပ်များအားလုံးကို ဖတ်နိုင်ပြီး သင်၏ ပြက္ခဒိန်ဒေတာများကို မျှဝေခြင်း သို့မဟုတ် သိမ်းဆည်းခြင်းတို့ ပြုလုပ်နိုင်သည်။"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"မနှောင့်ယှက်ရန် ချိန်ညှိမှုကို အပ်ဖ်များ ဖတ်ခြင်း ပြင်ခြင်းပြုလုပ်နိုင်ရန် ခွင့်ပြုမည်။"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"အစမြင်ကွင်း ခွင့်ပြုချက် အသုံးပြုမှု"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"အက်ပ်တစ်ခုအတွက် ခွင့်ပြုချက်စတင်အသုံးပြုမှုကို ကိုင်ဆောင်သူအား ခွင့်ပြုသည်။ ပုံမှန်အက်ပ်များအတွက် ဘယ်သောအခါမျှ မလိုအပ်ပါ။"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ခွင့်ပြုသည့် ဆုံးဖြတ်ချက်များကို စတင်ကြည့်ခြင်း"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ခွင့်ပြုထားသည့်အက်ပ်အား ခွင့်ပြုသည့်ဆုံးဖြတ်ချက်များကို ကြည့်နိုင်ရန်အတွက် စခရင်စတင်ရန် ခွင့်ပြုနိုင်သည်။ သာမန်အက်ပ်များအတွက် မည်သည့်အခါမျှ မလိုအပ်နိုင်ပါ။"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"အက်ပ်ဝန်ဆောင်မှုများကို စတင်ကြည့်ခြင်း"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ဝန်ဆောင်မှုအချက်အလက်ကိုများကို ခွင့်ပြုချက်ရထားသည့် အက်ပ်အား စတင်ကြည့်နိုင်ရန် ခွင့်ပြုသည်။"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"နမူနာနှုန်းမြင့်သော အာရုံခံစနစ်ဒေတာကို သုံးပါ"</string>
@@ -1711,12 +1715,12 @@
     <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ဖယ်ရှားရန်"</string>
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။"</string>
-    <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"</string>
+    <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးနိုင်သည့် ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"</string>
     <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများအတွက် ဖြတ်လမ်းကို ဖွင့်မလား။"</string>
     <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nလက်ရှိ ဝန်ဆောင်မှုများ-\n<xliff:g id="SERVICE">%1$s</xliff:g>\n\'ဆက်တင်များ &gt; အများသုံးစွဲနိုင်မှု\' တွင် ရွေးထားသည့် ဝန်ဆောင်မှုများကို ပြောင်းနိုင်သည်။"</string>
     <string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
     <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> ဖြတ်လမ်းကို ဖွင့်မလား။"</string>
-    <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုဖြစ်သော <xliff:g id="SERVICE">%1$s</xliff:g> ကို ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nဤဖြတ်လမ်းလင့်ခ်ကို \'ဆက်တင်များ &gt; အများသုံးစွဲနိုင်မှု\' တွင် နောက်ဝန်ဆောင်မှုတစ်ခုသို့ ပြောင်းနိုင်သည်။"</string>
+    <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးနိုင်သည့် ဝန်ဆောင်မှုဖြစ်သော <xliff:g id="SERVICE">%1$s</xliff:g> ကို ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nဤဖြတ်လမ်းလင့်ခ်ကို ဆက်တင်များ &gt; အများသုံးနိုင်မှုတွင် နောက်ဝန်ဆောင်မှုတစ်ခုသို့ ပြောင်းနိုင်သည်။"</string>
     <string name="accessibility_shortcut_on" msgid="5463618449556111344">"ဖွင့်ရန်"</string>
     <string name="accessibility_shortcut_off" msgid="3651336255403648739">"မဖွင့်ပါနှင့်"</string>
     <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ဖွင့်"</string>
@@ -1745,9 +1749,9 @@
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို သုံးရန် အသံအတိုးအလျှော့ ခလုတ်နှစ်ခုလုံးကို သုံးစက္ကန့်ကြာ ဖိထားပါ"</string>
-    <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"အများသုံးစွဲနိုင်မှု ခလုတ်ကို တို့သည့်အခါ အသုံးပြုမည့် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ−"</string>
-    <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"အများသုံးစွဲနိုင်မှုလက်ဟန်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုတစ်ခုကို ရွေးပါ (ဖန်သားပြင်အောက်ခြေမှနေ၍ လက်နှစ်ချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ)-"</string>
-    <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"အများသုံးစွဲနိုင်မှုလက်ဟန်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုတစ်ခုကို ရွေးပါ (ဖန်သားပြင်အောက်ခြေမှနေ၍ လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ)-"</string>
+    <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"သုံးနိုင်မှုခလုတ်ကို တို့ပြီးလျှင် ဝန်ဆောင်မှုတစ်ခု ရွေးပါ−"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"သုံးနိုင်မှုလက်ဟန်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုတစ်ခုကို ရွေးပါ (ဖန်သားပြင်အောက်ခြေမှနေ၍ လက်နှစ်ချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ)-"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"သုံးနိုင်မှုလက်ဟန်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုတစ်ခုကို ရွေးပါ (ဖန်သားပြင်အောက်ခြေမှနေ၍ လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ)-"</string>
     <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ဝန်ဆောင်မှုများအကြား ပြောင်းရန် အများသုံးစွဲနိုင်မှုခလုတ်ကို ဖိထားပါ။"</string>
     <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ဝန်ဆောင်မှုများအကြား ပြောင်းရန် လက်နှစ်ချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပြီး ဖိထားပါ။"</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ဝန်ဆောင်မှုများအကြား ပြောင်းရန် လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပြီး ဖိထားပါ။"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"စိတ်ကြိုက်အက်ပ် အကြောင်းကြားချက်"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ဖြင့်အသုံးပြုသူအသစ်ကို <xliff:g id="APP">%1$s</xliff:g> အား ဖန်တီးခွင့်ပြုလိုပါသလား (ဤအကောင့်ဖြင့် အသုံးပြုသူ ရှိနှင့်ပြီးဖြစ်သည်) ။"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ဖြင့်အသုံးပြုသူအသစ်ကို <xliff:g id="APP">%1$s</xliff:g> အား ဖန်တီးခွင့်ပြုလိုပါသလား ။"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"ကြီးကြပ်ခံ အသုံးပြုသူ ထည့်ရန်"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ဘာသာစကားတစ်ခု ထည့်ပါ"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"ဒေသရွေးချယ်မှု"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"ဘာသာစကားအမည် ထည့်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 7c5ae01..2ddd45d 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Lar appen endre samtaleloggen på Android TV-enheten din, inkludert data om innkommende og utgående anrop. Skadelige apper kan bruke denne tillatelsen til å slette eller endre samtaleloggen din."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Lar appen endre telefonens samtalelogg, inkludert data om innkommende og utgående anrop. Skadelige apper kan utnytte denne tillatelsen til å slette eller endre samtaleloggen din."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"få tilgang til kroppssensorer (f.eks. pulsmålere)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Gir appen tilgang til data fra sensorer som overvåker den fysiske tilstanden din, for eksempel hjertefrekvensen din."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Tilgang til data fra kroppssensorer, for eksempel puls, temperatur, oksygenmetning i blodet osv."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"få tilgang til kroppssensorer (f.eks. pulsmålere) i bakgrunnen"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Tilgang til data fra kroppssensorer, for eksempel puls, temperatur, oksygenmetning i blodet osv., i bakgrunnen."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Les kalenderaktivitet og detaljer"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Denne appen kan lese all kalenderaktivitet som er lagret på nettbrettet ditt, og dele eller lagre kalenderdataene."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Denne appen kan lese all kalenderaktivitet som er lagret på Android TV-enheten din, og dele eller lagre kalenderdataene."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lar appen lese og skrive konfigurasjon av Ikke forstyrr."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start visning av bruk av tillatelser"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lar innehaveren starte bruk av tillatelser for en app. Dette skal aldri være nødvendig for vanlige apper."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"starte visning av avgjørelser om tillatelser"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Lar innehaveren starte skjermen for å gjennomgå avgjørelser om tillatelser. Dette skal aldri være nødvendig for vanlige apper."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"starte visning av appfunksjoner"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Lar innehaveren se informasjon om funksjonene for en app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"tilgang til sensordata ved høy samplingfrekvens"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Tilpasset appvarsel"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Vil du la <xliff:g id="APP">%1$s</xliff:g> opprette en ny bruker med <xliff:g id="ACCOUNT">%2$s</xliff:g> (en bruker med denne kontoen eksisterer allerede)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Vil du la <xliff:g id="APP">%1$s</xliff:g> opprette en ny bruker med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Legg til administrert bruker"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Legg til et språk"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Regionsinnstilling"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Skriv inn språknavn"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 7f9a832..d4828b6 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"एपलाई तपाईंको Android टिभी डिभाइसको आगमन र बहिर्गमन कलसम्बन्धी डेटासहित कल लग परिमार्जन गर्ने अनुमति दिन्छ। हानिकारक एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्छन्।"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"एपलाई तपाईंको फोनको आउने र बाहिर जाने कलहरूको बारेको डेटा सहित कल लग परिमार्जन गर्न अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्दछ।"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीरका सेन्सरहरूमा पहुँच गराउनुहोस् (जस्तै हृदय धड्कन निगरानीहरू)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"तपाईँको हृदय गति जस्तो सेंसर बाट डेटा पहुँचको लागि एप अनुमति दिन्छ जसले तपाईँको भौतिक अवस्था अनुगमन गर्छ।"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"बडी सेन्सरका सहायताले हृदयको गति, शरीरको तापक्रम, रगतमा रहेको अक्सिजनको प्रतिशतलगायतका डेटा हेर्ने अनुमति।"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ब्याकग्राउन्डमा काम गर्ने बडी सेन्सरका सहायताले (हृदयको गति मापन जस्ता) डेटा हेर्ने अनुमति"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ब्याकग्राउन्डमा काम गर्ने बडी सेन्सरका सहायताले हृदयको गति, शरीरको तापक्रम, रगतमा रहेको अक्सिजनको प्रतिशतलगायतका डेटा हेर्ने अनुमति।"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"पात्रोका कार्यक्रम र विवरणहरू पढ्ने"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"यस एपले तपाईंको ट्याब्लेटमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"यस एपले तपाईंको Android टिभी डिभाइसमा भण्डार गरिएका पात्रोसम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"बाधा नपुर्याउँनुहोस् कन्फिगरेसन पढ्न र लेख्‍नको लागि एपलाई अनुमति दिनुहोस्।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"हेर्ने अनुमतिको प्रयोग सुरु गर्नुहोस्"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै एपसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"अनुमतिसम्बन्धी निर्णयहरू हेर्न सुरु गर्नुहोस्"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"होल्डरलाई अनुमतिसम्बन्धी निर्णयहरू समीक्षा गर्ने स्क्रिन सुरु गर्न दिन्छ। सामान्य एपहरूलाई कहिल्यै पनि नचाहिनु पर्ने हो।"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"एपका सुविधासम्बन्धी जानकारी हेर्न थाल्नुहोस्"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"होल्डरलाई एपका सुविधासम्बन्धी जानकारी हेर्न दिन्छ।"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"नमुना लिने उच्च दरमा सेन्सरसम्बन्धी डेटा प्रयोग गर्ने"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5dcdda4..3b6db06 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Toestaan dat de app de gesprekslijst van je Android TV-apparaat aanpast, waaronder gegevens over inkomende en uitgaande gesprekken. Schadelijke apps kunnen hiermee je gesprekslijst wissen of aanpassen."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Toestaan dat de app de gesprekslijst van je telefoon aanpast, waaronder gegevens over inkomende en uitgaande gesprekken. Schadelijke apps kunnen hiermee je gesprekslijst wissen of aanpassen."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"toegang tot lichaamssensoren (zoals hartslagmeters)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Hiermee kan de app toegang krijgen tot gegevens van sensoren die je lichamelijke conditie controleren, zoals je hartslag."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Toegang tot gegevens van lichaamssensoren, zoals hartslag, temperatuur en zuurstofpercentage in het bloed."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"toegang tot lichaamssensoren (zoals hartslagmeters) op de achtergrond"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Toegang tot gegevens van lichaamssensoren, zoals hartslag, temperatuur en zuurstofpercentage in het bloed, terwijl de app op de achtergrond wordt uitgevoerd."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Agenda-afspraken en -gegevens lezen"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Deze app kan alle agenda-afspraken lezen die zijn opgeslagen op je tablet en je agendagegevens delen of opslaan."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Deze app kan alle agenda-afspraken lezen die zijn opgeslagen op je Android TV-apparaat en je agendagegevens delen of opslaan."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Hiermee kan de app configuratie voor Niet storen lezen en schrijven."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rechtengebruik starten"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Hiermee kan de houder het rechtengebruik voor een app starten. Nooit vereist voor normale apps."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"bekijken van rechtenbeslissingen starten"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Hiermee kan de houder het scherm starten om rechtenbeslissingen te bekijken. Nooit vereist voor normale apps."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"app-functies bekijken"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Hiermee kan de houder informatie over functies bekijken voor een app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"toegang krijgen tot sensorgegevens met een hoge samplingsnelheid"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 919d193..0220ce3 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ଇନ୍‍କମିଂ ତଥା ଆଉଟ୍‌ଗୋଇଂ କଲ୍ ଡାଟା ସହ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍‌ର କଲ୍ ଲଗ୍ ସଂଶୋଧନ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ। କ୍ଷତିକାରକ ଆପ୍‌ଗୁଡ଼ିକ ଆପଣଙ୍କ କଲ୍ ଲଗ୍ ଲିଭାଇବାକୁ କିମ୍ବା ସଂଶୋଧନ କରିବା ପାଇଁ ଏହାକୁ ବ୍ୟବହାର କରିପାରନ୍ତି।"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ଇନ୍‍କମିଙ୍ଗ ତଥା ଆଉଟ୍‍ଗୋଇଙ୍ଗ କଲ୍‌ ଡାଟା ସହ ଆପଣଙ୍କ ଫୋନ୍‍ର କଲ୍‍ ଲଗ୍‍ ବଦଳାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ କଲ୍‍ ଲଗ୍‍ ଲିଭାଇବାକୁ କିମ୍ବା ବଦଳାଇବାକୁ ଏହା ବ୍ୟବହାର କରିପାରନ୍ତି।"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ବଡୀ ସେନ୍ସର୍‍ ଆକ୍ସେସ୍‍ କରେ (ଯେପରିକି ହୃଦ୍‍ ହାର ମନିଟର୍‍)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"ଆପ୍‌କୁ ସେନ୍ସର୍ ଡେଟା ପର୍ଯ୍ୟନ୍ତ ପହଞ୍ଚିବାକୁ ଦେଇଥାଏ, ଯାହା ଆପଣଙ୍କ ଶାରୀରିକ ସ୍ଥିତିର ନିରୀକ୍ଷଣ କରିଥାଏ, ଯେପରିକି ଆପଣଙ୍କ ହୃଦୟ ସ୍ତର।"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"ହାର୍ଟ ରେଟ, ତାପମାତ୍ରା, ରକ୍ତରେ ଅମ୍ଳଜାନ ଶତକଡ଼ା ଇତ୍ୟାଦି ପରି ବଡି ସେନ୍ସରରୁ ଡାଟାକୁ ଆକ୍ସେସ।"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ପୃଷ୍ଠପଟରେ ଥିବା ସମୟରେ (ହାର୍ଟ ରେଟ ମନିଟର ପରି) ବଡି ସେନ୍ସରକୁ ଆକ୍ସେସ"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ପୃଷ୍ଠପଟରେ ଥିବା ସମୟରେ ହାର୍ଟ ରେଟ, ତାପମାତ୍ରା, ରକ୍ତରେ ଅମ୍ଳଜାନ ଶତକଡ଼ା ଇତ୍ୟାଦି ପରି ବଡି ସେନ୍ସରରୁ ଡାଟାକୁ ଆକ୍ସେସ।"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"କ୍ୟାଲେଣ୍ଡର୍‍ ଇଭେଣ୍ଟ ଏବଂ ବିବରଣୀ ପଢ଼େ"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ସମସ୍ତ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ଏହି ଆପ୍‍ ପଢ଼ିପାରେ ଏବଂ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର ଡାଟା ସେୟାର୍‍ କରିପାରେ କିମ୍ବା ସେଭ୍‍ କରିପାରେ।"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ଏହି ଆପ୍ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍‌ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ପଢ଼ିପାରେ ଏବଂ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର ଡାଟା ସେୟାର୍ କରିପାରେ କିମ୍ବା ସେଭ୍ କରିପାରେ।"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" କନଫିଗରେଶନ୍‍ ପଢ଼ିବା ତଥା ଲେଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ଅନୁମତି ବ୍ୟବହାର ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ଏକ ଆପ୍ ପାଇଁ ଅନୁମତିର ବ୍ୟବହାର ଆରମ୍ଭ କରିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାଧାରଣ ଆପ୍‌ଗୁଡ଼ିକ ପାଇଁ ଏହା ଆବଶ୍ୟକ ନୁହେଁ।"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ଅନୁମତି ନିଷ୍ପତ୍ତିଗୁଡ଼ିକ ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ଅନୁମତି ନିଷ୍ପତ୍ତିଗୁଡ଼ିକର ସମୀକ୍ଷା କରିବାକୁ ସ୍କ୍ରିନ ଚାଲୁ କରିବା ପାଇଁ ହୋଲଡରଙ୍କୁ ଅନୁମତି ଦିଏ। ସାଧାରଣ ଆପଗୁଡ଼ିକ ପାଇଁ ଏହା କେବେ ବି ଆବଶ୍ୟକ ହେବ ନାହିଁ।"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ଆପର ଫିଚରଗୁଡ଼ିକୁ ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"କୌଣସି ଆପ ପାଇଁ ଫିଚରଗୁଡ଼ିକ ବିଷୟରେ ସୂଚନା ଦେଖିବା ଆରମ୍ଭ କରିବାକୁ ହୋଲଡରଙ୍କୁ ଅନୁମତି ଦିଏ।"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ଏକ ଉଚ୍ଚ ନମୁନାକରଣ ରେଟରେ ସେନ୍ସର୍ ଡାଟାକୁ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"କଷ୍ଟମ୍ ଆପ୍ ବିଜ୍ଞପ୍ତି"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g>ରେ ଏକ ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବା ପାଇଁ <xliff:g id="ACCOUNT">%2$s</xliff:g>କୁ (ପୂର୍ବରୁ ଏହି ଆକାଉଣ୍ଟ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ନାମରେ ଅଛି) ଅନୁମତି ଦେବେ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g>ରେ ଏକ ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବା ପାଇଁ <xliff:g id="ACCOUNT">%2$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"ନିରୀକ୍ଷିତ ଉପଯୋଗକର୍ତ୍ତା ଯୋଗ କରନ୍ତୁ"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ଏକ ଭାଷା ଯୋଗ କରନ୍ତୁ"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"ପସନ୍ଦର ଅଞ୍ଚଳ"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"ଭାଷାର ନାମ ଟାଇପ୍‍ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index c628f1f..7a3bc33 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -174,7 +174,7 @@
     <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਬੇਨਤੀਆਂ ਦੀ ਪ੍ਰਕਿਰਿਆ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g> ਲਈ ਸਾਈਨਇਨ ਅਸ਼ੁੱਧੀ"</string>
     <string name="contentServiceSync" msgid="2341041749565687871">"ਸਿੰਕ ਕਰੋ"</string>
-    <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"ਸਮਕਾਲੀਕਿਰਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+    <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"ਸਿੰਕ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"ਬਹੁਤ ਸਾਰੀਆਂ <xliff:g id="CONTENT_TYPE">%s</xliff:g> ਨੂੰ ਮਿਟਾਉਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਗਈ।"</string>
     <string name="low_memory" product="tablet" msgid="5557552311566179924">"ਟੈਬਲੈੱਟ ਸਟੋਰੇਜ ਪੂਰੀ ਭਰੀ ਹੈ। ਜਗ੍ਹਾ ਖਾਲੀ ਕਰਨ ਲਈ ਕੁਝ ਫ਼ਾਈਲਾਂ ਮਿਟਾਓ।"</string>
     <string name="low_memory" product="watch" msgid="3479447988234030194">"ਘੜੀ ਸਟੋਰੇਜ ਪੂਰੀ ਭਰੀ ਹੈ। ਜਗ੍ਹਾ ਖਾਲੀ ਕਰਨ ਲਈ ਕੁਝ ਫ਼ਾਈਲਾਂ ਮਿਟਾਓ।"</string>
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ ਦਾ ਕਾਲ ਲੌਗ ਸੋਧਣ ਦਿੰਦੀ ਹੈ, ਇਸ ਵਿੱਚ ਇਨਕਮਿੰਗ ਅਤੇ ਆਊਟਗੋਇੰਗ ਕਾਲਾਂ ਬਾਰੇ ਡਾਟਾ ਵੀ ਸ਼ਾਮਲ ਹੈ। ਭੈੜੀਆਂ ਐਪਾਂ ਤੁਹਾਡੀ ਜਾਣਕਾਰੀ ਤੋਂ ਬਿਨਾਂ ਕਾਲ ਲੌਗ ਡਾਟਾ ਮਿਟਾ ਜਾਂ ਸੋਧ ਸਕਦੀਆਂ ਹਨ।"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦਾ ਕਾਲ ਲੌਗ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਇਨਕਮਿੰਗ ਅਤੇ ਆਊਟਗੋਇੰਗ ਕਾਲਾਂ ਬਾਰੇ ਡਾਟਾ ਸਮੇਤ। ਖਰਾਬ ਐਪਾਂ ਇਸਦੀ ਵਰਤੋਂ ਤੁਹਾਡੇ ਕਾਲ ਲੌਗ ਨੂੰ ਮਿਟਾਉਣ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਲਈ ਕਰ ਸਕਦੀਆਂ ਹਨ।"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"ਸਰੀਰ ਸੰਵੇਦਕਾਂ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (ਜਿਵੇਂ ਦਿਲ ਦੀ ਧੜਕਣ ਦੇ ਨਿਰੀਖਕ)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"ਐਪ ਨੂੰ ਉਹਨਾਂ ਸੰਵੇਦਕਾਂ ਦੇ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜੋ ਤੁਹਾਡੀ ਸਰੀਰਕ ਸਥਿਤੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦੇ ਹਨ, ਜਿਵੇਂ ਤੁਹਾਡੇ ਦਿਲ ਦੀ ਧੜਕਣ।"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"ਸਰੀਰ ਸੰਬੰਧੀ ਸੈਂਸਰਾਂ ਜਿਵੇਂ ਕਿ ਦਿਲ ਦੀ ਧੜਕਣ, ਤਾਪਮਾਨ, ਖੂਨ ਵਿੱਚ ਮੌਜੂਦ ਆਕਸੀਜਨ ਦੀ ਫ਼ੀਸਦ ਆਦਿ ਤੋਂ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕਰੋ।"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਰਹਿੰਦੇ ਹੋਏ (ਜਿਵੇਂ ਕਿ ਦਿਲ ਦੀ ਧੜਕਣ ਮਾਪਣ ਵਾਲੇ ਮਾਨੀਟਰ) ਸਰੀਰ ਸੰਬੰਧੀ ਸੈਂਸਰਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਰਹਿੰਦੇ ਹੋਏ ਸਰੀਰ ਸੰਬੰਧੀ ਸੈਂਸਰਾਂ ਜਿਵੇਂ ਕਿ ਦਿਲ ਦੀ ਧੜਕਣ, ਤਾਪਮਾਨ, ਖੂਨ ਵਿੱਚ ਮੌਜੂਦ ਆਕਸੀਜਨ ਦੀ ਫ਼ੀਸਦ ਆਦਿ ਤੋਂ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕਰੋ।"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"ਕੈਲੰਡਰ ਵਰਤਾਰਿਆਂ ਅਤੇ ਵੇਰਵਿਆਂ ਨੂੰ ਪੜ੍ਹੋ"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ \'ਤੇ ਸਟੋਰ ਕੀਤੇ ਸਾਰੇ ਕੈਲੰਡਰ ਇਵੈਂਟਾਂ ਨੂੰ ਪੜ੍ਹ ਸਕਦੀ ਹੈ ਅਤੇ ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਡਾਟੇ ਨੂੰ ਸਾਂਝਾ ਜਾਂ ਰੱਖਿਅਤ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ਇਹ ਐਪ ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ \'ਤੇ ਸਟੋਰ ਕੀਤੇ ਸਾਰੇ ਕੈਲੰਡਰ ਇਵੈਂਟਾਂ ਨੂੰ ਪੜ੍ਹ ਸਕਦੀ ਹੈ ਅਤੇ ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਡਾਟੇ ਨੂੰ ਸਾਂਝਾ ਜਾਂ ਰੱਖਿਅਤ ਕਰ ਸਕਦੀ ਹੈ।"</string>
@@ -674,11 +676,11 @@
     <string name="face_error_vendor_unknown" msgid="7387005932083302070">"ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="face_icon_content_description" msgid="465030547475916280">"ਚਿਹਰਾ ਪ੍ਰਤੀਕ"</string>
     <string name="permlab_readSyncSettings" msgid="6250532864893156277">"ਸਿੰਕ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹੋ"</string>
-    <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ਐਪ ਨੂੰ ਕਿਸੇ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਹ ਪਤਾ ਕਰ ਸਕਦਾ ਹੈ ਕਿ People ਐਪ ਦਾ ਕਿਸੇ ਖਾਤੇ ਨਾਲ ਸਮਕਾਲੀਕਿਰਤ ਕੀਤਾ ਗਿਆ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
-    <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਅਤੇ ਬੰਦ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
-    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ਐਪ ਨੂੰ ਇੱਕ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸੈਟਿੰਗਾਂ ਸੋਧਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸਦੀ ਵਰਤੋਂ ਕਿਸੇ ਖਾਤੇ ਨਾਲ People ਐਪ ਦਾ ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਕਰਨ ਲਈ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ਐਪ ਨੂੰ ਕਿਸੇ ਖਾਤੇ ਲਈ ਸਿੰਕ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਹ ਪਤਾ ਕਰ ਸਕਦਾ ਹੈ ਕਿ People ਐਪ ਨੂੰ ਕਿਸੇ ਖਾਤੇ ਨਾਲ ਸਿੰਕ ਕੀਤਾ ਗਿਆ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
+    <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"ਸਿੰਕ ਚਾਲੂ ਅਤੇ ਬੰਦ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
+    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ਐਪ ਨੂੰ ਇੱਕ ਖਾਤੇ ਲਈ ਸਿੰਕ ਸੈਟਿੰਗਾਂ ਸੋਧਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸਦੀ ਵਰਤੋਂ ਕਿਸੇ ਖਾਤੇ ਨਾਲ People ਐਪ ਦਾ ਸਿੰਕ ਚਾਲੂ ਕਰਨ ਲਈ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_readSyncStats" msgid="3747407238320105332">"ਸਿੰਕ ਅੰਕੜੇ ਪੜ੍ਹੋ"</string>
-    <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ਐਪ ਨੂੰ ਇੱਕ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸਥਿਤੀ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ, ਇਸ ਵਿੱਚ ਸਮਕਾਲੀਕਰਨ ਵਰਤਾਰਿਆਂ ਦਾ ਇਤਿਹਾਸ ਅਤੇ ਕਿੰਨੇ ਡਾਟਾ ਦਾ ਸਮਕਾਲੀਕਿਰਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਵੀ ਸ਼ਾਮਲ ਹੈ।"</string>
+    <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ਐਪ ਨੂੰ ਇੱਕ ਖਾਤੇ ਲਈ ਸਿੰਕ ਸਥਿਤੀ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ, ਇਸ ਵਿੱਚ ਸਿੰਕ ਇਵੈਂਟਾਂ ਦਾ ਇਤਿਹਾਸ ਅਤੇ ਕਿੰਨਾ ਡਾਟਾ ਸਿੰਕ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਇਹ ਵੀ ਸ਼ਾਮਲ ਹੈ।"</string>
     <string name="permlab_sdcardRead" msgid="5791467020950064920">"ਸਮੱਗਰੀਆਂ ਪੜ੍ਹੋ"</string>
     <string name="permdesc_sdcardRead" msgid="6872973242228240382">"ਐਪ ਨੂੰ ਸਮੱਗਰੀਆਂ ਪੜ੍ਹਨ ਦਿੰਦੀ ਹੈ।"</string>
     <string name="permlab_sdcardWrite" msgid="4863021819671416668">"ਸਮੱਗਰੀਆਂ ਦਾ ਸੰਸ਼ੋਧਨ ਕਰੋ ਜਾਂ ਮਿਟਾਓ"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ਐਪ ਨੂੰ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਕੌਂਫਿਗਰੇਸ਼ਨ ਨੂੰ ਪੜ੍ਹਨ ਅਤੇ ਲਿਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਦੇਖਣਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ਧਾਰਕ ਨੂੰ ਕਿਸੇ ਹੋਰ ਐਪ ਲਈ ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਾਂ ਲਈ ਕਦੇ ਵੀ ਲੋੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਫ਼ੈਸਲਿਆਂ ਨੂੰ ਦੇਖਣਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਫ਼ੈਸਲਿਆਂ ਦੀ ਸਮੀਖਿਆ ਕਰਨ ਲਈ ਹੋਲਡਰ ਨੂੰ ਸਕ੍ਰੀਨ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ। ਸਧਾਰਨ ਐਪਾਂ ਲਈ ਕਦੇ ਵੀ ਲੋੜੀਂਦੀ ਨਹੀਂ ਹੋਵੇਗੀ।"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ਐਪ ਦੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਦੇਖਣਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ਇਸ ਨਾਲ ਹੋਲਡਰ ਨੂੰ ਕਿਸੇ ਐਪ ਦੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਦੇਖਣ ਦੀ ਆਗਿਆ ਮਿਲਦੀ ਹੈ।"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ਉੱਚ ਸੈਂਪਲਿੰਗ ਰੇਟ \'ਤੇ ਸੈਂਸਰ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6689a1a..bbc1867 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Pozwala aplikacji modyfikować rejestr połączeń na urządzeniu z Androidem TV, w tym dane o połączeniach przychodzących i wychodzących. Złośliwe aplikacje mogą wykorzystać tę możliwość, by wykasować lub zmodyfikować rejestr połączeń."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Zezwala aplikacji na modyfikowanie rejestru połączeń telefonu, w tym danych o połączeniach przychodzących i wychodzących. Złośliwe aplikacje mogą wykorzystać tę możliwość, by wyczyścić lub zmodyfikować rejestr połączeń."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"dostęp do czujników ciała (np. monitorujących tętno)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Pozwala aplikacji na dostęp do danych z czujników, które monitorują Twój stan fizyczny (np. tętno)."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Dostęp do danych pochodzących z czujników na ciele, takich jak tętno, temperatura, procent tlenu we krwi itd."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"dostęp do czujników na ciele (np. monitorujących tętno) podczas pracy w tle"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Dostęp do danych pochodzących z czujników na ciele, takich jak tętno, temperatura, procent tlenu we krwi itd., gdy aplikacja pracuje w tle."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Odczytywanie wydarzeń i informacji z kalendarza"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ta aplikacja może odczytywać wszystkie zapisane na tablecie wydarzenia z kalendarza i udostępniać oraz zapisywać dane kalendarza."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ta aplikacja może odczytywać wszystkie wydarzenia z kalendarza zapisane na urządzeniu z Androidem TV oraz udostępniać i zapisywać dane z kalendarza."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Pozwala aplikacji na odczyt i zmianę konfiguracji trybu Nie przeszkadzać."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rozpocząć wyświetlanie użycia uprawnień"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umożliwia rozpoczęcie korzystania z uprawnienia dotyczącego danej aplikacji jego posiadaczowi. Zwykłe aplikacje nie powinny potrzebować tego uprawnienia."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"rozpoczęcie wyświetlania decyzji dotyczących uprawnień"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Umożliwia posiadaczowi uruchomienie ekranu w celu przeglądania decyzji dotyczących uprawnień. Zwykłe aplikacje nie powinny tego potrzebować."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"rozpoczęcie wyświetlania funkcji aplikacji"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Umożliwia posiadaczowi rozpoczęcie przeglądania informacji o funkcjach aplikacji."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostęp do danych czujnika z wysoką częstotliwością"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Niestandardowe powiadomienie z aplikacji"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Zezwolić aplikacji <xliff:g id="APP">%1$s</xliff:g> na utworzenie nowego użytkownika dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g> (użytkownik dla tego konta już istnieje)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Zezwolić aplikacji <xliff:g id="APP">%1$s</xliff:g> na utworzenie nowego użytkownika dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Dodaj nadzorowanego użytkownika"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Dodaj język"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Ustawienie regionu"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Wpisz nazwę języka"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index e2bd6c5..3835310 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que o app modifique o registro de chamadas do seu dispositivo Android TV, incluindo dados sobre chamadas recebidas e realizadas. Apps maliciosos podem usar essa permissão para apagar ou modificar seu registro de chamadas."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite que o app modifique o registro de chamadas de seu telefone, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"acessar sensores corporais (como monitores de frequência cardíaca)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite que o app acesse dados de sensores que monitoram sua condição física, como a frequência cardíaca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acesso a dados dos sensores corporais, como frequência cardíaca, temperatura, porcentagem de oxigênio no sangue etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"acessar sensores corporais (como monitores de frequência cardíaca) em segundo plano"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acesso a dados dos sensores corporais, como frequência cardíaca, temperatura, porcentagem de oxigênio no sangue etc. em segundo plano."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Ler detalhes e eventos da agenda"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Este app pode ler todos os eventos da agenda armazenados no seu tablet e compartilhar ou salvar os dados da sua agenda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Este app pode ler todos os eventos da agenda armazenados no seu dispositivo Android TV e compartilhar ou salvar os dados da sua agenda."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"decisões de permissão da visualização inicial"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Autoriza o detentor a iniciar a tela para revisar as decisões de permissão. Não deve ser necessário para apps normais."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ver recursos do app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o sistema comece a ver as informações de recursos de um app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1daf451..c2df19a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite à app modificar o registo de chamadas do seu dispositivo Android TV, incluindo os dados sobre as chamadas recebidas e efetuadas. As aplicações maliciosas podem utilizar esta funcionalidade para apagar ou modificar o seu registo de chamadas."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite à app modificar o registo de chamadas do telemóvel, incluindo os dados sobre as chamadas recebidas e efetuadas. As aplicações maliciosas podem utilizar esta funcionalidade para apagar ou modificar o seu registo de chamadas."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"aceder a sensores corporais (como monitores do ritmo cardíaco)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite que a app aceda a dados de sensores que monitorizam a sua condição física, como o ritmo cardíaco."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acesso a dados dos sensores de corpo, como o ritmo cardíaco, a temperatura, a percentagem de oxigénio no sangue, etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"aceder a sensores de corpo (como monitores do ritmo cardíaco) em segundo plano"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acesso a dados dos sensores de corpo, como o ritmo cardíaco, a temperatura, a percentagem de oxigénio no sangue, etc., em segundo plano."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Ler detalhes e eventos do calendário"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Esta app pode ler todos os eventos do calendário armazenados no seu tablet e partilhar ou guardar os dados do calendário."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Esta app pode ler todos os eventos do calendário armazenados no seu dispositivo Android TV e partilhar ou guardar os dados do calendário."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite à app ler e alterar a configuração de Não incomodar"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar utilização da autorização de visualização"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o titular inicie a utilização de autorizações para uma app. Nunca deverá ser necessário para aplicações normais."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"começar a ver as decisões de autorização"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite ao titular iniciar o ecrã para rever as decisões de autorização. Nunca deverá ser preciso para apps normais."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"começar a ver as funcionalidades da app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o titular comece a ver as informações das funcionalidades de uma app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aceder aos dados de sensores a uma taxa de amostragem elevada"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificação de app personalizada"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Permitir que a app <xliff:g id="APP">%1$s</xliff:g> crie um novo utilizador com a conta <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um utilizador com esta conta)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Permitir que a app <xliff:g id="APP">%1$s</xliff:g> crie um novo utilizador com a conta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Adicionar utilizador supervisionado"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Adicionar um idioma"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferência de região"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Intr. nome do idioma"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index e2bd6c5..3835310 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que o app modifique o registro de chamadas do seu dispositivo Android TV, incluindo dados sobre chamadas recebidas e realizadas. Apps maliciosos podem usar essa permissão para apagar ou modificar seu registro de chamadas."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite que o app modifique o registro de chamadas de seu telefone, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"acessar sensores corporais (como monitores de frequência cardíaca)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite que o app acesse dados de sensores que monitoram sua condição física, como a frequência cardíaca."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acesso a dados dos sensores corporais, como frequência cardíaca, temperatura, porcentagem de oxigênio no sangue etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"acessar sensores corporais (como monitores de frequência cardíaca) em segundo plano"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acesso a dados dos sensores corporais, como frequência cardíaca, temperatura, porcentagem de oxigênio no sangue etc. em segundo plano."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Ler detalhes e eventos da agenda"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Este app pode ler todos os eventos da agenda armazenados no seu tablet e compartilhar ou salvar os dados da sua agenda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Este app pode ler todos os eventos da agenda armazenados no seu dispositivo Android TV e compartilhar ou salvar os dados da sua agenda."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"decisões de permissão da visualização inicial"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Autoriza o detentor a iniciar a tela para revisar as decisões de permissão. Não deve ser necessário para apps normais."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ver recursos do app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o sistema comece a ver as informações de recursos de um app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 035d1eb..4469a67 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -430,7 +430,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite aplicației să modifice jurnalul de apeluri al dispozitivului Android TV, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite aplicației să modifice jurnalul de apeluri al telefonului dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"să acceseze senzorii corporali (cum ar fi monitoarele cardiace)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Permite aplicației să acceseze date de la senzorii care vă monitorizează starea fizică, cum ar fi ritmul cardiac."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Acces la date de la senzori corporali, cum ar fi senzorii pentru puls, temperatură, procentul de oxigen din sânge etc."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"Acces la senzorii corporali (cum ar fi monitoarele cardiace) din fundal"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Acces din fundal la date de la senzori corporali, cum ar fi senzorii pentru puls, temperatură, procentul de oxigen din sânge etc."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"să citească evenimentele din calendar și detaliile"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Această aplicație poate să citească toate evenimentele din calendar stocate pe tabletă și să trimită sau să salveze datele din calendar."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Această aplicație poate să citească toate evenimentele din calendar stocate pe dispozitivul Android TV și să trimită sau să salveze datele din calendar."</string>
@@ -734,6 +736,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"să înceapă să examineze deciziile privind permisiunile"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite proprietarului să deschidă ecranul pentru a examina deciziile privind permisiunile. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"începeți să vedeți funcțiile aplicației"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite proprietarului să înceapă să vadă informațiile despre funcții pentru o aplicație."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"să acceseze date de la senzori la o rată de eșantionare mare"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1b64a3b..6b874b9 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Приложение сможет изменять список вызовов и данные о входящих и исходящих звонках на устройстве Android TV. Вредоносные программы смогут воспользоваться этим для удаления или изменения информации о звонках."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Приложение сможет вносить изменения в список вызовов телефона и данные о входящих и исходящих звонках. Вредоносные приложения смогут воспользоваться этим для удаления или изменения информации о звонках."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"Датчики (например, пульсометр)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Приложение сможет получить доступ к данным датчиков, размещенных на теле, например измеряющих частоту сердцебиения."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Доступ к данным датчиков на теле (например, пульсу, температуре, уровню кислорода в крови)."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"Доступ к датчикам на теле (например, пульсометру) в фоновом режиме"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Доступ к данным датчиков на теле (например, пульсу, температуре, уровню кислорода в крови) в фоновом режиме."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Чтение мероприятий и данных"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Приложение может считывать, отправлять и сохранять информацию о мероприятиях в календаре планшета."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Приложение может считывать, отправлять и сохранять информацию о мероприятиях в календаре устройства Android TV."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Открывает приложению доступ к настройкам режима \"Не беспокоить\" и позволяет изменять их."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Просмотр данных об используемых разрешениях"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Приложение получит доступ к данным об используемых разрешениях. Это разрешение не требуется обычным приложениям."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"Просмотр действий с разрешениями"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Позволяет просматривать действия с разрешениями. Не используется обычными приложениями."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"Просмотр функций приложения"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Позволяет просматривать информацию о функциях приложения."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Доступ к данным датчиков при высокой частоте дискретизации"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Уведомление пользовательского приложения"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя с аккаунтом <xliff:g id="ACCOUNT">%2$s</xliff:g> (пользователь с этим аккаунтом уже существует)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя с аккаунтом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Создать контролируемый профиль"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Добавить язык"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Региональные настройки"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Введите название языка"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index cf7fc87..3443681 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ලැබෙන ඇමතුම් සහ පිටවන ඇමතුම් දත්ත ඇතුළත්ව ඔබගේ Android TV උපාංගයෙහි ඇමතුම් ලොගය වෙනස් කිරීමට යෙදුමට ඉඩ දෙයි. ඔබගේ ඇමතුම් ලොගය මැකීමට හෝ වෙනස් කිරීමට අනිෂ්ට යෙදුම් මෙය භාවිත කළ හැකිය."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"පැමිණෙන සහ පිටවෙන ඇමතුම් දත්ත ඇතුළුව ඔබගේ දුරකථනයේ ඇමතුම් ලොගය වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. ඔබගේ ඇමතුම් ලොගය මැකීමට හෝ වෙනස් කිරීමට අනිෂ්ට යෙදුම් මෙය භාවිත කල හැක."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"දේහ සංවේදකවලට (හෘද ස්පන්දන වේග මොනිටර වැනි) පිවිසීම"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"හෘද ස්පන්දන වේගය වැනි ඔබගේ ශාරීරික තත්ත්වය නිරීක්ෂණය කරන සංවේදක වලින් දත්ත ලබාගැනීමට යෙදුමට ඉඩ දෙන්න."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"හෘද ස්පන්දන වේගය, උෂ්ණත්වය, රුධිර ඔක්සිජන් ප්‍රතිශතය වැනි ශරීර සංවේදකවලින් දත්ත වෙත ප්‍රවේශය."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"පසුබිමේ සිටියදී ශරීර සංවේදක (හෘද ස්පන්දන වේග මොනිටර වැනි) වෙත ප්‍රවේශය"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"පසුබිමේ සිටියදී, හෘද ස්පන්දන වේගය, උෂ්ණත්වය, රුධිර ඔක්සිජන් ප්‍රතිශතය වැනි ශරීර සංවේදකවලින් දත්ත වෙත ප්‍රවේශය."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"දින දර්ශන සිදුවීම් හා විස්තර කියවන්න"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"මෙම යෙදුමට ඔබගේ ටැබ්ලට් පරිගණකය මත ගබඩා වී ඇති සියලු දින දර්ශන කියවීමට සහ සහ ඔබගේ දින දර්ශන දත්ත බෙදා ගැනීමට සහ සුරැකීමට හැකිය."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"මෙම යෙදුමට ඔබගේ Android TV මත ගබඩා කර ඇති සියලු දින දර්ශන සිදුවීම් කියවීමට සහ ඔබගේ දින දර්ශන දත්ත බෙදා ගැනීමට හෝ සුරැකීමට හැකිය."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"බාධා නොකරන්න වින්‍යාස කිරීම කියවීමට සහ ලිවීමට යෙදුමට ඉඩ දෙයි."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"අවසර භාවිතය බැලීමට ආරම්භ කරන්න"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"තබා සිටින්නාට යෙදුමක් සඳහා අවසර භාවිතය ආරම්භ කිරීමට ඉඩ දෙයි. සාමාන්‍ය යෙදුම් සඳහා කිසි විටෙක අවශ්‍ය නොවිය යුතු ය."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"නව අවසර තීරණ ආරම්භ කරන්න"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"අවසර තීරණ සමාලෝචනය කිරීමට තිරය ආරම්භ කිරීමට දරන්නාට ඉඩ දෙයි. සාමාන්‍ය යෙදුම් සඳහා කිසිදා අවශ්‍ය නොවිය යුතුය."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"යෙදුම බලන්න විශේෂාංග ආරම්භ කරන්න"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"යෙදුමක් සඳහා විශේෂාංග තොරතුරු බැලීම ආරම්භ කිරීමට දරන්නාට ඉඩ දෙන්න."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ඉහළ නියැදි කිරීමේ වේගයකින් සංවේදක දත්ත වෙත පිවිසෙන්න"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"අභිරුචි යෙදුම් දැනුම් දීම"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g> හට <xliff:g id="ACCOUNT">%2$s</xliff:g> සමගින් නව පරිශීලකයෙකු සෑදීමට ඉඩ දෙන්නද (මෙම ගිණුම සහිත පරිශීලකයෙකු දැනටමත් සිටී) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g> හට <xliff:g id="ACCOUNT">%2$s</xliff:g> සමගින් නව පරිශීලකයෙකු සෑදීමට ඉඩ දෙන්නද ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"අධීක්ෂණය කළ පරිශීලක එක් කරන්න"</string>
     <string name="language_selection_title" msgid="52674936078683285">"භාෂාවක් එක් කරන්න"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"ප්‍රදේශ මනාපය"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"භාෂා නම ටයිප් කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c6423dc..517ce6c 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Umožňuje aplikácii upravovať denník hovorov zariadenia Android TV vrátane údajov o prichádzajúcich a odchádzajúcich hovoroch. Škodlivé aplikácie to môžu zneužiť na vymazanie alebo úpravu denníkov hovorov."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Umožňuje aplikácii upravovať zoznam hovorov vo vašom telefóne vrátane údajov o prichádzajúcich a odchádzajúcich hovoroch. Škodlivé aplikácie to môžu zneužiť na vymazanie alebo úpravu vášho zoznamu hovorov."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"prístup k telovým senzorom (ako sú snímače tepu)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Umožňuje aplikácii získať prístup k údajom senzorov monitorujúcich vašu fyzickú kondíciu (napríklad pulz)."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Prístup k údajom z telových senzorov, ako sú pulz, teplota, saturácia atď."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"prístup k telovým senzorom (ako sú snímače pulzu) na pozadí"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Prístup k údajom z telových senzorov, ako sú pulz, teplota, saturácia atď., na pozadí."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Čítanie udalostí kalendára a podrobností"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Táto aplikácia môže čítať všetky udalosti kalendára uložené vo vašom tablete a zdieľať alebo ukladať dáta kalendára."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Táto aplikácia môže čítať všetky udalosti kalendára uložené vo vašom zariadení Android TV a zdieľať alebo ukladať údaje kalendára."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikácii čítať a zapisovať konfiguráciu režimu bez vyrušení."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"spustenie používania povolenia na zobrazenie"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje držiteľovi spustiť používanie povolenia aplikáciou. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"spustenie zobrazenia rozhodnutí o povolení"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Umožňuje držiteľovi spustiť obrazovku a skontrolovať rozhodnutia o povoleniach. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"zobrazenie informácií o funkciách aplikácie"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Umožňuje držiteľovi zobraziť informácie o funkciách aplikácie."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"prístup k dátam senzorom s vysokou vzorkovacou frekvenciou"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Vlastné upozornenie na aplikáciu"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Chcete povoliť aplikácii <xliff:g id="APP">%1$s</xliff:g> vytvoriť nového používateľa pomocou účtu <xliff:g id="ACCOUNT">%2$s</xliff:g> (používateľ s týmto účtom už existuje)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Chcete povoliť aplikácii <xliff:g id="APP">%1$s</xliff:g> vytvoriť nového používateľa pomocou účtu <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Pridať kontrolovaného používateľa"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Pridať jazyk"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferovaný región"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Zadajte názov jazyka"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 1e8f264..7bcc257 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Aplikaciji dovoljuje spreminjanje dnevnika klicev v napravi Android TV, vključno s podatki o dohodnih in odhodnih klicih. Zlonamerne aplikacije lahko tako izbrišejo ali spreminjajo vaš dnevnik klicev."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Aplikaciji dovoli spreminjanje dnevnika klicev v telefonu, vključno s podatki o dohodnih in odhodnih klicih. Zlonamerne aplikacije lahko tako izbrišejo ali spreminjajo vaš dnevnik klicev."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"dostop do tipal telesnih funkcij (npr. merilnikov srčnega utripa)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Aplikaciji omogoča dostop do podatkov tipal, ki nadzirajo vaše fizično stanje, med drugim vaš srčni utrip."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Dostop do podatkov iz tipal telesnih funkcij, kot so srčni utrip, temperatura, odstotek kisika v krvi itd."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"dostop do tipal telesnih funkcij (npr. merilnika srčnega utripa) v ozadju"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Dostop do podatkov iz tipal telesnih funkcij, kot so srčni utrip, temperatura, odstotek kisika v krvi itd., med delovanjem v ozadju."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Branje dogodkov v koledarjih in podrobnosti koledarjev"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ta aplikacija lahko prebere vse dogodke v koledarju, ki so shranjeni v tabličnem računalniku, ter shrani podatke koledarja ali jih deli z drugimi."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ta aplikacija lahko prebere vse dogodke v koledarju, ki so shranjeni v napravi Android TV, ter shrani podatke koledarja ali jih deli z drugimi."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Aplikaciji omogoča branje in pisanje konfiguracije načina »ne moti«."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"začetek uporabe dovoljenja za ogledovanje"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Imetniku omogoča začetek uporabe dovoljenj za aplikacijo. Nikoli ni potrebno za navadne aplikacije."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"prikaz odločitev o dovoljenjih"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Imetniku omogoča prikaz zaslona za pregled odločitev o dovoljenjih. Tega ni treba nikoli uporabiti za navadne aplikacije."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"začetek ogledovanja funkcij aplikacije"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Imetniku omogoča začetek ogledovanja informacij o funkcijah poljubne aplikacije."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostop do podatkov tipal z večjo hitrostjo vzorčenja"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 705e309..0737100 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Lejon aplikacionin të modifikojë ditarin e telefonatave të pajisjes sate Android TV, duke përfshirë të dhëna rreth telefonatave hyrëse dhe dalëse. Aplikacionet keqdashëse mund ta përdorin këtë për të spastruar ose modifikuar evidencën tënde të telefonatave."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Lejon aplikacionin të modifikojë ditarin e telefonatave të telefonit tënd, përfshirë të dhënat rreth telefonatave hyrëse dhe dalëse. Aplikacionet keqdashëse mund ta përdorin këtë për të fshirë ose modifikuar ditarin tënd të telefonatave."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"qasu te sensorët e trupit (si monitorimet e rrahjeve të zemrës)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Lejon aplikacionin të ketë qasje në të dhënat nga ndjesorë që monitorojnë gjendjen tënde fizike, siç janë rrahjet e zemrës."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Qasje te të dhënat nga sensorët e trupit, si p.sh. rrahjet e zemrës, temperatura, përqindja e oksigjenit në gjak etj."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"qasje te sensorët e trupit (si p.sh. monitorët e rrahjeve të zemrës) ndërkohë që është në sfond"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Qasje te të dhënat nga sensorët e trupit, si p.sh. rrahjet e zemrës, temperatura, përqindja e oksigjenit në gjak etj. ndërkohë që është në sfond."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Lexo ngjarjet e kalendarit dhe detajet"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ky aplikacion mund të lexojë të gjitha ngjarjet e kalendarit të ruajtura në tabletin tënd dhe të ndajë ose të ruajë të dhënat e kalendarit."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ky aplikacion mund të lexojë të gjitha ngjarjet e kalendarit të ruajtura në pajisjen tënde Android TV dhe të ndajë ose të ruajë të dhënat e kalendarit."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lejon aplikacionin të lexojë dhe shkruajë konfigurimin e \"Mos shqetëso\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"nis përdorimin e lejes për shikimin"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lejon që mbajtësi të nisë përdorimin e lejeve për një aplikacion. Nuk duhet të nevojitet asnjëherë për aplikacionet normale."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"nisë shikimin e vendimeve për lejet"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Lejon që zotëruesi të nisë ekranin për shqyrtimin e vendimeve për lejet. Nuk duhet të nevojitet asnjëherë për aplikacionet normale."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"fillojë shikimin e veçorive të aplikacionit"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Lejon që zotëruesi të fillojë të shikojë informacionin e veçorive për një aplikacion."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"qasu te të dhënat e sensorit me një shpejtësi kampionimi më të lartë"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Njoftim i personalizuar për aplikacionin"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Të lejohet <xliff:g id="APP">%1$s</xliff:g> që të krijojë një përdorues të ri me <xliff:g id="ACCOUNT">%2$s</xliff:g> (një përdorues me këtë llogari ekziston tashmë) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Të lejohet <xliff:g id="APP">%1$s</xliff:g> që të krijojë një përdorues të ri me <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Shto përdorues të kontrolluar"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Shto një gjuhë"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Preferenca e rajonit"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Shkruaj emrin e gjuhës"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index dde0e33..e117bda 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -430,7 +430,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Дозвољава апликацији да мења евиденцију позива на Android TV уређају, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе за брисање или мењање евиденције позива."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Дозвољава апликацији да мења евиденцију позива на телефону, укључујући податке о долазним и одлазним позивима. Злонамерне апликације могу ово да користе да би брисале или мењале евиденцију позива."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"приступ сензорима на телу (попут монитора за праћење пулса)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Дозвољава апликацији да приступа подацима са сензора који надгледају физичку кондицију, као што је број откуцаја срца."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Приступ подацима сензора за тело, попут пулса, температуре, проценат кисеоника у крви итд."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"приступ сензорима на телу (нпр. монитори за праћење пулса) током рада у позадини"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Приступ подацима сензора за тело, попут пулса, температуре, проценат кисеоника у крви итд, током рада у позадини."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Читање догађаја и података из календара"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ова апликација може да чита све догађаје из календара које чувате на таблету, као и да дели или чува податке из календара."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ова апликација може да чита све догађаје из календара које чувате на Android TV уређају, као и да дели или чува податке из календара."</string>
@@ -734,6 +736,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозвољава апликацији да чита и уписује конфигурацију подешавања Не узнемиравај."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"почетак коришћења дозволе за преглед"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозвољава власнику да започне коришћење дозволе за апликацију. Никада не би требало да буде потребна за уобичајене апликације."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"покретање прегледа одлука о дозволама"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Дозвољава власнику да покрене екран за проверу одлука о дозволама. Никада не би требало да буде потребно за обичне апликације."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"покретање приказа функција апликације"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дозвољава носиоцу дозволе да започне прегледање информација о функцијама апликације."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"приступ подацима сензора при великој брзини узорковања"</string>
@@ -1546,7 +1550,7 @@
     <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Веза са увек укљученим VPN-ом је прекинута"</string>
     <string name="vpn_lockdown_error" msgid="4453048646854247947">"Повезивање на стално укључени VPN није успело"</string>
     <string name="vpn_lockdown_config" msgid="8331697329868252169">"Промените подешавања VPN-а"</string>
-    <string name="upload_file" msgid="8651942222301634271">"Одабери датотеку"</string>
+    <string name="upload_file" msgid="8651942222301634271">"Одабери фајл"</string>
     <string name="no_file_chosen" msgid="4146295695162318057">"Није изабрана ниједна датотека"</string>
     <string name="reset" msgid="3865826612628171429">"Ресетуј"</string>
     <string name="submit" msgid="862795280643405865">"Пошаљи"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2003e64..90de245 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Tillåter att appen gör ändringar i Android TV-enhetens samtalshistorik, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att radera eller ändra samtalshistoriken."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"få åtkomst till kroppssensorer (till exempel pulsmätare)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Åtkomst till data från kroppssensorer, t.ex. puls, kroppstemperatur, procentandel syre i blodet med mera."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"åtkomst till kroppssensorer (t.ex. pulsmätare) i bakgrunden"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Åtkomst till data från kroppssensorer, t.ex. puls, kroppstemperatur, procentandel syre i blodet med mera, i bakgrunden."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Läsa kalenderhändelser och kalenderuppgifter"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Appen kan läsa alla kalenderhändelser som sparats på surfplattan och dela eller spara uppgifter i din kalender."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Appen kan läsa alla kalenderhändelser som sparats på Android TV-enheten och dela eller spara uppgifter i din kalender."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ger appen läs- och skrivbehörighet till konfigurationen för Stör ej."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"börja visa behörighetsanvändningen"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Gör att innehavaren kan öppna behörighetsanvändning för en app. Ska inte behövas för vanliga appar."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"börja visa behörighetsbeslut"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Tillåter att innehavaren öppnar skärmen för att granska behörighetsbeslut. Detta ska inte behövas för vanliga appar."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"börja visa appfunktioner"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Tillåter att innehavaren börjar visa information om funktioner för en app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"åtkomst till sensordata med en hög samplingsfrekvens"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index f955203..ec24102 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Huruhusu programu irekebishe kumbukumbu za simu ya kifaa chako cha Android TV, ikiwa ni pamoja na data kuhusu simu zinazoingia na simu unazopiga. Huenda programu hasidi zikatumia ruhusa hii ili kufuta au kurekebisha kumbukumbu zako za simu."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Huruhusu programu kurekebisha rajisi ya simu yako, ikiwa ni pamoja na simu zinazoingia na kutoka. Huenda programu hasidi zikatumia hii ili kufuta au kurekebisha rajisi ya simu yako."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"fikia vitambua shughuli za mwili (kama vifuatiliaji vya mapigo ya moyo)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Huruhusu programu kufikia data kutoka vihisi vinavyofuatilia hali yako ya kimwili, kama vile mapigo ya moyo."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Kufikia data katika vitambuzi vya shughuli za mwili kama vile mapigo ya moyo, halijoto, asilimia ya oksijeni kwenye damu n.k."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"kufikia vitambuzi vya shughuli za mwili (kama vile vifuatiliaji vya mapigo ya moyo) inapotumika chinichini"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Kufikia data katika vitambuzi vya shughuli za mwili kama vile mapigo ya moyo, halijoto, asilimia ya oksijeni kwenye damu n.k. inapotumika chinichini."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Soma matukio na maelezo ya kalenda"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Programu hii inaweza kusoma matukio yote ya kalenda yaliyohifadhiwa kwenye kompyuta yako kibao na kushiriki au kuhifadhi data yako ya kalenda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Programu hii inaweza kusoma matukio yote ya kalenda yaliyohifadhiwa kwenye kifaa chako cha Android TV na kushiriki au kuhifadhi data ya kalenda yako."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Inaruhusu programu kusoma na kuandika usanidi wa kipengee cha Usinisumbue."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"anzisha kipengele cha kuona matumizi ya ruhusa"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Huruhusu kishikiliaji kuanzisha matumizi ya ruhusa ya programu. Haipaswi kuhitajika kwa ajili ya programu za kawaida."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"kuanzisha uamuzi wa ruhusa za kuangalia"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Humruhusu mmiliki kuanzisha skrini ili kukagua uamuzi wa ruhusa. Haipaswi kuhitajika kwenye programu za kawaida."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"anzisha kipengele cha kuangalia vipengele vya programu"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Huruhusu mmiliki kuanza kuangalia maelezo ya vipengele vya programu."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"fikia data ya vitambuzi kwa kasi ya juu ya sampuli"</string>
diff --git a/core/res/res/values-sw360dp/dimens.xml b/core/res/res/values-sw360dp/dimens.xml
index 4c74264..00de60e 100644
--- a/core/res/res/values-sw360dp/dimens.xml
+++ b/core/res/res/values-sw360dp/dimens.xml
@@ -18,4 +18,8 @@
 -->
 <resources>
     <dimen name="chooser_grid_padding">16dp</dimen>
+
+    <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME -->
+    <dimen name="input_method_navigation_key_width">80dip</dimen>
+
 </resources>
diff --git a/core/res/res/values-sw372dp/dimens.xml b/core/res/res/values-sw372dp/dimens.xml
new file mode 100644
index 0000000..cb29a19
--- /dev/null
+++ b/core/res/res/values-sw372dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- Copied from SysUI's @dimen/nav_content_padding for the embedded nav bar in the IME -->
+    <dimen name="input_method_nav_content_padding">8dp</dimen>
+</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index e8f15fd..4c70ea3 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -41,6 +41,11 @@
     <!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
     <dimen name="keyguard_lockscreen_outerring_diameter">364dp</dimen>
 
+    <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME -->
+    <dimen name="input_method_navigation_key_width">128dp</dimen>
+    <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME -->
+    <dimen name="input_method_navigation_key_padding">25dp</dimen>
+
     <!-- Height of FaceUnlock view in keyguard -->
     <dimen name="face_unlock_height">430dip</dimen>
 
diff --git a/core/res/res/values-sw900dp/dimens.xml b/core/res/res/values-sw900dp/dimens.xml
index 11092b2..9ec4204 100644
--- a/core/res/res/values-sw900dp/dimens.xml
+++ b/core/res/res/values-sw900dp/dimens.xml
@@ -24,4 +24,12 @@
          the same as @dimen/navigation_bar_height -->
     <dimen name="navigation_bar_height_landscape">56dp</dimen>
 
+    <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME. -->
+    <dimen name="input_method_navigation_key_width">80dp</dimen>
+    <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME. -->
+    <dimen name="input_method_navigation_key_padding">0dp</dimen>
+    <!-- Copied from SysUI's @dimen/key_button_ripple_max_width for the embedded nav bar in the
+         IME. -->
+    <dimen name="input_method_nav_key_button_ripple_max_width">76dp</dimen>
+
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index a7296dc..e7ea59d 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"உள்வரும், வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உட்பட உங்கள் Android TVயின் அழைப்புப் பதிவைத் திருத்த ஆப்ஸை அனுமதிக்கும். உங்கள் அழைப்புப் பதிவை அழிக்கவோ திருத்தவோ தீங்கு விளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தக்கூடும்."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் மொபைல் அழைப்புப் பதிவைத் திருத்துவதற்குப் ஆப்ஸை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தலாம்."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"உடல் உணர்விகளை (இதயத் துடிப்பு மானிட்டர்கள் போன்றவை) அணுகுதல்"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"உங்கள் இதயத்துடிப்பு விகிதம் போன்ற உங்கள் உடல்நிலையைக் கண்காணிக்கும் உணர்விகளில் இருந்து தரவை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"இதயத் துடிப்பு, வெப்பநிலை, ரத்த ஆக்ஸிஜன் சதவீதம் போன்ற உடல் சென்சார்களில் இருந்து கிடைக்கும் தரவை அணுகலாம்."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"பின்னணியில் உடல் சென்சார்களை (இதயத் துடிப்பைக் கண்காணித்தல் போன்றவை) அணுகுதல்"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"பின்னணியில் இருக்கும்போது இதயத் துடிப்பு, வெப்பநிலை, ரத்த ஆக்ஸிஜன் சதவீதம் போன்ற உடல் சென்சார்களில் இருந்து கிடைக்கும் தரவை அணுகலாம்."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"கேலெண்டர் நிகழ்வுகளையும் விவரங்களையும் படிக்கலாம்"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"இந்த ஆப்ஸ் உங்கள் டேப்லெட்டில் சேமிக்கப்பட்டுள்ள கேலெண்டர் நிகழ்வுகள் அனைத்தையும் படிக்கலாம், உங்கள் கேலெண்டர் தரவைப் பகிரலாம் அல்லது சேமிக்கலாம்."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"உங்கள் Android TVயில் சேமித்துள்ள அனைத்துக் கேலெண்டர் நிகழ்வுகளையும் இந்த ஆப்ஸால் தெரிந்துகொள்ள முடியும். அத்துடன் உங்களின் கேலெண்டர் தரவைப் பகிரவும் சேமிக்கவும் முடியும்."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"தொந்தரவு செய்ய வேண்டாம் உள்ளமைவைப் படிக்கவும் எழுதவும், ஆப்ஸை அனுமதிக்கிறது."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"அனுமதி உபயோகத்தை அணுகுதல்"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ஆப்ஸிற்கான அனுமதி உபயோகத்தை ஹோல்டருக்கு வழங்கும். இயல்பான ஆப்ஸிற்கு இது எப்போதுமே தேவைப்படாது."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"அனுமதி முடிவுகளைப் பார்க்கத் தொடங்குதல்"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"அனுமதி தொடர்பான முடிவுகளை மதிப்பாய்வு செய்ய, திரையைத் தொடங்குவதற்கான அனுமதியை ஹோல்டருக்கு வழங்கும். இயல்பான ஆப்ஸிற்கு இது எப்போதுமே தேவைப்படாது."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ஆப்ஸின் அம்சங்களைப் பார்க்கத் தொடங்குதல்"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ஆப்ஸின் அம்சங்கள் குறித்த தகவல்களைப் பார்ப்பதற்கான அனுமதியை ஹோல்டருக்கு வழங்கும்."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"அதிகளவிலான சாம்பிளிங் ரேட்டில் சென்சார் தரவை அணுகுதல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index dc21719..176d845 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"శరీర సెన్సార్‌లను (గుండె స్పందన రేటు మానిటర్‌ల వంటివి) యాక్సెస్ చేయండి"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"మీ శారీరక పరిస్థితిని అనగా మీ గుండె స్పందన రేటు వంటి వాటిని పర్యవేక్షించే సెన్సార్‌ల నుండి డేటాను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"గుండె స్పందన రేటు, ఉష్ణోగ్రత, రక్తంలో ఆక్సిజన్ శాతం మొదలైన శరీర సెన్సార్‌ల నుండి డేటాను యాక్సెస్ చేయండి."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"బ్యాక్‌గ్రౌండ్‌లో శరీర సెన్సార్‌లను (గుండె రేటు మానిటర్స్) యాక్సెస్ చేయండి"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"బ్యాక్‌గ్రౌండ్‌లో ఉన్నప్పుడు గుండె స్పందన రేటు, ఉష్ణోగ్రత, రక్తంలో ఆక్సిజన్ శాతం మొదలైన శరీర సెన్సార్‌ల నుండి డేటాకు యాక్సెస్ చేయండి."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"క్యాలెండర్ ఈవెంట్‌లు మరియు వివరాలను చదవడం"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ఈ యాప్ మీ టాబ్లెట్‌లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ఈ యాప్‌ మీ Android TV పరికరంలో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు, మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్‌నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్‌‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"వీక్షణ అనుమతి నిర్ణయాలను ప్రారంభించండి"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"అనుమతి నిర్ణయాలను రివ్యూ చేయడానికి స్క్రీన్‌ను ప్రారంభించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"యాప్ ఫీచర్‌లను చూడటాన్ని ప్రారంభించండి"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"యాప్ ఫీచర్‌ల సమాచారాన్ని చూడటాన్ని ప్రారంభించడానికి హోల్డర్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"అధిక శాంపిల్ రేటు వద్ద సెన్సార్ డేటాను యాక్సెస్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 15d7f30..30b3d0b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"อนุญาตให้แอปแก้ไขบันทึกการโทรจากอุปกรณ์ Android TV รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและสายโทรออก แอปที่เป็นอันตรายอาจใช้สิทธิ์นี้เพื่อลบหรือแก้ไขบันทึกการโทรได้"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"อนุญาตให้แอปแก้ไขบันทึกการโทรจากโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขบันทึกการโทรของคุณ"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"เข้าถึงเซ็นเซอร์ร่างกาย (เช่น ตัววัดอัตราการเต้นของหัวใจ)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"อนุญาตให้แอปเข้าถึงข้อมูลจากเซ็นเซอร์ที่ตรวจสอบสภาพทางกายภาพ เช่น อัตราการเต้นของหัวใจ"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"เข้าถึงข้อมูลจากเซ็นเซอร์ร่างกาย เช่น อัตราการเต้นของหัวใจ อุณหภูมิ เปอร์เซ็นต์ออกซิเจนในเลือด ฯลฯ"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"เข้าถึงเซ็นเซอร์ร่างกาย (เช่น เครื่องวัดอัตราการเต้นของหัวใจ) ในเบื้องหลัง"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"เข้าถึงข้อมูลจากเซ็นเซอร์ร่างกาย เช่น อัตราการเต้นของหัวใจ อุณหภูมิ เปอร์เซ็นต์ออกซิเจนในเลือด ฯลฯ ในเบื้องหลัง"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"อ่านกิจกรรมในปฏิทินและรายละเอียด"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"แอปนี้สามารถอ่านกิจกรรมทั้งหมดในปฏิทินที่เก็บไว้ในแท็บเล็ต รวมถึงแชร์หรือบันทึกข้อมูลในปฏิทินของคุณ"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"แอปนี้อ่านกิจกรรมทั้งหมดในปฏิทินที่จัดเก็บไว้ในอุปกรณ์ Android TV ได้ รวมถึงแชร์หรือบันทึกข้อมูลในปฏิทินของคุณได้ด้วย"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"อนุญาตให้แอปอ่านและเขียนการกำหนดค่าโหมดห้ามรบกวน"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"เริ่มการใช้สิทธิ์การดู"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"อนุญาตให้เจ้าของเริ่มการใช้สิทธิ์ของแอป ไม่จำเป็นสำหรับแอปทั่วไป"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"เริ่มดูสิทธิ์ที่เลือกไว้"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"อนุญาตให้แอปเริ่มหน้าจอเพื่อดูสิทธิ์ที่เลือกไว้ แอปทั่วไปไม่จำเป็นต้องใช้"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"เริ่มดูฟีเจอร์ของแอป"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"อนุญาตให้เจ้าของเริ่มดูข้อมูลฟีเจอร์สำหรับแอป"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"เข้าถึงข้อมูลเซ็นเซอร์ที่อัตราการสุ่มตัวอย่างสูง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index cb6c485..c0bbc69 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Nagbibigay-daan sa app na baguhin ang log ng tawag ng iyong Android TV device, kasama ang data tungkol sa mga papasok at papalabas na tawag. Puwede itong gamitin ng mga mapaminsalang app para burahin o baguhin ang iyong log ng tawag."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Binibigyan-daan ang app na baguhin ang log ng tawag ng iyong telepono, kabilang ang data tungkol sa mga paparating at papalabas na tawag. Maaari itong gamitin ng nakakahamak na apps upang burahin o baguhin ang iyong log ng tawag."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"i-access ang mga sensor sa katawan (tulad ng mga monitor ng bilis ng tibok ng puso)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Pinapayagan ang app na i-access ang data mula sa mga sensor na sumusubaybay sa iyong pisikal na kondisyon, tulad ng iyong heart rate."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Access sa data mula sa mga sensor ng katawan gaya ng bilis ng tibok ng puso, temperatura, porsyento ng oxygen sa dugo, atbp."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"i-access ang body sensors (tulad ng heart rate monitors) habang nasa background"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Access sa data mula sa mga sensor ng katawan gaya ng bilis ng tibok ng puso, temperatura, porsyento ng oxygen sa dugo, atbp. habang nasa background."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Magbasa ng mga event sa kalendaryo at detalye"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Mababasa ng app na ito ang lahat ng event sa kalendaryo na naka-store sa iyong tablet at maibabahagi o mase-save nito ang data ng iyong kalendaryo."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Mababasa ng app na ito ang lahat ng event sa kalendaryo na naka-store sa iyong Android TV device at maibabahagi o mase-save nito ang data ng kalendaryo mo."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Nagbibigay-daan sa app na basahin at isulat ang configuration ng Huwag Istorbohin."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"simulan ang paggamit sa pahintulot sa pagtingin"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Binibigyang-daan ang may hawak na simulan ang paggamit ng pahintulot para sa isang app. Hindi dapat kailanganin kailanman para sa mga normal na app."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"simulan ang mga desisyon sa pahintulot na tumingin"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Nagbibigay-daan sa may hawak na simulan ang screen para masuri ang mga desisyon sa pahintulot. Hindi dapat kailanman kailanganin para sa mga karaniwang app."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"simulang tingnan ang mga feature ng app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Nagbibigay-daan sa may hawak na simulang tingnan ang impormasyon ng mga feature para sa isang app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mag-access ng data ng sensor sa mataas na rate ng pag-sample"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom na notification ng app"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Payagan ang <xliff:g id="APP">%1$s</xliff:g> na gumawa ng bagong User sa <xliff:g id="ACCOUNT">%2$s</xliff:g> (mayroon nang User sa account na ito) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Payagan ang <xliff:g id="APP">%1$s</xliff:g> na gumawa ng bagong User sa <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Magdagdag ng pinapangasiwaang user"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Magdagdag ng wika"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Kagustuhan sa rehiyon"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"I-type ang wika"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index df4a960..8a86227 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Uygulamaya, Android TV cihazınızın arama günlüğünde (gelen ve giden aramalarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak arama günlüğünüzü silebilir veya değiştirebilir."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Uygulamaya telefonunuzun arama günlüğünde (gelen ve giden aramalarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak arama günlüğünüzü silebilir veya değiştirebilir."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"vücut sensörlerine erişme (nabız takip cihazları gibi)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Uygulamanın, nabzınız gibi fiziksel durumunuzu izleyen sensörlerin gönderdiği verilere erişmesine izin verir."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Nabız, vücut ısısı, kandaki oksijen yüzdesi gibi vücut sensörlerinden gelen verilere erişim."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"arka plandayken vücut sensörlerine erişim (nabız takip cihazları gibi)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Arka plandayken nabız, vücut ısısı, kandaki oksijen yüzdesi gibi vücut sensörlerinden gelen verilere erişim."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Takvim etkinlikleri ve ayrıntılarını okuma"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Bu uygulama, tabletinizde kayıtlı tüm takvim etkinliklerini okuyabilir ve takvim verilerinizi paylaşabilir ya da kaydedebilir."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Bu uygulama, Android TV cihazınızda kayıtlı tüm takvim etkinliklerini okuyabilir ve takvim verilerinizi paylaşabilir ya da kaydedebilir."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Uygulamaya, Rahatsız Etmeyin yapılandırmasını okuma ve yazma izni verir."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"izin kullanımı görüntülemeye başlama"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"İzin sahibinin bir uygulama için izin kullanımı başlatmasına olanak tanır. Normal uygulamalar için hiçbir zaman kullanılmamalıdır."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"izin kararlarını görüntülemeye başlama"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"İzin sahibinin, izin kararlarını incelemek için ekranı başlatmasına izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"uygulama özelliklerini görüntülemeye başlama"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"İzin sahibinin, bir uygulamanın özellik bilgilerini görüntülemeye başlamasına izin verir."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensör verilerine daha yüksek örnekleme hızında eriş"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Özel uygulama bildirimi"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabına sahip yeni bir Kullanıcı eklemesine izin verilsin mi (bu hesaba sahip bir kullanıcı zaten var)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabına sahip yeni bir Kullanıcı eklemesine izin verilsin mi?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Denetlenen kullanıcı ekle"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Dil ekleyin"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Bölge tercihi"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Dil adını yazın"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0539beba..f4fb575 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -433,7 +433,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Дозволяє додатку змінювати журнал викликів пристрою, зокрема дані про вхідні та вихідні дзвінки. Шкідливі додатки можуть стирати або змінювати журнал викликів."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Дозволяє програмі змінювати журнал викликів вашого телефону, включно з даними про вхідні та вихідні дзвінки. Шкідливі програми можуть використовувати це для стирання або зміни вашого журналу викликів."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"отримувати дані з датчиків на тілі (наприклад, з пульсометра)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Додаток має доступ до даних із датчиків, які відстежують фізичний стан, зокрема пульс."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Доступ до даних із датчиків на тілі, наприклад про пульс, температуру, вміст кисню в крові тощо."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"отримувати дані з датчиків на тілі (наприклад, з пульсометра) у фоновому режимі"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Доступ до даних із датчиків на тілі, наприклад про пульс, температуру, вміст кисню в крові тощо (у фоновому режимі)."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Переглядати події календаря й додаткову інформацію"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Цей додаток може переглядати всі події календаря, збережені на вашому планшеті, а також надсилати та зберігати дані календаря."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Додаток може переглядати всі події календаря, збережені на вашому пристрої Android TV, а також надсилати та зберігати дані календаря."</string>
@@ -737,6 +739,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Додаток зможе переглядати та змінювати конфігурацію режиму \"Не турбувати\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"перегляньте дані про використання дозволів"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Власник зможе використовувати дозволи для цього додатка. Цей дозвіл не потрібен для звичайних додатків."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"почати перегляд рішень щодо дозволів"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"У додатку може відкриватися екран для перегляду рішень щодо дозволів. Звичайні додатки ніколи не використовують цей дозвіл."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"почати перегляд функцій додатка"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дозволяє додатку почати перегляд інформації про функції іншого додатка."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"доступ до даних датчиків із високою частотою дикретизації"</string>
@@ -2053,8 +2057,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Користувацьке сповіщення додатка"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Дозволити додатку <xliff:g id="APP">%1$s</xliff:g> створити нового користувача з обліковим записом <xliff:g id="ACCOUNT">%2$s</xliff:g> (користувач із таким обліковим записом уже існує)?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Дозволити додатку <xliff:g id="APP">%1$s</xliff:g> створити нового користувача з обліковим записом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Додати контрольованого користувача"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Додати мову"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Вибір регіону"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Введіть назву мови"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 9d3c357..2c712f4 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"‏آنے والی اور باہر جانے والی کالز کے بارے میں ڈیٹا سمیت، ایپ کو آپ کے Android TV آلہ کے کال لاگ میں ترمیم کرنے کی اجازت دیتا ہے۔ نقصان دہ ایپس آپ کی کال لاگ مٹانے یا اس میں ترمیم کرنے کیلئے اسے استعمال کر سکتی ہیں۔"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ایپ کو آپ کے فون کی کال لاگ، بشمول آنے والی اور باہر جانے والی کالوں کے بارے میں ڈیٹا میں ترمیم کرنے کی اجازت دیتا ہے۔ نقصان دہ ایپس آپ کی کال لاگ مٹانے یا اس میں ترمیم کرنے کیلئے اسے استعمال کرسکتی ہیں۔"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"باڈی سینسرز تک رسائی حاصل کریں (جیسے حرکت قلب شرح مانیٹرز)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"ان سینسرز سے ڈیٹا تک رسائی حاصل کرنے کی اجازت دیتی ہے جو آپ کی حرکت قلب کی شرح جیسی آپ کی فزیکل صورتحال کو مانیٹر کرتے ہیں۔"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"باڈی سینسرز سے ڈیٹا تک رسائی حاصل کریں جیسے حرکت قلب کی شرح، درجہ حرارت، خون میں آکسیجن فیصد وغیرہ۔"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"پس منظر میں رہتے ہوئے باڈی سینسرز تک رسائی حاصل کریں (جیسے حرکت قلب کی شرح کے مانیٹرز)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"پس منظر میں رہتے ہوئے باڈی سینسرز سے ڈیٹا تک رسائی حاصل کریں جیسے حرکت قلب کی شرح، درجہ حرارت، خون میں آکسیجن کا فیصد وغیرہ۔"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"کیلنڈر ایونٹس اور تفاصیل پڑھیں"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"یہ ایپ آپ کے ٹیبلیٹ پر اسٹور کردہ سبھی کیلنڈر ایونٹس کو پڑھ سکتی ہے اور آپ کے  کیلنڈر ڈیٹا کا اشتراک یا اسے محفوظ کر سکتی ہے۔"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"‏یہ ایپ آپ کے Android TV آلہ پر اسٹور کردہ سبھی کیلنڈر ایونٹس کو پڑھ سکتی ہے اور آپ کے کیلنڈر ڈیٹا کا اشتراک یا اسے محفوظ کر سکتی ہے۔"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ایپ کو ڈسٹرب نہ کریں کنفیگریشن لکھنے اور پڑھنے کے قابل کرتا ہے۔"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"اجازت کی استعمال کا ملاحظہ شروع کریں"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"حامل کو ایپ کی اجازت کے استعمال کو شروع کرنے کی اجازت دیتا ہے۔ عام ایپس کے لیے کبھی بھی درکار نہیں ہونا چاہیے۔"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"اجازت کے فیصلوں کو دیکھنا شروع کریں"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ہولڈر کو اجازت کے فیصلوں کے جائزے کے لیے اسکرین شروع کرنے کی اجازت دیتا ہے۔ عام ایپس کے لیے کبھی بھی اس کی ضرورت نہيں ہونی چاہیے۔"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ایپ کی خصوصیات کا ملاحظہ شروع کریں"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ہولڈر کو ایپ کے لیے خصوصیات کی معلومات دیکھنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"نمونہ کاری کی اعلی شرح پر سینسر کے ڈیٹا تک رسائی حاصل کریں"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"حسب ضرورت ایپ کی اطلاع"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g> کو <xliff:g id="ACCOUNT">%2$s</xliff:g> کے ساتھ ایک نیا صارف بنانے کی اجازت دیں (اس اکاؤنٹ کے ساتھ ایک صارف پہلے سے موجود ہے) ؟"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g> کے ساتھ نئے صارف کو تخلیق کرنے کے لیے <xliff:g id="APP">%1$s</xliff:g> کو اجازت دیں ؟"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"زیر نگرانی صارف شامل کریں"</string>
     <string name="language_selection_title" msgid="52674936078683285">"ایک زبان شامل کریں"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"علاقہ کی ترجیح"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"زبان کا نام ٹائپ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index ab03056..42a88d7 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Ilovaga Android TV qurilmangizdagi chaqiruvlar jurnali, kirish va chiqish chaqiruvlari haqidagi axborotni oʻzgartirish huquqini beradi. Zararli ilovalar undan chaqiruvlar jurnalini oʻzgartirish yoki oʻchirish uchun foydalanishi mumkin."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Ilovaga telefoningizdagi qo‘ng‘iroq jurnallari, kiruvchi va chiquvchi qo‘ng‘rioqlar haqidagi ma’lumotlarni o‘zgartirishga ruxsat beradi. Zararli ilovalar bundan qo‘ng‘iroqlar jurnalini o‘zgartirish yoki o‘chirish uchun foydalanishi mumkin."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"tana sezgichlari (m-n, yurak urishi sensori) ma’lumotlaridan foydalanishga ruxsat"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Ilovaga sezgichlardan olingan jismoniy holatingiz haqidagi ma’lumotlarni, masalan, yurak urishini kuzatish uchun ruxsat beradi."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Yurak urishi, harorat, qondagi kislorod foizi kabi tana sezgilaridan olingan maʼlumotlarga kirish."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"orqa fonda tana sezgilari (m-n, yurak urishi sensori) maʼlumotlariga kirish"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Yurak urishi, harorat, qondagi kislorod foizi kabi tana sezgilaridan olingan maʼlumotlarga orqa fonda kirish."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Taqvim tadbirlari va tafsilotlarini o‘qish"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Bu ilova planshetdagi barcha taqvim tadbirlarini o‘qiy olishi hamda taqvim ma’lumotlarini ulashishi yoki saqlashi mumkin."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Bu ilova Android TV qurilmangizda barcha taqvim tadbirlarini oʻqiy olishi hamda taqvim maʼlumotlarini ulashishi yoki saqlashi mumkin."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"“Bezovta qilinmasin” rejimi sozlamalarini ko‘rish va o‘zgartirish."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"foydalaniladigan ruxsatlar axborotini ochish"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ilova foydalanadigan ruxsatlar axborotini ishga tushirishga ruxsat beradi. Oddiy ilovalar uchun talab qilinmaydi."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ilova ruxsatlarini tekshirishni boshlash"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Qurilma egasiga ilova ruxsatlarini tekshirishni boshlash imkonini beradi. Oddiy ilovalar uchun talab qilinmaydi."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ilova funksiyalari axborotini koʻrish"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Qurilma egasiga ilova funksiyalari axborotini koʻrishga ruxsat beradi."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"yuqori diskretlash chastotali sensor axborotiga ruxsat"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9225c16..05899ed 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Cho phép ứng dụng sửa đổi nhật ký cuộc gọi trên thiết bị Android TV, bao gồm cả dữ liệu về cuộc gọi đến và cuộc gọi đi. Các ứng dụng độc hại có thể sử dụng quyền này để xóa hoặc sửa đổi nhật ký cuộc gọi của bạn."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Cho phép ứng dụng sửa đổi nhật ký cuộc gọi trên điện thoại của bạn, bao gồm dữ liệu về các cuộc gọi đến và gọi đi. Các ứng dụng độc hại có thể sử dụng quyền này để xóa hoặc sửa đổi nhật ký cuộc gọi của bạn."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"truy cập cảm biến cơ thể (như máy đo nhịp tim)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Cho phép ứng dụng truy cập dữ liệu từ bộ cảm biến giám sát tình trạng sức khỏe của bạn, ví dụ như nhịp tim."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Quyền truy cập vào dữ liệu từ các cảm biến cơ thể, chẳng hạn như dữ liệu về nhịp tim, nhiệt độ, tỷ lệ phần trăm oxy trong máu và các dữ liệu khác."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"truy cập vào các cảm biến cơ thể (như máy đo nhịp tim) khi ở trong nền"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Quyền truy cập vào dữ liệu từ các cảm biến cơ thể, chẳng hạn như dữ liệu về nhịp tim, nhiệt độ, tỷ lệ phần trăm oxy trong máu và các dữ liệu khác khi ở trong nền."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Đọc chi tiết và sự kiện lịch"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Ứng dụng này có thể đọc tất cả các sự kiện lịch được lưu trữ trên máy tính bảng của bạn và chia sẻ hoặc lưu dữ liệu lịch của bạn."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Ứng dụng này có thể đọc tất cả sự kiện trên lịch mà bạn lưu vào thiết bị Android TV cũng như chia sẻ hoặc lưu dữ liệu lịch của bạn."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Cho phép ứng dụng đọc và ghi cấu hình Không làm phiền."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"cấp quyền xem"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Cho phép chủ sở hữu cấp quyền cho một ứng dụng. Các ứng dụng thông thường sẽ không bao giờ cần quyền này."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"bắt đầu xem các quyết định cấp quyền"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Cho phép chủ sở hữu bắt đầu kiểm tra để xem xét các quyết định cấp quyền. Việc này hoàn toàn không cần thiết đối với các ứng dụng thông thường."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"bắt đầu xem các tính năng của ứng dụng"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Cho phép chủ sở hữu bắt đầu xem thông tin về tính năng của một ứng dụng."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"truy cập vào dữ liệu cảm biến ở tốc độ lấy mẫu cao"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6793baa..6acd3e0 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"允许应用修改您的 Android TV 设备的通话记录,包括关于来电和去电的数据。恶意应用可能会借此清空或修改您的通话记录。"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"允许该应用修改手机的通话记录,包括有关来电和外拨电话的数据。恶意应用可能会借此清除或修改您的通话记录。"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"访问身体传感器(如心率监测器)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"允许该应用存取监测您身体状况的传感器所收集的数据,例如您的心率。"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"访问身体传感器获得的数据,例如心率、体温、血氧百分比等。"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"在后台时访问身体传感器(如心率监测器)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"在后台时访问身体传感器获得的数据,例如心率、体温、血氧百分比等。"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"读取日历活动和详情"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"此应用可读取您平板电脑上存储的所有日历活动,并分享或保存您的日历数据。"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"此应用可读取您的 Android TV 设备上存储的所有日历活动,以及分享或保存您的日历数据。"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允许此应用读取和写入“勿扰”模式配置。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"授权使用“查看权限”"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允许该应用开始查看应用的权限使用情况(普通应用绝不需要此权限)。"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"开始查看权限决策"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"允许具有该权限的应用启动屏幕以查看权限决策。普通应用绝不需要此权限。"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"开始查看应用功能"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"允许具有该权限的应用开始查看某个应用的功能信息。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高采样率访问传感器数据"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"自定义应用通知"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g>(目前已有用户使用此帐号)创建新用户吗?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 创建新用户吗?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"添加受监管用户"</string>
     <string name="language_selection_title" msgid="52674936078683285">"添加语言"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"区域偏好设置"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"输入语言名称"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index d63bf16..bf51c1a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"允許應用程式修改 Android TV 裝置的通話記錄,包括來電和撥出電話的相關資料。惡意應用程式可能會藉此清除或修改您的通話記錄。"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"允許應用程式修改手機的通話記錄,包括來電和已撥電話相關資料。惡意應用程式可能會藉此刪除或修改您的通話記錄。"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"存取人體感應器 (例如心跳監測器)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"允許應用程式存取感應器所收集的資料 (這類感應器可監測您的體能狀態,例如您的心率)。"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"存取人體感應器的資料,例如心率、體溫、血氧百分比等。"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"在背景中存取人體感應器 (例如心率感應器)"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"在背景中存取人體感應器的資料,例如心率、體溫、血氧百分比等。"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"讀取日曆活動和詳情"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"此應用程式可以讀取所有儲存在您的平板電腦的日曆活動,並分享或儲存您的日曆資料。"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"此應用程式可以讀取所有儲存在 Android TV 裝置上的日曆活動,並分享或儲存您的日曆資料。"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取和寫入「請勿騷擾」設定。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"開始查看權限使用情況"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始查看應用程式的權限使用情況 (一般應用程式並不需要)。"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"開始檢視權限決定"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"允許擁有者啟用螢幕以查看權限決定。不建議一般應用程式使用。"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"開始查看應用程式功能"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"允許擁有者開始查看應用程式的功能資料。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8890f66..bd0b9a2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"允許應用程式修改 Android TV 裝置的通話記錄,包括來電和撥出電話相關資料。惡意應用程式可能會藉此清除或修改你的通話記錄。"</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"允許應用程式修改手機的通話記錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改你的通話記錄。"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"存取人體感應器 (例如心跳速率監測器)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"允許應用程式存取感測器所收集的資料 (這類感測器可監測你的體能狀態,例如你的心跳速率)。"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"存取人體感應器的資料,例如心跳速率、體溫、血氧比例等等。"</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"在背景存取人體感應器 (例如心跳速率監測器) 的資料"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"在背景存取人體感應器的資料,例如心跳速率、體溫、血氧比例等等。"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"讀取日曆活動和詳細資訊"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"這個應用程式可讀取所有儲存在平板電腦上的日曆活動資訊,以及共用或儲存日曆資料。"</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"這個應用程式可讀取所有儲存在 Android TV 裝置上的日曆活動,以及共用或儲存日曆資料。"</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取及寫入「零打擾」設定。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"啟動檢視權限用途"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始使用其他應用程式 (一般應用程式並不需要)。"</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"開始檢視權限決定"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"允許應用程式啟動檢視權限決定的畫面 (一般應用程式並不需要)。"</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"開始查看應用程式功能"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"允許具有這項權限的應用程式開始查看其他應用程式的功能資訊。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"自訂應用程式通知"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"要允許「<xliff:g id="APP">%1$s</xliff:g>」替 <xliff:g id="ACCOUNT">%2$s</xliff:g> (這個帳戶目前已有使用者) 建立新使用者嗎?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"要允許「<xliff:g id="APP">%1$s</xliff:g>」替 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者嗎?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"新增受監管的使用者"</string>
     <string name="language_selection_title" msgid="52674936078683285">"新增語言"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"地區偏好設定"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"請輸入語言名稱"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index bfc0f1b..bf024528 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -427,7 +427,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Ivumela uhlelo lokusebenza ukuthi liguqule ilogi yekholi yedivayisi yakho ye-Android TV, okufaka idatha emayelana namakholi angenayo naphumayo. Izinhlelo zokusebenza ezinobungozi zingasebenzisa lokhu ukususa noma ukuguqula ilogi yakho yekholi."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Ivumela uhlelo lokusebenza ukushintsha irekhodi lamakholi efoni yakho, kufaka phakathi idatha emayelana namakholi angenayo naphumayo. Izinhlelo zikusebenza ezingalungile zingasebenzisa lokhu ukusula noma ukushintsha irekhodi lwamakholi wakho."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"finyelela kuzinzwa zomzimba (ezifana neziqaphi zokulinganisela inhliziyo)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Ivumela uhlelo lokusebenza ukuthi lufinyelele kudatha kusukela kuzinzwa eziqapha isimo sakho somzimba, esifana nesilinganiso senhliziyo yakho."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"Ukufinyelela kudatha kusuka kuzinzwa zomzimba ezifana nokushaya kwenhliziyo, izinga lokushisa, amaphesenti womoyampilo wegazi, njll."</string>
+    <string name="permlab_bodySensors_background" msgid="4352831883331744370">"finyelela izinzwa zomzimba (njengeziqapha ukushaya kwenhliziyo) kuyilapho ingemuva"</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8512392249166660872">"Ukufinyelela kudatha evela kuzinzwa zomzimba ezinjengokushaya kwenhliziyo, izinga lokushisa, amaphesenti omoyampilo wegazi, njll. kuyilapho ingemuva."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"Funda imicimbi yekhalenda nemininingwane"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Lolu hlelo lokusebenza lungafunda yonke imicimbi yekhalenda elondolozwe kuthebhulethi yakho nokwabelana noma ukulondoloza idatha yakho yekhalenda."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Lolu hlelo lokusebenza lungafunda yonke imicimbi yekhalenda elondolozwe kudivayisi yakho ye-Android TV nokwabelana noma ukulondoloza idatha yakho yekhalenda."</string>
@@ -731,6 +733,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ivumela izinhlelo zokusebenza ukufunda nokubhala ukulungiswa kokuthi Ungaphazamisi."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"qala ukusetshenziswa kokubuka imvume"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ivumela umphathi ukuthi aqale ukusetshenziswa kwemvume kohlelo lokusebenza. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
+    <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"qala ukubuka izinqumo zemvume"</string>
+    <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Ivumela obambile ukuthi aqale isikrini ukuze abuyekeze izinqumo zemvume. Akufanele idingeke ngama-app avamile."</string>
     <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"qala ukubuka izakhi ze-app"</string>
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Vumela isibambi ukuthi siqale ukubuka ulwazi lwezakhi lwe-app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"finyelela idatha yenzwa ngenani eliphezulu lokwenza isampuli"</string>
@@ -1390,7 +1394,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"I-MIDI nge-USB ivuliwe"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Insiza ye-USB ixhunyiwe"</string>
     <string name="usb_notification_message" msgid="4715163067192110676">"Thepha ngezinketho eziningi."</string>
-    <string name="usb_power_notification_message" msgid="7284765627437897702">"Ishaja idivayisi exhunyiwe. Thepha ukuze uthole ezinye izinketho."</string>
+    <string name="usb_power_notification_message" msgid="7284765627437897702">"Ishaja idivayisi exhunyiwe. Thepha ukuze uthole okunye okungakhethwa."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Kutholwe isisetshenziswa se-analog yomsindo"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Idivayisi enamathiselwe kwi-imeyili ayihambisani nale foni. Thepha ukuze ufunde kabanzi."</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"Ukulungisa iphutha le-USB kuxhunyiwe"</string>
@@ -1989,8 +1993,7 @@
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Isaziso sohlelo lokusebenza olungokwezifiso"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukuthi idale umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> (Umsebenzisi onale akhawunti usevele ukhona) ?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukuthi idale umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
-    <!-- no translation found for supervised_user_creation_label (6884904353827427515) -->
-    <skip />
+    <string name="supervised_user_creation_label" msgid="6884904353827427515">"Engeza umsebenzisi ogadiwe"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Engeza ulimi"</string>
     <string name="country_selection_title" msgid="5221495687299014379">"Okuncamelayo kwesifunda"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Thayipha igama lolimi"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1705371..8696f5a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3347,6 +3347,14 @@
              <p>Note that this flag will only be respected if the View's Outline returns true from
              {@link android.graphics.Outline#canClip()}. -->
         <attr name="clipToOutline" format="boolean" />
+
+        <!-- <p> Sets a preference to keep the bounds of this view clear from floating windows
+            above this view's window. This informs the system that the view is considered a vital
+            area for the user and that ideally it should not be covered. Setting this is only
+            appropriate for UI where the user would likely take action to uncover it.
+            <p>The system will try to respect this, but when not possible will ignore it.
+            See {@link android.view.View#setPreferKeepClear}. -->
+        <attr name="preferKeepClear" format="boolean" />
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3638,7 +3646,22 @@
              to re-retrieve all resources (including view layouts, drawables, etc)
              to correctly handle any configuration change.-->
         <attr name="configChanges" />
-        <!-- Specifies whether the IME supports Handwriting using stylus. Defaults to false. -->
+        <!-- Specifies whether the IME supports Handwriting using stylus. Defaults to false.
+            When IME implements support for stylus handwriting, on every ACTION_DOWN with stylus
+            on an editor,
+            {@link android.inputmethodservice.InputMethodService#onStartStylusHandwriting()}
+            is called.
+            If IME is ready for stylus input, it must return {@code true} for Handwriting sessions
+            to start. IME should attach it's View that renders Ink on screen to stylus handwriting
+            inking window
+            {@link android.inputmethodservice.InputMethodService#getStylusHandwritingWindow()}.
+            IME will then receive Stylus MotionEvent(s) on DecorView i.e. the Inking view
+            {@link android.view.View#onTouchEvent(MotionEvent)} attached by IME to Ink window.
+            Handwriting mode can be finished by calling
+            {@link android.inputmethodservice.InputMethodService#finishStylusHandwriting()} or will
+            be finished by framework on next
+            {@link android.inputmethodservice.InputMethodService#onFinishInput()}.
+        -->
         <attr name="supportsStylusHandwriting" format="boolean" />
 
     </declare-styleable>
@@ -5015,6 +5038,10 @@
         <attr name="fontFeatureSettings" format="string" />
         <!-- Font variation settings. -->
         <attr name="fontVariationSettings" format="string"/>
+        <!-- Specifies the strictness of line-breaking rules applied within an element. -->
+        <attr name="lineBreakStyle" />
+        <!-- Specifies the phrase-based breaking opportunities. -->
+        <attr name="lineBreakWordStyle" />
     </declare-styleable>
     <declare-styleable name="TextClock">
         <!-- Specifies the formatting pattern used to show the time and/or date
@@ -5402,6 +5429,24 @@
              ignores some hyphen character related typographic features, e.g. kerning. -->
             <enum name="fullFast" value="4" />
         </attr>
+        <!-- Indicates the line break strategies can be used when calculating the text wrapping. -->
+        <attr name="lineBreakStyle">
+            <!-- No line break style specific. -->
+            <enum name="none" value="0" />
+            <!-- Use the least restrictive rule for line-breaking. -->
+            <enum name="loose" value="1" />
+            <!-- Indicate breaking text with the most comment set of line-breaking rules. -->
+            <enum name="normal" value="2" />
+            <!-- ndicates breaking text with the most strictest line-breaking rules. -->
+            <enum name="strict" value="3" />
+        </attr>
+        <!-- Specify the phrase-based line break can be used when calculating the text wrapping.-->
+        <attr name="lineBreakWordStyle">
+            <!-- No line break word style specific. -->
+            <enum name="none" value="0" />
+            <!-- Specify the phrase based breaking. -->
+            <enum name="phrase" value="1" />
+        </attr>
         <!-- Specify the type of auto-size. Note that this feature is not supported by EditText,
         works only for TextView. -->
         <attr name="autoSizeTextType" format="enum">
@@ -6869,6 +6914,9 @@
         <!-- Special option for window animations: whether window should have rounded corners.
              @see ScreenDecorationsUtils#getWindowCornerRadius(Resources) -->
         <attr name="hasRoundedCorners" format="boolean" />
+        <!-- Special option for window animations: whether the window's background should be used as
+             a background to the animation. -->
+        <attr name="showBackground" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="AnimationSet">
@@ -8442,6 +8490,9 @@
         <attr name="settingsActivity" />
         <!-- A preview, in a drawable resource id, of what the Dream will look like. -->
         <attr name="previewImage" format="reference" />
+        <!-- Whether to show clock and other complications such as weather in the overlay. Default
+             to true. Note that the overlay on dreams is currently only supported on tablets. -->
+        <attr name="showClockAndComplications" format="boolean" />
     </declare-styleable>
 
     <!--  Use <code>trust-agent</code> as the root tag of the XML resource that
@@ -9344,11 +9395,12 @@
         <attr name="canPauseRecording" format="boolean" />
     </declare-styleable>
 
-    <!-- Use <code>tv-iapp</code> as the root tag of the XML resource that describes a
-         {@link android.media.tv.interactive.TvIAppService}, which is referenced from its
-         {@link android.media.tv.interactive.TvIAppService#SERVICE_META_DATA} meta-data entry.
-         Described here are the attributes that can be included in that tag. -->
-    <declare-styleable name="TvIAppService">
+    <!-- Use <code>tv-interactive-app</code> as the root tag of the XML resource that describes a
+         {@link android.media.tv.interactive.TvInteractiveAppService}, which is referenced
+         from its
+         {@link android.media.tv.interactive.TvInteractiveAppService#SERVICE_META_DATA}
+         meta-data entry. Described here are the attributes that can be included in that tag. -->
+    <declare-styleable name="TvInteractiveAppService">
         <!-- The interactive app types that the TV interactive app service supports.
              Reference to a string array resource that describes the supported types,
              e.g. HbbTv, Ginga. -->
@@ -9647,4 +9699,11 @@
     <attr name="iconfactoryBadgeSize" format="dimension"/>
     <!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
     <attr name="lStar" format="float"/>
-</resources>
+
+    <!-- The attributes of the {@code <locale>} tag within {@code <locale-config>}. -->
+    <declare-styleable name="LocaleConfig_Locale">
+        <!-- The <a href="https://www.rfc-editor.org/rfc/bcp/bcp47.txt">IETF BCP47 language tag</a>
+        of the supported locale. {@link android.app.LocaleConfig} -->
+        <attr name="name" />
+    </declare-styleable>
+    </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index db24475..3a2fb6e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -401,6 +401,15 @@
          and before. -->
     <attr name="sharedUserMaxSdkVersion" format="integer" />
 
+    <!-- Whether the application should inherit all AndroidKeyStore keys of its shared user
+         group in the case of leaving its shared user ID in an upgrade.  If set to false, all
+         AndroidKeyStore keys will remain in the shared user group, and the application will no
+         longer have access to those keys after the upgrade. If set to true, all AndroidKeyStore
+         keys owned by the shared user group will be transferred to the upgraded application;
+         other applications in the shared user group will no longer have access to those keys
+         after the migration. The default value is false if not explicitly set. -->
+    <attr name="inheritKeyStoreKeys" format="boolean" />
+
     <!-- Internal version code.  This is the number used to determine whether
          one version is more recent than another: it has no other meaning than
          that higher numbers are more recent.  You could use this number to
@@ -1519,6 +1528,9 @@
     <!-- An XML resource with the application's Network Security Config. -->
     <attr name="networkSecurityConfig" format="reference" />
 
+    <!-- An XML resource with the application's {@link android.app.LocaleConfig} -->
+    <attr name="localeConfig" format="reference" />
+
     <!-- When an application is partitioned into splits, this is the name of the
          split that contains the defined component. -->
     <attr name="splitName" format="string" />
@@ -1674,6 +1686,7 @@
         <attr name="sharedUserId" />
         <attr name="sharedUserLabel" />
         <attr name="sharedUserMaxSdkVersion" />
+        <attr name="inheritKeyStoreKeys" />
         <attr name="installLocation" />
         <attr name="isolatedSplits" />
         <attr name="isFeatureSplit" />
@@ -1801,6 +1814,7 @@
         <attr name="maxAspectRatio" />
         <attr name="minAspectRatio" />
         <attr name="networkSecurityConfig" />
+        <attr name="localeConfig" />
         <!-- Declare the category of this app. Categories are used to cluster multiple apps
              together into meaningful groups, such as when summarizing battery, network, or
              disk usage. Apps should only define this value when they fit well into one of
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7d8bcea..c0c8618 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1179,10 +1179,18 @@
     <string-array translatable="false" name="config_ringtoneEffectUris">
     </string-array>
 
+    <!-- The default intensity level for alarm vibrations. See
+         Settings.System.ALARM_VIBRATION_INTENSITY more details on the constant values and
+         meanings. -->
+    <integer name="config_defaultAlarmVibrationIntensity">2</integer>
     <!-- The default intensity level for haptic feedback. See
          Settings.System.HAPTIC_FEEDBACK_INTENSITY more details on the constant values and
          meanings. -->
     <integer name="config_defaultHapticFeedbackIntensity">2</integer>
+    <!-- The default intensity level for media vibrations. See
+         Settings.System.MEDIA_VIBRATION_INTENSITY more details on the constant values and
+         meanings. -->
+    <integer name="config_defaultMediaVibrationIntensity">2</integer>
     <!-- The default intensity level for notification vibrations. See
          Settings.System.NOTIFICATION_VIBRATION_INTENSITY more details on the constant values and
          meanings. -->
@@ -2125,6 +2133,13 @@
     <string name="config_deviceManager" translatable="false"></string>
     <!-- The name of the package that will hold the app protection service role. -->
     <string name="config_systemAppProtectionService" translatable="false"></string>
+    <!-- The name of the package that will hold the system calendar sync manager role. -->
+    <string name="config_systemAutomotiveCalendarSyncManager" translatable="false"></string>
+    <!-- The name of the package that will hold the default automotive navigation role. -->
+    <string name="config_defaultAutomotiveNavigation" translatable="false"></string>
+
+    <!-- The name of the package that will handle updating the device management role. -->
+    <string name="config_deviceManagerUpdater" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
@@ -2685,10 +2700,27 @@
     <!-- Configure mobile tcp buffer sizes in the form:
          rat-name:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
          If no value is found for the rat-name in use, the system default will be applied.
+
+         This is deprecated. Please use config_tcp_buffers.
     -->
     <string-array name="config_mobile_tcp_buffers">
     </string-array>
 
+    <!-- Configure tcp buffer sizes in the form:
+         rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
+         If this is configured as an empty string, the system default will be applied.
+
+         For now this config is used by mobile data only. In the future it should be
+         used by Wi-Fi as well.
+
+         Note that starting from Android 13, the TCP buffer size is fixed after boot up, and should
+         never be changed based on carriers or the network types. The value should be configured
+         appropriately based on the device's memory and performance. It is recommended to use lower
+         values if the device has low memory or doesn't support high-speed network such like LTE,
+         NR, or Wifi.
+    -->
+    <string name="config_tcp_buffers" translatable="false"></string>
+
     <!-- Configure ethernet tcp buffersizes in the form:
          rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max -->
     <string name="config_ethernet_tcp_buffers" translatable="false">524288,1048576,3145728,524288,1048576,2097152</string>
@@ -2773,6 +2805,16 @@
         <item>350</item>
     </integer-array>
 
+    <!-- A vibration waveform for notifications that specify DEFAULT_VIBRATE.
+         This value is a float array with values grouped as
+         { targetAmplitude (within [0,1]), targetFrequency (in hertz), duration (in milliseconds) }
+         This is only applied on devices with vibration frequency control. If the device doesn't
+         support frequency control, then the vibration specified in
+         config_defaultNotificationVibePattern is used instead.
+     -->
+    <array name="config_defaultNotificationVibeWaveform">
+    </array>
+
     <!-- Vibrator pattern to be used as the default for notifications
          that do not specify vibration but vibrate anyway because the device
          is in vibrate mode.
@@ -2784,6 +2826,16 @@
         <item>100</item>
     </integer-array>
 
+    <!-- A vibration waveform for notifications that do not specify vibration but vibrate anyway,
+         because the device is in vibrate mode. This value is a float array with values grouped as
+         { targetAmplitude (within [0,1]), targetFrequency (in hertz), duration (in milliseconds) }
+         This is only applied on devices with vibration frequency control. If the device doesn't
+         support frequency control, then the vibration specified in
+         config_notificationFallbackVibePattern is used instead.
+     -->
+    <array name="config_notificationFallbackVibeWaveform">
+    </array>
+
     <!-- Flag indicating if the speed up audio on mt call code should be executed -->
     <bool name="config_speed_up_audio_on_mt_calls">false</bool>
 
@@ -4025,6 +4077,12 @@
    -->
     <string name="config_defaultRotationResolverService" translatable="false"></string>
 
+    <!-- The component name for the default system AmbientContextEvent detection service.
+        This service must be trusted, as it can be activated without explicit consent of the user.
+        See android.service.ambientcontext.AmbientContextDetectionService.
+   -->
+    <string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
+
     <!-- The component name for the system-wide captions service.
          This service must be trusted, as it controls part of the UI of the volume bar.
          Example: "com.android.captions/.SystemCaptionsService"
@@ -4195,7 +4253,7 @@
     <string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
 
     <!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
-    <integer translatable="false" name="config_autoGroupAtCount">4</integer>
+    <integer translatable="false" name="config_autoGroupAtCount">2</integer>
 
     <!-- The OEM specified sensor type for the lift trigger to launch the camera app. -->
     <integer name="config_cameraLiftTriggerSensorType">-1</integer>
@@ -5557,4 +5615,10 @@
 
     <!-- Determines whether SafetyCenter feature is enabled. -->
     <bool name="config_enableSafetyCenter">true</bool>
+
+    <!-- Flag indicating if help links for Settings app should be enabled. -->
+    <bool name="config_settingsHelpLinksEnabled">false</bool>
+
+    <!-- Whether or not to enable the lock screen entry point for the QR code scanner. -->
+    <bool name="config_enableQrCodeScannerOnLockScreen">false</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a877bd3..3f08e4b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -131,6 +131,19 @@
         corners. -->
     <dimen name="rounded_corner_radius_bottom_adjustment">0px</dimen>
 
+    <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME. -->
+    <dimen name="input_method_navigation_key_width">70dp</dimen>
+    <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME. -->
+    <dimen name="input_method_navigation_key_padding">0dp</dimen>
+    <!-- Copied from SysUI's @dimen/nav_content_padding for the embedded nav bar in the IME. -->
+    <dimen name="input_method_nav_content_padding">0px</dimen>
+    <!-- Copied from SysUI's @dimen/rounded_corner_content_padding for the embedded nav bar in the
+         IME. -->
+    <dimen name="input_method_rounded_corner_content_padding">0px</dimen>
+    <!-- Copied from SysUI's @dimen/key_button_ripple_max_width for the embedded nav bar in the
+         IME. -->
+    <dimen name="input_method_nav_key_button_ripple_max_width">95dp</dimen>
+
     <!-- Width of the window of the divider bar used to resize docked stacks. -->
     <dimen name="docked_stack_divider_thickness">48dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1f560f4..505fe59 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3211,6 +3211,8 @@
   </staging-public-group-final>
 
   <public type="attr" name="shouldUseDefaultUnfoldTransition" id="0x0101064c" />
+  <public type="attr" name="lineBreakStyle" id="0x0101064d" />
+  <public type="attr" name="lineBreakWordStyle" id="0x0101064e" />
 
   <staging-public-group-final type="id" first-id="0x01fe0000">
     <public name="accessibilityActionDragStart" />
@@ -3250,8 +3252,13 @@
     <public name="supportedTypes" />
     <public name="resetEnabledSettingsOnAppDataCleared" />
     <public name="supportsStylusHandwriting" />
+    <public name="showClockAndComplications" />
     <!-- @hide @SystemApi -->
     <public name="gameSessionService" />
+    <public name="localeConfig" />
+    <public name="showBackground" />
+    <public name="inheritKeyStoreKeys" />
+    <public name="preferKeepClear" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
@@ -3263,6 +3270,7 @@
   </staging-public-group>
 
   <staging-public-group type="style" first-id="0x01dd0000">
+    <public name="TextAppearance.DeviceDefault.Headline" />
   </staging-public-group>
 
   <staging-public-group type="string" first-id="0x01dc0000">
@@ -3272,6 +3280,10 @@
     <public name="config_deviceManager" />
     <!-- @hide @SystemApi -->
     <public name="config_systemAppProtectionService" />
+    <!-- @hide @SystemApi @TestApi -->
+    <public name="config_systemAutomotiveCalendarSyncManager" />
+    <!-- @hide @SystemApi -->
+    <public name="config_defaultAutomotiveNavigation" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01db0000">
@@ -3281,6 +3293,8 @@
   </staging-public-group>
 
   <staging-public-group type="array" first-id="0x01d90000">
+    <!-- @hide @SystemApi -->
+    <public name="config_optionalIpSecAlgorithms" />
   </staging-public-group>
 
   <staging-public-group type="drawable" first-id="0x01d80000">
@@ -3311,10 +3325,10 @@
   </staging-public-group>
 
   <staging-public-group type="bool" first-id="0x01cf0000">
-    <!-- @hide @SystemApi -->
-    <public name="config_systemCaptionsServiceCallsEnabled" />
     <!-- @hide @TestApi -->
     <public name="config_preventImeStartupUnlessTextEditor" />
+    <!-- @hide @SystemApi -->
+    <public name="config_enableQrCodeScannerOnLockScreen" />
   </staging-public-group>
 
   <staging-public-group type="fraction" first-id="0x01ce0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2879759..1a5d8b7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3277,6 +3277,11 @@
     <!-- Title for EditText context menu [CHAR LIMIT=20] -->
     <string name="editTextMenuTitle">Text actions</string>
 
+    <!-- Content description of the back button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="input_method_nav_back_button_desc">Back</string>
+    <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="input_method_ime_switch_button_desc">Switch input method</string>
+
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
@@ -4046,11 +4051,16 @@
     <!-- Description of an application permission that lets it ask user to ignore battery optimizations for that app-->
     <string name="permdesc_requestIgnoreBatteryOptimizations">Allows an app to ask for permission to ignore battery optimizations for that app.</string>
 
-    <!-- Title of an application permission that lets query all other packages. [CHAR LIMIT=NONE] -->
+    <!-- Title of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
     <string name="permlab_queryAllPackages">query all packages</string>
     <!-- Description of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
     <string name="permdesc_queryAllPackages">Allows an app to see all installed packages.</string>
 
+    <!-- Title of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE] -->
+    <string name="permlab_accessSupplementalApi">access SupplementalApis</string>
+    <!-- Description of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE]-->
+    <string name="permdesc_accessSupplementalApi">Allows an application to access SupplementalApis.</string>
+
     <!-- Shown in the tutorial for tap twice for zoom control. -->
     <string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
 
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index d8111ea..3b2f244 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -42,7 +42,6 @@
         <item name="outlineSpotShadowColor">@color/btn_colored_background_material</item>
         <item name="textAppearance">?attr/textAppearanceButton</item>
         <item name="textColor">@color/btn_colored_text_material</item>
-        <item name="drawableTint">@color/btn_colored_text_material</item>
     </style>
     <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView" />
     <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
@@ -398,7 +397,7 @@
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
     <style name="TextAppearance.DeviceDefault.Headline" parent="TextAppearance.Material.Headline">
-        <item name="fontFamily">@string/config_bodyFontFamily</item>
+        <item name="fontFamily">@string/config_headlineFontFamily</item>
     </style>
     <style name="TextAppearance.DeviceDefault.Display1" parent="TextAppearance.Material.Display1">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ba4aa81..8c8ef12 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -481,6 +481,7 @@
   <java-symbol type="integer" name="config_safe_media_volume_usb_mB" />
   <java-symbol type="integer" name="config_mobile_mtu" />
   <java-symbol type="array"   name="config_mobile_tcp_buffers" />
+  <java-symbol type="string"  name="config_tcp_buffers" />
   <java-symbol type="integer" name="config_volte_replacement_rat"/>
   <java-symbol type="integer" name="config_valid_wappush_index" />
   <java-symbol type="integer" name="config_overrideHasPermanentMenuKey" />
@@ -1935,7 +1936,9 @@
   <java-symbol type="array" name="config_locationExtraPackageNames" />
   <java-symbol type="array" name="config_testLocationProviders" />
   <java-symbol type="array" name="config_defaultNotificationVibePattern" />
+  <java-symbol type="array" name="config_defaultNotificationVibeWaveform" />
   <java-symbol type="array" name="config_notificationFallbackVibePattern" />
+  <java-symbol type="array" name="config_notificationFallbackVibeWaveform" />
   <java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" />
   <java-symbol type="bool" name="config_useAttentionLight" />
   <java-symbol type="bool" name="config_adaptive_sleep_available" />
@@ -2218,6 +2221,7 @@
   <java-symbol type="integer" name="config_dreamsBatteryLevelMinimumWhenNotPowered" />
   <java-symbol type="integer" name="config_dreamsBatteryLevelDrainCutoff" />
   <java-symbol type="string" name="config_dreamsDefaultComponent" />
+  <java-symbol type="drawable" name="default_dream_preview" />
   <java-symbol type="string" name="config_dozeComponent" />
   <java-symbol type="string" name="enable_explore_by_touch_warning_title" />
   <java-symbol type="string" name="enable_explore_by_touch_warning_message" />
@@ -2338,6 +2342,7 @@
   <java-symbol type="string" name="nas_upgrade_notification_disable_action" />
   <java-symbol type="string" name="nas_upgrade_notification_learn_more_action" />
   <java-symbol type="string" name="nas_upgrade_notification_learn_more_content" />
+  <java-symbol type="bool" name="config_settingsHelpLinksEnabled" />
 
   <!-- ImfTest -->
   <java-symbol type="layout" name="auto_complete_list" />
@@ -2434,6 +2439,24 @@
   <!-- From PinyinIME(!!!) -->
   <java-symbol type="string" name="inputMethod" />
 
+  <!-- Gestural Nav buttons within InputMethodService -->
+  <java-symbol type="dimen" name="input_method_nav_key_button_ripple_max_width" />
+  <java-symbol type="drawable" name="ic_ime_nav_back" />
+  <java-symbol type="drawable" name="ic_ime_switcher" />
+  <java-symbol type="id" name="input_method_nav_back" />
+  <java-symbol type="id" name="input_method_nav_buttons" />
+  <java-symbol type="id" name="input_method_nav_center_group" />
+  <java-symbol type="id" name="input_method_nav_ends_group" />
+  <java-symbol type="id" name="input_method_nav_home_handle" />
+  <java-symbol type="id" name="input_method_nav_horizontal" />
+  <java-symbol type="id" name="input_method_nav_ime_switcher" />
+  <java-symbol type="id" name="input_method_nav_inflater" />
+  <java-symbol type="layout" name="input_method_navigation_bar" />
+  <java-symbol type="layout" name="input_method_navigation_layout" />
+  <java-symbol type="layout" name="input_method_nav_back" />
+  <java-symbol type="layout" name="input_method_nav_home_handle" />
+  <java-symbol type="layout" name="input_method_nav_ime_switcher" />
+
   <!-- From Chromium-WebView -->
   <java-symbol type="attr" name="actionModeWebSearchDrawable" />
   <java-symbol type="string" name="websearch" />
@@ -3647,9 +3670,12 @@
   <java-symbol type="string" name="config_defaultRotationResolverService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
+  <java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
+  <java-symbol type="bool" name="config_systemCaptionsServiceCallsEnabled" />
+
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
   <java-symbol type="string" name="foreground_service_apps_in_background" />
@@ -3867,7 +3893,9 @@
   <java-symbol type="drawable" name="ic_arrow_forward" />
   <java-symbol type="drawable" name="ic_permission" />
 
+  <java-symbol type="integer" name="config_defaultAlarmVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
+  <java-symbol type="integer" name="config_defaultMediaVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultRingVibrationIntensity" />
 
@@ -4637,4 +4665,6 @@
   <java-symbol type="string" name="config_supervisedUserCreationPackage"/>
 
   <java-symbol type="bool" name="config_enableSafetyCenter" />
+
+  <java-symbol type="string" name="config_deviceManagerUpdater" />
 </resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index d310736..fc63657 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -144,17 +144,49 @@
     <value>2</value>    <!-- 4097-/hr -->
   </array>
 
-  <!-- Cellular modem related values. Default is 0.-->
-  <item name="modem.controller.sleep">0</item>
-  <item name="modem.controller.idle">0</item>
-  <item name="modem.controller.rx">0</item>
-  <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
-    <value>0</value>
-    <value>0</value>
-    <value>0</value>
-    <value>0</value>
-    <value>0</value>
-  </array>
+  <!-- Cellular modem related values.-->
+  <modem>
+    <!-- Modem sleep drain current value in mA. -->
+    <sleep>0</sleep>
+    <!-- Modem idle drain current value in mA. -->
+    <idle>0</idle>
+    <!-- Modem active drain current values.
+         Multiple <active /> can be defined to specify current drain for different modes of
+         operation.
+         Available attributes:
+             rat - Specify the current drain for a Radio Access Technology.
+                   Available options are "LTE", "NR" and "DEFAULT".
+                   <active rat="default" /> will be used for any usage that does not match any other
+                   defined <active /> rat.
+
+             nrFrequency - Specify the current drain for a frequency level while NR is active.
+                           Available options are "LOW", "MID", "HIGH", "MMWAVE", and "DEFAULT",
+                           where,
+                           "LOW" indicated <1GHz frequencies,
+                           "MID" indicates 1GHz to 3GHz frequencies,
+                           "HIGH" indicates 3GHz to 6GHz frequencies,
+                           "MMWAVE"indicates >6GHz frequencies.
+                           <active rat="NR" nrFrequency="default"/> will be used for any usage that
+                           does not match any other defined <active rat="NR" /> nrFrequency.
+    -->
+    <active rat="DEFAULT">
+      <!-- Transmit current drain in mA. -->
+      <receive>0</receive>
+
+      <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+      <transmit level="0">0</transmit>
+      <transmit level="1">0</transmit>
+      <transmit level="2">0</transmit>
+      <transmit level="3">0</transmit>
+      <transmit level="4">0</transmit>
+    </active>
+    <!-- Additional <active /> may be defined.
+         Example:
+             <active rat="LTE"> ... </active>
+             <active rat="NR" nrFrequency="MMWAVE"> ... </active>
+             <active rat="NR" nrFrequency="DEFAULT"> ... </active>
+    -->
+  </modem>
   <item name="modem.controller.voltage">0</item>
 
   <!-- GPS related values. Default is 0.-->
@@ -163,5 +195,4 @@
     <value>0</value>
   </array>
   <item name="gps.voltage">0</item>
-
 </device>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 1b1f64a..bd987a0 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -16,8 +16,7 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.frameworks.core.batterystatsviewer"
-          android:sharedUserId="android.uid.system">
+          package="com.android.frameworks.core.batterystatsviewer">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.BATTERY_STATS"/>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index eb378b9..6a53f68 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -130,23 +130,20 @@
                     || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
                 addEntry(metricTitle, EntryType.UID_POWER_MODELED,
                         requestedBatteryConsumer.getConsumedPower(component),
-                        totalPowerByComponentMah[component]
-                );
+                        totalPowerByComponentMah[component]);
+                addProcessStateEntries(metricTitle, EntryType.UID_POWER_MODELED_PROCESS_STATE,
+                        requestedBatteryConsumer, component);
             } else {
                 addEntry(metricTitle + " (measured)", EntryType.UID_POWER_MEASURED,
                         requestedBatteryConsumer.getConsumedPower(component),
-                        totalPowerByComponentMah[component]
-                );
+                        totalPowerByComponentMah[component]);
                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_MEASURED_PROCESS_STATE,
-                        requestedBatteryConsumer, component
-                );
+                        requestedBatteryConsumer, component);
                 addEntry(metricTitle + " (modeled)", EntryType.UID_POWER_MODELED,
                         requestedModeledBatteryConsumer.getConsumedPower(component),
-                        totalModeledPowerByComponentMah[component]
-                );
+                        totalModeledPowerByComponentMah[component]);
                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_MODELED_PROCESS_STATE,
-                        requestedModeledBatteryConsumer, component
-                );
+                        requestedModeledBatteryConsumer, component);
             }
         }
 
diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp
deleted file mode 100644
index 68416dd..0000000
--- a/core/tests/bluetoothtests/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
-    name: "BluetoothTests",
-    // Include all test java files.
-    srcs: ["src/**/*.java"],
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-    ],
-    static_libs: [
-        "junit",
-        "modules-utils-bytesmatcher",
-    ],
-    platform_apis: true,
-    certificate: "platform",
-}
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
deleted file mode 100644
index 75583d5..0000000
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.bluetooth.tests"
-          android:sharedUserId="android.uid.bluetooth" >
-
-    <uses-permission android:name="android.permission.BLUETOOTH" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
-    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
-    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
-    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
-    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
-    <uses-permission android:name="android.permission.RECEIVE_SMS" />
-    <uses-permission android:name="android.permission.READ_SMS"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-
-    <application >
-        <uses-library android:name="android.test.runner" />
-    </application>
-    <instrumentation android:name="android.bluetooth.BluetoothTestRunner"
-            android:targetPackage="com.android.bluetooth.tests"
-            android:label="Bluetooth Tests" />
-    <instrumentation android:name="android.bluetooth.BluetoothInstrumentation"
-            android:targetPackage="com.android.bluetooth.tests"
-            android:label="Bluetooth Test Utils" />
-
-</manifest>
diff --git a/core/tests/bluetoothtests/AndroidTest.xml b/core/tests/bluetoothtests/AndroidTest.xml
deleted file mode 100644
index f93c4eb..0000000
--- a/core/tests/bluetoothtests/AndroidTest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for Bluetooth test cases">
-    <option name="test-suite-tag" value="apct"/>
-    <option name="test-suite-tag" value="apct-instrumentation"/>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="BluetoothTests.apk" />
-    </target_preparer>
-
-    <option name="test-suite-tag" value="apct"/>
-    <option name="test-tag" value="BluetoothTests"/>
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.bluetooth.tests" />
-        <option name="hidden-api-checks" value="false"/>
-        <option name="runner" value="android.bluetooth.BluetoothTestRunner"/>
-    </test>
-</configuration>
diff --git a/core/tests/bluetoothtests/OWNERS b/core/tests/bluetoothtests/OWNERS
deleted file mode 100644
index 98bb877..0000000
--- a/core/tests/bluetoothtests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /core/java/android/bluetooth/OWNERS
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
deleted file mode 100644
index bd55426..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
+++ /dev/null
@@ -1,329 +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 android.bluetooth;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link BluetoothCodecConfig}.
- * <p>
- * To run this test, use:
- * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
- */
-public class BluetoothCodecConfigTest extends TestCase {
-    private static final int[] kCodecTypeArray = new int[] {
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
-    };
-    private static final int[] kCodecPriorityArray = new int[] {
-        BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
-        BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-        BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
-    };
-    private static final int[] kSampleRateArray = new int[] {
-        BluetoothCodecConfig.SAMPLE_RATE_NONE,
-        BluetoothCodecConfig.SAMPLE_RATE_44100,
-        BluetoothCodecConfig.SAMPLE_RATE_48000,
-        BluetoothCodecConfig.SAMPLE_RATE_88200,
-        BluetoothCodecConfig.SAMPLE_RATE_96000,
-        BluetoothCodecConfig.SAMPLE_RATE_176400,
-        BluetoothCodecConfig.SAMPLE_RATE_192000,
-    };
-    private static final int[] kBitsPerSampleArray = new int[] {
-        BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-    };
-    private static final int[] kChannelModeArray = new int[] {
-        BluetoothCodecConfig.CHANNEL_MODE_NONE,
-        BluetoothCodecConfig.CHANNEL_MODE_MONO,
-        BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-    };
-    private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, };
-    private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, };
-    private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, };
-    private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, };
-
-    private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length *
-        kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length *
-        kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length *
-        kCodecSpecific4Array.length;
-
-    private int selectCodecType(int configId) {
-        int left = kCodecTypeArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecTypeArray.length;
-        return kCodecTypeArray[index];
-    }
-
-    private int selectCodecPriority(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecPriorityArray.length;
-        return kCodecPriorityArray[index];
-    }
-
-    private int selectSampleRate(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kSampleRateArray.length;
-        return kSampleRateArray[index];
-    }
-
-    private int selectBitsPerSample(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kBitsPerSampleArray.length;
-        return kBitsPerSampleArray[index];
-    }
-
-    private int selectChannelMode(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kChannelModeArray.length;
-        return kChannelModeArray[index];
-    }
-
-    private long selectCodecSpecific1(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific1Array.length;
-        return kCodecSpecific1Array[index];
-    }
-
-    private long selectCodecSpecific2(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
-            kCodecSpecific2Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific2Array.length;
-        return kCodecSpecific2Array[index];
-    }
-
-    private long selectCodecSpecific3(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
-            kCodecSpecific2Array.length * kCodecSpecific3Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific3Array.length;
-        return kCodecSpecific3Array[index];
-    }
-
-    private long selectCodecSpecific4(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
-            kCodecSpecific2Array.length * kCodecSpecific3Array.length *
-            kCodecSpecific4Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific4Array.length;
-        return kCodecSpecific4Array[index];
-    }
-
-    @SmallTest
-    public void testBluetoothCodecConfig_valid_get_methods() {
-
-        for (int config_id = 0; config_id < kTotalConfigs; config_id++) {
-            int codec_type = selectCodecType(config_id);
-            int codec_priority = selectCodecPriority(config_id);
-            int sample_rate = selectSampleRate(config_id);
-            int bits_per_sample = selectBitsPerSample(config_id);
-            int channel_mode = selectChannelMode(config_id);
-            long codec_specific1 = selectCodecSpecific1(config_id);
-            long codec_specific2 = selectCodecSpecific2(config_id);
-            long codec_specific3 = selectCodecSpecific3(config_id);
-            long codec_specific4 = selectCodecSpecific4(config_id);
-
-            BluetoothCodecConfig bcc = buildBluetoothCodecConfig(codec_type, codec_priority,
-                                                                sample_rate, bits_per_sample,
-                                                                channel_mode, codec_specific1,
-                                                                codec_specific2, codec_specific3,
-                                                                codec_specific4);
-
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
-                assertTrue(bcc.isMandatoryCodec());
-            } else {
-                assertFalse(bcc.isMandatoryCodec());
-            }
-
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
-                assertEquals("SBC", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) {
-                assertEquals("AAC", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) {
-                assertEquals("aptX", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) {
-                assertEquals("aptX HD", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) {
-                assertEquals("LDAC", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
-                assertEquals("INVALID CODEC", bcc.getCodecName());
-            }
-
-            assertEquals(codec_type, bcc.getCodecType());
-            assertEquals(codec_priority, bcc.getCodecPriority());
-            assertEquals(sample_rate, bcc.getSampleRate());
-            assertEquals(bits_per_sample, bcc.getBitsPerSample());
-            assertEquals(channel_mode, bcc.getChannelMode());
-            assertEquals(codec_specific1, bcc.getCodecSpecific1());
-            assertEquals(codec_specific2, bcc.getCodecSpecific2());
-            assertEquals(codec_specific3, bcc.getCodecSpecific3());
-            assertEquals(codec_specific4, bcc.getCodecSpecific4());
-        }
-    }
-
-    @SmallTest
-    public void testBluetoothCodecConfig_equals() {
-        BluetoothCodecConfig bcc1 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-
-        BluetoothCodecConfig bcc2_same =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertTrue(bcc1.equals(bcc2_same));
-
-        BluetoothCodecConfig bcc3_codec_type =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc3_codec_type));
-
-        BluetoothCodecConfig bcc4_codec_priority =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc4_codec_priority));
-
-        BluetoothCodecConfig bcc5_sample_rate =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc5_sample_rate));
-
-        BluetoothCodecConfig bcc6_bits_per_sample =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc6_bits_per_sample));
-
-        BluetoothCodecConfig bcc7_channel_mode =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc7_channel_mode));
-
-        BluetoothCodecConfig bcc8_codec_specific1 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1001, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc8_codec_specific1));
-
-        BluetoothCodecConfig bcc9_codec_specific2 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2002, 3000, 4000);
-        assertFalse(bcc1.equals(bcc9_codec_specific2));
-
-        BluetoothCodecConfig bcc10_codec_specific3 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3003, 4000);
-        assertFalse(bcc1.equals(bcc10_codec_specific3));
-
-        BluetoothCodecConfig bcc11_codec_specific4 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4004);
-        assertFalse(bcc1.equals(bcc11_codec_specific4));
-    }
-
-    private BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType,
-            int codecPriority, int sampleRate, int bitsPerSample, int channelMode,
-            long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) {
-        return new BluetoothCodecConfig.Builder()
-                    .setCodecType(sourceCodecType)
-                    .setCodecPriority(codecPriority)
-                    .setSampleRate(sampleRate)
-                    .setBitsPerSample(bitsPerSample)
-                    .setChannelMode(channelMode)
-                    .setCodecSpecific1(codecSpecific1)
-                    .setCodecSpecific2(codecSpecific2)
-                    .setCodecSpecific3(codecSpecific3)
-                    .setCodecSpecific4(codecSpecific4)
-                    .build();
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
deleted file mode 100644
index 1cb2dca..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
+++ /dev/null
@@ -1,493 +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 android.bluetooth;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Unit test cases for {@link BluetoothCodecStatus}.
- * <p>
- * To run this test, use:
- * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
- */
-public class BluetoothCodecStatusTest extends TestCase {
-
-    // Codec configs: A and B are same; C is different
-    private static final BluetoothCodecConfig config_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig config_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig config_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    // Local capabilities: A and B are same; C is different
-    private static final BluetoothCodecConfig local_capability1_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability1_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability1_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-
-    private static final BluetoothCodecConfig local_capability2_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability2_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability2_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability3_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability3_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability3_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability4_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability4_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability4_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability5_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability5_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability5_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-
-    // Selectable capabilities: A and B are same; C is different
-    private static final BluetoothCodecConfig selectable_capability1_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability1_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability1_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability2_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability2_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability2_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability3_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability3_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability3_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability4_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability4_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability4_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability5_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability5_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability5_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_A =
-            new ArrayList() {{
-                    add(local_capability1_A);
-                    add(local_capability2_A);
-                    add(local_capability3_A);
-                    add(local_capability4_A);
-                    add(local_capability5_A);
-            }};
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B =
-            new ArrayList() {{
-                    add(local_capability1_B);
-                    add(local_capability2_B);
-                    add(local_capability3_B);
-                    add(local_capability4_B);
-                    add(local_capability5_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B_REORDERED =
-            new ArrayList() {{
-                    add(local_capability5_B);
-                    add(local_capability4_B);
-                    add(local_capability2_B);
-                    add(local_capability3_B);
-                    add(local_capability1_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_C =
-            new ArrayList() {{
-                    add(local_capability1_C);
-                    add(local_capability2_C);
-                    add(local_capability3_C);
-                    add(local_capability4_C);
-                    add(local_capability5_C);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_A =
-            new ArrayList() {{
-                    add(selectable_capability1_A);
-                    add(selectable_capability2_A);
-                    add(selectable_capability3_A);
-                    add(selectable_capability4_A);
-                    add(selectable_capability5_A);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B =
-            new ArrayList() {{
-                    add(selectable_capability1_B);
-                    add(selectable_capability2_B);
-                    add(selectable_capability3_B);
-                    add(selectable_capability4_B);
-                    add(selectable_capability5_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B_REORDERED =
-            new ArrayList() {{
-                    add(selectable_capability5_B);
-                    add(selectable_capability4_B);
-                    add(selectable_capability2_B);
-                    add(selectable_capability3_B);
-                    add(selectable_capability1_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_C =
-            new ArrayList() {{
-                    add(selectable_capability1_C);
-                    add(selectable_capability2_C);
-                    add(selectable_capability3_C);
-                    add(selectable_capability4_C);
-                    add(selectable_capability5_C);
-            }};
-
-    private static final BluetoothCodecStatus bcs_A =
-            new BluetoothCodecStatus(config_A, LOCAL_CAPABILITY_A, SELECTABLE_CAPABILITY_A);
-    private static final BluetoothCodecStatus bcs_B =
-            new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B, SELECTABLE_CAPABILITY_B);
-    private static final BluetoothCodecStatus bcs_B_reordered =
-            new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B_REORDERED,
-                                 SELECTABLE_CAPABILITY_B_REORDERED);
-    private static final BluetoothCodecStatus bcs_C =
-            new BluetoothCodecStatus(config_C, LOCAL_CAPABILITY_C, SELECTABLE_CAPABILITY_C);
-
-    @SmallTest
-    public void testBluetoothCodecStatus_get_methods() {
-
-        assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A));
-        assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B));
-        assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C));
-
-        assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_A));
-        assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_B));
-        assertFalse(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_C));
-
-        assertTrue(bcs_A.getCodecsSelectableCapabilities()
-                                 .equals(SELECTABLE_CAPABILITY_A));
-        assertTrue(bcs_A.getCodecsSelectableCapabilities()
-                                  .equals(SELECTABLE_CAPABILITY_B));
-        assertFalse(bcs_A.getCodecsSelectableCapabilities()
-                                  .equals(SELECTABLE_CAPABILITY_C));
-    }
-
-    @SmallTest
-    public void testBluetoothCodecStatus_equals() {
-        assertTrue(bcs_A.equals(bcs_B));
-        assertTrue(bcs_B.equals(bcs_A));
-        assertTrue(bcs_A.equals(bcs_B_reordered));
-        assertTrue(bcs_B_reordered.equals(bcs_A));
-        assertFalse(bcs_A.equals(bcs_C));
-        assertFalse(bcs_C.equals(bcs_A));
-    }
-
-    private static BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType,
-            int codecPriority, int sampleRate, int bitsPerSample, int channelMode,
-            long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) {
-        return new BluetoothCodecConfig.Builder()
-                    .setCodecType(sourceCodecType)
-                    .setCodecPriority(codecPriority)
-                    .setSampleRate(sampleRate)
-                    .setBitsPerSample(bitsPerSample)
-                    .setChannelMode(channelMode)
-                    .setCodecSpecific1(codecSpecific1)
-                    .setCodecSpecific2(codecSpecific2)
-                    .setCodecSpecific3(codecSpecific3)
-                    .setCodecSpecific4(codecSpecific4)
-                    .build();
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
deleted file mode 100644
index 37b2a50..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.bluetooth;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.os.Bundle;
-
-import junit.framework.Assert;
-
-import java.util.Set;
-
-public class BluetoothInstrumentation extends Instrumentation {
-
-    private BluetoothTestUtils mUtils = null;
-    private BluetoothAdapter mAdapter = null;
-    private Bundle mArgs = null;
-    private Bundle mSuccessResult = null;
-
-    private BluetoothTestUtils getBluetoothTestUtils() {
-        if (mUtils == null) {
-            mUtils = new BluetoothTestUtils(getContext(),
-                    BluetoothInstrumentation.class.getSimpleName());
-        }
-        return mUtils;
-    }
-
-    private BluetoothAdapter getBluetoothAdapter() {
-        if (mAdapter == null) {
-            mAdapter = ((BluetoothManager)getContext().getSystemService(
-                    Context.BLUETOOTH_SERVICE)).getAdapter();
-        }
-        return mAdapter;
-    }
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        super.onCreate(arguments);
-        mArgs = arguments;
-        // create the default result response, but only use it in success code path
-        mSuccessResult = new Bundle();
-        mSuccessResult.putString("result", "SUCCESS");
-        start();
-    }
-
-    @Override
-    public void onStart() {
-        String command = mArgs.getString("command");
-        if ("enable".equals(command)) {
-            enable();
-        } else if ("disable".equals(command)) {
-            disable();
-        } else if ("unpairAll".equals(command)) {
-            unpairAll();
-        } else if ("getName".equals(command)) {
-            getName();
-        } else if ("getAddress".equals(command)) {
-            getAddress();
-        } else if ("getBondedDevices".equals(command)) {
-            getBondedDevices();
-        } else {
-            finish(null);
-        }
-    }
-
-    public void enable() {
-        getBluetoothTestUtils().enable(getBluetoothAdapter());
-        finish(mSuccessResult);
-    }
-
-    public void disable() {
-        getBluetoothTestUtils().disable(getBluetoothAdapter());
-        finish(mSuccessResult);
-    }
-
-    public void unpairAll() {
-        getBluetoothTestUtils().unpairAll(getBluetoothAdapter());
-        finish(mSuccessResult);
-    }
-
-    public void getName() {
-        String name = getBluetoothAdapter().getName();
-        mSuccessResult.putString("name", name);
-        finish(mSuccessResult);
-    }
-
-    public void getAddress() {
-        String name = getBluetoothAdapter().getAddress();
-        mSuccessResult.putString("address", name);
-        finish(mSuccessResult);
-    }
-
-    public void getBondedDevices() {
-        Set<BluetoothDevice> devices = getBluetoothAdapter().getBondedDevices();
-        int i = 0;
-        for (BluetoothDevice device : devices) {
-            mSuccessResult.putString(String.format("device-%02d", i), device.getAddress());
-            i++;
-        }
-        finish(mSuccessResult);
-    }
-
-    public void finish(Bundle result) {
-        if (result == null) {
-            result = new Bundle();
-        }
-        finish(Activity.RESULT_OK, result);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java
deleted file mode 100644
index c3d707c..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link BluetoothLeAudioCodecConfig}.
- */
-public class BluetoothLeAudioCodecConfigTest extends TestCase {
-    private int[] mCodecTypeArray = new int[] {
-        BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3,
-        BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID,
-    };
-
-    @SmallTest
-    public void testBluetoothLeAudioCodecConfig_valid_get_methods() {
-
-        for (int codecIdx = 0; codecIdx < mCodecTypeArray.length; codecIdx++) {
-            int codecType = mCodecTypeArray[codecIdx];
-
-            BluetoothLeAudioCodecConfig leAudioCodecConfig =
-                    buildBluetoothLeAudioCodecConfig(codecType);
-
-            if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) {
-                assertEquals("LC3", leAudioCodecConfig.getCodecName());
-            }
-            if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
-                assertEquals("INVALID CODEC", leAudioCodecConfig.getCodecName());
-            }
-
-            assertEquals(codecType, leAudioCodecConfig.getCodecType());
-        }
-    }
-
-    private BluetoothLeAudioCodecConfig buildBluetoothLeAudioCodecConfig(int sourceCodecType) {
-        return new BluetoothLeAudioCodecConfig.Builder()
-                    .setCodecType(sourceCodecType)
-                    .build();
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
deleted file mode 100644
index 33e9dd7..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
+++ /dev/null
@@ -1,94 +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.
- */
-
-package android.bluetooth;
-
-import android.content.Context;
-import android.test.InstrumentationTestCase;
-
-/**
- * Instrumentation test case for stress test involving rebooting the device.
- * <p>
- * This test case tests that bluetooth is enabled after a device reboot. Because
- * the device will reboot, the instrumentation must be driven by a script on the
- * host side.
- */
-public class BluetoothRebootStressTest extends InstrumentationTestCase {
-    private static final String TAG = "BluetoothRebootStressTest";
-    private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt";
-
-    private BluetoothTestUtils mTestUtils;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Context context = getInstrumentation().getTargetContext();
-        mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        mTestUtils.close();
-    }
-
-    /**
-     * Test method used to start the test by turning bluetooth on.
-     */
-    public void testStart() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        mTestUtils.enable(adapter);
-    }
-
-    /**
-     * Test method used in the middle iterations of the test to check if
-     * bluetooth is on. Does not toggle bluetooth after the check. Assumes that
-     * bluetooth has been turned on by {@code #testStart()}
-     */
-    public void testMiddleNoToggle() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        assertTrue(adapter.isEnabled());
-    }
-
-    /**
-     * Test method used in the middle iterations of the test to check if
-     * bluetooth is on. Toggles bluetooth after the check. Assumes that
-     * bluetooth has been turned on by {@code #testStart()}
-     */
-    public void testMiddleToggle() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        assertTrue(adapter.isEnabled());
-
-        mTestUtils.disable(adapter);
-        mTestUtils.enable(adapter);
-    }
-
-    /**
-     * Test method used in the stop the test by turning bluetooth off. Assumes
-     * that bluetooth has been turned on by {@code #testStart()}
-     */
-    public void testStop() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        assertTrue(adapter.isEnabled());
-
-        mTestUtils.disable(adapter);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
deleted file mode 100644
index 89dbe3f..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ /dev/null
@@ -1,393 +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.
- */
-
-package android.bluetooth;
-
-import android.content.Context;
-import android.test.InstrumentationTestCase;
-
-/**
- * Stress test suite for Bluetooth related functions.
- *
- * Includes tests for enabling/disabling bluetooth, enabling/disabling discoverable mode,
- * starting/stopping scans, connecting/disconnecting to HFP, A2DP, HID, PAN profiles, and verifying
- * that remote connections/disconnections occur for the PAN profile.
- * <p>
- * This test suite uses {@link android.bluetooth.BluetoothTestRunner} to for parameters such as the
- * number of iterations and the addresses of remote Bluetooth devices.
- */
-public class BluetoothStressTest extends InstrumentationTestCase {
-    private static final String TAG = "BluetoothStressTest";
-    private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt";
-    /** The amount of time to sleep between issuing start/stop SCO in ms. */
-    private static final long SCO_SLEEP_TIME = 2 * 1000;
-
-    private BluetoothAdapter mAdapter;
-    private BluetoothTestUtils mTestUtils;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Context context = getInstrumentation().getTargetContext();
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
-
-        // Start all tests in a disabled state.
-        if (mAdapter.isEnabled()) {
-            mTestUtils.disable(mAdapter);
-        }
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        mTestUtils.close();
-    }
-
-    /**
-     * Stress test for enabling and disabling Bluetooth.
-     */
-    public void testEnable() {
-        int iterations = BluetoothTestRunner.sEnableIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.enable(mAdapter);
-            mTestUtils.disable(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for putting the device in and taking the device out of discoverable mode.
-     */
-    public void testDiscoverable() {
-        int iterations = BluetoothTestRunner.sDiscoverableIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        mTestUtils.enable(mAdapter);
-        mTestUtils.undiscoverable(mAdapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.discoverable(mAdapter);
-            mTestUtils.undiscoverable(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for starting and stopping Bluetooth scans.
-     */
-    public void testScan() {
-        int iterations = BluetoothTestRunner.sScanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        mTestUtils.enable(mAdapter);
-        mTestUtils.stopScan(mAdapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.startScan(mAdapter);
-            mTestUtils.stopScan(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for enabling and disabling the PAN NAP profile.
-     */
-    public void testEnablePan() {
-        int iterations = BluetoothTestRunner.sEnablePanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        mTestUtils.enable(mAdapter);
-        mTestUtils.disablePan(mAdapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of "
-                    + iterations);
-            mTestUtils.enablePan(mAdapter);
-            mTestUtils.disablePan(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for pairing and unpairing with a remote device.
-     * <p>
-     * In this test, the local device initiates pairing with a remote device, and then unpairs with
-     * the device after the pairing has successfully completed.
-     */
-    public void testPair() {
-        int iterations = BluetoothTestRunner.sPairIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                    BluetoothTestRunner.sDevicePairPin);
-            mTestUtils.unpair(mAdapter, device);
-        }
-    }
-
-    /**
-     * Stress test for accepting a pairing request and unpairing with a remote device.
-     * <p>
-     * In this test, the local device waits for a pairing request from a remote device.  It accepts
-     * the request and then unpairs after the paring has successfully completed.
-     */
-    public void testAcceptPair() {
-        int iterations = BluetoothTestRunner.sPairIterations;
-        if (iterations == 0) {
-            return;
-        }
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                    BluetoothTestRunner.sDevicePairPin);
-            mTestUtils.unpair(mAdapter, device);
-        }
-    }
-
-    /**
-     * Stress test for connecting and disconnecting with an A2DP source.
-     * <p>
-     * In this test, the local device plays the role of an A2DP sink, and initiates connections and
-     * disconnections with an A2DP source.
-     */
-    public void testConnectA2dp() {
-        int iterations = BluetoothTestRunner.sConnectA2dpIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, null);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.A2DP,
-                    String.format("connectA2dp(device=%s)", device));
-            mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP,
-                    String.format("disconnectA2dp(device=%s)", device));
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for connecting and disconnecting the HFP with a hands free device.
-     * <p>
-     * In this test, the local device plays the role of an HFP audio gateway, and initiates
-     * connections and disconnections with a hands free device.
-     */
-    public void testConnectHeadset() {
-        int iterations = BluetoothTestRunner.sConnectHeadsetIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET,
-                    String.format("connectHeadset(device=%s)", device));
-            mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET,
-                    String.format("disconnectHeadset(device=%s)", device));
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for connecting and disconnecting with a HID device.
-     * <p>
-     * In this test, the local device plays the role of a HID host, and initiates connections and
-     * disconnections with a HID device.
-     */
-    public void testConnectInput() {
-        int iterations = BluetoothTestRunner.sConnectInputIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST,
-                    String.format("connectInput(device=%s)", device));
-            mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST,
-                    String.format("disconnectInput(device=%s)", device));
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for connecting and disconnecting with a PAN NAP.
-     * <p>
-     * In this test, the local device plays the role of a PANU, and initiates connections and
-     * disconnections with a NAP.
-     */
-    public void testConnectPan() {
-        int iterations = BluetoothTestRunner.sConnectPanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectPan(mAdapter, device);
-            mTestUtils.disconnectPan(mAdapter, device);
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for verifying a PANU connecting and disconnecting with the device.
-     * <p>
-     * In this test, the local device plays the role of a NAP which a remote PANU connects and
-     * disconnects from.
-     */
-    public void testIncomingPanConnection() {
-        int iterations = BluetoothTestRunner.sConnectPanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.disablePan(mAdapter);
-        mTestUtils.enablePan(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of "
-                    + iterations);
-            mTestUtils.incomingPanConnection(mAdapter, device);
-            mTestUtils.incomingPanDisconnection(mAdapter, device);
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.disablePan(mAdapter);
-    }
-
-    /**
-     * Stress test for verifying that AudioManager can open and close SCO connections.
-     * <p>
-     * In this test, a HSP connection is opened with an external headset and the SCO connection is
-     * repeatibly opened and closed.
-     */
-    public void testStartStopSco() {
-        int iterations = BluetoothTestRunner.sStartStopScoIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-        mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-        mTestUtils.stopSco(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.startSco(mAdapter, device);
-            sleep(SCO_SLEEP_TIME);
-            mTestUtils.stopSco(mAdapter, device);
-            sleep(SCO_SLEEP_TIME);
-        }
-
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /* Make sure there is at least 1 unread message in the last week on remote device */
-    public void testMceSetMessageStatus() {
-        int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null);
-        mTestUtils.mceGetUnreadMessage(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ);
-            mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD);
-        }
-
-        /**
-         * It is hard to find device to support set undeleted status, so just
-         * set deleted in 1 iteration
-         **/
-        mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED);
-    }
-
-    private void sleep(long time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-        }
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
deleted file mode 100644
index d19c2c3..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
+++ /dev/null
@@ -1,225 +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.
- */
-
-package android.bluetooth;
-
-import junit.framework.TestSuite;
-
-import android.os.Bundle;
-import android.test.InstrumentationTestRunner;
-import android.test.InstrumentationTestSuite;
-import android.util.Log;
-
-/**
- * Instrumentation test runner for Bluetooth tests.
- * <p>
- * To run:
- * <pre>
- * {@code
- * adb shell am instrument \
- *     [-e enable_iterations <iterations>] \
- *     [-e discoverable_iterations <iterations>] \
- *     [-e scan_iterations <iterations>] \
- *     [-e enable_pan_iterations <iterations>] \
- *     [-e pair_iterations <iterations>] \
- *     [-e connect_a2dp_iterations <iterations>] \
- *     [-e connect_headset_iterations <iterations>] \
- *     [-e connect_input_iterations <iterations>] \
- *     [-e connect_pan_iterations <iterations>] \
- *     [-e start_stop_sco_iterations <iterations>] \
- *     [-e mce_set_message_status_iterations <iterations>] \
- *     [-e pair_address <address>] \
- *     [-e headset_address <address>] \
- *     [-e a2dp_address <address>] \
- *     [-e input_address <address>] \
- *     [-e pan_address <address>] \
- *     [-e pair_pin <pin>] \
- *     [-e pair_passkey <passkey>] \
- *     -w com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner
- * }
- * </pre>
- */
-public class BluetoothTestRunner extends InstrumentationTestRunner {
-    private static final String TAG = "BluetoothTestRunner";
-
-    public static int sEnableIterations = 100;
-    public static int sDiscoverableIterations = 1000;
-    public static int sScanIterations = 1000;
-    public static int sEnablePanIterations = 1000;
-    public static int sPairIterations = 100;
-    public static int sConnectHeadsetIterations = 100;
-    public static int sConnectA2dpIterations = 100;
-    public static int sConnectInputIterations = 100;
-    public static int sConnectPanIterations = 100;
-    public static int sStartStopScoIterations = 100;
-    public static int sMceSetMessageStatusIterations = 100;
-
-    public static String sDeviceAddress = "";
-    public static byte[] sDevicePairPin = {'1', '2', '3', '4'};
-    public static int sDevicePairPasskey = 123456;
-
-    @Override
-    public TestSuite getAllTests() {
-        TestSuite suite = new InstrumentationTestSuite(this);
-        suite.addTestSuite(BluetoothStressTest.class);
-        return suite;
-    }
-
-    @Override
-    public ClassLoader getLoader() {
-        return BluetoothTestRunner.class.getClassLoader();
-    }
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        String val = arguments.getString("enable_iterations");
-        if (val != null) {
-            try {
-                sEnableIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("discoverable_iterations");
-        if (val != null) {
-            try {
-                sDiscoverableIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("scan_iterations");
-        if (val != null) {
-            try {
-                sScanIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("enable_pan_iterations");
-        if (val != null) {
-            try {
-                sEnablePanIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("pair_iterations");
-        if (val != null) {
-            try {
-                sPairIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_a2dp_iterations");
-        if (val != null) {
-            try {
-                sConnectA2dpIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_headset_iterations");
-        if (val != null) {
-            try {
-                sConnectHeadsetIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_input_iterations");
-        if (val != null) {
-            try {
-                sConnectInputIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_pan_iterations");
-        if (val != null) {
-            try {
-                sConnectPanIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("start_stop_sco_iterations");
-        if (val != null) {
-            try {
-                sStartStopScoIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("mce_set_message_status_iterations");
-        if (val != null) {
-            try {
-                sMceSetMessageStatusIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("device_address");
-        if (val != null) {
-            sDeviceAddress = val;
-        }
-
-        val = arguments.getString("device_pair_pin");
-        if (val != null) {
-            byte[] pin = BluetoothDevice.convertPinToBytes(val);
-            if (pin != null) {
-                sDevicePairPin = pin;
-            }
-        }
-
-        val = arguments.getString("device_pair_passkey");
-        if (val != null) {
-            try {
-                sDevicePairPasskey = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations));
-        Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations));
-        Log.i(TAG, String.format("scan_iterations=%d", sScanIterations));
-        Log.i(TAG, String.format("pair_iterations=%d", sPairIterations));
-        Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations));
-        Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations));
-        Log.i(TAG, String.format("connect_input_iterations=%d", sConnectInputIterations));
-        Log.i(TAG, String.format("connect_pan_iterations=%d", sConnectPanIterations));
-        Log.i(TAG, String.format("start_stop_sco_iterations=%d", sStartStopScoIterations));
-        Log.i(TAG, String.format("device_address=%s", sDeviceAddress));
-        Log.i(TAG, String.format("device_pair_pin=%s", new String(sDevicePairPin)));
-        Log.i(TAG, String.format("device_pair_passkey=%d", sDevicePairPasskey));
-
-        // Call onCreate last since we want to set the static variables first.
-        super.onCreate(arguments);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
deleted file mode 100644
index 8eb6ebc..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ /dev/null
@@ -1,1651 +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.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.BluetoothPan;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.os.Environment;
-import android.util.Log;
-
-import junit.framework.Assert;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-public class BluetoothTestUtils extends Assert {
-
-    /** Timeout for enable/disable in ms. */
-    private static final int ENABLE_DISABLE_TIMEOUT = 20000;
-    /** Timeout for discoverable/undiscoverable in ms. */
-    private static final int DISCOVERABLE_UNDISCOVERABLE_TIMEOUT = 5000;
-    /** Timeout for starting/stopping a scan in ms. */
-    private static final int START_STOP_SCAN_TIMEOUT = 5000;
-    /** Timeout for pair/unpair in ms. */
-    private static final int PAIR_UNPAIR_TIMEOUT = 20000;
-    /** Timeout for connecting/disconnecting a profile in ms. */
-    private static final int CONNECT_DISCONNECT_PROFILE_TIMEOUT = 20000;
-    /** Timeout to start or stop a SCO channel in ms. */
-    private static final int START_STOP_SCO_TIMEOUT = 10000;
-    /** Timeout to connect a profile proxy in ms. */
-    private static final int CONNECT_PROXY_TIMEOUT = 5000;
-    /** Time between polls in ms. */
-    private static final int POLL_TIME = 100;
-    /** Timeout to get map message in ms. */
-    private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000;
-    /** Timeout to set map message status in ms. */
-    private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000;
-
-    private abstract class FlagReceiver extends BroadcastReceiver {
-        private int mExpectedFlags = 0;
-        private int mFiredFlags = 0;
-        private long mCompletedTime = -1;
-
-        public FlagReceiver(int expectedFlags) {
-            mExpectedFlags = expectedFlags;
-        }
-
-        public int getFiredFlags() {
-            synchronized (this) {
-                return mFiredFlags;
-            }
-        }
-
-        public long getCompletedTime() {
-            synchronized (this) {
-                return mCompletedTime;
-            }
-        }
-
-        protected void setFiredFlag(int flag) {
-            synchronized (this) {
-                mFiredFlags |= flag;
-                if ((mFiredFlags & mExpectedFlags) == mExpectedFlags) {
-                    mCompletedTime = System.currentTimeMillis();
-                }
-            }
-        }
-    }
-
-    private class BluetoothReceiver extends FlagReceiver {
-        private static final int DISCOVERY_STARTED_FLAG = 1;
-        private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
-        private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
-        private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
-        private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
-        private static final int STATE_OFF_FLAG = 1 << 5;
-        private static final int STATE_TURNING_ON_FLAG = 1 << 6;
-        private static final int STATE_ON_FLAG = 1 << 7;
-        private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
-        private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9;
-        private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10;
-
-        public BluetoothReceiver(int expectedFlags) {
-            super(expectedFlags);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
-                setFiredFlag(DISCOVERY_STARTED_FLAG);
-            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
-                setFiredFlag(DISCOVERY_FINISHED_FLAG);
-            } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
-                int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1);
-                assertNotSame(-1, mode);
-                switch (mode) {
-                    case BluetoothAdapter.SCAN_MODE_NONE:
-                        setFiredFlag(SCAN_MODE_NONE_FLAG);
-                        break;
-                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
-                        setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG);
-                        break;
-                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
-                        setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG);
-                        break;
-                }
-            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
-                assertNotSame(-1, state);
-                switch (state) {
-                    case BluetoothAdapter.STATE_OFF:
-                        setFiredFlag(STATE_OFF_FLAG);
-                        break;
-                    case BluetoothAdapter.STATE_TURNING_ON:
-                        setFiredFlag(STATE_TURNING_ON_FLAG);
-                        break;
-                    case BluetoothAdapter.STATE_ON:
-                        setFiredFlag(STATE_ON_FLAG);
-                        break;
-                    case BluetoothAdapter.STATE_TURNING_OFF:
-                        setFiredFlag(STATE_TURNING_OFF_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-    private class PairReceiver extends FlagReceiver {
-        private static final int STATE_BONDED_FLAG = 1;
-        private static final int STATE_BONDING_FLAG = 1 << 1;
-        private static final int STATE_NONE_FLAG = 1 << 2;
-
-        private BluetoothDevice mDevice;
-        private int mPasskey;
-        private byte[] mPin;
-
-        public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) {
-            super(expectedFlags);
-
-            mDevice = device;
-            mPasskey = passkey;
-            mPin = pin;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
-                return;
-            }
-
-            if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
-                int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1);
-                assertNotSame(-1, varient);
-                switch (varient) {
-                    case BluetoothDevice.PAIRING_VARIANT_PIN:
-                    case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
-                        mDevice.setPin(mPin);
-                        break;
-                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                        break;
-                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
-                    case BluetoothDevice.PAIRING_VARIANT_CONSENT:
-                        mDevice.setPairingConfirmation(true);
-                        break;
-                    case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                        break;
-                }
-            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
-                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
-                assertNotSame(-1, state);
-                switch (state) {
-                    case BluetoothDevice.BOND_NONE:
-                        setFiredFlag(STATE_NONE_FLAG);
-                        break;
-                    case BluetoothDevice.BOND_BONDING:
-                        setFiredFlag(STATE_BONDING_FLAG);
-                        break;
-                    case BluetoothDevice.BOND_BONDED:
-                        setFiredFlag(STATE_BONDED_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-    private class ConnectProfileReceiver extends FlagReceiver {
-        private static final int STATE_DISCONNECTED_FLAG = 1;
-        private static final int STATE_CONNECTING_FLAG = 1 << 1;
-        private static final int STATE_CONNECTED_FLAG = 1 << 2;
-        private static final int STATE_DISCONNECTING_FLAG = 1 << 3;
-
-        private BluetoothDevice mDevice;
-        private int mProfile;
-        private String mConnectionAction;
-
-        public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) {
-            super(expectedFlags);
-
-            mDevice = device;
-            mProfile = profile;
-
-            switch (mProfile) {
-                case BluetoothProfile.A2DP:
-                    mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.HEADSET:
-                    mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.HID_HOST:
-                    mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.PAN:
-                    mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.MAP_CLIENT:
-                    mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                default:
-                    mConnectionAction = null;
-            }
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) {
-                if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
-                    return;
-                }
-
-                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-                assertNotSame(-1, state);
-                switch (state) {
-                    case BluetoothProfile.STATE_DISCONNECTED:
-                        setFiredFlag(STATE_DISCONNECTED_FLAG);
-                        break;
-                    case BluetoothProfile.STATE_CONNECTING:
-                        setFiredFlag(STATE_CONNECTING_FLAG);
-                        break;
-                    case BluetoothProfile.STATE_CONNECTED:
-                        setFiredFlag(STATE_CONNECTED_FLAG);
-                        break;
-                    case BluetoothProfile.STATE_DISCONNECTING:
-                        setFiredFlag(STATE_DISCONNECTING_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-    private class ConnectPanReceiver extends ConnectProfileReceiver {
-        private int mRole;
-
-        public ConnectPanReceiver(BluetoothDevice device, int role, int expectedFlags) {
-            super(device, BluetoothProfile.PAN, expectedFlags);
-
-            mRole = role;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mRole != intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, -1)) {
-                return;
-            }
-
-            super.onReceive(context, intent);
-        }
-    }
-
-    private class StartStopScoReceiver extends FlagReceiver {
-        private static final int STATE_CONNECTED_FLAG = 1;
-        private static final int STATE_DISCONNECTED_FLAG = 1 << 1;
-
-        public StartStopScoReceiver(int expectedFlags) {
-            super(expectedFlags);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())) {
-                int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
-                        AudioManager.SCO_AUDIO_STATE_ERROR);
-                assertNotSame(AudioManager.SCO_AUDIO_STATE_ERROR, state);
-                switch(state) {
-                    case AudioManager.SCO_AUDIO_STATE_CONNECTED:
-                        setFiredFlag(STATE_CONNECTED_FLAG);
-                        break;
-                    case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
-                        setFiredFlag(STATE_DISCONNECTED_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-
-    private class MceSetMessageStatusReceiver extends FlagReceiver {
-        private static final int MESSAGE_RECEIVED_FLAG = 1;
-        private static final int STATUS_CHANGED_FLAG = 1 << 1;
-
-        public MceSetMessageStatusReceiver(int expectedFlags) {
-            super(expectedFlags);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
-                String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE);
-                assertNotNull(handle);
-                setFiredFlag(MESSAGE_RECEIVED_FLAG);
-                mMsgHandle = handle;
-            } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) {
-                int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
-                assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
-                setFiredFlag(STATUS_CHANGED_FLAG);
-            } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) {
-                int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
-                assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
-                setFiredFlag(STATUS_CHANGED_FLAG);
-            }
-        }
-    }
-
-    private BluetoothProfile.ServiceListener mServiceListener =
-            new BluetoothProfile.ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            synchronized (this) {
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = (BluetoothA2dp) proxy;
-                        break;
-                    case BluetoothProfile.HEADSET:
-                        mHeadset = (BluetoothHeadset) proxy;
-                        break;
-                    case BluetoothProfile.HID_HOST:
-                        mInput = (BluetoothHidHost) proxy;
-                        break;
-                    case BluetoothProfile.PAN:
-                        mPan = (BluetoothPan) proxy;
-                        break;
-                    case BluetoothProfile.MAP_CLIENT:
-                        mMce = (BluetoothMapClient) proxy;
-                        break;
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(int profile) {
-            synchronized (this) {
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = null;
-                        break;
-                    case BluetoothProfile.HEADSET:
-                        mHeadset = null;
-                        break;
-                    case BluetoothProfile.HID_HOST:
-                        mInput = null;
-                        break;
-                    case BluetoothProfile.PAN:
-                        mPan = null;
-                        break;
-                    case BluetoothProfile.MAP_CLIENT:
-                        mMce = null;
-                        break;
-                }
-            }
-        }
-    };
-
-    private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>();
-
-    private BufferedWriter mOutputWriter;
-    private String mTag;
-    private String mOutputFile;
-
-    private Context mContext;
-    private BluetoothA2dp mA2dp = null;
-    private BluetoothHeadset mHeadset = null;
-    private BluetoothHidHost mInput = null;
-    private BluetoothPan mPan = null;
-    private BluetoothMapClient mMce = null;
-    private String mMsgHandle = null;
-
-    /**
-     * Creates a utility instance for testing Bluetooth.
-     *
-     * @param context The context of the application using the utility.
-     * @param tag The log tag of the application using the utility.
-     */
-    public BluetoothTestUtils(Context context, String tag) {
-        this(context, tag, null);
-    }
-
-    /**
-     * Creates a utility instance for testing Bluetooth.
-     *
-     * @param context The context of the application using the utility.
-     * @param tag The log tag of the application using the utility.
-     * @param outputFile The path to an output file if the utility is to write results to a
-     *        separate file.
-     */
-    public BluetoothTestUtils(Context context, String tag, String outputFile) {
-        mContext = context;
-        mTag = tag;
-        mOutputFile = outputFile;
-
-        if (mOutputFile == null) {
-            mOutputWriter = null;
-        } else {
-            try {
-                mOutputWriter = new BufferedWriter(new FileWriter(new File(
-                        Environment.getExternalStorageDirectory(), mOutputFile), true));
-            } catch (IOException e) {
-                Log.w(mTag, "Test output file could not be opened", e);
-                mOutputWriter = null;
-            }
-        }
-    }
-
-    /**
-     * Closes the utility instance and unregisters any BroadcastReceivers.
-     */
-    public void close() {
-        while (!mReceivers.isEmpty()) {
-            mContext.unregisterReceiver(mReceivers.remove(0));
-        }
-
-        if (mOutputWriter != null) {
-            try {
-                mOutputWriter.close();
-            } catch (IOException e) {
-                Log.w(mTag, "Test output file could not be closed", e);
-            }
-        }
-    }
-
-    /**
-     * Enables Bluetooth and checks to make sure that Bluetooth was turned on and that the correct
-     * actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void enable(BluetoothAdapter adapter) {
-        writeOutput("Enabling Bluetooth adapter.");
-        assertFalse(adapter.isEnabled());
-        int btState = adapter.getState();
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                        BluetoothAdapter.ERROR);
-                if (state == BluetoothAdapter.STATE_ON) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
-        // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
-        // So no assertion applied here.
-        adapter.enable();
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
-            writeOutput(String.format("enable() completed in 0 ms"));
-        } catch (final InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("enable() timeout: state=%d (expected %d)", btState,
-                    BluetoothAdapter.STATE_ON));
-        }
-    }
-
-    /**
-     * Disables Bluetooth and checks to make sure that Bluetooth was turned off and that the correct
-     * actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void disable(BluetoothAdapter adapter) {
-        writeOutput("Disabling Bluetooth adapter.");
-        assertTrue(adapter.isEnabled());
-        int btState = adapter.getState();
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                        BluetoothAdapter.ERROR);
-                if (state == BluetoothAdapter.STATE_OFF) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
-        // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
-        // So no assertion applied here.
-        adapter.disable();
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
-            writeOutput(String.format("disable() completed in 0 ms"));
-        } catch (final InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("disable() timeout: state=%d (expected %d)", btState,
-                    BluetoothAdapter.STATE_OFF));
-        }
-    }
-
-    /**
-     * Puts the local device into discoverable mode and checks to make sure that the local device
-     * is in discoverable mode and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void discoverable(BluetoothAdapter adapter) {
-        if (!adapter.isEnabled()) {
-            fail("discoverable() bluetooth not enabled");
-        }
-
-        int scanMode = adapter.getScanMode();
-        if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-            return;
-        }
-
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                        BluetoothAdapter.SCAN_MODE_NONE);
-                if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE),
-                BluetoothStatusCodes.SUCCESS);
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
-                    TimeUnit.MILLISECONDS);
-            writeOutput(String.format("discoverable() completed in 0 ms"));
-        } catch (final InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("discoverable() timeout: scanMode=%d (expected %d)", scanMode,
-                    BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
-        }
-    }
-
-    /**
-     * Puts the local device into connectable only mode and checks to make sure that the local
-     * device is in in connectable mode and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void undiscoverable(BluetoothAdapter adapter) {
-        if (!adapter.isEnabled()) {
-            fail("undiscoverable() bluetooth not enabled");
-        }
-
-        int scanMode = adapter.getScanMode();
-        if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-            return;
-        }
-
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                        BluetoothAdapter.SCAN_MODE_NONE);
-                if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE),
-                BluetoothStatusCodes.SUCCESS);
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
-                    TimeUnit.MILLISECONDS);
-            writeOutput(String.format("undiscoverable() completed in 0 ms"));
-        } catch (InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d)", scanMode,
-                    BluetoothAdapter.SCAN_MODE_CONNECTABLE));
-        }
-    }
-
-    /**
-     * Starts a scan for remote devices and checks to make sure that the local device is scanning
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void startScan(BluetoothAdapter adapter) {
-        int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG;
-
-        if (!adapter.isEnabled()) {
-            fail("startScan() bluetooth not enabled");
-        }
-
-        if (adapter.isDiscovering()) {
-            return;
-        }
-
-        BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
-        long start = System.currentTimeMillis();
-        assertTrue(adapter.startDiscovery());
-
-        while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) {
-            if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
-                writeOutput(String.format("startScan() completed in %d ms",
-                        (receiver.getCompletedTime() - start)));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
-                adapter.isDiscovering(), firedFlags, mask));
-    }
-
-    /**
-     * Stops a scan for remote devices and checks to make sure that the local device is not scanning
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void stopScan(BluetoothAdapter adapter) {
-        int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG;
-
-        if (!adapter.isEnabled()) {
-            fail("stopScan() bluetooth not enabled");
-        }
-
-        if (!adapter.isDiscovering()) {
-            return;
-        }
-
-        BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
-        long start = System.currentTimeMillis();
-        assertTrue(adapter.cancelDiscovery());
-
-        while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) {
-            if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
-                writeOutput(String.format("stopScan() completed in %d ms",
-                        (receiver.getCompletedTime() - start)));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
-                adapter.isDiscovering(), firedFlags, mask));
-
-    }
-
-    /**
-     * Enables PAN tethering on the local device and checks to make sure that tethering is enabled.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void enablePan(BluetoothAdapter adapter) {
-        if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-
-        long start = System.currentTimeMillis();
-        mPan.setBluetoothTethering(true);
-        long stop = System.currentTimeMillis();
-        assertTrue(mPan.isTetheringOn());
-
-        writeOutput(String.format("enablePan() completed in %d ms", (stop - start)));
-    }
-
-    /**
-     * Disables PAN tethering on the local device and checks to make sure that tethering is
-     * disabled.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void disablePan(BluetoothAdapter adapter) {
-        if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-
-        long start = System.currentTimeMillis();
-        mPan.setBluetoothTethering(false);
-        long stop = System.currentTimeMillis();
-        assertFalse(mPan.isTetheringOn());
-
-        writeOutput(String.format("disablePan() completed in %d ms", (stop - start)));
-    }
-
-    /**
-     * Initiates a pairing with a remote device and checks to make sure that the devices are paired
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param passkey The pairing passkey if pairing requires a passkey. Any value if not.
-     * @param pin The pairing pin if pairing requires a pin. Any value if not.
-     */
-    public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) {
-        pairOrAcceptPair(adapter, device, passkey, pin, true);
-    }
-
-    /**
-     * Accepts a pairing with a remote device and checks to make sure that the devices are paired
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param passkey The pairing passkey if pairing requires a passkey. Any value if not.
-     * @param pin The pairing pin if pairing requires a pin. Any value if not.
-     */
-    public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey,
-            byte[] pin) {
-        pairOrAcceptPair(adapter, device, passkey, pin, false);
-    }
-
-    /**
-     * Helper method used by {@link #pair(BluetoothAdapter, BluetoothDevice, int, byte[])} and
-     * {@link #acceptPair(BluetoothAdapter, BluetoothDevice, int, byte[])} to either pair or accept
-     * a pairing request.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param passkey The pairing passkey if pairing requires a passkey. Any value if not.
-     * @param pin The pairing pin if pairing requires a pin. Any value if not.
-     * @param shouldPair Whether to pair or accept the pair.
-     */
-    private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey,
-            byte[] pin, boolean shouldPair) {
-        int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG;
-        long start = -1;
-        String methodName;
-        if (shouldPair) {
-            methodName = String.format("pair(device=%s)", device);
-        } else {
-            methodName = String.format("acceptPair(device=%s)", device);
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        PairReceiver receiver = getPairReceiver(device, passkey, pin, mask);
-
-        int state = device.getBondState();
-        switch (state) {
-            case BluetoothDevice.BOND_NONE:
-                assertFalse(adapter.getBondedDevices().contains(device));
-                start = System.currentTimeMillis();
-                if (shouldPair) {
-                    assertTrue(device.createBond());
-                }
-                break;
-            case BluetoothDevice.BOND_BONDING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            case BluetoothDevice.BOND_BONDED:
-                assertTrue(adapter.getBondedDevices().contains(device));
-                return;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) {
-            state = device.getBondState();
-            if (state == BluetoothDevice.BOND_BONDED && (receiver.getFiredFlags() & mask) == mask) {
-                assertTrue(adapter.getBondedDevices().contains(device));
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask));
-    }
-
-    /**
-     * Deletes a pairing with a remote device and checks to make sure that the devices are unpaired
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void unpair(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = PairReceiver.STATE_NONE_FLAG;
-        long start = -1;
-        String methodName = String.format("unpair(device=%s)", device);
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        PairReceiver receiver = getPairReceiver(device, 0, null, mask);
-
-        int state = device.getBondState();
-        switch (state) {
-            case BluetoothDevice.BOND_NONE:
-                assertFalse(adapter.getBondedDevices().contains(device));
-                removeReceiver(receiver);
-                return;
-            case BluetoothDevice.BOND_BONDING:
-                start = System.currentTimeMillis();
-                assertTrue(device.removeBond());
-                break;
-            case BluetoothDevice.BOND_BONDED:
-                assertTrue(adapter.getBondedDevices().contains(device));
-                start = System.currentTimeMillis();
-                assertTrue(device.removeBond());
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) {
-            if (device.getBondState() == BluetoothDevice.BOND_NONE
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                assertFalse(adapter.getBondedDevices().contains(device));
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask));
-    }
-
-    /**
-     * Deletes all pairings of remote devices
-     * @param adapter the BT adapter
-     */
-    public void unpairAll(BluetoothAdapter adapter) {
-        Set<BluetoothDevice> devices = adapter.getBondedDevices();
-        for (BluetoothDevice device : devices) {
-            unpair(adapter, device);
-        }
-    }
-
-    /**
-     * Connects a profile from the local device to a remote device and checks to make sure that the
-     * profile is connected and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}..
-     * @param methodName The method name to printed in the logs.  If null, will be
-     * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
-     */
-    public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile,
-            String methodName) {
-        if (methodName == null) {
-            methodName = String.format("connectProfile(profile=%d, device=%s)", profile, device);
-        }
-        int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG
-                | ConnectProfileReceiver.STATE_CONNECTED_FLAG);
-        long start = -1;
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        BluetoothProfile proxy = connectProxy(adapter, profile);
-        assertNotNull(proxy);
-
-        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
-
-        int state = proxy.getConnectionState(device);
-        switch (state) {
-            case BluetoothProfile.STATE_CONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothProfile.STATE_CONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            case BluetoothProfile.STATE_DISCONNECTED:
-            case BluetoothProfile.STATE_DISCONNECTING:
-                start = System.currentTimeMillis();
-                if (profile == BluetoothProfile.A2DP) {
-                    assertTrue(((BluetoothA2dp)proxy).connect(device));
-                } else if (profile == BluetoothProfile.HEADSET) {
-                    assertTrue(((BluetoothHeadset)proxy).connect(device));
-                } else if (profile == BluetoothProfile.HID_HOST) {
-                    assertTrue(((BluetoothHidHost)proxy).connect(device));
-                } else if (profile == BluetoothProfile.MAP_CLIENT) {
-                    assertTrue(((BluetoothMapClient)proxy).connect(device));
-                }
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = proxy.getConnectionState(device);
-            if (state == BluetoothProfile.STATE_CONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothProfile.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Disconnects a profile between the local device and a remote device and checks to make sure
-     * that the profile is disconnected and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
-     * @param methodName The method name to printed in the logs.  If null, will be
-     * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
-     */
-    public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile,
-            String methodName) {
-        if (methodName == null) {
-            methodName = String.format("disconnectProfile(profile=%d, device=%s)", profile, device);
-        }
-        int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG
-                | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG);
-        long start = -1;
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        BluetoothProfile proxy = connectProxy(adapter, profile);
-        assertNotNull(proxy);
-
-        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
-
-        int state = proxy.getConnectionState(device);
-        switch (state) {
-            case BluetoothProfile.STATE_CONNECTED:
-            case BluetoothProfile.STATE_CONNECTING:
-                start = System.currentTimeMillis();
-                if (profile == BluetoothProfile.A2DP) {
-                    assertTrue(((BluetoothA2dp)proxy).disconnect(device));
-                } else if (profile == BluetoothProfile.HEADSET) {
-                    assertTrue(((BluetoothHeadset)proxy).disconnect(device));
-                } else if (profile == BluetoothProfile.HID_HOST) {
-                    assertTrue(((BluetoothHidHost)proxy).disconnect(device));
-                } else if (profile == BluetoothProfile.MAP_CLIENT) {
-                    assertTrue(((BluetoothMapClient)proxy).disconnect(device));
-                }
-                break;
-            case BluetoothProfile.STATE_DISCONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothProfile.STATE_DISCONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = proxy.getConnectionState(device);
-            if (state == BluetoothProfile.STATE_DISCONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Connects the PANU to a remote NAP and checks to make sure that the PANU is connected and that
-     * the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void connectPan(BluetoothAdapter adapter, BluetoothDevice device) {
-        connectPanOrIncomingPanConnection(adapter, device, true);
-    }
-
-    /**
-     * Checks that a remote PANU connects to the local NAP correctly and that the correct actions
-     * were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void incomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device) {
-        connectPanOrIncomingPanConnection(adapter, device, false);
-    }
-
-    /**
-     * Helper method used by {@link #connectPan(BluetoothAdapter, BluetoothDevice)} and
-     * {@link #incomingPanConnection(BluetoothAdapter, BluetoothDevice)} to either connect to a
-     * remote NAP or verify that a remote device connected to the local NAP.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param connect If the method should initiate the connection (is PANU)
-     */
-    private void connectPanOrIncomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device,
-            boolean connect) {
-        long start = -1;
-        int mask, role;
-        String methodName;
-
-        if (connect) {
-            methodName = String.format("connectPan(device=%s)", device);
-            mask = (ConnectProfileReceiver.STATE_CONNECTED_FLAG |
-                    ConnectProfileReceiver.STATE_CONNECTING_FLAG);
-            role = BluetoothPan.LOCAL_PANU_ROLE;
-        } else {
-            methodName = String.format("incomingPanConnection(device=%s)", device);
-            mask = ConnectProfileReceiver.STATE_CONNECTED_FLAG;
-            role = BluetoothPan.LOCAL_NAP_ROLE;
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-        ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask);
-
-        int state = mPan.getConnectionState(device);
-        switch (state) {
-            case BluetoothPan.STATE_CONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothPan.STATE_CONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            case BluetoothPan.STATE_DISCONNECTED:
-            case BluetoothPan.STATE_DISCONNECTING:
-                start = System.currentTimeMillis();
-                if (role == BluetoothPan.LOCAL_PANU_ROLE) {
-                    Log.i("BT", "connect to pan");
-                    assertTrue(mPan.connect(device));
-                }
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = mPan.getConnectionState(device);
-            if (state == BluetoothPan.STATE_CONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, state, BluetoothPan.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Disconnects the PANU from a remote NAP and checks to make sure that the PANU is disconnected
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void disconnectPan(BluetoothAdapter adapter, BluetoothDevice device) {
-        disconnectFromRemoteOrVerifyConnectNap(adapter, device, true);
-    }
-
-    /**
-     * Checks that a remote PANU disconnects from the local NAP correctly and that the correct
-     * actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void incomingPanDisconnection(BluetoothAdapter adapter, BluetoothDevice device) {
-        disconnectFromRemoteOrVerifyConnectNap(adapter, device, false);
-    }
-
-    /**
-     * Helper method used by {@link #disconnectPan(BluetoothAdapter, BluetoothDevice)} and
-     * {@link #incomingPanDisconnection(BluetoothAdapter, BluetoothDevice)} to either disconnect
-     * from a remote NAP or verify that a remote device disconnected from the local NAP.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param disconnect Whether the method should connect or verify.
-     */
-    private void disconnectFromRemoteOrVerifyConnectNap(BluetoothAdapter adapter,
-            BluetoothDevice device, boolean disconnect) {
-        long start = -1;
-        int mask, role;
-        String methodName;
-
-        if (disconnect) {
-            methodName = String.format("disconnectPan(device=%s)", device);
-            mask = (ConnectProfileReceiver.STATE_DISCONNECTED_FLAG |
-                    ConnectProfileReceiver.STATE_DISCONNECTING_FLAG);
-            role = BluetoothPan.LOCAL_PANU_ROLE;
-        } else {
-            methodName = String.format("incomingPanDisconnection(device=%s)", device);
-            mask = ConnectProfileReceiver.STATE_DISCONNECTED_FLAG;
-            role = BluetoothPan.LOCAL_NAP_ROLE;
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-        ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask);
-
-        int state = mPan.getConnectionState(device);
-        switch (state) {
-            case BluetoothPan.STATE_CONNECTED:
-            case BluetoothPan.STATE_CONNECTING:
-                start = System.currentTimeMillis();
-                if (role == BluetoothPan.LOCAL_PANU_ROLE) {
-                    assertTrue(mPan.disconnect(device));
-                }
-                break;
-            case BluetoothPan.STATE_DISCONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothPan.STATE_DISCONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = mPan.getConnectionState(device);
-            if (state == BluetoothHidHost.STATE_DISCONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Opens a SCO channel using {@link android.media.AudioManager#startBluetoothSco()} and checks
-     * to make sure that the channel is opened and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void startSco(BluetoothAdapter adapter, BluetoothDevice device) {
-        startStopSco(adapter, device, true);
-    }
-
-    /**
-     * Closes a SCO channel using {@link android.media.AudioManager#stopBluetoothSco()} and checks
-     *  to make sure that the channel is closed and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void stopSco(BluetoothAdapter adapter, BluetoothDevice device) {
-        startStopSco(adapter, device, false);
-    }
-    /**
-     * Helper method for {@link #startSco(BluetoothAdapter, BluetoothDevice)} and
-     * {@link #stopSco(BluetoothAdapter, BluetoothDevice)}.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param isStart Whether the SCO channel should be opened.
-     */
-    private void startStopSco(BluetoothAdapter adapter, BluetoothDevice device, boolean isStart) {
-        long start = -1;
-        int mask;
-        String methodName;
-
-        if (isStart) {
-            methodName = String.format("startSco(device=%s)", device);
-            mask = StartStopScoReceiver.STATE_CONNECTED_FLAG;
-        } else {
-            methodName = String.format("stopSco(device=%s)", device);
-            mask = StartStopScoReceiver.STATE_DISCONNECTED_FLAG;
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        assertNotNull(manager);
-
-        if (!manager.isBluetoothScoAvailableOffCall()) {
-            fail(String.format("%s device does not support SCO", methodName));
-        }
-
-        boolean isScoOn = manager.isBluetoothScoOn();
-        if (isStart == isScoOn) {
-            return;
-        }
-
-        StartStopScoReceiver receiver = getStartStopScoReceiver(mask);
-        start = System.currentTimeMillis();
-        if (isStart) {
-            manager.startBluetoothSco();
-        } else {
-            manager.stopBluetoothSco();
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < START_STOP_SCO_TIMEOUT) {
-            isScoOn = manager.isBluetoothScoOn();
-            if (isStart == isScoOn && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: on=%b (expected %b), flags=0x%x (expected 0x%x)",
-                methodName, isScoOn, isStart, firedFlags, mask));
-    }
-
-    /**
-     * Writes a string to the logcat and a file if a file has been specified in the constructor.
-     *
-     * @param s The string to be written.
-     */
-    public void writeOutput(String s) {
-        Log.i(mTag, s);
-        if (mOutputWriter == null) {
-            return;
-        }
-        try {
-            mOutputWriter.write(s + "\n");
-            mOutputWriter.flush();
-        } catch (IOException e) {
-            Log.w(mTag, "Could not write to output file", e);
-        }
-    }
-
-    public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask;
-        String methodName = "getUnreadMessage";
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
-        assertNotNull(mMce);
-
-        if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
-            fail(String.format("%s device is not connected", methodName));
-        }
-
-        mMsgHandle = null;
-        mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG;
-        MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
-        assertTrue(mMce.getUnreadMessages(device));
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) {
-            if ((receiver.getFiredFlags() & mask) == mask) {
-                writeOutput(String.format("%s completed", methodName));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Set a message to read/unread/deleted/undeleted
-     */
-    public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) {
-        int mask;
-        String methodName = "setMessageStatus";
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
-        assertNotNull(mMce);
-
-        if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
-            fail(String.format("%s device is not connected", methodName));
-        }
-
-        assertNotNull(mMsgHandle);
-        mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG;
-        MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
-
-        assertTrue(mMce.setMessageStatus(device, mMsgHandle, status));
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) {
-            if ((receiver.getFiredFlags() & mask) == mask) {
-                writeOutput(String.format("%s completed", methodName));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    private void addReceiver(BroadcastReceiver receiver, String[] actions) {
-        IntentFilter filter = new IntentFilter();
-        for (String action: actions) {
-            filter.addAction(action);
-        }
-        mContext.registerReceiver(receiver, filter);
-        mReceivers.add(receiver);
-    }
-
-    private BluetoothReceiver getBluetoothReceiver(int expectedFlags) {
-        String[] actions = {
-                BluetoothAdapter.ACTION_DISCOVERY_FINISHED,
-                BluetoothAdapter.ACTION_DISCOVERY_STARTED,
-                BluetoothAdapter.ACTION_SCAN_MODE_CHANGED,
-                BluetoothAdapter.ACTION_STATE_CHANGED};
-        BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin,
-            int expectedFlags) {
-        String[] actions = {
-                BluetoothDevice.ACTION_PAIRING_REQUEST,
-                BluetoothDevice.ACTION_BOND_STATE_CHANGED};
-        PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile,
-            int expectedFlags) {
-        String[] actions = {
-                BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED};
-        ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
-                expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private ConnectPanReceiver getConnectPanReceiver(BluetoothDevice device, int role,
-            int expectedFlags) {
-        String[] actions = {BluetoothPan.ACTION_CONNECTION_STATE_CHANGED};
-        ConnectPanReceiver receiver = new ConnectPanReceiver(device, role, expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) {
-        String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED};
-        StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device,
-            int expectedFlags) {
-        String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED,
-            BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED,
-            BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED};
-        MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private void removeReceiver(BroadcastReceiver receiver) {
-        mContext.unregisterReceiver(receiver);
-        mReceivers.remove(receiver);
-    }
-
-    private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) {
-        switch (profile) {
-            case BluetoothProfile.A2DP:
-                if (mA2dp != null) {
-                    return mA2dp;
-                }
-                break;
-            case BluetoothProfile.HEADSET:
-                if (mHeadset != null) {
-                    return mHeadset;
-                }
-                break;
-            case BluetoothProfile.HID_HOST:
-                if (mInput != null) {
-                    return mInput;
-                }
-                break;
-            case BluetoothProfile.PAN:
-                if (mPan != null) {
-                    return mPan;
-                }
-            case BluetoothProfile.MAP_CLIENT:
-                if (mMce != null) {
-                    return mMce;
-                }
-                break;
-            default:
-                return null;
-        }
-        adapter.getProfileProxy(mContext, mServiceListener, profile);
-        long s = System.currentTimeMillis();
-        switch (profile) {
-            case BluetoothProfile.A2DP:
-                while (mA2dp == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mA2dp;
-            case BluetoothProfile.HEADSET:
-                while (mHeadset == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mHeadset;
-            case BluetoothProfile.HID_HOST:
-                while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mInput;
-            case BluetoothProfile.PAN:
-                while (mPan == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mPan;
-            case BluetoothProfile.MAP_CLIENT:
-                while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mMce;
-            default:
-                return null;
-        }
-    }
-
-    private void sleep(long time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-        }
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
deleted file mode 100644
index 536d722..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link BluetoothUuid}.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class BluetoothUuidTest extends TestCase {
-
-    @SmallTest
-    public void testUuidParser() {
-        byte[] uuid16 = new byte[] {
-                0x0B, 0x11 };
-        assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
-                BluetoothUuid.parseUuidFrom(uuid16));
-
-        byte[] uuid32 = new byte[] {
-                0x0B, 0x11, 0x33, (byte) 0xFE };
-        assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"),
-                BluetoothUuid.parseUuidFrom(uuid32));
-
-        byte[] uuid128 = new byte[] {
-                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF };
-        assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"),
-                BluetoothUuid.parseUuidFrom(uuid128));
-    }
-
-    @SmallTest
-    public void testUuidType() {
-        assertTrue(BluetoothUuid.is16BitUuid(
-                ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB")));
-        assertFalse(BluetoothUuid.is32BitUuid(
-                ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB")));
-
-        assertFalse(BluetoothUuid.is16BitUuid(
-                ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB")));
-        assertTrue(BluetoothUuid.is32BitUuid(
-                ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB")));
-        assertFalse(BluetoothUuid.is32BitUuid(
-                ParcelUuid.fromString("FE33110B-1000-1000-8000-00805F9B34FB")));
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
deleted file mode 100644
index e58d905..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link AdvertiseData}.
- * <p>
- * To run the test, use adb shell am instrument -e class 'android.bluetooth.le.AdvertiseDataTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class AdvertiseDataTest extends TestCase {
-
-    private AdvertiseData.Builder mAdvertiseDataBuilder;
-
-    @Override
-    protected void setUp() throws Exception {
-        mAdvertiseDataBuilder = new AdvertiseData.Builder();
-    }
-
-    @SmallTest
-    public void testEmptyData() {
-        Parcel parcel = Parcel.obtain();
-        AdvertiseData data = mAdvertiseDataBuilder.build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testEmptyServiceUuid() {
-        Parcel parcel = Parcel.obtain();
-        AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testEmptyManufacturerData() {
-        Parcel parcel = Parcel.obtain();
-        int manufacturerId = 50;
-        byte[] manufacturerData = new byte[0];
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addManufacturerData(manufacturerId, manufacturerData).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testEmptyServiceData() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        byte[] serviceData = new byte[0];
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceData(uuid, serviceData).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testServiceUuid() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceUuid(uuid).addServiceUuid(uuid2).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testManufacturerData() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-
-        int manufacturerId = 50;
-        byte[] manufacturerData = new byte[] {
-                (byte) 0xF0, 0x00, 0x02, 0x15 };
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceUuid(uuid).addServiceUuid(uuid2)
-                        .addManufacturerData(manufacturerId, manufacturerData).build();
-
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testServiceData() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        byte[] serviceData = new byte[] {
-                (byte) 0xF0, 0x00, 0x02, 0x15 };
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceData(uuid, serviceData).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
deleted file mode 100644
index 35da4bc..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanRecord;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for Bluetooth LE scan filters.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class ScanFilterTest extends TestCase {
-
-    private static final String DEVICE_MAC = "01:02:03:04:05:AB";
-    private ScanResult mScanResult;
-    private ScanFilter.Builder mFilterBuilder;
-
-    @Override
-    protected void setUp() throws Exception {
-        byte[] scanRecord = new byte[] {
-                0x02, 0x01, 0x1a, // advertising flags
-                0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
-                0x04, 0x09, 0x50, 0x65, 0x64, // setName
-                0x02, 0x0A, (byte) 0xec, // tx power level
-                0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
-                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
-                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
-        };
-
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC);
-        mScanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord),
-                -10, 1397545200000000L);
-        mFilterBuilder = new ScanFilter.Builder();
-    }
-
-    @SmallTest
-    public void testsetNameFilter() {
-        ScanFilter filter = mFilterBuilder.setDeviceName("Ped").build();
-        assertTrue("setName filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setDeviceName("Pem").build();
-        assertFalse("setName filter fails", filter.matches(mScanResult));
-
-    }
-
-    @SmallTest
-    public void testDeviceFilter() {
-        ScanFilter filter = mFilterBuilder.setDeviceAddress(DEVICE_MAC).build();
-        assertTrue("device filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build();
-        assertFalse("device filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testsetServiceUuidFilter() {
-        ScanFilter filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build();
-        assertTrue("uuid filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
-        assertFalse("uuid filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder
-                .setServiceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
-                        ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
-                .build();
-        assertTrue("uuid filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testsetServiceDataFilter() {
-        byte[] setServiceData = new byte[] {
-                0x50, 0x64 };
-        ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-        ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] emptyData = new byte[0];
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] prefixData = new byte[] {
-                0x50 };
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] nonMatchData = new byte[] {
-                0x51, 0x64 };
-        byte[] mask = new byte[] {
-                (byte) 0x00, (byte) 0xFF };
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build();
-        assertTrue("partial service data filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build();
-        assertFalse("service data filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testManufacturerSpecificData() {
-        byte[] setManufacturerData = new byte[] {
-                0x02, 0x15 };
-        int manufacturerId = 0xE0;
-        ScanFilter filter =
-                mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build();
-        assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
-
-        byte[] emptyData = new byte[0];
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build();
-        assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
-
-        byte[] prefixData = new byte[] {
-                0x02 };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build();
-        assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
-
-        // Test data mask
-        byte[] nonMatchData = new byte[] {
-                0x02, 0x14 };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build();
-        assertFalse("manufacturer data filter fails", filter.matches(mScanResult));
-        byte[] mask = new byte[] {
-                (byte) 0xFF, (byte) 0x00
-        };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build();
-        assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testReadWriteParcel() {
-        ScanFilter filter = mFilterBuilder.build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setDeviceName("Ped").build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
-                ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] serviceData = new byte[] {
-                0x50, 0x64 };
-
-        ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] serviceDataMask = new byte[] {
-                (byte) 0xFF, (byte) 0xFF };
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData, serviceDataMask)
-                .build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] manufacturerData = new byte[] {
-                0x02, 0x15 };
-        int manufacturerId = 0xE0;
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] manufacturerDataMask = new byte[] {
-                (byte) 0xFF, (byte) 0xFF
-        };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData,
-                manufacturerDataMask).build();
-        testReadWriteParcelForFilter(filter);
-    }
-
-    private void testReadWriteParcelForFilter(ScanFilter filter) {
-        Parcel parcel = Parcel.obtain();
-        filter.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        ScanFilter filterFromParcel =
-                ScanFilter.CREATOR.createFromParcel(parcel);
-        assertEquals(filter, filterFromParcel);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
deleted file mode 100644
index 4e817d4..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.util.HexDump;
-import com.android.modules.utils.BytesMatcher;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Predicate;
-
-/**
- * Unit test cases for {@link ScanRecord}.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanRecordTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class ScanRecordTest extends TestCase {
-    /**
-     * Example raw beacons captured from a Blue Charm BC011
-     */
-    private static final String RECORD_URL = "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
-    private static final String RECORD_UUID = "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
-    private static final String RECORD_TLM = "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000";
-    private static final String RECORD_IBEACON = "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000";
-
-    @SmallTest
-    public void testMatchesAnyField_Eddystone_Parser() {
-        final List<String> found = new ArrayList<>();
-        final Predicate<byte[]> matcher = (v) -> {
-            found.add(HexDump.toHexString(v));
-            return false;
-        };
-        ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_URL))
-                .matchesAnyField(matcher);
-
-        assertEquals(Arrays.asList(
-                "020106",
-                "0303AAFE",
-                "1716AAFE10EE01626C7565636861726D626561636F6E7300",
-                "09168020691E0EFE1355",
-                "1109426C7565436861726D5F313639363835"), found);
-    }
-
-    @SmallTest
-    public void testMatchesAnyField_Eddystone() {
-        final BytesMatcher matcher = BytesMatcher.decode("⊆0016AAFE/00FFFFFF");
-        assertMatchesAnyField(RECORD_URL, matcher);
-        assertMatchesAnyField(RECORD_UUID, matcher);
-        assertMatchesAnyField(RECORD_TLM, matcher);
-        assertNotMatchesAnyField(RECORD_IBEACON, matcher);
-    }
-
-    @SmallTest
-    public void testMatchesAnyField_iBeacon_Parser() {
-        final List<String> found = new ArrayList<>();
-        final Predicate<byte[]> matcher = (v) -> {
-            found.add(HexDump.toHexString(v));
-            return false;
-        };
-        ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_IBEACON))
-                .matchesAnyField(matcher);
-
-        assertEquals(Arrays.asList(
-                "020106",
-                "1AFF4C000215426C7565436861726D426561636F6E730EFE1355C5",
-                "09168020691E0EFE1355",
-                "1109426C7565436861726D5F313639363835"), found);
-    }
-
-    @SmallTest
-    public void testMatchesAnyField_iBeacon() {
-        final BytesMatcher matcher = BytesMatcher.decode("⊆00FF4C0002/00FFFFFFFF");
-        assertNotMatchesAnyField(RECORD_URL, matcher);
-        assertNotMatchesAnyField(RECORD_UUID, matcher);
-        assertNotMatchesAnyField(RECORD_TLM, matcher);
-        assertMatchesAnyField(RECORD_IBEACON, matcher);
-    }
-
-    @SmallTest
-    public void testParser() {
-        byte[] scanRecord = new byte[] {
-                0x02, 0x01, 0x1a, // advertising flags
-                0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
-                0x04, 0x09, 0x50, 0x65, 0x64, // name
-                0x02, 0x0A, (byte) 0xec, // tx power level
-                0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
-                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
-                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
-        };
-        ScanRecord data = ScanRecord.parseFromBytes(scanRecord);
-        assertEquals(0x1a, data.getAdvertiseFlags());
-        ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-        assertTrue(data.getServiceUuids().contains(uuid1));
-        assertTrue(data.getServiceUuids().contains(uuid2));
-
-        assertEquals("Ped", data.getDeviceName());
-        assertEquals(-20, data.getTxPowerLevel());
-
-        assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null);
-        assertArrayEquals(new byte[] {
-                0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0));
-
-        assertTrue(data.getServiceData().containsKey(uuid2));
-        assertArrayEquals(new byte[] {
-                0x50, 0x64 }, data.getServiceData().get(uuid2));
-    }
-
-    // Assert two byte arrays are equal.
-    private static void assertArrayEquals(byte[] expected, byte[] actual) {
-        if (!Arrays.equals(expected, actual)) {
-            fail("expected:<" + Arrays.toString(expected) +
-                    "> but was:<" + Arrays.toString(actual) + ">");
-        }
-
-    }
-
-    private static void assertMatchesAnyField(String record, BytesMatcher matcher) {
-        assertTrue(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
-                .matchesAnyField(matcher));
-    }
-
-    private static void assertNotMatchesAnyField(String record, BytesMatcher matcher) {
-        assertFalse(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
-                .matchesAnyField(matcher));
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
deleted file mode 100644
index 01d5c59..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for Bluetooth LE scans.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanResultTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class ScanResultTest extends TestCase {
-
-    /**
-     * Test read and write parcel of ScanResult
-     */
-    @SmallTest
-    public void testScanResultParceling() {
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
-                "01:02:03:04:05:06");
-        byte[] scanRecord = new byte[] {
-                1, 2, 3 };
-        int rssi = -10;
-        long timestampMicros = 10000L;
-
-        ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), rssi,
-                timestampMicros);
-        Parcel parcel = Parcel.obtain();
-        result.writeToParcel(parcel, 0);
-        // Need to reset parcel data position to the beginning.
-        parcel.setDataPosition(0);
-        ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel);
-        assertEquals(result, resultFromParcel);
-    }
-
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java
deleted file mode 100644
index 7c42c3b..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Test for Bluetooth LE {@link ScanSettings}.
- */
-public class ScanSettingsTest extends TestCase {
-
-    @SmallTest
-    public void testCallbackType() {
-        ScanSettings.Builder builder = new ScanSettings.Builder();
-        builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
-        builder.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH);
-        builder.setCallbackType(ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-        builder.setCallbackType(
-                ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-        try {
-            builder.setCallbackType(
-                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES | ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-            fail("should have thrown IllegalArgumentException!");
-        } catch (IllegalArgumentException e) {
-            // nothing to do
-        }
-
-        try {
-            builder.setCallbackType(
-                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES |
-                    ScanSettings.CALLBACK_TYPE_FIRST_MATCH);
-            fail("should have thrown IllegalArgumentException!");
-        } catch (IllegalArgumentException e) {
-            // nothing to do
-        }
-
-        try {
-            builder.setCallbackType(
-                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES |
-                    ScanSettings.CALLBACK_TYPE_FIRST_MATCH |
-                    ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-            fail("should have thrown IllegalArgumentException!");
-        } catch (IllegalArgumentException e) {
-            // nothing to do
-        }
-
-    }
-}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c18a70c..c1f3c4f 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -93,33 +93,9 @@
 java_genrule {
     name: "FrameworksCoreTests_apks_as_resources",
     srcs: [
-        ":FrameworksCoreTests_install",
-        ":FrameworksCoreTests_install_bad_dex",
-        ":FrameworksCoreTests_install_complete_package_info",
-        ":FrameworksCoreTests_install_decl_perm",
         ":FrameworksCoreTests_install_jni_lib_open_from_apk",
-        ":FrameworksCoreTests_install_loc_auto",
-        ":FrameworksCoreTests_install_loc_internal",
-        ":FrameworksCoreTests_install_loc_sdcard",
-        ":FrameworksCoreTests_install_loc_unspecified",
-        ":FrameworksCoreTests_install_use_perm_good",
-        ":FrameworksCoreTests_install_uses_feature",
         ":FrameworksCoreTests_install_verifier_bad",
         ":FrameworksCoreTests_install_verifier_good",
-        ":FrameworksCoreTests_keyset_permdef_sa_unone",
-        ":FrameworksCoreTests_keyset_permuse_sa_ua_ub",
-        ":FrameworksCoreTests_keyset_permuse_sb_ua_ub",
-        ":FrameworksCoreTests_keyset_sab_ua",
-        ":FrameworksCoreTests_keyset_sa_ua",
-        ":FrameworksCoreTests_keyset_sa_uab",
-        ":FrameworksCoreTests_keyset_sa_ua_ub",
-        ":FrameworksCoreTests_keyset_sa_ub",
-        ":FrameworksCoreTests_keyset_sa_unone",
-        ":FrameworksCoreTests_keyset_sau_ub",
-        ":FrameworksCoreTests_keyset_sb_ua",
-        ":FrameworksCoreTests_keyset_sb_ub",
-        ":FrameworksCoreTests_keyset_splata_api",
-        ":FrameworksCoreTests_keyset_splat_api",
         ":FrameworksCoreTests_locales",
         ":FrameworksCoreTests_overlay_config",
         ":FrameworksCoreTests_version_1",
@@ -173,4 +149,4 @@
         "framework",
         "framework-res",
     ],
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 14a3a01..a80424e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -38,6 +38,7 @@
     <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
+    <uses-permission android:name="android.permission.ACCESS_FPS_COUNTER" />
     <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -64,6 +65,7 @@
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.READ_DREAM_STATE" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS"/>
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
     <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
     <uses-permission android:name="android.permission.READ_LOGS"/>
@@ -1670,11 +1672,4 @@
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests"
             android:label="Frameworks Core Tests" />
-    <key-sets>
-        <key-set android:name="A" >
-          <public-key android:name="keyA"
-                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMpNthdOxud7roPDZMMomOqXgJJdRfIWpkKEqmC61Mv+Nf6QY3TorEwJeghjSmqj7IbBKrtvfQq4E2XJO1HuspmQO4Ng2gvn+r+6EwNfKc9k55d6s+27SR867jKurBbHNtZMG+tjL1yH4r+tNzcuJCsgyAFqLmxFdcxEwzNvREyRpoYc5RDR0mmTwkMCUhJ6CId1EYEKiCEdNzxv+fWPEb21u+/MWpleGCILs8kglRVb2q/WOzAAvGr4FY5plfaE6N+lr7+UschQ+aMi1+uqewo2o0qPFVmZP5hnwj55K4UMzu/NhhDqQQsX4cSGES1KgHo5MTqRqZjN/I7emw5pFQIDAQAB"/>
-        </key-set>
-        <upgrade-key-set android:name="A"/>
-    </key-sets>
 </manifest>
diff --git a/core/tests/coretests/apks/install/Android.bp b/core/tests/coretests/apks/install/Android.bp
deleted file mode 100644
index 652b491..0000000
--- a/core/tests/coretests/apks/install/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_bad_dex/Android.bp b/core/tests/coretests/apks/install_bad_dex/Android.bp
deleted file mode 100644
index 7b96c9b4..0000000
--- a/core/tests/coretests/apks/install_bad_dex/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_bad_dex_",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["src/**/*.java"],
-}
-
-// Inject bad classes.dex file.
-java_genrule {
-    name: "FrameworksCoreTests_install_bad_dex",
-    tools: [
-        "soong_zip",
-        "merge_zips",
-    ],
-    srcs: [
-        ":FrameworksCoreTests_install_bad_dex_",
-        "classes.dex",
-    ],
-    out: ["FrameworksCoreTests_install_bad_dex.apk"],
-    cmd: "$(location soong_zip) -o $(genDir)/classes.dex.zip -j -f $(location classes.dex) && " +
-        "$(location merge_zips) -ignore-duplicates $(out) $(genDir)/classes.dex.zip " +
-        "$(location :FrameworksCoreTests_install_bad_dex_)",
-}
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.bp b/core/tests/coretests/apks/install_complete_package_info/Android.bp
deleted file mode 100644
index 3fee0c6..0000000
--- a/core/tests/coretests/apks/install_complete_package_info/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_complete_package_info",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_decl_perm/Android.bp b/core/tests/coretests/apks/install_decl_perm/Android.bp
deleted file mode 100644
index bf1f0de..0000000
--- a/core/tests/coretests/apks/install_decl_perm/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_decl_perm",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_loc_auto/Android.bp b/core/tests/coretests/apks/install_loc_auto/Android.bp
deleted file mode 100644
index 37daf76..0000000
--- a/core/tests/coretests/apks/install_loc_auto/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_loc_auto",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_loc_internal/Android.bp b/core/tests/coretests/apks/install_loc_internal/Android.bp
deleted file mode 100644
index 3e23313..0000000
--- a/core/tests/coretests/apks/install_loc_internal/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_loc_internal",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.bp b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
deleted file mode 100644
index 708e655..0000000
--- a/core/tests/coretests/apks/install_loc_sdcard/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_loc_sdcard",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.bp b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
deleted file mode 100644
index 76869e9..0000000
--- a/core/tests/coretests/apks/install_loc_unspecified/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_loc_unspecified",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.bp b/core/tests/coretests/apks/install_use_perm_good/Android.bp
deleted file mode 100644
index 89700dd..0000000
--- a/core/tests/coretests/apks/install_use_perm_good/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_use_perm_good",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_uses_feature/Android.bp b/core/tests/coretests/apks/install_uses_feature/Android.bp
deleted file mode 100644
index 913a96a..0000000
--- a/core/tests/coretests/apks/install_uses_feature/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_uses_feature",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/keyset/Android.bp b/core/tests/coretests/apks/keyset/Android.bp
deleted file mode 100644
index 93c3b1f..0000000
--- a/core/tests/coretests/apks/keyset/Android.bp
+++ /dev/null
@@ -1,129 +0,0 @@
-//apks signed by keyset_A
-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: "FrameworksCoreTests_keyset_sa_unone",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "uNone/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sa_ua",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "uA/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sa_ub",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "uB/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sa_uab",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "uAB/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sa_ua_ub",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "uAuB/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_permdef_sa_unone",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "permDef/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_permuse_sa_ua_ub",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    manifest: "permUse/AndroidManifest.xml",
-}
-
-//apks signed by keyset_B
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sb_ua",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_B_cert",
-    manifest: "uA/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sb_ub",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_B_cert",
-    manifest: "uB/AndroidManifest.xml",
-}
-
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_permuse_sb_ua_ub",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_B_cert",
-    manifest: "permUse/AndroidManifest.xml",
-}
-
-//apks signed by keyset_A and keyset_B
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sab_ua",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    additional_certificates: [":FrameworksCoreTests_keyset_B_cert"],
-    manifest: "uA/AndroidManifest.xml",
-}
-
-//apks signed by keyset_A and unit_test
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_sau_ub",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
-    additional_certificates: [":FrameworksCoreTests_keyset_B_cert"],
-    manifest: "uB/AndroidManifest.xml",
-}
-
-//apks signed by platform only
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_splat_api",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: "platform",
-    manifest: "api_test/AndroidManifest.xml",
-}
-
-//apks signed by platform and keyset_A
-android_test_helper_app {
-    name: "FrameworksCoreTests_keyset_splata_api",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-    srcs: ["**/*.java"],
-    certificate: "platform",
-    additional_certificates: [":FrameworksCoreTests_keyset_A_cert"],
-    manifest: "api_test/AndroidManifest.xml",
-}
diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp
index 8411183..8d4ecf4 100644
--- a/core/tests/coretests/certs/Android.bp
+++ b/core/tests/coretests/certs/Android.bp
@@ -10,16 +10,6 @@
 }
 
 android_app_certificate {
-    name: "FrameworksCoreTests_keyset_A_cert",
-    certificate: "keyset_A",
-}
-
-android_app_certificate {
-    name: "FrameworksCoreTests_keyset_B_cert",
-    certificate: "keyset_B",
-}
-
-android_app_certificate {
     name: "FrameworksCoreTests_unit_test_cert",
     certificate: "unit_test",
 }
diff --git a/core/tests/coretests/res/xml/power_profile_test.xml b/core/tests/coretests/res/xml/power_profile_test.xml
new file mode 100644
index 0000000..2257114
--- /dev/null
+++ b/core/tests/coretests/res/xml/power_profile_test.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<device name="Android">
+    <!-- This is the battery capacity in mAh -->
+    <item name="battery.capacity">3000</item>
+
+    <!-- Number of cores each CPU cluster contains -->
+    <array name="cpu.clusters.cores">
+        <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
+        <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
+    </array>
+
+    <!-- Power consumption when CPU is suspended -->
+    <item name="cpu.suspend">5</item>
+    <!-- Additional power consumption when CPU is in a kernel idle loop -->
+    <item name="cpu.idle">1.11</item>
+    <!-- Additional power consumption by CPU excluding cluster and core when  running -->
+    <item name="cpu.active">2.55</item>
+
+    <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
+    <item name="cpu.cluster_power.cluster0">2.11</item>
+    <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
+    <item name="cpu.cluster_power.cluster1">2.22</item>
+
+    <!-- Different CPU speeds as reported in
+         /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+    <array name="cpu.core_speeds.cluster0">
+        <value>300000</value> <!-- 300 MHz CPU speed -->
+        <value>1000000</value> <!-- 1000 MHz CPU speed -->
+        <value>2000000</value> <!-- 2000 MHz CPU speed -->
+    </array>
+    <!-- Different CPU speeds as reported in
+         /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+    <array name="cpu.core_speeds.cluster1">
+        <value>300000</value> <!-- 300 MHz CPU speed -->
+        <value>1000000</value> <!-- 1000 MHz CPU speed -->
+        <value>2500000</value> <!-- 2500 MHz CPU speed -->
+        <value>3000000</value> <!-- 3000 MHz CPU speed -->
+    </array>
+
+    <!-- Additional power used by a CPU from cluster 0 when running at different
+         speeds. Currently this measurement also includes cluster cost. -->
+    <array name="cpu.core_power.cluster0">
+        <value>10</value> <!-- 300 MHz CPU speed -->
+        <value>20</value> <!-- 1000 MHz CPU speed -->
+        <value>30</value> <!-- 1900 MHz CPU speed -->
+    </array>
+    <!-- Additional power used by a CPU from cluster 1 when running at different
+         speeds. Currently this measurement also includes cluster cost. -->
+    <array name="cpu.core_power.cluster1">
+        <value>25</value> <!-- 300 MHz CPU speed -->
+        <value>35</value> <!-- 1000 MHz CPU speed -->
+        <value>50</value> <!-- 2500 MHz CPU speed -->
+        <value>60</value> <!-- 3000 MHz CPU speed -->
+    </array>
+
+    <!-- Power used by display unit in ambient display mode, including back lighting-->
+    <item name="ambient.on">0.5</item>
+    <!-- Additional power used when screen is turned on at minimum brightness -->
+    <item name="screen.on">100</item>
+    <!-- Additional power used when screen is at maximum brightness, compared to
+         screen at minimum brightness -->
+    <item name="screen.full">800</item>
+
+    <!-- Average power used by the camera flash module when on -->
+    <item name="camera.flashlight">500</item>
+    <!-- Average power use by the camera subsystem for a typical camera
+         application. Intended as a rough estimate for an application running a
+         preview and capturing approximately 10 full-resolution pictures per
+         minute. -->
+    <item name="camera.avg">600</item>
+
+    <!-- Additional power used by the audio hardware, probably due to DSP -->
+    <item name="audio">100.0</item>
+
+    <!-- Additional power used by the video hardware, probably due to DSP -->
+    <item name="video">150.0</item> <!-- ~50mA -->
+
+    <!-- Additional power used when GPS is acquiring a signal -->
+    <item name="gps.on">10</item>
+
+    <!-- Additional power used when cellular radio is transmitting/receiving -->
+    <item name="radio.active">60</item>
+    <!-- Additional power used when cellular radio is paging the tower -->
+    <item name="radio.scanning">3</item>
+    <!-- Additional power used when the cellular radio is on. Multi-value entry,
+         one per signal strength (no signal, weak, moderate, strong) -->
+    <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+        <value>6</value>       <!-- none -->
+        <value>5</value>       <!-- poor -->
+        <value>4</value>       <!-- moderate -->
+        <value>3</value>       <!-- good -->
+        <value>3</value>       <!-- great -->
+    </array>
+
+    <!-- Cellular modem related values. These constants are deprecated, but still supported and
+         need to be tested -->
+    <item name="modem.controller.sleep">123</item>
+    <item name="modem.controller.idle">456</item>
+    <item name="modem.controller.rx">789</item>
+    <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+        <value>10</value>
+        <value>20</value>
+        <value>30</value>
+        <value>40</value>
+        <value>50</value>
+    </array>
+</device>
\ No newline at end of file
diff --git a/core/tests/coretests/res/xml/power_profile_test_modem.xml b/core/tests/coretests/res/xml/power_profile_test_modem.xml
new file mode 100644
index 0000000..ff36a9c
--- /dev/null
+++ b/core/tests/coretests/res/xml/power_profile_test_modem.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<device name="test">
+    <test-modem name="testModemPowerProfile_defaultRat">
+        <!-- Modem sleep drain current value in mA. -->
+        <sleep>10</sleep>
+        <!-- Modem idle drain current value in mA. -->
+        <idle>20</idle>
+        <active rat="DEFAULT">
+            <!-- Transmit current drain in mA. -->
+            <receive>30</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">40</transmit>
+            <transmit level="1">50</transmit>
+            <transmit level="2">60</transmit>
+            <transmit level="3">70</transmit>
+            <transmit level="4">80</transmit>
+        </active>
+    </test-modem>
+
+    <test-modem name="testModemPowerProfile_partiallyDefined">
+        <!-- Modem sleep drain current value in mA. -->
+        <sleep>1</sleep>
+        <!-- Modem idle drain current value in mA. -->
+        <idle>2</idle>
+        <active rat="DEFAULT">
+            <!-- Transmit current drain in mA. -->
+            <receive>3</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">4</transmit>
+            <transmit level="1">5</transmit>
+            <transmit level="2">6</transmit>
+            <transmit level="3">7</transmit>
+            <transmit level="4">8</transmit>
+        </active>
+        <active rat="NR" nrFrequency="DEFAULT">
+            <!-- Transmit current drain in mA. -->
+            <receive>13</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">14</transmit>
+            <transmit level="1">15</transmit>
+            <transmit level="2">16</transmit>
+            <transmit level="3">17</transmit>
+            <transmit level="4">18</transmit>
+        </active>
+        <active rat="NR" nrFrequency="MMWAVE">
+            <!-- Transmit current drain in mA. -->
+            <receive>53</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">54</transmit>
+            <transmit level="1">55</transmit>
+            <transmit level="2">56</transmit>
+            <transmit level="3">57</transmit>
+            <transmit level="4">58</transmit>
+        </active>
+    </test-modem>
+
+    <test-modem name="testModemPowerProfile_fullyDefined">
+        <!-- Modem sleep drain current value in mA. -->
+        <sleep>1</sleep>
+        <!-- Modem idle drain current value in mA. -->
+        <idle>2</idle>
+        <active rat="DEFAULT">
+            <!-- Transmit current drain in mA. -->
+            <receive>3</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">4</transmit>
+            <transmit level="1">5</transmit>
+            <transmit level="2">6</transmit>
+            <transmit level="3">7</transmit>
+            <transmit level="4">8</transmit>
+        </active>
+        <active rat="LTE">
+            <!-- Transmit current drain in mA. -->
+            <receive>10</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">20</transmit>
+            <transmit level="1">30</transmit>
+            <transmit level="2">40</transmit>
+            <transmit level="3">50</transmit>
+            <transmit level="4">60</transmit>
+        </active>
+        <active rat="NR" nrFrequency="DEFAULT">
+            <!-- Transmit current drain in mA. -->
+            <receive>13</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">14</transmit>
+            <transmit level="1">15</transmit>
+            <transmit level="2">16</transmit>
+            <transmit level="3">17</transmit>
+            <transmit level="4">18</transmit>
+        </active>
+        <active rat="NR" nrFrequency="LOW">
+            <!-- Transmit current drain in mA. -->
+            <receive>23</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">24</transmit>
+            <transmit level="1">25</transmit>
+            <transmit level="2">26</transmit>
+            <transmit level="3">27</transmit>
+            <transmit level="4">28</transmit>
+        </active>
+        <active rat="NR" nrFrequency="MID">
+            <!-- Transmit current drain in mA. -->
+            <receive>33</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">34</transmit>
+            <transmit level="1">35</transmit>
+            <transmit level="2">36</transmit>
+            <transmit level="3">37</transmit>
+            <transmit level="4">38</transmit>
+        </active>
+        <active rat="NR" nrFrequency="HIGH">
+            <!-- Transmit current drain in mA. -->
+            <receive>43</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">44</transmit>
+            <transmit level="1">45</transmit>
+            <transmit level="2">46</transmit>
+            <transmit level="3">47</transmit>
+            <transmit level="4">48</transmit>
+        </active>
+        <active rat="NR" nrFrequency="MMWAVE">
+            <!-- Transmit current drain in mA. -->
+            <receive>53</receive>
+            <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+            <transmit level="0">54</transmit>
+            <transmit level="1">55</transmit>
+            <transmit level="2">56</transmit>
+            <transmit level="3">57</transmit>
+            <transmit level="4">58</transmit>
+        </active>
+    </test-modem>
+</device>
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 2c3c1ed..9bb064c 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
 import androidx.test.filters.LargeTest;
@@ -39,6 +40,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+@Presubmit
 @LargeTest
 public class ApplicationPackageManagerTest extends TestCase {
     private static final String sInternalVolPath = "/data";
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 7a2c63f..d3e8bb0 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -17,6 +17,8 @@
 package android.app;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
 
 import androidx.test.filters.SmallTest;
 
@@ -25,7 +27,9 @@
 
 /**
  * Test for verifying the behavior of {@link PropertyInvalidatedCache}.  This test does
- * not use any actual binder calls - it is entirely self-contained.
+ * not use any actual binder calls - it is entirely self-contained.  This test also relies
+ * on the test mode of {@link PropertyInvalidatedCache} because Android SELinux rules do
+ * not grant test processes the permission to set system properties.
  * <p>
  * Build/Install/Run:
  *  atest FrameworksCoreTests:PropertyInvalidatedCacheTests
@@ -33,6 +37,8 @@
 @SmallTest
 public class PropertyInvalidatedCacheTests {
 
+    // This property is never set.  The test process does not have permission to set any
+    // properties.
     static final String CACHE_PROPERTY = "cache_key.cache_test_a";
 
     // This class is a proxy for binder calls.  It contains a counter that increments
@@ -58,7 +64,8 @@
         }
     }
 
-    // Clear the test mode after every test, in case this process is used for other tests.
+    // Clear the test mode after every test, in case this process is used for other
+    // tests. This also resets the test property map.
     @After
     public void tearDown() throws Exception {
         PropertyInvalidatedCache.setTestMode(false);
@@ -77,11 +84,11 @@
         PropertyInvalidatedCache<Integer, Boolean> testCache =
                 new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
                     @Override
-                    protected Boolean recompute(Integer x) {
+                    public Boolean recompute(Integer x) {
                         return tester.query(x);
                     }
                     @Override
-                    protected boolean bypass(Integer x) {
+                    public boolean bypass(Integer x) {
                         return x % 13 == 0;
                     }
                 };
@@ -131,21 +138,21 @@
         PropertyInvalidatedCache<Integer, Boolean> cache1 =
                 new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
                     @Override
-                    protected Boolean recompute(Integer x) {
+                    public Boolean recompute(Integer x) {
                         return tester.query(x);
                     }
                 };
         PropertyInvalidatedCache<Integer, Boolean> cache2 =
                 new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
                     @Override
-                    protected Boolean recompute(Integer x) {
+                    public Boolean recompute(Integer x) {
                         return tester.query(x);
                     }
                 };
         PropertyInvalidatedCache<Integer, Boolean> cache3 =
                 new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
                     @Override
-                    protected Boolean recompute(Integer x) {
+                    public Boolean recompute(Integer x) {
                         return tester.query(x);
                     }
                 };
@@ -171,10 +178,166 @@
         // Create a new cache1.  Verify that the new instance is disabled.
         cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
                     @Override
-                    protected Boolean recompute(Integer x) {
+                    public Boolean recompute(Integer x) {
                         return tester.query(x);
                     }
                 };
         assertEquals(true, cache1.getDisabledState());
+
+        // Remove the record of caches being locally disabled.  This is a clean-up step.
+        cache1.clearDisableLocal();
+        assertEquals(true, cache1.getDisabledState());
+        assertEquals(true, cache2.getDisabledState());
+        assertEquals(false, cache3.getDisabledState());
+
+        // Create a new cache1.  Verify that the new instance is not disabled.
+        cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+                    @Override
+                    public Boolean recompute(Integer x) {
+                        return tester.query(x);
+                    }
+                };
+        assertEquals(false, cache1.getDisabledState());
+    }
+
+    private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
+
+    private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
+        TestCache() {
+            this(CACHE_PROPERTY);
+        }
+
+        TestCache(String key) {
+            super(4, key);
+            setTestMode(true);
+            testPropertyName(key);
+        }
+
+        @Override
+        public String recompute(Integer qv) {
+            mRecomputeCount += 1;
+            return "foo" + qv.toString();
+        }
+
+        int getRecomputeCount() {
+            return mRecomputeCount;
+        }
+
+        private int mRecomputeCount = 0;
+    }
+
+    @Test
+    public void testCacheRecompute() {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        assertEquals(cache.isDisabledLocal(), false);
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo6", cache.query(6));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+    @Test
+    public void testCacheInitialState() {
+        TestCache cache = new TestCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+    @Test
+    public void testCachePropertyUnset() {
+        TestCache cache = new TestCache(UNSET_KEY);
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+    }
+
+    @Test
+    public void testCacheDisableState() {
+        TestCache cache = new TestCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+        cache.disableSystemWide();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(5, cache.getRecomputeCount());
+        cache.invalidateCache();  // Should not reenable
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(7, cache.getRecomputeCount());
+    }
+
+    @Test
+    public void testRefreshSameObject() {
+        int[] refreshCount = new int[1];
+        TestCache cache = new TestCache() {
+            @Override
+            public String refresh(String oldResult, Integer query) {
+                refreshCount[0] += 1;
+                return oldResult;
+            }
+        };
+        cache.invalidateCache();
+        String result1 = cache.query(5);
+        assertEquals("foo5", result1);
+        String result2 = cache.query(5);
+        assertSame(result1, result2);
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals(1, refreshCount[0]);
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, refreshCount[0]);
+    }
+
+    @Test
+    public void testRefreshInvalidateRace() {
+        int[] refreshCount = new int[1];
+        TestCache cache = new TestCache() {
+            @Override
+            public String refresh(String oldResult, Integer query) {
+                refreshCount[0] += 1;
+                invalidateCache();
+                return new String(oldResult);
+            }
+        };
+        cache.invalidateCache();
+        String result1 = cache.query(5);
+        assertEquals("foo5", result1);
+        String result2 = cache.query(5);
+        assertEquals(result1, result2);
+        assertNotSame(result1, result2);
+        assertEquals(2, cache.getRecomputeCount());
+    }
+
+    @Test
+    public void testLocalProcessDisable() {
+        TestCache cache = new TestCache();
+        assertEquals(cache.isDisabledLocal(), false);
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals(cache.isDisabledLocal(), false);
+        cache.disableLocal();
+        assertEquals(cache.isDisabledLocal(), true);
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
     }
 }
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index ceebc62..c9a6d22 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -24,6 +24,7 @@
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
 import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -32,6 +33,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ContentProviderTest {
 
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 01e240a..7b70b41 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -35,6 +35,7 @@
 import android.os.MemoryFile;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 
 import androidx.test.InstrumentationRegistry;
@@ -45,6 +46,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ContentResolverTest {
 
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 3d7d807..e4a9ce5 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -34,6 +34,7 @@
 import android.hardware.display.VirtualDisplay;
 import android.media.ImageReader;
 import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -48,6 +49,7 @@
  *  Build/Install/Run:
  *   atest FrameworksCoreTests:ContextTest
  */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ContextTest {
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 1bc46a7..68d4cd4 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -20,6 +20,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.LargeTest;
 
@@ -36,6 +37,7 @@
  * Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \
  *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
+@Presubmit
 @LargeTest
 public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest {
     @Override
diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
index dbe0278..de4c572 100644
--- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.LargeTest;
 
@@ -34,6 +35,7 @@
  * Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \
  *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
+@Presubmit
 @LargeTest
 public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest {
     @Override
diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index 0cfcd8f8..b66642c 100644
--- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -16,11 +16,17 @@
 
 package android.content.pm;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
@@ -58,6 +64,8 @@
     @Mock
     private UserManager mUserManager;
     @Mock
+    private DevicePolicyManager mDevicePolicyManager;
+    @Mock
     private ICrossProfileApps mService;
     @Mock
     private Resources mResources;
@@ -75,6 +83,10 @@
         when(mContext.getPackageName()).thenReturn(MY_PACKAGE);
         when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mContext.getSystemServiceName(DevicePolicyManager.class)).thenReturn(
+                Context.DEVICE_POLICY_SERVICE);
+        when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mDevicePolicyManager);
     }
 
     @Before
@@ -98,7 +110,7 @@
         setValidTargetProfile(MANAGED_PROFILE);
 
         mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE);
-        verify(mResources).getString(R.string.managed_profile_label);
+        verify(mDevicePolicyManager).getString(eq(SWITCH_TO_WORK_LABEL), any());
     }
 
     @Test
@@ -106,7 +118,7 @@
         setValidTargetProfile(PERSONAL_PROFILE);
 
         mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
-        verify(mResources).getString(R.string.user_owner_label);
+        verify(mDevicePolicyManager).getString(eq(SWITCH_TO_PERSONAL_LABEL), any());
     }
 
     @Test(expected = SecurityException.class)
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
deleted file mode 100644
index c2519ca0..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ /dev/null
@@ -1,3013 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.system.OsConstants.S_IFDIR;
-import static android.system.OsConstants.S_IFMT;
-import static android.system.OsConstants.S_IRGRP;
-import static android.system.OsConstants.S_IROTH;
-import static android.system.OsConstants.S_IRWXU;
-import static android.system.OsConstants.S_ISDIR;
-import static android.system.OsConstants.S_IXGRP;
-import static android.system.OsConstants.S_IXOTH;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.StatFs;
-import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructStat;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.filters.Suppress;
-
-import com.android.frameworks.coretests.R;
-import com.android.internal.content.PackageHelper;
-
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
-
-@Presubmit
-public class PackageManagerTests extends AndroidTestCase {
-    private static final boolean localLOGV = true;
-
-    public static final String TAG = "PackageManagerTests";
-
-    public static final long MAX_WAIT_TIME = 25 * 1000;
-
-    public static final long WAIT_TIME_INCR = 5 * 1000;
-
-    private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec";
-
-    private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO;
-
-    private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL;
-
-    private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL;
-
-    void failStr(String errMsg) {
-        Log.w(TAG, "errMsg=" + errMsg);
-        fail(errMsg);
-    }
-
-    void failStr(Exception e) {
-        failStr(e.getMessage());
-    }
-
-    private abstract static class GenericReceiver extends BroadcastReceiver {
-        private boolean doneFlag = false;
-
-        boolean received = false;
-
-        Intent intent;
-
-        IntentFilter filter;
-
-        abstract boolean notifyNow(Intent intent);
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (notifyNow(intent)) {
-                synchronized (this) {
-                    received = true;
-                    doneFlag = true;
-                    this.intent = intent;
-                    notifyAll();
-                }
-            }
-        }
-
-        public boolean isDone() {
-            return doneFlag;
-        }
-
-        public void setFilter(IntentFilter filter) {
-            this.filter = filter;
-        }
-    }
-
-    private static class InstallReceiver extends GenericReceiver {
-        String pkgName;
-
-        InstallReceiver(String pkgName) {
-            this.pkgName = pkgName;
-            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-            filter.addDataScheme("package");
-            super.setFilter(filter);
-        }
-
-        public boolean notifyNow(Intent intent) {
-            String action = intent.getAction();
-            if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                return false;
-            }
-            Uri data = intent.getData();
-            String installedPkg = data.getEncodedSchemeSpecificPart();
-            if (pkgName.equals(installedPkg)) {
-                return true;
-            }
-            return false;
-        }
-    }
-
-    private static class LocalIntentReceiver {
-        private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
-
-        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
-            @Override
-            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
-                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-                try {
-                    mResult.offer(intent, 5, TimeUnit.SECONDS);
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        };
-
-        public IntentSender getIntentSender() {
-            return new IntentSender((IIntentSender) mLocalSender);
-        }
-
-        public Intent getResult() {
-            try {
-                return mResult.take();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private PackageManager getPm() {
-        return mContext.getPackageManager();
-    }
-
-    private PackageInstaller getPi() {
-        return getPm().getPackageInstaller();
-    }
-
-    private void writeSplitToInstallSession(PackageInstaller.Session session, String inPath,
-            String splitName) throws RemoteException {
-        long sizeBytes = 0;
-        final File file = new File(inPath);
-        if (file.isFile()) {
-            sizeBytes = file.length();
-        } else {
-            return;
-        }
-
-        InputStream in = null;
-        OutputStream out = null;
-        try {
-            in = new FileInputStream(inPath);
-            out = session.openWrite(splitName, 0, sizeBytes);
-
-            int total = 0;
-            byte[] buffer = new byte[65536];
-            int c;
-            while ((c = in.read(buffer)) != -1) {
-                total += c;
-                out.write(buffer, 0, c);
-            }
-            session.fsync(out);
-        } catch (IOException e) {
-            fail("Error: failed to write; " + e.getMessage());
-        } finally {
-            IoUtils.closeQuietly(out);
-            IoUtils.closeQuietly(in);
-            IoUtils.closeQuietly(session);
-        }
-    }
-
-    private void invokeInstallPackage(Uri packageUri, int flags, GenericReceiver receiver,
-            boolean shouldSucceed) {
-        mContext.registerReceiver(receiver, receiver.filter);
-        synchronized (receiver) {
-            final String inPath = packageUri.getPath();
-            PackageInstaller.Session session = null;
-            try {
-                final SessionParams sessionParams =
-                        new SessionParams(SessionParams.MODE_FULL_INSTALL);
-                sessionParams.installFlags = flags;
-                final int sessionId = getPi().createSession(sessionParams);
-                session = getPi().openSession(sessionId);
-                writeSplitToInstallSession(session, inPath, "base.apk");
-                final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
-                session.commit(localReceiver.getIntentSender());
-                final Intent result = localReceiver.getResult();
-                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                        PackageInstaller.STATUS_FAILURE);
-                if (shouldSucceed) {
-                    if (status != PackageInstaller.STATUS_SUCCESS) {
-                        fail("Installation should have succeeded, but got code " + status);
-                    }
-                } else {
-                    if (status == PackageInstaller.STATUS_SUCCESS) {
-                        fail("Installation should have failed");
-                    }
-                    // We'll never get a broadcast since the package failed to install
-                    return;
-                }
-                // Verify we received the broadcast
-                long waitTime = 0;
-                while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) {
-                    try {
-                        receiver.wait(WAIT_TIME_INCR);
-                        waitTime += WAIT_TIME_INCR;
-                    } catch (InterruptedException e) {
-                        Log.i(TAG, "Interrupted during sleep", e);
-                    }
-                }
-                if (!receiver.isDone()) {
-                    fail("Timed out waiting for PACKAGE_ADDED notification");
-                }
-            } catch (IllegalArgumentException | IOException | RemoteException e) {
-                Log.w(TAG, "Failed to install package; path=" + inPath, e);
-                fail("Failed to install package; path=" + inPath + ", e=" + e);
-            } finally {
-                IoUtils.closeQuietly(session);
-                mContext.unregisterReceiver(receiver);
-            }
-        }
-    }
-
-    private void invokeInstallPackageFail(Uri packageUri, int flags, int expectedResult) {
-        final String inPath = packageUri.getPath();
-        PackageInstaller.Session session = null;
-        try {
-            final SessionParams sessionParams =
-                    new SessionParams(SessionParams.MODE_FULL_INSTALL);
-            sessionParams.installFlags = flags;
-            final int sessionId = getPi().createSession(sessionParams);
-            session = getPi().openSession(sessionId);
-            writeSplitToInstallSession(session, inPath, "base.apk");
-            final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
-            session.commit(localReceiver.getIntentSender());
-            final Intent result = localReceiver.getResult();
-            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageInstaller.STATUS_SUCCESS);
-            String statusMessage = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-            assertEquals(statusMessage, expectedResult, status);
-        } catch (IllegalArgumentException | IOException | RemoteException e) {
-            Log.w(TAG, "Failed to install package; path=" + inPath, e);
-            fail("Failed to install package; path=" + inPath + ", e=" + e);
-        } finally {
-            IoUtils.closeQuietly(session);
-        }
-    }
-
-    private Uri getInstallablePackage(int fileResId, File outFile) {
-        Resources res = mContext.getResources();
-        InputStream is = null;
-        try {
-            is = res.openRawResource(fileResId);
-        } catch (NotFoundException e) {
-            failStr("Failed to load resource with id: " + fileResId);
-        }
-        FileUtils.setPermissions(outFile.getPath(),
-                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
-                -1, -1);
-        assertTrue(FileUtils.copyToFile(is, outFile));
-        FileUtils.setPermissions(outFile.getPath(),
-                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
-                -1, -1);
-        return Uri.fromFile(outFile);
-    }
-
-    private ParsingPackage parsePackage(Uri packageURI) {
-        final String archiveFilePath = packageURI.getPath();
-        ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
-                new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(),
-                false /*collectCertificates*/);
-        if (result.isError()) {
-            throw new IllegalStateException(result.getErrorMessage(), result.getException());
-        }
-        return result.getResult();
-    }
-
-    private boolean checkSd(long pkgLen) {
-        String status = Environment.getExternalStorageState();
-        if (!status.equals(Environment.MEDIA_MOUNTED)) {
-            return false;
-        }
-        long sdSize = -1;
-        StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
-        sdSize = (long) sdStats.getAvailableBlocks() * (long) sdStats.getBlockSize();
-        // TODO check for thresholds here
-        return pkgLen <= sdSize;
-
-    }
-
-    private boolean checkInt(long pkgLen) {
-        StatFs intStats = new StatFs(Environment.getDataDirectory().getPath());
-        long intSize = (long) intStats.getBlockCount() * (long) intStats.getBlockSize();
-        long iSize = (long) intStats.getAvailableBlocks() * (long) intStats.getBlockSize();
-        // TODO check for thresholds here?
-        return pkgLen <= iSize;
-    }
-
-    private static final int INSTALL_LOC_INT = 1;
-
-    private static final int INSTALL_LOC_SD = 2;
-
-    private static final int INSTALL_LOC_ERR = -1;
-
-    private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
-        // Flags explicitly over ride everything else.
-        if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
-            return INSTALL_LOC_INT;
-        }
-        // Manifest option takes precedence next
-        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
-            if (checkSd(pkgLen)) {
-                return INSTALL_LOC_SD;
-            }
-            if (checkInt(pkgLen)) {
-                return INSTALL_LOC_INT;
-            }
-            return INSTALL_LOC_ERR;
-        }
-        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
-            if (checkInt(pkgLen)) {
-                return INSTALL_LOC_INT;
-            }
-            return INSTALL_LOC_ERR;
-        }
-        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
-            // Check for free memory internally
-            if (checkInt(pkgLen)) {
-                return INSTALL_LOC_INT;
-            }
-            // Check for free memory externally
-            if (checkSd(pkgLen)) {
-                return INSTALL_LOC_SD;
-            }
-            return INSTALL_LOC_ERR;
-        }
-        // Check for settings preference.
-        boolean checkSd = false;
-        int userPref = getDefaultInstallLoc();
-        if (userPref == APP_INSTALL_DEVICE) {
-            if (checkInt(pkgLen)) {
-                return INSTALL_LOC_INT;
-            }
-            return INSTALL_LOC_ERR;
-        } else if (userPref == APP_INSTALL_SDCARD) {
-            if (checkSd(pkgLen)) {
-                return INSTALL_LOC_SD;
-            }
-            return INSTALL_LOC_ERR;
-        }
-        // Default system policy for apps with no manifest option specified.
-        // Check for free memory internally
-        if (checkInt(pkgLen)) {
-            return INSTALL_LOC_INT;
-        }
-        return INSTALL_LOC_ERR;
-    }
-
-    private void assertInstall(ParsingPackage pkg, int flags, int expInstallLocation) {
-        try {
-            String pkgName = pkg.getPackageName();
-            ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
-            assertNotNull(info);
-            assertEquals(pkgName, info.packageName);
-            File dataDir = Environment.getDataDirectory();
-            String appInstallParent = new File(dataDir, "app").getPath();
-            File srcDir = new File(info.sourceDir);
-            String srcPathParent = srcDir.getParentFile().getParentFile().getParent();
-            File publicSrcDir = new File(info.publicSourceDir);
-            String publicSrcPath = publicSrcDir.getParentFile().getParentFile().getParent();
-            long pkgLen = new File(info.sourceDir).length();
-            String expectedLibPath = new File(new File(info.sourceDir).getParentFile(), "lib")
-                    .getPath();
-
-            int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
-            if (rLoc == INSTALL_LOC_INT) {
-                assertEquals(appInstallParent, srcPathParent);
-                assertEquals(appInstallParent, publicSrcPath);
-                assertStartsWith("Native library should point to shared lib directory",
-                        expectedLibPath, info.nativeLibraryDir);
-                assertDirOwnerGroupPermsIfExists(
-                        "Native library directory should be owned by system:system and 0755",
-                        Process.SYSTEM_UID, Process.SYSTEM_UID,
-                        S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
-                        info.nativeLibraryDir);
-                assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
-
-                // Make sure the native library dir is not a symlink
-                final File nativeLibDir = new File(info.nativeLibraryDir);
-                if (nativeLibDir.exists()) {
-                    try {
-                        assertEquals("Native library dir should not be a symlink",
-                                info.nativeLibraryDir, nativeLibDir.getCanonicalPath());
-                    } catch (IOException e) {
-                        fail("Can't read " + nativeLibDir.getPath());
-                    }
-                }
-            } else if (rLoc == INSTALL_LOC_SD) {
-                assertTrue("Application flags (" + info.flags
-                        + ") should contain FLAG_EXTERNAL_STORAGE",
-                        (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
-                // Might need to check:
-                // ((info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0)
-                assertStartsWith("The APK path should point to the ASEC",
-                        SECURE_CONTAINERS_PREFIX, srcPathParent);
-                assertStartsWith("The public APK path should point to the ASEC",
-                        SECURE_CONTAINERS_PREFIX, publicSrcPath);
-                assertStartsWith("The native library path should point to the ASEC",
-                        SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir);
-
-                // Make sure the native library in /data/data/<app>/lib is a
-                // symlink to the ASEC
-                final File nativeLibSymLink = new File(info.dataDir, "lib");
-                assertTrue("Native library symlink should exist at " + nativeLibSymLink.getPath(),
-                        nativeLibSymLink.exists());
-                try {
-                    assertEquals(nativeLibSymLink.getPath() + " should be a symlink to "
-                            + info.nativeLibraryDir, info.nativeLibraryDir,
-                            nativeLibSymLink.getCanonicalPath());
-                } catch (IOException e) {
-                    fail("Can't read " + nativeLibSymLink.getPath());
-                }
-            } else {
-                // TODO handle error. Install should have failed.
-                fail("Install should have failed");
-            }
-        } catch (NameNotFoundException e) {
-            failStr("failed with exception : " + e);
-        }
-    }
-
-    private void assertDirOwnerGroupPermsIfExists(String reason, int uid, int gid, int perms,
-            String path) {
-        if (!new File(path).exists()) {
-            return;
-        }
-
-        final StructStat stat;
-        try {
-            stat = Os.lstat(path);
-        } catch (ErrnoException e) {
-            throw new AssertionError(reason + "\n" + "Got: " + path + " does not exist");
-        }
-
-        StringBuilder sb = new StringBuilder();
-
-        if (!S_ISDIR(stat.st_mode)) {
-            sb.append("\nExpected type: ");
-            sb.append(S_IFDIR);
-            sb.append("\ngot type: ");
-            sb.append((stat.st_mode & S_IFMT));
-        }
-
-        if (stat.st_uid != uid) {
-            sb.append("\nExpected owner: ");
-            sb.append(uid);
-            sb.append("\nGot owner: ");
-            sb.append(stat.st_uid);
-        }
-
-        if (stat.st_gid != gid) {
-            sb.append("\nExpected group: ");
-            sb.append(gid);
-            sb.append("\nGot group: ");
-            sb.append(stat.st_gid);
-        }
-
-        if ((stat.st_mode & ~S_IFMT) != perms) {
-            sb.append("\nExpected permissions: ");
-            sb.append(Integer.toOctalString(perms));
-            sb.append("\nGot permissions: ");
-            sb.append(Integer.toOctalString(stat.st_mode & ~S_IFMT));
-        }
-
-        if (sb.length() > 0) {
-            throw new AssertionError(reason + sb.toString());
-        }
-    }
-
-    private static void assertStartsWith(String prefix, String actual) {
-        assertStartsWith("", prefix, actual);
-    }
-
-    private static void assertStartsWith(String description, String prefix, String actual) {
-        if (!actual.startsWith(prefix)) {
-            StringBuilder sb = new StringBuilder(description);
-            sb.append("\nExpected prefix: ");
-            sb.append(prefix);
-            sb.append("\n     got: ");
-            sb.append(actual);
-            sb.append('\n');
-            throw new AssertionError(sb.toString());
-        }
-    }
-
-    private void assertNotInstalled(String pkgName) {
-        try {
-            ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
-            fail(pkgName + " shouldnt be installed");
-        } catch (NameNotFoundException e) {
-        }
-    }
-
-    class InstallParams {
-        Uri packageURI;
-
-        ParsingPackage pkg;
-
-        InstallParams(String outFileName, int rawResId) {
-            this.pkg = getParsedPackage(outFileName, rawResId);
-            this.packageURI = Uri.fromFile(new File(pkg.getPath()));
-        }
-
-        InstallParams(ParsingPackage pkg) {
-            this.packageURI = Uri.fromFile(new File(pkg.getPath()));
-            this.pkg = pkg;
-        }
-
-        long getApkSize() {
-            File file = new File(pkg.getPath());
-            return file.length();
-        }
-    }
-
-    private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp)
-            throws Exception {
-        return installFromRawResource("install.apk", R.raw.install, flags, cleanUp, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    static final String PERM_PACKAGE = "package";
-
-    static final String PERM_DEFINED = "defined";
-
-    static final String PERM_UNDEFINED = "undefined";
-
-    static final String PERM_USED = "used";
-
-    static final String PERM_NOTUSED = "notused";
-
-    private void assertPermissions(String[] cmds) {
-        final PackageManager pm = getPm();
-        String pkg = null;
-        PackageInfo pkgInfo = null;
-        String mode = PERM_DEFINED;
-        int i = 0;
-        while (i < cmds.length) {
-            String cmd = cmds[i++];
-            if (cmd == PERM_PACKAGE) {
-                pkg = cmds[i++];
-                try {
-                    pkgInfo = pm.getPackageInfo(pkg,
-                            PackageManager.GET_PERMISSIONS
-                            | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                } catch (NameNotFoundException e) {
-                    pkgInfo = null;
-                }
-            } else if (cmd == PERM_DEFINED || cmd == PERM_UNDEFINED
-                    || cmd == PERM_USED || cmd == PERM_NOTUSED) {
-                mode = cmds[i++];
-            } else {
-                if (mode == PERM_DEFINED) {
-                    try {
-                        PermissionInfo pi = pm.getPermissionInfo(cmd, 0);
-                        assertNotNull(pi);
-                        assertEquals(pi.packageName, pkg);
-                        assertEquals(pi.name, cmd);
-                        assertNotNull(pkgInfo);
-                        boolean found = false;
-                        for (int j = 0; j < pkgInfo.permissions.length && !found; j++) {
-                            if (pkgInfo.permissions[j].name.equals(cmd)) {
-                                found = true;
-                            }
-                        }
-                        if (!found) {
-                            fail("Permission not found: " + cmd);
-                        }
-                    } catch (NameNotFoundException e) {
-                        throw new RuntimeException(e);
-                    }
-                } else if (mode == PERM_UNDEFINED) {
-                    try {
-                        pm.getPermissionInfo(cmd, 0);
-                        throw new RuntimeException("Permission exists: " + cmd);
-                    } catch (NameNotFoundException e) {
-                    }
-                    if (pkgInfo != null) {
-                        boolean found = false;
-                        for (int j = 0; j < pkgInfo.permissions.length && !found; j++) {
-                            if (pkgInfo.permissions[j].name.equals(cmd)) {
-                                found = true;
-                            }
-                        }
-                        if (found) {
-                            fail("Permission still exists: " + cmd);
-                        }
-                    }
-                } else if (mode == PERM_USED || mode == PERM_NOTUSED) {
-                    boolean found = false;
-                    for (int j = 0; j < pkgInfo.requestedPermissions.length && !found; j++) {
-                        if (pkgInfo.requestedPermissions[j].equals(cmd)) {
-                            found = true;
-                        }
-                    }
-                    if (!found) {
-                        fail("Permission not requested: " + cmd);
-                    }
-                    if (mode == PERM_USED) {
-                        if (pm.checkPermission(cmd, pkg) != PackageManager.PERMISSION_GRANTED) {
-                            fail("Permission not granted: " + cmd);
-                        }
-                    } else {
-                        if (pm.checkPermission(cmd, pkg) != PackageManager.PERMISSION_DENIED) {
-                            fail("Permission granted: " + cmd);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private ParsingPackage getParsedPackage(String outFileName, int rawResId) {
-        PackageManager pm = mContext.getPackageManager();
-        File filesDir = mContext.getFilesDir();
-        File outFile = new File(filesDir, outFileName);
-        Uri packageURI = getInstallablePackage(rawResId, outFile);
-        return parsePackage(packageURI);
-    }
-
-    /*
-     * Utility function that reads a apk bundled as a raw resource
-     * copies it into own data directory and invokes
-     * PackageManager api to install it.
-     */
-    private void installFromRawResource(InstallParams ip, int flags, boolean cleanUp, boolean fail,
-            int result, int expInstallLocation) throws Exception {
-        PackageManager pm = mContext.getPackageManager();
-        ParsingPackage pkg = ip.pkg;
-        Uri packageURI = ip.packageURI;
-        if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
-            // Make sure the package doesn't exist
-            try {
-                ApplicationInfo appInfo = pm.getApplicationInfo(pkg.getPackageName(),
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                GenericReceiver receiver = new DeleteReceiver(pkg.getPackageName());
-                invokeDeletePackage(pkg.getPackageName(), 0, receiver);
-            } catch (IllegalArgumentException | NameNotFoundException e) {
-            }
-        }
-        try {
-            if (fail) {
-                invokeInstallPackageFail(packageURI, flags, result);
-                if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
-                    assertNotInstalled(pkg.getPackageName());
-                }
-            } else {
-                InstallReceiver receiver = new InstallReceiver(pkg.getPackageName());
-                invokeInstallPackage(packageURI, flags, receiver, true);
-                // Verify installed information
-                assertInstall(pkg, flags, expInstallLocation);
-            }
-        } finally {
-            if (cleanUp) {
-                cleanUpInstall(ip);
-            }
-        }
-    }
-
-    /*
-     * Utility function that reads a apk bundled as a raw resource
-     * copies it into own data directory and invokes
-     * PackageManager api to install it.
-     */
-    private InstallParams installFromRawResource(String outFileName, int rawResId, int flags,
-            boolean cleanUp, boolean fail, int result, int expInstallLocation) throws Exception {
-        InstallParams ip = new InstallParams(outFileName, rawResId);
-        installFromRawResource(ip, flags, cleanUp, fail, result, expInstallLocation);
-        return ip;
-    }
-
-    @LargeTest
-    public void testInstallNormalInternal() throws Exception {
-        sampleInstallFromRawResource(0, true);
-    }
-
-    /* ------------------------- Test replacing packages -------------- */
-    class ReplaceReceiver extends GenericReceiver {
-        String pkgName;
-
-        final static int INVALID = -1;
-
-        final static int REMOVED = 1;
-
-        final static int ADDED = 2;
-
-        final static int REPLACED = 3;
-
-        int removed = INVALID;
-
-        // for updated system apps only
-        boolean update = false;
-
-        ReplaceReceiver(String pkgName) {
-            this.pkgName = pkgName;
-            filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            if (update) {
-                filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-            }
-            filter.addDataScheme("package");
-            super.setFilter(filter);
-        }
-
-        public boolean notifyNow(Intent intent) {
-            String action = intent.getAction();
-            Uri data = intent.getData();
-            String installedPkg = data.getEncodedSchemeSpecificPart();
-            if (pkgName == null || !pkgName.equals(installedPkg)) {
-                return false;
-            }
-            if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                removed = REMOVED;
-            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                if (removed != REMOVED) {
-                    return false;
-                }
-                boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-                if (!replacing) {
-                    return false;
-                }
-                removed = ADDED;
-                if (!update) {
-                    return true;
-                }
-            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
-                if (removed != ADDED) {
-                    return false;
-                }
-                removed = REPLACED;
-                return true;
-            }
-            return false;
-        }
-    }
-
-    /*
-     * Utility function that reads a apk bundled as a raw resource
-     * copies it into own data directory and invokes
-     * PackageManager api to install first and then replace it
-     * again.
-     */
-    private void sampleReplaceFromRawResource(int flags) throws Exception {
-        InstallParams ip = sampleInstallFromRawResource(flags, false);
-        boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
-        Log.i(TAG, "replace=" + replace);
-        GenericReceiver receiver;
-        if (replace) {
-            receiver = new ReplaceReceiver(ip.pkg.getPackageName());
-            Log.i(TAG, "Creating replaceReceiver");
-        } else {
-            receiver = new InstallReceiver(ip.pkg.getPackageName());
-        }
-        try {
-            invokeInstallPackage(ip.packageURI, flags, receiver, true);
-            if (replace) {
-                assertInstall(ip.pkg, flags, ip.pkg.getInstallLocation());
-            }
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    @LargeTest
-    public void testReplaceFlagDoesNotNeedToBeSet() throws Exception {
-        sampleReplaceFromRawResource(0);
-    }
-
-    @LargeTest
-    public void testReplaceNormalInternal() throws Exception {
-        sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
-    }
-
-    /* -------------- Delete tests --- */
-    private static class DeleteObserver extends IPackageDeleteObserver.Stub {
-        private CountDownLatch mLatch = new CountDownLatch(1);
-
-        private int mReturnCode;
-
-        private final String mPackageName;
-
-        private String mObservedPackage;
-
-        public DeleteObserver(String packageName) {
-            mPackageName = packageName;
-        }
-
-        public boolean isSuccessful() {
-            return mReturnCode == PackageManager.DELETE_SUCCEEDED;
-        }
-
-        public void packageDeleted(String packageName, int returnCode) throws RemoteException {
-            mObservedPackage = packageName;
-
-            mReturnCode = returnCode;
-
-            mLatch.countDown();
-        }
-
-        public void waitForCompletion(long timeoutMillis) {
-            final long deadline = SystemClock.uptimeMillis() + timeoutMillis;
-
-            long waitTime = timeoutMillis;
-            while (waitTime > 0) {
-                try {
-                    boolean done = mLatch.await(waitTime, TimeUnit.MILLISECONDS);
-                    if (done) {
-                        assertEquals(mPackageName, mObservedPackage);
-                        return;
-                    }
-                } catch (InterruptedException e) {
-                    // TODO Auto-generated catch block
-                    e.printStackTrace();
-                }
-                waitTime = deadline - SystemClock.uptimeMillis();
-            }
-
-            throw new AssertionError("Timeout waiting for package deletion");
-        }
-    }
-
-    class DeleteReceiver extends GenericReceiver {
-        String pkgName;
-
-        DeleteReceiver(String pkgName) {
-            this.pkgName = pkgName;
-            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addDataScheme("package");
-            super.setFilter(filter);
-        }
-
-        public boolean notifyNow(Intent intent) {
-            String action = intent.getAction();
-            if (!Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                return false;
-            }
-            Uri data = intent.getData();
-            String installedPkg = data.getEncodedSchemeSpecificPart();
-            if (pkgName.equals(installedPkg)) {
-                return true;
-            }
-            return false;
-        }
-    }
-
-    public boolean invokeDeletePackage(final String pkgName, int flags, GenericReceiver receiver)
-            throws Exception {
-        ApplicationInfo info = getPm().getApplicationInfo(pkgName,
-                PackageManager.MATCH_UNINSTALLED_PACKAGES);
-
-        mContext.registerReceiver(receiver, receiver.filter);
-        try {
-            final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
-            getPi().uninstall(pkgName,
-                    flags | PackageManager.DELETE_ALL_USERS,
-                    localReceiver.getIntentSender());
-            localReceiver.getResult();
-
-            assertUninstalled(info);
-
-            // Verify we received the broadcast
-            // TODO replace this with a CountDownLatch
-            synchronized (receiver) {
-                long waitTime = 0;
-                while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) {
-                    receiver.wait(WAIT_TIME_INCR);
-                    waitTime += WAIT_TIME_INCR;
-                }
-                if (!receiver.isDone()) {
-                    throw new Exception("Timed out waiting for PACKAGE_REMOVED notification");
-                }
-            }
-            return receiver.received;
-        } finally {
-            mContext.unregisterReceiver(receiver);
-        }
-    }
-
-    private static void assertUninstalled(ApplicationInfo info) throws Exception {
-        File nativeLibraryFile = new File(info.nativeLibraryDir);
-        assertFalse("Native library directory " + info.nativeLibraryDir
-                + " should be erased", nativeLibraryFile.exists());
-    }
-
-    public void deleteFromRawResource(int iFlags, int dFlags) throws Exception {
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0);
-        GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
-        try {
-            assertTrue(invokeDeletePackage(ip.pkg.getPackageName(), dFlags, receiver));
-            ApplicationInfo info = null;
-            Log.i(TAG, "okay4");
-            try {
-                info = getPm().getApplicationInfo(ip.pkg.getPackageName(),
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES);
-            } catch (NameNotFoundException e) {
-                info = null;
-            }
-            if (retainData) {
-                assertNotNull(info);
-                assertEquals(info.packageName, ip.pkg.getPackageName());
-            } else {
-                assertNull(info);
-            }
-        } catch (Exception e) {
-            failStr(e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    @LargeTest
-    public void testDeleteNormalInternal() throws Exception {
-        deleteFromRawResource(0, 0);
-    }
-
-
-    @LargeTest
-    public void testDeleteNormalInternalRetainData() throws Exception {
-        deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
-    }
-
-    void cleanUpInstall(InstallParams ip) throws Exception {
-        if (ip == null) {
-            return;
-        }
-        Runtime.getRuntime().gc();
-        try {
-            cleanUpInstall(ip.pkg.getPackageName());
-        } finally {
-            File outFile = new File(ip.pkg.getPath());
-            if (outFile != null && outFile.exists()) {
-                outFile.delete();
-            }
-        }
-    }
-
-    private void cleanUpInstall(String pkgName) throws Exception {
-        if (pkgName == null) {
-            return;
-        }
-        Log.i(TAG, "Deleting package : " + pkgName);
-        try {
-            final ApplicationInfo info = getPm().getApplicationInfo(pkgName,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES);
-            if (info != null) {
-                final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
-                getPi().uninstall(pkgName,
-                        PackageManager.DELETE_ALL_USERS,
-                        localReceiver.getIntentSender());
-                localReceiver.getResult();
-                assertUninstalled(info);
-            }
-        } catch (IllegalArgumentException | NameNotFoundException e) {
-        }
-    }
-
-    @LargeTest
-    public void testManifestInstallLocationInternal() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                0, true, false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
-    public void testManifestInstallLocationSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                0, true, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testManifestInstallLocationAuto() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
-    }
-
-    @LargeTest
-    public void testManifestInstallLocationUnspecified() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_unspecified,
-                0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = 0;
-        int iApk = R.raw.install_loc_internal;
-        int rFlags = 0;
-        int rApk = R.raw.install_loc_sdcard;
-        InstallParams ip = installFromRawResource("install.apk", iApk,
-                iFlags, false,
-                false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.getPackageName());
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            InstallParams rp = installFromRawResource("install.apk", rApk,
-                    replaceFlags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-            assertInstall(rp.pkg, replaceFlags, rp.pkg.getInstallLocation());
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    @LargeTest
-    public void testManifestInstallLocationReplaceSdcardInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = 0;
-        int iApk = R.raw.install_loc_sdcard;
-        int rFlags = 0;
-        int rApk = R.raw.install_loc_unspecified;
-        InstallParams ip = installFromRawResource("install.apk", iApk,
-                iFlags, false,
-                false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            InstallParams rp = installFromRawResource("install.apk", rApk,
-                    replaceFlags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-            assertInstall(rp.pkg, replaceFlags, ip.pkg.getInstallLocation());
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    class MoveReceiver extends GenericReceiver {
-        String pkgName;
-
-        final static int INVALID = -1;
-
-        final static int REMOVED = 1;
-
-        final static int ADDED = 2;
-
-        int removed = INVALID;
-
-        MoveReceiver(String pkgName) {
-            this.pkgName = pkgName;
-            filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-            filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-            super.setFilter(filter);
-        }
-
-        public boolean notifyNow(Intent intent) {
-            String action = intent.getAction();
-            Log.i(TAG, "MoveReceiver::" + action);
-            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                if (list != null) {
-                    for (String pkg : list) {
-                        if (pkg.equals(pkgName)) {
-                            removed = REMOVED;
-                            break;
-                        }
-                    }
-                }
-                removed = REMOVED;
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-                if (removed != REMOVED) {
-                    return false;
-                }
-                String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                if (list != null) {
-                    for (String pkg : list) {
-                        if (pkg.equals(pkgName)) {
-                            removed = ADDED;
-                            return true;
-                        }
-                    }
-                }
-            }
-            return false;
-        }
-    }
-
-    public boolean invokeMovePackage(String pkgName, int flags, GenericReceiver receiver)
-            throws Exception {
-        throw new UnsupportedOperationException();
-    }
-
-    private boolean invokeMovePackageFail(String pkgName, int flags, int errCode) throws Exception {
-        throw new UnsupportedOperationException();
-    }
-
-    private int getDefaultInstallLoc() {
-        int origDefaultLoc = PackageInfo.INSTALL_LOCATION_AUTO;
-        try {
-            origDefaultLoc = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.DEFAULT_INSTALL_LOCATION);
-        } catch (SettingNotFoundException e1) {
-        }
-        return origDefaultLoc;
-    }
-
-    private void setInstallLoc(int loc) {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
-    }
-
-    /*
-     * Tests for moving apps between internal and external storage
-     */
-    /*
-     * Utility function that reads a apk bundled as a raw resource
-     * copies it into own data directory and invokes
-     * PackageManager api to install first and then replace it
-     * again.
-     */
-
-    private void moveFromRawResource(String outFileName, int rawResId, int installFlags,
-            int moveFlags, boolean cleanUp, boolean fail, int result) throws Exception {
-        int origDefaultLoc = getDefaultInstallLoc();
-        InstallParams ip = null;
-        try {
-            setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
-            // Install first
-            ip = installFromRawResource("install.apk", rawResId, installFlags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            ApplicationInfo oldAppInfo = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
-            if (fail) {
-                assertTrue(invokeMovePackageFail(ip.pkg.getPackageName(), moveFlags, result));
-                ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
-                assertNotNull(info);
-                assertEquals(oldAppInfo.flags, info.flags);
-            } else {
-                // Create receiver based on expRetCode
-                MoveReceiver receiver = new MoveReceiver(ip.pkg.getPackageName());
-                boolean retCode = invokeMovePackage(ip.pkg.getPackageName(), moveFlags, receiver);
-                assertTrue(retCode);
-                ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
-                assertNotNull("ApplicationInfo for recently installed application should exist",
-                        info);
-                if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) {
-                    assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should NOT be set",
-                            (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0);
-                    assertStartsWith("Native library dir should be in dataDir",
-                            info.dataDir, info.nativeLibraryDir);
-                } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0) {
-                    assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should be set",
-                            (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
-                    assertStartsWith("Native library dir should point to ASEC",
-                            SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir);
-                }
-            }
-        } catch (NameNotFoundException e) {
-            failStr("Pkg hasnt been installed correctly");
-        } finally {
-            if (ip != null) {
-                cleanUpInstall(ip);
-            }
-            // Restore default install location
-            setInstallLoc(origDefaultLoc);
-        }
-    }
-
-    private void sampleMoveFromRawResource(int installFlags, int moveFlags, boolean fail,
-            int result) throws Exception {
-        moveFromRawResource("install.apk",
-                R.raw.install, installFlags, moveFlags, true,
-                fail, result);
-    }
-
-    @LargeTest
-    public void testMoveAppInternalToExternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_INTERNAL;
-        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
-        boolean fail = false;
-        int result = PackageManager.MOVE_SUCCEEDED;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @Suppress
-    @LargeTest
-    public void testMoveAppInternalToInternal() throws Exception {
-        int installFlags = PackageManager.INSTALL_INTERNAL;
-        int moveFlags = PackageManager.MOVE_INTERNAL;
-        boolean fail = true;
-        int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
-    public void testMoveAppFailInternalToExternalDelete() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = 0;
-        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
-        boolean fail = true;
-        final int result = PackageManager.MOVE_FAILED_DOESNT_EXIST;
-
-        int rawResId = R.raw.install;
-        int origDefaultLoc = getDefaultInstallLoc();
-        InstallParams ip = null;
-        try {
-            PackageManager pm = getPm();
-            setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
-            // Install first
-            ip = installFromRawResource("install.apk", R.raw.install, installFlags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            // Delete the package now retaining data.
-            GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
-            invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
-            assertTrue(invokeMovePackageFail(ip.pkg.getPackageName(), moveFlags, result));
-        } catch (Exception e) {
-            failStr(e);
-        } finally {
-            if (ip != null) {
-                cleanUpInstall(ip);
-            }
-            // Restore default install location
-            setInstallLoc(origDefaultLoc);
-        }
-    }
-
-    /*---------- Recommended install location tests ----*/
-    /*
-     * PrecedenceSuffixes:
-     * Flag : FlagI, FlagE, FlagF
-     * I - internal, E - external, F - forward locked, Flag suffix absent if not using any option.
-     * Manifest: ManifestI, ManifestE, ManifestA, Manifest suffix absent if not using any option.
-     * Existing: Existing suffix absent if not existing.
-     * User: UserI, UserE, UserA, User suffix absent if not existing.
-     *
-     */
-
-    /*
-     * Install an app on internal flash
-     */
-    @LargeTest
-    public void testFlagI() throws Exception {
-        sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, true);
-    }
-
-    /*
-     * Install an app with both internal and manifest option set.
-     * should install on internal.
-     */
-    @LargeTest
-    public void testFlagIManifestI() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                PackageManager.INSTALL_INTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-    /*
-     * Install an app with both internal and manifest preference for
-     * preferExternal. Should install on internal.
-     */
-    @LargeTest
-    public void testFlagIManifestE() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_INTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-    /*
-     * Install an app with both internal and manifest preference for
-     * auto. should install internal.
-     */
-    @LargeTest
-    public void testFlagIManifestA() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                PackageManager.INSTALL_INTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    /*
-     * The following test functions verify install location for existing apps.
-     * ie existing app can be installed internally or externally. If install
-     * flag is explicitly set it should override current location. If manifest location
-     * is set, that should over ride current location too. if not the existing install
-     * location should be honoured.
-     * testFlagI/E/F/ExistingI/E -
-     */
-    @LargeTest
-    public void testFlagIExistingI() throws Exception {
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    /*
-     * The following set of tests verify the installation of apps with
-     * install location attribute set to internalOnly, preferExternal and auto.
-     * The manifest option should dictate the install location.
-     * public void testManifestI/E/A
-     * TODO out of memory fall back behaviour.
-     */
-    @LargeTest
-    public void testManifestI() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                0,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
-    public void testManifestE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                0,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testManifestA() throws Exception {
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                0,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    /*
-     * The following set of tests verify the installation of apps
-     * with install location attribute set to internalOnly, preferExternal and auto
-     * for already existing apps. The manifest option should take precedence.
-     * TODO add out of memory fall back behaviour.
-     * testManifestI/E/AExistingI/E
-     */
-    @LargeTest
-    public void testManifestIExistingI() throws Exception {
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
-    public void testManifestEExistingI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testManifestAExistingI() throws Exception {
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_AUTO);
-    }
-
-    /*
-     * The following set of tests check install location for existing
-     * application based on user setting.
-     */
-    private int getExpectedInstallLocation(int userSetting) {
-        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
-        boolean enable = getUserSettingSetInstallLocation();
-        if (enable) {
-            if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
-                iloc = PackageInfo.INSTALL_LOCATION_AUTO;
-            } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
-                iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
-            } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
-                iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
-            }
-        }
-        return iloc;
-    }
-
-    private void setExistingXUserX(int userSetting, int iFlags, int iloc) throws Exception {
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        int origSetting = getDefaultInstallLoc();
-        try {
-            // Set user setting
-            setInstallLoc(userSetting);
-            // Replace now
-            installFromRawResource("install.apk", R.raw.install,
-                    rFlags,
-                    true,
-                    false, -1,
-                    iloc);
-        } finally {
-            setInstallLoc(origSetting);
-        }
-    }
-    @LargeTest
-    public void testExistingIUserI() throws Exception {
-        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
-    public void testExistingIUserE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
-    public void testExistingIUserA() throws Exception {
-        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    /*
-     * The following set of tests verify that the user setting defines
-     * the install location.
-     *
-     */
-    private boolean getUserSettingSetInstallLocation() {
-        try {
-            return Settings.Global.getInt(
-                    mContext.getContentResolver(), Settings.Global.SET_INSTALL_LOCATION) != 0;
-        } catch (SettingNotFoundException e1) {
-        }
-        return false;
-    }
-
-    private void setUserSettingSetInstallLocation(boolean value) {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.SET_INSTALL_LOCATION, value ? 1 : 0);
-    }
-
-    private void setUserX(boolean enable, int userSetting, int iloc) throws Exception {
-        boolean origUserSetting = getUserSettingSetInstallLocation();
-        int origSetting = getDefaultInstallLoc();
-        try {
-            setUserSettingSetInstallLocation(enable);
-            // Set user setting
-            setInstallLoc(userSetting);
-            // Replace now
-            installFromRawResource("install.apk", R.raw.install,
-                    0,
-                    true,
-                    false, -1,
-                    iloc);
-        } finally {
-            // Restore original setting
-            setUserSettingSetInstallLocation(origUserSetting);
-            setInstallLoc(origSetting);
-        }
-    }
-    @LargeTest
-    public void testUserI() throws Exception {
-        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-        int iloc = getExpectedInstallLocation(userSetting);
-        setUserX(true, userSetting, iloc);
-    }
-
-    @LargeTest
-    public void testUserE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-        int iloc = getExpectedInstallLocation(userSetting);
-        setUserX(true, userSetting, iloc);
-    }
-
-    @LargeTest
-    public void testUserA() throws Exception {
-        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-        int iloc = getExpectedInstallLocation(userSetting);
-        setUserX(true, userSetting, iloc);
-    }
-
-    /*
-     * The following set of tests turn on/off the basic
-     * user setting for turning on install location.
-     */
-    @LargeTest
-    public void testUserPrefOffUserI() throws Exception {
-        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
-        setUserX(false, userSetting, iloc);
-    }
-
-    @LargeTest
-    public void testUserPrefOffUserE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
-        setUserX(false, userSetting, iloc);
-    }
-
-    @LargeTest
-    public void testUserPrefOffA() throws Exception {
-        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
-        setUserX(false, userSetting, iloc);
-    }
-
-    static final String BASE_PERMISSIONS_DEFINED[] = new String[] {
-        PERM_PACKAGE, "com.android.unit_tests.install_decl_perm",
-        PERM_DEFINED,
-        "com.android.frameworks.coretests.NORMAL",
-        "com.android.frameworks.coretests.DANGEROUS",
-        "com.android.frameworks.coretests.SIGNATURE",
-    };
-
-    static final String BASE_PERMISSIONS_UNDEFINED[] = new String[] {
-        PERM_PACKAGE, "com.android.frameworks.coretests.install_decl_perm",
-        PERM_UNDEFINED,
-        "com.android.frameworks.coretests.NORMAL",
-        "com.android.frameworks.coretests.DANGEROUS",
-        "com.android.frameworks.coretests.SIGNATURE",
-    };
-
-    static final String BASE_PERMISSIONS_USED[] = new String[] {
-        PERM_PACKAGE, "com.android.frameworks.coretests.install_use_perm_good",
-        PERM_USED,
-        "com.android.frameworks.coretests.NORMAL",
-        "com.android.frameworks.coretests.DANGEROUS",
-        "com.android.frameworks.coretests.SIGNATURE",
-    };
-
-    static final String BASE_PERMISSIONS_NOTUSED[] = new String[] {
-        PERM_PACKAGE, "com.android.frameworks.coretests.install_use_perm_good",
-        PERM_NOTUSED,
-        "com.android.frameworks.coretests.NORMAL",
-        "com.android.frameworks.coretests.DANGEROUS",
-        "com.android.frameworks.coretests.SIGNATURE",
-    };
-
-    static final String BASE_PERMISSIONS_SIGUSED[] = new String[] {
-        PERM_PACKAGE, "com.android.frameworks.coretests.install_use_perm_good",
-        PERM_USED,
-        "com.android.frameworks.coretests.SIGNATURE",
-        PERM_NOTUSED,
-        "com.android.frameworks.coretests.NORMAL",
-        "com.android.frameworks.coretests.DANGEROUS",
-    };
-
-    /*
-     * Ensure that permissions are properly declared.
-     */
-    @LargeTest
-    public void testInstallDeclaresPermissions() throws Exception {
-        InstallParams ip = null;
-        InstallParams ip2 = null;
-        try {
-            // **: Upon installing a package, are its declared permissions published?
-
-            int iFlags = PackageManager.INSTALL_INTERNAL;
-            int iApk = R.raw.install_decl_perm;
-            ip = installFromRawResource("install.apk", iApk,
-                    iFlags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-            assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
-            assertPermissions(BASE_PERMISSIONS_DEFINED);
-
-            // **: Upon installing package, are its permissions granted?
-
-            int i2Flags = PackageManager.INSTALL_INTERNAL;
-            int i2Apk = R.raw.install_use_perm_good;
-            ip2 = installFromRawResource("install2.apk", i2Apk,
-                    i2Flags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-            assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
-            assertPermissions(BASE_PERMISSIONS_USED);
-
-            // **: Upon removing but not deleting, are permissions retained?
-
-            GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
-
-            try {
-                invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
-            } catch (Exception e) {
-                failStr(e);
-            }
-            assertPermissions(BASE_PERMISSIONS_DEFINED);
-            assertPermissions(BASE_PERMISSIONS_USED);
-
-            // **: Upon re-installing, are permissions retained?
-
-            ip = installFromRawResource("install.apk", iApk,
-                    iFlags | PackageManager.INSTALL_REPLACE_EXISTING, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-            assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
-            assertPermissions(BASE_PERMISSIONS_DEFINED);
-            assertPermissions(BASE_PERMISSIONS_USED);
-
-            // **: Upon deleting package, are all permissions removed?
-
-            try {
-                invokeDeletePackage(ip.pkg.getPackageName(), 0, receiver);
-                ip = null;
-            } catch (Exception e) {
-                failStr(e);
-            }
-            assertPermissions(BASE_PERMISSIONS_UNDEFINED);
-            assertPermissions(BASE_PERMISSIONS_NOTUSED);
-
-            // **: Delete package using permissions; nothing to check here.
-
-            GenericReceiver receiver2 = new DeleteReceiver(ip2.pkg.getPackageName());
-            try {
-                invokeDeletePackage(ip2.pkg.getPackageName(), 0, receiver);
-                ip2 = null;
-            } catch (Exception e) {
-                failStr(e);
-            }
-
-            // **: Re-install package using permissions; no permissions can be granted.
-
-            ip2 = installFromRawResource("install2.apk", i2Apk,
-                    i2Flags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-            assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
-            assertPermissions(BASE_PERMISSIONS_NOTUSED);
-
-            // **: Upon installing declaring package, are sig permissions granted
-            // to other apps (but not other perms)?
-
-            ip = installFromRawResource("install.apk", iApk,
-                    iFlags, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-            assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
-            assertPermissions(BASE_PERMISSIONS_DEFINED);
-            assertPermissions(BASE_PERMISSIONS_SIGUSED);
-
-            // **: Re-install package using permissions; are all permissions granted?
-
-            ip2 = installFromRawResource("install2.apk", i2Apk,
-                    i2Flags | PackageManager.INSTALL_REPLACE_EXISTING, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-            assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
-            assertPermissions(BASE_PERMISSIONS_NOTUSED);
-
-            // **: Upon deleting package, are all permissions removed?
-
-            try {
-                invokeDeletePackage(ip.pkg.getPackageName(), 0, receiver);
-                ip = null;
-            } catch (Exception e) {
-                failStr(e);
-            }
-            assertPermissions(BASE_PERMISSIONS_UNDEFINED);
-            assertPermissions(BASE_PERMISSIONS_NOTUSED);
-
-            // **: Delete package using permissions; nothing to check here.
-
-            try {
-                invokeDeletePackage(ip2.pkg.getPackageName(), 0, receiver);
-                ip2 = null;
-            } catch (Exception e) {
-                failStr(e);
-            }
-
-        } finally {
-            if (ip2 != null) {
-                cleanUpInstall(ip2);
-            }
-            if (ip != null) {
-                cleanUpInstall(ip);
-            }
-        }
-    }
-
-    /*
-     * The following series of tests are related to upgrading apps with
-     * different certificates.
-     */
-    private static final int APP1_UNSIGNED = R.raw.install_app1_unsigned;
-
-    private static final int APP1_CERT1 = R.raw.install_app1_cert1;
-
-    private static final int APP1_CERT2 = R.raw.install_app1_cert2;
-
-    private static final int APP1_CERT1_CERT2 = R.raw.install_app1_cert1_cert2;
-
-    private static final int APP1_CERT3_CERT4 = R.raw.install_app1_cert3_cert4;
-
-    private static final int APP1_CERT3 = R.raw.install_app1_cert3;
-
-    private static final int APP1_CERT5 = R.raw.install_app1_cert5;
-
-    private static final int APP1_CERT5_ROTATED_CERT6 = R.raw.install_app1_cert5_rotated_cert6;
-
-    private static final int APP1_CERT6 = R.raw.install_app1_cert6;
-
-    private static final int APP2_UNSIGNED = R.raw.install_app2_unsigned;
-
-    private static final int APP2_CERT1 = R.raw.install_app2_cert1;
-
-    private static final int APP2_CERT2 = R.raw.install_app2_cert2;
-
-    private static final int APP2_CERT1_CERT2 = R.raw.install_app2_cert1_cert2;
-
-    private static final int APP2_CERT3 = R.raw.install_app2_cert3;
-
-    private static final int APP2_CERT5_ROTATED_CERT6 = R.raw.install_app2_cert5_rotated_cert6;
-
-    private InstallParams replaceCerts(int apk1, int apk2, boolean cleanUp, boolean fail,
-            int retCode) throws Exception {
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        String apk1Name = "install1.apk";
-        String apk2Name = "install2.apk";
-        ParsingPackage pkg1 = getParsedPackage(apk1Name, apk1);
-        try {
-            InstallParams ip = installFromRawResource(apk1Name, apk1, 0, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            installFromRawResource(apk2Name, apk2, rFlags, false,
-                    fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            return ip;
-        } catch (Exception e) {
-            failStr(e.getMessage());
-        } finally {
-            if (cleanUp) {
-                cleanUpInstall(pkg1.getPackageName());
-            }
-        }
-        return null;
-    }
-
-    /*
-     * Test that an app signed with two certificates can be upgraded by the
-     * same app signed with two certificates.
-     */
-    @LargeTest
-    public void testReplaceMatchAllCerts() throws Exception {
-        replaceCerts(APP1_CERT1_CERT2, APP1_CERT1_CERT2, true, false, -1);
-    }
-
-    /*
-     * Test that an app signed with two certificates cannot be upgraded
-     * by an app signed with a different certificate.
-     */
-    @LargeTest
-    public void testReplaceMatchNoCerts1() throws Exception {
-        replaceCerts(APP1_CERT1_CERT2, APP1_CERT3, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Test that an app signed with two certificates cannot be upgraded
-     * by an app signed with a different certificate.
-     */
-    @LargeTest
-    public void testReplaceMatchNoCerts2() throws Exception {
-        replaceCerts(APP1_CERT1_CERT2, APP1_CERT3_CERT4, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Test that an app signed with two certificates cannot be upgraded by
-     * an app signed with a subset of initial certificates.
-     */
-    @LargeTest
-    public void testReplaceMatchSomeCerts1() throws Exception {
-        replaceCerts(APP1_CERT1_CERT2, APP1_CERT1, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Test that an app signed with two certificates cannot be upgraded by
-     * an app signed with the last certificate.
-     */
-    @LargeTest
-    public void testReplaceMatchSomeCerts2() throws Exception {
-        replaceCerts(APP1_CERT1_CERT2, APP1_CERT2, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Test that an app signed with a certificate can be upgraded by app
-     * signed with a superset of certificates.
-     */
-    @LargeTest
-    public void testReplaceMatchMoreCerts() throws Exception {
-        replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Test that an app signed with a certificate can be upgraded by app
-     * signed with a superset of certificates. Then verify that the an app
-     * signed with the original set of certs cannot upgrade the new one.
-     */
-    @LargeTest
-    public void testReplaceMatchMoreCertsReplaceSomeCerts() throws Exception {
-        InstallParams ip = replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, false, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-        try {
-            int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-            installFromRawResource("install.apk", APP1_CERT1, rFlags, false,
-                    false, -1,
-                    PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        } catch (Exception e) {
-            failStr(e.getMessage());
-        } finally {
-            if (ip != null) {
-                cleanUpInstall(ip);
-            }
-        }
-    }
-
-    /**
-     * The following tests are related to testing KeySets-based key rotation
-     */
-    /*
-     * Check if an apk which does not specify an upgrade-keyset may be upgraded
-     * by an apk which does
-     */
-    public void testNoKSToUpgradeKS() throws Exception {
-        replaceCerts(R.raw.keyset_sa_unone, R.raw.keyset_sa_ua, true, false, -1);
-    }
-
-    /*
-     * Check if an apk which does specify an upgrade-keyset may be downgraded to
-     * an apk which does not
-     */
-    public void testUpgradeKSToNoKS() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sa_unone, true, false, -1);
-    }
-
-    /*
-     * Check if an apk signed by a key other than the upgrade keyset can update
-     * an app
-     */
-    public void testUpgradeKSWithWrongKey() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sb_ua, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Check if an apk signed by its signing key, which is not an upgrade key,
-     * can upgrade an app.
-     */
-    public void testUpgradeKSWithWrongSigningKey() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ub, R.raw.keyset_sa_ub, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Check if an apk signed by its upgrade key, which is not its signing key,
-     * can upgrade an app.
-     */
-    public void testUpgradeKSWithUpgradeKey() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ub, R.raw.keyset_sb_ub, true, false, -1);
-    }
-    /*
-     * Check if an apk signed by its upgrade key, which is its signing key, can
-     * upgrade an app.
-     */
-    public void testUpgradeKSWithSigningUpgradeKey() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sa_ua, true, false, -1);
-    }
-
-    /*
-     * Check if an apk signed by multiple keys, one of which is its upgrade key,
-     * can upgrade an app.
-     */
-    public void testMultipleUpgradeKSWithUpgradeKey() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sab_ua, true, false, -1);
-    }
-
-    /*
-     * Check if an apk signed by multiple keys, one of which is its signing key,
-     * but none of which is an upgrade key, can upgrade an app.
-     */
-    public void testMultipleUpgradeKSWithSigningKey() throws Exception {
-        replaceCerts(R.raw.keyset_sau_ub, R.raw.keyset_sa_ua, true, true,
-                PackageInstaller.STATUS_FAILURE_CONFLICT);
-    }
-
-    /*
-     * Check if an apk which defines multiple (two) upgrade keysets is
-     * upgrade-able by either.
-     */
-    public void testUpgradeKSWithMultipleUpgradeKeySets() throws Exception {
-        replaceCerts(R.raw.keyset_sa_ua_ub, R.raw.keyset_sa_ua, true, false, -1);
-        replaceCerts(R.raw.keyset_sa_ua_ub, R.raw.keyset_sb_ub, true, false, -1);
-    }
-
-    /*
-     * Check if an apk's sigs are changed after upgrading with a non-signing
-     * key.
-     *
-     * TODO: consider checking against hard-coded Signatures in the Sig-tests
-     */
-    public void testSigChangeAfterUpgrade() throws Exception {
-        // install original apk and grab sigs
-        installFromRawResource("tmp.apk", R.raw.keyset_sa_ub,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        PackageManager pm = getPm();
-        String pkgName = "com.android.frameworks.coretests.keysets";
-        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should only have one signature, sig A",
-                pi.signatures.length == 1);
-        String sigBefore = pi.signatures[0].toCharsString();
-        // install apk signed by different upgrade KeySet
-        installFromRawResource("tmp2.apk", R.raw.keyset_sb_ub,
-                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should only have one signature, sig B",
-                pi.signatures.length == 1);
-        String sigAfter = pi.signatures[0].toCharsString();
-        assertFalse("Package signatures did not change after upgrade!",
-                sigBefore.equals(sigAfter));
-        cleanUpInstall(pkgName);
-    }
-
-    /*
-     * Check if an apk's sig is the same  after upgrading with a signing
-     * key.
-     */
-    public void testSigSameAfterUpgrade() throws Exception {
-        // install original apk and grab sigs
-        installFromRawResource("tmp.apk", R.raw.keyset_sa_ua,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        PackageManager pm = getPm();
-        String pkgName = "com.android.frameworks.coretests.keysets";
-        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should only have one signature, sig A",
-                pi.signatures.length == 1);
-        String sigBefore = pi.signatures[0].toCharsString();
-        // install apk signed by same upgrade KeySet
-        installFromRawResource("tmp2.apk", R.raw.keyset_sa_ua,
-                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should only have one signature, sig A",
-                pi.signatures.length == 1);
-        String sigAfter = pi.signatures[0].toCharsString();
-        assertTrue("Package signatures changed after upgrade!",
-                sigBefore.equals(sigAfter));
-        cleanUpInstall(pkgName);
-    }
-
-    /*
-     * Check if an apk's sigs are the same after upgrading with an app with
-     * a subset of the original signing keys.
-     */
-    public void testSigRemovedAfterUpgrade() throws Exception {
-        // install original apk and grab sigs
-        installFromRawResource("tmp.apk", R.raw.keyset_sab_ua,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        PackageManager pm = getPm();
-        String pkgName = "com.android.frameworks.coretests.keysets";
-        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should have two signatures, sig A and sig B",
-                pi.signatures.length == 2);
-        Set<String> sigsBefore = new HashSet<String>();
-        for (int i = 0; i < pi.signatures.length; i++) {
-            sigsBefore.add(pi.signatures[i].toCharsString());
-        }
-        // install apk signed subset upgrade KeySet
-        installFromRawResource("tmp2.apk", R.raw.keyset_sa_ua,
-                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should only have one signature, sig A",
-                pi.signatures.length == 1);
-        String sigAfter = pi.signatures[0].toCharsString();
-        assertTrue("Original package signatures did not contain new sig",
-                sigsBefore.contains(sigAfter));
-        cleanUpInstall(pkgName);
-    }
-
-    /*
-     * Check if an apk's sigs are added to after upgrading with an app with
-     * a superset of the original signing keys.
-     */
-    public void testSigAddedAfterUpgrade() throws Exception {
-        // install original apk and grab sigs
-        installFromRawResource("tmp.apk", R.raw.keyset_sa_ua,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        PackageManager pm = getPm();
-        String pkgName = "com.android.frameworks.coretests.keysets";
-        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should only have one signature, sig A",
-                pi.signatures.length == 1);
-        String sigBefore = pi.signatures[0].toCharsString();
-        // install apk signed subset upgrade KeySet
-        installFromRawResource("tmp2.apk", R.raw.keyset_sab_ua,
-                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
-        assertTrue("Package should have two signatures, sig A and sig B",
-                pi.signatures.length == 2);
-        Set<String> sigsAfter = new HashSet<String>();
-        for (int i = 0; i < pi.signatures.length; i++) {
-            sigsAfter.add(pi.signatures[i].toCharsString());
-        }
-        assertTrue("Package signatures did not change after upgrade!",
-                sigsAfter.contains(sigBefore));
-        cleanUpInstall(pkgName);
-    }
-
-    /*
-     * Check if an apk gains signature-level permission after changing to the a
-     * new signature, for which a permission should be granted.
-     */
-    public void testUpgradeSigPermGained() throws Exception {
-        // install apk which defines permission
-        installFromRawResource("permDef.apk", R.raw.keyset_permdef_sa_unone,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        // install apk which uses permission but does not have sig
-        installFromRawResource("permUse.apk", R.raw.keyset_permuse_sb_ua_ub,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        // verify that package does not have perm before
-        PackageManager pm = getPm();
-        String permPkgName = "com.android.frameworks.coretests.keysets_permdef";
-        String pkgName = "com.android.frameworks.coretests.keysets";
-        String permName = "com.android.frameworks.coretests.keysets_permdef.keyset_perm";
-        assertFalse("keyset permission granted to app without same signature!",
-                    pm.checkPermission(permName, pkgName)
-                    == PackageManager.PERMISSION_GRANTED);
-        // upgrade to apk with perm signature
-        installFromRawResource("permUse2.apk", R.raw.keyset_permuse_sa_ua_ub,
-                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        assertTrue("keyset permission not granted to app after upgrade to same sig",
-                    pm.checkPermission(permName, pkgName)
-                    == PackageManager.PERMISSION_GRANTED);
-        cleanUpInstall(permPkgName);
-        cleanUpInstall(pkgName);
-    }
-
-    /*
-     * Check if an apk loses signature-level permission after changing to the a
-     * new signature, from one which a permission should be granted.
-     */
-    public void testUpgradeSigPermLost() throws Exception {
-        // install apk which defines permission
-        installFromRawResource("permDef.apk", R.raw.keyset_permdef_sa_unone,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        // install apk which uses permission, signed by same sig
-        installFromRawResource("permUse.apk", R.raw.keyset_permuse_sa_ua_ub,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        // verify that package does not have perm before
-        PackageManager pm = getPm();
-        String permPkgName = "com.android.frameworks.coretests.keysets_permdef";
-        String pkgName = "com.android.frameworks.coretests.keysets";
-        String permName = "com.android.frameworks.coretests.keysets_permdef.keyset_perm";
-        assertTrue("keyset permission not granted to app with same sig",
-                    pm.checkPermission(permName, pkgName)
-                    == PackageManager.PERMISSION_GRANTED);
-        // upgrade to apk without perm signature
-        installFromRawResource("permUse2.apk", R.raw.keyset_permuse_sb_ua_ub,
-                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-
-        assertFalse("keyset permission not revoked from app which upgraded to a "
-                    + "different signature",
-                    pm.checkPermission(permName, pkgName)
-                    == PackageManager.PERMISSION_GRANTED);
-        cleanUpInstall(permPkgName);
-        cleanUpInstall(pkgName);
-    }
-
-    /**
-     * The following tests are related to testing KeySets-based API
-     */
-
-    /*
-     * testGetSigningKeySetNull - ensure getSigningKeySet() returns null on null
-     * input and when calling a package other than that which made the call.
-     */
-    public void testGetSigningKeySet() throws Exception {
-        PackageManager pm = getPm();
-        String mPkgName = mContext.getPackageName();
-        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
-        KeySet ks;
-        try {
-            ks = pm.getSigningKeySet(null);
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            ks = pm.getSigningKeySet("keysets.test.bogus.package");
-            assertTrue(false); // should have thrown
-        } catch (IllegalArgumentException e) {
-        }
-        final InstallParams ip = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            ks = pm.getSigningKeySet(otherPkgName);
-            assertTrue(false); // should have thrown
-        } catch (SecurityException e) {
-        } finally {
-            cleanUpInstall(ip);
-        }
-        ks = pm.getSigningKeySet(mContext.getPackageName());
-        assertNotNull(ks);
-    }
-
-    /*
-     * testGetKeySetByAlias - same as getSigningKeySet, but for keysets defined
-     * by this package.
-     */
-    public void testGetKeySetByAlias() throws Exception {
-        PackageManager pm = getPm();
-        String mPkgName = mContext.getPackageName();
-        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
-        KeySet ks;
-        try {
-            ks = pm.getKeySetByAlias(null, null);
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            ks = pm.getKeySetByAlias(null, "keysetBogus");
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            ks = pm.getKeySetByAlias("keysets.test.bogus.package", null);
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            ks = pm.getKeySetByAlias("keysets.test.bogus.package", "A");
-            assertTrue(false); // should have thrown
-        } catch(IllegalArgumentException e) {
-        }
-        try {
-            ks = pm.getKeySetByAlias(mPkgName, "keysetBogus");
-            assertTrue(false); // should have thrown
-        } catch(IllegalArgumentException e) {
-        }
-
-        // make sure we can get a KeySet from our pkg
-        ks = pm.getKeySetByAlias(mPkgName, "A");
-        assertNotNull(ks);
-
-        // and another
-        final InstallParams ip = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            ks = pm.getKeySetByAlias(otherPkgName, "A");
-            assertNotNull(ks);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    public void testIsSignedBy() throws Exception {
-        PackageManager pm = getPm();
-        String mPkgName = mContext.getPackageName();
-        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
-        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
-        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
-
-        try {
-            assertFalse(pm.isSignedBy(null, null));
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            assertFalse(pm.isSignedBy(null, mSigningKS));
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            assertFalse(pm.isSignedBy(mPkgName, null));
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            assertFalse(pm.isSignedBy("keysets.test.bogus.package", mDefinedKS));
-        } catch(IllegalArgumentException e) {
-        }
-        assertFalse(pm.isSignedBy(mPkgName, mDefinedKS));
-        assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder())));
-        assertTrue(pm.isSignedBy(mPkgName, mSigningKS));
-
-        final InstallParams ip1 = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS));
-            assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
-        } finally {
-            cleanUpInstall(ip1);
-        }
-
-        final InstallParams ip2 = installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS));
-            assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
-        } finally {
-            cleanUpInstall(ip2);
-        }
-    }
-
-    public void testIsSignedByExactly() throws Exception {
-        PackageManager pm = getPm();
-        String mPkgName = mContext.getPackageName();
-        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
-        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
-        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
-        try {
-            assertFalse(pm.isSignedBy(null, null));
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            assertFalse(pm.isSignedBy(null, mSigningKS));
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            assertFalse(pm.isSignedBy(mPkgName, null));
-            assertTrue(false); // should have thrown
-        } catch (NullPointerException e) {
-        }
-        try {
-            assertFalse(pm.isSignedByExactly("keysets.test.bogus.package", mDefinedKS));
-        } catch(IllegalArgumentException e) {
-        }
-        assertFalse(pm.isSignedByExactly(mPkgName, mDefinedKS));
-        assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder())));
-        assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS));
-
-        final InstallParams ip1 = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
-            assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS));
-        } finally {
-            cleanUpInstall(ip1);
-        }
-
-        final InstallParams ip2 = installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
-                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
-            assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS));
-        } finally {
-            cleanUpInstall(ip2);
-        }
-    }
-
-
-
-    /**
-     * The following tests are related to testing the checkSignatures api.
-     */
-    private void checkSignatures(int apk1, int apk2, int expMatchResult) throws Exception {
-        checkSharedSignatures(apk1, apk2, true, false, -1, expMatchResult);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesAllMatch() throws Exception {
-        int apk1 = APP1_CERT1_CERT2;
-        int apk2 = APP2_CERT1_CERT2;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesNoMatch() throws Exception {
-        int apk1 = APP1_CERT1;
-        int apk2 = APP2_CERT2;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesSomeMatch1() throws Exception {
-        int apk1 = APP1_CERT1_CERT2;
-        int apk2 = APP2_CERT1;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesSomeMatch2() throws Exception {
-        int apk1 = APP1_CERT1_CERT2;
-        int apk2 = APP2_CERT2;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesMoreMatch() throws Exception {
-        int apk1 = APP1_CERT1;
-        int apk2 = APP2_CERT1_CERT2;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesUnknown() throws Exception {
-        int apk1 = APP1_CERT1_CERT2;
-        int apk2 = APP2_CERT1_CERT2;
-        String apk1Name = "install1.apk";
-        String apk2Name = "install2.apk";
-
-        final InstallParams ip = installFromRawResource(apk1Name, apk1, 0, false,
-                false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        try {
-            PackageManager pm = mContext.getPackageManager();
-            // Delete app2
-            File filesDir = mContext.getFilesDir();
-            File outFile = new File(filesDir, apk2Name);
-            int rawResId = apk2;
-            Uri packageURI = getInstallablePackage(rawResId, outFile);
-            ParsingPackage pkg = parsePackage(packageURI);
-            try {
-                getPi().uninstall(pkg.getPackageName(),
-                        PackageManager.DELETE_ALL_USERS,
-                        null /*statusReceiver*/);
-            } catch (IllegalArgumentException ignore) {
-            }
-            // Check signatures now
-            int match = mContext.getPackageManager().checkSignatures(
-                    ip.pkg.getPackageName(), pkg.getPackageName());
-            assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    @LargeTest
-    public void testCheckSignaturesRotatedAgainstOriginal() throws Exception {
-        // checkSignatures should be backwards compatible with pre-rotation behavior; this test
-        // verifies that an app signed with a rotated key results in a signature match with an app
-        // signed with the original key in the lineage.
-        int apk1 = APP1_CERT5;
-        int apk2 = APP2_CERT5_ROTATED_CERT6;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesRotatedAgainstRotated() throws Exception {
-        // checkSignatures should be successful when both apps have been signed with the same
-        // rotated key since the initial signature comparison between the two apps should
-        // return a match.
-        int apk1 = APP1_CERT5_ROTATED_CERT6;
-        int apk2 = APP2_CERT5_ROTATED_CERT6;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
-    }
-
-    @LargeTest
-    public void testInstallNoCertificates() throws Exception {
-        int apk1 = APP1_UNSIGNED;
-        String apk1Name = "install1.apk";
-
-        installFromRawResource(apk1Name, apk1, 0, false,
-                true, PackageInstaller.STATUS_FAILURE_INVALID,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    /*
-     * The following tests are related to apps using shared uids signed with
-     * different certs.
-     */
-    private int SHARED1_UNSIGNED = R.raw.install_shared1_unsigned;
-
-    private int SHARED1_CERT1 = R.raw.install_shared1_cert1;
-
-    private int SHARED1_CERT2 = R.raw.install_shared1_cert2;
-
-    private int SHARED1_CERT1_CERT2 = R.raw.install_shared1_cert1_cert2;
-
-    private int SHARED2_UNSIGNED = R.raw.install_shared2_unsigned;
-
-    private int SHARED2_CERT1 = R.raw.install_shared2_cert1;
-
-    private int SHARED2_CERT2 = R.raw.install_shared2_cert2;
-
-    private int SHARED2_CERT1_CERT2 = R.raw.install_shared2_cert1_cert2;
-
-    private void checkSharedSignatures(int apk1, int apk2, boolean cleanUp, boolean fail,
-            int retCode, int expMatchResult) throws Exception {
-        String apk1Name = "install1.apk";
-        String apk2Name = "install2.apk";
-        ParsingPackage pkg1 = getParsedPackage(apk1Name, apk1);
-        ParsingPackage pkg2 = getParsedPackage(apk2Name, apk2);
-
-        try {
-            // Clean up before testing first.
-            cleanUpInstall(pkg1.getPackageName());
-            cleanUpInstall(pkg2.getPackageName());
-            installFromRawResource(apk1Name, apk1, 0, false, false, -1,
-                    PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            if (fail) {
-                installFromRawResource(apk2Name, apk2, 0, false, true, retCode,
-                        PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            } else {
-                installFromRawResource(apk2Name, apk2, 0, false, false, -1,
-                        PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-                // TODO: All checkSignatures tests should return the same result regardless of
-                // querying by package name or uid; however if there are any edge cases where
-                // individual packages within a shareduid are compared with signatures that do not
-                // match the full lineage of the shareduid this method should be overloaded to
-                // accept the expected response for the uid query.
-                PackageManager pm = getPm();
-                int matchByName = pm.checkSignatures(pkg1.getPackageName(), pkg2.getPackageName());
-                int pkg1Uid = pm.getApplicationInfo(pkg1.getPackageName(), 0).uid;
-                int pkg2Uid = pm.getApplicationInfo(pkg2.getPackageName(), 0).uid;
-                int matchByUid = pm.checkSignatures(pkg1Uid, pkg2Uid);
-                assertEquals(expMatchResult, matchByName);
-                assertEquals(expMatchResult, matchByUid);
-            }
-        } finally {
-            if (cleanUp) {
-                cleanUpInstall(pkg1.getPackageName());
-                cleanUpInstall(pkg2.getPackageName());
-            }
-        }
-    }
-
-    @LargeTest
-    public void testCheckSignaturesSharedAllMatch() throws Exception {
-        int apk1 = SHARED1_CERT1_CERT2;
-        int apk2 = SHARED2_CERT1_CERT2;
-        boolean fail = false;
-        int retCode = -1;
-        int expMatchResult = PackageManager.SIGNATURE_MATCH;
-        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesSharedNoMatch() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT2;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        int expMatchResult = -1;
-        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
-    }
-
-    /*
-     * Test that an app signed with cert1 and cert2 cannot be replaced when
-     * signed with cert1 alone.
-     */
-    @LargeTest
-    public void testCheckSignaturesSharedSomeMatch1() throws Exception {
-        int apk1 = SHARED1_CERT1_CERT2;
-        int apk2 = SHARED2_CERT1;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        int expMatchResult = -1;
-        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
-    }
-
-    /*
-     * Test that an app signed with cert1 and cert2 cannot be replaced when
-     * signed with cert2 alone.
-     */
-    @LargeTest
-    public void testCheckSignaturesSharedSomeMatch2() throws Exception {
-        int apk1 = SHARED1_CERT1_CERT2;
-        int apk2 = SHARED2_CERT2;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        int expMatchResult = -1;
-        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
-    }
-
-    @LargeTest
-    public void testCheckSignaturesSharedUnknown() throws Exception {
-        int apk1 = SHARED1_CERT1_CERT2;
-        int apk2 = SHARED2_CERT1_CERT2;
-        String apk1Name = "install1.apk";
-        String apk2Name = "install2.apk";
-        InstallParams ip1 = null;
-
-        try {
-            ip1 = installFromRawResource(apk1Name, apk1, 0, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-            PackageManager pm = mContext.getPackageManager();
-            // Delete app2
-            ParsingPackage pkg = getParsedPackage(apk2Name, apk2);
-            try {
-                getPi().uninstall(pkg.getPackageName(), PackageManager.DELETE_ALL_USERS,
-                        null /*statusReceiver*/);
-            } catch (IllegalArgumentException ignore) {
-            }
-            // Check signatures now
-            int match = mContext.getPackageManager().checkSignatures(
-                    ip1.pkg.getPackageName(), pkg.getPackageName());
-            assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
-        } finally {
-            if (ip1 != null) {
-                cleanUpInstall(ip1);
-            }
-        }
-    }
-
-    @LargeTest
-    public void testReplaceFirstSharedMatchAllCerts() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT1;
-        int rapk1 = SHARED1_CERT1;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
-        replaceCerts(apk1, rapk1, true, false, -1);
-    }
-
-    @LargeTest
-    public void testReplaceSecondSharedMatchAllCerts() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT1;
-        int rapk2 = SHARED2_CERT1;
-        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
-        replaceCerts(apk2, rapk2, true, false, -1);
-    }
-
-    @LargeTest
-    public void testReplaceFirstSharedMatchSomeCerts() throws Exception {
-        int apk1 = SHARED1_CERT1_CERT2;
-        int apk2 = SHARED2_CERT1_CERT2;
-        int rapk1 = SHARED1_CERT1;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
-        installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true,
-                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testReplaceSecondSharedMatchSomeCerts() throws Exception {
-        int apk1 = SHARED1_CERT1_CERT2;
-        int apk2 = SHARED2_CERT1_CERT2;
-        int rapk2 = SHARED2_CERT1;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
-        installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true,
-                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testReplaceFirstSharedMatchNoCerts() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT1;
-        int rapk1 = SHARED1_CERT2;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
-        installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true,
-                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testReplaceSecondSharedMatchNoCerts() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT1;
-        int rapk2 = SHARED2_CERT2;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
-        installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true,
-                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testReplaceFirstSharedMatchMoreCerts() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT1;
-        int rapk1 = SHARED1_CERT1_CERT2;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
-        installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true,
-                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testReplaceSecondSharedMatchMoreCerts() throws Exception {
-        int apk1 = SHARED1_CERT1;
-        int apk2 = SHARED2_CERT1;
-        int rapk2 = SHARED2_CERT1_CERT2;
-        boolean fail = true;
-        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
-        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
-        installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true,
-                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    /**
-     * Unknown features should be allowed to install. This prevents older phones
-     * from rejecting new packages that specify features that didn't exist when
-     * an older phone existed. All older phones are assumed to have those
-     * features.
-     * <p>
-     * Right now we allow all packages to be installed regardless of their
-     * features.
-     */
-    @LargeTest
-    public void testUsesFeatureUnknownFeature() throws Exception {
-        int retCode = PackageManager.INSTALL_SUCCEEDED;
-        installFromRawResource("install.apk", R.raw.install_uses_feature, 0, true, false, retCode,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    @LargeTest
-    public void testInstallNonexistentFile() throws Exception {
-        int retCode = PackageInstaller.STATUS_FAILURE_INVALID;
-        File invalidFile = new File("/nonexistent-file.apk");
-        invokeInstallPackageFail(Uri.fromFile(invalidFile), 0, retCode);
-    }
-
-    @SmallTest
-    public void testGetVerifierDeviceIdentity() throws Exception {
-        PackageManager pm = getPm();
-        VerifierDeviceIdentity id = pm.getVerifierDeviceIdentity();
-
-        assertNotNull("Verifier device identity should not be null", id);
-    }
-
-    public void testGetInstalledPackages() throws Exception {
-        List<PackageInfo> packages = getPm().getInstalledPackages(0);
-        assertNotNull("installed packages cannot be null", packages);
-        assertTrue("installed packages cannot be empty", packages.size() > 0);
-    }
-
-    public void testGetUnInstalledPackages() throws Exception {
-        List<PackageInfo> packages = getPm().getInstalledPackages(
-                PackageManager.MATCH_UNINSTALLED_PACKAGES);
-        assertNotNull("installed packages cannot be null", packages);
-        assertTrue("installed packages cannot be empty", packages.size() > 0);
-    }
-
-    /**
-     * Test that getInstalledPackages returns all the data specified in flags.
-     */
-    public void testGetInstalledPackagesAll() throws Exception {
-        final int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
-                | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
-                | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
-                | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
-                | PackageManager.GET_SIGNATURES | PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
-        final InstallParams ip =
-                installFromRawResource("install.apk", R.raw.install_complete_package_info,
-                        0 /*flags*/, false /*cleanUp*/, false /*fail*/, -1 /*result*/,
-                        PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-        try {
-            final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
-            assertNotNull("installed packages cannot be null", packages);
-            assertTrue("installed packages cannot be empty", packages.size() > 0);
-
-            PackageInfo packageInfo = null;
-
-            // Find the package with all components specified in the AndroidManifest
-            // to ensure no null values
-            for (PackageInfo pi : packages) {
-                if ("com.android.frameworks.coretests.install_complete_package_info"
-                        .equals(pi.packageName)) {
-                    packageInfo = pi;
-                    break;
-                }
-            }
-            assertNotNull("activities should not be null", packageInfo.activities);
-            assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
-            assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
-            assertNotNull("permissions should not be null", packageInfo.permissions);
-            assertNotNull("providers should not be null", packageInfo.providers);
-            assertNotNull("receivers should not be null", packageInfo.receivers);
-            assertNotNull("services should not be null", packageInfo.services);
-            assertNotNull("signatures should not be null", packageInfo.signatures);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    /**
-     * Test that getInstalledPackages returns all the data specified in
-     * flags when the GET_UNINSTALLED_PACKAGES flag is set.
-     */
-    public void testGetUnInstalledPackagesAll() throws Exception {
-        final int flags = PackageManager.MATCH_UNINSTALLED_PACKAGES
-                | PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
-                | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
-                | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
-                | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
-                | PackageManager.GET_SIGNATURES;
-
-        // first, install the package
-        final InstallParams ip =
-                installFromRawResource("install.apk", R.raw.install_complete_package_info,
-                        0 /*flags*/, false /*cleanUp*/, false /*fail*/, -1 /*result*/,
-                        PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-        try {
-            // then, remove it, keeping it's data around
-            final GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
-            invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
-
-            final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
-            assertNotNull("installed packages cannot be null", packages);
-            assertTrue("installed packages cannot be empty", packages.size() > 0);
-
-            PackageInfo packageInfo = null;
-
-            // Find the package with all components specified in the AndroidManifest
-            // to ensure no null values
-            for (PackageInfo pi : packages) {
-                if ("com.android.frameworks.coretests.install_complete_package_info"
-                        .equals(pi.packageName)) {
-                    packageInfo = pi;
-                    break;
-                }
-            }
-            assertNotNull("activities should not be null", packageInfo.activities);
-            assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
-            assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
-            assertNotNull("permissions should not be null", packageInfo.permissions);
-            assertNotNull("providers should not be null", packageInfo.providers);
-            assertNotNull("receivers should not be null", packageInfo.receivers);
-            assertNotNull("services should not be null", packageInfo.services);
-            assertNotNull("signatures should not be null", packageInfo.signatures);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    @Suppress
-    public void testInstall_BadDex_CleanUp() throws Exception {
-        int retCode = PackageInstaller.STATUS_FAILURE_INVALID;
-        installFromRawResource("install.apk", R.raw.install_bad_dex, 0, true, true, retCode,
-                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-    }
-
-    private static class TestDexModuleRegisterCallback
-            extends PackageManager.DexModuleRegisterCallback {
-        private String mDexModulePath = null;
-        private boolean mSuccess = false;
-        private String mMessage = null;
-        CountDownLatch doneSignal = new CountDownLatch(1);
-
-        @Override
-        public void onDexModuleRegistered(String dexModulePath, boolean success, String message) {
-            mDexModulePath = dexModulePath;
-            mSuccess = success;
-            mMessage = message;
-            doneSignal.countDown();
-        }
-
-        boolean waitTillDone() {
-            long startTime = System.currentTimeMillis();
-            while (System.currentTimeMillis() - startTime < MAX_WAIT_TIME) {
-                try {
-                    return doneSignal.await(MAX_WAIT_TIME, TimeUnit.MILLISECONDS);
-                } catch (InterruptedException e) {
-                    Log.i(TAG, "Interrupted during sleep", e);
-                }
-            }
-            return false;
-        }
-
-    }
-
-    // Verify that the base code path cannot be registered.
-    public void testRegisterDexModuleBaseCode() throws Exception {
-        PackageManager pm = getPm();
-        ApplicationInfo info = getContext().getApplicationInfo();
-        TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
-        pm.registerDexModule(info.getBaseCodePath(), callback);
-        assertTrue(callback.waitTillDone());
-        assertEquals(info.getBaseCodePath(), callback.mDexModulePath);
-        assertFalse("BaseCodePath should not be registered", callback.mSuccess);
-    }
-
-    // Verify that modules which are not own by the calling package are not registered.
-    public void testRegisterDexModuleNotOwningModule() throws Exception {
-        TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
-        String moduleBelongingToOtherPackage = "/data/user/0/com.google.android.gms/module.apk";
-        getPm().registerDexModule(moduleBelongingToOtherPackage, callback);
-        assertTrue(callback.waitTillDone());
-        assertEquals(moduleBelongingToOtherPackage, callback.mDexModulePath);
-        assertTrue(callback.waitTillDone());
-        assertFalse("Only modules belonging to the calling package can be registered",
-                callback.mSuccess);
-    }
-
-    // Verify that modules owned by the package are successfully registered.
-    public void testRegisterDexModuleSuccessfully() throws Exception {
-        ApplicationInfo info = getContext().getApplicationInfo();
-        // Copy the main apk into the data folder and use it as a "module".
-        File dexModuleDir = new File(info.dataDir, "module-dir");
-        File dexModule = new File(dexModuleDir, "module.apk");
-        try {
-            assertNotNull(FileUtils.createDir(
-                    dexModuleDir.getParentFile(), dexModuleDir.getName()));
-            Files.copy(Paths.get(info.getBaseCodePath()), dexModule.toPath(),
-                    StandardCopyOption.REPLACE_EXISTING);
-            TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
-            getPm().registerDexModule(dexModule.toString(), callback);
-            assertTrue(callback.waitTillDone());
-            assertEquals(dexModule.toString(), callback.mDexModulePath);
-            assertTrue(callback.waitTillDone());
-            assertTrue(callback.mMessage, callback.mSuccess);
-
-            // NOTE:
-            // This actually verifies internal behaviour which might change. It's not
-            // ideal but it's the best we can do since there's no other place we can currently
-            // write a better test.
-            for(String isa : getAppDexInstructionSets(info)) {
-                Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.odex"));
-                Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.vdex"));
-            }
-        } finally {
-            FileUtils.deleteContentsAndDir(dexModuleDir);
-        }
-    }
-
-    // If the module does not exist on disk we should get a failure.
-    public void testRegisterDexModuleNotExists() throws Exception {
-        ApplicationInfo info = getContext().getApplicationInfo();
-        String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString();
-        TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
-        getPm().registerDexModule(nonExistentApk, callback);
-        assertTrue(callback.waitTillDone());
-        assertEquals(nonExistentApk, callback.mDexModulePath);
-        assertTrue(callback.waitTillDone());
-        assertFalse("DexModule registration should fail", callback.mSuccess);
-    }
-
-    // If the module does not exist on disk we should get a failure.
-    public void testRegisterDexModuleNotExistsNoCallback() throws Exception {
-        ApplicationInfo info = getContext().getApplicationInfo();
-        String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString();
-        getPm().registerDexModule(nonExistentApk, null);
-    }
-
-    // Copied from com.android.server.pm.InstructionSets because we don't have access to it here.
-    private static String[] getAppDexInstructionSets(ApplicationInfo info) {
-        if (info.primaryCpuAbi != null) {
-            if (info.secondaryCpuAbi != null) {
-                return new String[] {
-                        VMRuntime.getInstructionSet(info.primaryCpuAbi),
-                        VMRuntime.getInstructionSet(info.secondaryCpuAbi) };
-            } else {
-                return new String[] {
-                        VMRuntime.getInstructionSet(info.primaryCpuAbi) };
-            }
-        }
-
-        return new String[] { VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]) };
-    }
-
-    /*---------- Recommended install location tests ----*/
-    /*
-     * TODO's
-     * check version numbers for upgrades
-     * check permissions of installed packages
-     * how to do tests on updated system apps?
-     * verify updates to system apps cannot be installed on the sdcard.
-     */
-}
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index 47b14bb..4f8b8555 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.platform.test.annotations.Presubmit;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.TypedValue;
 
@@ -25,6 +26,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 
+@Presubmit
 public class ConfigurationBoundResourceCacheTest
         extends ActivityInstrumentationTestCase2<ResourceCacheActivity> {
 
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 57f01e9..9aef2ca 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -45,6 +46,7 @@
 /**
  * Tests for {@link FontResourcesParser}.
  */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class FontResourcesParserTest {
diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
index c4df88b..f7f9569 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.ColorStateListDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -34,6 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ResourcesDrawableTest {
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index aa1a534..25c3db5 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -18,6 +18,7 @@
 
 import android.os.FileUtils;
 import android.os.LocaleList;
+import android.platform.test.annotations.Presubmit;
 import android.test.AndroidTestCase;
 import android.util.DisplayMetrics;
 
@@ -30,6 +31,7 @@
 import java.util.Arrays;
 import java.util.Locale;
 
+@Presubmit
 public class ResourcesLocaleTest extends AndroidTestCase {
 
     private String extractApkAndGetPath(int id) throws Exception {
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index e7ee9dc..34a8bde 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -20,6 +20,7 @@
 import android.app.ResourcesManager;
 import android.os.Binder;
 import android.os.LocaleList;
+import android.platform.test.annotations.Postsubmit;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Display;
@@ -32,6 +33,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+@Postsubmit
 public class ResourcesManagerTest extends TestCase {
     private static final int SECONDARY_DISPLAY_ID = 1;
     private static final String APP_ONE_RES_DIR = "app_one.apk";
diff --git a/core/tests/coretests/src/android/content/res/TEST_MAPPING b/core/tests/coretests/src/android/content/res/TEST_MAPPING
new file mode 100644
index 0000000..4ea6e40
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/TEST_MAPPING
@@ -0,0 +1,43 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.content.res."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.content.res."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index a42285e..f2d6ff8 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -2,11 +2,7 @@
 per-file BrightnessLimit.java = [email protected], [email protected]
 
 # Haptics
-per-file CombinedVibrationEffectTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file ExternalVibrationTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file VibrationEffectTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file VibratorInfoTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file VibratorTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file *Vibrat*.java = file:/services/core/java/com/android/server/vibrator/OWNERS
 
 # Power
 per-file PowerManager*.java = [email protected], [email protected]
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index 781564b..10cec82 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -125,8 +125,8 @@
         VibrationEffect.startWaveform()
                 .addStep(/* amplitude= */ 1, /* duration= */ 10)
                 .addRamp(/* amplitude= */ 0, /* duration= */ 20)
-                .addStep(/* amplitude= */ 1, /* frequency*/ 1, /* duration= */ 100)
-                .addRamp(/* amplitude= */ 0.5f, /* frequency*/ -1, /* duration= */ 50)
+                .addStep(/* amplitude= */ 1, /* frequencyHz= */ 1, /* duration= */ 100)
+                .addRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 50)
                 .build()
                 .validate();
 
@@ -150,10 +150,22 @@
                         .addStep(/* amplitude= */ -2, 10).build().validate());
         assertThrows(IllegalArgumentException.class,
                 () -> VibrationEffect.startWaveform()
+                        .addStep(1, /* frequencyHz= */ -1f, 10).build().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveform()
                         .addStep(1, /* duration= */ -1).build().validate());
         assertThrows(IllegalArgumentException.class,
                 () -> VibrationEffect.startWaveform()
-                        .addStep(1, 0, /* duration= */ -1).build().validate());
+                        .addStep(1, 100f, /* duration= */ -1).build().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveform()
+                        .addRamp(/* amplitude= */ -3, 10).build().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveform()
+                        .addRamp(1, /* frequencyHz= */ 0, 10).build().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveform()
+                        .addRamp(1, 10f, /* duration= */ -3).build().validate());
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 6e07fa2..d0e03a2 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.hardware.vibrator.Braking;
@@ -43,19 +44,17 @@
             /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
 
     private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
-            new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
+            new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
     private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
-            new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY,
-                    TEST_RESONANT_FREQUENCY, TEST_FREQUENCY_RESOLUTION,
-                    /* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP);
+            new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
+                    TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
 
     @Test
     public void testHasAmplitudeControl() {
         VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
         assertFalse(noCapabilities.hasAmplitudeControl());
         VibratorInfo composeAndAmplitudeControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
-                        | IVibrator.CAP_AMPLITUDE_CONTROL)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
                 .build();
         assertTrue(composeAndAmplitudeControl.hasAmplitudeControl());
     }
@@ -143,138 +142,95 @@
     }
 
     @Test
-    public void testGetFrequencyRange_invalidFrequencyMappingReturnsEmptyRange() {
+    public void testGetFrequencyRangeHz_invalidFrequencyMappingReturnsNull() {
         // Invalid, contains NaN values or empty array.
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(
-                TEST_VIBRATOR_ID).build().getFrequencyRange());
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyRangeHz());
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        Float.NaN, 150, 25, 50, TEST_AMPLITUDE_MAP))
-                .build().getFrequencyRange());
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                        Float.NaN, 50, 25, TEST_AMPLITUDE_MAP))
+                .build().getFrequencyRangeHz());
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        50, Float.NaN, 25, 50, TEST_AMPLITUDE_MAP))
-                .build().getFrequencyRange());
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                        150, Float.NaN, 25, TEST_AMPLITUDE_MAP))
+                .build().getFrequencyRangeHz());
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        50, 150, Float.NaN, 50, TEST_AMPLITUDE_MAP))
-                .build().getFrequencyRange());
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        50, 150, 25, Float.NaN, TEST_AMPLITUDE_MAP))
-                .build().getFrequencyRange());
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(50, 150, 25, 50, null))
-                .build().getFrequencyRange());
+                        150, 50, Float.NaN, TEST_AMPLITUDE_MAP))
+                .build().getFrequencyRangeHz());
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(150, 50, 25, null))
+                .build().getFrequencyRangeHz());
         // Invalid, minFrequency > resonantFrequency
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        /* minFrequencyHz= */ 250, /* resonantFrequency= */ 150, 25, 50, null))
-                .build().getFrequencyRange());
+                        /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, null))
+                .build().getFrequencyRangeHz());
         // Invalid, maxFrequency < resonantFrequency by changing resolution.
-        assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+        assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        50, 150, /* frequencyResolutionHz= */10, 50, null))
-                .build().getFrequencyRange());
+                        150, 50, /* frequencyResolutionHz= */ 10, null))
+                .build().getFrequencyRangeHz());
     }
 
     @Test
-    public void testGetFrequencyRange_safeRangeLimitedByMaxFrequency() {
+    public void testGetFrequencyRangeHz_resultRangeDerivedFromHalMapping() {
         VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        /* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
-                        /* frequencyResolutionHz= */ 25, /* suggestedSafeRangeHz= */ 200,
-                        TEST_AMPLITUDE_MAP))
+                        /* resonantFrequencyHz= */ 150,
+                        /* minFrequencyHz= */ 50,
+                        /* frequencyResolutionHz= */ 25,
+                        new float[]{
+                                /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
+                                /* 200Hz= */ 0.8f}))
                 .build();
 
-        // Mapping should range from 50Hz = -2 to 200Hz = 1
-        // Safe range [-1, 1] = [100Hz, 200Hz] defined by max - resonant = 50Hz
-        assertEquals(Range.create(-2f, 1f), info.getFrequencyRange());
+        assertEquals(Range.create(50f, 200f), info.getFrequencyRangeHz());
     }
 
     @Test
-    public void testGetFrequencyRange_safeRangeLimitedByMinFrequency() {
-        VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        /* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
-                        /* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 200,
-                        TEST_AMPLITUDE_MAP))
-                .build();
-
-        // Mapping should range from 50Hz = -1 to 350Hz = 2
-        // Safe range [-1, 1] = [50Hz, 250Hz] defined by resonant - min = 100Hz
-        assertEquals(Range.create(-1f, 2f), info.getFrequencyRange());
-    }
-
-    @Test
-    public void testGetFrequencyRange_validMappingReturnsFullRelativeRange() {
-        VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
-                        /* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
-                        /* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 100,
-                        TEST_AMPLITUDE_MAP))
-                .build();
-
-        // Mapping should range from 50Hz = -2 to 350Hz = 4
-        // Safe range [-1, 1] = [100Hz, 200Hz] defined by suggested safe range 100Hz
-        assertEquals(Range.create(-2f, 4f), info.getFrequencyRange());
-    }
-
-    @Test
-    public void testAbsoluteFrequency_emptyMappingReturnsNaN() {
+    public void testGetMaxAmplitude_emptyMappingReturnsAlwaysZero() {
         VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
-        assertTrue(Float.isNaN(info.getAbsoluteFrequency(-1)));
-        assertTrue(Float.isNaN(info.getAbsoluteFrequency(0)));
-        assertTrue(Float.isNaN(info.getAbsoluteFrequency(1)));
-    }
+        assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
+        assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
+        assertEquals(0f, info.getMaxAmplitude(200f), TEST_TOLERANCE);
 
-    @Test
-    public void testAbsoluteFrequency_validRangeReturnsOriginalValue() {
-        VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).setFrequencyMapping(
-                TEST_FREQUENCY_MAPPING).build();
-        assertEquals(TEST_RESONANT_FREQUENCY, info.getAbsoluteFrequency(0), TEST_TOLERANCE);
+        info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+                        /* resonantFrequencyHz= */ 150,
+                        /* minFrequencyHz= */ Float.NaN,
+                        /* frequencyResolutionHz= */ Float.NaN,
+                        null))
+                .build();
 
-        // Safe range [-1, 1] = [125Hz, 175Hz] defined by suggested safe range 100Hz
-        assertEquals(125, info.getAbsoluteFrequency(-1), TEST_TOLERANCE);
-        assertEquals(175, info.getAbsoluteFrequency(1), TEST_TOLERANCE);
-        assertEquals(155, info.getAbsoluteFrequency(0.2f), TEST_TOLERANCE);
-        assertEquals(140, info.getAbsoluteFrequency(-0.4f), TEST_TOLERANCE);
-
-        // Full range [-4, 2] = [50Hz, 200Hz] defined by min frequency and amplitude mapping size
-        assertEquals(50, info.getAbsoluteFrequency(info.getFrequencyRange().getLower()),
-                TEST_TOLERANCE);
-        assertEquals(200, info.getAbsoluteFrequency(info.getFrequencyRange().getUpper()),
-                TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testGetMaxAmplitude_emptyMappingReturnsOnlyResonantFrequency() {
-        VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
-        assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE);
-        assertEquals(0f, info.getMaxAmplitude(0.1f), TEST_TOLERANCE);
-        assertEquals(0f, info.getMaxAmplitude(-1), TEST_TOLERANCE);
+        assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
+        assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
+        assertEquals(0f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
     }
 
     @Test
     public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
         VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(/* minFrequencyHz= */ 50,
-                        /* resonantFrequencyHz= */ 150, /* frequencyResolutionHz= */ 25,
-                        /* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP))
+                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+                        /* resonantFrequencyHz= */ 150,
+                        /* minFrequencyHz= */ 50,
+                        /* frequencyResolutionHz= */ 25,
+                        new float[]{
+                                /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
+                                /* 200Hz= */ 0.8f}))
                 .build();
 
-        assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE); // 150Hz
-        assertEquals(0.9f, info.getMaxAmplitude(1), TEST_TOLERANCE); // 175Hz
-        assertEquals(0.8f, info.getMaxAmplitude(-1), TEST_TOLERANCE); // 125Hz
-        assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRange().getUpper()),
+        assertEquals(1f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
+        assertEquals(0.9f, info.getMaxAmplitude(175f), TEST_TOLERANCE);
+        assertEquals(0.8f, info.getMaxAmplitude(125f), TEST_TOLERANCE);
+        assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRangeHz().getUpper()),
                 TEST_TOLERANCE); // 200Hz
-        assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRange().getLower()),
+        assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRangeHz().getLower()),
                 TEST_TOLERANCE); // 50Hz
 
-        // Rounds 145Hz to the max amplitude for 125Hz, which is lower.
-        assertEquals(0.8f, info.getMaxAmplitude(-0.1f), TEST_TOLERANCE); // 145Hz
-        // Rounds 185Hz to the max amplitude for 200Hz, which is lower.
-        assertEquals(0.8f, info.getMaxAmplitude(1.2f), TEST_TOLERANCE); // 185Hz
+        // 145Hz maps to the max amplitude for 125Hz, which is lower.
+        assertEquals(0.8f, info.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
+        // 185Hz maps to the max amplitude for 200Hz, which is lower.
+        assertEquals(0.8f, info.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
     }
 
     @Test
@@ -317,9 +273,11 @@
         assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
 
         VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
-                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY + 10,
-                        TEST_RESONANT_FREQUENCY + 20, TEST_FREQUENCY_RESOLUTION + 5,
-                        /* suggestedSafeRangeHz= */ 100, TEST_AMPLITUDE_MAP))
+                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+                        TEST_RESONANT_FREQUENCY + 20,
+                        TEST_MIN_FREQUENCY + 10,
+                        TEST_FREQUENCY_RESOLUTION + 5,
+                        TEST_AMPLITUDE_MAP))
                 .build();
         assertNotEquals(complete, completeWithDifferentFrequencyMapping);
 
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index bdd76a5..981086d 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -27,14 +27,22 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
 import android.hardware.vibrator.IVibrator;
 import android.media.AudioAttributes;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -50,11 +58,19 @@
 @RunWith(MockitoJUnitRunner.class)
 public class VibratorTest {
 
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
+    private Context mContextSpy;
     private Vibrator mVibratorSpy;
 
     @Before
     public void setUp() {
-        mVibratorSpy = spy(InstrumentationRegistry.getContext().getSystemService(Vibrator.class));
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+
+        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+        mVibratorSpy = spy(new SystemVibrator(mContextSpy));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index 7ccbb01..e6660f3 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -170,7 +170,7 @@
         when(mFile.getUsableSpace()).thenReturn(10000L);
         when(mFile.getTotalSpace()).thenReturn(100000L);
         long result = mSm.getStorageCacheBytes(mFile, 0);
-        assertThat(result).isEqualTo(4666L);
+        assertThat(result).isEqualTo(4667L);
     }
 
     /**
diff --git a/core/tests/coretests/src/android/os/vibrator/OWNERS b/core/tests/coretests/src/android/os/vibrator/OWNERS
new file mode 100644
index 0000000..b54d6bf
--- /dev/null
+++ b/core/tests/coretests/src/android/os/vibrator/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
index 5f80d2a..3291b2d 100644
--- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
@@ -39,19 +39,19 @@
     @Test
     public void testCreation() {
         RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
-                /* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100);
+                /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 100);
 
         assertEquals(100L, ramp.getDuration());
         assertTrue(ramp.hasNonZeroAmplitude());
         assertEquals(1f, ramp.getStartAmplitude());
         assertEquals(0f, ramp.getEndAmplitude());
-        assertEquals(-1f, ramp.getStartFrequency());
-        assertEquals(1f, ramp.getEndFrequency());
+        assertEquals(100f, ramp.getStartFrequencyHz());
+        assertEquals(200f, ramp.getEndFrequencyHz());
     }
 
     @Test
     public void testSerialization() {
-        RampSegment original = new RampSegment(0, 1, 0, 0.5f, 10);
+        RampSegment original = new RampSegment(0, 1, 10, 20.5f, 10);
         Parcel parcel = Parcel.obtain();
         original.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
@@ -61,7 +61,9 @@
     @Test
     public void testValidate() {
         new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
-                /* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100).validate();
+                /* startFrequencyHz= */ 2, /* endFrequencyHz= */ 1, /* duration= */ 100).validate();
+        // Zero frequency is still used internally for unset frequency.
+        new RampSegment(0, 0, 0, 0, 0).validate();
 
         assertThrows(IllegalArgumentException.class,
                 () -> new RampSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate());
@@ -70,7 +72,15 @@
         assertThrows(IllegalArgumentException.class,
                 () -> new RampSegment(0, /* endAmplitude= */ 2, 0, 0, 0).validate());
         assertThrows(IllegalArgumentException.class,
+                () -> new RampSegment(0, 0, /* startFrequencyHz= */ -1, 0, 0).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> new RampSegment(0, 0, 0, /* endFrequencyHz= */ -3, 0).validate());
+        assertThrows(IllegalArgumentException.class,
                 () -> new RampSegment(0, 0, 0, 0, /* duration= */ -1).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> new RampSegment(/* startAmplitude= */ Float.NaN, 0, 0, 0, 0).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> new RampSegment(0, 0, /* startFrequencyHz= */ Float.NaN, 0, 0).validate());
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
index fdce86a..4424127 100644
--- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
@@ -38,13 +38,13 @@
 
     @Test
     public void testCreation() {
-        StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequency= */ -1f,
+        StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f,
                 /* duration= */ 100);
 
         assertEquals(100, step.getDuration());
         assertTrue(step.hasNonZeroAmplitude());
         assertEquals(1f, step.getAmplitude());
-        assertEquals(-1f, step.getFrequency());
+        assertEquals(1f, step.getFrequencyHz());
     }
 
     @Test
@@ -58,14 +58,22 @@
 
     @Test
     public void testValidate() {
-        new StepSegment(/* amplitude= */ 0f, /* frequency= */ -1f, /* duration= */ 100).validate();
+        new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 10f, /* duration= */ 10).validate();
+        // Zero frequency is still used internally for unset frequency.
+        new StepSegment(0, 0, 0).validate();
 
         assertThrows(IllegalArgumentException.class,
                 () -> new StepSegment(/* amplitude= */ -2, 1f, 10).validate());
         assertThrows(IllegalArgumentException.class,
                 () -> new StepSegment(/* amplitude= */ 2, 1f, 10).validate());
         assertThrows(IllegalArgumentException.class,
+                () -> new StepSegment(1, /* frequencyHz*/ -1f, 10).validate());
+        assertThrows(IllegalArgumentException.class,
                 () -> new StepSegment(2, 1f, /* duration= */ -1).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> new StepSegment(/* amplitude= */ Float.NaN, 1f, 10).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> new StepSegment(1, /* frequencyHz*/ Float.NaN, 10).validate());
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index d6a7682..045e746 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -136,8 +136,8 @@
         MeasuredParagraph mt = null;
 
         mt = MeasuredParagraph.buildForStaticLayout(
-                PAINT, "XXX", 0, 3, LTR, MeasuredText.Builder.HYPHENATION_MODE_NONE, false,
-                null /* no hint */, null);
+                PAINT, null /* line break config */, "XXX", 0, 3, LTR,
+                MeasuredText.Builder.HYPHENATION_MODE_NONE, false, null /* no hint */, null);
         assertNotNull(mt);
         assertNotNull(mt.getChars());
         assertEquals("XXX", charsToString(mt.getChars()));
@@ -152,8 +152,8 @@
 
         // Recycle it
         MeasuredParagraph mt2 = MeasuredParagraph.buildForStaticLayout(
-                PAINT, "_VVV_", 1, 4, RTL, MeasuredText.Builder.HYPHENATION_MODE_NONE, false,
-                null /* no hint */, mt);
+                PAINT, null /* line break config */, "_VVV_", 1, 4, RTL,
+                MeasuredText.Builder.HYPHENATION_MODE_NONE, false, null /* no hint */, mt);
         assertEquals(mt2, mt);
         assertNotNull(mt2.getChars());
         assertEquals("VVV", charsToString(mt.getChars()));
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 90ce305..412d6ec 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -48,7 +48,7 @@
         final TextLine tl = TextLine.obtain();
         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 */);
+                0, 0 /* no ellipsis */, false /* useFallbackLinespace */);
         final float originalWidth = tl.metrics(null);
         final float expandedWidth = 2 * originalWidth;
 
@@ -105,7 +105,7 @@
         tl.set(paint, str, 0, str.length(),
                 TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(str, 0, str.length()) ? -1 : 1,
                 layout.getLineDirections(0), tabStops != null, tabStops,
-                0, 0 /* no ellipsis */);
+                0, 0 /* no ellipsis */, false /* useFallbackLineSpacing */);
         return tl;
     }
 
@@ -276,7 +276,8 @@
 
         final TextLine tl = TextLine.obtain();
         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
-                false /* hasTabs */, null /* tabStops */, 9, 12);
+                false /* hasTabs */, null /* tabStops */, 9, 12,
+                false /* useFallbackLineSpacing */);
         tl.measure(text.length(), false /* trailing */, null /* fmi */);
 
         assertFalse(span.mIsUsed);
@@ -292,7 +293,8 @@
 
         final TextLine tl = TextLine.obtain();
         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
-                false /* hasTabs */, null /* tabStops */, 9, 12);
+                false /* hasTabs */, null /* tabStops */, 9, 12,
+                false /* useFallbackLineSpacing */);
         tl.measure(text.length(), false /* trailing */, null /* fmi */);
 
         assertTrue(span.mIsUsed);
@@ -308,7 +310,8 @@
 
         final TextLine tl = TextLine.obtain();
         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
-                false /* hasTabs */, null /* tabStops */, 9, 12);
+                false /* hasTabs */, null /* tabStops */, 9, 12,
+                false /* useFallbackLineSpacing */);
         tl.measure(text.length(), false /* trailing */, null /* fmi */);
         assertTrue(span.mIsUsed);
     }
diff --git a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
index 2dd3f69..ba9c8d9 100644
--- a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
@@ -64,12 +64,12 @@
     }
 
     @Test
-    public void testAdd() {
+    public void testIncrementValue() {
         final SparseDoubleArray sda = new SparseDoubleArray();
 
         sda.put(4, 6.1);
-        sda.add(4, -1.2);
-        sda.add(2, -1.2);
+        sda.incrementValue(4, -1.2);
+        sda.incrementValue(2, -1.2);
 
         assertEquals(6.1 - 1.2, sda.get(4), PRECISION);
         assertEquals(-1.2, sda.get(2), PRECISION);
diff --git a/core/tests/coretests/src/android/util/SparseLongArrayTest.java b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
index df2d752..b29b6f1 100644
--- a/core/tests/coretests/src/android/util/SparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
@@ -154,4 +154,16 @@
         assertRemoved(startIndex, endIndex);
         assertTrue(isSame(sparseLongArray2, mSparseLongArray));
     }
+
+    @Test
+    public void testIncrementValue() {
+        final SparseLongArray sla = new SparseLongArray();
+
+        sla.put(4, 6);
+        sla.incrementValue(4, 4);
+        sla.incrementValue(2, 5);
+
+        assertEquals(6 + 4, sla.get(4));
+        assertEquals(5, sla.get(2));
+    }
 }
diff --git a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
new file mode 100644
index 0000000..5ea9199
--- /dev/null
+++ b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link HandwritingInitiator}
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:HandwritingInitiatorTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HandwritingInitiatorTest {
+    private static final int TOUCH_SLOP = 8;
+    private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
+    private static final Rect sHwArea = new Rect(100, 200, 500, 500);
+
+    private HandwritingInitiator mHandwritingInitiator;
+    private View mTestView;
+
+    @Before
+    public void setup() {
+        final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        Context context = mInstrumentation.getTargetContext();
+        ViewConfiguration viewConfiguration = mock(ViewConfiguration.class);
+        when(viewConfiguration.getScaledTouchSlop()).thenReturn(TOUCH_SLOP);
+
+        InputMethodManager inputMethodManager = context.getSystemService(InputMethodManager.class);
+        mHandwritingInitiator =
+                spy(new HandwritingInitiator(viewConfiguration, inputMethodManager));
+
+        // mock a parent so that HandwritingInitiator can get
+        ViewGroup parent = new ViewGroup(context) {
+            @Override
+            protected void onLayout(boolean changed, int l, int t, int r, int b) {
+                // We don't layout this view.
+            }
+            @Override
+            public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+                r.set(sHwArea);
+                return true;
+            }
+        };
+
+        mTestView = mock(View.class);
+        when(mTestView.isAttachedToWindow()).thenReturn(true);
+        parent.addView(mTestView);
+    }
+
+    @Test
+    public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        final int x1 = (sHwArea.left + sHwArea.right) / 2;
+        final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // Stylus movement within HandwritingArea should trigger IMM.startHandwriting once.
+        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+    }
+
+    @Test
+    public void onTouchEvent_startHandwritingOnce_when_stylusMoveMultiTimes_withinHWArea() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        final int x1 = (sHwArea.left + sHwArea.right) / 2;
+        final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+
+        final int x3 = x2 + TOUCH_SLOP * 2;
+        final int y3 = y2;
+        MotionEvent stylusEvent3 = createStylusEvent(ACTION_MOVE, x3, y3, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent3);
+
+        MotionEvent stylusEvent4 = createStylusEvent(ACTION_UP, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent4);
+
+        // It only calls startHandwriting once for each ACTION_DOWN.
+        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+    }
+
+    @Test
+    public void onTouchEvent_startHandwriting_inputConnectionBuiltAfterStylusMove() {
+        final int x1 = (sHwArea.left + sHwArea.right) / 2;
+        final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // InputConnection is created after stylus movement.
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+
+        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+    }
+
+    @Test
+    public void onTouchEvent_notStartHandwriting_when_stylusTap_withinHWArea() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        final int x1 = 200;
+        final int y1 = 200;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP / 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_UP, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        verify(mHandwritingInitiator, never()).startHandwriting(mTestView);
+    }
+
+    @Test
+    public void onTouchEvent_notStartHandwriting_when_stylusMove_outOfHWArea() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        final int x1 = 10;
+        final int y1 = 10;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        verify(mHandwritingInitiator, never()).startHandwriting(mTestView);
+    }
+
+    @Test
+    public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTapTimeOut() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        final int x1 = 10;
+        final int y1 = 10;
+        final long time1 = 10L;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+        final long time2 = time1 + TAP_TIMEOUT + 10L;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, time2);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // stylus movement is after TAP_TIMEOUT it shouldn't call startHandwriting.
+        verify(mHandwritingInitiator, never()).startHandwriting(mTestView);
+    }
+
+    @Test
+    public void onInputConnectionCreated_inputConnectionCreated() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
+        assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
+    }
+
+    @Test
+    public void onInputConnectionCreated_inputConnectionClosed() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        mHandwritingInitiator.onInputConnectionClosed(mTestView);
+
+        assertThat(mHandwritingInitiator.mConnectedView).isNull();
+    }
+
+    @Test
+    public void onInputConnectionCreated_inputConnectionRestarted() {
+        // When IMM restarts input connection, View#onInputConnectionCreatedInternal might be
+        // called before View#onInputConnectionClosedInternal. As a result, we need to handle the
+        // case where "one view "2 InputConnections".
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        mHandwritingInitiator.onInputConnectionClosed(mTestView);
+
+        assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
+        assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
+    }
+
+    private MotionEvent createStylusEvent(int action, int x, int y, long eventTime) {
+        MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
+        properties[0].toolType = MotionEvent.TOOL_TYPE_STYLUS;
+
+        MotionEvent.PointerCoords[] coords = MotionEvent.PointerCoords.createArray(1);
+        coords[0].x = x;
+        coords[0].y = y;
+
+        return MotionEvent.obtain(0 /* downTime */, eventTime /* eventTime */, action, 1,
+                properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */,
+                1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+                InputDevice.SOURCE_TOUCHSCREEN, 0 /* flags */);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index c4c983d..78a8f7b 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import static android.view.InputDevice.SOURCE_CLASS_POINTER;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -215,27 +214,4 @@
         rotInvalid.transform(mat);
         assertEquals(-1, rotInvalid.getSurfaceRotation());
     }
-
-    @Test
-    public void testUsesPointerSourceByDefault() {
-        final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
-                ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
-        assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
-    }
-
-    @Test
-    public void testLocationOffsetOnlyAppliedToNonPointerSources() {
-        final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
-                ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
-        event.offsetLocation(40, 50);
-
-        // The offset should be applied since a pointer source is used by default.
-        assertEquals(50, (int) event.getX());
-        assertEquals(70, (int) event.getY());
-
-        // The offset should not be applied if the source is changed to a non-pointer source.
-        event.setSource(InputDevice.SOURCE_JOYSTICK);
-        assertEquals(10, (int) event.getX());
-        assertEquals(20, (int) event.getY());
-    }
 }
diff --git a/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java
deleted file mode 100644
index c15fc3a..0000000
--- a/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class SurfaceControlFpsListenerTest {
-
-    @Test
-    public void registersAndUnregisters() {
-
-        SurfaceControlFpsListener listener = new SurfaceControlFpsListener() {
-            @Override
-            public void onFpsReported(float fps) {
-                // Ignore
-            }
-        };
-
-        listener.register(0);
-
-        listener.unregister();
-    }
-}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 62d0b2e..02e5942 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -120,10 +120,18 @@
         return null;
     }
 
+    public Region getCurrentMagnificationRegion(int displayId) {
+        return null;
+    }
+
     public boolean resetMagnification(int displayId, boolean animate) {
         return false;
     }
 
+    public boolean resetCurrentMagnification(int displayId, boolean animate) {
+        return false;
+    }
+
     public boolean setMagnificationConfig(int displayId,
             @NonNull MagnificationConfig config, boolean animate) {
         return false;
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 9696fdf..4f95cb8 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -28,7 +28,7 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
-import static com.android.internal.widget.FloatingToolbarPopup.MenuItemRepr;
+import static com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.MenuItemRepr;
 
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
@@ -42,7 +42,7 @@
 import androidx.test.espresso.ViewAction;
 import androidx.test.espresso.ViewInteraction;
 
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
diff --git a/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java b/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
new file mode 100644
index 0000000..bf508db
--- /dev/null
+++ b/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class TaskFpsCallbackTest {
+
+    private Context mContext;
+    private WindowManager mWindowManager;
+    private ActivityTaskManager mActivityTaskManager;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+    }
+
+    @Test
+    public void testRegisterAndUnregister() {
+
+        final TaskFpsCallback.OnFpsCallbackListener listener = fps -> {
+            // Ignore
+        };
+        final TaskFpsCallback callback = new TaskFpsCallback(Runnable::run, listener);
+
+        final List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1);
+        assertEquals(tasks.size(), 1);
+        mWindowManager.registerTaskFpsCallback(tasks.get(0).taskId, callback);
+        mWindowManager.unregisterTaskFpsCallback(callback);
+    }
+}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
new file mode 100644
index 0000000..a1a1e20
--- /dev/null
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.OnBackInvokedCallback;
+import android.view.OnBackInvokedDispatcher;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowOnBackInvokedDispatcherTest}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:WindowOnBackInvokedDispatcherTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowOnBackInvokedDispatcherTest {
+    @Mock
+    private IWindowSession mWindowSession;
+    @Mock
+    private IWindow mWindow;
+    private WindowOnBackInvokedDispatcher mDispatcher;
+    @Mock
+    private OnBackInvokedCallback mCallback1;
+    @Mock
+    private OnBackInvokedCallback mCallback2;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mDispatcher = new WindowOnBackInvokedDispatcher();
+        mDispatcher.attachToWindow(mWindowSession, mWindow);
+    }
+
+    private void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
+    public void propagatesTopCallback_samePriority() throws RemoteException {
+        ArgumentCaptor<IOnBackInvokedCallback> captor =
+                ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+        verify(mWindowSession, times(2))
+                .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+        captor.getAllValues().get(0).onBackStarted();
+        waitForIdle();
+        verify(mCallback1).onBackStarted();
+        verifyZeroInteractions(mCallback2);
+
+        captor.getAllValues().get(1).onBackStarted();
+        waitForIdle();
+        verify(mCallback2).onBackStarted();
+        verifyNoMoreInteractions(mCallback1);
+    }
+
+    @Test
+    public void propagatesTopCallback_differentPriority() throws RemoteException {
+        ArgumentCaptor<IOnBackInvokedCallback> captor =
+                ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback1, OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+        verify(mWindowSession)
+                .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+        verifyNoMoreInteractions(mWindowSession);
+        captor.getValue().onBackStarted();
+        waitForIdle();
+        verify(mCallback1).onBackStarted();
+    }
+
+    @Test
+    public void propagatesTopCallback_withRemoval() throws RemoteException {
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+        reset(mWindowSession);
+        mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
+        verifyZeroInteractions(mWindowSession);
+
+        mDispatcher.unregisterOnBackInvokedCallback(mCallback2);
+        verify(mWindowSession).setOnBackInvokedCallback(Mockito.eq(mWindow), isNull());
+    }
+
+
+    @Test
+    public void propagatesTopCallback_sameInstanceAddedTwice() throws RemoteException {
+        ArgumentCaptor<IOnBackInvokedCallback> captor =
+                ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+        mDispatcher.registerOnBackInvokedCallback(mCallback1,
+                OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+        reset(mWindowSession);
+        mDispatcher.registerOnBackInvokedCallback(
+                mCallback2, OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+        verify(mWindowSession)
+                .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+        captor.getValue().onBackStarted();
+        waitForIdle();
+        verify(mCallback2).onBackStarted();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index 43590ba..1f6b57e 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -297,7 +297,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector).showToast(anyInt(), anyInt());
+        verify(sInjector).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -312,7 +312,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -324,7 +324,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -336,7 +336,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -348,7 +348,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -360,7 +360,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -372,7 +372,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector).showToast(anyInt(), anyInt());
+        verify(sInjector).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -386,7 +386,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -399,7 +399,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -412,7 +412,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -425,7 +425,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -438,7 +438,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -452,7 +452,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -466,7 +466,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -480,7 +480,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -494,7 +494,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -507,7 +507,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector).showToast(anyInt(), anyInt());
+        verify(sInjector).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -521,7 +521,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector).showToast(anyInt(), anyInt());
+        verify(sInjector).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -535,7 +535,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector).showToast(anyInt(), anyInt());
+        verify(sInjector).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -551,7 +551,7 @@
         mActivityRule.launchActivity(intent);
 
         verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
-        verify(sInjector, never()).showToast(anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyString(), anyInt());
     }
 
     @Test
@@ -692,6 +692,6 @@
         }
 
         @Override
-        public void showToast(int messageId, int duration) {}
+        public void showToast(String message, int duration) {}
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
index 2c31b08..187803c 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
@@ -20,8 +20,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.when;
 
-import android.content.pm.parsing.ParsingPackageRead;
-import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -80,7 +79,7 @@
         }
 
         public boolean isMatchRequiredSystemProperty() {
-            return ParsingPackageUtils.checkRequiredSystemProperties(
+            return FrameworkParsingPackageUtils.checkRequiredSystemProperties(
                     requiredSystemPropertyName, requiredSystemPropertyValue);
         }
     }
@@ -174,11 +173,12 @@
                 mIteration = Iteration.SYSTEM_SERVER;
                 doAnswer((InvocationOnMock invocation) -> {
                     final Object[] args = invocation.getArguments();
-                    final TriConsumer<ParsingPackageRead, Boolean, File> f =
-                            (TriConsumer<ParsingPackageRead, Boolean, File>) args[0];
+                    final TriConsumer<PackageProvider.Package, Boolean, File> f =
+                            (TriConsumer<PackageProvider.Package, Boolean, File>) args[0];
                     for (Map.Entry<File, TestOverlayInfo> overlay :
                             mTestOverlayInfos.entrySet()) {
-                        final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
+                        final PackageProvider.Package a =
+                                Mockito.mock(PackageProvider.Package.class);
                         final TestOverlayInfo info = overlay.getValue();
                         if ((!TextUtils.isEmpty(info.requiredSystemPropertyName)
                                 || !TextUtils.isEmpty(info.requiredSystemPropertyValue))
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
index aea453e..caec365 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
@@ -24,6 +24,7 @@
 
 import android.os.FileUtils;
 import android.os.SystemProperties;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -46,6 +47,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class OverlayConfigTest {
     private static final String TEST_APK_PACKAGE_NAME =
diff --git a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
new file mode 100644
index 0000000..9aed8be
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.content."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 0d2d047..a409129 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -176,7 +176,6 @@
     private InteractionJankMonitor createMockedInteractionJankMonitor() {
         InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
         doReturn(true).when(monitor).shouldMonitor(anyInt());
-        doNothing().when(monitor).notifyEvents(any(), any(), any());
         return monitor;
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index c1a45c4..be8045d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -22,6 +22,8 @@
 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -35,7 +37,12 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.UidTraffic;
 import android.os.BatteryStats;
+import android.os.BluetoothBatteryStats;
+import android.os.WakeLockStats;
+import android.os.WorkSource;
 import android.util.SparseArray;
 import android.view.Display;
 
@@ -44,12 +51,16 @@
 
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 @SuppressWarnings("GuardedBy")
@@ -61,6 +72,8 @@
     private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader;
     @Mock
     private KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+    @Mock
+    private PowerProfile mPowerProfile;
 
     private final MockClock mMockClock = new MockClock();
     private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -74,6 +87,7 @@
         when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
         when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
         mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
+                .setPowerProfile(mPowerProfile)
                 .setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
                 .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
     }
@@ -507,4 +521,85 @@
         final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
         u.addIsolatedUid(childUid);
     }
+
+    @Test
+    public void testGetWakeLockStats() {
+        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+        // First wakelock, acquired once, not currently held
+        mMockClock.realtime = 1000;
+        mBatteryStatsImpl.noteStartWakeLocked(10100, 100, null, "wakeLock1", null,
+                BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+        mMockClock.realtime = 3000;
+        mBatteryStatsImpl.noteStopWakeLocked(10100, 100, null, "wakeLock1", null,
+                BatteryStats.WAKE_TYPE_PARTIAL);
+
+        // Second wakelock, acquired twice, still held
+        mMockClock.realtime = 4000;
+        mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
+                BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+        mMockClock.realtime = 5000;
+        mBatteryStatsImpl.noteStopWakeLocked(10200, 101, null, "wakeLock2", null,
+                BatteryStats.WAKE_TYPE_PARTIAL);
+
+        mMockClock.realtime = 6000;
+        mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
+                BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+        mMockClock.realtime = 9000;
+
+        List<WakeLockStats.WakeLock> wakeLockStats =
+                mBatteryStatsImpl.getWakeLockStats().getWakeLocks();
+        assertThat(wakeLockStats).hasSize(2);
+
+        WakeLockStats.WakeLock wakeLock1 = wakeLockStats.stream()
+                .filter(wl -> wl.uid == 10100 && wl.name.equals("wakeLock1")).findFirst().get();
+
+        assertThat(wakeLock1.timesAcquired).isEqualTo(1);
+        assertThat(wakeLock1.timeHeldMs).isEqualTo(0);  // Not currently held
+        assertThat(wakeLock1.totalTimeHeldMs).isEqualTo(2000); // 3000-1000
+
+        WakeLockStats.WakeLock wakeLock2 = wakeLockStats.stream()
+                .filter(wl -> wl.uid == 10200 && wl.name.equals("wakeLock2")).findFirst().get();
+
+        assertThat(wakeLock2.timesAcquired).isEqualTo(2);
+        assertThat(wakeLock2.timeHeldMs).isEqualTo(3000);  // 9000-6000
+        assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
+    }
+
+    @Test
+    public void testGetBluetoothBatteryStats() {
+        when(mPowerProfile.getAveragePower(
+                PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)).thenReturn(3.0);
+        mBatteryStatsImpl.setOnBatteryInternal(true);
+        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+        final WorkSource ws = new WorkSource(10042);
+        mBatteryStatsImpl.noteBluetoothScanStartedFromSourceLocked(ws, false, 1000, 1000);
+        mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, false, 5000, 5000);
+        mBatteryStatsImpl.noteBluetoothScanStartedFromSourceLocked(ws, true, 6000, 6000);
+        mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000);
+        mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000);
+
+        BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 9000, 8000, 12000, 0);
+        info.setUidTraffic(ImmutableList.of(
+                new UidTraffic(10042, 3000, 4000),
+                new UidTraffic(10043, 5000, 8000)));
+        mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000);
+
+        BluetoothBatteryStats stats =
+                mBatteryStatsImpl.getBluetoothBatteryStats();
+        assertThat(stats.getUidStats()).hasSize(2);
+
+        final BluetoothBatteryStats.UidStats uidStats =
+                stats.getUidStats().stream().filter(u -> u.uid == 10042).findFirst().get();
+        assertThat(uidStats.scanTimeMs).isEqualTo(7000);  // 4000+3000
+        assertThat(uidStats.unoptimizedScanTimeMs).isEqualTo(3000);
+        assertThat(uidStats.scanResultCount).isEqualTo(42);
+        assertThat(uidStats.rxTimeMs).isEqualTo(7375);  // Some scan time is treated as RX
+        assertThat(uidStats.txTimeMs).isEqualTo(7666);  // Some scan time is treated as TX
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 7db31fb..9b3876f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -39,6 +39,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import libcore.testing.io.TestIoUtils;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,9 +54,12 @@
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final long MINUTE_IN_MS = 60 * 1000;
 
+    private final File mHistoryDir =
+            TestIoUtils.createTemporaryDirectory(getClass().getSimpleName());
     @Rule
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345)
-            .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
+    public final BatteryUsageStatsRule mStatsRule =
+            new BatteryUsageStatsRule(12345, mHistoryDir)
+                    .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
 
     @Test
     public void test_getBatteryUsageStats() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index f75a6df..b3056e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -40,6 +40,7 @@
 import org.junit.runners.model.Statement;
 import org.mockito.stubbing.Answer;
 
+import java.io.File;
 import java.util.Arrays;
 
 public class BatteryUsageStatsRule implements TestRule {
@@ -57,14 +58,18 @@
     private boolean mScreenOn;
 
     public BatteryUsageStatsRule() {
-        this(0);
+        this(0, null);
     }
 
     public BatteryUsageStatsRule(long currentTime) {
+        this(currentTime, null);
+    }
+
+    public BatteryUsageStatsRule(long currentTime, File historyDir) {
         Context context = InstrumentationRegistry.getContext();
         mPowerProfile = spy(new PowerProfile(context, true /* forTest */));
         mMockClock.currentTime = currentTime;
-        mBatteryStats = new MockBatteryStatsImpl(mMockClock);
+        mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
         mBatteryStats.setPowerProfile(mPowerProfile);
         mBatteryStats.onSystemReady();
     }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 69e617a..8cc4c34 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -83,7 +83,7 @@
         final Parcel parcel = Parcel.obtain();
         parcel.writeParcelable(outBatteryUsageStats, 0);
 
-        assertThat(parcel.dataSize()).isLessThan(5500);
+        assertThat(parcel.dataSize()).isLessThan(7000);
 
         parcel.setDataPosition(0);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index d361da9..ed035e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -25,18 +25,21 @@
 import android.os.BatteryStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
+import android.os.UidBatteryConsumer;
+import android.os.WorkSource;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class BluetoothPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -50,6 +53,12 @@
 
     @Test
     public void testTimerBasedModel() {
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        final WorkSource ws = new WorkSource(APP_UID);
+        batteryStats.noteBluetoothScanStartedFromSourceLocked(ws, false, 0, 0);
+        batteryStats.noteBluetoothScanStoppedFromSourceLocked(ws, false, 1000, 1000);
+
         setupBluetoothEnergyInfo(0, BatteryStats.POWER_DATA_UNAVAILABLE);
 
         BluetoothPowerCalculator calculator =
@@ -57,8 +66,81 @@
 
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
 
-        assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
-                BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.06944, 3000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.19444, 9000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+    }
+
+    @Test
+    public void testTimerBasedModel_byProcessState() {
+        mStatsRule.setTime(1000, 1000);
+
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+        BluetoothActivityEnergyInfo info1 = new BluetoothActivityEnergyInfo(2000,
+                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+        info1.setUidTraffic(ImmutableList.of(
+                new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+                new UidTraffic(APP_UID, 3000, 4000)));
+
+        batteryStats.updateBluetoothStateLocked(info1,
+                0/*1_000_000*/, 2000, 2000);
+
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+        BluetoothActivityEnergyInfo info2 = new BluetoothActivityEnergyInfo(4000,
+                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000);
+        info2.setUidTraffic(ImmutableList.of(
+                new UidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
+                new UidTraffic(APP_UID, 7000, 8000)));
+
+        batteryStats.updateBluetoothStateLocked(info2,
+                0 /*5_000_000 */, 4000, 4000);
+
+        BluetoothPowerCalculator calculator =
+                new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+                .powerProfileModeledOnly()
+                .includePowerModels()
+                .includeProcessStateData()
+                .build(), calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+                .isEqualTo(6166);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+                .isWithin(PRECISION).of(0.1226666);
+        assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+        final BatteryConsumer.Key foreground = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key background = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        final BatteryConsumer.Key fgs = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(0.081);
+        assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.0416666);
+        assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
     }
 
     @Test
@@ -71,8 +153,18 @@
         mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
                 calculator);
 
-        assertCalculatedPower(0.08216, 0.18169, 0.30030, 0.26386,
-                BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.30030, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
 
     @Test
@@ -85,11 +177,85 @@
 
         mStatsRule.apply(calculator);
 
-        assertCalculatedPower(0.10378, 0.22950, 0.33333, 0.33329,
-                BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
     }
 
     @Test
+    public void testMeasuredEnergyBasedModel_byProcessState() {
+        mStatsRule.initMeasuredEnergyStatsLocked();
+        mStatsRule.setTime(1000, 1000);
+
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+        BluetoothActivityEnergyInfo info1 = new BluetoothActivityEnergyInfo(2000,
+                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+        info1.setUidTraffic(ImmutableList.of(
+                new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+                new UidTraffic(APP_UID, 3000, 4000)));
+
+        batteryStats.updateBluetoothStateLocked(info1,
+                1_000_000, 2000, 2000);
+
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+        BluetoothActivityEnergyInfo info2 = new BluetoothActivityEnergyInfo(4000,
+                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000);
+        info2.setUidTraffic(ImmutableList.of(
+                new UidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
+                new UidTraffic(APP_UID, 7000, 8000)));
+
+        batteryStats.updateBluetoothStateLocked(info2,
+                5_000_000, 4000, 4000);
+
+        BluetoothPowerCalculator calculator =
+                new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+                .includePowerModels()
+                .includeProcessStateData()
+                .build(), calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+                .isEqualTo(6166);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+                .isWithin(PRECISION).of(0.8220561);
+        assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+        final BatteryConsumer.Key foreground = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key background = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        final BatteryConsumer.Key fgs = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(0.4965352);
+        assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.3255208);
+        assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+    }
+
+
+    @Test
     public void testIgnoreMeasuredEnergyBasedModel() {
         mStatsRule.initMeasuredEnergyStatsLocked();
         setupBluetoothEnergyInfo(4000000, 1200000);
@@ -99,38 +265,31 @@
 
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
 
-        assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
-                BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
 
     private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
         final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
                 BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
                 reportedEnergyUc);
-        info.setUidTraffic(new ArrayList<UidTraffic>(){{
-                add(new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000));
-                add(new UidTraffic(APP_UID, 3000, 4000));
-            }});
+        info.setUidTraffic(ImmutableList.of(
+                new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+                new UidTraffic(APP_UID, 3000, 4000)));
         mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
                 consumedEnergyUc, 1000, 1000);
     }
 
-    private void assertCalculatedPower(double bluetoothUidPowerMah, double appPowerMah,
-            double devicePowerMah, double allAppsPowerMah, int powerModelPowerProfile) {
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
-                bluetoothUidPowerMah, 3583, powerModelPowerProfile);
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getUidBatteryConsumer(APP_UID),
-                appPowerMah, 8416, powerModelPowerProfile);
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getDeviceBatteryConsumer(),
-                devicePowerMah, 12000, powerModelPowerProfile);
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getAppsBatteryConsumer(),
-                allAppsPowerMah, 11999, powerModelPowerProfile);
-    }
-
     private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer,
             double powerMah, int durationMs, @BatteryConsumer.PowerModel int powerModel) {
         assertThat(batteryConsumer).isNotNull();
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 8d9d79d..ce2f764 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
@@ -44,6 +45,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -51,6 +53,8 @@
 public class MobileRadioPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+    @Mock
+    NetworkStatsManager mNetworkStatsManager;
 
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -95,7 +99,8 @@
 
         ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
                 new int[]{100, 200, 300, 400, 500}, 600);
-        stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
+        stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000,
+                mNetworkStatsManager);
 
         mStatsRule.setTime(12_000_000, 12_000_000);
 
@@ -157,7 +162,8 @@
         mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
 
-        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000);
+        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000,
+                mNetworkStatsManager);
 
         uid.setProcessStateForTest(
                 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
@@ -165,7 +171,8 @@
         mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
 
-        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000);
+        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000,
+                mNetworkStatsManager);
 
         assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0);
         // 12000-8000 = 4000 ms == 4_000_000 us
@@ -239,7 +246,7 @@
 
         ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
                 new int[]{100, 200, 300, 400, 500}, 600);
-        stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
+        stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000, mNetworkStatsManager);
 
         mStatsRule.setTime(12_000_000, 12_000_000);
 
@@ -301,7 +308,7 @@
         mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
 
-        stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000);
+        stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager);
 
         uid.setProcessStateForTest(
                 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
@@ -309,7 +316,7 @@
         mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
 
-        stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000);
+        stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager);
 
         mStatsRule.setTime(20000, 20000);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 4faf349..1bb41a8 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -20,6 +20,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkStats;
 import android.os.Handler;
 import android.os.Looper;
@@ -41,6 +43,7 @@
  */
 public class MockBatteryStatsImpl extends BatteryStatsImpl {
     public boolean mForceOnBattery;
+    // The mNetworkStats will be used for both wifi and mobile categories
     private NetworkStats mNetworkStats;
     private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync();
 
@@ -116,10 +119,16 @@
     }
 
     @Override
-    protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
+    protected NetworkStats readMobileNetworkStatsLocked(
+            @NonNull NetworkStatsManager networkStatsManager) {
         return mNetworkStats;
     }
 
+    @Override
+    protected NetworkStats readWifiNetworkStatsLocked(
+            @NonNull NetworkStatsManager networkStatsManager) {
+        return mNetworkStats;
+    }
     public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) {
         mPowerProfile = powerProfile;
         return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 88ee405..bc3b422 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -21,29 +21,49 @@
 import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
 import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
 
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.frameworks.coretests.R;
+import com.android.internal.power.ModemPowerProfile;
+import com.android.internal.util.XmlUtils;
+
 import junit.framework.TestCase;
 
 import org.junit.Before;
 import org.junit.Test;
 
 /*
- * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml
+ * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml and
+ * frameworks/base/core/tests/coretests/res/xml/power_profile_test_modem.xml
+ *
+ * Run with:
+ *     atest com.android.internal.os.PowerProfileTest
  */
 @SmallTest
 public class PowerProfileTest extends TestCase {
 
+    static final String TAG_TEST_MODEM = "test-modem";
+    static final String ATTR_NAME = "name";
+
     private PowerProfile mProfile;
+    private Context mContext;
 
     @Before
     public void setUp() {
-        mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true);
+        mContext = InstrumentationRegistry.getContext();
+        mProfile = new PowerProfile(mContext);
     }
 
     @Test
     public void testPowerProfile() {
+        mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+
         assertEquals(2, mProfile.getNumCpuClusters());
         assertEquals(4, mProfile.getNumCoresInCpuCluster(0));
         assertEquals(4, mProfile.getNumCoresInCpuCluster(1));
@@ -65,6 +85,435 @@
                 mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
         assertEquals(100.0, mProfile.getAveragePower(PowerProfile.POWER_AUDIO));
         assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO));
+
+        assertEquals(123.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP));
+        assertEquals(456.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE));
+        assertEquals(789.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX));
+        assertEquals(10.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 0));
+        assertEquals(20.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 1));
+        assertEquals(30.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 2));
+        assertEquals(40.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 3));
+        assertEquals(50.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 4));
+
+        // Deprecated Modem constants should work with current format.
+        assertEquals(123.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+        assertEquals(456.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+        assertEquals(789.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(10.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(20.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(30.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(40.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(50.0, mProfile.getAverageBatteryDrainMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
     }
 
+    @Test
+    public void testModemPowerProfile_defaultRat() throws Exception {
+        final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+                "testModemPowerProfile_defaultRat");
+        ModemPowerProfile mpp = new ModemPowerProfile();
+        mpp.parseFromXml(parser);
+        assertEquals(10.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+        assertEquals(20.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+
+        // Only default RAT was defined, all other RAT's should fallback to the default value.
+        assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+
+        assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+
+        assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+
+        assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+
+        assertEquals(70.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(70.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(70.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+
+        assertEquals(80.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+        assertEquals(80.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+        assertEquals(80.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+    }
+
+    @Test
+    public void testModemPowerProfile_partiallyDefined() throws Exception {
+        final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+                "testModemPowerProfile_partiallyDefined");
+        ModemPowerProfile mpp = new ModemPowerProfile();
+        mpp.parseFromXml(parser);
+        assertEquals(1.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+        assertEquals(2.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+
+        assertEquals(3.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(4.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(5.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(6.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(7.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(8.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        // LTE RAT power constants were not defined, fallback to defaults
+        assertEquals(3.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(4.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(5.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(6.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(7.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(8.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        // Non-mmwave NR frequency power constants were not defined, fallback to defaults
+        assertEquals(13.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(14.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(15.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(16.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(17.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(18.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(13.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(14.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(15.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(16.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(17.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(18.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(53.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(54.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(55.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(56.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(57.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(58.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+    }
+
+    @Test
+    public void testModemPowerProfile_fullyDefined() throws Exception {
+        final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+                "testModemPowerProfile_fullyDefined");
+        ModemPowerProfile mpp = new ModemPowerProfile();
+        mpp.parseFromXml(parser);
+        assertEquals(1.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+        assertEquals(2.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+
+        // Only default RAT was defined, all other RAT's should fallback to the default value.
+        assertEquals(3.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(4.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(5.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(6.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(7.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(8.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(10.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(20.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(23.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(24.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(25.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(26.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(27.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(28.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(33.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(34.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(35.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(36.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(37.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(38.0, mpp.getAverageBatteryDrainMa(
+                ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+                        | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+                        | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(43.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(44.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(45.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(46.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(47.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(48.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+        assertEquals(53.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+        assertEquals(54.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+        assertEquals(55.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+        assertEquals(56.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+        assertEquals(57.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+        assertEquals(58.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+                | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+                | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+    }
+
+    private XmlResourceParser getTestModemElement(@XmlRes int xmlId, String elementName)
+            throws Exception {
+        final String element = TAG_TEST_MODEM;
+        final Resources resources = mContext.getResources();
+        XmlResourceParser parser = resources.getXml(xmlId);
+        while (true) {
+            XmlUtils.nextElement(parser);
+            final String e = parser.getName();
+            if (e == null) break;
+            if (!e.equals(element)) continue;
+
+            final String name = parser.getAttributeValue(null, ATTR_NAME);
+            if (!name.equals(elementName)) continue;
+
+            return parser;
+        }
+        fail("Unanable to find element " + element + " with name " + elementName);
+        return null;
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index fc44ddc..a368399 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -21,9 +21,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
 import android.os.WorkSource;
@@ -35,14 +38,19 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class WifiPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
+    @Mock
+    NetworkStatsManager mNetworkStatsManager;
+
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
@@ -61,14 +69,18 @@
         batteryStats.noteNetworkInterfaceForTransports("wifi",
                 new int[]{NetworkCapabilities.TRANSPORT_WIFI});
 
-        NetworkStats networkStats = new NetworkStats(10000, 1)
-                .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
-                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
-        mStatsRule.setNetworkStats(networkStats);
+        mStatsRule.setNetworkStats(buildNetworkStats(10000, 1000, 100, 2000, 20));
 
         return batteryStats;
     }
 
+    private NetworkStats buildNetworkStats(long elapsedRealtime, int rxBytes, int rxPackets,
+            int txBytes, int txPackets) {
+        return new NetworkStats(elapsedRealtime, 1)
+                .insertEntry("wifi", APP_UID, 0, 0, rxBytes, rxPackets, txBytes, txPackets, 100)
+                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+    }
+
     /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */
     private WifiActivityEnergyInfo setupPowerControllerBasedModelEnergyNumbersInfo() {
         return new WifiActivityEnergyInfo(10000,
@@ -80,22 +92,26 @@
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
 
-        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000);
+        batteryStats.noteWifiScanStartedLocked(APP_UID, 500, 500);
+        batteryStats.noteWifiScanStoppedLocked(APP_UID, 1500, 1500);
+
+        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 2000, 2000,
+                mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
 
         UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(1423);
+                .isEqualTo(2473);
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isWithin(PRECISION).of(0.2214666);
+                .isWithin(PRECISION).of(0.3964);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
 
         BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(4002);
+                .isEqualTo(4001);
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.86666);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
@@ -109,11 +125,66 @@
     }
 
     @Test
+    public void testPowerControllerBasedModel_powerProfile_byProcessState() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+
+        mStatsRule.setTime(1000, 1000);
+
+        BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(2000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000),
+                POWER_DATA_UNAVAILABLE, 2000, 2000,
+                mNetworkStatsManager);
+
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+        mStatsRule.setNetworkStats(buildNetworkStats(4000, 5000, 200, 7000, 80));
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(4000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000),
+                POWER_DATA_UNAVAILABLE, 4000, 4000,
+                mNetworkStatsManager);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+                .powerProfileModeledOnly()
+                .includePowerModels()
+                .includeProcessStateData()
+                .build(), calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(12423);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(2.0214666);
+        assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+        final BatteryConsumer.Key foreground = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key background = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        final BatteryConsumer.Key fgs = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(1.1214666);
+        assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.9);
+        assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+    }
+
+    @Test
     public void testPowerControllerBasedModel_measured() {
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
 
-        batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000);
+        batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000, mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(calculator);
@@ -142,6 +213,60 @@
                 .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
     }
 
+    @Test
+    public void testPowerControllerBasedModel_measured_byProcessState() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+
+        mStatsRule.setTime(1000, 1000);
+
+        BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(2000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000),
+                1_000_000, 2000, 2000,
+                mNetworkStatsManager);
+
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+        mStatsRule.setNetworkStats(buildNetworkStats(4000, 5000, 200, 7000, 80));
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(4000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000),
+                5_000_000, 4000, 4000,
+                mNetworkStatsManager);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+                .includePowerModels()
+                .includeProcessStateData()
+                .build(), calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(12423);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(1.0325211);
+        assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+        final BatteryConsumer.Key foreground = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key background = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        final BatteryConsumer.Key fgs = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(0.5517519);
+        assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.4807691);
+        assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+    }
+
     /** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */
     private BatteryStatsImpl setupTimerBasedModelTestNumbers() {
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
@@ -160,7 +285,8 @@
 
         // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
         // on the packet counts.
-        batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000);
+        batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000,
+                mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -180,7 +306,8 @@
 
         // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
         // on the packet counts.
-        batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000);
+        batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000,
+                mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(calculator);
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/DeviceFeaturesTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/DeviceFeaturesTest.java
new file mode 100644
index 0000000..875ab38
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/DeviceFeaturesTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link DeviceFeatures} */
+@RunWith(JUnit4.class)
+@SmallTest
+public class DeviceFeaturesTest {
+
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN)
+                .addEqualityGroup(DeviceFeatures.NO_FEATURES_SUPPORTED)
+                .addEqualityGroup(
+                        DeviceFeatures.fromOperand(
+                                new byte[]{(byte) 0b0111_0000}),
+                        DeviceFeatures.fromOperand(
+                                new byte[]{(byte) 0b1111_0000}),
+                        DeviceFeatures.fromOperand(
+                                new byte[]{(byte) 0b1111_0000, (byte) 0b0101_0101}),
+                        DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                                .setRecordTvScreenSupport(FEATURE_SUPPORTED)
+                                .setSetOsdStringSupport(FEATURE_SUPPORTED)
+                                .setDeckControlSupport(FEATURE_SUPPORTED)
+                                .setSetAudioRateSupport(FEATURE_NOT_SUPPORTED)
+                                .setArcTxSupport(FEATURE_NOT_SUPPORTED)
+                                .setArcRxSupport(FEATURE_NOT_SUPPORTED)
+                                .setSetAudioVolumeLevelSupport(FEATURE_NOT_SUPPORTED)
+                                .build()
+                )
+                .testEquals();
+    }
+
+    @Test
+    public void testDeviceFeaturesOperandConversion() {
+        DeviceFeatures info = DeviceFeatures.fromOperand(
+                new byte[]{(byte) 0b0111_0000});
+
+        assertThat(info.getRecordTvScreenSupport()).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(info.getSetOsdStringSupport()).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(info.getDeckControlSupport()).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(info.getSetAudioRateSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(info.getArcTxSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(info.getArcRxSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(info.getSetAudioVolumeLevelSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+
+        assertThat(info.toOperand()).isEqualTo(new byte[]{(byte) 0b0111_0000});
+    }
+
+    @Test
+    public void testUpdate() {
+        DeviceFeatures oldFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_SUPPORTED)
+                .setSetOsdStringSupport(FEATURE_SUPPORTED)
+                .setDeckControlSupport(FEATURE_NOT_SUPPORTED)
+                .setSetAudioRateSupport(FEATURE_NOT_SUPPORTED)
+                .setArcTxSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setArcRxSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORT_UNKNOWN)
+                .build();
+
+        DeviceFeatures newFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_NOT_SUPPORTED)
+                .setSetOsdStringSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setDeckControlSupport(FEATURE_SUPPORTED)
+                .setSetAudioRateSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setArcTxSupport(FEATURE_SUPPORTED)
+                .setArcRxSupport(FEATURE_NOT_SUPPORTED)
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORT_UNKNOWN)
+                .build();
+
+        // Always take the field from newFeatures, unless it's FEATURE_SUPPORT_UNKNOWN
+        DeviceFeatures updatedFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_NOT_SUPPORTED)
+                .setSetOsdStringSupport(FEATURE_SUPPORTED)
+                .setDeckControlSupport(FEATURE_SUPPORTED)
+                .setSetAudioRateSupport(FEATURE_NOT_SUPPORTED)
+                .setArcTxSupport(FEATURE_SUPPORTED)
+                .setArcRxSupport(FEATURE_NOT_SUPPORTED)
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORT_UNKNOWN)
+                .build();
+
+        assertThat(oldFeatures.toBuilder().update(newFeatures).build()).isEqualTo(updatedFeatures);
+    }
+}
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
index 4ce072c..5f7468e 100755
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
@@ -43,44 +43,32 @@
         int adopterId = 2;
 
         new EqualsTester()
-                .addEqualityGroup(new HdmiDeviceInfo())
+                .addEqualityGroup(HdmiDeviceInfo.INACTIVE_DEVICE)
                 .addEqualityGroup(
-                        new HdmiDeviceInfo(phyAddr, portId), new HdmiDeviceInfo(phyAddr, portId))
+                        HdmiDeviceInfo.hardwarePort(phyAddr, portId),
+                        HdmiDeviceInfo.hardwarePort(phyAddr, portId))
                 .addEqualityGroup(
-                        new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId),
-                        new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId))
+                        HdmiDeviceInfo.mhlDevice(phyAddr, portId, adopterId, deviceId),
+                        HdmiDeviceInfo.mhlDevice(phyAddr, portId, adopterId, deviceId))
                 .addEqualityGroup(
-                        new HdmiDeviceInfo(
-                                logicalAddr, phyAddr, portId, deviceType, vendorId, displayName),
-                        new HdmiDeviceInfo(
-                                logicalAddr, phyAddr, portId, deviceType, vendorId, displayName))
-                .addEqualityGroup(
-                        new HdmiDeviceInfo(
-                                logicalAddr,
-                                phyAddr,
-                                portId,
-                                deviceType,
-                                vendorId,
-                                displayName,
-                                powerStatus),
-                        new HdmiDeviceInfo(
-                                logicalAddr,
-                                phyAddr,
-                                portId,
-                                deviceType,
-                                vendorId,
-                                displayName,
-                                powerStatus))
-                .addEqualityGroup(
-                        new HdmiDeviceInfo(
-                                logicalAddr,
-                                phyAddr,
-                                portId,
-                                deviceType,
-                                vendorId,
-                                displayName,
-                                powerStatus,
-                                cecVersion))
+                        HdmiDeviceInfo.cecDeviceBuilder()
+                                .setLogicalAddress(logicalAddr)
+                                .setPhysicalAddress(phyAddr)
+                                .setPortId(portId)
+                                .setDeviceType(deviceType)
+                                .setVendorId(vendorId)
+                                .setDisplayName(displayName)
+                                .setDevicePowerStatus(powerStatus)
+                                .setCecVersion(cecVersion).build(),
+                        HdmiDeviceInfo.cecDeviceBuilder()
+                                .setLogicalAddress(logicalAddr)
+                                .setPhysicalAddress(phyAddr)
+                                .setPortId(portId)
+                                .setDeviceType(deviceType)
+                                .setVendorId(vendorId)
+                                .setDisplayName(displayName)
+                                .setDevicePowerStatus(powerStatus)
+                                .setCecVersion(cecVersion).build())
                 .testEquals();
     }
 }
diff --git a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
deleted file mode 100644
index c4080e8..0000000
--- a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.app.PropertyInvalidatedCache;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-public class PropertyInvalidatedCacheTest extends TestCase {
-    private static final String KEY = "sys.testkey";
-    private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
-
-    private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
-        TestCache() {
-            this(KEY);
-        }
-
-        TestCache(String key) {
-            super(4, key);
-        }
-
-        @Override
-        protected String recompute(Integer qv) {
-            mRecomputeCount += 1;
-            return "foo" + qv.toString();
-        }
-
-        int getRecomputeCount() {
-            return mRecomputeCount;
-        }
-
-        private int mRecomputeCount = 0;
-    }
-
-    @Override
-    protected void setUp() {
-        SystemProperties.set(KEY, "");
-    }
-
-    @SmallTest
-    public void testCacheRecompute() throws Exception {
-        TestCache cache = new TestCache();
-        cache.invalidateCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals(1, cache.getRecomputeCount());
-        assertEquals("foo5", cache.query(5));
-        assertEquals(1, cache.getRecomputeCount());
-        assertEquals("foo6", cache.query(6));
-        assertEquals(2, cache.getRecomputeCount());
-        cache.invalidateCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(3, cache.getRecomputeCount());
-    }
-
-    @SmallTest
-    public void testCacheInitialState() throws Exception {
-        TestCache cache = new TestCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(2, cache.getRecomputeCount());
-        cache.invalidateCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(3, cache.getRecomputeCount());
-    }
-
-    @SmallTest
-    public void testCachePropertyUnset() throws Exception {
-        TestCache cache = new TestCache(UNSET_KEY);
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(2, cache.getRecomputeCount());
-    }
-
-    @SmallTest
-    public void testCacheDisableState() throws Exception {
-        TestCache cache = new TestCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(2, cache.getRecomputeCount());
-        cache.invalidateCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(3, cache.getRecomputeCount());
-        cache.disableSystemWide();
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(5, cache.getRecomputeCount());
-        cache.invalidateCache();  // Should not reenable
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(7, cache.getRecomputeCount());
-    }
-
-    @SmallTest
-    public void testRefreshSameObject() throws Exception {
-        int[] refreshCount = new int[1];
-        TestCache cache = new TestCache() {
-            @Override
-            protected String refresh(String oldResult, Integer query) {
-                refreshCount[0] += 1;
-                return oldResult;
-            }
-        };
-        cache.invalidateCache();
-        String result1 = cache.query(5);
-        assertEquals("foo5", result1);
-        String result2 = cache.query(5);
-        assertSame(result1, result2);
-        assertEquals(1, cache.getRecomputeCount());
-        assertEquals(1, refreshCount[0]);
-        assertEquals("foo5", cache.query(5));
-        assertEquals(2, refreshCount[0]);
-    }
-
-    @SmallTest
-    public void testRefreshInvalidateRace() throws Exception {
-        int[] refreshCount = new int[1];
-        TestCache cache = new TestCache() {
-            @Override
-            protected String refresh(String oldResult, Integer query) {
-                refreshCount[0] += 1;
-                invalidateCache();
-                return new String(oldResult);
-            }
-        };
-        cache.invalidateCache();
-        String result1 = cache.query(5);
-        assertEquals("foo5", result1);
-        String result2 = cache.query(5);
-        assertEquals(result1, result2);
-        assertNotSame(result1, result2);
-        assertEquals(2, cache.getRecomputeCount());
-    }
-
-    @SmallTest
-    public void testLocalProcessDisable() throws Exception {
-        TestCache cache = new TestCache();
-        cache.invalidateCache();
-        assertEquals("foo5", cache.query(5));
-        assertEquals(1, cache.getRecomputeCount());
-        assertEquals("foo5", cache.query(5));
-        assertEquals(1, cache.getRecomputeCount());
-        assertEquals(cache.isDisabledLocal(), false);
-        cache.disableLocal();
-        assertEquals(cache.isDisabledLocal(), true);
-        assertEquals("foo5", cache.query(5));
-        assertEquals("foo5", cache.query(5));
-        assertEquals(3, cache.getRecomputeCount());
-    }
-
-}
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index 50e8474..b659f37 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -21,13 +21,16 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
 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.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
+import android.os.RemoteException;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
@@ -38,12 +41,16 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.nio.charset.StandardCharsets;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LockPatternUtilsTest {
@@ -102,4 +109,84 @@
         configureTest(false, true, 0);
         assertFalse(mLockPatternUtils.isLockScreenDisabled(DEMO_USER_ID));
     }
+
+    @Test
+    public void testAddWeakEscrowToken() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        byte[] testToken = "test_token".getBytes(StandardCharsets.UTF_8);
+        int testUserId = 10;
+        IWeakEscrowTokenActivatedListener listener = createWeakEscrowTokenListener();
+        mLockPatternUtils.addWeakEscrowToken(testToken, testUserId, listener);
+        verify(ils).addWeakEscrowToken(eq(testToken), eq(testUserId), eq(listener));
+    }
+
+    @Test
+    public void testRegisterWeakEscrowTokenRemovedListener() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        IWeakEscrowTokenRemovedListener testListener = createTestAutoEscrowTokenRemovedListener();
+        mLockPatternUtils.registerWeakEscrowTokenRemovedListener(testListener);
+        verify(ils).registerWeakEscrowTokenRemovedListener(eq(testListener));
+    }
+
+    @Test
+    public void testUnregisterWeakEscrowTokenRemovedListener() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        IWeakEscrowTokenRemovedListener testListener = createTestAutoEscrowTokenRemovedListener();
+        mLockPatternUtils.unregisterWeakEscrowTokenRemovedListener(testListener);
+        verify(ils).unregisterWeakEscrowTokenRemovedListener(eq(testListener));
+    }
+
+    @Test
+    public void testRemoveAutoEscrowToken() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        int testUserId = 10;
+        long testHandle = 100L;
+        mLockPatternUtils.removeWeakEscrowToken(testHandle, testUserId);
+        verify(ils).removeWeakEscrowToken(eq(testHandle), eq(testUserId));
+    }
+
+    @Test
+    public void testIsAutoEscrowTokenActive() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        int testUserId = 10;
+        long testHandle = 100L;
+        mLockPatternUtils.isWeakEscrowTokenActive(testHandle, testUserId);
+        verify(ils).isWeakEscrowTokenActive(eq(testHandle), eq(testUserId));
+    }
+
+    @Test
+    public void testIsAutoEscrowTokenValid() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        int testUserId = 10;
+        byte[] testToken = "test_token".getBytes(StandardCharsets.UTF_8);
+        long testHandle = 100L;
+        mLockPatternUtils.isWeakEscrowTokenValid(testHandle, testToken, testUserId);
+        verify(ils).isWeakEscrowTokenValid(eq(testHandle), eq(testToken), eq(testUserId));
+    }
+
+    private ILockSettings createTestLockSettings() {
+        final Context context = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+        mLockPatternUtils = spy(new LockPatternUtils(context));
+        final ILockSettings ils = Mockito.mock(ILockSettings.class);
+        when(mLockPatternUtils.getLockSettings()).thenReturn(ils);
+        return ils;
+    }
+
+    private IWeakEscrowTokenActivatedListener createWeakEscrowTokenListener() {
+        return new IWeakEscrowTokenActivatedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenActivated(long handle, int userId) {
+                // Do nothing.
+            }
+        };
+    }
+
+    private IWeakEscrowTokenRemovedListener createTestAutoEscrowTokenRemovedListener() {
+        return new IWeakEscrowTokenRemovedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenRemoved(long handle, int userId) {
+                // Do nothing.
+            }
+        };
+    }
 }
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index 3fdb0da..ddcab6e 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -30,6 +30,7 @@
         <permission name="android.permission.MANAGE_DEBUGGING"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MANAGE_GAME_MODE" />
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
@@ -59,5 +60,6 @@
         <permission name="android.permission.READ_DREAM_STATE"/>
         <permission name="android.permission.READ_DREAM_SUPPRESSION"/>
         <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
+        <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index f2a33de..d95644a 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -30,6 +30,7 @@
         <permission name="android.permission.GET_APP_OPS_STATS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_DEBUGGING"/>
+        <permission name="android.permission.MANAGE_GAME_MODE" />
         <permission name="android.permission.MANAGE_SENSOR_PRIVACY"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MANAGE_USERS"/>
@@ -50,6 +51,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.SET_WALLPAPER_DIM_AMOUNT"/>
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
         <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
         <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
@@ -71,5 +73,6 @@
         <permission name="android.permission.USE_BACKGROUND_BLUR" />
         <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
         <permission name="android.permission.FORCE_STOP_PACKAGES" />
+        <permission name="android.permission.ACCESS_FPS_COUNTER" />
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f17fa3b..de086df 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -275,6 +275,7 @@
     <privapp-permissions package="com.android.server.telecom">
         <permission name="android.permission.BIND_CONNECTION_SERVICE"/>
         <permission name="android.permission.BIND_INCALL_SERVICE"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
         <permission name="android.permission.CALL_PRIVILEGED"/>
         <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
@@ -293,6 +294,7 @@
 
     <privapp-permissions package="com.android.shell">
         <!-- Needed for test only -->
+        <permission name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"/>
         <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
         <permission name="android.permission.ACCESS_LOWPAN_STATE"/>
         <permission name="android.permission.BACKUP"/>
@@ -331,6 +333,7 @@
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+        <permission name="android.permission.MANAGE_GAME_MODE"/>
         <permission name="android.permission.MANAGE_ROLLBACKS"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
@@ -390,7 +393,11 @@
         <permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/>
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
+        <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+        <!-- Permission required for CTS test - TrustTestCases -->
+        <permission name="android.permission.PROVIDE_TRUST_AGENT" />
+        <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
         <!-- Permissions required for Incremental CTS tests -->
         <permission name="com.android.permission.USE_INSTALLER_V2"/>
         <permission name="android.permission.LOADER_USAGE_STATS"/>
@@ -518,13 +525,9 @@
         <permission name="android.permission.MANAGE_VOICE_KEYPHRASES" />
         <!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
         <permission name="android.permission.LOCK_DEVICE" />
-        <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
+        <!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
-        <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
-        <!-- Permission required for CTS test - CommunalManagerTest -->
-        <permission name="android.permission.WRITE_COMMUNAL_STATE" />
-        <permission name="android.permission.READ_COMMUNAL_STATE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -572,6 +575,7 @@
     <privapp-permissions package="com.android.settings">
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
+        <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.bips">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 9584994..0752329 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -103,18 +103,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
-    "-2002500255": {
-      "message": "Defer removing snapshot surface in %dms",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
-    },
-    "-1991255017": {
-      "message": "Drawing snapshot surface sizeMismatch=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
-    },
     "-1980468143": {
       "message": "DisplayArea appeared name=%s",
       "level": "VERBOSE",
@@ -505,12 +493,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-1556507536": {
-      "message": "Passing transform hint %d for window %s%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-1554521902": {
       "message": "showInsets(ime) was requested by different window: %s ",
       "level": "WARN",
@@ -745,6 +727,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1343787701": {
+      "message": "startBackNavigation task=%s, topRunningActivity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "-1340540100": {
       "message": "Creating SnapshotStartingData",
       "level": "VERBOSE",
@@ -1597,12 +1585,6 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "-405536909": {
-      "message": "Removing snapshot surface",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
-    },
     "-401282500": {
       "message": "destroyIfPossible: r=%s destroy returned removed=%s",
       "level": "DEBUG",
@@ -1867,6 +1849,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
+    "-134091882": {
+      "message": "Screenshotting Activity %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-124316973": {
       "message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
       "level": "VERBOSE",
@@ -1951,6 +1939,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
+    "-23020844": {
+      "message": "Back: Reset surfaces",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "-21399771": {
       "message": "activity %s already destroying, skipping request with reason:%s",
       "level": "VERBOSE",
@@ -2005,12 +1999,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "44438983": {
-      "message": "performLayout: Activity exiting now removed %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ADD_REMOVE",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "45285419": {
       "message": "startingWindow was set but startingSurface==null, couldn't remove",
       "level": "VERBOSE",
@@ -2767,6 +2755,12 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "751854538": {
+      "message": "DisplayArea keep clear rects changed name =%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "765395228": {
       "message": "onAnimationFinished(): controller=%s reorderMode=%d",
       "level": "DEBUG",
@@ -3271,12 +3265,6 @@
       "group": "WM_DEBUG_LAYER_MIRRORING",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "1417601133": {
-      "message": "Enqueueing ADD_STARTING",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "1422781269": {
       "message": "Resuming rotation after re-position",
       "level": "DEBUG",
@@ -3397,6 +3385,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1554795024": {
+      "message": "Previous Activity is %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "1557732761": {
       "message": "For Intent %s bringing to top: %s",
       "level": "DEBUG",
@@ -3691,6 +3685,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1874559932": {
+      "message": "The TaskDisplayArea with %s does not exist.",
+      "level": "WARN",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java"
+    },
     "1891501279": {
       "message": "cancelAnimation(): reason=%s",
       "level": "DEBUG",
@@ -3912,6 +3912,9 @@
     "WM_DEBUG_APP_TRANSITIONS_ANIM": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_BACK_PREVIEW": {
+      "tag": "CoreBackPreview"
+    },
     "WM_DEBUG_BOOT": {
       "tag": "WindowManager"
     },
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a612265..425a378 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -67,7 +67,7 @@
      * @hide
      */
     protected int mDensity = Bitmap.DENSITY_NONE;
-    private boolean mAllowHwBitmapsInSwMode = false;
+    private boolean mAllowHwFeaturesInSwMode = false;
 
     protected void throwIfCannotDraw(Bitmap bitmap) {
         if (bitmap.isRecycled()) {
@@ -101,14 +101,14 @@
 
     public void drawArc(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle, boolean useCenter, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
                 useCenter, paint.getNativeInstance());
     }
 
     public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
                 paint);
     }
@@ -119,14 +119,14 @@
 
     public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top,
                 paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
                 bitmap.mDensity);
     }
 
     public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(),
                 paint != null ? paint.getNativeInstance() : 0);
     }
@@ -137,7 +137,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
 
         int left, top, right, bottom;
@@ -163,7 +163,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
 
         float left, top, right, bottom;
@@ -202,7 +202,7 @@
                 || (lastScanline + width > length)) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         // quick escape if there's nothing to draw
         if (width == 0 || height == 0) {
             return;
@@ -226,7 +226,7 @@
         if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (meshWidth == 0 || meshHeight == 0) {
             return;
         }
@@ -243,7 +243,7 @@
     }
 
     public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
     }
 
@@ -275,23 +275,23 @@
 
     public void drawLine(float startX, float startY, float stopX, float stopY,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
     }
 
     public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
     public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawLines(pts, 0, pts.length, paint);
     }
 
     public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
     }
 
@@ -299,18 +299,19 @@
         if (oval == null) {
             throw new NullPointerException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
     }
 
     public void drawPaint(@NonNull Paint paint) {
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
     }
 
     public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
         nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -320,7 +321,7 @@
     public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
         nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -328,7 +329,7 @@
     }
 
     public void drawPath(@NonNull Path path, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (path.isSimplePath && path.rects != null) {
             nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
         } else {
@@ -337,18 +338,18 @@
     }
 
     public void drawPoint(float x, float y, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
     }
 
     public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
     public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawPoints(pts, 0, pts.length, paint);
     }
 
@@ -359,7 +360,7 @@
         if (index < 0 || index + count > text.length || count * 2 > pos.length) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         for (int i = 0; i < count; i++) {
             drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
         }
@@ -368,22 +369,22 @@
     @Deprecated
     public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
     }
 
     public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
     }
 
     public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawRect(r.left, r.top, r.right, r.bottom, paint);
     }
 
     public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawRect(mNativeCanvasWrapper,
                 rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
     }
@@ -394,13 +395,13 @@
 
     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
                 paint.getNativeInstance());
     }
 
     public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
     }
 
@@ -410,7 +411,7 @@
      */
     public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
             @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         float outerLeft = outer.left;
         float outerTop = outer.top;
         float outerRight = outer.right;
@@ -431,7 +432,7 @@
      */
     public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
             @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (innerRadii == null || outerRadii == null
                 || innerRadii.length != 8 || outerRadii.length != 8) {
             throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
@@ -509,7 +510,7 @@
                 (text.length - index - count)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
                 paint.getNativeInstance());
     }
@@ -519,7 +520,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -537,7 +538,7 @@
     }
 
     public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
                 paint.getNativeInstance());
     }
@@ -547,7 +548,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
                 paint.getNativeInstance());
     }
@@ -557,7 +558,7 @@
         if (index < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
                 path.readOnlyNI(), hOffset, vOffset,
                 paint.mBidiFlags, paint.getNativeInstance());
@@ -566,7 +567,7 @@
     public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
             float vOffset, @NonNull Paint paint) {
         if (text.length() > 0) {
-            throwIfHasHwBitmapInSwMode(paint);
+            throwIfHasHwFeaturesInSwMode(paint);
             nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
                     paint.mBidiFlags, paint.getNativeInstance());
         }
@@ -587,7 +588,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
                 x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
     }
@@ -606,7 +607,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
@@ -664,7 +665,7 @@
         if (indices != null) {
             checkRange(indices.length, indexOffset, indexCount);
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
                 vertOffset, texs, texOffset, colors, colorOffset,
                 indices, indexOffset, indexCount, paint.getNativeInstance());
@@ -680,50 +681,52 @@
     /**
      * @hide
      */
-    public void setHwBitmapsInSwModeEnabled(boolean enabled) {
-        mAllowHwBitmapsInSwMode = enabled;
+    public void setHwFeaturesInSwModeEnabled(boolean enabled) {
+        mAllowHwFeaturesInSwMode = enabled;
     }
 
     /**
      * @hide
      */
-    public boolean isHwBitmapsInSwModeEnabled() {
-        return mAllowHwBitmapsInSwMode;
+    public boolean isHwFeaturesInSwModeEnabled() {
+        return mAllowHwFeaturesInSwMode;
     }
 
     /**
+     * If true throw an exception
      * @hide
      */
-    protected void onHwBitmapInSwMode() {
-        if (!mAllowHwBitmapsInSwMode) {
+    protected boolean onHwFeatureInSwMode() {
+        return !mAllowHwFeaturesInSwMode;
+    }
+
+    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
+        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
+                && onHwFeatureInSwMode()) {
             throw new IllegalArgumentException(
                     "Software rendering doesn't support hardware bitmaps");
         }
     }
 
-    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
-        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
-            onHwBitmapInSwMode();
-        }
-    }
-
-    private void throwIfHasHwBitmapInSwMode(Paint p) {
+    private void throwIfHasHwFeaturesInSwMode(Paint p) {
         if (isHardwareAccelerated() || p == null) {
             return;
         }
-        throwIfHasHwBitmapInSwMode(p.getShader());
+        throwIfHasHwFeaturesInSwMode(p.getShader());
     }
 
-    private void throwIfHasHwBitmapInSwMode(Shader shader) {
+    private void throwIfHasHwFeaturesInSwMode(Shader shader) {
         if (shader == null) {
             return;
         }
         if (shader instanceof BitmapShader) {
             throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
-        }
-        if (shader instanceof ComposeShader) {
-            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA);
-            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB);
+        } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
+            throw new IllegalArgumentException(
+                    "Software rendering doesn't support RuntimeShader");
+        } else if (shader instanceof ComposeShader) {
+            throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
+            throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
         }
     }
 
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index e6ff187..43cb5ee 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -16,8 +16,12 @@
 
 package android.graphics;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
  * mirrored by setting the tiling mode.
@@ -31,6 +35,47 @@
     private int mTileX;
     private int mTileY;
 
+    /** @hide */
+    @IntDef(prefix = {"FILTER_MODE"}, value = {
+            FILTER_MODE_DEFAULT,
+            FILTER_MODE_NEAREST,
+            FILTER_MODE_LINEAR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FilterMode {}
+
+    /**
+     * This FilterMode value will respect the value of the Paint#isFilterBitmap flag while the
+     * shader is attached to the Paint.
+     *
+     * <p>The exception to this rule is when a Shader is attached as input to a RuntimeShader. In
+     *    that case this mode will default to FILTER_MODE_NEAREST.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_DEFAULT = 0;
+    /**
+     * This FilterMode value will cause the shader to sample from the nearest pixel to the requested
+     * sample point.
+     *
+     * <p>This value will override the effect of Paint#isFilterBitmap.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_NEAREST = 1;
+    /**
+     * This FilterMode value will cause the shader to interpolate the output of the shader from a
+     * 2x2 grid of pixels nearest to the sample point (i.e. bilinear interpolation).
+     *
+     * <p>This value will override the effect of Paint#isFilterBitmap.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_LINEAR = 2;
+
+    @FilterMode
+    private int mFilterMode;
+
     /*
      *  This is cache of the last value from the Paint of bitmap-filtering.
      *  In the future, BitmapShaders will carry their own (expanded) data for this
@@ -49,6 +94,15 @@
     private boolean mFilterFromPaint;
 
     /**
+     *  Stores whether or not the contents of this shader's bitmap will be sampled
+     *  without modification or if the bitmap's properties, like colorspace and
+     *  premultiplied alpha, will be respected when sampling from the bitmap's buffer.
+     */
+    private boolean mIsDirectSampled;
+
+    private boolean mRequestDirectSampling;
+
+    /**
      * Call this to create a new shader that will draw with a bitmap.
      *
      * @param bitmap The bitmap to use inside the shader
@@ -66,24 +120,60 @@
         mBitmap = bitmap;
         mTileX = tileX;
         mTileY = tileY;
+        mFilterMode = FILTER_MODE_DEFAULT;
         mFilterFromPaint = false;
+        mIsDirectSampled = false;
+        mRequestDirectSampling = false;
+    }
+
+    /**
+     * Returns the filter mode used when sampling from this shader
+     */
+    @FilterMode
+    public int getFilterMode() {
+        return mFilterMode;
+    }
+
+    /**
+     * Set the filter mode to be used when sampling from this shader
+     */
+    public void setFilterMode(@FilterMode int mode) {
+        if (mode != mFilterMode) {
+            mFilterMode = mode;
+            discardNativeInstance();
+        }
+    }
+
+    /** @hide */
+    /* package */ synchronized long getNativeInstanceWithDirectSampling() {
+        mRequestDirectSampling = true;
+        return getNativeInstance();
     }
 
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
-        mFilterFromPaint = filterFromPaint;
+        boolean enableLinearFilter = mFilterMode == FILTER_MODE_LINEAR;
+        if (mFilterMode == FILTER_MODE_DEFAULT) {
+            mFilterFromPaint = filterFromPaint;
+            enableLinearFilter = mFilterFromPaint;
+        }
+
+        mIsDirectSampled = mRequestDirectSampling;
+        mRequestDirectSampling = false;
+
         return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, mTileY,
-                            mFilterFromPaint);
+                            enableLinearFilter, mIsDirectSampled);
     }
 
     /** @hide */
     @Override
     protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) {
-        return mFilterFromPaint != filterFromPaint;
+        return mIsDirectSampled != mRequestDirectSampling
+                || (mFilterMode == FILTER_MODE_DEFAULT && mFilterFromPaint != filterFromPaint);
     }
 
     private static native long nativeCreate(long nativeMatrix, long bitmapHandle,
-            int shaderTileModeX, int shaderTileModeY, boolean filter);
+            int shaderTileModeX, int shaderTileModeY, boolean filter, boolean isDirectSampled);
 }
 
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index fc7f84c..618e6dc 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -169,8 +169,7 @@
     }
 
     /**
-     * Sets the Outline to the rounded rect defined by the input rect, and
-     * corner radius.
+     * Sets the Outline to the rect defined by the input coordinates.
      */
     public void setRect(int left, int top, int right, int bottom) {
         setRoundRect(left, top, right, bottom, 0.0f);
@@ -184,7 +183,7 @@
     }
 
     /**
-     * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
+     * Sets the Outline to the rounded rect defined by the input coordinates and corner radius.
      * <p>
      * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
      */
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42e470b..eefad8d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -46,6 +46,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
+import java.util.Objects;
 
 /**
  * The Paint class holds the style and color information about how to draw
@@ -2131,6 +2132,116 @@
     }
 
     /**
+     * Returns the font metrics value for the given text.
+     *
+     * If the text is rendered with multiple font files, this function returns the large ascent and
+     * descent that are enough for drawing all font files.
+     *
+     * The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
+     * changes letter shape based on its location or surrounding characters.
+     *
+     * @param text a text to be measured.
+     * @param start a starting offset in the text.
+     * @param count a length of the text to be measured.
+     * @param contextStart a context starting offset in the text.
+     * @param contextCount a length of the context to be used.
+     * @param isRtl true if measuring on RTL context, otherwise false.
+     * @param outMetrics the output font metrics.
+     */
+    public void getFontMetricsInt(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int count,
+            @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
+            boolean isRtl,
+            @NonNull FontMetricsInt outMetrics) {
+
+        if (text == null) {
+            throw new IllegalArgumentException("text must not be null");
+        }
+        if (start < 0 || start >= text.length()) {
+            throw new IllegalArgumentException("start argument is out of bounds.");
+        }
+        if (count < 0 || start + count > text.length()) {
+            throw new IllegalArgumentException("count argument is out of bounds.");
+        }
+        if (contextStart < 0 || contextStart >= text.length()) {
+            throw new IllegalArgumentException("ctxStart argument is out of bounds.");
+        }
+        if (contextCount < 0 || contextStart + contextCount > text.length()) {
+            throw new IllegalArgumentException("ctxCount argument is out of bounds.");
+        }
+        if (outMetrics == null) {
+            throw new IllegalArgumentException("outMetrics must not be null.");
+        }
+
+        if (count == 0) {
+            getFontMetricsInt(outMetrics);
+            return;
+        }
+
+        if (text instanceof String) {
+            nGetFontMetricsIntForText(mNativePaint, (String) text, start, count, contextStart,
+                    contextCount, isRtl, outMetrics);
+        } else {
+            char[] buf = TemporaryBuffer.obtain(contextCount);
+            TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
+            nGetFontMetricsIntForText(mNativePaint, buf, start - contextStart, count, 0,
+                    contextCount, isRtl, outMetrics);
+        }
+
+    }
+
+    /**
+     * Returns the font metrics value for the given text.
+     *
+     * If the text is rendered with multiple font files, this function returns the large ascent and
+     * descent that are enough for drawing all font files.
+     *
+     * The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
+     * changes letter shape based on its location or surrounding characters.
+     *
+     * @param text a text to be measured.
+     * @param start a starting offset in the text.
+     * @param count a length of the text to be measured.
+     * @param contextStart a context starting offset in the text.
+     * @param contextCount a length of the context to be used.
+     * @param isRtl true if measuring on RTL context, otherwise false.
+     * @param outMetrics the output font metrics.
+     */
+    public void getFontMetricsInt(@NonNull char[] text,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int count,
+            @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
+            boolean isRtl,
+            @NonNull FontMetricsInt outMetrics) {
+        if (text == null) {
+            throw new IllegalArgumentException("text must not be null");
+        }
+        if (start < 0 || start >= text.length) {
+            throw new IllegalArgumentException("start argument is out of bounds.");
+        }
+        if (count < 0 || start + count > text.length) {
+            throw new IllegalArgumentException("count argument is out of bounds.");
+        }
+        if (contextStart < 0 || contextStart >= text.length) {
+            throw new IllegalArgumentException("ctxStart argument is out of bounds.");
+        }
+        if (contextCount < 0 || contextStart + contextCount > text.length) {
+            throw new IllegalArgumentException("ctxCount argument is out of bounds.");
+        }
+        if (outMetrics == null) {
+            throw new IllegalArgumentException("outMetrics must not be null.");
+        }
+
+        if (count == 0) {
+            getFontMetricsInt(outMetrics);
+            return;
+        }
+
+        nGetFontMetricsIntForText(mNativePaint, text, start, count, contextStart, contextCount,
+                isRtl, outMetrics);
+    }
+
+    /**
      * Convenience method for callers that want to have FontMetrics values as
      * integers.
      */
@@ -2163,6 +2274,23 @@
                     " descent=" + descent + " bottom=" + bottom +
                     " leading=" + leading;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof FontMetricsInt)) return false;
+            FontMetricsInt that = (FontMetricsInt) o;
+            return top == that.top
+                    && ascent == that.ascent
+                    && descent == that.descent
+                    && bottom == that.bottom
+                    && leading == that.leading;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(top, ascent, descent, bottom, leading);
+        }
     }
 
     /**
@@ -3117,6 +3245,13 @@
             int contextStart, int contextEnd, boolean isRtl, int offset);
     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,
+            int start, int count, int ctxStart, int ctxCount, boolean isRtl,
+            FontMetricsInt outMetrics);
+    private static native void nGetFontMetricsIntForText(long paintPtr, String text,
+            int start, int count, int ctxStart, int ctxCount, boolean isRtl,
+            FontMetricsInt outMetrics);
+
 
 
     // ---------------- @FastNative ------------------------
@@ -3130,7 +3265,6 @@
     @FastNative
     private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
 
-
     // ---------------- @CriticalNative ------------------------
 
     @CriticalNative
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 390d3d4..ee4165b 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -124,7 +124,7 @@
     public void endRecording() {
         verifyValid();
         if (mRecordingCanvas != null) {
-            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
+            mRequiresHwAcceleration = mRecordingCanvas.mUsesHwFeature;
             mRecordingCanvas = null;
             nativeEndRecording(mNativePicture);
         }
@@ -182,8 +182,10 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
-        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
-            canvas.onHwBitmapInSwMode();
+        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()
+                && canvas.onHwFeatureInSwMode()) {
+            throw new IllegalArgumentException("Software rendering not supported for Pictures that"
+                    + " require hardware acceleration");
         }
         nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
     }
@@ -242,7 +244,7 @@
 
     private static class PictureCanvas extends Canvas {
         private final Picture mPicture;
-        boolean mHoldsHwBitmap;
+        boolean mUsesHwFeature;
 
         public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
@@ -265,8 +267,9 @@
         }
 
         @Override
-        protected void onHwBitmapInSwMode() {
-            mHoldsHwBitmap = true;
+        protected boolean onHwFeatureInSwMode() {
+            mUsesHwFeature = true;
+            return false;
         }
     }
 }
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 9ca8e3b..57046f5 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -178,36 +178,6 @@
         setUniform(uniformName, values, false);
     }
 
-    /**
-     * Old method signature used by some callers within the platform code
-     * @hide
-     * @deprecated use setFloatUniform instead
-     */
-    @Deprecated
-    public void setUniform(@NonNull String uniformName, float[] values) {
-        setFloatUniform(uniformName, values);
-    }
-
-    /**
-     * Old method signature used by some callers within the platform code
-     * @hide
-     * @deprecated use setFloatUniform instead
-     */
-    @Deprecated
-    public void setUniform(@NonNull String uniformName, float value) {
-        setFloatUniform(uniformName, value);
-    }
-
-    /**
-     * Old method signature used by some callers within the platform code
-     * @hide
-     * @deprecated use setFloatUniform instead
-     */
-    @Deprecated
-    public void setUniform(@NonNull String uniformName, float value1, float value2) {
-        setFloatUniform(uniformName, value1, value2);
-    }
-
     private void setFloatUniform(@NonNull String uniformName, float value1, float value2,
             float value3, float value4, int count) {
         if (uniformName == null) {
@@ -309,11 +279,11 @@
     }
 
     /**
-     * Sets the uniform shader that is declares as input to this shader.  If the shader does not
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
      * have a uniform shader with that name then an IllegalArgumentException is thrown.
      *
-     * @param shaderName name matching the uniform declared in the SKSL shader
-     * @param shader shader passed into the SKSL shader for sampling
+     * @param shaderName name matching the uniform declared in the AGSL shader program
+     * @param shader shader passed into the AGSL shader program for sampling
      */
     public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
         if (shaderName == null) {
@@ -327,6 +297,28 @@
         discardNativeInstance();
     }
 
+    /**
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
+     * have a uniform shader with that name then an IllegalArgumentException is thrown.
+     *
+     * Unlike setInputShader this method returns samples directly from the bitmap's buffer. This
+     * means that there will be no transformation of the sampled pixels, such as colorspace
+     * conversion or alpha premultiplication.
+     */
+    public void setInputBuffer(@NonNull String shaderName, @NonNull BitmapShader shader) {
+        if (shaderName == null) {
+            throw new NullPointerException("The shaderName parameter must not be null");
+        }
+        if (shader == null) {
+            throw new NullPointerException("The shader parameter must not be null");
+        }
+
+        nativeUpdateShader(mNativeInstanceRuntimeShaderBuilder, shaderName,
+                shader.getNativeInstanceWithDirectSampling());
+        discardNativeInstance();
+    }
+
+
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b843589..ffaa4ea 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -868,7 +868,7 @@
     private void drawPatterned(@NonNull Canvas canvas) {
         final Rect bounds = mHotspotBounds;
         final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-        boolean useCanvasProps = shouldUseCanvasProps(canvas);
+        boolean useCanvasProps = !mForceSoftware;
         if (isBounded()) {
             canvas.clipRect(getDirtyBounds());
         }
@@ -914,7 +914,11 @@
         }
         for (int i = 0; i < mRunningAnimations.size(); i++) {
             RippleAnimationSession s = mRunningAnimations.get(i);
-            if (useCanvasProps) {
+            if (!canvas.isHardwareAccelerated()) {
+                Log.e(TAG, "The RippleDrawable.STYLE_PATTERNED animation is not supported for a "
+                        + "non-hardware accelerated Canvas. Skipping animation.");
+                break;
+            } else if (useCanvasProps) {
                 RippleAnimationSession.AnimationProperties<CanvasProperty<Float>,
                         CanvasProperty<Paint>>
                         p = s.getCanvasProperties();
@@ -1002,10 +1006,6 @@
         return color;
     }
 
-    private boolean shouldUseCanvasProps(Canvas c) {
-        return !mForceSoftware && c.isHardwareAccelerated();
-    }
-
     @Override
     public void invalidateSelf() {
         invalidateSelf(true);
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 272b8402..53a6731 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -17,7 +17,6 @@
 package android.graphics.drawable;
 
 import android.annotation.ColorInt;
-import android.graphics.Color;
 import android.graphics.RuntimeShader;
 import android.graphics.Shader;
 
@@ -37,8 +36,8 @@
             + "uniform vec2 in_tRotation1;\n"
             + "uniform vec2 in_tRotation2;\n"
             + "uniform vec2 in_tRotation3;\n"
-            + "uniform vec4 in_color;\n"
-            + "uniform vec4 in_sparkleColor;\n"
+            + "layout(color) uniform vec4 in_color;\n"
+            + "layout(color) uniform vec4 in_sparkleColor;\n"
             + "uniform shader in_shader;\n";
     private static final String SHADER_LIB =
             "float triangleNoise(vec2 n) {\n"
@@ -134,78 +133,68 @@
         if (shader != null) {
             setInputShader("in_shader", shader);
         }
-        setUniform("in_hasMask", shader == null ? 0 : 1);
+        setFloatUniform("in_hasMask", shader == null ? 0 : 1);
     }
 
     public void setRadius(float radius) {
-        setUniform("in_maxRadius", radius * 2.3f);
+        setFloatUniform("in_maxRadius", radius * 2.3f);
     }
 
     public void setOrigin(float x, float y) {
-        setUniform("in_origin", new float[] {x, y});
+        setFloatUniform("in_origin", x, y);
     }
 
     public void setTouch(float x, float y) {
-        setUniform("in_touch", new float[] {x, y});
+        setFloatUniform("in_touch", x, y);
     }
 
     public void setProgress(float progress) {
-        setUniform("in_progress", progress);
+        setFloatUniform("in_progress", progress);
     }
 
     /**
      * Continuous offset used as noise phase.
      */
     public void setNoisePhase(float phase) {
-        setUniform("in_noisePhase", phase * 0.001f);
+        setFloatUniform("in_noisePhase", phase * 0.001f);
 
         //
         // Keep in sync with: frameworks/base/libs/hwui/pipeline/skia/AnimatedDrawables.h
         //
         final float turbulencePhase = phase;
-        setUniform("in_turbulencePhase", turbulencePhase);
+        setFloatUniform("in_turbulencePhase", turbulencePhase);
         final float scale = 1.5f;
-        setUniform("in_tCircle1", new float[]{
+        setFloatUniform("in_tCircle1",
                 (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.cos(scale * 0.55))),
-                (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.sin(scale * 0.55)))
-        });
-        setUniform("in_tCircle2", new float[]{
+                (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.sin(scale * 0.55))));
+        setFloatUniform("in_tCircle2",
                 (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.cos(scale * 0.45))),
-                (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.sin(scale * 0.45)))
-        });
-        setUniform("in_tCircle3", new float[]{
+                (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.sin(scale * 0.45))));
+        setFloatUniform("in_tCircle3",
                 (float) (scale + (turbulencePhase * -0.0066 * Math.cos(scale * 0.35))),
-                (float) (scale + (turbulencePhase * -0.0066 * Math.sin(scale * 0.35)))
-        });
+                (float) (scale + (turbulencePhase * -0.0066 * Math.sin(scale * 0.35))));
         final double rotation1 = turbulencePhase * PI_ROTATE_RIGHT + 1.7 * Math.PI;
-        setUniform("in_tRotation1", new float[]{
-                (float) Math.cos(rotation1), (float) Math.sin(rotation1)
-        });
+        setFloatUniform("in_tRotation1",
+                (float) Math.cos(rotation1), (float) Math.sin(rotation1));
         final double rotation2 = turbulencePhase * PI_ROTATE_LEFT + 2 * Math.PI;
-        setUniform("in_tRotation2", new float[]{
-                (float) Math.cos(rotation2), (float) Math.sin(rotation2)
-        });
+        setFloatUniform("in_tRotation2",
+                (float) Math.cos(rotation2), (float) Math.sin(rotation2));
         final double rotation3 = turbulencePhase * PI_ROTATE_RIGHT + 2.75 * Math.PI;
-        setUniform("in_tRotation3", new float[]{
-                (float) Math.cos(rotation3), (float) Math.sin(rotation3)
-        });
+        setFloatUniform("in_tRotation3",
+                (float) Math.cos(rotation3), (float) Math.sin(rotation3));
     }
 
     /**
      * Color of the circle that's under the sparkles. Sparkles will always be white.
      */
     public void setColor(@ColorInt int colorInt, @ColorInt int sparkleColorInt) {
-        Color color = Color.valueOf(colorInt);
-        Color sparkleColor = Color.valueOf(sparkleColorInt);
-        setUniform("in_color", new float[] {color.red(),
-                color.green(), color.blue(), color.alpha()});
-        setUniform("in_sparkleColor", new float[] {sparkleColor.red(),
-                sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()});
+        setColorUniform("in_color", colorInt);
+        setColorUniform("in_sparkleColor", sparkleColorInt);
     }
 
     public void setResolution(float w, float h) {
         final float densityScale = 2.1f;
-        setUniform("in_resolutionScale", new float[] {1f / w, 1f / h});
-        setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h});
+        setFloatUniform("in_resolutionScale", 1f / w, 1f / h);
+        setFloatUniform("in_noiseScale", densityScale / w, densityScale / h);
     }
 }
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
new file mode 100644
index 0000000..cffdf28
--- /dev/null
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Indicates the strategies can be used when calculating the text wrapping.
+ *
+ * See <a href="https://drafts.csswg.org/css-text/#line-break-property">the line-break property</a>
+ */
+public final class LineBreakConfig {
+
+    /**
+     * No line break style specified.
+     */
+    public static final int LINE_BREAK_STYLE_NONE = 0;
+
+    /**
+     * Use the least restrictive rule for line-breaking. This is usually used for short lines.
+     */
+    public static final int LINE_BREAK_STYLE_LOOSE = 1;
+
+    /**
+     * Indicate breaking text with the most comment set of line-breaking rules.
+     */
+    public static final int LINE_BREAK_STYLE_NORMAL = 2;
+
+    /**
+     * Indicates breaking text with the most strictest line-breaking rules.
+     */
+    public static final int LINE_BREAK_STYLE_STRICT = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "LINE_BREAK_STYLE_" }, value = {
+            LINE_BREAK_STYLE_NONE, LINE_BREAK_STYLE_LOOSE, LINE_BREAK_STYLE_NORMAL,
+            LINE_BREAK_STYLE_STRICT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LineBreakStyle {}
+
+    /**
+     * No line break word style specified.
+     */
+    public static final int LINE_BREAK_WORD_STYLE_NONE = 0;
+
+    /**
+     * Indicates the line breaking is based on the phrased. This makes text wrapping only on
+     * meaningful words. The support of the text wrapping word style varies depending on the
+     * locales. If the locale does not support the phrase based text wrapping,
+     * there will be no effect.
+     */
+    public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1;
+
+    /** @hide */
+    @IntDef(prefix = { "LINE_BREAK_WORD_STYLE_" }, value = {
+        LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LineBreakWordStyle {}
+
+    private @LineBreakStyle int mLineBreakStyle = LINE_BREAK_STYLE_NONE;
+    private @LineBreakWordStyle int mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_NONE;
+
+    public LineBreakConfig() {
+    }
+
+    /**
+     * Set the line break configuration.
+     *
+     * @param lineBreakConfig the new line break configuration.
+     */
+    public void set(@NonNull LineBreakConfig lineBreakConfig) {
+        Objects.requireNonNull(lineBreakConfig);
+        mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
+        mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
+    }
+
+    /**
+     * Get the line break style.
+     *
+     * @return The current line break style to be used for the text wrapping.
+     */
+    public @LineBreakStyle int getLineBreakStyle() {
+        return mLineBreakStyle;
+    }
+
+    /**
+     * Set the line break style.
+     *
+     * @param lineBreakStyle the new line break style.
+     */
+    public void setLineBreakStyle(@LineBreakStyle int lineBreakStyle) {
+        mLineBreakStyle = lineBreakStyle;
+    }
+
+    /**
+     * Get the line break word style.
+     *
+     * @return The current line break word style to be used for the text wrapping.
+     */
+    public @LineBreakWordStyle int getLineBreakWordStyle() {
+        return mLineBreakWordStyle;
+    }
+
+    /**
+     * Set the line break word style.
+     *
+     * @param lineBreakWordStyle the new line break word style.
+     */
+    public void setLineBreakWordStyle(@LineBreakWordStyle int lineBreakWordStyle) {
+        mLineBreakWordStyle = lineBreakWordStyle;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        if (!(o instanceof LineBreakConfig)) return false;
+        LineBreakConfig that = (LineBreakConfig) o;
+        return (mLineBreakStyle == that.mLineBreakStyle)
+                && (mLineBreakWordStyle == that.mLineBreakWordStyle);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLineBreakStyle, mLineBreakWordStyle);
+    }
+}
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index a34d0ab..6d691c1 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -239,11 +239,35 @@
          */
         public @NonNull Builder appendStyleRun(@NonNull Paint paint, @IntRange(from = 0) int length,
                 boolean isRtl) {
+            return appendStyleRun(paint, null, length, isRtl);
+        }
+
+        /**
+         * Apply styles to the given length.
+         *
+         * Keeps an internal offset which increases at every append. The initial value for this
+         * offset is zero. After the style is applied the internal offset is moved to {@code offset
+         * + length}, and next call will start from this new position.
+         *
+         * @param paint a paint
+         * @param lineBreakConfig a line break configuration.
+         * @param length a length to be applied with a given paint, can not exceed the length of the
+         *               text
+         * @param isRtl true if the text is in RTL context, otherwise false.
+         */
+        public @NonNull Builder appendStyleRun(@NonNull Paint paint,
+                @Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length,
+                boolean isRtl) {
             Preconditions.checkNotNull(paint);
             Preconditions.checkArgument(length > 0, "length can not be negative");
             final int end = mCurrentOffset + length;
             Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
-            nAddStyleRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, isRtl);
+            int lbStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakStyle() :
+                    LineBreakConfig.LINE_BREAK_STYLE_NONE;
+            int lbWordStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakWordStyle() :
+                    LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+            nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle,
+                    mCurrentOffset, end, isRtl);
             mCurrentOffset = end;
             return this;
         }
@@ -423,12 +447,16 @@
          *
          * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
          * @param paintPtr The native paint pointer to be applied.
+         * @param lineBreakStyle The line break style(lb) of the text.
+         * @param lineBreakWordStyle The line break word style(lw) of the text.
          * @param start The start offset in the copied buffer.
          * @param end The end offset in the copied buffer.
          * @param isRtl True if the text is RTL.
          */
         private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
                                                 /* Non Zero */ long paintPtr,
+                                                int lineBreakStyle,
+                                                int lineBreakWordStyle,
                                                 @IntRange(from = 0) int start,
                                                 @IntRange(from = 0) int end,
                                                 boolean isRtl);
diff --git a/identity/java/android/security/identity/CredentialDataRequest.java b/identity/java/android/security/identity/CredentialDataRequest.java
new file mode 100644
index 0000000..2a47a02
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataRequest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An object representing a request for credential data.
+ */
+public class CredentialDataRequest {
+    CredentialDataRequest() {}
+
+    /**
+     * Gets the device-signed entries to request.
+     *
+     * @return the device-signed entries to request.
+     */
+    public @NonNull Map<String, Collection<String>> getDeviceSignedEntriesToRequest() {
+        return mDeviceSignedEntriesToRequest;
+    }
+
+    /**
+     * Gets the issuer-signed entries to request.
+     *
+     * @return the issuer-signed entries to request.
+     */
+    public @NonNull Map<String, Collection<String>> getIssuerSignedEntriesToRequest() {
+        return mIssuerSignedEntriesToRequest;
+    }
+
+    /**
+     * Gets whether to allow using an authentication key which use count has been exceeded.
+     *
+     * <p>By default this is set to true.
+     *
+     * @return whether to allow using an authentication key which use
+     *         count has been exceeded if no other key is available.
+     */
+    public boolean isAllowUsingExhaustedKeys() {
+        return mAllowUsingExhaustedKeys;
+    }
+
+    /**
+     * Gets whether to allow using an authentication key which is expired.
+     *
+     * <p>By default this is set to false.
+     *
+     * @return whether to allow using an authentication key which is
+     *         expired if no other key is available.
+     */
+    public boolean isAllowUsingExpiredKeys() {
+        return mAllowUsingExpiredKeys;
+    }
+
+    /**
+     * Gets whether to increment the use-count for the authentication key used.
+     *
+     * <p>By default this is set to true.
+     *
+     * @return whether to increment the use count of the authentication key used.
+     */
+    public boolean isIncrementUseCount() {
+        return mIncrementUseCount;
+    }
+
+    /**
+     * Gets the request message CBOR.
+     *
+     * <p>This data structure is described in the documentation for the
+     * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+     *
+     * @return the request message CBOR as described above.
+     */
+    public @Nullable byte[] getRequestMessage() {
+        return mRequestMessage;
+    }
+
+    /**
+     * Gets the reader signature.
+     *
+     * <p>This data structure is described in the documentation for the
+     * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+     *
+     * @return a {@code COSE_Sign1} structure as described above.
+     */
+    public @Nullable byte[] getReaderSignature() {
+        return mReaderSignature;
+    }
+
+    Map<String, Collection<String>> mDeviceSignedEntriesToRequest = new LinkedHashMap<>();
+    Map<String, Collection<String>> mIssuerSignedEntriesToRequest = new LinkedHashMap<>();
+    boolean mAllowUsingExhaustedKeys = true;
+    boolean mAllowUsingExpiredKeys = false;
+    boolean mIncrementUseCount = true;
+    byte[] mRequestMessage = null;
+    byte[] mReaderSignature = null;
+
+    /**
+     * A builder for {@link CredentialDataRequest}.
+     */
+    public static final class Builder {
+        private CredentialDataRequest mData;
+
+        /**
+         * Creates a new builder.
+         */
+        public Builder() {
+            mData = new CredentialDataRequest();
+        }
+
+        /**
+         * Sets the device-signed entries to request.
+         *
+         * @param entriesToRequest the device-signed entries to request.
+         */
+        public @NonNull Builder setDeviceSignedEntriesToRequest(
+                @NonNull Map<String, Collection<String>> entriesToRequest) {
+            mData.mDeviceSignedEntriesToRequest = entriesToRequest;
+            return this;
+        }
+
+        /**
+         * Sets the issuer-signed entries to request.
+         *
+         * @param entriesToRequest the issuer-signed entries to request.
+         * @return the builder.
+         */
+        public @NonNull Builder setIssuerSignedEntriesToRequest(
+                @NonNull Map<String, Collection<String>> entriesToRequest) {
+            mData.mIssuerSignedEntriesToRequest = entriesToRequest;
+            return this;
+        }
+
+        /**
+         * Sets whether to allow using an authentication key which use count has been exceeded.
+         *
+         * By default this is set to true.
+         *
+         * @param allowUsingExhaustedKeys whether to allow using an authentication key which use
+         *                                count has been exceeded if no other key is available.
+         * @return the builder.
+         */
+        public @NonNull Builder setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+            mData.mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+            return this;
+        }
+
+        /**
+         * Sets whether to allow using an authentication key which is expired.
+         *
+         * By default this is set to false.
+         *
+         * @param allowUsingExpiredKeys whether to allow using an authentication key which is
+         *                              expired if no other key is available.
+         * @return the builder.
+         */
+        public @NonNull Builder setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+            mData.mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+            return this;
+        }
+
+        /**
+         * Sets whether to increment the use-count for the authentication key used.
+         *
+         * By default this is set to true.
+         *
+         * @param incrementUseCount whether to increment the use count of the authentication
+         *                          key used.
+         * @return the builder.
+         */
+        public @NonNull Builder setIncrementUseCount(boolean incrementUseCount) {
+            mData.mIncrementUseCount = incrementUseCount;
+            return this;
+        }
+
+        /**
+         * Sets the request message CBOR.
+         *
+         * <p>This data structure is described in the documentation for the
+         * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+         *
+         * @param requestMessage the request message CBOR as described above.
+         * @return the builder.
+         */
+        public @NonNull Builder setRequestMessage(@NonNull byte[] requestMessage) {
+            mData.mRequestMessage = requestMessage;
+            return this;
+        }
+
+        /**
+         * Sets the reader signature.
+         *
+         * <p>This data structure is described in the documentation for the
+         * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+         *
+         * @param readerSignature a {@code COSE_Sign1} structure as described above.
+         * @return the builder.
+         */
+        public @NonNull Builder setReaderSignature(@NonNull byte[] readerSignature) {
+            mData.mReaderSignature = readerSignature;
+            return this;
+        }
+
+        /**
+         * Finishes building a {@link CredentialDataRequest}.
+         *
+         * @return the {@link CredentialDataRequest} object.
+         */
+        public @NonNull CredentialDataRequest build() {
+            return mData;
+        }
+    }
+}
diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java
new file mode 100644
index 0000000..beb03af
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataResult.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.util.Collection;
+
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested in a {@link PresentationSession}.
+ */
+public abstract class CredentialDataResult {
+    /**
+     * @hide
+     */
+    protected CredentialDataResult() {}
+
+    /**
+     * Returns a CBOR structure containing the retrieved device-signed data.
+     *
+     * <p>This structure - along with the session transcript - may be cryptographically
+     * authenticated to prove to the reader that the data is from a trusted credential and
+     * {@link #getDeviceMac()} can be used to get a MAC.
+     *
+     * <p>The CBOR structure which is cryptographically authenticated is the
+     * {@code DeviceAuthenticationBytes} structure according to the following
+     * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
+     *
+     * <pre>
+     *   DeviceAuthentication = [
+     *     "DeviceAuthentication",
+     *     SessionTranscript,
+     *     DocType,
+     *     DeviceNameSpacesBytes
+     *   ]
+     *
+     *   DocType = tstr
+     *   SessionTranscript = any
+     *   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+     *   DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+     * </pre>
+     *
+     * <p>where
+     *
+     * <pre>
+     *   DeviceNameSpaces = {
+     *     * NameSpace => DeviceSignedItems
+     *   }
+     *
+     *   DeviceSignedItems = {
+     *     + DataItemName => DataItemValue
+     *   }
+     *
+     *   NameSpace = tstr
+     *   DataItemName = tstr
+     *   DataItemValue = any
+     * </pre>
+     *
+     * <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
+     * as defined above.
+     *
+     * @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
+     */
+    public abstract @NonNull byte[] getDeviceNameSpaces();
+
+    /**
+     * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
+     * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
+     * is from a trusted credential.
+     *
+     * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
+     * produced by using the key agreement and key derivation function from the ciphersuite
+     * with the authentication private key and the reader ephemeral public key to compute a
+     * shared message authentication code (MAC) key, then using the MAC function from the
+     * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
+     * ISO/IEC 18013-5 for details of this operation.
+     *
+     * <p>If the session transcript or reader ephemeral key wasn't set on the {@link
+     * PresentationSession} used to obtain this data no message authencation code will be produced
+     * and this method will return {@code null}.
+     *
+     * @return A COSE_Mac0 structure with the message authentication code as described above
+     *         or {@code null} if the conditions specified above are not met.
+     */
+    public abstract @Nullable byte[] getDeviceMac();
+
+    /**
+     * Returns the static authentication data associated with the dynamic authentication
+     * key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
+     *
+     * @return The static authentication data associated with dynamic authentication key used to
+     * MAC the data.
+     */
+    public abstract @NonNull byte[] getStaticAuthenticationData();
+
+    /**
+     * Gets the device-signed entries that was returned.
+     *
+     * @return an object to examine the entries returned.
+     */
+    public abstract @NonNull Entries getDeviceSignedEntries();
+
+    /**
+     * Gets the issuer-signed entries that was returned.
+     *
+     * @return an object to examine the entries returned.
+     */
+    public abstract @NonNull Entries getIssuerSignedEntries();
+
+    /**
+     * A class for representing data elements returned.
+     */
+    public interface Entries {
+        /** Value was successfully retrieved. */
+        int STATUS_OK = 0;
+
+        /** The entry does not exist. */
+        int STATUS_NO_SUCH_ENTRY = 1;
+
+        /** The entry was not requested. */
+        int STATUS_NOT_REQUESTED = 2;
+
+        /** The entry wasn't in the request message. */
+        int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+        /** The entry was not retrieved because user authentication failed. */
+        int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+        /** The entry was not retrieved because reader authentication failed. */
+        int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+        /**
+         * The entry was not retrieved because it was configured without any access
+         * control profile.
+         */
+        int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+        /**
+         * Gets the names of namespaces with retrieved entries.
+         *
+         * @return collection of name of namespaces containing retrieved entries. May be empty if no
+         *     data was retrieved.
+         */
+        @NonNull Collection<String> getNamespaces();
+
+        /**
+         * Get the names of all requested entries in a name space.
+         *
+         * <p>This includes the name of entries that wasn't successfully retrieved.
+         *
+         * @param namespaceName the namespace name to get entries for.
+         * @return A collection of names for the given namespace or the empty collection if no
+         *   entries was returned for the given name space.
+         */
+        @NonNull Collection<String> getEntryNames(@NonNull String namespaceName);
+
+        /**
+         * Get the names of all entries that was successfully retrieved from a name space.
+         *
+         * <p>This only return entries for which {@link #getStatus(String, String)} will return
+         * {@link #STATUS_OK}.
+         *
+         * @param namespaceName the namespace name to get entries for.
+         * @return The entries in the given namespace that were successfully rerieved or the
+         *   empty collection if no entries was returned for the given name space.
+         */
+        @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName);
+
+        /**
+         * Gets the status of an entry.
+         *
+         * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link
+         * #STATUS_NO_SUCH_ENTRY} if the given entry wasn't retrieved, {@link
+         * #STATUS_NOT_REQUESTED} if it wasn't requested, {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if
+         * the request message was set but the entry wasn't present in the request message, {@link
+         * #STATUS_USER_AUTHENTICATION_FAILED} if the value wasn't retrieved because the necessary
+         * user authentication wasn't performed, {@link #STATUS_READER_AUTHENTICATION_FAILED} if
+         * the supplied reader certificate chain didn't match the set of certificates the entry was
+         * provisioned with, or {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was
+         * configured without any access control profiles.
+         *
+         * @param namespaceName the namespace name of the entry.
+         * @param name the name of the entry to get the value for.
+         * @return the status indicating whether the value was retrieved and if not, why.
+         */
+        @Status int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+        /**
+         * Gets the raw CBOR data for the value of an entry.
+         *
+         * <p>This should only be called on an entry for which the {@link #getStatus(String,
+         * String)} method returns {@link #STATUS_OK}.
+         *
+         * @param namespaceName the namespace name of the entry.
+         * @param name the name of the entry to get the value for.
+         * @return the raw CBOR data or {@code null} if no entry with the given name exists.
+         */
+        @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
+
+        /**
+         * The type of the entry status.
+         * @hide
+         */
+        @Retention(SOURCE)
+        @IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED,
+                    STATUS_NOT_IN_REQUEST_MESSAGE, STATUS_USER_AUTHENTICATION_FAILED,
+                    STATUS_READER_AUTHENTICATION_FAILED, STATUS_NO_ACCESS_CONTROL_PROFILES})
+                    @interface Status {}
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
new file mode 100644
index 0000000..7afe3d4
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreCredentialDataResult extends CredentialDataResult {
+
+    ResultData mDeviceSignedResult;
+    ResultData mIssuerSignedResult;
+    CredstoreEntries mDeviceSignedEntries;
+    CredstoreEntries mIssuerSignedEntries;
+
+    CredstoreCredentialDataResult(ResultData deviceSignedResult, ResultData issuerSignedResult) {
+        mDeviceSignedResult = deviceSignedResult;
+        mIssuerSignedResult = issuerSignedResult;
+        mDeviceSignedEntries = new CredstoreEntries(deviceSignedResult);
+        mIssuerSignedEntries = new CredstoreEntries(issuerSignedResult);
+    }
+
+    @Override
+    public @NonNull byte[] getDeviceNameSpaces() {
+        return mDeviceSignedResult.getAuthenticatedData();
+    }
+
+    @Override
+    public @Nullable byte[] getDeviceMac() {
+        return mDeviceSignedResult.getMessageAuthenticationCode();
+    }
+
+    @Override
+    public @NonNull byte[] getStaticAuthenticationData() {
+        return mDeviceSignedResult.getStaticAuthenticationData();
+    }
+
+    @Override
+    public @NonNull CredentialDataResult.Entries getDeviceSignedEntries() {
+        return mDeviceSignedEntries;
+    }
+
+    @Override
+    public @NonNull CredentialDataResult.Entries getIssuerSignedEntries() {
+        return mIssuerSignedEntries;
+    }
+
+    static class CredstoreEntries implements CredentialDataResult.Entries {
+        ResultData mResultData;
+
+        CredstoreEntries(ResultData resultData) {
+            mResultData = resultData;
+        }
+
+        @Override
+        public @NonNull Collection<String> getNamespaces() {
+            return mResultData.getNamespaces();
+        }
+
+        @Override
+        public @NonNull Collection<String> getEntryNames(@NonNull String namespaceName) {
+            Collection<String> ret = mResultData.getEntryNames(namespaceName);
+            if (ret == null) {
+                ret = new LinkedList<String>();
+            }
+            return ret;
+        }
+
+        @Override
+        public @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+            Collection<String> ret = mResultData.getRetrievedEntryNames(namespaceName);
+            if (ret == null) {
+                ret = new LinkedList<String>();
+            }
+            return ret;
+        }
+
+        @Override
+        @Status
+        public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+            return mResultData.getStatus(namespaceName, name);
+        }
+
+        @Override
+        public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+            return mResultData.getEntry(namespaceName, name);
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 6398cee..8e01105 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -58,14 +58,17 @@
     private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
     private Context mContext;
     private ICredential mBinder;
+    private CredstorePresentationSession mSession;
 
     CredstoreIdentityCredential(Context context, String credentialName,
             @IdentityCredentialStore.Ciphersuite int cipherSuite,
-            ICredential binder) {
+            ICredential binder,
+            @Nullable CredstorePresentationSession session) {
         mContext = context;
         mCredentialName = credentialName;
         mCipherSuite = cipherSuite;
         mBinder = binder;
+        mSession = session;
     }
 
     private KeyPair mEphemeralKeyPair = null;
@@ -239,6 +242,7 @@
 
     private boolean mAllowUsingExhaustedKeys = true;
     private boolean mAllowUsingExpiredKeys = false;
+    private boolean mIncrementKeyUsageCount = true;
 
     @Override
     public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
@@ -250,6 +254,11 @@
         mAllowUsingExpiredKeys = allowUsingExpiredKeys;
     }
 
+    @Override
+    public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+        mIncrementKeyUsageCount = incrementKeyUsageCount;
+    }
+
     private boolean mOperationHandleSet = false;
     private long mOperationHandle = 0;
 
@@ -264,7 +273,8 @@
         if (!mOperationHandleSet) {
             try {
                 mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
-                        mAllowUsingExpiredKeys);
+                                                         mAllowUsingExpiredKeys,
+                                                         mIncrementKeyUsageCount);
                 mOperationHandleSet = true;
             } catch (android.os.RemoteException e) {
                 throw new RuntimeException("Unexpected RemoteException ", e);
@@ -315,7 +325,8 @@
                 sessionTranscript != null ? sessionTranscript : new byte[0],
                 readerSignature != null ? readerSignature : new byte[0],
                 mAllowUsingExhaustedKeys,
-                mAllowUsingExpiredKeys);
+                mAllowUsingExpiredKeys,
+                mIncrementKeyUsageCount);
         } catch (android.os.RemoteException e) {
             throw new RuntimeException("Unexpected RemoteException ", e);
         } catch (android.os.ServiceSpecificException e) {
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index d8d4742..fb0880c 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -126,7 +126,8 @@
             ICredential credstoreCredential;
             credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
             return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
-                    credstoreCredential);
+                    credstoreCredential,
+                    null);
         } catch (android.os.RemoteException e) {
             throw new RuntimeException("Unexpected RemoteException ", e);
         } catch (android.os.ServiceSpecificException e) {
@@ -162,4 +163,23 @@
                     + e.errorCode, e);
         }
     }
+
+    @Override
+    public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException {
+        try {
+            ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
+            return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
+                throw new CipherSuiteNotSupportedException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
 }
diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java
new file mode 100644
index 0000000..e3c6689
--- /dev/null
+++ b/identity/java/android/security/identity/CredstorePresentationSession.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class CredstorePresentationSession extends PresentationSession {
+    private static final String TAG = "CredstorePresentationSession";
+
+    private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+    private Context mContext;
+    private CredstoreIdentityCredentialStore mStore;
+    private ISession mBinder;
+    private Map<String, CredstoreIdentityCredential> mCredentialCache = new LinkedHashMap<>();
+    private KeyPair mEphemeralKeyPair = null;
+    private byte[] mSessionTranscript = null;
+    private boolean mOperationHandleSet = false;
+    private long mOperationHandle = 0;
+
+    CredstorePresentationSession(Context context,
+            @IdentityCredentialStore.Ciphersuite int cipherSuite,
+            CredstoreIdentityCredentialStore store,
+            ISession binder) {
+        mContext = context;
+        mCipherSuite = cipherSuite;
+        mStore = store;
+        mBinder = binder;
+    }
+
+    private void ensureEphemeralKeyPair() {
+        if (mEphemeralKeyPair != null) {
+            return;
+        }
+        try {
+            // This PKCS#12 blob is generated in credstore, using BoringSSL.
+            //
+            // The main reason for this convoluted approach and not just sending the decomposed
+            // key-pair is that this would require directly using (device-side) BouncyCastle which
+            // is tricky due to various API hiding efforts. So instead we have credstore generate
+            // this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
+            // doesn't support not using encryption when building a PKCS#12 blob).
+            //
+            byte[] pkcs12 = mBinder.getEphemeralKeyPair();
+            String alias = "ephemeralKey";
+            char[] password = {};
+
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
+            ks.load(bais, password);
+            PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
+
+            Certificate cert = ks.getCertificate(alias);
+            PublicKey pubKey = cert.getPublicKey();
+
+            mEphemeralKeyPair = new KeyPair(pubKey, privKey);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        } catch (android.os.RemoteException
+                | KeyStoreException
+                | CertificateException
+                | UnrecoverableKeyException
+                | NoSuchAlgorithmException
+                | IOException e) {
+            throw new RuntimeException("Unexpected exception ", e);
+        }
+    }
+
+    @Override
+    public @NonNull KeyPair getEphemeralKeyPair() {
+        ensureEphemeralKeyPair();
+        return mEphemeralKeyPair;
+    }
+
+    @Override
+    public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException {
+        try {
+            byte[] uncompressedForm =
+                    Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
+            mBinder.setReaderEphemeralPublicKey(uncompressedForm);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public void setSessionTranscript(@NonNull byte[] sessionTranscript) {
+        try {
+            mBinder.setSessionTranscript(sessionTranscript);
+            mSessionTranscript = sessionTranscript;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public @Nullable CredentialDataResult getCredentialData(@NonNull String credentialName,
+                                                            @NonNull CredentialDataRequest request)
+            throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+            InvalidRequestMessageException, EphemeralPublicKeyNotFoundException {
+        try {
+            // Cache the IdentityCredential to satisfy the property that AuthKey usage counts are
+            // incremented on only the _first_ getCredentialData() call.
+            //
+            CredstoreIdentityCredential credential = mCredentialCache.get(credentialName);
+            if (credential == null) {
+                ICredential credstoreCredential =
+                    mBinder.getCredentialForPresentation(credentialName);
+                credential = new CredstoreIdentityCredential(mContext, credentialName,
+                                                             mCipherSuite, credstoreCredential,
+                                                             this);
+                mCredentialCache.put(credentialName, credential);
+
+                credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
+                credential.setAllowUsingExpiredKeys(request.isAllowUsingExpiredKeys());
+                credential.setIncrementKeyUsageCount(request.isIncrementUseCount());
+            }
+
+            ResultData deviceSignedResult = credential.getEntries(
+                    request.getRequestMessage(),
+                    request.getDeviceSignedEntriesToRequest(),
+                    mSessionTranscript,
+                    request.getReaderSignature());
+
+            // By design this second getEntries() call consumes the same auth-key.
+
+            ResultData issuerSignedResult = credential.getEntries(
+                    request.getRequestMessage(),
+                    request.getIssuerSignedEntriesToRequest(),
+                    mSessionTranscript,
+                    request.getReaderSignature());
+
+            return new CredstoreCredentialDataResult(deviceSignedResult, issuerSignedResult);
+
+        } catch (SessionTranscriptMismatchException e) {
+            throw new RuntimeException("Unexpected ", e);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+                return null;
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    @Override
+    public long getCredstoreOperationHandle() {
+        if (!mOperationHandleSet) {
+            try {
+                mOperationHandle = mBinder.getAuthChallenge();
+                mOperationHandleSet = true;
+            } catch (android.os.RemoteException e) {
+                throw new RuntimeException("Unexpected RemoteException ", e);
+            } catch (android.os.ServiceSpecificException e) {
+                if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+                    // The NoAuthenticationKeyAvailableException will be thrown when
+                    // the caller proceeds to call getEntries().
+                }
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+        return mOperationHandle;
+    }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 1e68585..cdf746f 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -48,7 +48,9 @@
      * encryption".
      *
      * @return ephemeral key pair to use to establish a secure channel with a reader.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public @NonNull abstract KeyPair createEphemeralKeyPair();
 
     /**
@@ -58,7 +60,9 @@
      * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
      *                                 establish a secure session.
      * @throws InvalidKeyException if the given key is invalid.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
             throws InvalidKeyException;
 
@@ -72,7 +76,10 @@
      *
      * @param messagePlaintext unencrypted message to encrypt.
      * @return encrypted message.
+     * @deprecated Applications should use {@link PresentationSession} and
+     *             implement encryption/decryption themselves.
      */
+    @Deprecated
     public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
 
     /**
@@ -86,7 +93,10 @@
      * @param messageCiphertext encrypted message to decrypt.
      * @return decrypted message.
      * @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+     * @deprecated Applications should use {@link PresentationSession} and
+     *             implement encryption/decryption themselves.
      */
+    @Deprecated
     public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
             throws MessageDecryptionException;
 
@@ -111,7 +121,9 @@
      *
      * @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
      *                                has been exceeded if no other key is available.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
 
     /**
@@ -128,12 +140,36 @@
      *
      * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
      *                              has been exceeded if no other key is available.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
         throw new UnsupportedOperationException();
     }
 
     /**
+     * @hide
+     *
+     * Sets whether the usage count of an authentication key should be increased. This must be
+     * called prior to calling
+     * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this object.
+     *
+     * <p>By default this is set to true.
+     *
+     * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+     * fails with {@link UnsupportedOperationException}. See
+     * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+     * feature versions.
+     *
+     * @param incrementKeyUsageCount whether the usage count of the key should be increased.
+     * @deprecated Use {@link PresentationSession} instead.
+     */
+    public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
      * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
      * operation handle.
      *
@@ -149,15 +185,19 @@
      * by using the {@link ResultData#getStatus(String, String)} method on each of the requested
      * entries.
      *
-     * <p>It is the responsibility of the calling application to know if authentication is needed
-     * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
-     * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
-     * references this object. If needed, this must be done before calling
-     * {@link #getEntries(byte[], Map, byte[], byte[])}.
-     *
      * <p>It is permissible to call this method multiple times using the same instance but if this
      * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
      * not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
+     * Additionally, if this is done the same auth-key will be used.
+     *
+     * <p>The application should not make any assumptions on whether user authentication is needed.
+     * Instead, the application should request the data elements values first and then examine
+     * the returned {@link ResultData}. If {@link ResultData#STATUS_USER_AUTHENTICATION_FAILED}
+     * is returned the application should get a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+     * authentication the application may call {@link #getEntries(byte[], Map, byte[], byte[])}
+     * again.
      *
      * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
      * from the verifier. The content can be defined in the way appropriate for the credential, but
@@ -269,7 +309,9 @@
      * @throws InvalidRequestMessageException         if the requestMessage is malformed.
      * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
      *                                                the session transcript.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract @NonNull ResultData getEntries(
             @Nullable byte[] requestMessage,
             @NonNull Map<String, Collection<String>> entriesToRequest,
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 6ccd0e8..dbb8aaa 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -209,6 +209,25 @@
     @Deprecated
     public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
 
+    /**
+     * Creates a new presentation session.
+     *
+     * <p>This method gets an object to be used for interaction with a remote verifier for
+     * presentation of one or more credentials.
+     *
+     * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+     * fails with {@link UnsupportedOperationException}. See
+     * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+     * feature versions.
+     *
+     * @param cipherSuite    the cipher suite to use for communicating with the verifier.
+     * @return The presentation session.
+     */
+    public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java
new file mode 100644
index 0000000..afaafce
--- /dev/null
+++ b/identity/java/android/security/identity/PresentationSession.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+/**
+ * Class for presenting multiple documents to a remote verifier.
+ *
+ * Use {@link IdentityCredentialStore#createPresentationSession(int)} to create a {@link
+ * PresentationSession} instance.
+ */
+public abstract class PresentationSession {
+    /**
+     * @hide
+     */
+    protected PresentationSession() {}
+
+    /**
+     * Gets the ephemeral key pair to use to establish a secure channel with the verifier.
+     *
+     * <p>Applications should use this key-pair for the communications channel with the verifier
+     * using a protocol / cipher-suite appropriate for the application. One example of such a
+     * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5.
+     *
+     * <p>The ephemeral key pair is tied to the {@link PresentationSession} instance so subsequent
+     * calls to this method will return the same key-pair.
+     *
+     * @return ephemeral key pair to use to establish a secure channel with a reader.
+     */
+    public @NonNull abstract KeyPair getEphemeralKeyPair();
+
+    /**
+     * Set the ephemeral public key provided by the verifier.
+     *
+     * <p>If called, this must be called before any calls to
+     * {@link #getCredentialData(String, CredentialDataRequest)}.
+     *
+     * <p>This method can only be called once per {@link PresentationSession} instance.
+     *
+     * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
+     *                                 establish a secure session.
+     * @throws InvalidKeyException if the given key is invalid.
+     */
+    public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException;
+
+    /**
+     * Set the session transcript.
+     *
+     * <p>If called, this must be called before any calls to
+     * {@link #getCredentialData(String, CredentialDataRequest)}.
+     *
+     * <p>The X and Y coordinates of the public part of the key-pair returned by {@link
+     * #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR.  Each of
+     * these coordinates must appear encoded with the most significant bits first and use the exact
+     * amount of bits indicated by the key size of the ephemeral keys. For example, if the
+     * ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
+     * the most significant bits first must appear somewhere and ditto for the 32 bytes for the Y
+     * coordinate.
+     *
+     * <p>This method can only be called once per {@link PresentationSession} instance.
+     *
+     * @param sessionTranscript the session transcript.
+     */
+    public abstract void setSessionTranscript(@NonNull byte[] sessionTranscript);
+
+    /**
+     * Retrieves data from a named credential in the current presentation session.
+     *
+     * <p>If an access control check fails for one of the requested entries or if the entry
+     * doesn't exist, the entry is simply not returned. The application can detect this
+     * by using the {@link CredentialDataResult.Entries#getStatus(String, String)} method on
+     * each of the requested entries.
+     *
+     * <p>The application should not make any assumptions on whether user authentication is needed.
+     * Instead, the application should request the data elements values first and then examine
+     * the returned {@link CredentialDataResult.Entries}. If
+     * {@link CredentialDataResult.Entries#STATUS_USER_AUTHENTICATION_FAILED} is returned the
+     * application should get a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+     * authentication the application may call
+     * {@link #getCredentialData(String, CredentialDataRequest)} again.
+     *
+     * <p>It is permissible to call this method multiple times using the same credential name.
+     * If this is done the same auth-key will be used.
+     *
+     * <p>If the reader signature is set in the request parameter (via the
+     * {@link CredentialDataRequest.Builder#setReaderSignature(byte[])} method) it must contain
+     * the bytes of a {@code COSE_Sign1} structure as defined in RFC 8152. For the payload
+     * {@code nil} shall be used and the detached payload is the {@code ReaderAuthenticationBytes}
+     * CBOR described below.
+     * <pre>
+     *     ReaderAuthentication = [
+     *       "ReaderAuthentication",
+     *       SessionTranscript,
+     *       ItemsRequestBytes
+     *     ]
+     *
+     *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+     *
+     *     ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+     * </pre>
+     *
+     * <p>where {@code ItemsRequestBytes} are the bytes of the request message set in
+     * the request parameter (via the
+     * {@link CredentialDataRequest.Builder#setRequestMessage(byte[])} method).
+     *
+     * <p>The public key corresponding to the key used to make the signature, can be found in the
+     * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
+     * described in
+     * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-08">draft-ietf-cose-x509-08</a>).
+     * There will be at least one certificate in said element and there may be more (and if so,
+     * each certificate must be signed by its successor).
+     *
+     * <p>Data elements protected by reader authentication are returned if, and only if,
+     * {@code requestMessage} is signed by the top-most certificate in the reader's certificate
+     * chain, and the data element is configured with an {@link AccessControlProfile} configured
+     * with an X.509 certificate for a key which appear in the certificate chain.
+     *
+     * <p>Note that the request message CBOR is used only for enforcing reader authentication, it's
+     * not used for determining which entries this API will return. The application is expected to
+     * have parsed the request message and filtered it according to user preference and/or consent.
+     *
+     * @param credentialName the name of the credential to retrieve.
+     * @param request the data to retrieve from the credential
+     * @return If the credential wasn't found, returns null. Otherwise a
+     *         {@link CredentialDataResult} object containing entry data organized by namespace and
+     *         a cryptographically authenticated representation of the same data, bound to the
+     *         current session.
+     * @throws NoAuthenticationKeyAvailableException  if authentication keys were never
+     *                                                provisioned for the credential or if they
+     *                                                are expired or exhausted their use-count.
+     * @throws InvalidRequestMessageException         if the requestMessage is malformed.
+     * @throws InvalidReaderSignatureException        if the reader signature is invalid, or it
+     *                                                doesn't contain a certificate chain, or if
+     *                                                the signature failed to validate.
+     * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
+     *                                                the session transcript.
+     */
+    public abstract @Nullable CredentialDataResult getCredentialData(
+            @NonNull String credentialName, @NonNull CredentialDataRequest request)
+            throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+            InvalidRequestMessageException, EphemeralPublicKeyNotFoundException;
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    public abstract long getCredstoreOperationHandle();
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 71860d2..d46f985 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -28,7 +28,10 @@
 /**
  * An object that contains the result of retrieving data from a credential. This is used to return
  * data requested from a {@link IdentityCredential}.
+ *
+ * @deprecated Use {@link PresentationSession} instead.
  */
+@Deprecated
 public abstract class ResultData {
 
     /** Value was successfully retrieved. */
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 919a93b..05fb4c3 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.security.keystore.KeyProperties;
 import android.security.maintenance.IKeystoreMaintenance;
 import android.system.keystore2.Domain;
 import android.system.keystore2.KeyDescriptor;
@@ -157,6 +158,11 @@
      * Migrates a key given by the source descriptor to the location designated by the destination
      * descriptor.
      *
+     * If Domain::APP is selected in either source or destination, nspace must be set to
+     * {@link KeyProperties#NAMESPACE_APPLICATION}, implying the caller's UID.
+     * If the caller has the MIGRATE_ANY_KEY permission, Domain::APP may be used with
+     * other nspace values which then indicates the UID of a different application.
+     *
      * @param source - The key to migrate may be specified by Domain.APP, Domain.SELINUX, or
      *               Domain.KEY_ID. The caller needs the permissions use, delete, and grant for the
      *               source namespace.
@@ -183,4 +189,20 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * @see IKeystoreMaintenance#listEntries(int, long)
+     */
+    @Nullable
+    public static KeyDescriptor[] listEntries(int domain, long nspace) {
+        try {
+            return getService().listEntries(domain, nspace);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "listEntries failed", e);
+            return null;
+        } catch (Exception e) {
+            Log.e(TAG, "Can not connect to keystore", e);
+            return null;
+        }
+    }
 }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index a954344..8811a7f 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -20,7 +20,6 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.security.maintenance.UserState;
-import android.system.keystore2.Domain;
 
 /**
  * @hide This should not be made public in its present form because it
@@ -120,15 +119,6 @@
     }
 
     /**
-     * Forwards the request to clear a UID to Keystore 2.0.
-     * @hide
-     */
-    public boolean clearUid(int uid) {
-        return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0;
-    }
-
-
-    /**
      * Add an authentication record to the keystore authorization table.
      *
      * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a6aa4f2..54bab4a 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -276,7 +276,7 @@
  *         "HMACSHA256", sharedSecret, salt, info.toByteArray(), 32));
  * byte[] associatedData = {};
  * return key.decrypt(ciphertext, associatedData);
- * }
+ * }</pre>
  */
 public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
     private static final X500Principal DEFAULT_ATTESTATION_CERT_SUBJECT =
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 df751fc..180c772 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -79,22 +79,23 @@
     }
 
     @Override
-    public void registerOrganizer() {
-        if (mAnimationController != null) {
-            throw new IllegalStateException("Must unregister the organizer before re-register.");
+    public void unregisterOrganizer() {
+        stopOverrideSplitAnimation();
+        mAnimationController = null;
+        super.unregisterOrganizer();
+    }
+
+    void startOverrideSplitAnimation() {
+        if (mAnimationController == null) {
+            mAnimationController = new TaskFragmentAnimationController(this);
         }
-        super.registerOrganizer();
-        mAnimationController = new TaskFragmentAnimationController(this);
         mAnimationController.registerRemoteAnimations();
     }
 
-    @Override
-    public void unregisterOrganizer() {
+    void stopOverrideSplitAnimation() {
         if (mAnimationController != null) {
             mAnimationController.unregisterRemoteAnimations();
-            mAnimationController = null;
         }
-        super.unregisterOrganizer();
     }
 
     /**
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 b8e8b01..8f368c2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -33,6 +33,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -63,6 +64,9 @@
     private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
     private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
 
+    // We currently only support split activity embedding within the one root Task.
+    private final Rect mParentBounds = new Rect();
+
     public SplitController() {
         mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
         ActivityThread activityThread = ActivityThread.currentActivityThread();
@@ -79,6 +83,7 @@
     public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
         mSplitRules.clear();
         mSplitRules.addAll(rules);
+        updateAnimationOverride();
     }
 
     @NonNull
@@ -158,6 +163,7 @@
     @Override
     public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
             @NonNull Configuration parentConfig) {
+        onParentBoundsMayChange(parentConfig.windowConfiguration.getBounds());
         TaskFragmentContainer container = getContainer(fragmentToken);
         if (container != null) {
             mPresenter.updateContainer(container);
@@ -165,6 +171,51 @@
         }
     }
 
+    private void onParentBoundsMayChange(Activity activity) {
+        if (activity.isFinishing()) {
+            return;
+        }
+
+        onParentBoundsMayChange(mPresenter.getParentContainerBounds(activity));
+    }
+
+    private void onParentBoundsMayChange(Rect parentBounds) {
+        if (!parentBounds.isEmpty() && !mParentBounds.equals(parentBounds)) {
+            mParentBounds.set(parentBounds);
+            updateAnimationOverride();
+        }
+    }
+
+    /**
+     * Updates if we should override transition animation. We only want to override if the Task
+     * bounds is large enough for at least one split rule.
+     */
+    private void updateAnimationOverride() {
+        if (mParentBounds.isEmpty()) {
+            // We don't know about the parent bounds yet.
+            return;
+        }
+
+        // Check if the parent container bounds can support any split rule.
+        boolean supportSplit = false;
+        for (EmbeddingRule rule : mSplitRules) {
+            if (!(rule instanceof SplitRule)) {
+                continue;
+            }
+            if (mPresenter.shouldShowSideBySide(mParentBounds, (SplitRule) rule)) {
+                supportSplit = true;
+                break;
+            }
+        }
+
+        // We only want to override if it supports split.
+        if (supportSplit) {
+            mPresenter.startOverrideSplitAnimation();
+        } else {
+            mPresenter.stopOverrideSplitAnimation();
+        }
+    }
+
     void onActivityCreated(@NonNull Activity launchedActivity) {
         handleActivityCreated(launchedActivity);
         updateCallbackIfNecessary();
@@ -180,6 +231,11 @@
         final TaskFragmentContainer currentContainer = getContainerWithActivity(
                 launchedActivity.getActivityToken());
 
+        if (currentContainer == null) {
+            // Initial check before any TaskFragment is created.
+            onParentBoundsMayChange(launchedActivity);
+        }
+
         // Check if the activity is configured to always be expanded.
         if (shouldExpand(launchedActivity, null, splitRules)) {
             if (shouldContainerBeExpanded(currentContainer)) {
@@ -257,6 +313,8 @@
             // onTaskFragmentParentInfoChanged
             return;
         }
+        // The bounds of the container may have been changed.
+        onParentBoundsMayChange(activity);
 
         // Check if activity requires a placeholder
         launchPlaceholderIfNecessary(activity);
@@ -346,7 +404,7 @@
     TaskFragmentContainer getTopActiveContainer() {
         for (int i = mContainers.size() - 1; i >= 0; i--) {
             TaskFragmentContainer container = mContainers.get(i);
-            if (!container.isFinished()) {
+            if (!container.isFinished() && container.getTopNonFinishingActivity() != null) {
                 return container;
             }
         }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
index 3c7d2de..a801dc8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -37,32 +37,42 @@
 
     private final TaskFragmentOrganizer mOrganizer;
     private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
+    private final RemoteAnimationDefinition mDefinition;
+    private boolean mIsRegister;
 
     TaskFragmentAnimationController(TaskFragmentOrganizer organizer) {
         mOrganizer = organizer;
+        mDefinition = new RemoteAnimationDefinition();
+        final RemoteAnimationAdapter animationAdapter =
+                new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
     }
 
     void registerRemoteAnimations() {
         if (DEBUG) {
             Log.v(TAG, "registerRemoteAnimations");
         }
-        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
-        final RemoteAnimationAdapter animationAdapter =
-                new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
-        definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
-        mOrganizer.registerRemoteAnimations(definition);
+        if (mIsRegister) {
+            return;
+        }
+        mOrganizer.registerRemoteAnimations(mDefinition);
+        mIsRegister = true;
     }
 
     void unregisterRemoteAnimations() {
         if (DEBUG) {
             Log.v(TAG, "unregisterRemoteAnimations");
         }
+        if (!mIsRegister) {
+            return;
+        }
         mOrganizer.unregisterRemoteAnimations();
+        mIsRegister = false;
     }
 }
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
deleted file mode 100644
index 29d9b25..0000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
deleted file mode 100644
index 70f553b..0000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="0"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
deleted file mode 100644
index 29d9b25..0000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
deleted file mode 100644
index 70f553b..0000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="0"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
new file mode 100644
index 0000000..d8f3561
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@color/tv_pip_menu_focus_border"
+        android:pathData="M7,10l5,5 5,-5H7z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
new file mode 100644
index 0000000..3e0011c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@color/tv_pip_menu_focus_border"
+        android:pathData="M14,7l-5,5 5,5V7z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
new file mode 100644
index 0000000..f6b3c72
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@color/tv_pip_menu_focus_border"
+        android:pathData="M10,17l5,-5 -5,-5v10z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
new file mode 100644
index 0000000..1a34462
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@color/tv_pip_menu_focus_border"
+        android:pathData="M7,14l5,-5 5,5H7z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
new file mode 100644
index 0000000..37f4c87
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+    <path
+        android:pathData="M11,5.83L11,10h2L13,5.83l1.83,1.83 1.41,-1.42L12,2 7.76,6.24l1.41,1.42zM17.76,7.76l-1.42,1.41L18.17,11L14,11v2h4.17l-1.83,1.83 1.42,1.41L22,12zM13,18.17L13,14h-2v4.17l-1.83,-1.83 -1.41,1.42L12,22l4.24,-4.24 -1.41,-1.42zM10,13v-2L5.83,11l1.83,-1.83 -1.42,-1.41L2,12l4.24,4.24 1.42,-1.41L5.83,13z"
+        android:fillColor="#FFFFFF" />
+
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/background_panel.xml b/libs/WindowManager/Shell/res/layout/background_panel.xml
new file mode 100644
index 0000000..c3569d8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/background_panel.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/background_panel_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center_horizontal | center_vertical"
+    android:background="@android:color/transparent">
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
index 4ac972c..44b2f45 100644
--- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -21,7 +21,7 @@
     android:orientation="vertical"
     android:clipToPadding="false"
     android:paddingEnd="@dimen/compat_hint_padding_end"
-    android:paddingBottom="5dp"
+    android:paddingBottom="8dp"
     android:clickable="true">
 
     <TextView
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index c99f3fe..dfaeeeb 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -33,8 +33,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:clipToPadding="false"
-        android:layout_marginEnd="16dp"
-        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="@dimen/compat_button_margin"
+        android:layout_marginBottom="@dimen/compat_button_margin"
         android:orientation="vertical">
 
         <ImageButton
@@ -62,8 +62,10 @@
     <ImageButton
         android:id="@+id/size_compat_restart_button"
         android:visibility="gone"
-        android:layout_width="@dimen/size_compat_button_width"
-        android:layout_height="@dimen/size_compat_button_height"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/compat_button_margin"
+        android:layout_marginBottom="@dimen/compat_button_margin"
         android:src="@drawable/size_compat_restart_button_ripple"
         android:background="@android:color/transparent"
         android:contentDescription="@string/restart_button_description"/>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 5b90c99..b56b114 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -15,57 +15,101 @@
     limitations under the License.
 -->
 <!-- Layout for TvPipMenuView -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/tv_pip_menu"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
 
-    <FrameLayout
+    <HorizontalScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_centerHorizontal="true"
+        android:gravity="center"
+        android:scrollbars="none"
+        android:layout_centerInParent="true"
+        android:layout_margin="@dimen/pip_menu_outer_space">
+
+        <LinearLayout
+            android:id="@+id/tv_pip_menu_action_buttons"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
+            android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
+            android:gravity="center"
+            android:orientation="horizontal"
+            android:alpha="0">
+
+            <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+                android:id="@+id/tv_pip_menu_fullscreen_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/pip_ic_fullscreen_white"
+                android:text="@string/pip_fullscreen" />
+
+            <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+                android:id="@+id/tv_pip_menu_move_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/pip_ic_move_white"
+                android:text="@String/pip_move" />
+
+            <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+                android:id="@+id/tv_pip_menu_close_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/pip_ic_close_white"
+                android:text="@string/pip_close" />
+
+            <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+
+        </LinearLayout>
+    </HorizontalScrollView>
+
+    <View
         android:id="@+id/tv_pip_menu_frame"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:alpha="0" >
+        android:alpha="0"
+        android:layout_margin="@dimen/pip_menu_outer_space_frame"
+        android:background="@drawable/tv_pip_menu_border"/>
 
-        <HorizontalScrollView
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_centerHorizontal="true"
-            android:gravity="center"
-            android:scrollbars="none"
-            android:requiresFadingEdge="vertical"
-            android:fadingEdgeLength="30dp">
+    <ImageView
+        android:id="@+id/tv_pip_menu_arrow_up"
+        android:layout_width="@dimen/pip_menu_arrow_size"
+        android:layout_height="@dimen/pip_menu_arrow_size"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentTop="true"
+        android:alpha="0"
+        android:elevation="@dimen/pip_menu_arrow_elevation"
+        android:src="@drawable/pip_ic_move_up" />
 
-            <LinearLayout
-                android:id="@+id/tv_pip_menu_action_buttons"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
-                android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
-                android:gravity="center"
-                android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/tv_pip_menu_arrow_right"
+        android:layout_width="@dimen/pip_menu_arrow_size"
+        android:layout_height="@dimen/pip_menu_arrow_size"
+        android:layout_centerVertical="true"
+        android:layout_alignParentRight="true"
+        android:alpha="0"
+        android:elevation="@dimen/pip_menu_arrow_elevation"
+        android:src="@drawable/pip_ic_move_right" />
 
-                <com.android.wm.shell.pip.tv.TvPipMenuActionButton
-                    android:id="@+id/tv_pip_menu_fullscreen_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:src="@drawable/pip_ic_fullscreen_white"
-                    android:text="@string/pip_fullscreen" />
+    <ImageView
+        android:id="@+id/tv_pip_menu_arrow_down"
+        android:layout_width="@dimen/pip_menu_arrow_size"
+        android:layout_height="@dimen/pip_menu_arrow_size"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentBottom="true"
+        android:alpha="0"
+        android:elevation="@dimen/pip_menu_arrow_elevation"
+        android:src="@drawable/pip_ic_move_down" />
 
-                <com.android.wm.shell.pip.tv.TvPipMenuActionButton
-                    android:id="@+id/tv_pip_menu_close_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:src="@drawable/pip_ic_close_white"
-                    android:text="@string/pip_close" />
-
-                <!-- More TvPipMenuActionButtons may be added here at runtime. -->
-
-            </LinearLayout>
-        </HorizontalScrollView>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:background="@drawable/tv_pip_menu_border"/>
-    </FrameLayout>
-</FrameLayout>
+    <ImageView
+        android:id="@+id/tv_pip_menu_arrow_left"
+        android:layout_width="@dimen/pip_menu_arrow_size"
+        android:layout_height="@dimen/pip_menu_arrow_size"
+        android:layout_centerVertical="true"
+        android:layout_alignParentLeft="true"
+        android:alpha="0"
+        android:elevation="@dimen/pip_menu_arrow_elevation"
+        android:src="@drawable/pip_ic_move_left" />
+</RelativeLayout>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 8b32b38..ec0e9ea 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
index 6ce5880..3edb8e9 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 8b898c0..646a0d3 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index fcb87c5..b1c6542 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index a9eafdd..a184fe4 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
index 4eef29e..dfc5053 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ليس هناك عنوان للبرنامج)"</string>
     <string name="pip_close" msgid="9135220303720555525">"‏إغلاق PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index a81d4e3..c58c025 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index 170b2db..352bde5 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
     <string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 45e3fdb..945f738 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
index c9f1acb..9b46d5f 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıqsız proqram)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP bağlayın"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index c17b3c4..cac983b 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
index 6fbc91b..790a6d47 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index bcf7186..791bcef 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
index d33bf99..c4df7fc 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 5f48744..2974b85 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
index f4fad60..cbb00ae 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без заглавие)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index f96d65f..e359d46 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
index 0eb83a0..f24c92a 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিরোনামহীন প্রোগ্রাম)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 1e068c6..e3990e0 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
index 8e301b0..80bac2a 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -19,6 +19,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Slika u slici"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
-    <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
+    <string name="pip_close" msgid="9135220303720555525">"Zatvori sliku u slici"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 97585ef..f3c2470 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
index b80fc41..66cd93a 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sense títol)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Tanca PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 7a3a890..d1c9a46 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
index 56abcbe..500050b 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Bez názvu)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Ukončit obraz v obraze (PIP)"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 4a025ba..94f9a7a 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
index fdb6b78..896895b 100644
--- a/libs/WindowManager/Shell/res/values-da/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uden titel)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Luk integreret billede"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index bacd512..d1851ea 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
index 02cce9d..7809efe 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Kein Sendungsname gefunden)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index f306ba3..21d7474 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
index 880ea37..088bcc3 100644
--- a/libs/WindowManager/Shell/res/values-el/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 1ffbd039..7ac28ea9 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
index e3f08c8..7900fdc 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 1ffbd039..7ac28ea9 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
index e3f08c8..7900fdc 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 1ffbd039..7ac28ea9 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
index e3f08c8..7900fdc 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 1ffbd039..7ac28ea9 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
index e3f08c8..7900fdc 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
index 3f9ef0e..3b12d90 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -21,4 +21,5 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎(No title program)‎‏‎‎‏‎"</string>
     <string name="pip_close" msgid="9135220303720555525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎Close PIP‎‏‎‎‏‎"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎Full screen‎‏‎‎‏‎"</string>
+    <string name="pip_move" msgid="1544227837964635439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎Move PIP‎‏‎‎‏‎"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 1ca7bfc..d8db8e1 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
index 5d5954a..3be850a 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sin título de programa)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 3d46421..9a7d1c0 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
index d31b9b4..7eba361 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sin título)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 4f5d563..9dfd6cf 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
index bc7a6ad..ca6e669 100644
--- a/libs/WindowManager/Shell/res/values-et/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programmi pealkiri puudub)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index b76ccc8..210c441 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
index cf5f988..3f47e95 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa izengabea)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index aef3c62..87c7f8b 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
index 5b815b4..cc5cf64 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
     <string name="pip_close" msgid="9135220303720555525">"‏بستن PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 5f76ba4..7b2ea03 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
index 77ad6ee..b779886 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nimetön)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index e1843e7..338b8bb 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
index 0ec7f40..1798c7d 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Aucun programme de titre)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Fermer mode IDI"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index b3f3349..22d1d19 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
index 27fd155..b039934 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programme sans titre)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Fermer mode PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 3cdddf3..4bd70a1 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
index df96f6c..0d91eba 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sen título)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Pechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 09d206e..81c5ba9 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
index 3608f1d..a748df3 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 0ce5114..56c3271 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
index 720bb6c..040072b 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्‍क्रीन"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index e8c83f1..d71bc1d 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
index 21f8cb6..20081e4 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 6d60513..a69f197 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
index 0010086..c78146d 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Cím nélküli program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP bezárása"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index f1ec076..e38285e 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
index cb18762..55d5bd7 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Առանց վերնագրի ծրագիր)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 62dfcb7..036acb8 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
index 8f3a287..6401852 100644
--- a/libs/WindowManager/Shell/res/values-in/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tanpa judul)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Layar penuh"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index feab362..2f98487 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
index 1f148d9..fa36829 100644
--- a/libs/WindowManager/Shell/res/values-is/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Efni án titils)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Loka mynd í mynd"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index bfcd385..e4da3939 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
index 127454c..f6e91be 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma senza titolo)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Chiudi PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 7ef5035..99294f9 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index ef98a9c..356e8d5 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
     <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fb58abb..1ee606a 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
index b7ab28c..07684d3 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 297d9af..e8efa8d 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
index 1bf4b8e..043e5eb 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 7ca2f23..0c3f8f54 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
index 8f1e725..7943797 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index dae2293..e5ecf81 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហា​ពាក់ព័ន្ធនឹង​កាមេរ៉ាឬ?\nចុចដើម្បី​ដោះស្រាយ"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
index b559970..2e56254 100644
--- a/libs/WindowManager/Shell/res/values-km/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(កម្មវិធី​គ្មានចំណងជើង)"</string>
     <string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 72ba8ea..69a6df8 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 9d3942f..6c8880d 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index d99abc4..804b78c 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
index 46d6ad4..35b1b19 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(제목 없는 프로그램)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP 닫기"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"전체화면"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index fe25161..c7e7a05 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
index d5d1d7e..72d70f0 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Аталышы жок программа)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 786d837..a94507f 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
index f6362c1..3604726 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
     <string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 32bdfc4..dae4d71 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
index e4695a0..fa5a4c4 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa be pavadinimo)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Uždaryti PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 5d0f318..564002d 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
index f2b037f..cafd43a 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma bez nosaukuma)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Aizvērt PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 8c2e44c..a9b0019 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
index 25dc764..b927b56 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 6c8883a9..9326134 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index c74e0bb..aef059f 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index d297923..8f21592 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
index 55519d4..7dfec68 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 3d9b53c..936c252 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
index ad2cfc6..447cb7d 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 0aafb59..15c122c 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
index b2d7214..3a5584d 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 899c607..c54e3b3 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
index c18d539..84ec0e5 100644
--- a/libs/WindowManager/Shell/res/values-my/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ခေါင်းစဉ်မဲ့ အစီအစဉ်)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP ကိုပိတ်ပါ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 60891d3..ae33f7a 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
index 8a7f315..78ec6db 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uten tittel)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 8bd6907..bbf247c 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
index 87fa327..4458a14 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 3fddf34..6a4d310 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
index df3809e..d21515d 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 217556e..09fe95b 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
index 295a5c4..a679350 100644
--- a/libs/WindowManager/Shell/res/values-or/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(କୌଣସି ଟାଇଟଲ୍‍ ପ୍ରୋଗ୍ରାମ୍‍ ନାହିଁ)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index dc4dae1..3c7f373 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
index e32895a..a0ff4f3 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index fccd138..cb89c39 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
index 286fd7b..6320893 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez tytułu)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Zamknij PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 5664030..73cf177 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
index 57edcdf..fef9d47 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index abe6a01..8ea4c2f 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
index 9372e0f..461571f 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sem título do programa)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 5664030..73cf177 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
index 57edcdf..fef9d47 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 39dec90..eb77c6a 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ați remediat problema?\nAtingeți pentru a reveni"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu aveți probleme cu camera foto? Atingeți pentru a închide."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index 9438e49..80bf151 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Închideți PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index e3c2215..64de668 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
index 24785aa..de5348a 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
     <string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index efaf1f3..8c3aae9 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
index 62ee6d4..0470040 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index a827153..5dbe5ad 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
index a7a515c..41a432c 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez názvu)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Zavrieť režim PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 7437654..61155a9 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
index fe5c9ae..de5605f 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program brez naslova)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Zapri način PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index bda10c8..8c11ea5 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
index 1d5583b..08a6409 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program pa titull)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 98e13be..910108d 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
index 62ad1e8..f932928 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 3080088..b3fa582 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
index 74fb590..1428fdb 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Namnlöst program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Stäng PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 9fb7460..286b53c 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
index cf0d8a9..615209f 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programu isiyo na jina)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Funga PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index d7f17aa..b929a1c 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
index 8bca463..71c242c 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 4e421b5..84cf285a 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్‌లోకి వెళ్లండి."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
index 47489ef..f2dfb39 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIPని మూసివేయి"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"పూర్తి స్క్రీన్"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 52f94e6..f0d9362 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
index d3797e7..e810c88 100644
--- a/libs/WindowManager/Shell/res/values-th/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ไม่มีชื่อรายการ)"</string>
     <string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index d7b671d..1c3b8f1 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
index b01c111..11d2953 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Walang pamagat na programa)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Isara ang PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index c618f6c..41c6c89 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
index c92c4d0..6ed6e9f 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıksız program)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP\'yi kapat"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index e41ebc4..558ec51 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -21,7 +21,14 @@
     <dimen name="pip_menu_icon_size">20dp</dimen>
     <dimen name="pip_menu_button_margin">4dp</dimen>
     <dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
-    <dimen name="pip_menu_border_width">2dp</dimen>
-    <dimen name="pip_menu_border_radius">0dp</dimen>
+    <dimen name="pip_menu_border_width">4dp</dimen>
+    <dimen name="pip_menu_border_radius">4dp</dimen>
+    <dimen name="pip_menu_outer_space">24dp</dimen>
+
+    <!-- outer space minus border width -->
+    <dimen name="pip_menu_outer_space_frame">20dp</dimen>
+
+    <dimen name="pip_menu_arrow_size">24dp</dimen>
+    <dimen name="pip_menu_arrow_elevation">5dp</dimen>
 </resources>
 
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index ec2e550..8094d5d 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
index 74d4723..482f59a 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без назви)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 18e1e51..0f6061e 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index 3179533..c1954c7 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
     <string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index f6c7ed2..12fbd0f 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
index ae5a647..5140552 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 1cab8e7..4623b6b 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
index 082d125..e54d866 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Không có chương trình tiêu đề)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Đóng PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index dc12c4f..5936677 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
index cb3fcf7..9ce1e6c 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
     <string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index fe114ac..55045371 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
index 956243e..9846772 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(沒有標題的節目)"</string>
     <string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 0e4fc30..aa0bdd0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
     <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
index 08b2f4b..7314d48 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無標題的節目)"</string>
     <string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index f563f1f..688f36b 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -73,10 +73,7 @@
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
-    <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
-    <skip />
-    <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
-    <skip />
-    <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
-    <skip />
+    <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
+    <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
+    <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
index 89c7f49..63d9dd5 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -21,4 +21,6 @@
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Alukho uhlelo lwesihloko)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Vala i-PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
+    <!-- no translation found for pip_move (1544227837964635439) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
index 17387fa..08d3cef 100644
--- a/libs/WindowManager/Shell/res/values/colors_tv.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -19,6 +19,6 @@
     <color name="tv_pip_menu_icon_unfocused">#E8EAED</color>
     <color name="tv_pip_menu_icon_disabled">#80868B</color>
     <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
-    <color name="tv_pip_menu_icon_bg_unfocused">#777777</color>
-    <color name="tv_pip_menu_focus_border">#CCE8EAED</color>
+    <color name="tv_pip_menu_icon_bg_unfocused">#990E0E0F</color>
+    <color name="tv_pip_menu_focus_border">#E8EAED</color>
 </resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 0cdaa20..1b8032b 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -43,6 +43,9 @@
     <!-- PiP minimum size, which is a % based off the shorter side of display width and height -->
     <fraction name="config_pipShortestEdgePercent">40%</fraction>
 
+    <!-- Show PiP enter split icon, which allows apps to directly enter splitscreen from PiP. -->
+    <bool name="config_pipEnableEnterSplitButton">false</bool>
+
     <!-- Animation duration when using long press on recents to dock -->
     <integer name="long_press_dock_anim_duration">250</integer>
 
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d338e3b..1c19a10 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -200,11 +200,8 @@
     <!-- Size of user education views on large screens (phone is just match parent). -->
     <dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
 
-    <!-- The width of the size compat restart button including padding. -->
-    <dimen name="size_compat_button_width">80dp</dimen>
-
-    <!-- The height of the size compat restart button including padding. -->
-    <dimen name="size_compat_button_height">64dp</dimen>
+    <!-- Bottom and end margin for compat buttons. -->
+    <dimen name="compat_button_margin">16dp</dimen>
 
     <!-- The radius of the corners of the compat hint bubble. -->
     <dimen name="compat_hint_corner_radius">28dp</dimen>
@@ -212,8 +209,8 @@
     <!-- The width of the compat hint point. -->
     <dimen name="compat_hint_point_width">10dp</dimen>
 
-    <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
-         - compat_hint_corner_radius - compat_hint_point_width /2). -->
+    <!-- The end padding for the compat hint. Computed as (compat button width (=48) / 2
+        + compat_button_margin - compat_hint_corner_radius - compat_hint_point_width / 2). -->
     <dimen name="compat_hint_padding_end">7dp</dimen>
 
     <!-- The width of the size compat hint. -->
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index 2dfdcab..730d808 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -30,5 +30,8 @@
 
     <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
     <string name="pip_fullscreen">Full screen</string>
+
+    <!-- Button to move picture-in-picture (PIP) via DPAD in the PIP menu [CHAR LIMIT=30] -->
+    <string name="pip_move">Move PIP</string>
 </resources>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
new file mode 100644
index 0000000..b310dd6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import android.view.MotionEvent;
+
+/**
+ * Interface for SysUI to get access to the Back animation related methods.
+ */
+public interface BackAnimation {
+
+    /**
+     * Called when a {@link MotionEvent} is generated by a back gesture.
+     */
+    void onBackMotion(MotionEvent event);
+
+    /**
+     * Sets whether the back gesture is past the trigger threshold or not.
+     */
+    void setTriggerBack(boolean triggerBack);
+}
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
new file mode 100644
index 0000000..229e8ee0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+/**
+ * Controls the window animation run when a user initiates a back gesture.
+ */
+public class BackAnimationController {
+
+    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+    public static final boolean IS_ENABLED = SystemProperties
+            .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+    private static final String TAG = "BackAnimationController";
+
+    /**
+     * Location of the initial touch event of the back gesture.
+     */
+    private final PointF mInitTouchLocation = new PointF();
+
+    /**
+     * Raw delta between {@link #mInitTouchLocation} and the last touch location.
+     */
+    private final Point mTouchEventDelta = new Point();
+    private final ShellExecutor mShellExecutor;
+
+    /** True when a back gesture is ongoing */
+    private boolean mBackGestureStarted = false;
+
+    /** @see #setTriggerBack(boolean) */
+    private boolean mTriggerBack;
+
+    @Nullable
+    private BackNavigationInfo mBackNavigationInfo;
+    private final SurfaceControl.Transaction mTransaction;
+    private final IActivityTaskManager mActivityTaskManager;
+
+    public BackAnimationController(@ShellMainThread ShellExecutor shellExecutor) {
+        this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService());
+    }
+
+    @VisibleForTesting
+    BackAnimationController(@NonNull ShellExecutor shellExecutor,
+            @NonNull SurfaceControl.Transaction transaction,
+            @NonNull IActivityTaskManager activityTaskManager) {
+        mShellExecutor = shellExecutor;
+        mTransaction = transaction;
+        mActivityTaskManager = activityTaskManager;
+    }
+
+    public BackAnimation getBackAnimationImpl() {
+        return mBackAnimation;
+    }
+
+    private final BackAnimation mBackAnimation = new BackAnimationImpl();
+
+    private class BackAnimationImpl implements BackAnimation {
+
+        @Override
+        public void onBackMotion(MotionEvent event) {
+            mShellExecutor.execute(() -> onMotionEvent(event));
+        }
+
+        @Override
+        public void setTriggerBack(boolean triggerBack) {
+            mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
+        }
+    }
+
+    /**
+     * Called when a new motion event needs to be transferred to this
+     * {@link BackAnimationController}
+     */
+    public void onMotionEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            initAnimation(event);
+        } else if (action == MotionEvent.ACTION_MOVE) {
+            onMove(event);
+        } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            onGestureFinished();
+        }
+    }
+
+    private void initAnimation(MotionEvent event) {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
+        if (mBackGestureStarted) {
+            Log.e(TAG, "Animation is being initialized but is already started.");
+            return;
+        }
+
+        if (mBackNavigationInfo != null) {
+            finishAnimation();
+        }
+        mInitTouchLocation.set(event.getX(), event.getY());
+        mBackGestureStarted = true;
+
+        try {
+            mBackNavigationInfo = mActivityTaskManager.startBackNavigation();
+            onBackNavigationInfoReceived(mBackNavigationInfo);
+        } catch (RemoteException remoteException) {
+            Log.e(TAG, "Failed to initAnimation", remoteException);
+            finishAnimation();
+        }
+    }
+
+    private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo) {
+        if (backNavigationInfo == null
+                || backNavigationInfo.getDepartingWindowContainer() == null) {
+            Log.e(TAG, "Received BackNavigationInfo is null.");
+            finishAnimation();
+            return;
+        }
+
+        HardwareBuffer hardwareBuffer = backNavigationInfo.getScreenshotHardwareBuffer();
+        if (hardwareBuffer != null) {
+            displayTargetScreenshot(hardwareBuffer,
+                    backNavigationInfo.getTaskWindowConfiguration());
+        }
+        mTransaction.apply();
+    }
+
+    /**
+     * Display the screenshot of the activity beneath.
+     *
+     * @param hardwareBuffer The buffer containing the screenshot.
+     */
+    private void displayTargetScreenshot(@NonNull HardwareBuffer hardwareBuffer,
+            WindowConfiguration taskWindowConfiguration) {
+        SurfaceControl screenshotSurface =
+                mBackNavigationInfo == null ? null : mBackNavigationInfo.getScreenshotSurface();
+        if (screenshotSurface == null) {
+            Log.e(TAG, "BackNavigationInfo doesn't contain a surface for the screenshot. ");
+            return;
+        }
+
+        // Scale the buffer to fill the whole Task
+        float sx = 1;
+        float sy = 1;
+        float w = taskWindowConfiguration.getBounds().width();
+        float h = taskWindowConfiguration.getBounds().height();
+
+        if (w != hardwareBuffer.getWidth()) {
+            sx = w / hardwareBuffer.getWidth();
+        }
+
+        if (h != hardwareBuffer.getHeight()) {
+            sy = h / hardwareBuffer.getHeight();
+        }
+        mTransaction.setScale(screenshotSurface, sx, sy);
+        mTransaction.setBuffer(screenshotSurface, hardwareBuffer);
+        mTransaction.setVisibility(screenshotSurface, true);
+    }
+
+    private void onMove(MotionEvent event) {
+        if (!mBackGestureStarted || mBackNavigationInfo == null) {
+            return;
+        }
+        int deltaX = Math.round(event.getX() - mInitTouchLocation.x);
+        int deltaY = Math.round(event.getY() - mInitTouchLocation.y);
+        ProtoLog.v(WM_SHELL_BACK_PREVIEW, "Runner move: %d %d", deltaX, deltaY);
+        SurfaceControl topWindowLeash = mBackNavigationInfo.getDepartingWindowContainer();
+        mTransaction.setPosition(topWindowLeash, deltaX, deltaY);
+        mTouchEventDelta.set(deltaX, deltaY);
+        mTransaction.apply();
+    }
+
+    private void onGestureFinished() {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
+        if (mBackGestureStarted) {
+            if (mTriggerBack) {
+                prepareTransition();
+            } else {
+                resetPositionAnimated();
+            }
+        }
+        mBackGestureStarted = false;
+        mTriggerBack = false;
+    }
+
+    /**
+     * Animate the top window leash to its initial position.
+     */
+    private void resetPositionAnimated() {
+        mBackGestureStarted = false;
+        // TODO(208786853) Handle overlap with a new coming gesture.
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Runner: Back not triggered, cancelling animation "
+                + "mLastPos=%s mInitTouch=%s", mTouchEventDelta, mInitTouchLocation);
+
+        // TODO(208427216) : Replace placeholder animation with an actual one.
+        ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f).setDuration(200);
+        animation.addUpdateListener(animation1 -> {
+            if (mBackNavigationInfo == null) {
+                return;
+            }
+            float fraction = animation1.getAnimatedFraction();
+            int deltaX = Math.round(mTouchEventDelta.x - (mTouchEventDelta.x * fraction));
+            int deltaY = Math.round(mTouchEventDelta.y - (mTouchEventDelta.y * fraction));
+            mTransaction.setPosition(mBackNavigationInfo.getDepartingWindowContainer(),
+                    deltaX, deltaY);
+            mTransaction.apply();
+        });
+
+        animation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onAnimationEnd");
+                finishAnimation();
+            }
+        });
+        animation.start();
+    }
+
+    private void prepareTransition() {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "prepareTransition()");
+        mTriggerBack = false;
+        mBackGestureStarted = false;
+    }
+
+    /**
+     * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
+     */
+    public void setTriggerBack(boolean triggerBack) {
+        mTriggerBack = triggerBack;
+    }
+
+    private void finishAnimation() {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishAnimation()");
+        mBackGestureStarted = false;
+        mTouchEventDelta.set(0, 0);
+        mInitTouchLocation.set(0, 0);
+        BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
+        mBackNavigationInfo = null;
+        if (backNavigationInfo == null) {
+            return;
+        }
+        SurfaceControl topWindowLeash = backNavigationInfo.getDepartingWindowContainer();
+        if (topWindowLeash != null && topWindowLeash.isValid()) {
+            mTransaction.remove(topWindowLeash);
+        }
+        SurfaceControl screenshotSurface = backNavigationInfo.getScreenshotSurface();
+        if (screenshotSurface != null && screenshotSurface.isValid()) {
+            mTransaction.remove(screenshotSurface);
+        }
+        mTransaction.apply();
+        backNavigationInfo.onBackNavigationFinished();
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
deleted file mode 100644
index dc20f7b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import android.os.SystemProperties;
-import android.view.IWindowManager;
-
-import javax.inject.Inject;
-
-/**
- * Handle the preview of what a back gesture will lead to.
- */
-public class BackPreviewHandler {
-
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-
-    public static boolean isEnabled() {
-        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
-    }
-
-    private final IWindowManager mWmService;
-
-    @Inject
-    public BackPreviewHandler(IWindowManager windowManagerService) {
-        mWmService = windowManagerService;
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 8d43f13..cf4647a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -108,6 +108,8 @@
     private Bitmap mBubbleBitmap;
     // The app badge for the bubble
     private Bitmap mBadgeBitmap;
+    // App badge without any markings for important conversations
+    private Bitmap mRawBadgeBitmap;
     private int mDotColor;
     private Path mDotPath;
     private int mFlags;
@@ -248,6 +250,11 @@
     }
 
     @Override
+    public Bitmap getRawAppBadge() {
+        return mRawBadgeBitmap;
+    }
+
+    @Override
     public int getDotColor() {
         return mDotColor;
     }
@@ -357,13 +364,15 @@
      * @param context the context for the bubble.
      * @param controller the bubble controller.
      * @param stackView the stackView the bubble is eventually added to.
-     * @param iconFactory the iconfactory use to create badged images for the bubble.
+     * @param iconFactory the icon factory use to create images for the bubble.
+     * @param badgeIconFactory the icon factory to create app badges for the bubble.
      */
     void inflate(BubbleViewInfoTask.Callback callback,
             Context context,
             BubbleController controller,
             BubbleStackView stackView,
             BubbleIconFactory iconFactory,
+            BubbleBadgeIconFactory badgeIconFactory,
             boolean skipInflation) {
         if (isBubbleLoading()) {
             mInflationTask.cancel(true /* mayInterruptIfRunning */);
@@ -373,6 +382,7 @@
                 controller,
                 stackView,
                 iconFactory,
+                badgeIconFactory,
                 skipInflation,
                 callback,
                 mMainExecutor);
@@ -409,6 +419,7 @@
         mFlyoutMessage = info.flyoutMessage;
 
         mBadgeBitmap = info.badgeBitmap;
+        mRawBadgeBitmap = info.mRawBadgeBitmap;
         mBubbleBitmap = info.bubbleBitmap;
 
         mDotColor = info.dotColor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
new file mode 100644
index 0000000..4eeb207
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ShadowGenerator;
+import com.android.wm.shell.R;
+
+/**
+ * Factory for creating app badge icons that are shown on bubbles.
+ */
+public class BubbleBadgeIconFactory extends BaseIconFactory {
+
+    public BubbleBadgeIconFactory(Context context) {
+        super(context, context.getResources().getConfiguration().densityDpi,
+                context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size));
+    }
+
+    /**
+     * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
+     * will include the workprofile indicator on the badge if appropriate.
+     */
+    BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
+        ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
+        Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
+
+        if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
+            userBadgedBitmap = Bitmap.createScaledBitmap(
+                    getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
+                            userBadgedAppIcon.getIntrinsicWidth()),
+                    mIconBitmapSize, mIconBitmapSize, /* filter */ true);
+        }
+
+        if (isImportantConversation) {
+            final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.importance_ring_stroke_width);
+            final int importantConversationColor = mContext.getResources().getColor(
+                    R.color.important_conversation, null);
+            Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
+                    userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
+            Canvas c = new Canvas(badgeAndRing);
+
+            Paint ringPaint = new Paint();
+            ringPaint.setStyle(Paint.Style.FILL);
+            ringPaint.setColor(importantConversationColor);
+            ringPaint.setAntiAlias(true);
+            c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
+
+            final int bitmapTop = (int) ringStrokeWidth;
+            final int bitmapLeft = (int) ringStrokeWidth;
+            final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
+            final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
+
+            Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
+                    bitmapHeight, /* filter */ true);
+            c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
+
+            shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
+            return createIconBitmap(badgeAndRing);
+        } else {
+            Canvas c = new Canvas();
+            c.setBitmap(userBadgedBitmap);
+            shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
+            return createIconBitmap(userBadgedBitmap);
+        }
+    }
+
+    private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
+        Drawable foreground = icon.getForeground();
+        Drawable background = icon.getBackground();
+        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas();
+        canvas.setBitmap(bitmap);
+
+        // Clip canvas to circle.
+        Path circlePath = new Path();
+        circlePath.addCircle(/* x */ size / 2f,
+                /* y */ size / 2f,
+                /* radius */ size / 2f,
+                Path.Direction.CW);
+        canvas.clipPath(circlePath);
+
+        // Draw background.
+        background.setBounds(0, 0, size, size);
+        background.draw(canvas);
+
+        // Draw foreground. The foreground and background drawables are derived from adaptive icons
+        // Some icon shapes fill more space than others, so adaptive icons are normalized to about
+        // the same size. This size is smaller than the original bounds, so we estimate
+        // the difference in this offset.
+        int offset = size / 5;
+        foreground.setBounds(-offset, -offset, size + offset, size + offset);
+        foreground.draw(canvas);
+
+        canvas.setBitmap(null);
+        return bitmap;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index ce1f870..57cb7a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -17,6 +17,8 @@
 package com.android.wm.shell.bubbles;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
@@ -42,6 +44,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -151,6 +154,7 @@
     private BubbleData mBubbleData;
     @Nullable private BubbleStackView mStackView;
     private BubbleIconFactory mBubbleIconFactory;
+    private BubbleBadgeIconFactory mBubbleBadgeIconFactory;
     private BubblePositioner mBubblePositioner;
     private Bubbles.SysuiProxy mSysuiProxy;
 
@@ -278,23 +282,28 @@
         mBubbleData = data;
         mSavedBubbleKeysPerUser = new SparseSetArray<>();
         mBubbleIconFactory = new BubbleIconFactory(context);
+        mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context);
         mDisplayController = displayController;
         mTaskViewTransitions = taskViewTransitions;
         mOneHandedOptional = oneHandedOptional;
         mSyncQueue = syncQueue;
     }
 
-    private static void registerOneHandedState(OneHandedController oneHanded) {
+    private void registerOneHandedState(OneHandedController oneHanded) {
         oneHanded.registerTransitionCallback(
                 new OneHandedTransitionCallback() {
                     @Override
                     public void onStartFinished(Rect bounds) {
-                        // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+                        if (mStackView != null) {
+                            mStackView.onVerticalOffsetChanged(bounds.top);
+                        }
                     }
 
                     @Override
                     public void onStopFinished(Rect bounds) {
-                        // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+                        if (mStackView != null) {
+                            mStackView.onVerticalOffsetChanged(bounds.top);
+                        }
                     }
                 });
     }
@@ -423,7 +432,7 @@
                     }
                 });
 
-        mOneHandedOptional.ifPresent(BubbleController::registerOneHandedState);
+        mOneHandedOptional.ifPresent(this::registerOneHandedState);
     }
 
     @VisibleForTesting
@@ -496,6 +505,7 @@
             }
             mStackView.updateStackPosition();
             mBubbleIconFactory = new BubbleIconFactory(mContext);
+            mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext);
             mStackView.onDisplaySizeChanged();
         }
         if (b.getBoolean(EXTRA_BUBBLE_OVERFLOW_OPENED, false)) {
@@ -774,13 +784,17 @@
             mStackView.onThemeChanged();
         }
         mBubbleIconFactory = new BubbleIconFactory(mContext);
+        mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext);
+
         // Reload each bubble
-        for (Bubble b: mBubbleData.getBubbles()) {
+        for (Bubble b : mBubbleData.getBubbles()) {
             b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory,
+                    mBubbleBadgeIconFactory,
                     false /* skipInflation */);
         }
-        for (Bubble b: mBubbleData.getOverflowBubbles()) {
+        for (Bubble b : mBubbleData.getOverflowBubbles()) {
             b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory,
+                    mBubbleBadgeIconFactory,
                     false /* skipInflation */);
         }
     }
@@ -796,6 +810,7 @@
                 mScreenBounds.set(newConfig.windowConfiguration.getBounds());
                 mBubbleData.onMaxBubblesChanged();
                 mBubbleIconFactory = new BubbleIconFactory(mContext);
+                mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext);
                 mStackView.onDisplaySizeChanged();
             }
             if (newConfig.fontScale != mFontScale) {
@@ -957,7 +972,8 @@
                 }
                 bubble.inflate(
                         (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
-                        mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */);
+                        mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory,
+                        true /* skipInflation */);
             });
             return null;
         });
@@ -992,7 +1008,8 @@
         ensureStackViewCreated();
         bubble.setInflateSynchronously(mInflateSynchronously);
         bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
-                mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */);
+                mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory,
+                false /* skipInflation */);
     }
 
     /**
@@ -1078,6 +1095,24 @@
         }
     }
 
+    @VisibleForTesting
+    public void onNotificationChannelModified(String pkg, UserHandle user,
+            NotificationChannel channel, int modificationType) {
+        // Only query overflow bubbles here because active bubbles will have an active notification
+        // and channel changes we care about would result in a ranking update.
+        List<Bubble> overflowBubbles = new ArrayList<>(mBubbleData.getOverflowBubbles());
+        for (int i = 0; i < overflowBubbles.size(); i++) {
+            Bubble b = overflowBubbles.get(i);
+            if (Objects.equals(b.getShortcutId(), channel.getConversationId())
+                    && b.getPackageName().equals(pkg)
+                    && b.getUser().getIdentifier() == user.getIdentifier()) {
+                if (!channel.canBubble() || channel.isDeleted()) {
+                    mBubbleData.dismissBubbleWithKey(b.getKey(), DISMISS_NO_LONGER_BUBBLE);
+                }
+            }
+        }
+    }
+
     /**
      * Retrieves any bubbles that are part of the notification group represented by the provided
      * group key.
@@ -1315,6 +1350,7 @@
      * Updates the visibility of the bubbles based on current state.
      * Does not un-bubble, just hides or un-hides.
      * Updates stack description for TalkBack focus.
+     * Updates bubbles' icon views clickable states
      */
     public void updateStack() {
         if (mStackView == null) {
@@ -1332,6 +1368,8 @@
         }
 
         mStackView.updateContentDescription();
+
+        mStackView.updateBubblesAcessibillityStates();
     }
 
     @VisibleForTesting
@@ -1653,6 +1691,19 @@
         }
 
         @Override
+        public void onNotificationChannelModified(String pkg,
+                UserHandle user, NotificationChannel channel, int modificationType) {
+            // Bubbles only cares about updates or deletions.
+            if (modificationType == NOTIFICATION_CHANNEL_OR_GROUP_UPDATED
+                    || modificationType == NOTIFICATION_CHANNEL_OR_GROUP_DELETED) {
+                mMainExecutor.execute(() -> {
+                    BubbleController.this.onNotificationChannelModified(pkg, user, channel,
+                            modificationType);
+                });
+            }
+        }
+
+        @Override
         public void onStatusBarVisibilityChanged(boolean visible) {
             mMainExecutor.execute(() -> {
                 BubbleController.this.onStatusBarVisibilityChanged(visible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 9374da4..f878a46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -231,8 +231,9 @@
      * Fade animation for consecutive flyouts.
      */
     void animateUpdate(Bubble.FlyoutMessage flyoutMessage, PointF stackPos,
-            boolean hideDot, Runnable onHide) {
+            boolean hideDot, float[] dotCenter, Runnable onHide) {
         mOnHide = onHide;
+        mDotCenter = dotCenter;
         final Runnable afterFadeOut = () -> {
             updateFlyoutMessage(flyoutMessage);
             // Wait for TextViews to layout with updated height.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index b0e029f..9d3bf34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -21,19 +21,12 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
 import com.android.wm.shell.R;
 
 /**
@@ -44,12 +37,9 @@
 @VisibleForTesting
 public class BubbleIconFactory extends BaseIconFactory {
 
-    private int mBadgeSize;
-
     public BubbleIconFactory(Context context) {
         super(context, context.getResources().getConfiguration().densityDpi,
                 context.getResources().getDimensionPixelSize(R.dimen.bubble_size));
-        mBadgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size);
     }
 
     /**
@@ -75,84 +65,4 @@
             return null;
         }
     }
-
-    /**
-     * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
-     * will include the workprofile indicator on the badge if appropriate.
-     */
-    BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
-        ShadowGenerator shadowGenerator = new ShadowGenerator(mBadgeSize);
-        Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mBadgeSize);
-
-        if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
-            userBadgedBitmap = Bitmap.createScaledBitmap(
-                    getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
-                            userBadgedAppIcon.getIntrinsicWidth()),
-                    mBadgeSize, mBadgeSize, /* filter */ true);
-        }
-
-        if (isImportantConversation) {
-            final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.importance_ring_stroke_width);
-            final int importantConversationColor = mContext.getResources().getColor(
-                    R.color.important_conversation, null);
-            Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
-                    userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
-            Canvas c = new Canvas(badgeAndRing);
-
-            Paint ringPaint = new Paint();
-            ringPaint.setStyle(Paint.Style.FILL);
-            ringPaint.setColor(importantConversationColor);
-            ringPaint.setAntiAlias(true);
-            c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
-
-            final int bitmapTop = (int) ringStrokeWidth;
-            final int bitmapLeft = (int) ringStrokeWidth;
-            final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
-            final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
-
-            Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
-                    bitmapHeight, /* filter */ true);
-            c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
-
-            shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
-            return createIconBitmap(badgeAndRing);
-        } else {
-            Canvas c = new Canvas();
-            c.setBitmap(userBadgedBitmap);
-            shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
-            return createIconBitmap(userBadgedBitmap);
-        }
-    }
-
-    public Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
-        Drawable foreground = icon.getForeground();
-        Drawable background = icon.getBackground();
-        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas();
-        canvas.setBitmap(bitmap);
-
-        // Clip canvas to circle.
-        Path circlePath = new Path();
-        circlePath.addCircle(/* x */ size / 2f,
-                /* y */ size / 2f,
-                /* radius */ size / 2f,
-                Path.Direction.CW);
-        canvas.clipPath(circlePath);
-
-        // Draw background.
-        background.setBounds(0, 0, size, size);
-        background.draw(canvas);
-
-        // Draw foreground. The foreground and background drawables are derived from adaptive icons
-        // Some icon shapes fill more space than others, so adaptive icons are normalized to about
-        // the same size. This size is smaller than the original bounds, so we estimate
-        // the difference in this offset.
-        int offset = size / 5;
-        foreground.setBounds(-offset, -offset, size + offset, size + offset);
-        foreground.draw(canvas);
-
-        canvas.setBitmap(null);
-        return bitmap;
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index a175929..dd751d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -141,6 +141,10 @@
         return null
     }
 
+    override fun getRawAppBadge(): Bitmap? {
+        return null
+    }
+
     override fun getBubbleIcon(): Bitmap {
         return bitmap
     }
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 79b7653..a477bd7 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
@@ -1485,6 +1485,69 @@
         }
     }
 
+    /**
+     * Update bubbles' icon views accessibility states.
+     */
+    public void updateBubblesAcessibillityStates() {
+        for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
+            Bubble prevBubble = i > 0 ? mBubbleData.getBubbles().get(i - 1) : null;
+            Bubble bubble = mBubbleData.getBubbles().get(i);
+
+            View bubbleIconView = bubble.getIconView();
+            if (bubbleIconView == null) {
+                continue;
+            }
+
+            if (mIsExpanded) {
+                // when stack is expanded
+                // all bubbles are important for accessibility
+                bubbleIconView
+                        .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+                View prevBubbleIconView = prevBubble != null ? prevBubble.getIconView() : null;
+
+                if (prevBubbleIconView != null) {
+                    bubbleIconView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+                        @Override
+                        public void onInitializeAccessibilityNodeInfo(View v,
+                                AccessibilityNodeInfo info) {
+                            super.onInitializeAccessibilityNodeInfo(v, info);
+                            info.setTraversalAfter(prevBubbleIconView);
+                        }
+                    });
+                }
+            } else {
+                // when stack is collapsed, only the top bubble is important for accessibility,
+                bubbleIconView.setImportantForAccessibility(
+                        i == 0 ? View.IMPORTANT_FOR_ACCESSIBILITY_YES :
+                                View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+            }
+        }
+
+        if (mIsExpanded) {
+            // make the overflow bubble last in the accessibility traversal order
+
+            View bubbleOverflowIconView =
+                    mBubbleOverflow != null ? mBubbleOverflow.getIconView() : null;
+            if (bubbleOverflowIconView != null && !mBubbleData.getBubbles().isEmpty()) {
+                Bubble lastBubble =
+                        mBubbleData.getBubbles().get(mBubbleData.getBubbles().size() - 1);
+                View lastBubbleIconView = lastBubble.getIconView();
+                if (lastBubbleIconView != null) {
+                    bubbleOverflowIconView.setAccessibilityDelegate(
+                            new View.AccessibilityDelegate() {
+                                @Override
+                                public void onInitializeAccessibilityNodeInfo(View v,
+                                        AccessibilityNodeInfo info) {
+                                    super.onInitializeAccessibilityNodeInfo(v, info);
+                                    info.setTraversalAfter(lastBubbleIconView);
+                                }
+                            });
+                }
+            }
+        }
+    }
+
     private void updateSystemGestureExcludeRects() {
         // Exclude the region occupied by the first BubbleView in the stack
         Rect excludeZone = mSystemGestureExclusionRects.get(0);
@@ -2497,6 +2560,7 @@
             if (mFlyout.getVisibility() == View.VISIBLE) {
                 mFlyout.animateUpdate(bubble.getFlyoutMessage(),
                         mStackAnimationController.getStackPosition(), !bubble.showDot(),
+                        bubble.getIconView().getDotCenter(),
                         mAfterFlyoutHidden /* onHide */);
             } else {
                 mFlyout.setVisibility(INVISIBLE);
@@ -2615,11 +2679,13 @@
 
         // If available, update the manage menu's settings option with the expanded bubble's app
         // name and icon.
-        if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
+        if (show) {
             final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
-            mManageSettingsIcon.setImageBitmap(bubble.getAppBadge());
-            mManageSettingsText.setText(getResources().getString(
-                    R.string.bubbles_app_settings, bubble.getAppName()));
+            if (bubble != null) {
+                mManageSettingsIcon.setImageBitmap(bubble.getRawAppBadge());
+                mManageSettingsText.setText(getResources().getString(
+                        R.string.bubbles_app_settings, bubble.getAppName()));
+            }
         }
 
         if (mExpandedBubble.getExpandedView().getTaskView() != null) {
@@ -3013,6 +3079,16 @@
     }
 
     /**
+     * Handles vertical offset changes, e.g. when one handed mode is switched on/off.
+     *
+     * @param offset new vertical offset.
+     */
+    void onVerticalOffsetChanged(int offset) {
+        // adjust dismiss view vertical position, so that it is still visible to the user
+        mDismissView.setPadding(/* left = */ 0, /* top = */ 0, /* right = */ 0, offset);
+    }
+
+    /**
      * Holds some commonly queried information about the stack.
      */
     public static class StackViewState {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 91aff3e..69762c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -71,6 +71,7 @@
     private WeakReference<BubbleController> mController;
     private WeakReference<BubbleStackView> mStackView;
     private BubbleIconFactory mIconFactory;
+    private BubbleBadgeIconFactory mBadgeIconFactory;
     private boolean mSkipInflation;
     private Callback mCallback;
     private Executor mMainExecutor;
@@ -84,6 +85,7 @@
             BubbleController controller,
             BubbleStackView stackView,
             BubbleIconFactory factory,
+            BubbleBadgeIconFactory badgeFactory,
             boolean skipInflation,
             Callback c,
             Executor mainExecutor) {
@@ -92,6 +94,7 @@
         mController = new WeakReference<>(controller);
         mStackView = new WeakReference<>(stackView);
         mIconFactory = factory;
+        mBadgeIconFactory = badgeFactory;
         mSkipInflation = skipInflation;
         mCallback = c;
         mMainExecutor = mainExecutor;
@@ -100,7 +103,7 @@
     @Override
     protected BubbleViewInfo doInBackground(Void... voids) {
         return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(),
-                mIconFactory, mBubble, mSkipInflation);
+                mIconFactory, mBadgeIconFactory, mBubble, mSkipInflation);
     }
 
     @Override
@@ -127,6 +130,7 @@
         String appName;
         Bitmap bubbleBitmap;
         Bitmap badgeBitmap;
+        Bitmap mRawBadgeBitmap;
         int dotColor;
         Path dotPath;
         Bubble.FlyoutMessage flyoutMessage;
@@ -134,7 +138,8 @@
         @VisibleForTesting
         @Nullable
         public static BubbleViewInfo populate(Context c, BubbleController controller,
-                BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b,
+                BubbleStackView stackView, BubbleIconFactory iconFactory,
+                BubbleBadgeIconFactory badgeIconFactory, Bubble b,
                 boolean skipInflation) {
             BubbleViewInfo info = new BubbleViewInfo();
 
@@ -186,9 +191,11 @@
                 bubbleDrawable = appIcon;
             }
 
-            BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
+            BitmapInfo badgeBitmapInfo = badgeIconFactory.getBadgeBitmap(badgedIcon,
                     b.isImportantConversation());
             info.badgeBitmap = badgeBitmapInfo.icon;
+            // Raw badge bitmap never includes the important conversation ring
+            info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
             info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
 
             // Dot color & placement
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index 7e55282..3f6d41b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -43,6 +43,9 @@
     /** App badge drawable to draw above bubble icon. */
     @Nullable Bitmap getAppBadge();
 
+    /** Base app badge drawable without any markings. */
+    @Nullable Bitmap getRawAppBadge();
+
     /** Path of normalized bubble icon to draw dot on. */
     Path getDotPath();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index c82249b..af403d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -21,9 +21,12 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.app.NotificationChannel;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -191,10 +194,26 @@
      * @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
      *                       bubble up
      */
-    void onRankingUpdated(RankingMap rankingMap,
+    void onRankingUpdated(
+            RankingMap rankingMap,
             HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey);
 
     /**
+     * Called when a notification channel is modified, in response to
+     * {@link NotificationListenerService#onNotificationChannelModified}.
+     *
+     * @param pkg the package the notification channel belongs to.
+     * @param user the user the notification channel belongs to.
+     * @param channel the channel being modified.
+     * @param modificationType the type of modification that occurred to the channel.
+     */
+    void onNotificationChannelModified(
+            String pkg,
+            UserHandle user,
+            NotificationChannel channel,
+            int modificationType);
+
+    /**
      * Called when the status bar has become visible or invisible (either permanently or
      * temporarily).
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 7db49f0..e2bc360 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -34,6 +35,7 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -243,6 +245,19 @@
         }
     }
 
+    private void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+        synchronized (mDisplays) {
+            if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+                Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown"
+                        + " display, displayId=" + displayId);
+                return;
+            }
+            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+                mDisplayChangedListeners.get(i).onKeepClearAreasChanged(displayId, keepClearAreas);
+            }
+        }
+    }
+
     private static class DisplayRecord {
         private int mDisplayId;
         private Context mContext;
@@ -301,6 +316,13 @@
                 DisplayController.this.onFixedRotationFinished(displayId);
             });
         }
+
+        @Override
+        public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+            mMainExecutor.execute(() -> {
+                DisplayController.this.onKeepClearAreasChanged(displayId, keepClearAreas);
+            });
+        }
     }
 
     /**
@@ -335,5 +357,10 @@
          * Called when fixed rotation on a display is finished.
          */
         default void onFixedRotationFinished(int displayId) {}
+
+        /**
+         * Called when keep-clear areas on a display have changed.
+         */
+        default void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
index 9e01259..aac1d062 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
@@ -17,13 +17,10 @@
 
 import android.annotation.SuppressLint
 import android.content.Context
-import android.database.ContentObserver
 import android.graphics.PointF
-import android.os.Handler
-import android.os.UserHandle
+import android.os.VibrationAttributes
 import android.os.VibrationEffect
 import android.os.Vibrator
-import android.provider.Settings
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import android.view.View
@@ -147,6 +144,8 @@
 
     private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
     private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+    private val vibrationAttributes: VibrationAttributes = VibrationAttributes.createForUsage(
+            VibrationAttributes.USAGE_TOUCH)
 
     private var touchDown = PointF()
     private var touchSlop = 0
@@ -268,10 +267,6 @@
      */
     var flungIntoTargetSpringConfig = springConfig
 
-    init {
-        initHapticSettingObserver(context)
-    }
-
     /**
      * Adds the provided MagneticTarget to this object. The object will now be attracted to the
      * target if it strays within its magnetic field or is flung towards it.
@@ -468,8 +463,8 @@
     /** Plays the given vibration effect if haptics are enabled. */
     @SuppressLint("MissingPermission")
     private fun vibrateIfEnabled(effectId: Int) {
-        if (hapticsEnabled && systemHapticsEnabled) {
-            vibrator.vibrate(VibrationEffect.createPredefined(effectId))
+        if (hapticsEnabled) {
+            vibrator.vibrate(VibrationEffect.createPredefined(effectId), vibrationAttributes)
         }
     }
 
@@ -622,44 +617,6 @@
     }
 
     companion object {
-
-        /**
-         * Whether the HAPTIC_FEEDBACK_ENABLED setting is true.
-         *
-         * We put it in the companion object because we need to register a settings observer and
-         * [MagnetizedObject] doesn't have an obvious lifecycle so we don't have a good time to
-         * remove that observer. Since this settings is shared among all instances we just let all
-         * instances read from this value.
-         */
-        private var systemHapticsEnabled = false
-        private var hapticSettingObserverInitialized = false
-
-        private fun initHapticSettingObserver(context: Context) {
-            if (hapticSettingObserverInitialized) {
-                return
-            }
-
-            val hapticSettingObserver =
-                    object : ContentObserver(Handler.getMain()) {
-                        override fun onChange(selfChange: Boolean) {
-                            systemHapticsEnabled =
-                                    Settings.System.getIntForUser(
-                                            context.contentResolver,
-                                            Settings.System.HAPTIC_FEEDBACK_ENABLED,
-                                            0,
-                                            UserHandle.USER_CURRENT) != 0
-                        }
-                    }
-
-            context.contentResolver.registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED),
-                    true /* notifyForDescendants */, hapticSettingObserver)
-
-            // Trigger the observer once to initialize systemHapticsEnabled.
-            hapticSettingObserver.onChange(false /* selfChange */)
-            hapticSettingObserverInitialized = true
-        }
-
         /**
          * Magnetizes the given view. Magnetized views are attracted to one or more magnetic
          * targets. Magnetic targets attract objects that are dragged near them, and hold them there
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index ad9ebb2..36e55ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -137,14 +137,16 @@
             return;
         }
 
-        if (mIcon == null) {
-            // TODO: add fade-in animation.
+        if (mBackgroundLeash == null) {
             mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
                     RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
             t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
                     .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
                     .show(mBackgroundLeash);
+        }
 
+        if (mIcon == null && resizingTask.topActivityInfo != null) {
+            // TODO: add fade-in animation.
             mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
             mResizingIconView.setImageDrawable(mIcon);
             mResizingIconView.setVisibility(View.VISIBLE);
@@ -168,12 +170,16 @@
             return;
         }
 
+        if (mBackgroundLeash != null) {
+            t.remove(mBackgroundLeash);
+            mBackgroundLeash = null;
+        }
+
         if (mIcon != null) {
             mResizingIconView.setVisibility(View.GONE);
             mResizingIconView.setImageDrawable(null);
-            t.remove(mBackgroundLeash).hide(mIconLeash);
+            t.hide(mIconLeash);
             mIcon = null;
-            mBackgroundLeash = null;
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 711a0ac..f91d7e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -30,7 +30,6 @@
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -39,6 +38,7 @@
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
 import com.android.wm.shell.pip.tv.TvPipController;
 import com.android.wm.shell.pip.tv.TvPipMenuController;
 import com.android.wm.shell.pip.tv.TvPipNotificationController;
@@ -61,7 +61,7 @@
     static Optional<Pip> providePip(
             Context context,
             PipBoundsState pipBoundsState,
-            PipBoundsAlgorithm pipBoundsAlgorithm,
+            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
             TvPipMenuController tvPipMenuController,
             PipMediaController pipMediaController,
@@ -74,7 +74,7 @@
                 TvPipController.create(
                         context,
                         pipBoundsState,
-                        pipBoundsAlgorithm,
+                        tvPipBoundsAlgorithm,
                         pipTaskOrganizer,
                         pipTransitionController,
                         tvPipMenuController,
@@ -93,9 +93,9 @@
 
     @WMSingleton
     @Provides
-    static PipBoundsAlgorithm providePipBoundsAlgorithm(Context context,
+    static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
             PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
-        return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
+        return new TvPipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
     }
 
     @WMSingleton
@@ -109,10 +109,11 @@
     @Provides
     static PipTransitionController provideTvPipTransition(
             Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
-            PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipAnimationController pipAnimationController,
+            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) {
         return new TvPipTransition(pipBoundsState, pipMenuController,
-                pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+                tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
     }
 
     @WMSingleton
@@ -156,7 +157,7 @@
             SyncTransactionQueue syncTransactionQueue,
             PipBoundsState pipBoundsState,
             PipTransitionState pipTransitionState,
-            PipBoundsAlgorithm pipBoundsAlgorithm,
+            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
             PipTransitionController pipTransitionController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@@ -166,7 +167,7 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
-                syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
+                syncTransactionQueue, pipTransitionState, pipBoundsState, tvPipBoundsAlgorithm,
                 tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
                 pipTransitionController, splitScreenOptional, newSplitScreenOptional,
                 displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 23d9b8b..f61e624 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -40,6 +40,8 @@
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.back.BackAnimationController;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.common.DisplayController;
@@ -238,6 +240,17 @@
     }
 
     //
+    // Back animation
+    //
+
+    @WMSingleton
+    @Provides
+    static Optional<BackAnimation> provideBackAnimation(
+            Optional<BackAnimationController> backAnimationController) {
+        return backAnimationController.map(BackAnimationController::getBackAnimationImpl);
+    }
+
+    //
     // Bubbles (optional feature)
     //
 
@@ -678,4 +691,16 @@
                 legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
                 hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
     }
+
+    @WMSingleton
+    @Provides
+    static Optional<BackAnimationController> provideBackAnimationController(
+            @ShellMainThread ShellExecutor shellExecutor
+    ) {
+        if (BackAnimationController.IS_ENABLED) {
+            return Optional.of(
+                    new BackAnimationController(shellExecutor));
+        }
+        return Optional.empty();
+    }
 }
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 4f01dc6..7879e7a 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
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.view.WindowManager;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.launcher3.icons.IconProvider;
@@ -143,10 +144,10 @@
     static OneHandedController provideOneHandedController(Context context,
             WindowManager windowManager, DisplayController displayController,
             DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
-            UiEventLogger uiEventLogger, @ShellMainThread ShellExecutor mainExecutor,
-            @ShellMainThread Handler mainHandler) {
+            UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
+            @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
         return OneHandedController.create(context, windowManager, displayController, displayLayout,
-                taskStackListener, uiEventLogger, mainExecutor, mainHandler);
+                taskStackListener, jankMonitor, uiEventLogger, mainExecutor, mainHandler);
     }
 
     //
@@ -245,10 +246,11 @@
             PipBoundsState pipBoundsState, PipMediaController pipMediaController,
             SystemWindows systemWindows,
             Optional<SplitScreenController> splitScreenOptional,
+            PipUiEventLogger pipUiEventLogger,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
         return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
-                systemWindows, splitScreenOptional, mainExecutor, mainHandler);
+                systemWindows, splitScreenOptional, pipUiEventLogger, mainExecutor, mainHandler);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 8e6c05d..eda09e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -62,6 +62,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.InstanceId;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -132,6 +133,8 @@
         final Rect fullscreenHitRegion = new Rect(displayRegion);
         final boolean inLandscape = mSession.displayLayout.isLandscape();
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+        final float dividerWidth = mContext.getResources().getDimensionPixelSize(
+                R.dimen.split_divider_bar_width);
         // We allow splitting if we are already in split-screen or the running task is a standard
         // task in fullscreen mode.
         final boolean allowSplit = inSplitScreen
@@ -153,8 +156,11 @@
 
                 // If we have existing split regions use those bounds, otherwise split it 50/50
                 if (inSplitScreen) {
+                    // Add the divider bounds to each side since that counts for the hit region.
                     leftHitRegion.set(topOrLeftBounds);
+                    leftHitRegion.right += dividerWidth / 2;
                     rightHitRegion.set(bottomOrRightBounds);
+                    rightHitRegion.left -= dividerWidth / 2;
                 } else {
                     displayRegion.splitVertically(leftHitRegion, rightHitRegion);
                 }
@@ -170,8 +176,11 @@
 
                 // If we have existing split regions use those bounds, otherwise split it 50/50
                 if (inSplitScreen) {
+                    // Add the divider bounds to each side since that counts for the hit region.
                     topHitRegion.set(topOrLeftBounds);
+                    topHitRegion.bottom += dividerWidth / 2;
                     bottomHitRegion.set(bottomOrRightBounds);
+                    bottomHitRegion.top -= dividerWidth / 2;
                 } else {
                     displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
new file mode 100644
index 0000000..c20b7d9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.onehanded;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.io.PrintWriter;
+
+/**
+ * Holds view hierarchy of a root surface and helps inflate a themeable view for background.
+ */
+public final class BackgroundWindowManager extends WindowlessWindowManager {
+    private static final String TAG = BackgroundWindowManager.class.getSimpleName();
+    private static final int THEME_COLOR_OFFSET = 10;
+
+    private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
+            mTransactionFactory;
+
+    private Context mContext;
+    private Rect mDisplayBounds;
+    private SurfaceControlViewHost mViewHost;
+    private SurfaceControl mLeash;
+    private View mBackgroundView;
+    private @OneHandedState.State int mCurrentState;
+
+    public BackgroundWindowManager(Context context) {
+        super(context.getResources().getConfiguration(), null /* rootSurface */,
+                null /* hostInputToken */);
+        mContext = context;
+        mTransactionFactory = SurfaceControl.Transaction::new;
+    }
+
+    @Override
+    public SurfaceControl getSurfaceControl(IWindow window) {
+        return super.getSurfaceControl(window);
+    }
+
+    @Override
+    public void setConfiguration(Configuration configuration) {
+        super.setConfiguration(configuration);
+        mContext = mContext.createConfigurationContext(configuration);
+    }
+
+    /**
+     * onConfigurationChanged events for updating background theme color.
+     */
+    public void onConfigurationChanged() {
+        if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
+            updateThemeOnly();
+        }
+    }
+
+    /**
+     * One-handed mode state changed callback
+     * @param newState of One-handed mode representing by {@link OneHandedState}
+     */
+    public void onStateChanged(int newState) {
+        mCurrentState = newState;
+    }
+
+    @Override
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+                .setColorLayer()
+                .setBufferSize(mDisplayBounds.width(), mDisplayBounds.height())
+                .setFormat(PixelFormat.RGB_888)
+                .setOpaque(true)
+                .setName(TAG)
+                .setCallsite("BackgroundWindowManager#attachToParentSurface");
+        mLeash = builder.build();
+        b.setParent(mLeash);
+    }
+
+    /** Inflates background view on to the root surface. */
+    boolean initView() {
+        if (mBackgroundView != null || mViewHost != null) {
+            return false;
+        }
+
+        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        mBackgroundView = (View) LayoutInflater.from(mContext)
+                .inflate(R.layout.background_panel, null /* root */);
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                mDisplayBounds.width(), mDisplayBounds.height(), 0 /* TYPE NONE */,
+                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
+                        | FLAG_SLIPPERY, PixelFormat.TRANSLUCENT);
+        lp.token = new Binder();
+        lp.setTitle("background-panel");
+        lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+        mBackgroundView.setBackgroundColor(getThemeColorForBackground());
+        mViewHost.setView(mBackgroundView, lp);
+        return true;
+    }
+
+    /**
+     * Called when onDisplayAdded() or onDisplayRemoved() callback.
+     * @param displayLayout The latest {@link DisplayLayout} for display bounds.
+     */
+    public void onDisplayChanged(DisplayLayout displayLayout) {
+        // One-handed mode is only available on portrait.
+        if (displayLayout.height() > displayLayout.width()) {
+            mDisplayBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
+        } else {
+            mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
+        }
+    }
+
+    private void updateThemeOnly() {
+        if (mBackgroundView == null || mViewHost == null || mLeash == null) {
+            Slog.w(TAG, "Background view or SurfaceControl does not exist when trying to "
+                    + "update theme only!");
+            return;
+        }
+
+        WindowManager.LayoutParams lp = (WindowManager.LayoutParams)
+                mBackgroundView.getLayoutParams();
+        mBackgroundView.setBackgroundColor(getThemeColorForBackground());
+        mViewHost.setView(mBackgroundView, lp);
+    }
+
+    /**
+     * Shows the background layer when One-handed mode triggered.
+     */
+    public void showBackgroundLayer() {
+        if (!initView()) {
+            updateThemeOnly();
+            return;
+        }
+        if (mLeash == null) {
+            Slog.w(TAG, "SurfaceControl mLeash is null, can't show One-handed mode "
+                    + "background panel!");
+            return;
+        }
+
+        mTransactionFactory.getTransaction()
+                .setAlpha(mLeash, 1.0f)
+                .setLayer(mLeash, -1 /* at bottom-most layer */)
+                .show(mLeash)
+                .apply();
+    }
+
+    /**
+     * Remove the leash of background layer after stop One-handed mode.
+     */
+    public void removeBackgroundLayer() {
+        if (mBackgroundView != null) {
+            mBackgroundView = null;
+        }
+
+        if (mViewHost != null) {
+            mViewHost.release();
+            mViewHost = null;
+        }
+
+        if (mLeash != null) {
+            mTransactionFactory.getTransaction().remove(mLeash).apply();
+            mLeash = null;
+        }
+    }
+
+    /**
+     * Gets {@link SurfaceControl} of the background layer.
+     * @return {@code null} if not exist.
+     */
+    @Nullable
+    SurfaceControl getSurfaceControl() {
+        return mLeash;
+    }
+
+    private int getThemeColor() {
+        final Context themedContext = new ContextThemeWrapper(mContext,
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        return themedContext.getColor(R.color.one_handed_tutorial_background_color);
+    }
+
+    int getThemeColorForBackground() {
+        final int origThemeColor = getThemeColor();
+        return android.graphics.Color.argb(Color.alpha(origThemeColor),
+                Color.red(origThemeColor) - THEME_COLOR_OFFSET,
+                Color.green(origThemeColor) - THEME_COLOR_OFFSET,
+                Color.blue(origThemeColor) - THEME_COLOR_OFFSET);
+    }
+
+    private float adjustColor(int origColor) {
+        return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
+    }
+
+    void dump(@NonNull PrintWriter pw) {
+        final String innerPrefix = "  ";
+        pw.println(TAG);
+        pw.print(innerPrefix + "mDisplayBounds=");
+        pw.println(mDisplayBounds);
+        pw.print(innerPrefix + "mViewHost=");
+        pw.println(mViewHost);
+        pw.print(innerPrefix + "mLeash=");
+        pw.println(mLeash);
+        pw.print(innerPrefix + "mBackgroundView=");
+        pw.println(mBackgroundView);
+    }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
deleted file mode 100644
index 9e1c61a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.onehanded;
-
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.view.ContextThemeWrapper;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.animation.LinearInterpolator;
-import android.window.DisplayAreaAppearedInfo;
-import android.window.DisplayAreaInfo;
-import android.window.DisplayAreaOrganizer;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
-
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Manages OneHanded color background layer areas.
- * To avoid when turning the Dark theme on, users can not clearly identify
- * the screen has entered one handed mode.
- */
-public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
-        implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener {
-    private static final String TAG = "OneHandedBackgroundPanelOrganizer";
-    private static final int THEME_COLOR_OFFSET = 10;
-    private static final int ALPHA_ANIMATION_DURATION = 200;
-
-    private final Context mContext;
-    private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
-            mTransactionFactory;
-
-    private @OneHandedState.State int mCurrentState;
-    private ValueAnimator mAlphaAnimator;
-
-    private float mTranslationFraction;
-    private float[] mThemeColor;
-
-    /**
-     * The background to distinguish the boundary of translated windows and empty region when
-     * one handed mode triggered.
-     */
-    private Rect mBkgBounds;
-    private Rect mStableInsets;
-
-    @Nullable
-    @VisibleForTesting
-    SurfaceControl mBackgroundSurface;
-    @Nullable
-    private SurfaceControl mParentLeash;
-
-    public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
-            OneHandedSettingsUtil settingsUtil, Executor executor) {
-        super(executor);
-        mContext = context;
-        mTranslationFraction = settingsUtil.getTranslationFraction(context);
-        mTransactionFactory = SurfaceControl.Transaction::new;
-        updateThemeColors();
-    }
-
-    @Override
-    public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
-            @NonNull SurfaceControl leash) {
-        mParentLeash = leash;
-    }
-
-    @Override
-    public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
-        final List<DisplayAreaAppearedInfo> displayAreaInfos;
-        displayAreaInfos = super.registerOrganizer(displayAreaFeature);
-        for (int i = 0; i < displayAreaInfos.size(); i++) {
-            final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
-            onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
-        }
-        return displayAreaInfos;
-    }
-
-    @Override
-    public void unregisterOrganizer() {
-        super.unregisterOrganizer();
-        removeBackgroundPanelLayer();
-        mParentLeash = null;
-    }
-
-    @Override
-    public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
-        final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos);
-        tx.setPosition(mBackgroundSurface, 0, yTopPos);
-    }
-
-    @Nullable
-    @VisibleForTesting
-    boolean isRegistered() {
-        return mParentLeash != null;
-    }
-
-    void createBackgroundSurface() {
-        mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
-                .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
-                .setColorLayer()
-                .setFormat(PixelFormat.RGB_888)
-                .setOpaque(true)
-                .setName("one-handed-background-panel")
-                .setCallsite("OneHandedBackgroundPanelOrganizer")
-                .build();
-
-        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
-        mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
-        mAlphaAnimator.setInterpolator(new LinearInterpolator());
-        mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION);
-        mAlphaAnimator.addUpdateListener(
-                animator -> detachBackgroundFromParent(animator));
-    }
-
-    void detachBackgroundFromParent(ValueAnimator animator) {
-        if (mBackgroundSurface == null || mParentLeash == null) {
-            return;
-        }
-        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
-        final float currentValue = (float) animator.getAnimatedValue();
-        final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
-        if (currentValue == 0.0f) {
-            tx.reparent(mBackgroundSurface, null).apply();
-        } else {
-            tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply();
-        }
-    }
-
-    /**
-     * Called when onDisplayAdded() or onDisplayRemoved() callback.
-     *
-     * @param displayLayout The latest {@link DisplayLayout} representing current displayId
-     */
-    public void onDisplayChanged(DisplayLayout displayLayout) {
-        mStableInsets = displayLayout.stableInsets();
-        // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
-        if (displayLayout.height() > displayLayout.width()) {
-            mBkgBounds = new Rect(0, 0, displayLayout.width(),
-                    Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top);
-        } else {
-            mBkgBounds = new Rect(0, 0, displayLayout.height(),
-                    Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top);
-        }
-    }
-
-    @VisibleForTesting
-    void onStart() {
-        if (mBackgroundSurface == null) {
-            createBackgroundSurface();
-        }
-        showBackgroundPanelLayer();
-    }
-
-    /**
-     * Called when transition finished.
-     */
-    public void onStopFinished() {
-        if (mAlphaAnimator == null) {
-            return;
-        }
-        mAlphaAnimator.start();
-    }
-
-    @VisibleForTesting
-    void showBackgroundPanelLayer() {
-        if (mParentLeash == null) {
-            return;
-        }
-
-        if (mBackgroundSurface == null) {
-            createBackgroundSurface();
-        }
-
-        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
-        if (mAlphaAnimator.isRunning()) {
-            mAlphaAnimator.end();
-        }
-
-        mTransactionFactory.getTransaction()
-                .reparent(mBackgroundSurface, mParentLeash)
-                .setAlpha(mBackgroundSurface, 1.0f)
-                .setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
-                .setColor(mBackgroundSurface, mThemeColor)
-                .show(mBackgroundSurface)
-                .apply();
-    }
-
-    @VisibleForTesting
-    void removeBackgroundPanelLayer() {
-        if (mBackgroundSurface == null) {
-            return;
-        }
-
-        mTransactionFactory.getTransaction()
-                .remove(mBackgroundSurface)
-                .apply();
-        mBackgroundSurface = null;
-    }
-
-    /**
-     * onConfigurationChanged events for updating tutorial text.
-     */
-    public void onConfigurationChanged() {
-        updateThemeColors();
-
-        if (mCurrentState != STATE_ACTIVE) {
-            return;
-        }
-        showBackgroundPanelLayer();
-    }
-
-    private void updateThemeColors() {
-        final Context themedContext = new ContextThemeWrapper(mContext,
-                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
-        final int themeColor = themedContext.getColor(
-                R.color.one_handed_tutorial_background_color);
-        mThemeColor = new float[]{
-                adjustColor(Color.red(themeColor)),
-                adjustColor(Color.green(themeColor)),
-                adjustColor(Color.blue(themeColor))};
-    }
-
-    private float adjustColor(int origColor) {
-        return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        mCurrentState = newState;
-    }
-
-    void dump(@NonNull PrintWriter pw) {
-        final String innerPrefix = "  ";
-        pw.println(TAG);
-        pw.print(innerPrefix + "mBackgroundSurface=");
-        pw.println(mBackgroundSurface);
-        pw.print(innerPrefix + "mBkgBounds=");
-        pw.println(mBkgBounds);
-        pw.print(innerPrefix + "mThemeColor=");
-        pw.println(mThemeColor);
-        pw.print(innerPrefix + "mTranslationFraction=");
-        pw.println(mTranslationFraction);
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c9c73fd8..48acfc1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -45,6 +45,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayChangeController;
@@ -98,7 +99,6 @@
 
     private OneHandedEventCallback mEventCallback;
     private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
-    private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
     private OneHandedUiEventLogger mOneHandedUiEventLogger;
 
     private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -162,7 +162,6 @@
                 public void onStopFinished(Rect bounds) {
                     mState.setState(STATE_NONE);
                     notifyShortcutStateChanged(STATE_NONE);
-                    mBackgroundPanelOrganizer.onStopFinished();
                 }
             };
 
@@ -194,37 +193,34 @@
     public static OneHandedController create(
             Context context, WindowManager windowManager, DisplayController displayController,
             DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
-            UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
+            InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
+            ShellExecutor mainExecutor, Handler mainHandler) {
         OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
         OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
         OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
         OneHandedState oneHandedState = new OneHandedState();
+        BackgroundWindowManager backgroundWindowManager = new BackgroundWindowManager(context);
         OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
-                settingsUtil, windowManager);
+                settingsUtil, windowManager, backgroundWindowManager);
         OneHandedAnimationController animationController =
                 new OneHandedAnimationController(context);
         OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
                 mainExecutor);
-        OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
-                new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil,
-                        mainExecutor);
         OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
                 context, displayLayout, settingsUtil, animationController, tutorialHandler,
-                oneHandedBackgroundPanelOrganizer, mainExecutor);
+                jankMonitor, mainExecutor);
         OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
         IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
-        return new OneHandedController(context, displayController,
-                oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
-                settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
-                oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
-                mainHandler);
+        return new OneHandedController(context, displayController, organizer, touchHandler,
+                tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
+                jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener,
+                mainExecutor, mainHandler);
     }
 
     @VisibleForTesting
     OneHandedController(Context context,
             DisplayController displayController,
-            OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer,
             OneHandedDisplayAreaOrganizer displayAreaOrganizer,
             OneHandedTouchHandler touchHandler,
             OneHandedTutorialHandler tutorialHandler,
@@ -232,6 +228,7 @@
             OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
             OneHandedTimeoutHandler timeoutHandler,
             OneHandedState state,
+            InteractionJankMonitor jankMonitor,
             OneHandedUiEventLogger uiEventsLogger,
             IOverlayManager overlayManager,
             TaskStackListenerImpl taskStackListener,
@@ -240,7 +237,6 @@
         mContext = context;
         mOneHandedSettingsUtil = settingsUtil;
         mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
-        mBackgroundPanelOrganizer = backgroundPanelOrganizer;
         mDisplayAreaOrganizer = displayAreaOrganizer;
         mDisplayController = displayController;
         mTouchHandler = touchHandler;
@@ -283,7 +279,6 @@
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityStateChangeListener);
 
-        mState.addSListeners(mBackgroundPanelOrganizer);
         mState.addSListeners(mTutorialHandler);
     }
 
@@ -365,7 +360,6 @@
                 mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
         mOneHandedAccessibilityUtil.announcementForScreenReader(
                 mOneHandedAccessibilityUtil.getOneHandedStartDescription());
-        mBackgroundPanelOrganizer.onStart();
         mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
         mTimeoutHandler.resetTimer();
         mOneHandedUiEventLogger.writeEvent(
@@ -458,7 +452,6 @@
         }
         mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
         mTutorialHandler.onDisplayChanged(newDisplayLayout);
-        mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
     }
 
     private ContentObserver getObserver(Runnable onChangeRunnable) {
@@ -582,7 +575,6 @@
 
         if (!mIsOneHandedEnabled) {
             mDisplayAreaOrganizer.unregisterOrganizer();
-            mBackgroundPanelOrganizer.unregisterOrganizer();
             // Do NOT register + unRegister DA in the same call
             return;
         }
@@ -591,11 +583,6 @@
             mDisplayAreaOrganizer.registerOrganizer(
                     OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
         }
-
-        if (!mBackgroundPanelOrganizer.isRegistered()) {
-            mBackgroundPanelOrganizer.registerOrganizer(
-                    OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
-        }
     }
 
     @VisibleForTesting
@@ -610,13 +597,12 @@
     }
 
     private void onConfigChanged(Configuration newConfig) {
-        if (mTutorialHandler == null || mBackgroundPanelOrganizer == null) {
+        if (mTutorialHandler == null) {
             return;
         }
         if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
             return;
         }
-        mBackgroundPanelOrganizer.onConfigurationChanged();
         mTutorialHandler.onConfigurationChanged();
     }
 
@@ -647,10 +633,6 @@
         pw.print(innerPrefix + "mIsSwipeToNotificationEnabled=");
         pw.println(mIsSwipeToNotificationEnabled);
 
-        if (mBackgroundPanelOrganizer != null) {
-            mBackgroundPanelOrganizer.dump(pw);
-        }
-
         if (mDisplayAreaOrganizer != null) {
             mDisplayAreaOrganizer.dump(pw);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index ec3ef5a..f61d1b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,12 +16,15 @@
 
 package com.android.wm.shell.onehanded;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_EXIT_TRANSITION;
 import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
 import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaAppearedInfo;
@@ -34,6 +37,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -41,6 +45,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Manages OneHanded display areas such as offset.
@@ -62,6 +67,8 @@
     private final Rect mLastVisualDisplayBounds = new Rect();
     private final Rect mDefaultDisplayBounds = new Rect();
     private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+    private final InteractionJankMonitor mJankMonitor;
+    private final Context mContext;
 
     private boolean mIsReady;
     private float mLastVisualOffset = 0;
@@ -73,7 +80,6 @@
             mSurfaceControlTransactionFactory;
     private OneHandedTutorialHandler mTutorialHandler;
     private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
-    private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
 
     @VisibleForTesting
     OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -95,7 +101,11 @@
                 public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
                         OneHandedAnimationController.OneHandedTransitionAnimator animator) {
                     mAnimationController.removeAnimator(animator.getToken());
+                    final boolean isEntering = animator.getTransitionDirection()
+                            == TRANSITION_DIRECTION_TRIGGER;
                     if (mAnimationController.isAnimatorsConsumed()) {
+                        endCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+                                : CUJ_ONE_HANDED_EXIT_TRANSITION);
                         finishOffset((int) animator.getDestinationOffset(),
                                 animator.getTransitionDirection());
                     }
@@ -105,7 +115,11 @@
                 public void onOneHandedAnimationCancel(
                         OneHandedAnimationController.OneHandedTransitionAnimator animator) {
                     mAnimationController.removeAnimator(animator.getToken());
+                    final boolean isEntering = animator.getTransitionDirection()
+                            == TRANSITION_DIRECTION_TRIGGER;
                     if (mAnimationController.isAnimatorsConsumed()) {
+                        cancelCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+                                : CUJ_ONE_HANDED_EXIT_TRANSITION);
                         finishOffset((int) animator.getDestinationOffset(),
                                 animator.getTransitionDirection());
                     }
@@ -120,19 +134,20 @@
             OneHandedSettingsUtil oneHandedSettingsUtil,
             OneHandedAnimationController animationController,
             OneHandedTutorialHandler tutorialHandler,
-            OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
+            InteractionJankMonitor jankMonitor,
             ShellExecutor mainExecutor) {
         super(mainExecutor);
+        mContext = context;
         setDisplayLayout(displayLayout);
         mOneHandedSettingsUtil = oneHandedSettingsUtil;
         mAnimationController = animationController;
+        mJankMonitor = jankMonitor;
         final int animationDurationConfig = context.getResources().getInteger(
                 R.integer.config_one_handed_translate_animation_duration);
         mEnterExitAnimationDurationMs =
                 SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
                         animationDurationConfig);
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
-        mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
         mTutorialHandler = tutorialHandler;
     }
 
@@ -197,6 +212,11 @@
         final int direction = yOffset > 0
                 ? TRANSITION_DIRECTION_TRIGGER
                 : TRANSITION_DIRECTION_EXIT;
+        if (direction == TRANSITION_DIRECTION_TRIGGER) {
+            beginCUJTracing(CUJ_ONE_HANDED_ENTER_TRANSITION, "enterOneHanded");
+        } else {
+            beginCUJTracing(CUJ_ONE_HANDED_EXIT_TRANSITION, "stopOneHanded");
+        }
         mDisplayAreaTokenMap.forEach(
                 (token, leash) -> {
                     animateWindows(token, leash, fromPos, yOffset, direction,
@@ -235,7 +255,6 @@
             animator.setTransitionDirection(direction)
                     .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
                     .addOneHandedAnimationCallback(mTutorialHandler)
-                    .addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
                     .setDuration(durationMs)
                     .start();
         }
@@ -302,6 +321,26 @@
         mTransitionCallbacks.add(callback);
     }
 
+    void beginCUJTracing(@InteractionJankMonitor.CujType int cujType, @Nullable String tag) {
+        final Map.Entry<WindowContainerToken, SurfaceControl> firstEntry =
+                getDisplayAreaTokenMap().entrySet().iterator().next();
+        final InteractionJankMonitor.Configuration.Builder builder =
+                InteractionJankMonitor.Configuration.Builder.withSurface(
+                        cujType, mContext, firstEntry.getValue());
+        if (!TextUtils.isEmpty(tag)) {
+            builder.setTag(tag);
+        }
+        mJankMonitor.begin(builder);
+    }
+
+    void endCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+        mJankMonitor.end(cujType);
+    }
+
+    void cancelCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+        mJankMonitor.cancel(cujType);
+    }
+
     void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 88f3375..04e8cf9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -32,7 +32,6 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.SystemProperties;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -65,6 +64,7 @@
 
     private final float mTutorialHeightRatio;
     private final WindowManager mWindowManager;
+    private final BackgroundWindowManager mBackgroundWindowManager;
 
     private @OneHandedState.State int mCurrentState;
     private int mTutorialAreaHeight;
@@ -79,9 +79,10 @@
     private int mAlphaAnimationDurationMs;
 
     public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil,
-            WindowManager windowManager) {
+            WindowManager windowManager, BackgroundWindowManager backgroundWindowManager) {
         mContext = context;
         mWindowManager = windowManager;
+        mBackgroundWindowManager = backgroundWindowManager;
         mTutorialHeightRatio = settingsUtil.getTranslationFraction(context);
         mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context);
     }
@@ -110,8 +111,19 @@
     }
 
     @Override
+    public void onStartFinished(Rect bounds) {
+        fillBackgroundColor();
+    }
+
+    @Override
+    public void onStopFinished(Rect bounds) {
+        removeBackgroundSurface();
+    }
+
+    @Override
     public void onStateChanged(int newState) {
         mCurrentState = newState;
+        mBackgroundWindowManager.onStateChanged(newState);
         switch (newState) {
             case STATE_ENTERING:
                 createViewAndAttachToWindow(mContext);
@@ -126,7 +138,6 @@
             case STATE_NONE:
                 checkTransitionEnd();
                 removeTutorialFromWindowManager();
-                break;
             default:
                 break;
         }
@@ -146,6 +157,7 @@
         }
         mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
         mAlphaTransitionStart = mTutorialAreaHeight * START_TRANSITION_FRACTION;
+        mBackgroundWindowManager.onDisplayChanged(displayLayout);
     }
 
     @VisibleForTesting
@@ -169,6 +181,7 @@
     private void attachTargetToWindow() {
         try {
             mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
+            mBackgroundWindowManager.showBackgroundLayer();
         } catch (IllegalStateException e) {
             // This shouldn't happen, but if the target is already added, just update its
             // layout params.
@@ -186,6 +199,11 @@
         mTargetViewContainer = null;
     }
 
+    @VisibleForTesting
+    void removeBackgroundSurface() {
+        mBackgroundWindowManager.removeBackgroundLayer();
+    }
+
     /**
      * Returns layout params for the dismiss target, using the latest display metrics.
      */
@@ -213,9 +231,12 @@
      * onConfigurationChanged events for updating tutorial text.
      */
     public void onConfigurationChanged() {
+        mBackgroundWindowManager.onConfigurationChanged();
+
         removeTutorialFromWindowManager();
         if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
             createViewAndAttachToWindow(mContext);
+            fillBackgroundColor();
             updateThemeColor();
             checkTransitionEnd();
         }
@@ -247,6 +268,14 @@
         tutorialDesc.setTextColor(themedTextColorSecondary);
     }
 
+    private void fillBackgroundColor() {
+        if (mTargetViewContainer == null || mBackgroundWindowManager == null) {
+            return;
+        }
+        mTargetViewContainer.setBackgroundColor(
+                mBackgroundWindowManager.getThemeColorForBackground());
+    }
+
     private void setupAlphaTransition(boolean isEntering) {
         final float start = isEntering ? 0.0f : 1.0f;
         final float end = isEntering ? 1.0f : 0.0f;
@@ -282,5 +311,9 @@
         pw.println(mAlphaTransitionStart);
         pw.print(innerPrefix + "mAlphaAnimationDurationMs=");
         pw.println(mAlphaAnimationDurationMs);
+
+        if (mBackgroundWindowManager != null) {
+            mBackgroundWindowManager.dump(pw);
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 9575b0a..e616172 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -584,9 +584,11 @@
                     setCurrentValue(bounds);
                     if (inScaleTransition() || sourceHintRect == null) {
                         if (isOutPipDirection) {
-                            getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
+                            getSurfaceTransactionHelper().crop(tx, leash, end)
+                                    .scale(tx, leash, end, bounds);
                         } else {
-                            getSurfaceTransactionHelper().scale(tx, leash, base, bounds, angle)
+                            getSurfaceTransactionHelper().crop(tx, leash, base)
+                                    .scale(tx, leash, base, bounds, angle)
                                     .round(tx, leash, base, bounds);
                         }
                     } else {
@@ -622,13 +624,13 @@
                         if (rotationDelta == ROTATION_90) {
                             degree = 90 * (1 - fraction);
                             x = fraction * (end.left - start.left)
-                                    + start.left + start.right * (1 - fraction);
+                                    + start.left + start.width() * (1 - fraction);
                             y = fraction * (end.top - start.top) + start.top;
                         } else {
                             degree = -90 * (1 - fraction);
                             x = fraction * (end.left - start.left) + start.left;
                             y = fraction * (end.top - start.top)
-                                    + start.top + start.bottom * (1 - fraction);
+                                    + start.top + start.height() * (1 - fraction);
                         }
                     } else {
                         if (rotationDelta == ROTATION_90) {
@@ -646,8 +648,10 @@
                     getSurfaceTransactionHelper()
                             .rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
                                     insets, degree, x, y, isOutPipDirection,
-                                    rotationDelta == ROTATION_270 /* clockwise */)
-                            .round(tx, leash, sourceBounds, bounds);
+                                    rotationDelta == ROTATION_270 /* clockwise */);
+                    if (shouldApplyCornerRadius()) {
+                        getSurfaceTransactionHelper().round(tx, leash, sourceBounds, bounds);
+                    }
                     tx.apply();
                 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index a4b866aa..1a3c51e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -44,7 +44,7 @@
     private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
     private static final float INVALID_SNAP_FRACTION = -1f;
 
-    private final @NonNull PipBoundsState mPipBoundsState;
+    protected final @NonNull PipBoundsState mPipBoundsState;
     private final PipSnapAlgorithm mSnapAlgorithm;
 
     private float mDefaultSizePercent;
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 ae7b82f..17005ea 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
@@ -397,8 +397,6 @@
             return;
         }
 
-        mPipUiEventLoggerLogger.log(
-                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
         if (ENABLE_SHELL_TRANSITIONS) {
@@ -416,17 +414,32 @@
         final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                 : TRANSITION_DIRECTION_LEAVE_PIP;
-        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
-        mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
-        tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
-        // We set to fullscreen here for now, but later it will be set to UNDEFINED for
-        // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
-        wct.setActivityWindowingMode(mToken,
-                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
-                        ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        : WINDOWING_MODE_FULLSCREEN);
-        wct.setBounds(mToken, destinationBounds);
-        wct.setBoundsChangeTransaction(mToken, tx);
+
+        if (Transitions.ENABLE_SHELL_TRANSITIONS && direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+            // When exit to fullscreen with Shell transition enabled, we update the Task windowing
+            // mode directly so that it can also trigger display rotation and visibility update in
+            // the same transition if there will be any.
+            wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+            // We can inherit the parent bounds as it is going to be fullscreen. The
+            // destinationBounds calculated above will be incorrect if this is with rotation.
+            wct.setBounds(mToken, null);
+        } else {
+            final SurfaceControl.Transaction tx =
+                    mSurfaceControlTransactionFactory.getTransaction();
+            mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
+                    mPipBoundsState.getBounds());
+            tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+            // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+            // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+            wct.setActivityWindowingMode(mToken,
+                    direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+                            && !requestEnterSplit
+                            ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                            : WINDOWING_MODE_FULLSCREEN);
+            wct.setBounds(mToken, destinationBounds);
+            wct.setBoundsChangeTransaction(mToken, tx);
+        }
+
         // Set the exiting state first so if there is fixed rotation later, the running animation
         // won't be interrupted by alpha animation for existing PiP.
         mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
@@ -729,24 +742,18 @@
         if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
             return;
         }
+        if (Transitions.ENABLE_SHELL_TRANSITIONS
+                && mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP) {
+            // With Shell transition, we do the cleanup in PipTransition after exiting PIP.
+            return;
+        }
         final WindowContainerToken token = info.token;
         Objects.requireNonNull(token, "Requires valid WindowContainerToken");
         if (token.asBinder() != mToken.asBinder()) {
             Log.wtf(TAG, "Unrecognized token: " + token);
             return;
         }
-        clearWaitForFixedRotation();
-        mPipTransitionState.setInSwipePipToHomeTransition(false);
-        mPictureInPictureParams = null;
-        mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
-        // Re-set the PIP bounds to none.
-        mPipBoundsState.setBounds(new Rect());
-        mPipUiEventLoggerLogger.setTaskInfo(null);
-        mPipMenuController.detach();
-
-        if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
-            mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
-        }
+        onExitPipFinished(info);
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             mPipTransitionController.forceFinishTransition();
@@ -843,6 +850,22 @@
         clearWaitForFixedRotation();
     }
 
+    /** Called when exiting PIP tranisiton is finished to do the state cleanup. */
+    void onExitPipFinished(TaskInfo info) {
+        clearWaitForFixedRotation();
+        mPipTransitionState.setInSwipePipToHomeTransition(false);
+        mPictureInPictureParams = null;
+        mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
+        // Re-set the PIP bounds to none.
+        mPipBoundsState.setBounds(new Rect());
+        mPipUiEventLoggerLogger.setTaskInfo(null);
+        mPipMenuController.detach();
+
+        if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
+            mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
+        }
+    }
+
     private void fadeExistingPip(boolean show) {
         final float alphaStart = show ? 0 : 1;
         final float alphaEnd = show ? 1 : 0;
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 2749bc8..3e5d5f6 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
@@ -19,9 +19,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.util.RotationUtils.deltaRotation;
+import static android.util.RotationUtils.rotateBounds;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -30,6 +35,7 @@
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
 import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -38,13 +44,14 @@
 import android.app.TaskInfo;
 import android.content.Context;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
-import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.NonNull;
@@ -53,6 +60,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.CounterRotatorHelper;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -72,8 +80,12 @@
     private final Optional<SplitScreenController> mSplitScreenOptional;
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private Transitions.TransitionFinishCallback mFinishCallback;
-    private Rect mExitDestinationBounds = new Rect();
-    private IBinder mExitTransition = null;
+    private final Rect mExitDestinationBounds = new Rect();
+    @Nullable
+    private IBinder mExitTransition;
+    /** The Task window that is currently in PIP windowing mode. */
+    @Nullable
+    private WindowContainerToken mCurrentPipTaskToken;
 
     public PipTransition(Context context,
             PipBoundsState pipBoundsState,
@@ -119,112 +131,69 @@
     }
 
     @Override
-    public boolean startAnimation(@android.annotation.NonNull IBinder transition,
-            @android.annotation.NonNull TransitionInfo info,
-            @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
-            @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
-            @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+    public boolean startAnimation(@NonNull IBinder transition,
+            @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        // Exiting PIP.
         final int type = info.getType();
-        if (mExitTransition == transition) {
+        if (transition.equals(mExitTransition)) {
+            mExitDestinationBounds.setEmpty();
             mExitTransition = null;
 
-            if (type == TRANSIT_EXIT_PIP_TO_SPLIT) {
-                return startExitToSplitAnimation(
-                        info, startTransaction, finishTransaction, finishCallback);
-            }
-
-            if (info.getChanges().size() == 1) {
-                if (mFinishCallback != null) {
-                    mFinishCallback.onTransitionFinished(null, null);
-                    mFinishCallback = null;
-                    throw new RuntimeException("Previous callback not called, aborting exit PIP.");
-                }
-
-                final TransitionInfo.Change change = info.getChanges().get(0);
-                mFinishCallback = finishCallback;
-                startTransaction.apply();
-                boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
-                        new Rect(mExitDestinationBounds));
-                mExitDestinationBounds.setEmpty();
-                return success;
-            } else {
-                Log.e(TAG, "Got an exit-pip transition with unexpected change-list");
-            }
-        }
-
-        if (type == TRANSIT_REMOVE_PIP) {
             if (mFinishCallback != null) {
-                mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+                mFinishCallback.onTransitionFinished(null, null);
                 mFinishCallback = null;
-                throw new RuntimeException("Previous callback not called, aborting remove PIP.");
+                throw new RuntimeException("Previous callback not called, aborting exit PIP.");
             }
 
-            startTransaction.apply();
-            finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
-                    mPipBoundsState.getDisplayBounds());
-            finishCallback.onTransitionFinished(null, null);
+            final TransitionInfo.Change exitPipChange = findCurrentPipChange(info);
+            if (exitPipChange == null) {
+                throw new RuntimeException("Cannot find the pip window for exit-pip transition.");
+            }
+
+            switch (type) {
+                case TRANSIT_EXIT_PIP:
+                    startExitAnimation(info, startTransaction, finishTransaction, finishCallback,
+                            exitPipChange);
+                    break;
+                case TRANSIT_EXIT_PIP_TO_SPLIT:
+                    startExitToSplitAnimation(info, startTransaction, finishTransaction,
+                            finishCallback, exitPipChange);
+                    break;
+                case TRANSIT_REMOVE_PIP:
+                    removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
+                            exitPipChange);
+                    break;
+                default:
+                    throw new IllegalStateException("mExitTransition with unexpected transit type="
+                            + transitTypeToString(type));
+            }
+            mCurrentPipTaskToken = null;
             return true;
         }
 
-        // We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
-        // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
-        if (type != TRANSIT_PIP && type != TRANSIT_OPEN) {
-            // In case the PIP window is part of rotation transition, reset the bounds and rounded
-            // corner.
-            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                if (change.getMode() == TRANSIT_CHANGE && change.getTaskInfo() != null
-                        && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
-                        == WINDOWING_MODE_PINNED) {
-                    final SurfaceControl leash = change.getLeash();
-                    final Rect destBounds = mPipBoundsState.getBounds();
-                    final boolean isInPip = mPipTransitionState.isInPip();
-                    mSurfaceTransactionHelper
-                            .crop(startTransaction, leash, destBounds)
-                            .round(startTransaction, leash, isInPip);
-                    mSurfaceTransactionHelper
-                            .crop(finishTransaction, leash, destBounds)
-                            .round(finishTransaction, leash, isInPip);
-                    break;
-                }
-            }
-            return false;
+        // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
+        // happen when a new activity requests enter PIP). In this case, we just show this Task in
+        // its end state, and play other animation as normal.
+        final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+        if (currentPipChange != null
+                && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
+            resetPrevPip(currentPipChange, startTransaction);
         }
 
-        // Search for an Enter PiP transition (along with a show wallpaper one)
-        TransitionInfo.Change enterPip = null;
-        TransitionInfo.Change wallpaper = null;
-        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-            final TransitionInfo.Change change = info.getChanges().get(i);
-            if (change.getTaskInfo() != null
-                    && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
-                    == WINDOWING_MODE_PINNED) {
-                enterPip = change;
-            } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
-                wallpaper = change;
-            }
-        }
-        if (enterPip == null) {
-            return false;
+        // Entering PIP.
+        if (isEnteringPip(info, mCurrentPipTaskToken)) {
+            return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
         }
 
-        if (mFinishCallback != null) {
-            mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
-            mFinishCallback = null;
-            throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+        // For transition that we don't animate, but contains the PIP leash, we need to update the
+        // PIP surface, otherwise it will be reset after the transition.
+        if (currentPipChange != null) {
+            updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
         }
-
-        // Show the wallpaper if there is a wallpaper change.
-        if (wallpaper != null) {
-            startTransaction.show(wallpaper.getLeash());
-            startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
-        }
-
-        mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
-        mFinishCallback = finishCallback;
-        return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
-                startTransaction, finishTransaction, enterPip.getStartRotation(),
-                enterPip.getEndRotation());
+        return false;
     }
 
     @Nullable
@@ -266,6 +235,7 @@
                     new Rect(mExitDestinationBounds));
         }
         mExitDestinationBounds.setEmpty();
+        mCurrentPipTaskToken = null;
     }
 
     @Override
@@ -298,7 +268,118 @@
         mFinishCallback = null;
     }
 
-    private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+    @Nullable
+    private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) {
+        if (mCurrentPipTaskToken == null) {
+            return null;
+        }
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (mCurrentPipTaskToken.equals(change.getContainer())) {
+                return change;
+            }
+        }
+        return null;
+    }
+
+    private void startExitAnimation(@NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull TransitionInfo.Change pipChange) {
+        TransitionInfo.Change displayRotationChange = null;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getMode() == TRANSIT_CHANGE
+                    && (change.getFlags() & FLAG_IS_DISPLAY) != 0
+                    && change.getStartRotation() != change.getEndRotation()) {
+                displayRotationChange = change;
+                break;
+            }
+        }
+
+        if (displayRotationChange != null) {
+            // Exiting PIP to fullscreen with orientation change.
+            startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
+                    finishCallback, displayRotationChange, pipChange);
+            return;
+        }
+
+        // When there is no rotation, we can simply expand the PIP window.
+        mFinishCallback = (wct, wctCB) -> {
+            mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+            finishCallback.onTransitionFinished(wct, wctCB);
+        };
+
+        // Set the initial frame as scaling the end to the start.
+        final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
+        final Point offset = pipChange.getEndRelOffset();
+        destinationBounds.offset(-offset.x, -offset.y);
+        startTransaction.setWindowCrop(pipChange.getLeash(), destinationBounds);
+        mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(),
+                destinationBounds, mPipBoundsState.getBounds());
+        startTransaction.apply();
+        startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds);
+    }
+
+    private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull TransitionInfo.Change displayRotationChange,
+            @NonNull TransitionInfo.Change pipChange) {
+        final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
+                displayRotationChange.getEndRotation());
+
+        // Counter-rotate all "going-away" things since they are still in the old orientation.
+        final CounterRotatorHelper rotator = new CounterRotatorHelper();
+        rotator.handleClosingChanges(info, startTransaction, displayRotationChange);
+
+        mFinishCallback = (wct, wctCB) -> {
+            mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+            finishCallback.onTransitionFinished(wct, wctCB);
+        };
+
+        // Get the start bounds in new orientation.
+        final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
+        rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
+        final Rect endBounds = new Rect(pipChange.getEndAbsBounds());
+        final Point offset = pipChange.getEndRelOffset();
+        startBounds.offset(-offset.x, -offset.y);
+        endBounds.offset(-offset.x, -offset.y);
+
+        // Reverse the rotation direction for expansion.
+        final int pipRotateDelta = deltaRotation(rotateDelta, 0);
+
+        // Set the start frame.
+        final int degree, x, y;
+        if (pipRotateDelta == ROTATION_90) {
+            degree = 90;
+            x = startBounds.right;
+            y = startBounds.top;
+        } else {
+            degree = -90;
+            x = startBounds.left;
+            y = startBounds.bottom;
+        }
+        mSurfaceTransactionHelper.rotateAndScaleWithCrop(startTransaction, pipChange.getLeash(),
+                endBounds, startBounds, new Rect(), degree, x, y, true /* isExpanding */,
+                pipRotateDelta == ROTATION_270 /* clockwise */);
+        startTransaction.apply();
+        rotator.cleanUp(finishTransaction);
+
+        // Expand and rotate the pip window to fullscreen.
+        final PipAnimationController.PipTransitionAnimator animator =
+                mPipAnimationController.getAnimator(pipChange.getTaskInfo(), pipChange.getLeash(),
+                        startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP,
+                        0 /* startingAngle */, pipRotateDelta);
+        animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+                .setPipAnimationCallback(mPipAnimationCallback)
+                .setDuration(mEnterExitAnimationDuration)
+                .start();
+    }
+
+    private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
             final Rect destinationBounds) {
         PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
@@ -309,8 +390,87 @@
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
                 .start();
+    }
 
-        return true;
+    /** For {@link Transitions#TRANSIT_REMOVE_PIP}, we just immediately remove the PIP Task. */
+    private void removePipImmediately(@NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull TransitionInfo.Change pipChange) {
+        startTransaction.apply();
+        finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+                mPipBoundsState.getDisplayBounds());
+        mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+        finishCallback.onTransitionFinished(null, null);
+    }
+
+    /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
+    private static boolean isEnteringPip(@NonNull TransitionInfo info,
+            @Nullable WindowContainerToken currentPipTaskToken) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getTaskInfo() != null
+                    && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
+                    && !change.getContainer().equals(currentPipTaskToken)) {
+                // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
+                // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
+                if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) {
+                    return true;
+                }
+                // This can happen if the request to enter PIP happens when we are collecting for
+                // another transition, such as TRANSIT_CHANGE (display rotation).
+                if (info.getType() == TRANSIT_CHANGE) {
+                    return true;
+                }
+
+                // Please file a bug to handle the unexpected transition type.
+                throw new IllegalStateException("Entering PIP with unexpected transition type="
+                        + transitTypeToString(info.getType()));
+            }
+        }
+        return false;
+    }
+
+    private boolean startEnterAnimation(@NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        // Search for an Enter PiP transition (along with a show wallpaper one)
+        TransitionInfo.Change enterPip = null;
+        TransitionInfo.Change wallpaper = null;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getTaskInfo() != null
+                    && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+                enterPip = change;
+            } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+                wallpaper = change;
+            }
+        }
+        if (enterPip == null) {
+            return false;
+        }
+        // Keep track of the PIP task.
+        mCurrentPipTaskToken = enterPip.getContainer();
+
+        if (mFinishCallback != null) {
+            mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+            mFinishCallback = null;
+            throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+        }
+
+        // Show the wallpaper if there is a wallpaper change.
+        if (wallpaper != null) {
+            startTransaction.show(wallpaper.getLeash());
+            startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+        }
+
+        mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+        mFinishCallback = finishCallback;
+        return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+                startTransaction, finishTransaction, enterPip.getStartRotation(),
+                enterPip.getEndRotation());
     }
 
     private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
@@ -387,10 +547,11 @@
         return true;
     }
 
-    private boolean startExitToSplitAnimation(TransitionInfo info,
+    private void startExitToSplitAnimation(TransitionInfo info,
             SurfaceControl.Transaction startTransaction,
             SurfaceControl.Transaction finishTransaction,
-            Transitions.TransitionFinishCallback finishCallback) {
+            Transitions.TransitionFinishCallback finishCallback,
+            TransitionInfo.Change pipChange) {
         final int changeSize = info.getChanges().size();
         if (changeSize < 4) {
             throw new RuntimeException(
@@ -417,8 +578,41 @@
         }
         mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
         startTransaction.apply();
+
+        mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
         finishCallback.onTransitionFinished(null, null);
-        return true;
+    }
+
+    private void resetPrevPip(@NonNull TransitionInfo.Change prevPipChange,
+            @NonNull SurfaceControl.Transaction startTransaction) {
+        final SurfaceControl leash = prevPipChange.getLeash();
+        final Rect bounds = prevPipChange.getEndAbsBounds();
+        final Point offset = prevPipChange.getEndRelOffset();
+        bounds.offset(-offset.x, -offset.y);
+
+        startTransaction.setWindowCrop(leash, null);
+        startTransaction.setMatrix(leash, 1, 0, 0, 1);
+        startTransaction.setCornerRadius(leash, 0);
+        startTransaction.setPosition(leash, bounds.left, bounds.top);
+
+        mCurrentPipTaskToken = null;
+        mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
+    }
+
+    private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
+        // When the PIP window is visible and being a part of the transition, such as display
+        // rotation, we need to update its bounds and rounded corner.
+        final SurfaceControl leash = pipChange.getLeash();
+        final Rect destBounds = mPipBoundsState.getBounds();
+        final boolean isInPip = mPipTransitionState.isInPip();
+        mSurfaceTransactionHelper
+                .crop(startTransaction, leash, destBounds)
+                .round(startTransaction, leash, isInPip);
+        mSurfaceTransactionHelper
+                .crop(finishTransaction, leash, destBounds)
+                .round(finishTransaction, leash, isInPip);
     }
 
     private void finishResizeForMenu(Rect destinationBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index a0a76d8..9c23a32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -107,7 +107,10 @@
         PICTURE_IN_PICTURE_STASH_LEFT(710),
 
         @UiEvent(doc = "User stashed picture-in-picture to the right side")
-        PICTURE_IN_PICTURE_STASH_RIGHT(711);
+        PICTURE_IN_PICTURE_STASH_RIGHT(711),
+
+        @UiEvent(doc = "User taps on the settings button in PiP menu")
+        PICTURE_IN_PICTURE_SHOW_SETTINGS(933);
 
         private final int mId;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 101a55d..6ec8f5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -44,6 +44,7 @@
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipMediaController.ActionListener;
 import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.io.PrintWriter;
@@ -118,6 +119,7 @@
     private final ArrayList<Listener> mListeners = new ArrayList<>();
     private final SystemWindows mSystemWindows;
     private final Optional<SplitScreenController> mSplitScreenController;
+    private final PipUiEventLogger mPipUiEventLogger;
     private ParceledListSlice<RemoteAction> mAppActions;
     private ParceledListSlice<RemoteAction> mMediaActions;
     private SyncRtSurfaceTransactionApplier mApplier;
@@ -150,6 +152,7 @@
     public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
             PipMediaController mediaController, SystemWindows systemWindows,
             Optional<SplitScreenController> splitScreenOptional,
+            PipUiEventLogger pipUiEventLogger,
             ShellExecutor mainExecutor, Handler mainHandler) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
@@ -158,6 +161,7 @@
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
         mSplitScreenController = splitScreenOptional;
+        mPipUiEventLogger = pipUiEventLogger;
     }
 
     public boolean isMenuVisible() {
@@ -187,7 +191,7 @@
             detachPipMenuView();
         }
         mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
-                mSplitScreenController);
+                mSplitScreenController, mPipUiEventLogger);
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 92a3598..915c593 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -140,6 +140,7 @@
         });
 
         mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+        mMagnetizedPip.clearAllTargets();
         mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
         updateMagneticTargetSize();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index da4bbe8..225305b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -63,6 +63,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
@@ -104,8 +105,6 @@
     private static final float MENU_BACKGROUND_ALPHA = 0.3f;
     private static final float DISABLED_ACTION_ALPHA = 0.54f;
 
-    private static final boolean ENABLE_ENTER_SPLIT = true;
-
     private int mMenuState;
     private boolean mAllowMenuTimeout = true;
     private boolean mAllowTouches = true;
@@ -121,8 +120,9 @@
     private int mBetweenActionPaddingLand;
 
     private AnimatorSet mMenuContainerAnimator;
-    private PhonePipMenuController mController;
-    private Optional<SplitScreenController> mSplitScreenControllerOptional;
+    private final PhonePipMenuController mController;
+    private final Optional<SplitScreenController> mSplitScreenControllerOptional;
+    private final PipUiEventLogger mPipUiEventLogger;
 
     private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
             new ValueAnimator.AnimatorUpdateListener() {
@@ -152,13 +152,15 @@
 
     public PipMenuView(Context context, PhonePipMenuController controller,
             ShellExecutor mainExecutor, Handler mainHandler,
-            Optional<SplitScreenController> splitScreenController) {
+            Optional<SplitScreenController> splitScreenController,
+            PipUiEventLogger pipUiEventLogger) {
         super(context, null, 0);
         mContext = context;
         mController = controller;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
         mSplitScreenControllerOptional = splitScreenController;
+        mPipUiEventLogger = pipUiEventLogger;
 
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         inflate(context, R.layout.pip_menu, this);
@@ -277,6 +279,8 @@
             boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
         mAllowMenuTimeout = allowMenuTimeout;
         mDidLastShowMenuResize = resizeMenuOnShow;
+        final boolean enableEnterSplit =
+                mContext.getResources().getBoolean(R.bool.config_pipEnableEnterSplitButton);
         if (mMenuState != menuState) {
             // Disallow touches if the menu needs to resize while showing, and we are transitioning
             // to/from a full menu state.
@@ -297,7 +301,7 @@
                     mDismissButton.getAlpha(), 1f);
             ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
                     mEnterSplitButton.getAlpha(),
-                    ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
+                    enableEnterSplit && mFocusedTaskAllowSplitScreen ? 1f : 0f);
             if (menuState == MENU_STATE_FULL) {
                 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
                         enterSplitAnim);
@@ -539,6 +543,8 @@
         // handles the message
         hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* resize */,
                 ANIM_TYPE_HIDE);
+        mPipUiEventLogger.log(
+                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
     }
 
     private void dismissPip() {
@@ -547,6 +553,7 @@
             // any other dismissal that will update the touch state and fade out the PIP task
             // and the menu view at the same time.
             mController.onPipDismiss();
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
         }
     }
 
@@ -566,6 +573,7 @@
                     Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
             settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
             mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second));
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_SETTINGS);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 3ace5f4..350f285 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -149,7 +149,6 @@
 
         @Override
         public void onPipDismiss() {
-            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
             mTouchState.removeDoubleTapTimeoutCallback();
             mMotionHelper.dismissPip();
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
new file mode 100644
index 0000000..33f3bfb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.tv;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+
+/**
+ * Contains pip bounds calculations that are specific to TV.
+ */
+public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
+
+    private static final String TAG = TvPipBoundsAlgorithm.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    public TvPipBoundsAlgorithm(Context context,
+            @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+        super(context, pipBoundsState, pipSnapAlgorithm);
+    }
+
+    /**
+     * The normal bounds at a different position on the screen.
+     */
+    public Rect getTvNormalBounds(int gravity) {
+        Rect normalBounds = getNormalBounds();
+        Rect insetBounds = new Rect();
+        getInsetBounds(insetBounds);
+
+        if (mPipBoundsState.isImeShowing()) {
+            if (DEBUG) Log.d(TAG, "IME showing, height: " + mPipBoundsState.getImeHeight());
+            insetBounds.bottom -= mPipBoundsState.getImeHeight();
+        }
+
+        Rect result = new Rect();
+        Gravity.apply(gravity, normalBounds.width(), normalBounds.height(), insetBounds, result);
+
+        if (DEBUG) {
+            Log.d(TAG, "normalBounds: " + normalBounds.toShortString());
+            Log.d(TAG, "insetBounds: " + insetBounds.toShortString());
+            Log.d(TAG, "gravity: " + Gravity.toString(gravity));
+            Log.d(TAG, "resultBounds: " + result.toShortString());
+        }
+
+        return result;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index b165706..de53939 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -18,6 +18,10 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
+import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
+import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
+import static android.view.KeyEvent.KEYCODE_DPAD_UP;
 
 import android.annotation.IntDef;
 import android.app.ActivityManager;
@@ -33,6 +37,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.DisplayInfo;
+import android.view.Gravity;
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.WindowManagerShellWrapper;
@@ -42,7 +47,6 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -85,10 +89,12 @@
      */
     private static final int STATE_PIP_MENU = 2;
 
+    private static final int DEFAULT_GRAVITY = Gravity.BOTTOM | Gravity.RIGHT;
+
     private final Context mContext;
 
     private final PipBoundsState mPipBoundsState;
-    private final PipBoundsAlgorithm mPipBoundsAlgorithm;
+    private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
     private final PipTaskOrganizer mPipTaskOrganizer;
     private final PipMediaController mPipMediaController;
     private final TvPipNotificationController mPipNotificationController;
@@ -97,6 +103,7 @@
     private final TvPipImpl mImpl = new TvPipImpl();
 
     private @State int mState = STATE_NO_PIP;
+    private @Gravity.GravityFlags int mGravity = DEFAULT_GRAVITY;
     private int mPinnedTaskId = NONEXISTENT_TASK_ID;
 
     private int mResizeAnimationDuration;
@@ -104,7 +111,7 @@
     public static Pip create(
             Context context,
             PipBoundsState pipBoundsState,
-            PipBoundsAlgorithm pipBoundsAlgorithm,
+            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
             PipTransitionController pipTransitionController,
             TvPipMenuController tvPipMenuController,
@@ -116,7 +123,7 @@
         return new TvPipController(
                 context,
                 pipBoundsState,
-                pipBoundsAlgorithm,
+                tvPipBoundsAlgorithm,
                 pipTaskOrganizer,
                 pipTransitionController,
                 tvPipMenuController,
@@ -130,7 +137,7 @@
     private TvPipController(
             Context context,
             PipBoundsState pipBoundsState,
-            PipBoundsAlgorithm pipBoundsAlgorithm,
+            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
             PipTransitionController pipTransitionController,
             TvPipMenuController tvPipMenuController,
@@ -145,7 +152,7 @@
         mPipBoundsState = pipBoundsState;
         mPipBoundsState.setDisplayId(context.getDisplayId());
         mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
-        mPipBoundsAlgorithm = pipBoundsAlgorithm;
+        mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
 
         mPipMediaController = pipMediaController;
 
@@ -192,24 +199,19 @@
     public void showPictureInPictureMenu() {
         if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), state=" + stateToName(mState));
 
-        if (mState != STATE_PIP) {
+        if (mState == STATE_NO_PIP) {
             if (DEBUG) Log.d(TAG, "  > cannot open Menu from the current state.");
             return;
         }
 
         setState(STATE_PIP_MENU);
-        resizePinnedStack(STATE_PIP_MENU);
+        movePinnedStack();
     }
 
-    /**
-     * Moves Pip window to its "normal" position.
-     */
     @Override
-    public void movePipToNormalPosition() {
-        if (DEBUG) Log.d(TAG, "movePipToNormalPosition(), state=" + stateToName(mState));
-
+    public void closeMenu() {
+        if (DEBUG) Log.d(TAG, "closeMenu(), state before=" + stateToName(mState));
         setState(STATE_PIP);
-        resizePinnedStack(STATE_PIP);
     }
 
     /**
@@ -223,6 +225,69 @@
         onPipDisappeared();
     }
 
+    @Override
+    public void movePip(int keycode) {
+        if (updatePosition(keycode)) {
+            if (DEBUG) Log.d(TAG, "New gravity: " + Gravity.toString(mGravity));
+            mTvPipMenuController.updateMenu(mGravity);
+            movePinnedStack();
+        } else {
+            if (DEBUG) Log.d(TAG, "Position hasn't changed");
+        }
+    }
+
+    @Override
+    public int getPipGravity() {
+        return mGravity;
+    }
+
+    /**
+     * @return true if position changed
+     */
+    private boolean updatePosition(int keycode) {
+        if (DEBUG) Log.d(TAG, "updatePosition, keycode: " + keycode);
+
+        int updatedGravity;
+        switch (keycode) {
+            case KEYCODE_DPAD_UP:
+                updatedGravity = (mGravity & (~Gravity.BOTTOM)) | Gravity.TOP;
+                break;
+            case KEYCODE_DPAD_DOWN:
+                updatedGravity =  (mGravity & (~Gravity.TOP)) | Gravity.BOTTOM;
+                break;
+            case KEYCODE_DPAD_LEFT:
+                updatedGravity = (mGravity & (~Gravity.RIGHT)) | Gravity.LEFT;
+                break;
+            case KEYCODE_DPAD_RIGHT:
+                updatedGravity = (mGravity & (~Gravity.LEFT)) | Gravity.RIGHT;
+                break;
+            default:
+                updatedGravity = mGravity;
+        }
+
+        if (updatedGravity != mGravity) {
+            mGravity = updatedGravity;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Animate to the updated position of the PiP based on the state and position of the PiP.
+     */
+    private void movePinnedStack() {
+        if (mState == STATE_NO_PIP) {
+            return;
+        }
+
+        Rect bounds = mTvPipBoundsAlgorithm.getTvNormalBounds(mGravity);
+        if (DEBUG) Log.d(TAG, "movePinnedStack() - new pip bounds: " + bounds.toShortString());
+        mPipTaskOrganizer.scheduleAnimateResizePip(bounds,
+                mResizeAnimationDuration, rect -> {
+                    if (DEBUG) Log.d(TAG, "movePinnedStack() animation done");
+                });
+    }
+
     /**
      * Closes Pip window.
      */
@@ -234,41 +299,6 @@
         onPipDisappeared();
     }
 
-    /**
-     * Resizes the Pip task/window to the appropriate size for the given state.
-     * This is a legacy API. Now we expect that the state argument passed to it should always match
-     * the current state of the Controller. If it does not match an {@link IllegalArgumentException}
-     * will be thrown. However, if the passed state does match - we'll determine the right bounds
-     * to the state and will move Pip task/window there.
-     *
-     * @param state the to determine the Pip bounds. IMPORTANT: should always match the current
-     *              state of the Controller.
-     */
-    private void resizePinnedStack(@State int state) {
-        if (state != mState) {
-            throw new IllegalArgumentException("The passed state should match the current state!");
-        }
-        if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + stateToName(mState));
-
-        final Rect newBounds;
-        switch (mState) {
-            case STATE_PIP_MENU:
-            case STATE_PIP:
-                // Let PipBoundsAlgorithm figure out what the correct bounds are at the moment.
-                // Internally, it will get the "default" bounds from PipBoundsState and adjust them
-                // as needed to account for things like IME state (will query PipBoundsState for
-                // this information as well, so it's important to keep PipBoundsState up to date).
-                newBounds = mPipBoundsAlgorithm.getNormalBounds();
-                break;
-
-            case STATE_NO_PIP:
-            default:
-                return;
-        }
-
-        mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null);
-    }
-
     private void registerSessionListenerForCurrentUser() {
         mPipMediaController.registerSessionListenerForCurrentUser();
     }
@@ -298,6 +328,7 @@
 
         mPipNotificationController.dismiss();
         mTvPipMenuController.hideMenu();
+        mGravity = DEFAULT_GRAVITY;
         setState(STATE_NO_PIP);
         mPinnedTaskId = NONEXISTENT_TASK_ID;
     }
@@ -384,10 +415,9 @@
                         return;
                     }
                     mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
-                    // "Normal" Pip bounds may have changed, so if we are in the "normal" state,
-                    // let's update the bounds.
-                    if (mState == STATE_PIP) {
-                        resizePinnedStack(STATE_PIP);
+
+                    if (mState != STATE_NO_PIP) {
+                        movePinnedStack();
                     }
                 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java
new file mode 100644
index 0000000..927c1ec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.tv;
+
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * All interpolators needed for TV specific Pip animations
+ */
+public class TvPipInterpolators {
+
+    /**
+     * A standard ease-in-out curve reserved for moments of interaction (button and card states).
+     */
+    public static final Interpolator STANDARD = new PathInterpolator(0.2f, 0.1f, 0f, 1f);
+
+    /**
+     * A sharp ease-out-expo curve created for snappy but fluid browsing between cards and clusters.
+     */
+    public static final Interpolator BROWSE = new PathInterpolator(0.18f, 1f, 0.22f, 1f);
+
+    /**
+     * A smooth ease-out-expo curve created for incoming elements (forward, back, overlay).
+     */
+    public static final Interpolator ENTER = new PathInterpolator(0.12f, 1f, 0.4f, 1f);
+
+    /**
+     * A smooth ease-in-out-expo curve created for outgoing elements (forward, back, overlay).
+     */
+    public static final Interpolator EXIT = new PathInterpolator(0.4f, 1f, 0.12f, 1f);
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 77bfa07..72ead00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -24,13 +24,18 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.util.Log;
 import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
 
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
@@ -53,11 +58,33 @@
 
     private Delegate mDelegate;
     private SurfaceControl mLeash;
-    private TvPipMenuView mMenuView;
+    private TvPipMenuView mPipMenuView;
+
+    // User can actively move the PiP via the DPAD.
+    private boolean mInMoveMode;
 
     private final List<RemoteAction> mMediaActions = new ArrayList<>();
     private final List<RemoteAction> mAppActions = new ArrayList<>();
 
+    private SyncRtSurfaceTransactionApplier mApplier;
+    RectF mTmpSourceRectF = new RectF();
+    RectF mTmpDestinationRectF = new RectF();
+    Matrix mMoveTransform = new Matrix();
+
+    private final float[] mTmpValues = new float[9];
+    private final Runnable mUpdateEmbeddedMatrix = () -> {
+        if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+            return;
+        }
+        mMoveTransform.getValues(mTmpValues);
+        try {
+            mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection()
+                    .setScreenMatrix(mTmpValues);
+        } catch (RemoteException e) {
+            if (DEBUG) e.printStackTrace();
+        }
+    };
+
     public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
             SystemWindows systemWindows, PipMediaController pipMediaController,
             Handler mainHandler) {
@@ -107,13 +134,13 @@
     private void attachPipMenuView() {
         if (DEBUG) Log.d(TAG, "attachPipMenuView()");
 
-        if (mMenuView != null) {
+        if (mPipMenuView != null) {
             detachPipMenuView();
         }
 
-        mMenuView = new TvPipMenuView(mContext);
-        mMenuView.setListener(this);
-        mSystemWindows.addView(mMenuView,
+        mPipMenuView = new TvPipMenuView(mContext);
+        mPipMenuView.setListener(this);
+        mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
     }
@@ -122,38 +149,76 @@
     public void showMenu() {
         if (DEBUG) Log.d(TAG, "showMenu()");
 
-        if (mMenuView != null) {
-            Rect pipBounds = mPipBoundsState.getBounds();
-            mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(
-                    MENU_WINDOW_TITLE, pipBounds.width(), pipBounds.height()));
+        if (mPipMenuView != null) {
+            Rect menuBounds = getMenuBounds(mPipBoundsState.getBounds());
+            mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(
+                    MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height()));
             maybeUpdateMenuViewActions();
 
-            SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mMenuView);
+            SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mPipMenuView);
             if (menuSurfaceControl != null) {
                 SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-                t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, 1);
-                t.setPosition(menuSurfaceControl, pipBounds.left, pipBounds.top);
+                t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1);
+                t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
                 t.apply();
             }
-            mMenuView.show();
+            mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity());
         }
     }
 
+    void updateMenu(int gravity) {
+        mPipMenuView.showMovementHints(gravity);
+    }
+
+    private Rect getMenuBounds(Rect pipBounds) {
+        int extraSpaceInPx = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.pip_menu_outer_space);
+        Rect menuBounds = new Rect(pipBounds);
+        menuBounds.inset(-extraSpaceInPx, -extraSpaceInPx);
+        return menuBounds;
+    }
+
     void hideMenu() {
-        hideMenu(true);
+        if (!isMenuVisible()) {
+            if (DEBUG) Log.d(TAG, "hideMenu() - Menu isn't visible, so don't hide");
+            return;
+        } else {
+            if (DEBUG) Log.d(TAG, "hideMenu()");
+        }
+
+        mPipMenuView.hide(mInMoveMode);
+        if (!mInMoveMode) {
+            mDelegate.closeMenu();
+        }
     }
 
-    void hideMenu(boolean movePipWindow) {
-        if (DEBUG) Log.d(TAG, "hideMenu(), movePipWindow=" + movePipWindow);
+    @Override
+    public void onEnterMoveMode() {
+        if (DEBUG) Log.d(TAG, "onEnterMoveMode - " + mInMoveMode);
+        mInMoveMode = true;
+        mPipMenuView.showMenuButtons(false);
+        mPipMenuView.showMovementHints(mDelegate.getPipGravity());
+    }
 
-        if (!isMenuVisible()) {
-            return;
+    @Override
+    public boolean onExitMoveMode() {
+        if (DEBUG) Log.d(TAG, "onExitMoveMode - " + mInMoveMode);
+        if (mInMoveMode) {
+            mInMoveMode = false;
+            mPipMenuView.showMenuButtons(true);
+            mPipMenuView.hideMovementHints();
+            return true;
         }
+        return false;
+    }
 
-        mMenuView.hide();
-        if (movePipWindow) {
-            mDelegate.movePipToNormalPosition();
+    @Override
+    public boolean onPipMovement(int keycode) {
+        if (DEBUG) Log.d(TAG, "onPipMovement - " + mInMoveMode);
+        if (mInMoveMode) {
+            mDelegate.movePip(keycode);
         }
+        return mInMoveMode;
     }
 
     @Override
@@ -163,17 +228,6 @@
         mLeash = null;
     }
 
-    private void detachPipMenuView() {
-        if (DEBUG) Log.d(TAG, "detachPipMenuView()");
-
-        if (mMenuView == null) {
-            return;
-        }
-
-        mSystemWindows.removeView(mMenuView);
-        mMenuView = null;
-    }
-
     @Override
     public void setAppActions(ParceledListSlice<RemoteAction> actions) {
         if (DEBUG) Log.d(TAG, "setAppActions()");
@@ -209,24 +263,146 @@
     }
 
     private void maybeUpdateMenuViewActions() {
-        if (mMenuView == null) {
+        if (mPipMenuView == null) {
             return;
         }
         if (!mAppActions.isEmpty()) {
-            mMenuView.setAdditionalActions(mAppActions, mMainHandler);
+            mPipMenuView.setAdditionalActions(mAppActions, mMainHandler);
         } else {
-            mMenuView.setAdditionalActions(mMediaActions, mMainHandler);
+            mPipMenuView.setAdditionalActions(mMediaActions, mMainHandler);
         }
     }
 
     @Override
     public boolean isMenuVisible() {
-        return mMenuView != null && mMenuView.isVisible();
+        boolean isVisible = mPipMenuView != null && mPipMenuView.isVisible();
+        if (DEBUG) Log.d(TAG, "isMenuVisible: " + isVisible);
+        return isVisible;
+    }
+
+    /**
+     * Does an immediate window crop of the PiP menu.
+     */
+    @Override
+    public void resizePipMenu(@android.annotation.Nullable SurfaceControl pipLeash,
+            @android.annotation.Nullable SurfaceControl.Transaction t,
+            Rect destinationBounds) {
+        if (DEBUG) Log.d(TAG, "resizePipMenu: " + destinationBounds.toShortString());
+        if (destinationBounds.isEmpty()) {
+            return;
+        }
+
+        if (!maybeCreateSyncApplier()) {
+            return;
+        }
+
+        SurfaceControl surfaceControl = getSurfaceControl();
+        SyncRtSurfaceTransactionApplier.SurfaceParams
+                params = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(surfaceControl)
+                .withWindowCrop(getMenuBounds(destinationBounds))
+                .build();
+        if (pipLeash != null && t != null) {
+            SyncRtSurfaceTransactionApplier.SurfaceParams
+                    pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
+                    .withMergeTransaction(t)
+                    .build();
+            mApplier.scheduleApply(params, pipParams);
+        } else {
+            mApplier.scheduleApply(params);
+        }
+    }
+
+    private SurfaceControl getSurfaceControl() {
+        return mSystemWindows.getViewSurface(mPipMenuView);
+    }
+
+    @Override
+    public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
+            Rect pipDestBounds) {
+        if (DEBUG) Log.d(TAG, "movePipMenu: " + pipDestBounds.toShortString());
+
+        if (pipDestBounds.isEmpty()) {
+            if (transaction == null && DEBUG) Log.d(TAG, "no transaction given");
+            return;
+        }
+        if (!maybeCreateSyncApplier()) {
+            return;
+        }
+
+        Rect menuDestBounds = getMenuBounds(pipDestBounds);
+        Rect mTmpSourceBounds = new Rect();
+        // If there is no pip leash supplied, that means the PiP leash is already finalized
+        // resizing and the PiP menu is also resized. We then want to do a scale from the current
+        // new menu bounds.
+        if (pipLeash != null && transaction != null) {
+            if (DEBUG) Log.d(TAG, "mTmpSourceBounds based on mPipMenuView.getBoundsOnScreen()");
+            mPipMenuView.getBoundsOnScreen(mTmpSourceBounds);
+        } else {
+            if (DEBUG) Log.d(TAG, "mTmpSourceBounds based on menu width and height");
+            mTmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height());
+        }
+
+        mTmpSourceRectF.set(mTmpSourceBounds);
+        mTmpDestinationRectF.set(menuDestBounds);
+        mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
+
+        SurfaceControl surfaceControl = getSurfaceControl();
+        SyncRtSurfaceTransactionApplier.SurfaceParams params =
+                new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+                        surfaceControl).withMatrix(mMoveTransform).build();
+
+        if (pipLeash != null && transaction != null) {
+            SyncRtSurfaceTransactionApplier.SurfaceParams
+                    pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
+                    .withMergeTransaction(transaction)
+                    .build();
+            mApplier.scheduleApply(params, pipParams);
+        } else {
+            mApplier.scheduleApply(params);
+        }
+
+        if (mPipMenuView.getViewRootImpl() != null) {
+            mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
+            mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
+        }
+    }
+
+    private boolean maybeCreateSyncApplier() {
+        if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+            Log.v(TAG, "Not going to move PiP, either menu or its parent is not created.");
+            return false;
+        }
+
+        if (mApplier == null) {
+            mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView);
+        }
+        return true;
+    }
+
+    private void detachPipMenuView() {
+        if (mPipMenuView == null) {
+            return;
+        }
+
+        mApplier = null;
+        mSystemWindows.removeView(mPipMenuView);
+        mPipMenuView = null;
+    }
+
+    @Override
+    public void updateMenuBounds(Rect destinationBounds) {
+        Rect menuBounds = getMenuBounds(destinationBounds);
+        if (DEBUG) Log.d(TAG, "updateMenuBounds: " + menuBounds.toShortString());
+        mSystemWindows.updateViewLayout(mPipMenuView,
+                getPipMenuLayoutParams(MENU_WINDOW_TITLE, menuBounds.width(),
+                        menuBounds.height()));
     }
 
     @Override
     public void onBackPress() {
-        hideMenu();
+        if (!onExitMoveMode()) {
+            hideMenu();
+        }
     }
 
     @Override
@@ -240,8 +416,14 @@
     }
 
     interface Delegate {
-        void movePipToNormalPosition();
         void movePipToFullscreen();
+
+        void movePip(int keycode);
+
+        int getPipGravity();
+
+        void closeMenu();
+
         void closePip();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 4327f15..0141b6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -16,17 +16,21 @@
 
 package com.android.wm.shell.pip.tv;
 
-import static android.animation.AnimatorInflater.loadAnimator;
 import static android.view.KeyEvent.ACTION_UP;
 import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
+import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
+import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
+import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
+import static android.view.KeyEvent.KEYCODE_DPAD_UP;
 
-import android.animation.Animator;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
@@ -34,6 +38,7 @@
 import android.view.ViewRootImpl;
 import android.view.WindowManagerGlobal;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
@@ -45,16 +50,14 @@
 import java.util.List;
 
 /**
- * A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu
- * actions: Fullscreen and Close, but could also display "additional" actions, that may be set via
- * a {@link #setAdditionalActions(List, Handler)} call.
+ * A View that represents Pip Menu on TV. It's responsible for displaying 3 ever-present Pip Menu
+ * actions: Fullscreen, Move and Close, but could also display "additional" actions, that may be set
+ * via a {@link #setAdditionalActions(List, Handler)} call.
  */
 public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
     private static final String TAG = "TvPipMenuView";
     private static final boolean DEBUG = TvPipController.DEBUG;
 
-    private final Animator mFadeInAnimation;
-    private final Animator mFadeOutAnimation;
     @Nullable
     private Listener mListener;
 
@@ -62,6 +65,11 @@
     private final View mMenuFrameView;
     private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
 
+    private final ImageView mArrowUp;
+    private final ImageView mArrowRight;
+    private final ImageView mArrowDown;
+    private final ImageView mArrowLeft;
+
     public TvPipMenuView(@NonNull Context context) {
         this(context, null);
     }
@@ -85,35 +93,68 @@
                 .setOnClickListener(this);
         mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button)
                 .setOnClickListener(this);
+        mActionButtonsContainer.findViewById(R.id.tv_pip_menu_move_button)
+                .setOnClickListener(this);
 
         mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
-        mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation);
-        mFadeInAnimation.setTarget(mMenuFrameView);
 
-        mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation);
-        mFadeOutAnimation.setTarget(mMenuFrameView);
+        mArrowUp = findViewById(R.id.tv_pip_menu_arrow_up);
+        mArrowRight = findViewById(R.id.tv_pip_menu_arrow_right);
+        mArrowDown = findViewById(R.id.tv_pip_menu_arrow_down);
+        mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left);
     }
 
     void setListener(@Nullable Listener listener) {
         mListener = listener;
     }
 
-    void show() {
-        if (DEBUG) Log.d(TAG, "show()");
-
-        mFadeInAnimation.start();
+    void show(boolean inMoveMode, int gravity) {
+        if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
         grantWindowFocus(true);
+
+        if (inMoveMode) {
+            showMovementHints(gravity);
+        } else {
+            animateAlphaTo(1, mActionButtonsContainer);
+        }
+        animateAlphaTo(1, mMenuFrameView);
     }
 
-    void hide() {
+    void hide(boolean isInMoveMode) {
         if (DEBUG) Log.d(TAG, "hide()");
+        animateAlphaTo(0, mActionButtonsContainer);
+        animateAlphaTo(0, mMenuFrameView);
+        hideMovementHints();
 
-        mFadeOutAnimation.start();
-        grantWindowFocus(false);
+        if (!isInMoveMode) {
+            grantWindowFocus(false);
+        }
+    }
+
+    private void animateAlphaTo(float alpha, View view) {
+        view.animate()
+                .alpha(alpha)
+                .setInterpolator(alpha == 0f ? TvPipInterpolators.EXIT : TvPipInterpolators.ENTER)
+                .setDuration(500)
+                .withStartAction(() -> {
+                    if (alpha != 0) {
+                        view.setVisibility(VISIBLE);
+                    }
+                })
+                .withEndAction(() -> {
+                    if (alpha == 0) {
+                        view.setVisibility(GONE);
+                    }
+                });
     }
 
     boolean isVisible() {
-        return mMenuFrameView != null && mMenuFrameView.getAlpha() != 0.0f;
+        return mMenuFrameView.getAlpha() != 0f
+                || mActionButtonsContainer.getAlpha() != 0f
+                || mArrowUp.getAlpha() != 0f
+                || mArrowRight.getAlpha() != 0f
+                || mArrowDown.getAlpha() != 0f
+                || mArrowLeft.getAlpha() != 0f;
     }
 
     private void grantWindowFocus(boolean grantFocus) {
@@ -188,6 +229,8 @@
         final int id = v.getId();
         if (id == R.id.tv_pip_menu_fullscreen_button) {
             mListener.onFullscreenButtonClick();
+        } else if (id == R.id.tv_pip_menu_move_button) {
+            mListener.onEnterMoveMode();
         } else if (id == R.id.tv_pip_menu_close_button) {
             mListener.onCloseButtonClick();
         } else {
@@ -207,17 +250,79 @@
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK
-                && mListener != null) {
-            mListener.onBackPress();
-            return true;
+        if (DEBUG) {
+            Log.d(TAG, "dispatchKeyEvent, action: " + event.getAction()
+                    + ", keycode: " + event.getKeyCode());
+        }
+        if (mListener != null && event.getAction() == ACTION_UP) {
+            switch (event.getKeyCode()) {
+                case KEYCODE_BACK:
+                    mListener.onBackPress();
+                    return true;
+                case KEYCODE_DPAD_UP:
+                case KEYCODE_DPAD_DOWN:
+                case KEYCODE_DPAD_LEFT:
+                case KEYCODE_DPAD_RIGHT:
+                    return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
+                            event);
+                case KEYCODE_DPAD_CENTER:
+                    return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
+                default:
+                    break;
+            }
         }
         return super.dispatchKeyEvent(event);
     }
 
+    /**
+     * Shows user hints for moving the PiP, e.g. arrows.
+     */
+    public void showMovementHints(int gravity) {
+        if (DEBUG) Log.d(TAG, "showMovementHints(), position: " + Gravity.toString(gravity));
+
+        animateAlphaTo((gravity & Gravity.BOTTOM) == Gravity.BOTTOM ? 1f : 0f, mArrowUp);
+        animateAlphaTo((gravity & Gravity.TOP) == Gravity.TOP ? 1f : 0f, mArrowDown);
+        animateAlphaTo((gravity & Gravity.RIGHT) == Gravity.RIGHT ? 1f : 0f, mArrowLeft);
+        animateAlphaTo((gravity & Gravity.LEFT) == Gravity.LEFT ? 1f : 0f, mArrowRight);
+    }
+
+    /**
+     * Hides user hints for moving the PiP, e.g. arrows.
+     */
+    public void hideMovementHints() {
+        if (DEBUG) Log.d(TAG, "hideMovementHints()");
+        animateAlphaTo(0, mArrowUp);
+        animateAlphaTo(0, mArrowRight);
+        animateAlphaTo(0, mArrowDown);
+        animateAlphaTo(0, mArrowLeft);
+    }
+
+    /**
+     * Show or hide the pip user actions.
+     */
+    public void showMenuButtons(boolean show) {
+        if (DEBUG) Log.d(TAG, "showMenuButtons: " + show);
+        animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
+    }
+
     interface Listener {
+
         void onBackPress();
+
+        void onEnterMoveMode();
+
+        /**
+         * @return whether move mode was exited
+         */
+        boolean onExitMoveMode();
+
+        /**
+         * @return whether pip movement was handled.
+         */
+        boolean onPipMovement(int keycode);
+
         void onCloseButtonClick();
+
         void onFullscreenButtonClick();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 551476d..5062cc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -29,7 +29,6 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMenuController;
 import com.android.wm.shell.pip.PipTransitionController;
@@ -42,11 +41,11 @@
 public class TvPipTransition extends PipTransitionController {
     public TvPipTransition(PipBoundsState pipBoundsState,
             PipMenuController pipMenuController,
-            PipBoundsAlgorithm pipBoundsAlgorithm,
+            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
             Transitions transitions,
             @NonNull ShellTaskOrganizer shellTaskOrganizer) {
-        super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController,
+        super(pipBoundsState, pipMenuController, tvPipBoundsAlgorithm, pipAnimationController,
                 transitions, shellTaskOrganizer);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 79c1df2..20c4e21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -34,6 +34,8 @@
             Consts.TAG_WM_SHELL),
     WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM_STARTING_WINDOW),
+    WM_SHELL_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            "ShellBackPreview"),
     TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
 
     private final boolean mEnabled;
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 7decb54..338c944 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
@@ -292,7 +292,6 @@
          * Invalidates this instance, preventing future calls from updating the controller.
          */
         void invalidate() {
-            Slog.d("b/206648922", "invalidating controller: " + mController);
             mController = null;
         }
 
@@ -313,13 +312,16 @@
         @Override
         public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
                 throws RemoteException {
+            if (mController == null) {
+                // The controller is already invalidated -- just return an empty task list for now
+                return new GroupedRecentTaskInfo[0];
+            }
+
             final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null};
             executeRemoteCallWithTaskPermission(mController, "getRecentTasks",
                     (controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
                             .toArray(new GroupedRecentTaskInfo[0]),
                     true /* blocking */);
-            Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]
-                    + " mController=" + mController);
             return out[0];
         }
     }
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 0aa8d7e..d30d0cc 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
@@ -44,6 +44,7 @@
 import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.TransactionPool;
@@ -66,28 +67,27 @@
 
     DismissTransition mPendingDismiss = null;
     IBinder mPendingEnter = null;
+    IBinder mPendingRecent = null;
 
     private IBinder mAnimatingTransition = null;
-    private OneShotRemoteHandler mRemoteHandler = null;
+    private OneShotRemoteHandler mPendingRemoteHandler = null;
+    private OneShotRemoteHandler mActiveRemoteHandler = null;
 
-    private Transitions.TransitionFinishCallback mRemoteFinishCB = (wct, wctCB) -> {
-        if (wct != null || wctCB != null) {
-            throw new UnsupportedOperationException("finish transactions not supported yet.");
-        }
-        onFinish();
-    };
+    private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
 
     /** Keeps track of currently running animations */
     private final ArrayList<Animator> mAnimations = new ArrayList<>();
+    private final StageCoordinator mStageCoordinator;
 
     private Transitions.TransitionFinishCallback mFinishCallback = null;
     private SurfaceControl.Transaction mFinishTransaction;
 
     SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
-            @NonNull Runnable onFinishCallback) {
+            @NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
         mTransactionPool = pool;
         mTransitions = transitions;
         mOnFinish = onFinishCallback;
+        mStageCoordinator = stageCoordinator;
     }
 
     void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@@ -97,10 +97,11 @@
             @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
         mFinishCallback = finishCallback;
         mAnimatingTransition = transition;
-        if (mRemoteHandler != null) {
-            mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
-                    mRemoteFinishCB);
-            mRemoteHandler = null;
+        if (mPendingRemoteHandler != null) {
+            mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
+                    finishTransaction, mRemoteFinishCB);
+            mActiveRemoteHandler = mPendingRemoteHandler;
+            mPendingRemoteHandler = null;
             return;
         }
         playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
@@ -166,22 +167,21 @@
             }
         }
         t.apply();
-        onFinish();
+        onFinish(null /* wct */, null /* wctCB */);
     }
 
     /** Starts a transition to enter split with a remote transition animator. */
     IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
             @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
             @NonNull Transitions.TransitionHandler handler) {
-        if (remoteTransition != null) {
-            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
-            mRemoteHandler = new OneShotRemoteHandler(
-                    mTransitions.getMainExecutor(), remoteTransition);
-        }
         final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
         mPendingEnter = transition;
-        if (mRemoteHandler != null) {
-            mRemoteHandler.setTransition(transition);
+
+        if (remoteTransition != null) {
+            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
+            mPendingRemoteHandler = new OneShotRemoteHandler(
+                    mTransitions.getMainExecutor(), remoteTransition);
+            mPendingRemoteHandler.setTransition(transition);
         }
         return transition;
     }
@@ -203,7 +203,33 @@
         return transition;
     }
 
-    void onFinish() {
+    IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+            Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) {
+        if (transition == null) {
+            transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler);
+        }
+        mPendingRecent = transition;
+
+        if (remoteTransition != null) {
+            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
+            mPendingRemoteHandler = new OneShotRemoteHandler(
+                    mTransitions.getMainExecutor(), remoteTransition);
+            mPendingRemoteHandler.setTransition(transition);
+        }
+
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+                        + " deduced Enter recent panel");
+        return transition;
+    }
+
+    void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
+            IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
+        if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
+            mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+        }
+    }
+
+    void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
         if (!mAnimations.isEmpty()) return;
         mOnFinish.run();
         if (mFinishTransaction != null) {
@@ -211,14 +237,25 @@
             mTransactionPool.release(mFinishTransaction);
             mFinishTransaction = null;
         }
-        mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
-        mFinishCallback = null;
+        if (mFinishCallback != null) {
+            mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */);
+            mFinishCallback = null;
+        }
         if (mAnimatingTransition == mPendingEnter) {
             mPendingEnter = null;
         }
         if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
             mPendingDismiss = null;
         }
+        if (mAnimatingTransition == mPendingRecent) {
+            // If the wct is not null while finishing recent transition, it indicates it's not
+            // dismissing split and thus need to reorder split task so they can be on top again.
+            final boolean dismissSplit = wct == null;
+            mStageCoordinator.finishRecentAnimation(dismissSplit);
+            mPendingRecent = null;
+        }
+        mPendingRemoteHandler = null;
+        mActiveRemoteHandler = null;
         mAnimatingTransition = null;
     }
 
@@ -240,7 +277,7 @@
             mTransactionPool.release(transaction);
             mTransitions.getMainExecutor().execute(() -> {
                 mAnimations.remove(va);
-                onFinish();
+                onFinish(null /* wct */, null /* wctCB */);
             });
         };
         va.addListener(new Animator.AnimatorListener() {
@@ -288,7 +325,7 @@
             mTransactionPool.release(transaction);
             mTransitions.getMainExecutor().execute(() -> {
                 mAnimations.remove(va);
-                onFinish();
+                onFinish(null /* wct */, null /* wctCB */);
             });
         };
         va.addListener(new AnimatorListenerAdapter() {
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 83830ec..e592101 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
@@ -20,6 +20,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -165,17 +166,6 @@
     @StageType
     private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
 
-    private final Runnable mOnTransitionAnimationComplete = () -> {
-        // If still playing, let it finish.
-        if (!isSplitScreenVisible()) {
-            // Update divider state after animation so that it is still around and positioned
-            // properly for the animation itself.
-            mSplitLayout.release();
-            mSplitLayout.resetDividerPosition();
-            mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
-        }
-    };
-
     private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
             new SplitWindowManager.ParentContainerCallbacks() {
                 @Override
@@ -236,7 +226,7 @@
         deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
                 new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
         mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
-                mOnTransitionAnimationComplete);
+                this::onTransitionAnimationComplete, this);
         mDisplayController.addDisplayWindowListener(this);
         mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
         transitions.addHandler(this);
@@ -266,7 +256,7 @@
         mRootTDAOrganizer.registerListener(displayId, this);
         mSplitLayout = splitLayout;
         mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
-                mOnTransitionAnimationComplete);
+                this::onTransitionAnimationComplete, this);
         mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
         mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
         mLogger = logger;
@@ -1233,7 +1223,7 @@
         final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
         if (triggerTask == null) {
             // Still want to monitor everything while in split-screen, so return non-null.
-            return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
+            return mMainStage.isActive() ? new WindowContainerTransaction() : null;
         } else if (triggerTask.displayId != mDisplayId) {
             // Skip handling task on the other display.
             return null;
@@ -1249,7 +1239,7 @@
             mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
         }
 
-        if (isSplitScreenVisible()) {
+        if (mMainStage.isActive()) {
             // Try to handle everything while in split-screen, so return a WCT even if it's empty.
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  split is active so using split"
                             + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -1271,13 +1261,16 @@
                 final int activityType = triggerTask.getActivityType();
                 if (activityType == ACTIVITY_TYPE_ASSISTANT) {
                     // We don't want assistant panel to dismiss split screen, so do nothing.
+                } else if (activityType == ACTIVITY_TYPE_HOME
+                        || activityType == ACTIVITY_TYPE_RECENTS) {
+                    // Enter overview panel, so start recent transition.
+                    mSplitTransitions.startRecentTransition(transition, out, this,
+                            request.getRemoteTransition());
                 } else {
-                    // Going home or occluded by the other fullscreen task, so dismiss both.
+                    // Occluded by the other fullscreen task, so dismiss both.
                     prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
-                    final int exitReason = activityType == ACTIVITY_TYPE_HOME
-                            ? EXIT_REASON_RETURN_HOME : EXIT_REASON_UNKNOWN;
                     mSplitTransitions.startDismissTransition(transition, out, this,
-                            STAGE_TYPE_UNDEFINED, exitReason);
+                            STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
                 }
             }
         } else {
@@ -1292,6 +1285,13 @@
     }
 
     @Override
+    public void mergeAnimation(IBinder transition, TransitionInfo info,
+            SurfaceControl.Transaction t, IBinder mergeTarget,
+            Transitions.TransitionFinishCallback finishCallback) {
+        mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+    }
+
+    @Override
     public void onTransitionMerged(@NonNull IBinder transition) {
         // Once the pending enter transition got merged, make sure to bring divider bar visible and
         // clear the pending transition from cache to prevent mess-up the following state.
@@ -1307,13 +1307,14 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        if (transition != mSplitTransitions.mPendingEnter && (
-                mSplitTransitions.mPendingDismiss == null
+        if (transition != mSplitTransitions.mPendingEnter
+                && transition != mSplitTransitions.mPendingRecent
+                && (mSplitTransitions.mPendingDismiss == null
                         || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
             // Not entering or exiting, so just do some house-keeping and validation.
 
             // If we're not in split-mode, just abort so something else can handle it.
-            if (!isSplitScreenVisible()) return false;
+            if (!mMainStage.isActive()) return false;
 
             for (int iC = 0; iC < info.getChanges().size(); ++iC) {
                 final TransitionInfo.Change change = info.getChanges().get(iC);
@@ -1356,6 +1357,8 @@
         boolean shouldAnimate = true;
         if (mSplitTransitions.mPendingEnter == transition) {
             shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
+        } else if (mSplitTransitions.mPendingRecent == transition) {
+            shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
         } else if (mSplitTransitions.mPendingDismiss != null
                 && mSplitTransitions.mPendingDismiss.mTransition == transition) {
             shouldAnimate = startPendingDismissAnimation(
@@ -1368,6 +1371,17 @@
         return true;
     }
 
+    void onTransitionAnimationComplete() {
+        // If still playing, let it finish.
+        if (!mMainStage.isActive()) {
+            // Update divider state after animation so that it is still around and positioned
+            // properly for the animation itself.
+            mSplitLayout.release();
+            mSplitLayout.resetDividerPosition();
+            mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+        }
+    }
+
     private boolean startPendingEnterAnimation(@NonNull IBinder transition,
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
         // First, verify that we actually have opened apps in both splits.
@@ -1500,6 +1514,32 @@
         return true;
     }
 
+    private boolean startPendingRecentAnimation(@NonNull IBinder transition,
+            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+        setDividerVisibility(false, t);
+        return true;
+    }
+
+    void finishRecentAnimation(boolean dismissSplit) {
+        // Exclude the case that the split screen has been dismissed already.
+        if (!mMainStage.isActive()) {
+            // The latest split dismissing transition might be a no-op transition and thus won't
+            // callback startAnimation, update split visibility here to cover this kind of no-op
+            // transition case.
+            setSplitsVisible(false);
+            return;
+        }
+
+        if (dismissSplit) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+            mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
+                    STAGE_TYPE_UNDEFINED, EXIT_REASON_RETURN_HOME);
+        } else {
+            setDividerVisibility(true, null /* t */);
+        }
+    }
+
     private void addDividerBarToTransition(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, boolean show) {
         final SurfaceControl leash = mSplitLayout.getDividerLeash();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 73f65b0..f8902c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -269,6 +269,8 @@
         // touchable or focusable by the user.  We also add in the ALT_FOCUSABLE_IM
         // flag because we do know that the next window will take input
         // focus, so we want to get the IME window up on top of us right away.
+        // Touches will only pass through to the host activity window and will be blocked from
+        // passing to any other windows.
         windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -276,9 +278,6 @@
         params.token = appToken;
         params.packageName = activityInfo.packageName;
         params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        // Setting as trusted overlay to let touches pass through. This is safe because this
-        // window is controlled by the system.
-        params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
         if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
             params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
new file mode 100644
index 0000000..19133e2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.RotationUtils;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.util.CounterRotator;
+
+import java.util.List;
+
+/**
+ * The helper class that performs counter-rotate for all "going-away" window containers if they are
+ * still in the old rotation in a transition.
+ */
+public class CounterRotatorHelper {
+    private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>();
+    private final Rect mLastDisplayBounds = new Rect();
+    private int mLastRotationDelta;
+
+    /** Puts the surface controls of closing changes to counter-rotated surfaces. */
+    public void handleClosingChanges(@NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull TransitionInfo.Change displayRotationChange) {
+        final int rotationDelta = RotationUtils.deltaRotation(
+                displayRotationChange.getStartRotation(), displayRotationChange.getEndRotation());
+        final Rect displayBounds = displayRotationChange.getEndAbsBounds();
+        final int displayW = displayBounds.width();
+        final int displayH = displayBounds.height();
+        mLastRotationDelta = rotationDelta;
+        mLastDisplayBounds.set(displayBounds);
+
+        final List<TransitionInfo.Change> changes = info.getChanges();
+        final int numChanges = changes.size();
+        for (int i = numChanges - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = changes.get(i);
+            final WindowContainerToken parent = change.getParent();
+            if (!Transitions.isClosingType(change.getMode())
+                    || !TransitionInfo.isIndependent(change, info) || parent == null) {
+                continue;
+            }
+
+            CounterRotator crot = mRotatorMap.get(parent);
+            if (crot == null) {
+                crot = new CounterRotator();
+                crot.setup(startTransaction, info.getChange(parent).getLeash(), rotationDelta,
+                        displayW, displayH);
+                final SurfaceControl rotatorSc = crot.getSurface();
+                if (rotatorSc != null) {
+                    // Wallpaper should be placed at the bottom.
+                    final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
+                            ? numChanges - i
+                            : -1;
+                    startTransaction.setLayer(rotatorSc, layer);
+                }
+                mRotatorMap.put(parent, crot);
+            }
+            crot.addChild(startTransaction, change.getLeash());
+        }
+    }
+
+    /**
+     * Returns the rotated end bounds if the change is put in previous rotation. Otherwise the
+     * original end bounds are returned.
+     */
+    @NonNull
+    public Rect getEndBoundsInStartRotation(@NonNull TransitionInfo.Change change) {
+        if (mLastRotationDelta == 0) return change.getEndAbsBounds();
+        final Rect rotatedBounds = new Rect(change.getEndAbsBounds());
+        RotationUtils.rotateBounds(rotatedBounds, mLastDisplayBounds, mLastRotationDelta);
+        return rotatedBounds;
+    }
+
+    /**
+     * Removes the counter rotation surface in the finish transaction. No need to reparent the
+     * children as the finish transaction should have already taken care of that.
+     *
+     * This can only be called after startTransaction for {@link #handleClosingChanges} is applied.
+     */
+    public void cleanUp(@NonNull SurfaceControl.Transaction finishTransaction) {
+        for (int i = mRotatorMap.size() - 1; i >= 0; --i) {
+            mRotatorMap.valueAt(i).cleanUp(finishTransaction);
+        }
+        mRotatorMap.clear();
+        mLastRotationDelta = 0;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 072b925..13e81bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -43,7 +43,6 @@
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
-import static android.window.TransitionInfo.isIndependent;
 
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -78,7 +77,6 @@
 import android.window.TransitionInfo;
 import android.window.TransitionMetrics;
 import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.R;
@@ -92,7 +90,6 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.util.CounterRotator;
 
 import java.util.ArrayList;
 
@@ -127,6 +124,7 @@
     /** Keeps track of the currently-running animations associated with each transition. */
     private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
 
+    private final CounterRotatorHelper mRotator = new CounterRotatorHelper();
     private final Rect mInsets = new Rect(0, 0, 0, 0);
     private float mTransitionAnimationScaleSetting = 1.0f;
 
@@ -280,16 +278,9 @@
         final ArrayList<Animator> animations = new ArrayList<>();
         mAnimations.put(transition, animations);
 
-        final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
-
         final Runnable onAnimFinish = () -> {
             if (!animations.isEmpty()) return;
 
-            for (int i = 0; i < counterRotators.size(); ++i) {
-                counterRotators.valueAt(i).cleanUp(info.getRootLeash());
-            }
-            counterRotators.clear();
-
             if (mRotationAnimation != null) {
                 mRotationAnimation.kill();
                 mRotationAnimation = null;
@@ -299,17 +290,13 @@
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
         };
 
-        boolean requireBackgroundForTransition = false;
-
+        @ColorInt int backgroundColorForTransition = 0;
         final int wallpaperTransit = getWallpaperTransitType(info);
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
             final boolean isTask = change.getTaskInfo() != null;
 
             if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
-                int rotateDelta = change.getEndRotation() - change.getStartRotation();
-                int displayW = change.getEndAbsBounds().width();
-                int displayH = change.getEndAbsBounds().height();
                 if (info.getType() == TRANSIT_CHANGE) {
                     boolean isSeamless = isRotationSeamless(info, mDisplayController);
                     final int anim = getRotationAnimation(info);
@@ -322,29 +309,8 @@
                         continue;
                     }
                 } else {
-                    // opening/closing an app into a new orientation. Counter-rotate all
-                    // "going-away" things since they are still in the old orientation.
-                    for (int j = info.getChanges().size() - 1; j >= 0; --j) {
-                        final TransitionInfo.Change innerChange = info.getChanges().get(j);
-                        if (!Transitions.isClosingType(innerChange.getMode())
-                                || !isIndependent(innerChange, info)
-                                || innerChange.getParent() == null) {
-                            continue;
-                        }
-                        CounterRotator crot = counterRotators.get(innerChange.getParent());
-                        if (crot == null) {
-                            crot = new CounterRotator();
-                            crot.setup(startTransaction,
-                                    info.getChange(innerChange.getParent()).getLeash(),
-                                    rotateDelta, displayW, displayH);
-                            if (crot.getSurface() != null) {
-                                int layer = info.getChanges().size() - j;
-                                startTransaction.setLayer(crot.getSurface(), layer);
-                            }
-                            counterRotators.put(innerChange.getParent(), crot);
-                        }
-                        crot.addChild(startTransaction, innerChange.getLeash());
-                    }
+                    // Opening/closing an app into a new orientation.
+                    mRotator.handleClosingChanges(info, startTransaction, change);
                 }
             }
 
@@ -380,8 +346,19 @@
 
             Animation a = loadAnimation(info, change, wallpaperTransit);
             if (a != null) {
-                if (changeRequiresBackground(info, change)) {
-                    requireBackgroundForTransition = true;
+                if (isTask) {
+                    final @TransitionType int type = info.getType();
+                    final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN
+                            || type == TRANSIT_CLOSE
+                            || type == TRANSIT_TO_FRONT
+                            || type == TRANSIT_TO_BACK;
+                    if (isOpenOrCloseTransition) {
+                        // Use the overview background as the background for the animation
+                        final Context uiContext = ActivityThread.currentActivityThread()
+                                .getSystemUiContext();
+                        backgroundColorForTransition =
+                                uiContext.getColor(R.color.overview_background);
+                    }
                 }
 
                 float cornerRadius = 0;
@@ -393,9 +370,21 @@
                             ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
                 }
 
+                if (a.getShowBackground()) {
+                    // use the window's background color if provided as the background color for the
+                    // animation - the top most window with a valid background color and
+                    // showBackground set takes precedence.
+                    if (change.getBackgroundColor() != 0) {
+                        backgroundColorForTransition = change.getBackgroundColor();
+                    }
+                }
+
+                final Rect clipRect = Transitions.isClosingType(change.getMode())
+                        ? mRotator.getEndBoundsInStartRotation(change)
+                        : change.getEndAbsBounds();
                 startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
                         mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
-                        cornerRadius, change.getEndAbsBounds());
+                        cornerRadius, clipRect);
 
                 if (info.getAnimationOptions() != null) {
                     attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -404,35 +393,26 @@
             }
         }
 
-        if (requireBackgroundForTransition) {
-            addBackgroundToTransition(info.getRootLeash(), startTransaction, finishTransaction);
+        if (backgroundColorForTransition != 0) {
+            addBackgroundToTransition(info.getRootLeash(), backgroundColorForTransition,
+                    startTransaction, finishTransaction);
         }
 
         startTransaction.apply();
+        mRotator.cleanUp(finishTransaction);
         TransitionMetrics.getInstance().reportAnimationStart(transition);
         // run finish now in-case there are no animations
         onAnimFinish.run();
         return true;
     }
 
-    private boolean changeRequiresBackground(TransitionInfo info,
-            TransitionInfo.Change change) {
-        final boolean isTask = change.getTaskInfo() != null;
-        final @TransitionType int type = info.getType();
-        final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN || type == TRANSIT_CLOSE
-                || type == TRANSIT_TO_FRONT || type == TRANSIT_TO_BACK;
-        return isTask && isOpenOrCloseTransition;
-    }
-
     private void addBackgroundToTransition(
             @NonNull SurfaceControl rootLeash,
+            @ColorInt int color,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction
     ) {
-        final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
-        final @ColorInt int overviewBackgroundColor =
-                uiContext.getColor(R.color.overview_background);
-        final Color bgColor = Color.valueOf(overviewBackgroundColor);
+        final Color bgColor = Color.valueOf(color);
         final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
 
         final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
@@ -476,6 +456,9 @@
         final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
         final int overrideType = options != null ? options.getType() : ANIM_NONE;
         final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
+        final Rect endBounds = Transitions.isClosingType(changeMode)
+                ? mRotator.getEndBoundsInStartRotation(change)
+                : change.getEndAbsBounds();
 
         if (info.isKeyguardGoingAway()) {
             a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
@@ -493,8 +476,7 @@
             a = new AlphaAnimation(1.f, 1.f);
             a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
         } else if (type == TRANSIT_RELAUNCH) {
-            a = mTransitionAnimation.createRelaunchAnimation(
-                    change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
+            a = mTransitionAnimation.createRelaunchAnimation(endBounds, mInsets, endBounds);
         } else if (overrideType == ANIM_CUSTOM
                 && (canCustomContainer || options.getOverrideTaskTransition())) {
             a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
@@ -503,16 +485,15 @@
             a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
         } else if (overrideType == ANIM_CLIP_REVEAL) {
             a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
-                    change.getEndAbsBounds(), change.getEndAbsBounds(),
-                    options.getTransitionBounds());
+                    endBounds, endBounds, options.getTransitionBounds());
         } else if (overrideType == ANIM_SCALE_UP) {
             a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
-                    change.getEndAbsBounds(), options.getTransitionBounds());
+                    endBounds, options.getTransitionBounds());
         } else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
                 || overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
             final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
             a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
-                    change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
+                    endBounds, type, wallpaperTransit, options.getThumbnail(),
                     options.getTransitionBounds());
         } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
             // This received a transferred starting window, so don't animate
@@ -585,8 +566,9 @@
 
         if (a != null) {
             if (!a.isInitialized()) {
-                Rect end = change.getEndAbsBounds();
-                a.initialize(end.width(), end.height(), end.width(), end.height());
+                final int width = endBounds.width();
+                final int height = endBounds.height();
+                a.initialize(width, height, width, height);
             }
             a.restrictDuration(MAX_ANIMATION_DURATION);
             a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
index b9b6716..7f8eaf1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
@@ -18,14 +18,11 @@
 
 import android.view.SurfaceControl;
 
-import java.util.ArrayList;
-
 /**
  * Utility class that takes care of counter-rotating surfaces during a transition animation.
  */
 public class CounterRotator {
-    SurfaceControl mSurface = null;
-    ArrayList<SurfaceControl> mRotateChildren = null;
+    private SurfaceControl mSurface = null;
 
     /** Gets the surface with the counter-rotation. */
     public SurfaceControl getSurface() {
@@ -41,7 +38,6 @@
     public void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
             float displayW, float displayH) {
         if (rotateDelta == 0) return;
-        mRotateChildren = new ArrayList<>();
         // We want to counter-rotate, so subtract from 4
         rotateDelta = 4 - (rotateDelta + 4) % 4;
         mSurface = new SurfaceControl.Builder()
@@ -64,24 +60,19 @@
     }
 
     /**
-     * Add a surface that needs to be counter-rotate.
+     * Adds a surface that needs to be counter-rotate.
      */
     public void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
         if (mSurface == null) return;
         t.reparent(child, mSurface);
-        mRotateChildren.add(child);
     }
 
     /**
-     * Clean-up. This undoes any reparenting and effectively stops the counter-rotation.
+     * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove the
+     * counter rotation surface.
      */
-    public void cleanUp(SurfaceControl rootLeash) {
+    public void cleanUp(SurfaceControl.Transaction finishTransaction) {
         if (mSurface == null) return;
-        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
-            t.reparent(mRotateChildren.get(i), rootLeash);
-        }
-        t.remove(mSurface);
-        t.apply();
+        finishTransaction.remove(mSurface);
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
new file mode 100644
index 0000000..8446b37
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index af629cc..f8d14c6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -24,6 +24,9 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import org.junit.Assume
+import org.junit.Before
 import org.junit.runner.RunWith
 import org.junit.Test
 import org.junit.runners.Parameterized
@@ -59,6 +62,12 @@
             }
         }
 
+    @Before
+    fun setup() {
+        // This test doesn't work in shell transitions because of b/205288792
+        Assume.assumeFalse(BaseAppHelper.isShellTransitionsEnabled())
+    }
+
     @Presubmit
     @Test
     fun testAppIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index add11c1..c93c5ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -25,6 +25,9 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import org.junit.Assume
+import org.junit.Before
 import org.junit.runner.RunWith
 import org.junit.Test
 import org.junit.runners.Parameterized
@@ -67,6 +70,12 @@
             }
         }
 
+    @Before
+    fun setup() {
+        // This test doesn't work in shell transitions because of b/205288792
+        Assume.assumeFalse(BaseAppHelper.isShellTransitionsEnabled())
+    }
+
     @Presubmit
     @Test
     fun testAppIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
new file mode 100644
index 0000000..566acc8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Bubbles
+# Bug component: 555586
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 8e6fa5f..7e232ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -66,7 +66,7 @@
         stringExtras: Map<String, String>
     ) {
         super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
-        wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
+        wmHelper.waitPipShown()
     }
 
     private fun focusOnObject(selector: BySelector): Boolean {
@@ -88,7 +88,7 @@
         clickObject(ENTER_PIP_BUTTON_ID)
 
         // Wait on WMHelper or simply wait for 3 seconds
-        wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+        wmHelper?.waitPipShown() ?: SystemClock.sleep(3_000)
         // when entering pip, the dismiss button is visible at the start. to ensure the pip
         // animation is complete, wait until the pip dismiss button is no longer visible. 
         // b/176822698: dismiss-only state will be removed in the future
@@ -148,7 +148,7 @@
         }
 
         // Wait for animation to complete.
-        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+        wmHelper.waitPipGone()
         wmHelper.waitForHomeActivityVisible()
     }
 
@@ -165,7 +165,7 @@
                 ?: error("PIP window expand button not found")
         val expandButtonBounds = expandPipObject.visibleBounds
         uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
-        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+        wmHelper.waitPipGone()
         wmHelper.waitForAppTransitionIdle()
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 2c02d2c..fb1004b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -27,7 +27,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -41,7 +40,6 @@
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -112,11 +110,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
new file mode 100644
index 0000000..8446b37
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 7d7add4..264d482 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -29,7 +29,6 @@
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.resizeSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
@@ -45,7 +44,6 @@
 import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
 import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -137,11 +135,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Test
     fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 5678899..d703ea0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -35,7 +34,6 @@
 import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -79,11 +77,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index c2edf9d..6b18839 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -35,7 +34,6 @@
 import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -78,11 +76,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 777998c..acd658b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -37,7 +36,6 @@
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -88,11 +86,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index 914b11d..b40be8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -37,7 +36,6 @@
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -93,11 +91,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @FlakyTest
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index d3bb008..db94de2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -26,9 +27,7 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -93,11 +92,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     /**
      * Checks [pipApp] window remains visible throughout the animation
@@ -187,13 +182,14 @@
     }
 
     /**
-     * Checks the focus doesn't change during the animation
+     * Checks that the focus changes between the [pipApp] window and the launcher when
+     * closing the pip window
      */
-    @FlakyTest
+    @Postsubmit
     @Test
-    fun focusDoesNotChange() {
+    fun focusChanges() {
         testSpec.assertEventLog {
-            this.focusDoesNotChange()
+            this.focusChanges(pipApp.`package`, "NexusLauncherActivity")
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index fa9fbcd..dee13c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -27,7 +27,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -36,7 +35,6 @@
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -100,7 +98,7 @@
                 // Enter PiP, and assert that the PiP is within bounds now that the device is back
                 // in portrait
                 broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
-                wmHelper.waitFor { it.wmState.hasPipWindow() }
+                wmHelper.waitPipShown()
                 wmHelper.waitForAppTransitionIdle()
                 // during rotation the status bar becomes invisible and reappears at the end
                 wmHelper.waitForNavBarStatusBarVisible()
@@ -121,11 +119,7 @@
      */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     /**
      * Checks that all parts of the screen are covered at the start and end of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index f8a3aff..990872f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,9 +18,7 @@
 
 import android.platform.test.annotations.Presubmit
 import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import org.junit.Assume.assumeFalse
 import org.junit.Test
 
 /**
@@ -29,15 +27,6 @@
 abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     protected val testApp = FixedAppHelper(instrumentation)
 
-    /** {@inheritDoc}  */
-    @Presubmit
-    @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
-
     /**
      * Checks that the pip app window remains inside the display bounds throughout the whole
      * animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 6c9fed9..173140d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -16,12 +16,13 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.Test
 
@@ -51,11 +52,28 @@
     @Presubmit
     @Test
     open fun pipWindowBecomesInvisible() {
-        testSpec.assertWm {
-            this.invoke("hasPipWindow") {
-                it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
-            }.then().invoke("!hasPipWindow") {
-                it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
+        if (isShellTransitionsEnabled) {
+            // When Shell transition is enabled, we change the windowing mode at start, but
+            // update the visibility after the transition is finished, so we can't check isNotPinned
+            // and isAppWindowInvisible in the same assertion block.
+            testSpec.assertWm {
+                this.invoke("hasPipWindow") {
+                    it.isPinned(pipApp.component)
+                            .isAppWindowVisible(pipApp.component)
+                            .isAppWindowOnTop(pipApp.component)
+                }.then().invoke("!hasPipWindow") {
+                    it.isNotPinned(pipApp.component)
+                            .isAppWindowNotOnTop(pipApp.component)
+                }
+            }
+            testSpec.assertWmEnd { isAppWindowInvisible(pipApp.component) }
+        } else {
+            testSpec.assertWm {
+                this.invoke("hasPipWindow") {
+                    it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
+                }.then().invoke("!hasPipWindow") {
+                    it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
+                }
             }
         }
     }
@@ -78,14 +96,13 @@
     }
 
     /**
-     * Checks that the focus changes between the [pipApp] window and the launcher when
-     * closing the pip window
+     * Checks that the focus doesn't change between windows during the transition
      */
-    @FlakyTest(bugId = 151179149)
+    @Postsubmit
     @Test
-    open fun focusChanges() {
+    open fun focusDoesNotChange() {
         testSpec.assertEventLog {
-            this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+            this.focusDoesNotChange()
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 2231d88..3a9a070 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -24,8 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -82,11 +80,12 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+    /** {@inheritDoc}  */
+    @FlakyTest(bugId = 197726610)
+    @Test
+    override fun pipLayerExpands() = super.pipLayerExpands()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index fcac2c7..03c8929f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -24,9 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -101,11 +99,12 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+    /** {@inheritDoc}  */
+    @FlakyTest(bugId = 197726610)
+    @Test
+    override fun pipLayerExpands() = super.pipLayerExpands()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index c75076d..976b7c6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -24,10 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
-import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -88,20 +85,15 @@
         flickerRule.checkFlakyAssertions()
     }
 
-    @Before
-    fun onBefore() {
-        // This CUJ don't work in shell transitions because of b/204570898 b/204562589
-        assumeFalse(isShellTransitionsEnabled)
-    }
-
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+    /** {@inheritDoc}  */
+    @FlakyTest(bugId = 215869110)
+    @Test
+    override fun focusDoesNotChange() = super.focusDoesNotChange()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 8e6603b..6524182 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -24,9 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -65,7 +63,7 @@
                 val pipCenterY = pipRegion.centerY()
                 val displayCenterX = device.displayWidth / 2
                 device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10)
-                wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+                wmHelper.waitPipGone()
                 wmHelper.waitForWindowSurfaceDisappeared(pipApp.component)
                 wmHelper.waitForAppTransitionIdle()
             }
@@ -81,11 +79,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 52177c2..8d14f70 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -26,9 +27,6 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
-import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -65,12 +63,6 @@
             }
         }
 
-    @Before
-    fun onBefore() {
-        // This CUJ don't work in shell transitions because of b/204570898 b/204562589
-        assumeFalse(isShellTransitionsEnabled)
-    }
-
     /**
      * Checks that the pip app window remains inside the display bounds throughout the whole
      * animation
@@ -157,7 +149,7 @@
     /**
      * Checks that the focus doesn't change between windows during the transition
      */
-    @FlakyTest
+    @Postsubmit
     @Test
     fun focusDoesNotChange() {
         testSpec.assertEventLog {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index f9e180e..e415024 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -24,8 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
 import com.android.server.wm.flicker.traces.region.RegionSubject
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -85,11 +83,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index b7bfa1b..4a4c46c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -25,8 +25,6 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -85,11 +83,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
new file mode 100644
index 0000000..172e24bf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Picture-In-Picture
+# Bug component: 316251
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 93a4e1b..bb66f7b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -125,13 +125,13 @@
                     removeAllTasksButHome()
                     if (!eachRun) {
                         pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
-                        wmHelper.waitFor { it.wmState.hasPipWindow() }
+                        wmHelper.waitPipShown()
                     }
                 }
                 eachRun {
                     if (eachRun) {
                         pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
-                        wmHelper.waitFor { it.wmState.hasPipWindow() }
+                        wmHelper.waitPipShown()
                     }
                 }
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index dbd3d8c..8dd9104 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -26,11 +26,9 @@
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -39,7 +37,7 @@
 
 /**
  * Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
@@ -84,8 +82,6 @@
     @Presubmit
     @Test
     fun displayEndsAt90Degrees() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWmEnd {
             hasRotation(Surface.ROTATION_90)
         }
@@ -93,41 +89,23 @@
 
     @Presubmit
     @Test
-    override fun navBarLayerIsVisible() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
-        super.navBarLayerIsVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarLayerIsVisible() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerIsVisible()
-    }
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @FlakyTest
     @Test
-    override fun navBarLayerRotatesAndScales() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
-        super.navBarLayerRotatesAndScales()
-    }
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
     fun pipWindowInsideDisplay() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWmStart {
             frameRegion(pipApp.component).coversAtMost(startingBounds)
         }
@@ -136,8 +114,6 @@
     @Presubmit
     @Test
     fun pipAppShowsOnTop() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWmEnd {
             isAppWindowOnTop(pipApp.component)
         }
@@ -146,8 +122,6 @@
     @Presubmit
     @Test
     fun pipLayerInsideDisplay() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayersStart {
             visibleRegion(pipApp.component).coversAtMost(startingBounds)
         }
@@ -156,8 +130,6 @@
     @Presubmit
     @Test
     fun pipAlwaysVisible() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.isAppWindowVisible(pipApp.component)
         }
@@ -166,8 +138,6 @@
     @Presubmit
     @Test
     fun pipAppLayerCoversFullScreen() {
-        // This test doesn't work in shell transitions because of b/208576418
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayersEnd {
             visibleRegion(pipApp.component).coversExactly(endingBounds)
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 2cdbffa..f40aa66 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -25,6 +25,7 @@
                   android:resizeableActivity="true"
                   android:supportsPictureInPicture="true"
                   android:launchMode="singleTop"
+                  android:theme="@style/CutoutShortEdges"
                   android:label="FixedApp"
                   android:exported="true">
             <intent-filter>
@@ -37,6 +38,7 @@
                  android:supportsPictureInPicture="true"
                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
                  android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity"
+                 android:theme="@style/CutoutShortEdges"
                  android:launchMode="singleTop"
                  android:label="PipApp"
                  android:exported="true">
@@ -52,6 +54,7 @@
 
         <activity android:name=".ImeActivity"
                  android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
+                 android:theme="@style/CutoutShortEdges"
                  android:label="ImeApp"
                  android:launchMode="singleTop"
                  android:exported="true">
@@ -68,6 +71,7 @@
         <activity android:name=".SplitScreenActivity"
                   android:resizeableActivity="true"
                   android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
+                  android:theme="@style/CutoutShortEdges"
                   android:label="SplitScreenPrimaryApp"
                   android:exported="true">
             <intent-filter>
@@ -79,6 +83,7 @@
         <activity android:name=".SplitScreenSecondaryActivity"
                   android:resizeableActivity="true"
                   android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
+                  android:theme="@style/CutoutShortEdges"
                   android:label="SplitScreenSecondaryApp"
                   android:exported="true">
             <intent-filter>
@@ -90,6 +95,7 @@
         <activity android:name=".NonResizeableActivity"
                   android:resizeableActivity="false"
                   android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity"
+                  android:theme="@style/CutoutShortEdges"
                   android:label="NonResizeableApp"
                   android:exported="true">
             <intent-filter>
@@ -100,6 +106,7 @@
 
         <activity android:name=".SimpleActivity"
                   android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity"
+                  android:theme="@style/CutoutShortEdges"
                   android:label="SimpleApp"
                   android:exported="true">
             <intent-filter>
@@ -111,6 +118,7 @@
             android:name=".LaunchBubbleActivity"
             android:label="LaunchBubbleApp"
             android:exported="true"
+            android:theme="@style/CutoutShortEdges"
             android:launchMode="singleTop">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -121,6 +129,7 @@
             android:name=".BubbleActivity"
             android:label="BubbleApp"
             android:exported="false"
+            android:theme="@style/CutoutShortEdges"
             android:resizeableActivity="true" />
     </application>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 0000000..87a61a8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <style name="CutoutDefault">
+        <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+    </style>
+
+    <style name="CutoutShortEdges">
+        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+    </style>
+
+    <style name="CutoutNever">
+        <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
new file mode 100644
index 0000000..960c7ac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+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 android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest WMShellUnitTests:BackAnimationControllerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BackAnimationControllerTest {
+
+    private final ShellExecutor mShellExecutor = new TestShellExecutor();
+
+    @Mock
+    private SurfaceControl.Transaction mTransaction;
+
+    @Mock
+    private IActivityTaskManager mActivityTaskManager;
+
+    private BackAnimationController mController;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mController = new BackAnimationController(
+                mShellExecutor, mTransaction, mActivityTaskManager);
+    }
+
+    private void createNavigationInfo(SurfaceControl topWindowLeash,
+            SurfaceControl screenshotSurface,
+            HardwareBuffer hardwareBuffer) {
+        BackNavigationInfo navigationInfo = new BackNavigationInfo(
+                BackNavigationInfo.TYPE_RETURN_TO_HOME,
+                topWindowLeash,
+                screenshotSurface,
+                hardwareBuffer,
+                new WindowConfiguration(),
+                new RemoteCallback((bundle) -> {}));
+        try {
+            doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation();
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    @Test
+    public void screenshotAttachedAndVisible() {
+        SurfaceControl topWindowLeash = new SurfaceControl();
+        SurfaceControl screenshotSurface = new SurfaceControl();
+        HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+        createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+        mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+        verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer);
+        verify(mTransaction).setVisibility(screenshotSurface, true);
+        verify(mTransaction).apply();
+    }
+
+    @Test
+    public void surfaceMovesWithGesture() {
+        SurfaceControl topWindowLeash = new SurfaceControl();
+        SurfaceControl screenshotSurface = new SurfaceControl();
+        HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+        createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+        mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+        mController.onMotionEvent(MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0));
+        verify(mTransaction).setPosition(topWindowLeash, 100, 100);
+        verify(mTransaction, atLeastOnce()).apply();
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index fe66e22..35e4982 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -111,7 +110,6 @@
     private ActivityManager.RunningTaskInfo mHomeTask;
     private ActivityManager.RunningTaskInfo mFullscreenAppTask;
     private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
-    private ActivityManager.RunningTaskInfo mSplitPrimaryAppTask;
 
     @Before
     public void setUp() throws RemoteException {
@@ -144,8 +142,6 @@
         mNonResizeableFullscreenAppTask =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         mNonResizeableFullscreenAppTask.isResizeable = false;
-        mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                ACTIVITY_TYPE_STANDARD);
 
         setRunningTask(mFullscreenAppTask);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java
new file mode 100644
index 0000000..f3f7067
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.TestableLooper;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link BackgroundWindowManager} */
+@SmallTest
[email protected](setAsMainLooper = true)
+@RunWith(AndroidJUnit4.class)
+public class BackgroundWindowManagerTest extends ShellTestCase {
+    private BackgroundWindowManager mBackgroundWindowManager;
+    @Mock
+    private DisplayLayout  mMockDisplayLayout;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mBackgroundWindowManager = new BackgroundWindowManager(mContext);
+        mBackgroundWindowManager.onDisplayChanged(mMockDisplayLayout);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testInitRelease() {
+        mBackgroundWindowManager.initView();
+        assertThat(mBackgroundWindowManager.getSurfaceControl()).isNotNull();
+
+        mBackgroundWindowManager.removeBackgroundLayer();
+        assertThat(mBackgroundWindowManager.getSurfaceControl()).isNull();
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
deleted file mode 100644
index 7b9553c..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.onehanded;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
-
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.SurfaceControl;
-import android.window.DisplayAreaInfo;
-import android.window.IWindowContainerToken;
-import android.window.WindowContainerToken;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
[email protected]
-public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
-    private DisplayAreaInfo mDisplayAreaInfo;
-    private Display mDisplay;
-    private DisplayLayout mDisplayLayout;
-    private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
-    private WindowContainerToken mToken;
-    private SurfaceControl mLeash;
-
-    @Mock
-    IWindowContainerToken mMockRealToken;
-    @Mock
-    DisplayController mMockDisplayController;
-    @Mock
-    OneHandedSettingsUtil mMockSettingsUtil;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mToken = new WindowContainerToken(mMockRealToken);
-        mLeash = new SurfaceControl();
-        mDisplay = mContext.getDisplay();
-        mDisplayLayout = new DisplayLayout(mContext, mDisplay);
-        when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
-        mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
-                FEATURE_ONE_HANDED_BACKGROUND_PANEL);
-
-        mSpiedBackgroundPanelOrganizer = spy(
-                new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil,
-                        Runnable::run));
-        mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout);
-    }
-
-    @Test
-    public void testOnDisplayAreaAppeared() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-
-        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue();
-        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
-    }
-
-    @Test
-    public void testShowBackgroundLayer() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null);
-        mSpiedBackgroundPanelOrganizer.onStart();
-
-        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
-    }
-
-    @Test
-    public void testRemoveBackgroundLayer() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-
-        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull();
-
-        reset(mSpiedBackgroundPanelOrganizer);
-        mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
-
-        assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull();
-    }
-
-    @Test
-    public void testStateNone_onConfigurationChanged() {
-        mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE);
-        mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
-
-        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
-    }
-
-    @Test
-    public void testStateActivate_onConfigurationChanged() {
-        mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE);
-        mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
-
-        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 16bc5075..2886b97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -46,6 +46,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -72,8 +73,6 @@
     @Mock
     DisplayController mMockDisplayController;
     @Mock
-    OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-    @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
     @Mock
     OneHandedEventCallback mMockEventCallback;
@@ -86,6 +85,8 @@
     @Mock
     OneHandedUiEventLogger mMockUiEventLogger;
     @Mock
+    InteractionJankMonitor mMockJankMonitor;
+    @Mock
     IOverlayManager mMockOverlayManager;
     @Mock
     TaskStackListenerImpl mMockTaskStackListener;
@@ -112,7 +113,6 @@
         when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
         when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
-        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -131,7 +131,6 @@
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
                 mMockDisplayController,
-                mMockBackgroundOrganizer,
                 mMockDisplayAreaOrganizer,
                 mMockTouchHandler,
                 mMockTutorialHandler,
@@ -139,6 +138,7 @@
                 mOneHandedAccessibilityUtil,
                 mSpiedTimeoutHandler,
                 mSpiedTransitionState,
+                mMockJankMonitor,
                 mMockUiEventLogger,
                 mMockOverlayManager,
                 mMockTaskStackListener,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 1d92a48..9c7f723 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -50,6 +50,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -94,11 +95,11 @@
     @Mock
     WindowContainerTransaction mMockWindowContainerTransaction;
     @Mock
-    OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-    @Mock
     ShellExecutor mMockShellMainExecutor;
     @Mock
     OneHandedSettingsUtil mMockSettingsUitl;
+    @Mock
+    InteractionJankMonitor mJankMonitor;
 
     List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
 
@@ -140,7 +141,7 @@
                 mMockSettingsUitl,
                 mMockAnimationController,
                 mTutorialHandler,
-                mMockBackgroundOrganizer,
+                mJankMonitor,
                 mMockShellMainExecutor));
 
         for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) {
@@ -427,7 +428,7 @@
                         mMockSettingsUitl,
                         mMockAnimationController,
                         mTutorialHandler,
-                        mMockBackgroundOrganizer,
+                        mJankMonitor,
                         mMockShellMainExecutor));
 
         assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index bea69c5..dba1b8b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -40,6 +40,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -66,8 +67,6 @@
     @Mock
     DisplayController mMockDisplayController;
     @Mock
-    OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-    @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
     @Mock
     OneHandedTouchHandler mMockTouchHandler;
@@ -78,6 +77,8 @@
     @Mock
     OneHandedUiEventLogger mMockUiEventLogger;
     @Mock
+    InteractionJankMonitor mMockJankMonitor;
+    @Mock
     IOverlayManager mMockOverlayManager;
     @Mock
     TaskStackListenerImpl mMockTaskStackListener;
@@ -102,7 +103,6 @@
 
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
-        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -120,7 +120,6 @@
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
                 mMockDisplayController,
-                mMockBackgroundOrganizer,
                 mMockDisplayAreaOrganizer,
                 mMockTouchHandler,
                 mMockTutorialHandler,
@@ -128,6 +127,7 @@
                 mOneHandedAccessibilityUtil,
                 mSpiedTimeoutHandler,
                 mSpiedState,
+                mMockJankMonitor,
                 mMockUiEventLogger,
                 mMockOverlayManager,
                 mMockTaskStackListener,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index b1434ca..63d8bfd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -56,6 +56,8 @@
     OneHandedSettingsUtil mMockSettingsUtil;
     @Mock
     WindowManager mMockWindowManager;
+    @Mock
+    BackgroundWindowManager mMockBackgroundWindowManager;
 
     @Before
     public void setUp() {
@@ -63,10 +65,11 @@
         when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0);
 
         mDisplay = mContext.getDisplay();
-        mDisplayLayout = new DisplayLayout(mContext, mDisplay);
+        mDisplayLayout = new DisplayLayout(getTestContext().getApplicationContext(), mDisplay);
         mSpiedTransitionState = spy(new OneHandedState());
         mSpiedTutorialHandler = spy(
-                new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager));
+                new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager,
+                        mMockBackgroundWindowManager));
         mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index ea94cf0..59c377a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -251,7 +251,7 @@
     }
 
     @Test
-    public void testDismissToHome() {
+    public void testEnterRecents() {
         enterSplit();
 
         ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -264,7 +264,7 @@
         IBinder transition = mock(IBinder.class);
         WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
 
-        assertTrue(containsSplitExit(result));
+        assertTrue(result.isEmpty());
 
         // make sure we haven't made any local changes yet (need to wait until transition is ready)
         assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -284,7 +284,7 @@
                 mock(SurfaceControl.Transaction.class),
                 mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
-        assertFalse(mStageCoordinator.isSplitScreenVisible());
+        assertTrue(mStageCoordinator.isSplitScreenVisible());
     }
 
     @Test
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index 3eedda8..d87a3ce 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -29,40 +29,33 @@
 
 namespace android {
 
-void LocaleValue::set_language(const char* language_chars) {
+template <size_t N, class Transformer>
+static void safe_transform_copy(const char* source, char (&dest)[N], Transformer t) {
   size_t i = 0;
-  while ((*language_chars) != '\0') {
-    language[i++] = ::tolower(*language_chars);
-    language_chars++;
+  while (i < N && (*source) != '\0') {
+    dest[i++] = t(i, *source);
+    source++;
   }
+  while (i < N) {
+    dest[i++] = '\0';
+  }
+}
+
+void LocaleValue::set_language(const char* language_chars) {
+  safe_transform_copy(language_chars, language, [](size_t, char c) { return ::tolower(c); });
 }
 
 void LocaleValue::set_region(const char* region_chars) {
-  size_t i = 0;
-  while ((*region_chars) != '\0') {
-    region[i++] = ::toupper(*region_chars);
-    region_chars++;
-  }
+  safe_transform_copy(region_chars, region, [](size_t, char c) { return ::toupper(c); });
 }
 
 void LocaleValue::set_script(const char* script_chars) {
-  size_t i = 0;
-  while ((*script_chars) != '\0') {
-    if (i == 0) {
-      script[i++] = ::toupper(*script_chars);
-    } else {
-      script[i++] = ::tolower(*script_chars);
-    }
-    script_chars++;
-  }
+  safe_transform_copy(script_chars, script,
+                      [](size_t i, char c) { return i ? ::tolower(c) : ::toupper(c); });
 }
 
 void LocaleValue::set_variant(const char* variant_chars) {
-  size_t i = 0;
-  while ((*variant_chars) != '\0') {
-    variant[i++] = *variant_chars;
-    variant_chars++;
-  }
+  safe_transform_copy(variant_chars, variant, [](size_t, char c) { return c; });
 }
 
 static inline bool is_alpha(const std::string& str) {
@@ -234,6 +227,10 @@
   return static_cast<ssize_t>(iter - start_iter);
 }
 
+// Make sure the following memcpy's are properly sized.
+static_assert(sizeof(ResTable_config::localeScript) == sizeof(LocaleValue::script));
+static_assert(sizeof(ResTable_config::localeVariant) == sizeof(LocaleValue::variant));
+
 void LocaleValue::InitFromResTable(const ResTable_config& config) {
   config.unpackLanguage(language);
   config.unpackRegion(region);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index a2d0103..dc31bdd 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -35,7 +35,6 @@
         "skia_deps",
         //"hwui_bugreport_font_cache_usage",
         //"hwui_compile_for_perf",
-        "hwui_pgo",
         "hwui_lto",
     ],
 
@@ -159,22 +158,6 @@
     ],
 }
 
-// Build libhwui with PGO by default.
-// Location of PGO profile data is defined in build/soong/cc/pgo.go
-// and is separate from hwui.
-// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
-// or set enable_profile_use property to false.
-cc_defaults {
-    name: "hwui_pgo",
-
-    pgo: {
-        instrumentation: true,
-        profile_file: "hwui/hwui.profdata",
-        benchmarks: ["hwui"],
-        enable_profile_use: true,
-    },
-}
-
 // Build hwui library with ThinLTO by default.
 cc_defaults {
     name: "hwui_lto",
@@ -631,6 +614,7 @@
             version_script: "libhwui.map.txt",
         },
     },
+    afdo: true,
 }
 
 cc_library_static {
@@ -764,15 +748,3 @@
         "tests/microbench/RenderNodeBench.cpp",
     ],
 }
-
-// ----------------------------------------
-// Phony target to build benchmarks for PGO
-// ----------------------------------------
-
-phony {
-    name: "pgo-targets-hwui",
-    required: [
-        "hwuimicro",
-        "hwuimacro",
-    ],
-}
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index b802908..e359145 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -95,6 +95,16 @@
                                         endHyphen, advances);
 }
 
+minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
+                                                   const Typeface* typeface, const uint16_t* buf,
+                                                   size_t start, size_t count, size_t bufSize) {
+    minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
+    const minikin::U16StringPiece textBuf(buf, bufSize);
+    const minikin::Range range(start, start + count);
+
+    return minikin::getFontExtent(textBuf, range, bidiFlags, minikinPaint);
+}
+
 bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
     const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
     return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index a15803a..009b84b 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -56,6 +56,10 @@
                                          size_t start, size_t count, size_t bufSize,
                                          float* advances);
 
+    static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
+                                                const Typeface* typeface, const uint16_t* buf,
+                                                size_t start, size_t count, size_t bufSize);
+
     static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint,
                                                  uint32_t vs);
 
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index b7ddd21..08fc80f 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -67,7 +67,7 @@
         size_t chunkSize = obj != NULL ? env->GetArrayLength(obj) : 0;
         if (chunkSize < (int) (sizeof(Res_png_9patch))) {
             jniThrowRuntimeException(env, "Array too small for chunk.");
-            return NULL;
+            return 0;
         }
 
         int8_t* storage = new int8_t[chunkSize];
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 22a1e1f..f768632 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -541,26 +541,6 @@
         return result;
     }
 
-    // ------------------ @FastNative ---------------------------
-
-    static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        ScopedUtfChars localesChars(env, locales);
-        jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
-        obj->setMinikinLocaleListId(minikinLocaleListId);
-        return minikinLocaleListId;
-    }
-
-    static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        if (!settings) {
-            paint->setFontFeatureSettings(std::string());
-        } else {
-            ScopedUtfChars settingsChars(env, settings);
-            paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
-        }
-    }
-
     static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
         const int kElegantTop = 2500;
         const int kElegantBottom = -1000;
@@ -593,6 +573,67 @@
         return spacing;
     }
 
+    static void doFontExtent(JNIEnv* env, jlong paintHandle, const jchar buf[], jint start,
+                             jint count, jint bufSize, jboolean isRtl, jobject fmi) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+        minikin::MinikinExtent extent =
+                MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize);
+
+        SkFontMetrics metrics;
+        getMetricsInternal(paintHandle, &metrics);
+
+        metrics.fAscent = extent.ascent;
+        metrics.fDescent = extent.descent;
+
+        // If top/bottom is narrower than ascent/descent, adjust top/bottom to ascent/descent.
+        metrics.fTop = std::min(metrics.fAscent, metrics.fTop);
+        metrics.fBottom = std::max(metrics.fDescent, metrics.fBottom);
+
+        GraphicsJNI::set_metrics_int(env, fmi, metrics);
+    }
+
+    static void getFontMetricsIntForText___C(JNIEnv* env, jclass, jlong paintHandle,
+                                             jcharArray text, jint start, jint count, jint ctxStart,
+                                             jint ctxCount, jboolean isRtl, jobject fmi) {
+        ScopedCharArrayRO textArray(env, text);
+
+        doFontExtent(env, paintHandle, textArray.get() + ctxStart, start - ctxStart, count,
+                     ctxCount, isRtl, fmi);
+    }
+
+    static void getFontMetricsIntForText___String(JNIEnv* env, jclass, jlong paintHandle,
+                                                  jstring text, jint start, jint count,
+                                                  jint ctxStart, jint ctxCount, jboolean isRtl,
+                                                  jobject fmi) {
+        ScopedStringChars textChars(env, text);
+
+        doFontExtent(env, paintHandle, textChars.get() + ctxStart, start - ctxStart, count,
+                     ctxCount, isRtl, fmi);
+    }
+
+    // ------------------ @FastNative ---------------------------
+
+    static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        ScopedUtfChars localesChars(env, locales);
+        jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
+        obj->setMinikinLocaleListId(minikinLocaleListId);
+        return minikinLocaleListId;
+    }
+
+    static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle,
+                                       jstring settings) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (!settings) {
+            paint->setFontFeatureSettings(std::string());
+        } else {
+            ScopedUtfChars settingsChars(env, settings);
+            paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+        }
+    }
+
     static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
         SkFontMetrics metrics;
         SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
@@ -1015,6 +1056,11 @@
     {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
     {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+    {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
+      (void*)PaintGlue::getFontMetricsIntForText___C},
+    {"nGetFontMetricsIntForText",
+      "(JLjava/lang/String;IIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
+      (void*)PaintGlue::getFontMetricsIntForText___String},
 
     // --------------- @FastNative ----------------------
 
@@ -1093,6 +1139,7 @@
     {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
 };
 
+
 int register_android_graphics_Paint(JNIEnv* env) {
     return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
 }
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index c4366f75..c505b53 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -64,7 +64,8 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
-        jint tileModeX, jint tileModeY, bool filter) {
+                                      jint tileModeX, jint tileModeY, bool filter,
+                                      bool isDirectSampled) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     sk_sp<SkImage> image;
     if (bitmapHandle) {
@@ -79,8 +80,12 @@
     }
     SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
                                SkMipmapMode::kNone);
-    sk_sp<SkShader> shader = image->makeShader(
-            (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    sk_sp<SkShader> shader;
+    if (isDirectSampled) {
+        shader = image->makeRawShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    } else {
+        shader = image->makeShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    }
     ThrowIAE_IfNull(env, shader.get());
 
     if (matrix) {
@@ -393,7 +398,7 @@
 };
 
 static const JNINativeMethod gBitmapShaderMethods[] = {
-    { "nativeCreate",      "(JJIIZ)J",  (void*)BitmapShader_constructor },
+        {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor},
 };
 
 static const JNINativeMethod gLinearGradientMethods[] = {
diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp
index 72995ef..8cbb70e 100644
--- a/libs/hwui/jni/android_util_PathParser.cpp
+++ b/libs/hwui/jni/android_util_PathParser.cpp
@@ -61,7 +61,7 @@
     } else {
         delete pathData;
         doThrowIAE(env, result.failureMessage.c_str());
-        return NULL;
+        return 0;
     }
 }
 
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
index bd9bd71..76ea2d5 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -65,11 +65,13 @@
 
 // Regular JNI
 static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
-                         jlong paintPtr, jint start, jint end, jboolean isRtl) {
+                         jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end,
+                         jboolean isRtl) {
     Paint* paint = toPaint(paintPtr);
     const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
     minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
-    toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), isRtl);
+    toBuilder(builderPtr)
+            ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, isRtl);
 }
 
 // Regular JNI
@@ -144,7 +146,7 @@
 static const JNINativeMethod gMTBuilderMethods[] = {
         // MeasuredParagraphBuilder native functions.
         {"nInitBuilder", "()J", (void*)nInitBuilder},
-        {"nAddStyleRun", "(JJIIZ)V", (void*)nAddStyleRun},
+        {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun},
         {"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun},
         {"nBuildMeasuredText", "(JJ[CZZZ)J", (void*)nBuildMeasuredText},
         {"nFreeBuilder", "(J)V", (void*)nFreeBuilder},
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 0d78f58..f627a3c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -119,7 +119,7 @@
             AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(cbData);
     AVsyncId vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
             cbData, preferredFrameTimelineIndex);
-    int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+    int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
             cbData, preferredFrameTimelineIndex);
     int64_t frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(cbData);
     // TODO(b/193273294): Remove when shared memory in use w/ expected present time always current.
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 55f932d..6c0fd5f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -55,6 +55,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wthread-safety",
     ],
 
 }
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index f43586f..1dc74e5 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -18,11 +18,13 @@
 //#define LOG_NDEBUG 0
 
 #include "PointerController.h"
-#include "PointerControllerContext.h"
 
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkColor.h>
+#include <android-base/thread_annotations.h>
+
+#include "PointerControllerContext.h"
 
 namespace android {
 
@@ -36,8 +38,18 @@
 
 void PointerController::DisplayInfoListener::onWindowInfosChanged(
         const std::vector<android::gui::WindowInfo>&,
-        const std::vector<android::gui::DisplayInfo>& displayInfo) {
-    mPointerController.onDisplayInfosChanged(displayInfo);
+        const std::vector<android::gui::DisplayInfo>& displayInfos) {
+    std::scoped_lock lock(mLock);
+    if (mPointerController == nullptr) return;
+
+    // PointerController uses DisplayInfoListener's lock.
+    base::ScopedLockAssertion assumeLocked(mPointerController->getLock());
+    mPointerController->onDisplayInfosChangedLocked(displayInfos);
+}
+
+void PointerController::DisplayInfoListener::onPointerControllerDestroyed() {
+    std::scoped_lock lock(mLock);
+    mPointerController = nullptr;
 }
 
 // --- PointerController ---
@@ -68,16 +80,36 @@
 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
                                      const sp<Looper>& looper,
                                      const sp<SpriteController>& spriteController)
+      : PointerController(
+                policy, looper, spriteController,
+                [](const sp<android::gui::WindowInfosListener>& listener) {
+                    SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
+                },
+                [](const sp<android::gui::WindowInfosListener>& listener) {
+                    SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+                }) {}
+
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+                                     const sp<Looper>& looper,
+                                     const sp<SpriteController>& spriteController,
+                                     WindowListenerConsumer registerListener,
+                                     WindowListenerConsumer unregisterListener)
       : mContext(policy, looper, spriteController, *this),
         mCursorController(mContext),
-        mDisplayInfoListener(new DisplayInfoListener(*this)) {
-    std::scoped_lock lock(mLock);
+        mDisplayInfoListener(new DisplayInfoListener(this)),
+        mUnregisterWindowInfosListener(std::move(unregisterListener)) {
+    std::scoped_lock lock(getLock());
     mLocked.presentation = Presentation::SPOT;
-    SurfaceComposerClient::getDefault()->addWindowInfosListener(mDisplayInfoListener);
+    registerListener(mDisplayInfoListener);
 }
 
 PointerController::~PointerController() {
-    SurfaceComposerClient::getDefault()->removeWindowInfosListener(mDisplayInfoListener);
+    mDisplayInfoListener->onPointerControllerDestroyed();
+    mUnregisterWindowInfosListener(mDisplayInfoListener);
+}
+
+std::mutex& PointerController::getLock() const {
+    return mDisplayInfoListener->mLock;
 }
 
 bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -89,7 +121,7 @@
     const int32_t displayId = mCursorController.getDisplayId();
     vec2 transformed;
     {
-        std::scoped_lock lock(mLock);
+        std::scoped_lock lock(getLock());
         const auto& transform = getTransformForDisplayLocked(displayId);
         transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
     }
@@ -108,7 +140,7 @@
     const int32_t displayId = mCursorController.getDisplayId();
     vec2 transformed;
     {
-        std::scoped_lock lock(mLock);
+        std::scoped_lock lock(getLock());
         const auto& transform = getTransformForDisplayLocked(displayId);
         transformed = transform.transform(x, y);
     }
@@ -119,7 +151,7 @@
     const int32_t displayId = mCursorController.getDisplayId();
     mCursorController.getPosition(outX, outY);
     {
-        std::scoped_lock lock(mLock);
+        std::scoped_lock lock(getLock());
         const auto& transform = getTransformForDisplayLocked(displayId);
         const auto xy = transform.inverse().transform(*outX, *outY);
         *outX = xy.x;
@@ -132,17 +164,17 @@
 }
 
 void PointerController::fade(Transition transition) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.fade(transition);
 }
 
 void PointerController::unfade(Transition transition) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.unfade(transition);
 }
 
 void PointerController::setPresentation(Presentation presentation) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
 
     if (mLocked.presentation == presentation) {
         return;
@@ -162,7 +194,7 @@
 
 void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
                                  BitSet32 spotIdBits, int32_t displayId) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
     const ui::Transform& transform = getTransformForDisplayLocked(displayId);
 
@@ -185,11 +217,11 @@
 }
 
 void PointerController::clearSpots() {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     clearSpotsLocked();
 }
 
-void PointerController::clearSpotsLocked() REQUIRES(mLock) {
+void PointerController::clearSpotsLocked() {
     for (auto& [displayID, spotController] : mLocked.spotControllers) {
         spotController.clearSpots();
     }
@@ -200,7 +232,7 @@
 }
 
 void PointerController::reloadPointerResources() {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
 
     for (auto& [displayID, spotController] : mLocked.spotControllers) {
         spotController.reloadSpotResources();
@@ -216,7 +248,7 @@
 }
 
 void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
 
     bool getAdditionalMouseResources = false;
     if (mLocked.presentation == PointerController::Presentation::POINTER) {
@@ -226,12 +258,12 @@
 }
 
 void PointerController::updatePointerIcon(int32_t iconId) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.updatePointerIcon(iconId);
 }
 
 void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.setCustomPointerIcon(icon);
 }
 
@@ -245,7 +277,7 @@
         displayIdSet.insert(viewport.displayId);
     }
 
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
         int32_t displayID = it->first;
         if (!displayIdSet.count(displayID)) {
@@ -261,8 +293,8 @@
     }
 }
 
-void PointerController::onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfo) {
-    std::scoped_lock lock(mLock);
+void PointerController::onDisplayInfosChangedLocked(
+        const std::vector<gui::DisplayInfo>& displayInfo) {
     mLocked.mDisplayInfos = displayInfo;
 }
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 796077f..2e6e851 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -72,13 +72,31 @@
     void reloadPointerResources();
     void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
 
-    void onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfos);
+    void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+            REQUIRES(getLock());
+
+protected:
+    using WindowListenerConsumer =
+            std::function<void(const sp<android::gui::WindowInfosListener>&)>;
+
+    // Constructor used to test WindowInfosListener registration.
+    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+                      const sp<SpriteController>& spriteController,
+                      WindowListenerConsumer registerListener,
+                      WindowListenerConsumer unregisterListener);
 
 private:
+    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+                      const sp<SpriteController>& spriteController);
+
     friend PointerControllerContext::LooperCallback;
     friend PointerControllerContext::MessageHandler;
 
-    mutable std::mutex mLock;
+    // PointerController's DisplayInfoListener can outlive the PointerController because when the
+    // listener is registered, a strong pointer to the listener (which can extend its lifecycle)
+    // is given away. To avoid the small overhead of using two separate locks in these two objects,
+    // we use the DisplayInfoListener's lock in PointerController.
+    std::mutex& getLock() const;
 
     PointerControllerContext mContext;
 
@@ -89,24 +107,28 @@
 
         std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
-    } mLocked GUARDED_BY(mLock);
+    } mLocked GUARDED_BY(getLock());
 
     class DisplayInfoListener : public gui::WindowInfosListener {
     public:
-        explicit DisplayInfoListener(PointerController& pc) : mPointerController(pc){};
+        explicit DisplayInfoListener(PointerController* pc) : mPointerController(pc){};
         void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
                                   const std::vector<android::gui::DisplayInfo>&) override;
+        void onPointerControllerDestroyed();
+
+        // This lock is also used by PointerController. See PointerController::getLock().
+        std::mutex mLock;
 
     private:
-        PointerController& mPointerController;
+        PointerController* mPointerController GUARDED_BY(mLock);
     };
+
     sp<DisplayInfoListener> mDisplayInfoListener;
+    const WindowListenerConsumer mUnregisterWindowInfosListener;
 
-    const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(mLock);
+    const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(getLock());
 
-    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
-                      const sp<SpriteController>& spriteController);
-    void clearSpotsLocked();
+    void clearSpotsLocked() REQUIRES(getLock());
 };
 
 } // namespace android
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index b67088a..dae1fcc 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -255,4 +255,36 @@
     ensureDisplayViewportIsSet();
 }
 
+class PointerControllerWindowInfoListenerTest : public Test {};
+
+class TestPointerController : public PointerController {
+public:
+    TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
+                          const sp<Looper>& looper)
+          : PointerController(
+                    new MockPointerControllerPolicyInterface(), looper,
+                    new NiceMock<MockSpriteController>(looper),
+                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+                        // Register listener
+                        registeredListener = listener;
+                    },
+                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+                        // Unregister listener
+                        if (registeredListener == listener) registeredListener = nullptr;
+                    }) {}
+};
+
+TEST_F(PointerControllerWindowInfoListenerTest,
+       doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
+    sp<android::gui::WindowInfosListener> registeredListener;
+    sp<android::gui::WindowInfosListener> localListenerCopy;
+    {
+        TestPointerController pointerController(registeredListener, new Looper(false));
+        ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
+        localListenerCopy = registeredListener;
+    }
+    EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
+    localListenerCopy->onWindowInfosChanged({}, {});
+}
+
 }  // namespace android
diff --git a/libs/usb/tests/accessorytest/f_accessory.h b/libs/usb/tests/accessorytest/f_accessory.h
index 312f4ba..75e017c 100644
--- a/libs/usb/tests/accessorytest/f_accessory.h
+++ b/libs/usb/tests/accessorytest/f_accessory.h
@@ -1,148 +1,53 @@
-/*
- * Gadget Function Driver for Android USB accessories
- *
- * Copyright (C) 2011 Google, Inc.
- * Author: Mike Lockwood <[email protected]>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __LINUX_USB_F_ACCESSORY_H
-#define __LINUX_USB_F_ACCESSORY_H
-
-/* Use Google Vendor ID when in accessory mode */
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H
+#define _UAPI_LINUX_USB_F_ACCESSORY_H
 #define USB_ACCESSORY_VENDOR_ID 0x18D1
-
-
-/* Product ID to use when in accessory mode */
 #define USB_ACCESSORY_PRODUCT_ID 0x2D00
-
-/* Product ID to use when in accessory mode and adb is enabled */
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 #define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
-
-/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
-#define ACCESSORY_STRING_MANUFACTURER   0
-#define ACCESSORY_STRING_MODEL          1
-#define ACCESSORY_STRING_DESCRIPTION    2
-#define ACCESSORY_STRING_VERSION        3
-#define ACCESSORY_STRING_URI            4
-#define ACCESSORY_STRING_SERIAL         5
-
-/* Control request for retrieving device's protocol version
- *
- *	requestType:    USB_DIR_IN | USB_TYPE_VENDOR
- *	request:        ACCESSORY_GET_PROTOCOL
- *	value:          0
- *	index:          0
- *	data            version number (16 bits little endian)
- *                    1 for original accessory support
- *                    2 adds audio and HID support
- */
-#define ACCESSORY_GET_PROTOCOL  51
-
-/* Control request for host to send a string to the device
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_SEND_STRING
- *	value:          0
- *	index:          string ID
- *	data            zero terminated UTF8 string
- *
- *  The device can later retrieve these strings via the
- *  ACCESSORY_GET_STRING_* ioctls
- */
-#define ACCESSORY_SEND_STRING   52
-
-/* Control request for starting device in accessory mode.
- * The host sends this after setting all its strings to the device.
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_START
- *	value:          0
- *	index:          0
- *	data            none
- */
-#define ACCESSORY_START         53
-
-/* Control request for registering a HID device.
- * Upon registering, a unique ID is sent by the accessory in the
- * value parameter. This ID will be used for future commands for
- * the device
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_REGISTER_HID_DEVICE
- *	value:          Accessory assigned ID for the HID device
- *	index:          total length of the HID report descriptor
- *	data            none
- */
-#define ACCESSORY_REGISTER_HID         54
-
-/* Control request for unregistering a HID device.
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_REGISTER_HID
- *	value:          Accessory assigned ID for the HID device
- *	index:          0
- *	data            none
- */
-#define ACCESSORY_UNREGISTER_HID         55
-
-/* Control request for sending the HID report descriptor.
- * If the HID descriptor is longer than the endpoint zero max packet size,
- * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
- * commands. The data for the descriptor must be sent sequentially
- * if multiple packets are needed.
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_SET_HID_REPORT_DESC
- *	value:          Accessory assigned ID for the HID device
- *	index:          offset of data in descriptor
- *                  (needed when HID descriptor is too big for one packet)
- *	data            the HID report descriptor
- */
-#define ACCESSORY_SET_HID_REPORT_DESC         56
-
-/* Control request for sending HID events.
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_SEND_HID_EVENT
- *	value:          Accessory assigned ID for the HID device
- *	index:          0
- *	data            the HID report for the event
- */
-#define ACCESSORY_SEND_HID_EVENT         57
-
-/* Control request for setting the audio mode.
- *
- *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
- *	request:        ACCESSORY_SET_AUDIO_MODE
- *	value:          0 - no audio
- *                  1 - device to host, 44100 16-bit stereo PCM
- *	index:          0
- *	data            the HID report for the event
- */
-#define ACCESSORY_SET_AUDIO_MODE         58
-
-
-
-/* ioctls for retrieving strings set by the host */
-#define ACCESSORY_GET_STRING_MANUFACTURER   _IOW('M', 1, char[256])
-#define ACCESSORY_GET_STRING_MODEL          _IOW('M', 2, char[256])
-#define ACCESSORY_GET_STRING_DESCRIPTION    _IOW('M', 3, char[256])
-#define ACCESSORY_GET_STRING_VERSION        _IOW('M', 4, char[256])
-#define ACCESSORY_GET_STRING_URI            _IOW('M', 5, char[256])
-#define ACCESSORY_GET_STRING_SERIAL         _IOW('M', 6, char[256])
-/* returns 1 if there is a start request pending */
-#define ACCESSORY_IS_START_REQUESTED        _IO('M', 7)
-/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
-#define ACCESSORY_GET_AUDIO_MODE            _IO('M', 8)
-
-#endif /* __LINUX_USB_F_ACCESSORY_H */
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+#define ACCESSORY_GET_PROTOCOL 51
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_SEND_STRING 52
+#define ACCESSORY_START 53
+#define ACCESSORY_REGISTER_HID 54
+#define ACCESSORY_UNREGISTER_HID 55
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+#define ACCESSORY_SEND_HID_EVENT 57
+#define ACCESSORY_SET_AUDIO_MODE 58
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index e4a0d0c..a1583440 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -298,6 +298,7 @@
      * @param lowerLeftLongitude  the longitude of the lower left corner of the bounding box
      * @param upperRightLatitude  the latitude of the upper right corner of the bounding box
      * @param upperRightLongitude the longitude of the upper right corner of the bounding box
+     * @param listener            a listener for receiving results
      *
      * @throws IllegalArgumentException if locationName is null
      * @throws IllegalArgumentException if any latitude or longitude is invalid
diff --git a/location/java/android/location/GnssAutomaticGainControl.aidl b/location/java/android/location/GnssAutomaticGainControl.aidl
new file mode 100644
index 0000000..8298cb71
--- /dev/null
+++ b/location/java/android/location/GnssAutomaticGainControl.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+parcelable GnssAutomaticGainControl;
diff --git a/location/java/android/location/GnssAutomaticGainControl.java b/location/java/android/location/GnssAutomaticGainControl.java
new file mode 100644
index 0000000..e4f7304
--- /dev/null
+++ b/location/java/android/location/GnssAutomaticGainControl.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A class that contains GNSS Automatic Gain Control (AGC) information.
+ *
+ * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
+ * level may be used to indicate potential interference. Higher gain (and/or lower input power)
+ * shall be output as a positive number. Hence in cases of strong jamming, in the band of this
+ * signal, this value will go more negative. This value must be consistent given the same level
+ * of the incoming signal power.
+ *
+ * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * components) may also affect the typical output of this value on any given hardware design
+ * in an open sky test - the important aspect of this output is that changes in this value are
+ * indicative of changes on input signal power in the frequency band for this measurement.
+ */
+public final class GnssAutomaticGainControl implements Parcelable {
+    private final double mLevelDb;
+    private final int mConstellationType;
+    private final long mCarrierFrequencyHz;
+
+    /**
+     * Creates a {@link GnssAutomaticGainControl} with a full list of parameters.
+     */
+    private GnssAutomaticGainControl(double levelDb, int constellationType,
+            long carrierFrequencyHz) {
+        mLevelDb = levelDb;
+        mConstellationType = constellationType;
+        mCarrierFrequencyHz = carrierFrequencyHz;
+    }
+
+    /**
+     * Gets the Automatic Gain Control level in dB.
+     */
+    @FloatRange(from = -10000, to = 10000)
+    public double getLevelDb() {
+        return mLevelDb;
+    }
+
+    /**
+     * Gets the constellation type.
+     *
+     * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+     * {@link GnssStatus}.
+     */
+    @GnssStatus.ConstellationType
+    public int getConstellationType() {
+        return mConstellationType;
+    }
+
+    /**
+     * Gets the carrier frequency of the tracked signal.
+     *
+     * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+     * L5 = 1176.45 MHz, varying GLO channels, etc.
+     *
+     * @return the carrier frequency of the signal tracked in Hz.
+     */
+    @IntRange(from = 0)
+    public long getCarrierFrequencyHz() {
+        return mCarrierFrequencyHz;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flag) {
+        parcel.writeDouble(mLevelDb);
+        parcel.writeInt(mConstellationType);
+        parcel.writeLong(mCarrierFrequencyHz);
+    }
+
+    @NonNull
+    public static final Creator<GnssAutomaticGainControl> CREATOR =
+            new Creator<GnssAutomaticGainControl>() {
+                @Override
+                @NonNull
+                public GnssAutomaticGainControl createFromParcel(@NonNull Parcel parcel) {
+                    return new GnssAutomaticGainControl(parcel.readDouble(), parcel.readInt(),
+                            parcel.readLong());
+                }
+
+                @Override
+                public GnssAutomaticGainControl[] newArray(int i) {
+                    return new GnssAutomaticGainControl[i];
+                }
+            };
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("GnssAutomaticGainControl[");
+        s.append("Level=").append(mLevelDb).append(" dB");
+        s.append(" Constellation=").append(
+                GnssStatus.constellationTypeToString(mConstellationType));
+        s.append(" CarrierFrequency=").append(mCarrierFrequencyHz).append(" Hz");
+        s.append(']');
+        return s.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof GnssAutomaticGainControl)) {
+            return false;
+        }
+
+        GnssAutomaticGainControl other = (GnssAutomaticGainControl) obj;
+        if (Double.compare(mLevelDb, other.mLevelDb)
+                != 0) {
+            return false;
+        }
+        if (mConstellationType != other.mConstellationType) {
+            return false;
+        }
+        if (mCarrierFrequencyHz != other.mCarrierFrequencyHz) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLevelDb, mConstellationType, mCarrierFrequencyHz);
+    }
+
+    /** Builder for {@link GnssAutomaticGainControl} */
+    public static final class Builder {
+        private double mLevelDb;
+        private int mConstellationType;
+        private long mCarrierFrequencyHz;
+
+        /**
+         * Constructs a {@link GnssAutomaticGainControl.Builder} instance.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a {@link GnssAutomaticGainControl.Builder} instance by copying a
+         * {@link GnssAutomaticGainControl}.
+         */
+        public Builder(@NonNull GnssAutomaticGainControl agc) {
+            mLevelDb = agc.getLevelDb();
+            mConstellationType = agc.getConstellationType();
+            mCarrierFrequencyHz = agc.getCarrierFrequencyHz();
+        }
+
+        /**
+         * Sets the Automatic Gain Control level in dB.
+         */
+        @NonNull
+        public Builder setLevelDb(@FloatRange(from = -10000, to = 10000) double levelDb) {
+            Preconditions.checkArgument(levelDb >= -10000 && levelDb <= 10000);
+            mLevelDb = levelDb;
+            return this;
+        }
+
+        /**
+         * Sets the constellation type.
+         */
+        @NonNull
+        public Builder setConstellationType(@GnssStatus.ConstellationType int constellationType) {
+            mConstellationType = constellationType;
+            return this;
+        }
+
+        /**
+         * Sets the Carrier frequency in Hz.
+         */
+        @NonNull public Builder setCarrierFrequencyHz(@IntRange(from = 0) long carrierFrequencyHz) {
+            Preconditions.checkArgumentNonnegative(carrierFrequencyHz);
+            mCarrierFrequencyHz = carrierFrequencyHz;
+            return this;
+        }
+
+        /** Builds a {@link GnssAutomaticGainControl} instance as specified by this builder. */
+        @NonNull
+        public GnssAutomaticGainControl build() {
+            return new GnssAutomaticGainControl(mLevelDb, mConstellationType, mCarrierFrequencyHz);
+        }
+    }
+}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 2c94820d..ab3dafe 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1381,7 +1381,10 @@
     /**
      * Returns {@code true} if {@link #getAutomaticGainControlLevelDb()} is available,
      * {@code false} otherwise.
+     *
+     * @deprecated Use {@link GnssMeasurementsEvent#getGnssAutomaticGainControls()} instead.
      */
+    @Deprecated
     public boolean hasAutomaticGainControlLevelDb() {
         return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
     }
@@ -1401,7 +1404,10 @@
      * indicative of changes on input signal power in the frequency band for this measurement.
      *
      * <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+     *
+     * @deprecated Use {@link GnssMeasurementsEvent#getGnssAutomaticGainControls()} instead.
      */
+    @Deprecated
     public double getAutomaticGainControlLevelDb() {
         return mAutomaticGainControlLevelInDb;
     }
@@ -1409,7 +1415,9 @@
     /**
      * Sets the Automatic Gain Control level in dB.
      * @hide
+     * @deprecated Use {@link GnssMeasurementsEvent.Builder#setGnssAutomaticGainControls()} instead.
      */
+    @Deprecated
     @TestApi
     public void setAutomaticGainControlLevelInDb(double agcLevelDb) {
         setFlag(HAS_AUTOMATIC_GAIN_CONTROL);
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index b744017..0397740 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -18,16 +18,19 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * A class implementing a container for data associated with a measurement event.
@@ -35,7 +38,8 @@
  */
 public final class GnssMeasurementsEvent implements Parcelable {
     private final GnssClock mClock;
-    private final Collection<GnssMeasurement> mReadOnlyMeasurements;
+    private final List<GnssMeasurement> mMeasurements;
+    private final List<GnssAutomaticGainControl> mGnssAgcs;
 
     /**
      * Used for receiving GNSS satellite measurements from the GNSS engine.
@@ -116,20 +120,13 @@
     }
 
     /**
-     * @hide
+     * Create a {@link GnssMeasurementsEvent} instance with a full list of parameters.
      */
-    @TestApi
-    public GnssMeasurementsEvent(GnssClock clock, GnssMeasurement[] measurements) {
-        if (clock == null) {
-            throw new InvalidParameterException("Parameter 'clock' must not be null.");
-        }
-        if (measurements == null || measurements.length == 0) {
-            mReadOnlyMeasurements = Collections.emptyList();
-        } else {
-            Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
-            mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
-        }
-
+    private GnssMeasurementsEvent(@NonNull GnssClock clock,
+            @NonNull List<GnssMeasurement> measurements,
+            @NonNull List<GnssAutomaticGainControl> agcs) {
+        mMeasurements = measurements;
+        mGnssAgcs = agcs;
         mClock = clock;
     }
 
@@ -143,26 +140,31 @@
     }
 
     /**
-     * Gets a read-only collection of measurements associated with the current event.
+     * Gets the collection of measurements associated with the current event.
      */
     @NonNull
     public Collection<GnssMeasurement> getMeasurements() {
-        return mReadOnlyMeasurements;
+        return mMeasurements;
+    }
+
+    /**
+     * Gets the collection of {@link GnssAutomaticGainControl} associated with the
+     * current event.
+     */
+    @NonNull
+    public Collection<GnssAutomaticGainControl> getGnssAutomaticGainControls() {
+        return mGnssAgcs;
     }
 
     public static final @android.annotation.NonNull Creator<GnssMeasurementsEvent> CREATOR =
             new Creator<GnssMeasurementsEvent>() {
         @Override
         public GnssMeasurementsEvent createFromParcel(Parcel in) {
-            ClassLoader classLoader = getClass().getClassLoader();
-
-            GnssClock clock = in.readParcelable(classLoader, android.location.GnssClock.class);
-
-            int measurementsLength = in.readInt();
-            GnssMeasurement[] measurementsArray = new GnssMeasurement[measurementsLength];
-            in.readTypedArray(measurementsArray, GnssMeasurement.CREATOR);
-
-            return new GnssMeasurementsEvent(clock, measurementsArray);
+            GnssClock clock = in.readParcelable(getClass().getClassLoader(), android.location.GnssClock.class);
+            List<GnssMeasurement> measurements = in.createTypedArrayList(GnssMeasurement.CREATOR);
+            List<GnssAutomaticGainControl> agcs = in.createTypedArrayList(
+                    GnssAutomaticGainControl.CREATOR);
+            return new GnssMeasurementsEvent(clock, measurements, agcs);
         }
 
         @Override
@@ -179,28 +181,105 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeParcelable(mClock, flags);
-
-        int measurementsCount = mReadOnlyMeasurements.size();
-        GnssMeasurement[] measurementsArray =
-                mReadOnlyMeasurements.toArray(new GnssMeasurement[measurementsCount]);
-        parcel.writeInt(measurementsArray.length);
-        parcel.writeTypedArray(measurementsArray, flags);
+        parcel.writeTypedList(mMeasurements);
+        parcel.writeTypedList(mGnssAgcs);
     }
 
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder("[ GnssMeasurementsEvent:\n\n");
+        StringBuilder builder = new StringBuilder("GnssMeasurementsEvent[");
+        builder.append(mClock);
+        builder.append(' ').append(mMeasurements.toString());
+        builder.append(' ').append(mGnssAgcs.toString());
+        builder.append("]");
+        return builder.toString();
+    }
 
-        builder.append(mClock.toString());
-        builder.append("\n");
+    /** Builder for {@link GnssMeasurementsEvent} */
+    public static final class Builder {
+        private GnssClock mClock;
+        private List<GnssMeasurement> mMeasurements;
+        private List<GnssAutomaticGainControl> mGnssAgcs;
 
-        for (GnssMeasurement measurement : mReadOnlyMeasurements) {
-            builder.append(measurement.toString());
-            builder.append("\n");
+        /**
+         * Constructs a {@link GnssMeasurementsEvent.Builder} instance.
+         */
+        public Builder() {
+            mClock = new GnssClock();
+            mMeasurements = new ArrayList<>();
+            mGnssAgcs = new ArrayList<>();
         }
 
-        builder.append("]");
+        /**
+         * Constructs a {@link GnssMeasurementsEvent.Builder} instance by copying a
+         * {@link GnssMeasurementsEvent}.
+         */
+        public Builder(@NonNull GnssMeasurementsEvent event) {
+            mClock = event.getClock();
+            mMeasurements = (List<GnssMeasurement>) event.getMeasurements();
+            mGnssAgcs = (List<GnssAutomaticGainControl>) event.getGnssAutomaticGainControls();
+        }
 
-        return builder.toString();
+        /**
+         * Sets the {@link GnssClock}.
+         */
+        @NonNull
+        public Builder setClock(@NonNull GnssClock clock) {
+            Preconditions.checkNotNull(clock);
+            mClock = clock;
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssMeasurement}.
+         *
+         * This API exists for JNI since it is easier for JNI to work with an array than a
+         * collection.
+         * @hide
+         */
+        @NonNull
+        public Builder setMeasurements(@Nullable GnssMeasurement... measurements) {
+            mMeasurements = measurements == null ? Collections.emptyList() : Arrays.asList(
+                    measurements);
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssMeasurement}.
+         */
+        @NonNull
+        public Builder setMeasurements(@NonNull Collection<GnssMeasurement> measurements) {
+            mMeasurements = new ArrayList<>(measurements);
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssAutomaticGainControl}.
+         *
+         * This API exists for JNI since it is easier for JNI to work with an array than a
+         * collection.
+         * @hide
+         */
+        @NonNull
+        public Builder setGnssAutomaticGainControls(@Nullable GnssAutomaticGainControl... agcs) {
+            mGnssAgcs = agcs == null ? Collections.emptyList() : Arrays.asList(agcs);
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssAutomaticGainControl}.
+         */
+        @NonNull
+        public Builder setGnssAutomaticGainControls(
+                @NonNull Collection<GnssAutomaticGainControl> agcs) {
+            mGnssAgcs = new ArrayList<>(agcs);
+            return this;
+        }
+
+        /** Builds a {@link GnssMeasurementsEvent} instance as specified by this builder. */
+        @NonNull
+        public GnssMeasurementsEvent build() {
+            return new GnssMeasurementsEvent(mClock, mMeasurements, mGnssAgcs);
+        }
     }
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 61caa0b..9109a18 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -3640,7 +3640,7 @@
         }
 
         @Override
-        protected Boolean recompute(Integer userId) {
+        public Boolean recompute(Integer userId) {
             Preconditions.checkArgument(userId >= 0);
 
             if (mManager == null) {
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index 88a2479..529eddd 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -26,7 +26,9 @@
 import android.content.Intent;
 import android.location.Location;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -308,9 +310,7 @@
             synchronized (mBinder) {
                 try {
                     manager.onInitialize(mAllowed, mProperties, mAttributionTag);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                } catch (RuntimeException e) {
+                } catch (RemoteException | RuntimeException e) {
                     Log.w(mTag, e);
                 }
 
@@ -320,12 +320,28 @@
 
         @Override
         public void setRequest(ProviderRequest request) {
-            onSetRequest(request);
+            try {
+                onSetRequest(request);
+            } catch (RuntimeException e) {
+                // exceptions on one-way binder threads are dropped - move to a different thread
+                Log.w(mTag, e);
+                new Handler(Looper.getMainLooper()).post(() -> {
+                    throw new AssertionError(e);
+                });
+            }
         }
 
         @Override
         public void flush() {
-            onFlush(this::onFlushComplete);
+            try {
+                onFlush(this::onFlushComplete);
+            } catch (RuntimeException e) {
+                // exceptions on one-way binder threads are dropped - move to a different thread
+                Log.w(mTag, e);
+                new Handler(Looper.getMainLooper()).post(() -> {
+                    throw new AssertionError(e);
+                });
+            }
         }
 
         private void onFlushComplete() {
@@ -333,9 +349,7 @@
             if (manager != null) {
                 try {
                     manager.onFlushComplete();
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                } catch (RuntimeException e) {
+                } catch (RemoteException | RuntimeException e) {
                     Log.w(mTag, e);
                 }
             }
@@ -343,7 +357,15 @@
 
         @Override
         public void sendExtraCommand(String command, Bundle extras) {
-            onSendExtraCommand(command, extras);
+            try {
+                onSendExtraCommand(command, extras);
+            } catch (RuntimeException e) {
+                // exceptions on one-way binder threads are dropped - move to a different thread
+                Log.w(mTag, e);
+                new Handler(Looper.getMainLooper()).post(() -> {
+                    throw new AssertionError(e);
+                });
+            }
         }
     }
 }
diff --git a/media/OWNERS b/media/OWNERS
index 0aff43e..5f50137 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -3,7 +3,6 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
diff --git a/media/aidl/android/media/audio/common/AudioContentType.aidl b/media/aidl/android/media/audio/common/AudioContentType.aidl
index 50ac181..f42ae2f 100644
--- a/media/aidl/android/media/audio/common/AudioContentType.aidl
+++ b/media/aidl/android/media/audio/common/AudioContentType.aidl
@@ -50,4 +50,8 @@
      * in a game. These sounds are mostly synthesized or short Foley sounds.
      */
     SONIFICATION = 4,
+    /**
+     * Content type value to use when the content type is ultrasound.
+     */
+    ULTRASOUND = 1997,
 }
diff --git a/media/aidl/android/media/audio/common/AudioDeviceType.aidl b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
index afe6d10..8e200de 100644
--- a/media/aidl/android/media/audio/common/AudioDeviceType.aidl
+++ b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
@@ -168,4 +168,8 @@
      * Output into a speaker of a phone / table dock.
      */
     OUT_DOCK = 145,
+    /**
+     * Output to a broadcast group.
+     */
+    OUT_BROADCAST = 146,
 }
diff --git a/media/aidl/android/media/audio/common/AudioInputFlags.aidl b/media/aidl/android/media/audio/common/AudioInputFlags.aidl
index e4b6ec2..83a5d9d 100644
--- a/media/aidl/android/media/audio/common/AudioInputFlags.aidl
+++ b/media/aidl/android/media/audio/common/AudioInputFlags.aidl
@@ -59,4 +59,8 @@
      * Input contains an encoded audio stream.
      */
     DIRECT = 7,
+    /**
+     * Input is for capturing "ultrasound" audio commands.
+     */
+    ULTRASOUND = 8,
 }
diff --git a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
index 0505036..2556b68 100644
--- a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
+++ b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
@@ -26,7 +26,7 @@
  */
 @VintfStability
 @Backing(type="int")
-enum AudioOutputFlags {
+enum AudioOutputFlags{
     /**
      * Output must not be altered by the framework, it bypasses software mixers.
      */
@@ -97,4 +97,12 @@
      * tracks.
      */
     GAPLESS_OFFLOAD = 15,
+    /**
+     * Output is used for spatial audio.
+     */
+    SPATIALIZER = 16,
+    /**
+     * Output is used for transmitting ultrasound audio.
+     */
+    ULTRASOUND = 17,
 }
diff --git a/media/aidl/android/media/audio/common/AudioSource.aidl b/media/aidl/android/media/audio/common/AudioSource.aidl
index 527ee39..7779994 100644
--- a/media/aidl/android/media/audio/common/AudioSource.aidl
+++ b/media/aidl/android/media/audio/common/AudioSource.aidl
@@ -87,4 +87,8 @@
      * hotword detection. Same tuning as VOICE_RECOGNITION.
      */
     HOTWORD = 1999,
+    /** Microphone audio source for ultrasound sound if available,
+     *  behaves like DEFAULT otherwise.
+     */
+    ULTRASOUND = 2000,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
index 3798b82..f9ac614 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
@@ -40,4 +40,5 @@
   MUSIC = 2,
   MOVIE = 3,
   SONIFICATION = 4,
+  ULTRASOUND = 1997,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
index 0b7b77c..6a7b686 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
@@ -67,4 +67,5 @@
   OUT_SUBMIX = 143,
   OUT_TELEPHONY_TX = 144,
   OUT_DOCK = 145,
+  OUT_BROADCAST = 146,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
index 8a5dae0..37aa64a 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
@@ -43,4 +43,5 @@
   VOIP_TX = 5,
   HW_AV_SYNC = 6,
   DIRECT = 7,
+  ULTRASOUND = 8,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
index ed16d17..4a512a8 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
@@ -51,4 +51,6 @@
   VOIP_RX = 13,
   INCALL_MUSIC = 14,
   GAPLESS_OFFLOAD = 15,
+  SPATIALIZER = 16,
+  ULTRASOUND = 17,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
index d1dfe41..acf822e 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
@@ -50,4 +50,5 @@
   ECHO_REFERENCE = 1997,
   FM_TUNER = 1998,
   HOTWORD = 1999,
+  ULTRASOUND = 2000,
 }
diff --git a/media/java/Android.bp b/media/java/Android.bp
index eeaf6e9..c7c1d54 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -8,7 +8,7 @@
 }
 
 filegroup {
-    name: "framework-media-sources",
+    name: "framework-media-non-updatable-sources",
     srcs: [
         "**/*.java",
         "**/*.aidl",
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 85e49cc..ded9597 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -101,6 +101,13 @@
      * or short Foley sounds.
      */
     public final static int CONTENT_TYPE_SONIFICATION = 4;
+    /**
+     * @hide
+     * Content type value to use when the content type is ultrasound.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+    public static final int CONTENT_TYPE_ULTRASOUND = 1997;
 
     /**
      * Invalid value, only ever used for an uninitialized usage value
@@ -958,6 +965,26 @@
         }
 
         /**
+         * @hide
+         * Sets the attribute describing the content type of the audio signal, such as speech,
+         * , music or ultrasound.
+         * @param contentType the content type values.
+         * @return the same Builder instance.
+         */
+        @SystemApi
+        public @NonNull Builder setInternalContentType(@AttrInternalContentType int contentType) {
+            switch (contentType) {
+                case CONTENT_TYPE_ULTRASOUND:
+                    mContentType = contentType;
+                    break;
+                default:
+                    setContentType(contentType);
+                    break;
+            }
+            return this;
+        }
+
+        /**
          * Sets the combination of flags.
          *
          * This is a bitwise OR with the existing flags.
@@ -1234,7 +1261,8 @@
         /**
          * @hide
          * Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD,
-         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL and ECHO_REFERENCE.
+         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL, ECHO_REFERENCE
+         * and ULTRASOUND
          * @param preset
          * @return the same Builder instance.
          */
@@ -1246,7 +1274,8 @@
                     || (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK)
                     || (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
                     || (preset == MediaRecorder.AudioSource.VOICE_CALL)
-                    || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)) {
+                    || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)
+                    || (preset == MediaRecorder.AudioSource.ULTRASOUND)) {
                 mSource = preset;
             } else {
                 setCapturePreset(preset);
@@ -1589,6 +1618,7 @@
             case CONTENT_TYPE_MUSIC: return new String("CONTENT_TYPE_MUSIC");
             case CONTENT_TYPE_MOVIE: return new String("CONTENT_TYPE_MOVIE");
             case CONTENT_TYPE_SONIFICATION: return new String("CONTENT_TYPE_SONIFICATION");
+            case CONTENT_TYPE_ULTRASOUND: return new String("CONTENT_TYPE_ULTRASOUND");
             default: return new String("unknown content type " + mContentType);
         }
     }
@@ -1823,4 +1853,16 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AttributeContentType {}
+
+    /** @hide */
+    @IntDef({
+        CONTENT_TYPE_UNKNOWN,
+        CONTENT_TYPE_SPEECH,
+        CONTENT_TYPE_MUSIC,
+        CONTENT_TYPE_MOVIE,
+        CONTENT_TYPE_SONIFICATION,
+        CONTENT_TYPE_ULTRASOUND
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttrInternalContentType {}
 }
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 211a50e..dd17dc6 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -177,6 +177,11 @@
      */
     public static final int TYPE_HDMI_EARC         = 29;
 
+    /**
+     * A device type describing a Bluetooth Low Energy (BLE) broadcast group.
+     */
+    public static final int TYPE_BLE_BROADCAST   = 30;
+
     /** @hide */
     @IntDef(flag = false, prefix = "TYPE", value = {
             TYPE_BUILTIN_EARPIECE,
@@ -207,7 +212,8 @@
             TYPE_REMOTE_SUBMIX,
             TYPE_BLE_HEADSET,
             TYPE_BLE_SPEAKER,
-            TYPE_ECHO_REFERENCE}
+            TYPE_ECHO_REFERENCE,
+            TYPE_BLE_BROADCAST}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceType {}
@@ -264,7 +270,8 @@
             TYPE_HEARING_AID,
             TYPE_BUILTIN_SPEAKER_SAFE,
             TYPE_BLE_HEADSET,
-            TYPE_BLE_SPEAKER}
+            TYPE_BLE_SPEAKER,
+            TYPE_BLE_BROADCAST}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceTypeOut {}
@@ -296,6 +303,7 @@
             case TYPE_BUILTIN_SPEAKER_SAFE:
             case TYPE_BLE_HEADSET:
             case TYPE_BLE_SPEAKER:
+            case TYPE_BLE_BROADCAST:
                 return true;
             default:
                 return false;
@@ -636,6 +644,7 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_HEADSET, TYPE_BLE_HEADSET);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_SPEAKER, TYPE_BLE_SPEAKER);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_BROADCAST, TYPE_BLE_BROADCAST);
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -690,6 +699,7 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_BROADCAST, AudioSystem.DEVICE_OUT_BLE_BROADCAST);
 
         // privileges mapping to input device
         EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index ebe0882..9211c53 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -90,7 +90,8 @@
      * {@link AudioManager#DEVICE_OUT_BLE_HEADSET}, {@link AudioManager#DEVICE_OUT_BLE_SPEAKER})
      * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by
      * {@link BluetoothDevice#getAddress()}.
-     * - Deivces that do not have an address will indicate an empty string "".
+     * - Bluetooth LE broadcast group ({@link AudioManager#DEVICE_OUT_BLE_BROADCAST} use the group number.
+     * - Devices that do not have an address will indicate an empty string "".
      */
     public String address() {
         return mAddress;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 21fc6ec..c4cef4c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -79,6 +79,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -2995,19 +2996,17 @@
         void onModeChanged(@AudioMode int mode);
     }
 
-    private final Object mModeListenerLock = new Object();
     /**
-     * List of listeners for audio mode and their associated Executor.
-     * List is lazy-initialized on first registration
+     * manages the OnModeChangedListener listeners and the ModeDispatcherStub
      */
-    @GuardedBy("mModeListenerLock")
-    private @Nullable ArrayList<ListenerInfo<OnModeChangedListener>> mModeListeners;
+    private final CallbackUtil.LazyListenerManager<OnModeChangedListener> mModeChangedListenerMgr =
+            new CallbackUtil.LazyListenerManager();
 
-    @GuardedBy("mModeListenerLock")
-    private ModeDispatcherStub mModeDispatcherStub;
 
-    private final class ModeDispatcherStub extends IAudioModeDispatcher.Stub {
+    final class ModeDispatcherStub extends IAudioModeDispatcher.Stub
+            implements CallbackUtil.DispatcherStub {
 
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -3021,10 +3020,8 @@
         }
 
         @Override
-        @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchAudioModeChanged(int mode) {
-            CallbackUtil.callListeners(mModeListeners, mModeListenerLock,
-                    (listener) -> listener.onModeChanged(mode));
+            mModeChangedListenerMgr.callListeners((listener) -> listener.onModeChanged(mode));
         }
     }
 
@@ -3037,15 +3034,8 @@
     public void addOnModeChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnModeChangedListener listener) {
-        synchronized (mModeListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res =
-                    CallbackUtil.addListener("addOnModeChangedListener",
-                            executor, listener, mModeListeners, mModeDispatcherStub,
-                            () -> new ModeDispatcherStub(),
-                            stub -> stub.register(true));
-            mModeListeners = res.first;
-            mModeDispatcherStub = res.second;
-        }
+        mModeChangedListenerMgr.addListener(executor, listener, "addOnModeChangedListener",
+                () -> new ModeDispatcherStub());
     }
 
     /**
@@ -3054,14 +3044,7 @@
      * @param listener
      */
     public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) {
-        synchronized (mModeListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res =
-                    CallbackUtil.removeListener("removeOnModeChangedListener",
-                            listener, mModeListeners, mModeDispatcherStub,
-                            stub -> stub.register(false));
-            mModeListeners = res.first;
-            mModeDispatcherStub = res.second;
-        }
+        mModeChangedListenerMgr.removeListener(listener, "removeOnModeChangedListener");
     }
 
     /**
@@ -5457,6 +5440,10 @@
      */
     public static final int DEVICE_OUT_BLE_SPEAKER = AudioSystem.DEVICE_OUT_BLE_SPEAKER;
     /** @hide
+     * The audio output device code for a BLE audio brodcast group.
+     */
+    public static final int DEVICE_OUT_BLE_BROADCAST = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
+    /** @hide
      * This is not used as a returned value from {@link #getDevicesForStream}, but could be
      *  used in the future in a set method to select whatever default device is chosen by the
      *  platform-specific implementation.
@@ -5666,6 +5653,43 @@
     }
 
     /**
+     * Get the audio devices that would be used for the routing of the given audio attributes.
+     * These are the devices anticipated to play sound from an {@link AudioTrack} created with
+     * the specified {@link AudioAttributes}.
+     * The audio routing can change if audio devices are physically connected or disconnected or
+     * concurrently through {@link AudioRouting} or {@link MediaRouter}.
+     * @param attributes the {@link AudioAttributes} for which the routing is being queried
+     * @return an empty list if there was an issue with the request, a list of
+     * {@link AudioDeviceInfo} otherwise (typically one device, except for duplicated paths).
+     */
+    public @NonNull List<AudioDeviceInfo> getAudioDevicesForAttributes(
+            @NonNull AudioAttributes attributes) {
+        final List<AudioDeviceAttributes> devicesForAttributes;
+        try {
+            Objects.requireNonNull(attributes);
+            final IAudioService service = getService();
+            devicesForAttributes = service.getDevicesForAttributesUnprotected(attributes);
+        } catch (Exception e) {
+            Log.i(TAG, "No audio devices available for specified attributes.");
+            return Collections.emptyList();
+        }
+
+        // Map from AudioDeviceAttributes to AudioDeviceInfo
+        AudioDeviceInfo[] outputDeviceInfos = getDevicesStatic(GET_DEVICES_OUTPUTS);
+        List<AudioDeviceInfo> deviceInfosForAttributes = new ArrayList<>();
+        for (AudioDeviceAttributes deviceForAttributes : devicesForAttributes) {
+            for (AudioDeviceInfo deviceInfo : outputDeviceInfos) {
+                if (deviceForAttributes.getType() == deviceInfo.getType()
+                        && TextUtils.equals(deviceForAttributes.getAddress(),
+                                deviceInfo.getAddress())) {
+                    deviceInfosForAttributes.add(deviceInfo);
+                }
+            }
+        }
+        return Collections.unmodifiableList(deviceInfosForAttributes);
+    }
+
+    /**
      * @hide
      * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
      * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
@@ -7718,6 +7742,12 @@
     }
 
     /**
+     * manages the OnCommunicationDeviceChangedListener listeners and the
+     * CommunicationDeviceDispatcherStub
+     */
+    private final CallbackUtil.LazyListenerManager<OnCommunicationDeviceChangedListener>
+            mCommDeviceChangedListenerMgr = new CallbackUtil.LazyListenerManager();
+    /**
      * Adds a listener for being notified of changes to the communication audio device.
      * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
      * @param executor
@@ -7726,16 +7756,9 @@
     public void addOnCommunicationDeviceChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnCommunicationDeviceChangedListener listener) {
-        synchronized (mCommDevListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>,
-                    CommunicationDeviceDispatcherStub> res =
-                    CallbackUtil.addListener("addOnCommunicationDeviceChangedListener",
-                            executor, listener, mCommDevListeners, mCommDevDispatcherStub,
-                            () -> new CommunicationDeviceDispatcherStub(),
-                            stub -> stub.register(true));
-            mCommDevListeners = res.first;
-            mCommDevDispatcherStub = res.second;
-        }
+        mCommDeviceChangedListenerMgr.addListener(
+                            executor, listener, "addOnCommunicationDeviceChangedListener",
+                            () -> new CommunicationDeviceDispatcherStub());
     }
 
     /**
@@ -7745,32 +7768,14 @@
      */
     public void removeOnCommunicationDeviceChangedListener(
             @NonNull OnCommunicationDeviceChangedListener listener) {
-        synchronized (mCommDevListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>,
-                    CommunicationDeviceDispatcherStub> res =
-                    CallbackUtil.removeListener("removeOnCommunicationDeviceChangedListener",
-                            listener, mCommDevListeners, mCommDevDispatcherStub,
-                            stub -> stub.register(false));
-            mCommDevListeners = res.first;
-            mCommDevDispatcherStub = res.second;
-        }
+        mCommDeviceChangedListenerMgr.removeListener(listener,
+                "removeOnCommunicationDeviceChangedListener");
     }
 
-    private final Object mCommDevListenerLock = new Object();
-    /**
-     * List of listeners for preferred device for strategy and their associated Executor.
-     * List is lazy-initialized on first registration
-     */
-    @GuardedBy("mCommDevListenerLock")
-    private @Nullable
-            ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>> mCommDevListeners;
-
-    @GuardedBy("mCommDevListenerLock")
-    private CommunicationDeviceDispatcherStub mCommDevDispatcherStub;
-
     private final class CommunicationDeviceDispatcherStub
-            extends ICommunicationDeviceDispatcher.Stub {
+            extends ICommunicationDeviceDispatcher.Stub implements CallbackUtil.DispatcherStub {
 
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -7784,10 +7789,9 @@
         }
 
         @Override
-        @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchCommunicationDeviceChanged(int portId) {
             AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
-            CallbackUtil.callListeners(mCommDevListeners, mCommDevListenerLock,
+            mCommDeviceChangedListenerMgr.callListeners(
                     (listener) -> listener.onCommunicationDeviceChanged(device));
         }
     }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index e76bb42..5283889 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1033,11 +1033,12 @@
 
         //--------------
         // audio source
-        if ( (audioSource < MediaRecorder.AudioSource.DEFAULT) ||
-             ((audioSource > MediaRecorder.getAudioSourceMax()) &&
-              (audioSource != MediaRecorder.AudioSource.RADIO_TUNER) &&
-              (audioSource != MediaRecorder.AudioSource.ECHO_REFERENCE) &&
-              (audioSource != MediaRecorder.AudioSource.HOTWORD)) )  {
+        if ((audioSource < MediaRecorder.AudioSource.DEFAULT)
+                || ((audioSource > MediaRecorder.getAudioSourceMax())
+                    && (audioSource != MediaRecorder.AudioSource.RADIO_TUNER)
+                    && (audioSource != MediaRecorder.AudioSource.ECHO_REFERENCE)
+                    && (audioSource != MediaRecorder.AudioSource.HOTWORD)
+                    && (audioSource != MediaRecorder.AudioSource.ULTRASOUND))) {
             throw new IllegalArgumentException("Invalid audio source " + audioSource);
         }
         mRecordSource = audioSource;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d2c49e0..306479a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -249,7 +249,8 @@
             AUDIO_FORMAT_SBC,
             AUDIO_FORMAT_APTX,
             AUDIO_FORMAT_APTX_HD,
-            AUDIO_FORMAT_LDAC}
+            AUDIO_FORMAT_LDAC,
+            AUDIO_FORMAT_LC3}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioFormatNativeEnumForBtCodec {}
@@ -281,6 +282,7 @@
             case AUDIO_FORMAT_APTX: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
             case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
             case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
+            case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
             default:
                 Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
                         + " for conversion to BT codec");
@@ -321,6 +323,8 @@
                 return AudioSystem.AUDIO_FORMAT_APTX_HD;
             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
                 return AudioSystem.AUDIO_FORMAT_LDAC;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+                return AudioSystem.AUDIO_FORMAT_LC3;
             default:
                 Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
                         + " for conversion to audio format");
@@ -421,6 +425,8 @@
                 return "AUDIO_FORMAT_LHDC_LL";
             case /* AUDIO_FORMAT_APTX_TWSP       */ 0x2A000000:
                 return "AUDIO_FORMAT_APTX_TWSP";
+            case /* AUDIO_FORMAT_LC3             */ 0x2B000000:
+                return "AUDIO_FORMAT_LC3";
 
             /* Aliases */
             case /* AUDIO_FORMAT_PCM_16_BIT        */ 0x1:
@@ -983,6 +989,8 @@
     public static final int DEVICE_OUT_BLE_HEADSET = 0x20000000;
     /** @hide */
     public static final int DEVICE_OUT_BLE_SPEAKER = 0x20000001;
+    /** @hide */
+    public static final int DEVICE_OUT_BLE_BROADCAST = 0x20000002;
 
     /** @hide */
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
@@ -1043,6 +1051,7 @@
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_ECHO_CANCELLER);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_HEADSET);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_SPEAKER);
+        DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_BROADCAST);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_DEFAULT);
 
         DEVICE_OUT_ALL_A2DP_SET = new HashSet<>();
@@ -1073,6 +1082,7 @@
         DEVICE_OUT_ALL_BLE_SET = new HashSet<>();
         DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_HEADSET);
         DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_SPEAKER);
+        DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_BROADCAST);
     }
 
     // input devices
@@ -1256,6 +1266,7 @@
     /** @hide */ public static final String DEVICE_OUT_ECHO_CANCELLER_NAME = "echo_canceller";
     /** @hide */ public static final String DEVICE_OUT_BLE_HEADSET_NAME = "ble_headset";
     /** @hide */ public static final String DEVICE_OUT_BLE_SPEAKER_NAME = "ble_speaker";
+    /** @hide */ public static final String DEVICE_OUT_BLE_BROADCAST_NAME = "ble_broadcast";
 
     /** @hide */ public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
     /** @hide */ public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
@@ -1355,9 +1366,11 @@
             return DEVICE_OUT_BLE_HEADSET_NAME;
         case DEVICE_OUT_BLE_SPEAKER:
             return DEVICE_OUT_BLE_SPEAKER_NAME;
+        case DEVICE_OUT_BLE_BROADCAST:
+            return DEVICE_OUT_BLE_BROADCAST_NAME;
         case DEVICE_OUT_DEFAULT:
         default:
-            return Integer.toString(device);
+            return "0x" + Integer.toHexString(device);
         }
     }
 
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index 19ea2de..d1bb41e 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -34,7 +34,7 @@
     /** @hide */
     @IntDef({
             BluetoothProfile.A2DP,
-            BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper
+            BluetoothProfile.A2DP_SINK,
             BluetoothProfile.HEADSET, // Can only be set by BtHelper
             BluetoothProfile.HEARING_AID,
             BluetoothProfile.LE_AUDIO,
@@ -105,6 +105,16 @@
     }
 
     /**
+     * Constructor for A2dp sink info
+     * The {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     *
+     * @param volume of device -1 to ignore value
+     */
+    public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
+        return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+    }
+
+    /**
      * Constructor for hearing aid info
      *
      * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
diff --git a/media/java/android/media/CallbackUtil.java b/media/java/android/media/CallbackUtil.java
index ac39317..2b5fd25 100644
--- a/media/java/android/media/CallbackUtil.java
+++ b/media/java/android/media/CallbackUtil.java
@@ -18,11 +18,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.SafeCloseable;
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -221,4 +224,87 @@
         }
 
     }
+
+    /**
+     * Interface to be implemented by stub implementation for the events received from a server
+     * to the class managing the listener API.
+     * For an example see {@link AudioManager#ModeDispatcherStub} which registers with AudioService.
+     */
+    interface DispatcherStub {
+        /**
+         * Register/unregister the stub as a listener of the events to be forwarded to the listeners
+         * managed by LazyListenerManager.
+         * @param register true for registering, false to unregister
+         */
+        void register(boolean register);
+    }
+
+    /**
+     * Class to manage a list of listeners and their callback, and the associated stub which
+     * receives the events to be forwarded to the listeners.
+     * The list of listeners and the stub and its registration are lazily initialized and registered
+     * @param <T> the listener class
+     */
+    static class LazyListenerManager<T> {
+        private final Object mListenerLock = new Object();
+
+        @GuardedBy("mListenerLock")
+        private @Nullable ArrayList<ListenerInfo<T>> mListeners;
+
+        @GuardedBy("mListenerLock")
+        private @Nullable DispatcherStub mDispatcherStub;
+
+        LazyListenerManager() {
+            // nothing to initialize as instances of dispatcher and list of listeners
+            // are lazily initialized
+        }
+
+        /**
+         * Add a new listener / executor pair for the configured listener
+         * @param executor Executor for the callback
+         * @param listener the listener to register
+         * @param methodName the name of the method calling this utility method for easier to read
+         *          exception messages
+         * @param newStub how to build a new instance of the stub receiving the events when the
+         *          number of listeners goes from 0 to 1, not called until then.
+         */
+        void addListener(@NonNull Executor executor, @NonNull T listener, String methodName,
+                @NonNull java.util.function.Supplier<DispatcherStub> newStub) {
+            synchronized (mListenerLock) {
+                final Pair<ArrayList<ListenerInfo<T>>, DispatcherStub> res =
+                        CallbackUtil.addListener(methodName,
+                                executor, listener, mListeners, mDispatcherStub,
+                                newStub,
+                                stub -> stub.register(true));
+                mListeners = res.first;
+                mDispatcherStub = res.second;
+            }
+        }
+
+        /**
+         * Remove a previously registered listener
+         * @param listener the listener to unregister
+         * @param methodName the name of the method calling this utility method for easier to read
+         *          exception messages
+         */
+        void removeListener(@NonNull T listener, String methodName) {
+            synchronized (mListenerLock) {
+                final Pair<ArrayList<ListenerInfo<T>>, DispatcherStub> res =
+                        CallbackUtil.removeListener(methodName,
+                                listener, mListeners, mDispatcherStub,
+                                stub -> stub.register(false));
+                mListeners = res.first;
+                mDispatcherStub = res.second;
+            }
+        }
+
+        /**
+         * Call the registered listeners with the given callback method
+         * @param callback the listener method to invoke
+         */
+        @SuppressLint("GuardedBy") // lock applied inside callListeners method
+        void callListeners(CallbackMethod<T> callback) {
+            CallbackUtil.callListeners(mListeners, mListenerLock, callback);
+        }
+    }
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7f6fb90..96199a9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -308,6 +308,8 @@
 
     List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
 
+    List<AudioDeviceAttributes> getDevicesForAttributesUnprotected(in AudioAttributes attributes);
+
     int setAllowedCapturePolicy(in int capturePolicy);
 
     int getAllowedCapturePolicy();
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index bd0f32e..09d7fbd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -18,10 +18,13 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.graphics.GraphicBuffer;
 import android.graphics.ImageFormat;
 import android.graphics.ImageFormat.Format;
 import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
 import android.hardware.HardwareBuffer;
 import android.hardware.HardwareBuffer.Usage;
 import android.hardware.camera2.MultiResolutionImageReader;
@@ -136,8 +139,7 @@
         // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
         // work, and is inscrutable anyway
         return new ImageReader(width, height, format, maxImages,
-                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
-                /*parent*/ null);
+                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, null);
     }
 
     /**
@@ -268,44 +270,32 @@
         // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
         // work, and is inscrutable anyway
         return new ImageReader(width, height, format, maxImages,
-                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
-                parent);
+                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, parent);
     }
 
-
-    /**
-     * @hide
-     */
-    protected ImageReader(int width, int height, int format, int maxImages, long usage,
-            MultiResolutionImageReader parent) {
-        mWidth = width;
-        mHeight = height;
-        mFormat = format;
-        mUsage = usage;
-        mMaxImages = maxImages;
-        mParent = parent;
-
+    private void initializeImageReader(int width, int height, int imageFormat, int maxImages,
+            long usage, int hardwareBufferFormat, long dataSpace, boolean useLegacyImageFormat) {
         if (width < 1 || height < 1) {
             throw new IllegalArgumentException(
                 "The image dimensions must be positive");
         }
-        if (mMaxImages < 1) {
+
+        if (maxImages < 1) {
             throw new IllegalArgumentException(
                 "Maximum outstanding image count must be at least 1");
         }
 
-        if (format == ImageFormat.NV21) {
+        if (imageFormat == ImageFormat.NV21) {
             throw new IllegalArgumentException(
-                    "NV21 format is not supported");
+                "NV21 format is not supported");
         }
 
-        mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);
-
-        nativeInit(new WeakReference<>(this), width, height, format, maxImages, usage);
-
-        mSurface = nativeGetSurface();
+        nativeInit(new WeakReference<>(this), width, height, maxImages, usage,
+                hardwareBufferFormat, dataSpace);
 
         mIsReaderValid = true;
+
+        mSurface = nativeGetSurface();
         // Estimate the native buffer allocation size and register it so it gets accounted for
         // during GC. Note that this doesn't include the buffers required by the buffer queue
         // itself and the buffers requested by the producer.
@@ -313,10 +303,46 @@
         // complex, and 1 buffer is enough for the VM to treat the ImageReader as being of some
         // size.
         mEstimatedNativeAllocBytes = ImageUtils.getEstimatedNativeAllocBytes(
-                width, height, format, /*buffer count*/ 1);
+            width, height, useLegacyImageFormat ? imageFormat : hardwareBufferFormat,
+            /*buffer count*/ 1);
         VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
     }
 
+    private ImageReader(int width, int height, int imageFormat, int maxImages, long usage,
+            MultiResolutionImageReader parent) {
+        mWidth = width;
+        mHeight = height;
+        mFormat = imageFormat;
+        mUsage = usage;
+        mMaxImages = maxImages;
+        mParent = parent;
+        // retrieve hal Format and hal dataspace from imageFormat
+        mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mFormat);
+        mDataSpace = PublicFormatUtils.getHalDataspace(mFormat);
+        mUseLegacyImageFormat = true;
+        mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);
+
+        initializeImageReader(width, height, imageFormat, maxImages, usage, mHardwareBufferFormat,
+                mDataSpace, mUseLegacyImageFormat);
+    }
+
+    private ImageReader(int width, int height, int maxImages, long usage,
+            MultiResolutionImageReader parent, int hardwareBufferFormat, long dataSpace) {
+        mWidth = width;
+        mHeight = height;
+        mFormat = ImageFormat.UNKNOWN; // set default image format value as UNKNOWN
+        mUsage = usage;
+        mMaxImages = maxImages;
+        mParent = parent;
+        mHardwareBufferFormat = hardwareBufferFormat;
+        mDataSpace = dataSpace;
+        mUseLegacyImageFormat = false;
+        mNumPlanes = ImageUtils.getNumPlanesForHardwareBufferFormat(mHardwareBufferFormat);
+
+        initializeImageReader(width, height, mFormat, maxImages, usage, hardwareBufferFormat,
+                dataSpace, mUseLegacyImageFormat);
+    }
+
     /**
      * The default width of {@link Image Images}, in pixels.
      *
@@ -354,6 +380,10 @@
      * As of now, each format is only compatible to itself.
      * The actual format of the images can be found using {@link Image#getFormat}.</p>
      *
+     * <p>Use this function if the ImageReader instance is created by factory method
+     * {@code newInstance} function or by builder pattern {@code ImageReader.Builder} and using
+     * {@link Builder#setImageFormat}.</p>
+     *
      * @return the expected format of an Image
      *
      * @see ImageFormat
@@ -363,6 +393,32 @@
     }
 
     /**
+     * The default {@link HardwareBuffer} format of {@link Image Images}.
+     *
+     * <p>Use this function if the ImageReader instance is created by builder pattern
+     * {@code ImageReader.Builder} and using {@link Builder#setDefaultHardwareBufferFormat} and
+     * {@link Builder#setDefaultDataSpace}.</p>
+     *
+     * @return the expected {@link HardwareBuffer} format of an Image.
+     */
+    public @HardwareBuffer.Format int getHardwareBufferFormat() {
+        return mHardwareBufferFormat;
+    }
+
+    /**
+     * The default dataspace of {@link Image Images}.
+     *
+     * <p>Use this function if the ImageReader instance is created by builder pattern
+     * {@code ImageReader.Builder} and {@link Builder#setDefaultDataSpace}.</p>
+     *
+     * @return the expected dataspace of an Image.
+     */
+    @SuppressLint("MethodNameUnits")
+    public @NamedDataSpace long getDataSpace() {
+        return mDataSpace;
+    }
+
+    /**
      * Maximum number of images that can be acquired from the ImageReader by any time (for example,
      * with {@link #acquireNextImage}).
      *
@@ -384,6 +440,15 @@
     }
 
     /**
+     * The usage flag of images that can be produced by the ImageReader.
+     *
+     * @return The usage flag of the images for this ImageReader.
+     */
+    public @Usage long getUsage() {
+        return mUsage;
+    }
+
+    /**
      * <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this
      * {@code ImageReader}.</p>
      *
@@ -469,7 +534,12 @@
      * @hide
      */
     public Image acquireNextImageNoThrowISE() {
-        SurfaceImage si = new SurfaceImage(mFormat);
+        SurfaceImage si;
+        if (mUseLegacyImageFormat) {
+            si = new SurfaceImage(mFormat);
+        } else {
+            si = new SurfaceImage(mHardwareBufferFormat, mDataSpace);
+        }
         return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null;
     }
 
@@ -492,7 +562,7 @@
             // A null image will eventually be returned if ImageReader is already closed.
             int status = ACQUIRE_NO_BUFS;
             if (mIsReaderValid) {
-                status = nativeImageSetup(si);
+                status = nativeImageSetup(si, mUseLegacyImageFormat);
             }
 
             switch (status) {
@@ -545,7 +615,12 @@
     public Image acquireNextImage() {
         // Initialize with reader format, but can be overwritten by native if the image
         // format is different from the reader format.
-        SurfaceImage si = new SurfaceImage(mFormat);
+        SurfaceImage si;
+        if (mUseLegacyImageFormat) {
+            si = new SurfaceImage(mFormat);
+        } else {
+            si = new SurfaceImage(mHardwareBufferFormat, mDataSpace);
+        }
         int status = acquireNextSurfaceImage(si);
 
         switch (status) {
@@ -838,13 +913,161 @@
         }
     }
 
+    /**
+     * Builder class for {@link ImageReader} objects.
+     */
+    public static final class Builder {
+        private int mWidth;
+        private int mHeight;
+        private int mMaxImages = 1;
+        private int mImageFormat = ImageFormat.UNKNOWN;
+        private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+        private long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+        private long mUsage = HardwareBuffer.USAGE_CPU_READ_OFTEN;
+        private boolean mUseLegacyImageFormat = false;
+
+        /**
+         * Constructs a new builder for {@link ImageReader}.
+         *
+         * @param width The default width in pixels that will be passed to the producer.
+         *              May be overridden by the producer.
+         * @param height The default height in pixels that will be passed to the producer.
+         *              May be overridden by the producer.
+         * @see Image
+         */
+        public Builder(@IntRange(from = 1) int width, @IntRange(from = 1) int height) {
+            mWidth = width;
+            mHeight = height;
+        }
+
+        /**
+         * Set the maximal number of images.
+         *
+         * @param maxImages The maximum number of images the user will want to
+         *            access simultaneously. This should be as small as possible to
+         *            limit memory use. Default value is 1.
+         * @return the Builder instance with customized usage value.
+         */
+        public @NonNull Builder setMaxImages(int maxImages) {
+            mMaxImages = maxImages;
+            return this;
+        }
+
+        /**
+         * Set the consumer usage flag.
+         *
+         * @param usage The intended usage of the images consumed by this ImageReader.
+         *              See the usages on {@link HardwareBuffer} for a list of valid usage bits.
+         *              Default value is {@link HardwareBuffer#USAGE_CPU_READ_OFTEN}.
+         * @return the Builder instance with customized usage value.
+         *
+         * @see HardwareBuffer
+         */
+        public @NonNull Builder setUsage(long usage) {
+            mUsage = usage;
+            return this;
+        }
+
+        /**
+         * Set the default image format passed by the producer. May be overridden by the producer.
+         *
+         * <p>{@link #setImageFormat} function replaces the combination of
+         * {@link #setDefaultHardwareBufferFormat} and {@link #setDefaultDataSpace} functions.
+         * Either this or these two functions must be called to initialize an {@code ImageReader}
+         * instance.</p>
+         *
+         * @param imageFormat The format of the image that this reader will produce. This
+         *                    must be one of the {@link android.graphics.ImageFormat} or
+         *                   {@link android.graphics.PixelFormat} constants. Note that not
+         *                   all formats are supported, like ImageFormat.NV21. The default value is
+         *                   {@link ImageFormat#UNKNOWN}.
+         * @return the builder instance with customized image format value.
+         *
+         * @see #setDefaultHardwareBufferFormat
+         * @see #setDefaultDataSpace
+         */
+        public @NonNull Builder setImageFormat(@Format int imageFormat) {
+            mImageFormat = imageFormat;
+            mUseLegacyImageFormat = true;
+            mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+            mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+            return this;
+        }
+
+        /**
+         * Set the default hardwareBuffer format passed by the producer.
+         * May be overridden by the producer.
+         *
+         * <p>This function works together with {@link #setDefaultDataSpace} for an
+         * {@link ImageReader} instance. Setting at least one of these two replaces
+         * {@link #setImageFormat} function.</p>
+         *
+         * <p>The format of the Image can be overridden after {@link #setImageFormat} by calling
+         * this function and then {@link #setDefaultDataSpace} functions.
+         * <i>Warning:</i> Missing one of callings for initializing or overriding the format may
+         * involve undefined behaviors.</p>
+         *
+         * @param hardwareBufferFormat The HardwareBuffer format of the image that this reader
+         *                             will produce. The default value is
+         *                             {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}.
+         * @return the builder instance with customized hardwareBuffer value.
+         *
+         * @see #setDefaultDataSpace
+         * @see #setImageFormat
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setDefaultHardwareBufferFormat(
+                @HardwareBuffer.Format int hardwareBufferFormat) {
+            mHardwareBufferFormat = hardwareBufferFormat;
+            mUseLegacyImageFormat = false;
+            mImageFormat = ImageFormat.UNKNOWN;
+            return this;
+        }
+
+        /**
+         * Set the default dataspace passed by the producer.
+         * May be overridden by the producer.
+         *
+         * <p>This function works together with {@link #setDefaultHardwareBufferFormat} for an
+         * {@link ImageReader} instance. Setting at least one of these two replaces
+         * {@link #setImageFormat} function.</p>
+         *
+         * @param dataSpace The dataspace of the image that this reader will produce.
+         *                  The default value is {@link DataSpace#DATASPACE_UNKNOWN}.
+         * @return the builder instance with customized dataspace value.
+         *
+         * @see #setDefaultHardwareBufferFormat
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setDefaultDataSpace(@NamedDataSpace long dataSpace) {
+            mDataSpace = dataSpace;
+            mUseLegacyImageFormat = false;
+            mImageFormat = ImageFormat.UNKNOWN;
+            return this;
+        }
+
+        /**
+         * Builds a new ImageReader object.
+         *
+         * @return The new ImageReader object.
+         */
+        public @NonNull ImageReader build() {
+            if (mUseLegacyImageFormat) {
+                return new ImageReader(mWidth, mHeight, mImageFormat, mMaxImages, mUsage, null);
+            } else {
+                return new ImageReader(mWidth, mHeight, mMaxImages, mUsage, null,
+                    mHardwareBufferFormat, mDataSpace);
+            }
+        }
+    }
+
     private final int mWidth;
     private final int mHeight;
     private final int mFormat;
     private final long mUsage;
     private final int mMaxImages;
     private final int mNumPlanes;
-    private final Surface mSurface;
+    private Surface mSurface;
     private int mEstimatedNativeAllocBytes;
 
     private final Object mListenerLock = new Object();
@@ -861,6 +1084,12 @@
     // MultiResolutionImageReader.
     private final MultiResolutionImageReader mParent;
 
+    private final int mHardwareBufferFormat;
+
+    private final long mDataSpace;
+
+    private final boolean mUseLegacyImageFormat;
+
     /**
      * This field is used by native code, do not access or modify.
      */
@@ -895,6 +1124,14 @@
     private class SurfaceImage extends android.media.Image {
         public SurfaceImage(int format) {
             mFormat = format;
+            mHardwareBufferFormat = ImageReader.this.mHardwareBufferFormat;
+            mDataSpace = ImageReader.this.mDataSpace;
+        }
+
+        SurfaceImage(int hardwareBufferFormat, long dataSpace) {
+            mHardwareBufferFormat = hardwareBufferFormat;
+            mDataSpace = dataSpace;
+            mFormat = PublicFormatUtils.getPublicFormat(mHardwareBufferFormat, mDataSpace);
         }
 
         @Override
@@ -909,10 +1146,15 @@
         @Override
         public int getFormat() {
             throwISEIfImageIsInvalid();
-            int readerFormat = ImageReader.this.getImageFormat();
-            // Assume opaque reader always produce opaque images.
-            mFormat = (readerFormat == ImageFormat.PRIVATE) ? readerFormat :
-                nativeGetFormat(readerFormat);
+            // update mFormat only if ImageReader is initialized by factory pattern.
+            // if using builder pattern, mFormat has been updated upon initialization.
+            // no need update here.
+            if (ImageReader.this.mUseLegacyImageFormat) {
+                int readerFormat = ImageReader.this.getImageFormat();
+                // Assume opaque reader always produce opaque images.
+                mFormat = (readerFormat == ImageFormat.PRIVATE) ? readerFormat :
+                    nativeGetFormat(readerFormat);
+            }
             return mFormat;
         }
 
@@ -989,6 +1231,12 @@
         }
 
         @Override
+        public long getDataSpace() {
+            throwISEIfImageIsInvalid();
+            return mDataSpace;
+        }
+
+        @Override
         public void setTimestamp(long timestampNs) {
             throwISEIfImageIsInvalid();
             mTimestamp = timestampNs;
@@ -1125,6 +1373,8 @@
 
         private SurfacePlane[] mPlanes;
         private int mFormat = ImageFormat.UNKNOWN;
+        private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+        private long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
         // If this image is detached from the ImageReader.
         private AtomicBoolean mIsDetached = new AtomicBoolean(false);
 
@@ -1137,8 +1387,8 @@
         private synchronized native HardwareBuffer nativeGetHardwareBuffer();
     }
 
-    private synchronized native void nativeInit(Object weakSelf, int w, int h,
-                                                    int fmt, int maxImgs, long consumerUsage);
+    private synchronized native void nativeInit(Object weakSelf, int w, int h, int maxImgs,
+            long consumerUsage, int hardwareBufferFormat, long dataSpace);
     private synchronized native void nativeClose();
     private synchronized native void nativeReleaseImage(Image i);
     private synchronized native Surface nativeGetSurface();
@@ -1152,7 +1402,7 @@
      * @see #ACQUIRE_NO_BUFS
      * @see #ACQUIRE_MAX_IMAGES
      */
-    private synchronized native int nativeImageSetup(Image i);
+    private synchronized native int nativeImageSetup(Image i, boolean legacyValidateImageFormat);
 
     /**
      * @hide
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 7837d7e..2f1a36c 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -18,6 +18,7 @@
 
 import android.graphics.ImageFormat;
 import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
 import android.media.Image.Plane;
 import android.util.Size;
 
@@ -77,6 +78,34 @@
     }
 
     /**
+     * Only a subset of the formats defined in
+     * {@link android.graphics.HardwareBuffer.Format} constants are supported by ImageReader.
+     */
+    public static int getNumPlanesForHardwareBufferFormat(int hardwareBufferFormat) {
+        switch(hardwareBufferFormat) {
+            case HardwareBuffer.YCBCR_420_888:
+                return 3;
+            case HardwareBuffer.RGBA_8888:
+            case HardwareBuffer.RGBX_8888:
+            case HardwareBuffer.RGB_888:
+            case HardwareBuffer.RGB_565:
+            case HardwareBuffer.RGBA_FP16:
+            case HardwareBuffer.RGBA_1010102:
+            case HardwareBuffer.BLOB:
+            case HardwareBuffer.D_16:
+            case HardwareBuffer.D_24:
+            case HardwareBuffer.DS_24UI8:
+            case HardwareBuffer.D_FP32:
+            case HardwareBuffer.DS_FP32UI8:
+            case HardwareBuffer.S_UI8:
+                return 1;
+            default:
+                throw new UnsupportedOperationException(
+                    String.format("Invalid hardwareBuffer format specified %d",
+                            hardwareBufferFormat));
+        }
+    }
+    /**
      * <p>
      * Copy source image data to destination Image.
      * </p>
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 1fc2cf9..6168c22 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -18,12 +18,16 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.graphics.GraphicBuffer;
 import android.graphics.ImageFormat;
 import android.graphics.ImageFormat.Format;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
 import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SurfaceUtils;
 import android.os.Handler;
@@ -95,10 +99,18 @@
     private ListenerHandler mListenerHandler;
     private long mNativeContext;
 
+    private int mWidth;
+    private int mHeight;
+    private final int mMaxImages;
+    private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
+    private @HardwareBuffer.Format int mHardwareBufferFormat;
+    private @NamedDataSpace long mDataSpace;
+    private boolean mUseLegacyImageFormat;
+    private boolean mUseSurfaceImageFormatInfo;
+
     // Field below is used by native code, do not access or modify.
     private int mWriterFormat;
 
-    private final int mMaxImages;
     // Keep track of the currently dequeued Image. This need to be thread safe as the images
     // could be closed by different threads (e.g., application thread and GC thread).
     private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>();
@@ -131,7 +143,7 @@
      */
     public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
             @IntRange(from = 1) int maxImages) {
-        return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/,
+        return new ImageWriter(surface, maxImages, true, ImageFormat.UNKNOWN, -1 /*width*/,
                 -1 /*height*/);
     }
 
@@ -183,7 +195,7 @@
         if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
             throw new IllegalArgumentException("Invalid format is specified: " + format);
         }
-        return new ImageWriter(surface, maxImages, format, width, height);
+        return new ImageWriter(surface, maxImages, false, format, width, height);
     }
 
     /**
@@ -232,48 +244,49 @@
         if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
             throw new IllegalArgumentException("Invalid format is specified: " + format);
         }
-        return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/);
+        return new ImageWriter(surface, maxImages, false, format, -1 /*width*/, -1 /*height*/);
     }
 
-    /**
-     * @hide
-     */
-    protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) {
+    private void initializeImageWriter(Surface surface, int maxImages,
+            boolean useSurfaceImageFormatInfo, boolean useLegacyImageFormat, int imageFormat,
+            int hardwareBufferFormat, long dataSpace, int width, int height, long usage) {
         if (surface == null || maxImages < 1) {
             throw new IllegalArgumentException("Illegal input argument: surface " + surface
-                    + ", maxImages: " + maxImages);
+                + ", maxImages: " + maxImages);
         }
 
-        mMaxImages = maxImages;
-
+        mUseSurfaceImageFormatInfo = useSurfaceImageFormatInfo;
+        mUseLegacyImageFormat = useLegacyImageFormat;
         // Note that the underlying BufferQueue is working in synchronous mode
         // to avoid dropping any buffers.
-        mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width,
-                height);
+        mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height,
+            useSurfaceImageFormatInfo, hardwareBufferFormat, dataSpace, usage);
 
-        // nativeInit internally overrides UNKNOWN format. So does surface format query after
-        // nativeInit and before getEstimatedNativeAllocBytes().
-        if (format == ImageFormat.UNKNOWN) {
-            format = SurfaceUtils.getSurfaceFormat(surface);
-        }
-        // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
-        // allocation estimation sequence depends on the public formats values. To avoid
-        // possible errors, convert where necessary.
-        if (format == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
-            int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
-            switch (surfaceDataspace) {
-                case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
-                    format = ImageFormat.DEPTH_POINT_CLOUD;
-                    break;
-                case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
-                    format = ImageFormat.DEPTH_JPEG;
-                    break;
-                case StreamConfigurationMap.HAL_DATASPACE_HEIF:
-                    format = ImageFormat.HEIC;
-                    break;
-                default:
-                    format = ImageFormat.JPEG;
+        if (useSurfaceImageFormatInfo) {
+            // nativeInit internally overrides UNKNOWN format. So does surface format query after
+            // nativeInit and before getEstimatedNativeAllocBytes().
+            imageFormat = SurfaceUtils.getSurfaceFormat(surface);
+            // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
+            // allocation estimation sequence depends on the public formats values. To avoid
+            // possible errors, convert where necessary.
+            if (imageFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
+                int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
+                switch (surfaceDataspace) {
+                    case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
+                        imageFormat = ImageFormat.DEPTH_POINT_CLOUD;
+                        break;
+                    case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
+                        imageFormat = ImageFormat.DEPTH_JPEG;
+                        break;
+                    case StreamConfigurationMap.HAL_DATASPACE_HEIF:
+                        imageFormat = ImageFormat.HEIC;
+                        break;
+                    default:
+                        imageFormat = ImageFormat.JPEG;
+                }
             }
+            mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+            mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
         }
         // Estimate the native buffer allocation size and register it so it gets accounted for
         // during GC. Note that this doesn't include the buffers required by the buffer queue
@@ -282,12 +295,49 @@
         // complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some
         // size.
         Size surfSize = SurfaceUtils.getSurfaceSize(surface);
+        mWidth = width == -1 ? surfSize.getWidth() : width;
+        mHeight = height == -1 ? surfSize.getHeight() : height;
+
         mEstimatedNativeAllocBytes =
-                ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(),
-                        format, /*buffer count*/ 1);
+            ImageUtils.getEstimatedNativeAllocBytes(mWidth, mHeight,
+                useLegacyImageFormat ? imageFormat : hardwareBufferFormat, /*buffer count*/ 1);
         VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
     }
 
+    private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+            int imageFormat, int width, int height) {
+        mMaxImages = maxImages;
+        // update hal format and dataspace only if image format is overridden by producer.
+        mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+        mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+
+        initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true,
+                imageFormat, mHardwareBufferFormat, mDataSpace, width, height, mUsage);
+    }
+
+    private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+            int imageFormat, int width, int height, long usage) {
+        mMaxImages = maxImages;
+        mUsage = usage;
+        mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+        mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+
+        initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true,
+                imageFormat, mHardwareBufferFormat, mDataSpace, width, height, usage);
+    }
+
+    private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+            int hardwareBufferFormat, long dataSpace, int width, int height, long usage) {
+        mMaxImages = maxImages;
+        mUsage = usage;
+        mHardwareBufferFormat = hardwareBufferFormat;
+        mDataSpace = dataSpace;
+        int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+
+        initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false,
+                publicFormat, hardwareBufferFormat, dataSpace, width, height, usage);
+    }
+
     /**
      * <p>
      * Maximum number of Images that can be dequeued from the ImageWriter
@@ -316,6 +366,30 @@
     }
 
     /**
+     * The width of {@link Image Images}, in pixels.
+     *
+     * <p>If {@link Builder#setWidthAndHeight} is not called, the default width of the Image
+     * depends on the Surface provided by customer end-point.</p>
+     *
+     * @return the expected actual width of an Image.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * The height of {@link Image Images}, in pixels.
+     *
+     * <p>If {@link Builder#setWidthAndHeight} is not called, the default height of the Image
+     * depends on the Surface provided by customer end-point.</p>
+     *
+     * @return the expected height of an Image.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
      * <p>
      * Dequeue the next available input Image for the application to produce
      * data into.
@@ -490,6 +564,41 @@
     }
 
     /**
+     * Get the ImageWriter usage flag.
+     *
+     * @return The ImageWriter usage flag.
+     */
+    public @Usage long getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Get the ImageWriter hardwareBuffer format.
+     *
+     * <p>Use this function if the ImageWriter instance is created by builder pattern
+     * {@code ImageWriter.Builder} and using {@link Builder#setHardwareBufferFormat} and
+     * {@link Builder#setDataSpace}.</p>
+     *
+     * @return The ImageWriter hardwareBuffer format.
+     */
+    public @HardwareBuffer.Format int getHardwareBufferFormat() {
+        return mHardwareBufferFormat;
+    }
+
+    /**
+     * Get the ImageWriter dataspace.
+     *
+     * <p>Use this function if the ImageWriter instance is created by builder pattern
+     * {@code ImageWriter.Builder} and {@link Builder#setDataSpace}.</p>
+     *
+     * @return The ImageWriter dataspace.
+     */
+    @SuppressLint("MethodNameUnits")
+    public @NamedDataSpace long getDataSpace() {
+        return mDataSpace;
+    }
+
+    /**
      * ImageWriter callback interface, used to to asynchronously notify the
      * application of various ImageWriter events.
      */
@@ -755,6 +864,155 @@
         return true;
     }
 
+    /**
+     * Builder class for {@link ImageWriter} objects.
+     */
+    public static final class Builder {
+        private Surface mSurface;
+        private int mWidth = -1;
+        private int mHeight = -1;
+        private int mMaxImages = 1;
+        private int mImageFormat = ImageFormat.UNKNOWN;
+        private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
+        private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+        private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+        private boolean mUseSurfaceImageFormatInfo = true;
+        // set this as true temporarily now as a workaround to get correct format
+        // when using surface format by default without overriding the image format
+        // in the builder pattern
+        private boolean mUseLegacyImageFormat = true;
+
+        /**
+         * Constructs a new builder for {@link ImageWriter}.
+         *
+         * @param surface The destination Surface this writer produces Image data into.
+         */
+        public Builder(@NonNull Surface surface) {
+            mSurface = surface;
+        }
+
+        /**
+         * Set the width and height of images. Default size is dependent on the Surface that is
+         * provided by the downstream end-point.
+         *
+         * @param width The width in pixels that will be passed to the producer.
+         * @param height The height in pixels that will be passed to the producer.
+         * @return the Builder instance with customized width and height.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setWidthAndHeight(@IntRange(from = 1) int width,
+                @IntRange(from = 1) int height) {
+            mWidth = width;
+            mHeight = height;
+            return this;
+        }
+
+        /**
+         * Set the maximum number of images. Default value is 1.
+         *
+         * @param maxImages The maximum number of Images the user will want to access simultaneously
+         *                  for producing Image data.
+         * @return the Builder instance with customized usage value.
+         */
+        public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) {
+            mMaxImages = maxImages;
+            return this;
+        }
+
+        /**
+         * Set the image format of this ImageWriter.
+         * Default format depends on the Surface provided.
+         *
+         * @param imageFormat The format of the {@link ImageWriter}. It can be any valid specified
+         *                    by {@link ImageFormat} or {@link PixelFormat}.
+         * @return the Builder instance with customized image format.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setImageFormat(@Format int imageFormat) {
+            if (!ImageFormat.isPublicFormat(imageFormat)
+                    && !PixelFormat.isPublicFormat(imageFormat)) {
+                throw new IllegalArgumentException(
+                        "Invalid imageFormat is specified: " + imageFormat);
+            }
+            mImageFormat = imageFormat;
+            mUseLegacyImageFormat = true;
+            mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+            mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+            mUseSurfaceImageFormatInfo = false;
+            return this;
+        }
+
+        /**
+         * Set the hardwareBuffer format of this ImageWriter. The default value is
+         * {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}.
+         *
+         * <p>This function works together with {@link #setDataSpace} for an
+         * {@link ImageWriter} instance. Setting at least one of these two replaces
+         * {@link #setImageFormat} function.</p>
+         *
+         * @param hardwareBufferFormat The HardwareBuffer format of the image that this writer
+         *                             will produce.
+         * @return the Builder instance with customized buffer format.
+         *
+         * @see #setDataSpace
+         * @see #setImageFormat
+         */
+        public @NonNull Builder setHardwareBufferFormat(
+                @HardwareBuffer.Format int hardwareBufferFormat) {
+            mHardwareBufferFormat = hardwareBufferFormat;
+            mImageFormat = ImageFormat.UNKNOWN;
+            mUseLegacyImageFormat = false;
+            mUseSurfaceImageFormatInfo = false;
+            return this;
+        }
+
+        /**
+         * Set the dataspace of this ImageWriter.
+         * The default value is {@link DataSpace#DATASPACE_UNKNOWN}.
+         *
+         * @param dataSpace The dataspace of the image that this writer will produce.
+         * @return the builder instance with customized dataspace value.
+         *
+         * @see #setHardwareBufferFormat
+         */
+        public @NonNull Builder setDataSpace(@NamedDataSpace long dataSpace) {
+            mDataSpace = dataSpace;
+            mImageFormat = ImageFormat.UNKNOWN;
+            mUseLegacyImageFormat = false;
+            mUseSurfaceImageFormatInfo = false;
+            return this;
+        }
+
+        /**
+         * Set the usage flag of this ImageWriter.
+         * Default value is {@link HardwareBuffer#USAGE_CPU_WRITE_OFTEN}.
+         *
+         * @param usage The intended usage of the images produced by this ImageWriter.
+         * @return the Builder instance with customized usage flag.
+         *
+         * @see HardwareBuffer
+         */
+        public @NonNull Builder setUsage(@Usage long usage) {
+            mUsage = usage;
+            return this;
+        }
+
+        /**
+         * Builds a new ImageWriter object.
+         *
+         * @return The new ImageWriter object.
+         */
+        public @NonNull ImageWriter build() {
+            if (mUseLegacyImageFormat) {
+                return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo,
+                        mImageFormat, mWidth, mHeight, mUsage);
+            } else {
+                return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo,
+                        mHardwareBufferFormat, mDataSpace, mWidth, mHeight, mUsage);
+            }
+        }
+    }
+
     private static class WriterSurfaceImage extends android.media.Image {
         private ImageWriter mOwner;
         // This field is used by native code, do not access or modify.
@@ -774,6 +1032,13 @@
 
         public WriterSurfaceImage(ImageWriter writer) {
             mOwner = writer;
+            mWidth = writer.mWidth;
+            mHeight = writer.mHeight;
+
+            if (!writer.mUseLegacyImageFormat) {
+                mFormat = PublicFormatUtils.getPublicFormat(
+                        writer.mHardwareBufferFormat, writer.mDataSpace);
+            }
         }
 
         @Override
@@ -969,8 +1234,9 @@
     }
 
     // Native implemented ImageWriter methods.
-    private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs,
-            int format, int width, int height);
+    private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImages,
+            int width, int height, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat,
+            long dataSpace, long usage);
 
     private synchronized native void nativeClose(long nativeCtx);
 
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 3c152fb..e75df1d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -603,6 +603,18 @@
         public static final String FEATURE_QpBounds = "qp-bounds";
 
         /**
+         * <b>video encoder only</b>: codec supports exporting encoding statistics.
+         * Encoders with this feature can provide the App clients with the encoding statistics
+         * information about the frame.
+         * The scope of encoding statistics is controlled by
+         * {@link MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL}.
+         *
+         * @see MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL
+         */
+        @SuppressLint("AllUpper") // for consistency with other FEATURE_* constants
+        public static final String FEATURE_EncodingStatistics = "encoding-statistics";
+
+        /**
          * Query codec feature capabilities.
          * <p>
          * These features are supported to be used by the codec.  These
@@ -641,6 +653,7 @@
             new Feature(FEATURE_MultipleFrames, (1 << 1), false),
             new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
             new Feature(FEATURE_QpBounds, (1 << 3), false),
+            new Feature(FEATURE_EncodingStatistics, (1 << 4), false),
             // feature to exclude codec from REGULAR codec list
             new Feature(FEATURE_SpecialCodec,     (1 << 30), false, true),
         };
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4891d74..4956dbe 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1176,6 +1176,76 @@
     public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
 
     /**
+     * A key describing the level of encoding statistics information emitted from video encoder.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL =
+            "video-encoding-statistics-level";
+
+    /**
+     * Encoding Statistics Level None.
+     * Encoder generates no information about Encoding statistics.
+     */
+    public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0;
+
+    /**
+     * Encoding Statistics Level 1.
+     * Encoder generates {@link MediaFormat#KEY_PICTURE_TYPE} and
+     * {@link MediaFormat#KEY_VIDEO_QP_AVERAGE} for each frame.
+     */
+    public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1;
+
+    /** @hide */
+    @IntDef({
+        VIDEO_ENCODING_STATISTICS_LEVEL_NONE,
+        VIDEO_ENCODING_STATISTICS_LEVEL_1,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VideoEncodingStatisticsLevel {}
+
+    /**
+     * A key describing the per-frame average block QP (Quantization Parameter).
+     * This is a part of a video 'Encoding Statistics' export feature.
+     * This value is emitted from video encoder for a video frame.
+     * The average value is rounded down (using floor()) to integer value.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average";
+
+    /**
+     * A key describing the picture type of the encoded frame.
+     * This is a part of a video 'Encoding Statistics' export feature.
+     * This value is emitted from video encoder for a video frame.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_PICTURE_TYPE = "picture-type";
+
+    /** Picture Type is unknown. */
+    public static final int PICTURE_TYPE_UNKNOWN = 0;
+
+    /** Picture Type is I Frame. */
+    public static final int PICTURE_TYPE_I = 1;
+
+    /** Picture Type is P Frame. */
+    public static final int PICTURE_TYPE_P = 2;
+
+    /** Picture Type is B Frame. */
+    public static final int PICTURE_TYPE_B = 3;
+
+    /** @hide */
+    @IntDef({
+        PICTURE_TYPE_UNKNOWN,
+        PICTURE_TYPE_I,
+        PICTURE_TYPE_P,
+        PICTURE_TYPE_B,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PictureType {}
+
+    /**
      * A key describing the audio session ID of the AudioTrack associated
      * to a tunneled video codec.
      * The associated value is an integer.
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 60d21c0..ad8fc07 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -1077,7 +1077,9 @@
      * Releases any acquired resources. Call it when done with the object.
      *
      * @throws IOException When an {@link IOException} is thrown while closing a {@link
-     * MediaDataSource} passed to {@link #setDataSource(MediaDataSource)}.
+     * MediaDataSource} passed to {@link #setDataSource(MediaDataSource)}. This throws clause exists
+     * since API 33, but this method can throw in earlier API versions where the exception is not
+     * declared.
      */
     @Override
     public void close() throws IOException {
@@ -1088,7 +1090,9 @@
      * Releases any acquired resources. Call it when done with the object.
      *
      * @throws IOException When an {@link IOException} is thrown while closing a {@link
-     * MediaDataSource} passed to {@link #setDataSource(MediaDataSource)}.
+     * MediaDataSource} passed to {@link #setDataSource(MediaDataSource)}. This throws clause exists
+     * since API 33, but this method can throw in earlier API versions where the exception is not
+     * declared.
      */
     public native void release() throws IOException;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 77c1e55b..d7857a0 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -365,6 +365,7 @@
          */
         public static final int VOICE_PERFORMANCE = 10;
 
+
         /**
          * Source for an echo canceller to capture the reference signal to be cancelled.
          * <p>
@@ -408,6 +409,15 @@
         @SystemApi
         @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
         public static final int HOTWORD = 1999;
+
+        /** Microphone audio source for ultrasound sound if available, behaves like
+         *  {@link #DEFAULT} otherwise.
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+        public static final int ULTRASOUND = 2000;
+
     }
 
     /** @hide */
@@ -442,6 +452,7 @@
         AudioSource.ECHO_REFERENCE,
         AudioSource.RADIO_TUNER,
         AudioSource.HOTWORD,
+        AudioSource.ULTRASOUND,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SystemSource {}
@@ -454,20 +465,20 @@
      */
     public static boolean isSystemOnlyAudioSource(int source) {
         switch(source) {
-        case AudioSource.DEFAULT:
-        case AudioSource.MIC:
-        case AudioSource.VOICE_UPLINK:
-        case AudioSource.VOICE_DOWNLINK:
-        case AudioSource.VOICE_CALL:
-        case AudioSource.CAMCORDER:
-        case AudioSource.VOICE_RECOGNITION:
-        case AudioSource.VOICE_COMMUNICATION:
-        //case REMOTE_SUBMIX:  considered "system" as it requires system permissions
-        case AudioSource.UNPROCESSED:
-        case AudioSource.VOICE_PERFORMANCE:
-            return false;
-        default:
-            return true;
+            case AudioSource.DEFAULT:
+            case AudioSource.MIC:
+            case AudioSource.VOICE_UPLINK:
+            case AudioSource.VOICE_DOWNLINK:
+            case AudioSource.VOICE_CALL:
+            case AudioSource.CAMCORDER:
+            case AudioSource.VOICE_RECOGNITION:
+            case AudioSource.VOICE_COMMUNICATION:
+            //case REMOTE_SUBMIX:  considered "system" as it requires system permissions
+            case AudioSource.UNPROCESSED:
+            case AudioSource.VOICE_PERFORMANCE:
+                return false;
+            default:
+                return true;
         }
     }
 
@@ -491,6 +502,7 @@
             case AudioSource.ECHO_REFERENCE:
             case AudioSource.RADIO_TUNER:
             case AudioSource.HOTWORD:
+            case AudioSource.ULTRASOUND:
                 return true;
             default:
                 return false;
@@ -500,38 +512,40 @@
     /** @hide */
     public static final String toLogFriendlyAudioSource(int source) {
         switch(source) {
-        case AudioSource.DEFAULT:
-            return "DEFAULT";
-        case AudioSource.MIC:
-            return "MIC";
-        case AudioSource.VOICE_UPLINK:
-            return "VOICE_UPLINK";
-        case AudioSource.VOICE_DOWNLINK:
-            return "VOICE_DOWNLINK";
-        case AudioSource.VOICE_CALL:
-            return "VOICE_CALL";
-        case AudioSource.CAMCORDER:
-            return "CAMCORDER";
-        case AudioSource.VOICE_RECOGNITION:
-            return "VOICE_RECOGNITION";
-        case AudioSource.VOICE_COMMUNICATION:
-            return "VOICE_COMMUNICATION";
-        case AudioSource.REMOTE_SUBMIX:
-            return "REMOTE_SUBMIX";
-        case AudioSource.UNPROCESSED:
-            return "UNPROCESSED";
-        case AudioSource.ECHO_REFERENCE:
-            return "ECHO_REFERENCE";
-        case AudioSource.VOICE_PERFORMANCE:
-            return "VOICE_PERFORMANCE";
-        case AudioSource.RADIO_TUNER:
-            return "RADIO_TUNER";
-        case AudioSource.HOTWORD:
-            return "HOTWORD";
-        case AudioSource.AUDIO_SOURCE_INVALID:
-            return "AUDIO_SOURCE_INVALID";
-        default:
-            return "unknown source " + source;
+            case AudioSource.DEFAULT:
+                return "DEFAULT";
+            case AudioSource.MIC:
+                return "MIC";
+            case AudioSource.VOICE_UPLINK:
+                return "VOICE_UPLINK";
+            case AudioSource.VOICE_DOWNLINK:
+                return "VOICE_DOWNLINK";
+            case AudioSource.VOICE_CALL:
+                return "VOICE_CALL";
+            case AudioSource.CAMCORDER:
+                return "CAMCORDER";
+            case AudioSource.VOICE_RECOGNITION:
+                return "VOICE_RECOGNITION";
+            case AudioSource.VOICE_COMMUNICATION:
+                return "VOICE_COMMUNICATION";
+            case AudioSource.REMOTE_SUBMIX:
+                return "REMOTE_SUBMIX";
+            case AudioSource.UNPROCESSED:
+                return "UNPROCESSED";
+            case AudioSource.ECHO_REFERENCE:
+                return "ECHO_REFERENCE";
+            case AudioSource.VOICE_PERFORMANCE:
+                return "VOICE_PERFORMANCE";
+            case AudioSource.RADIO_TUNER:
+                return "RADIO_TUNER";
+            case AudioSource.HOTWORD:
+                return "HOTWORD";
+            case AudioSource.ULTRASOUND:
+                return "ULTRASOUND";
+            case AudioSource.AUDIO_SOURCE_INVALID:
+                return "AUDIO_SOURCE_INVALID";
+            default:
+                return "unknown source " + source;
         }
     }
 
diff --git a/media/java/android/media/PublicFormatUtils.java b/media/java/android/media/PublicFormatUtils.java
new file mode 100644
index 0000000..6268804
--- /dev/null
+++ b/media/java/android/media/PublicFormatUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * Package private utility class for PublicFormat related methods.
+ */
+class PublicFormatUtils {
+    public static int getHalFormat(int imageFormat) {
+        return nativeGetHalFormat(imageFormat);
+    }
+    public static long getHalDataspace(int imageFormat) {
+        return nativeGetHalDataspace(imageFormat);
+    }
+    public static int getPublicFormat(int imageFormat, long dataspace) {
+        return nativeGetPublicFormat(imageFormat, dataspace);
+    }
+    private static native int nativeGetHalFormat(int imageFormat);
+    private static native long nativeGetHalDataspace(int imageFormat);
+    private static native int nativeGetPublicFormat(int imageFormat, long dataspace);
+}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 3cf0341..86a94a9 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -504,49 +504,51 @@
     }
 
     private boolean playFallbackRingtone() {
-        if (mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(mAudioAttributes))
-                != 0) {
-            int ringtoneType = RingtoneManager.getDefaultType(mUri);
-            if (ringtoneType == -1 ||
-                    RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) != null) {
-                // Default ringtone, try fallback ringtone.
-                try {
-                    AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
-                            com.android.internal.R.raw.fallbackring);
-                    if (afd != null) {
-                        mLocalPlayer = new MediaPlayer();
-                        if (afd.getDeclaredLength() < 0) {
-                            mLocalPlayer.setDataSource(afd.getFileDescriptor());
-                        } else {
-                            mLocalPlayer.setDataSource(afd.getFileDescriptor(),
-                                    afd.getStartOffset(),
-                                    afd.getDeclaredLength());
-                        }
-                        mLocalPlayer.setAudioAttributes(mAudioAttributes);
-                        synchronized (mPlaybackSettingsLock) {
-                            applyPlaybackProperties_sync();
-                        }
-                        if (mVolumeShaperConfig != null) {
-                            mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
-                        }
-                        mLocalPlayer.prepare();
-                        startLocalPlayer();
-                        afd.close();
-                        return true;
-                    } else {
-                        Log.e(TAG, "Could not load fallback ringtone");
-                    }
-                } catch (IOException ioe) {
-                    destroyLocalPlayer();
-                    Log.e(TAG, "Failed to open fallback ringtone");
-                } catch (NotFoundException nfe) {
-                    Log.e(TAG, "Fallback ringtone does not exist");
-                }
-            } else {
-                Log.w(TAG, "not playing fallback for " + mUri);
-            }
+        int streamType = AudioAttributes.toLegacyStreamType(mAudioAttributes);
+        if (mAudioManager.getStreamVolume(streamType) == 0) {
+            return false;
         }
-        return false;
+        int ringtoneType = RingtoneManager.getDefaultType(mUri);
+        if (ringtoneType != -1 &&
+                RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) == null) {
+            Log.w(TAG, "not playing fallback for " + mUri);
+            return false;
+        }
+        // Default ringtone, try fallback ringtone.
+        try {
+            AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
+                    com.android.internal.R.raw.fallbackring);
+            if (afd == null) {
+                Log.e(TAG, "Could not load fallback ringtone");
+                return false;
+            }
+            mLocalPlayer = new MediaPlayer();
+            if (afd.getDeclaredLength() < 0) {
+                mLocalPlayer.setDataSource(afd.getFileDescriptor());
+            } else {
+                mLocalPlayer.setDataSource(afd.getFileDescriptor(),
+                        afd.getStartOffset(),
+                        afd.getDeclaredLength());
+            }
+            mLocalPlayer.setAudioAttributes(mAudioAttributes);
+            synchronized (mPlaybackSettingsLock) {
+                applyPlaybackProperties_sync();
+            }
+            if (mVolumeShaperConfig != null) {
+                mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+            }
+            mLocalPlayer.prepare();
+            startLocalPlayer();
+            afd.close();
+        } catch (IOException ioe) {
+            destroyLocalPlayer();
+            Log.e(TAG, "Failed to open fallback ringtone");
+            return false;
+        } catch (NotFoundException nfe) {
+            Log.e(TAG, "Fallback ringtone does not exist");
+            return false;
+        }
+        return true;
     }
 
     void setTitle(String title) {
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index c0793ec..030d212 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -29,7 +29,6 @@
 import android.media.permission.SafeCloseable;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -216,6 +215,29 @@
     public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2;
 
     /**
+     * @hide
+     * Head tracking mode to string conversion
+     * @param mode a valid head tracking mode
+     * @return a string containing the matching constant name
+     */
+    public static final String headtrackingModeToString(int mode) {
+        switch(mode) {
+            case HEAD_TRACKING_MODE_UNSUPPORTED:
+                return "HEAD_TRACKING_MODE_UNSUPPORTED";
+            case HEAD_TRACKING_MODE_DISABLED:
+                return "HEAD_TRACKING_MODE_DISABLED";
+            case HEAD_TRACKING_MODE_OTHER:
+                return "HEAD_TRACKING_MODE_OTHER";
+            case HEAD_TRACKING_MODE_RELATIVE_WORLD:
+                return "HEAD_TRACKING_MODE_RELATIVE_WORLD";
+            case HEAD_TRACKING_MODE_RELATIVE_DEVICE:
+                return "HEAD_TRACKING_MODE_RELATIVE_DEVICE";
+            default:
+                return "head tracking mode unknown " + mode;
+        }
+    }
+
+    /**
      * Return the level of support for the spatialization feature on this device.
      * This level of support is independent of whether the {@code Spatializer} is currently
      * enabled or available and will not change over time.
@@ -376,16 +398,8 @@
     public void addOnSpatializerStateChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnSpatializerStateChangedListener listener) {
-        synchronized (mStateListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnSpatializerStateChangedListener>>,
-                    SpatializerInfoDispatcherStub> res =
-                    CallbackUtil.addListener("addOnSpatializerStateChangedListener",
-                            executor, listener, mStateListeners, mInfoDispatcherStub,
-                            () -> new SpatializerInfoDispatcherStub(),
-                            stub -> stub.register(true));
-            mStateListeners = res.first;
-            mInfoDispatcherStub = res.second;
-        }
+        mStateListenerMgr.addListener(executor, listener, "addOnSpatializerStateChangedListener",
+                () -> new SpatializerInfoDispatcherStub());
     }
 
     /**
@@ -396,15 +410,7 @@
      */
     public void removeOnSpatializerStateChangedListener(
             @NonNull OnSpatializerStateChangedListener listener) {
-        synchronized (mStateListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnSpatializerStateChangedListener>>,
-                    SpatializerInfoDispatcherStub> res =
-                    CallbackUtil.removeListener("removeOnSpatializerStateChangedListener",
-                            listener, mStateListeners, mInfoDispatcherStub,
-                            stub -> stub.register(false));
-            mStateListeners = res.first;
-            mInfoDispatcherStub = res.second;
-        }
+        mStateListenerMgr.removeListener(listener, "removeOnSpatializerStateChangedListener");
     }
 
     /**
@@ -459,18 +465,16 @@
         }
     }
 
-    private final Object mStateListenerLock = new Object();
     /**
-     * List of listeners for state listener and their associated Executor.
-     * List is lazy-initialized on first registration
+     * manages the OnSpatializerStateChangedListener listeners and the
+     * SpatializerInfoDispatcherStub
      */
-    @GuardedBy("mStateListenerLock")
-    private @Nullable ArrayList<ListenerInfo<OnSpatializerStateChangedListener>> mStateListeners;
+    private final CallbackUtil.LazyListenerManager<OnSpatializerStateChangedListener>
+            mStateListenerMgr = new CallbackUtil.LazyListenerManager();
 
-    @GuardedBy("mStateListenerLock")
-    private @Nullable SpatializerInfoDispatcherStub mInfoDispatcherStub;
-
-    private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub {
+    private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub
+            implements CallbackUtil.DispatcherStub {
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -486,7 +490,7 @@
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerEnabledChanged(boolean enabled) {
-            CallbackUtil.callListeners(mStateListeners, mStateListenerLock,
+            mStateListenerMgr.callListeners(
                     (listener) -> listener.onSpatializerEnabledChanged(
                             Spatializer.this, enabled));
         }
@@ -494,7 +498,7 @@
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerAvailableChanged(boolean available) {
-            CallbackUtil.callListeners(mStateListeners, mStateListenerLock,
+            mStateListenerMgr.callListeners(
                     (listener) -> listener.onSpatializerAvailableChanged(
                             Spatializer.this, available));
         }
@@ -612,16 +616,9 @@
     public void addOnHeadTrackingModeChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnHeadTrackingModeChangedListener listener) {
-        synchronized (mHeadTrackingListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>,
-                    SpatializerHeadTrackingDispatcherStub> res = CallbackUtil.addListener(
-                        "addOnHeadTrackingModeChangedListener", executor, listener,
-                        mHeadTrackingListeners, mHeadTrackingDispatcherStub,
-                        () -> new SpatializerHeadTrackingDispatcherStub(),
-                        stub -> stub.register(true));
-            mHeadTrackingListeners = res.first;
-            mHeadTrackingDispatcherStub = res.second;
-        }
+        mHeadTrackingListenerMgr.addListener(executor, listener,
+                "addOnHeadTrackingModeChangedListener",
+                 () -> new SpatializerHeadTrackingDispatcherStub());
     }
 
     /**
@@ -634,15 +631,8 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
     public void removeOnHeadTrackingModeChangedListener(
             @NonNull OnHeadTrackingModeChangedListener listener) {
-        synchronized (mHeadTrackingListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>,
-                    SpatializerHeadTrackingDispatcherStub> res = CallbackUtil.removeListener(
-                        "removeOnHeadTrackingModeChangedListener", listener,
-                        mHeadTrackingListeners, mHeadTrackingDispatcherStub,
-                        stub -> stub.register(false));
-            mHeadTrackingListeners = res.first;
-            mHeadTrackingDispatcherStub = res.second;
-        }
+        mHeadTrackingListenerMgr.removeListener(listener,
+                "removeOnHeadTrackingModeChangedListener");
     }
 
     /**
@@ -828,20 +818,17 @@
     //-----------------------------------------------------------------------------
     // head tracking callback management and stub
 
-    private final Object mHeadTrackingListenerLock = new Object();
     /**
-     * List of listeners for head tracking mode listener and their associated Executor.
-     * List is lazy-initialized on first registration
+     * manages the OnHeadTrackingModeChangedListener listeners and the
+     * SpatializerHeadTrackingDispatcherStub
      */
-    @GuardedBy("mHeadTrackingListenerLock")
-    private @Nullable ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>
-            mHeadTrackingListeners;
-
-    @GuardedBy("mHeadTrackingListenerLock")
-    private @Nullable SpatializerHeadTrackingDispatcherStub mHeadTrackingDispatcherStub;
+    private final CallbackUtil.LazyListenerManager<OnHeadTrackingModeChangedListener>
+            mHeadTrackingListenerMgr = new CallbackUtil.LazyListenerManager();
 
     private final class SpatializerHeadTrackingDispatcherStub
-            extends ISpatializerHeadTrackingModeCallback.Stub {
+            extends ISpatializerHeadTrackingModeCallback.Stub
+            implements CallbackUtil.DispatcherStub {
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -857,14 +844,14 @@
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerActualHeadTrackingModeChanged(int mode) {
-            CallbackUtil.callListeners(mHeadTrackingListeners, mHeadTrackingListenerLock,
+            mHeadTrackingListenerMgr.callListeners(
                     (listener) -> listener.onHeadTrackingModeChanged(Spatializer.this, mode));
         }
 
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerDesiredHeadTrackingModeChanged(int mode) {
-            CallbackUtil.callListeners(mHeadTrackingListeners, mHeadTrackingListenerLock,
+            mHeadTrackingListenerMgr.callListeners(
                     (listener) -> listener.onDesiredHeadTrackingModeChanged(
                             Spatializer.this, mode));
         }
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index d3c8e0a..b03f785 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -31,6 +31,8 @@
 {
     MidiDeviceInfo[] getDevices();
 
+    MidiDeviceInfo[] getDevicesForTransport(int transport);
+
     // for device creation & removal notifications
     void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
     void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
@@ -43,7 +45,7 @@
     // for registering built-in MIDI devices
     MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
             int numOutputPorts, in String[] inputPortNames, in String[] outputPortNames,
-            in Bundle properties, int type);
+            in Bundle properties, int type, int defaultProtocol);
 
     // for unregistering built-in MIDI devices
     void unregisterDeviceServer(in IMidiDeviceServer server);
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index dd3b6db..48c50f0 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -16,11 +16,15 @@
 
 package android.media.midi;
 
+import android.annotation.IntDef;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This class contains information to describe a MIDI device.
  * For now we only have information that can be retrieved easily for USB devices,
@@ -54,6 +58,110 @@
     public static final int TYPE_BLUETOOTH = 3;
 
     /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use UMP to negotiate with the device with MIDI-CI.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_USE_MIDI_CI = 0;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_2_0 = 17;
+
+     /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18;
+
+    /**
+     * Constant representing a device with an unknown default protocol.
+     * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UNKNOWN = -1;
+
+    /**
+     * @see MidiDeviceInfo#getDefaultProtocol
+     * @hide
+     */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+            PROTOCOL_UMP_USE_MIDI_CI,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS,
+            PROTOCOL_UMP_MIDI_2_0,
+            PROTOCOL_UMP_MIDI_2_0_AND_JRTS,
+            PROTOCOL_UNKNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Protocol {}
+
+    /**
      * Bundle key for the device's user visible name property.
      * The value for this property is of type {@link java.lang.String}.
      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
@@ -196,6 +304,7 @@
     private final String[] mOutputPortNames;
     private final Bundle mProperties;
     private final boolean mIsPrivate;
+    private final int mDefaultProtocol;
 
     /**
      * MidiDeviceInfo should only be instantiated by MidiService implementation
@@ -203,7 +312,7 @@
      */
     public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
             String[] inputPortNames, String[] outputPortNames, Bundle properties,
-            boolean isPrivate) {
+            boolean isPrivate, int defaultProtocol) {
         // Check num ports for out-of-range values. Typical values will be
         // between zero and three. More than 16 would be very unlikely
         // because the port index field in the USB packet is only 4 bits.
@@ -234,6 +343,7 @@
         }
         mProperties = properties;
         mIsPrivate = isPrivate;
+        mDefaultProtocol = defaultProtocol;
     }
 
     /**
@@ -312,6 +422,18 @@
         return mIsPrivate;
     }
 
+    /**
+     * Returns the default protocol. For most devices, this will be {@link #PROTOCOL_UNKNOWN}.
+     * Returning {@link #PROTOCOL_UNKNOWN} is not an error; the device just doesn't support
+     * Universal MIDI Packets by default.
+     *
+     * @return the device's default protocol.
+     */
+    @Protocol
+    public int getDefaultProtocol() {
+        return mDefaultProtocol;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o instanceof MidiDeviceInfo) {
@@ -331,11 +453,12 @@
         // This is a hack to force the mProperties Bundle to unparcel so we can
         // print all the names and values.
         mProperties.getString(PROPERTY_NAME);
-        return ("MidiDeviceInfo[mType=" + mType +
-                ",mInputPortCount=" + mInputPortCount +
-                ",mOutputPortCount=" + mOutputPortCount +
-                ",mProperties=" + mProperties +
-                ",mIsPrivate=" + mIsPrivate);
+        return ("MidiDeviceInfo[mType=" + mType
+                + ",mInputPortCount=" + mInputPortCount
+                + ",mOutputPortCount=" + mOutputPortCount
+                + ",mProperties=" + mProperties
+                + ",mIsPrivate=" + mIsPrivate
+                + ",mDefaultProtocol=" + mDefaultProtocol);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<MidiDeviceInfo> CREATOR =
@@ -349,10 +472,12 @@
             String[] inputPortNames = in.createStringArray();
             String[] outputPortNames = in.createStringArray();
             boolean isPrivate = (in.readInt() == 1);
+            int defaultProtocol = in.readInt();
             Bundle basicPropertiesIgnored = in.readBundle();
             Bundle properties = in.readBundle();
             return new MidiDeviceInfo(type, id, inputPortCount, outputPortCount,
-                    inputPortNames, outputPortNames, properties, isPrivate);
+                    inputPortNames, outputPortNames, properties, isPrivate,
+                    defaultProtocol);
         }
 
         public MidiDeviceInfo[] newArray(int size) {
@@ -390,6 +515,7 @@
         parcel.writeStringArray(mInputPortNames);
         parcel.writeStringArray(mOutputPortNames);
         parcel.writeInt(mIsPrivate ? 1 : 0);
+        parcel.writeInt(mDefaultProtocol);
         // "Basic" properties only contain properties of primitive types
         // and thus can be read back by native code. "Extra" properties is
         // a superset that contains all properties.
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index dee94c68..5348d4e 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -16,19 +16,31 @@
 
 package android.media.midi;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.ArraySet;
 import android.util.Log;
 
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+// BLE-MIDI
 
 /**
  * This class is the public application interface to the MIDI service.
@@ -39,6 +51,39 @@
     private static final String TAG = "MidiManager";
 
     /**
+     * Constant representing MIDI devices.
+     * These devices do NOT support Universal MIDI Packets by default.
+     * These support the original MIDI 1.0 byte stream.
+     * When communicating to a USB device, a raw byte stream will be padded for USB.
+     * Likewise, for a Bluetooth device, the raw bytes will be converted for Bluetooth.
+     * For virtual devices, the byte stream will be passed directly.
+     * If Universal MIDI Packets are needed, please use MIDI-CI.
+     * @see MidiManager#getDevicesForTransport
+     */
+    public static final int TRANSPORT_MIDI_BYTE_STREAM = 1;
+
+    /**
+     * Constant representing Universal MIDI devices.
+     * These devices do support Universal MIDI Packets (UMP) by default.
+     * When sending data to these devices, please send UMP.
+     * Packets should always be a multiple of 4 bytes.
+     * UMP is defined in the USB MIDI 2.0 spec. Please read the standard for more info.
+     * @see MidiManager#getDevicesForTransport
+     */
+    public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2;
+
+    /**
+     * @see MidiManager#getDevicesForTransport
+     * @hide
+     */
+    @IntDef(prefix = { "TRANSPORT_" }, value = {
+            TRANSPORT_MIDI_BYTE_STREAM,
+            TRANSPORT_UNIVERSAL_MIDI_PACKETS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Transport {}
+
+    /**
      * Intent for starting BluetoothMidiService
      * @hide
      */
@@ -68,43 +113,67 @@
     private class DeviceListener extends IMidiDeviceListener.Stub {
         private final DeviceCallback mCallback;
         private final Handler mHandler;
+        private final Executor mExecutor;
+        private final int mTransport;
 
-        public DeviceListener(DeviceCallback callback, Handler handler) {
+        DeviceListener(DeviceCallback callback, Handler handler, int transport) {
             mCallback = callback;
             mHandler = handler;
+            mExecutor = null;
+            mTransport = transport;
+        }
+
+        DeviceListener(DeviceCallback callback, Executor executor, int transport) {
+            mCallback = callback;
+            mHandler = null;
+            mExecutor = executor;
+            mTransport = transport;
         }
 
         @Override
         public void onDeviceAdded(MidiDeviceInfo device) {
-            if (mHandler != null) {
-                final MidiDeviceInfo deviceF = device;
-                mHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mCallback.onDeviceAdded(deviceF);
-                        }
-                    });
-            } else {
-                mCallback.onDeviceAdded(device);
+            if (shouldInvokeCallback(device)) {
+                if (mExecutor != null) {
+                    mExecutor.execute(() ->
+                            mCallback.onDeviceAdded(device));
+                } else if (mHandler != null) {
+                    final MidiDeviceInfo deviceF = device;
+                    mHandler.post(new Runnable() {
+                            @Override public void run() {
+                                mCallback.onDeviceAdded(deviceF);
+                            }
+                        });
+                } else {
+                    mCallback.onDeviceAdded(device);
+                }
             }
         }
 
         @Override
         public void onDeviceRemoved(MidiDeviceInfo device) {
-            if (mHandler != null) {
-                final MidiDeviceInfo deviceF = device;
-                mHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mCallback.onDeviceRemoved(deviceF);
-                        }
-                    });
-            } else {
-                mCallback.onDeviceRemoved(device);
+            if (shouldInvokeCallback(device)) {
+                if (mExecutor != null) {
+                    mExecutor.execute(() ->
+                            mCallback.onDeviceRemoved(device));
+                } else if (mHandler != null) {
+                    final MidiDeviceInfo deviceF = device;
+                    mHandler.post(new Runnable() {
+                            @Override public void run() {
+                                mCallback.onDeviceRemoved(deviceF);
+                            }
+                        });
+                } else {
+                    mCallback.onDeviceRemoved(device);
+                }
             }
         }
 
         @Override
         public void onDeviceStatusChanged(MidiDeviceStatus status) {
-            if (mHandler != null) {
+            if (mExecutor != null) {
+                mExecutor.execute(() ->
+                        mCallback.onDeviceStatusChanged(status));
+            } else if (mHandler != null) {
                 final MidiDeviceStatus statusF = status;
                 mHandler.post(new Runnable() {
                         @Override public void run() {
@@ -115,6 +184,25 @@
                 mCallback.onDeviceStatusChanged(status);
             }
         }
+
+        /**
+         * Used to figure out whether callbacks should be invoked. Only invoke callbacks of
+         * the correct type.
+         *
+         * @param MidiDeviceInfo the device to check
+         * @return whether to invoke a callback
+         */
+        private boolean shouldInvokeCallback(MidiDeviceInfo device) {
+            // UMP devices have protocols that are not PROTOCOL_UNKNOWN
+            if (mTransport == TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
+                return (device.getDefaultProtocol() != MidiDeviceInfo.PROTOCOL_UNKNOWN);
+            } else if (mTransport == TRANSPORT_MIDI_BYTE_STREAM) {
+                return (device.getDefaultProtocol() == MidiDeviceInfo.PROTOCOL_UNKNOWN);
+            } else {
+                Log.e(TAG, "Invalid transport type: " + mTransport);
+                return false;
+            }
+        }
     }
 
     /**
@@ -167,8 +255,10 @@
     }
 
     /**
-     * Registers a callback to receive notifications when MIDI devices are added and removed.
-     *
+     * Registers a callback to receive notifications when MIDI 1.0 devices are added and removed.
+     * These are devices that do not default to Universal MIDI Packets. To register for a callback
+     * for those, call {@link #registerDeviceCallback} instead.
+
      * The {@link  DeviceCallback#onDeviceStatusChanged} method will be called immediately
      * for any devices that have open ports. This allows applications to know which input
      * ports are already in use and, therefore, unavailable.
@@ -180,9 +270,42 @@
      * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
      *                device notifications. If handler is null, then the thread used for the
      *                callback is unspecified.
+     * @deprecated Use the {@link #registerDeviceCallback}
+     *             method with Executor and transport instead.
      */
+    @Deprecated
     public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
-        DeviceListener deviceListener = new DeviceListener(callback, handler);
+        DeviceListener deviceListener = new DeviceListener(callback, handler,
+                TRANSPORT_MIDI_BYTE_STREAM);
+        try {
+            mService.registerListener(mToken, deviceListener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        mDeviceListeners.put(callback, deviceListener);
+    }
+
+    /**
+     * Registers a callback to receive notifications when MIDI devices are added and removed
+     * for a specific transport type.
+     *
+     * The {@link  DeviceCallback#onDeviceStatusChanged} method will be called immediately
+     * for any devices that have open ports. This allows applications to know which input
+     * ports are already in use and, therefore, unavailable.
+     *
+     * Applications should call {@link #getDevicesForTransport} before registering the callback
+     * to get a list of devices already added.
+     *
+     * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+     *            TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+     * @param executor The {@link Executor} that will be used for delivering the
+     *                device notifications.
+     * @param callback a {@link DeviceCallback} for MIDI device notifications
+     */
+    public void registerDeviceCallback(@Transport int transport,
+            @NonNull Executor executor, @NonNull DeviceCallback callback) {
+        Objects.requireNonNull(executor);
+        DeviceListener deviceListener = new DeviceListener(callback, executor, transport);
         try {
             mService.registerListener(mToken, deviceListener);
         } catch (RemoteException e) {
@@ -208,10 +331,14 @@
     }
 
     /**
-     * Gets the list of all connected MIDI devices.
+     * Gets a list of connected MIDI devices. This returns all devices that do
+     * not default to Universal MIDI Packets. To get those instead, please call
+     * {@link #getDevicesForTransport} instead.
      *
-     * @return an array of all MIDI devices
+     * @return an array of MIDI devices
+     * @deprecated Use {@link #getDevicesForTransport} instead.
      */
+    @Deprecated
     public MidiDeviceInfo[] getDevices() {
         try {
            return mService.getDevices();
@@ -220,6 +347,28 @@
         }
     }
 
+    /**
+     * Gets a list of connected MIDI devices by transport. TRANSPORT_MIDI_BYTE_STREAM
+     * is used for MIDI 1.0 and is the most common.
+     * For devices with built in Universal MIDI Packet support, use
+     * TRANSPORT_UNIVERSAL_MIDI_PACKETS instead.
+     *
+     * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+     *                  TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+     * @return a collection of MIDI devices
+     */
+    public @NonNull Set<MidiDeviceInfo> getDevicesForTransport(@Transport int transport) {
+        try {
+            MidiDeviceInfo[] devices = mService.getDevicesForTransport(transport);
+            if (devices == null) {
+                return Collections.emptySet();
+            }
+            return new ArraySet<>(devices);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private void sendOpenDeviceResponse(final MidiDevice device,
             final OnDeviceOpenedListener listener, Handler handler) {
         if (handler != null) {
@@ -284,9 +433,11 @@
         final OnDeviceOpenedListener listenerF = listener;
         final Handler handlerF = handler;
 
+        Log.d(TAG, "openBluetoothDevice() " + bluetoothDevice);
         IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
             @Override
             public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+                Log.d(TAG, "onDeviceOpened() server:" + server);
                 MidiDevice device = null;
                 if (server != null) {
                     try {
@@ -308,16 +459,26 @@
         }
     }
 
+    /** @hide */ // for now
+    public void closeBluetoothDevice(@NonNull MidiDevice midiDevice) {
+        try {
+            midiDevice.close();
+        } catch (IOException ex) {
+            Log.e(TAG, "Exception closing BLE-MIDI device" + ex);
+        }
+    }
+
     /** @hide */
     public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
             int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
-            Bundle properties, int type, MidiDeviceServer.Callback callback) {
+            Bundle properties, int type, int defaultProtocol,
+            MidiDeviceServer.Callback callback) {
         try {
             MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
                     numOutputPorts, callback);
             MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
                     inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
-                    properties, type);
+                    properties, type, defaultProtocol);
             if (deviceInfo == null) {
                 Log.e(TAG, "registerVirtualDevice failed");
                 return null;
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 33c5490..67df1b2 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -405,5 +405,46 @@
 <a href="#get_list_of_already_plugged_in_entities">MIDI device discovery calls described above</a>.
 </p>
 
+<h1 id=using_midi_2_0_over_usb>Using MIDI 2.0 over USB</h1>
+
+<p>An app can use MIDI 2.0 over USB starting in Android T. MIDI 2.0 packets are embedded in
+Universal MIDI Packets, or UMP for short. A MIDI 2.0 USB device should create two interfaces,
+one endpoint that accepts only MIDI 1.0 packets and one that accepts only UMP packets.
+For more info about MIDI 2.0 and UMP, please read the MIDI 2.0 USB spec.</p>
+
+<p>MidiManager.getDevices() would simply return the 1.0 interface. This interface should work
+exactly the same as before. In order to use the new UMP interface, retrieve the device with the
+following code snippet.</p>
+
+<pre class=prettyprint>
+Collection&#60;MidiDeviceInfo&#62; universalDeviceInfos = midiManager.getDevicesForTransport(
+        MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS);
+</pre>
+
+<p>UMP Packets are always in multiple of 4 bytes. For each set of 4 bytes, they are sent in network
+order. Compare the following NoteOn code snippet with the NoteOn code snippet above. </p>
+
+<pre class=prettyprint>
+byte[] buffer = new byte[32];
+int numBytes = 0;
+int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
+int group = 0;
+buffer[numBytes++] = (byte)(0x20 + group); // MIDI 1.0 voice message
+buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
+buffer[numBytes++] = (byte)60; // pitch is middle C
+buffer[numBytes++] = (byte)127; // max velocity
+int offset = 0;
+// post is non-blocking
+inputPort.send(buffer, offset, numBytes);
+</pre>
+
+<p>MIDI 2.0 messages can be sent through UMP after negotiating with the device. This is called
+MIDI-CI and is documented in the MIDI 2.0 spec. Some USB devices support pre-negotiated MIDI 2.0.
+For a MidiDeviceInfo, you can query the defaultProtocol.</p>
+
+<pre class=prettyprint>
+int defaultProtocol = info.getDefaultProtocol();
+</pre>
+
 </body>
 </html>
diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java
index ff4c625..71b1634 100644
--- a/media/java/android/media/tv/AitInfo.java
+++ b/media/java/android/media/tv/AitInfo.java
@@ -17,11 +17,12 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
+import android.media.tv.interactive.TvInteractiveAppInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * AIT info.
+ * AIT (Application Information Table) info.
  * @hide
  */
 public final class AitInfo implements Parcelable {
@@ -50,14 +51,15 @@
     /**
      * Constructs AIT info.
      */
-    public AitInfo(int type, int version) {
+    public AitInfo(@TvInteractiveAppInfo.InteractiveAppType int type, int version) {
         mType = type;
         mVersion = version;
     }
 
     /**
-     * Gets type.
+     * Gets interactive app type.
      */
+    @TvInteractiveAppInfo.InteractiveAppType
     public int getType() {
         return mType;
     }
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index 85ad3cd..6ba9133 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -49,8 +49,10 @@
                             return StreamEventRequest.createFromParcelBody(source);
                         case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
                             return DsmccRequest.createFromParcelBody(source);
-                        case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+                        case TvInputManager.BROADCAST_INFO_TYPE_COMMAND:
                             return CommandRequest.createFromParcelBody(source);
+                        case TvInputManager.BROADCAST_INFO_TYPE_TIMELINE:
+                            return TimelineRequest.createFromParcelBody(source);
                         default:
                             throw new IllegalStateException(
                                     "Unexpected broadcast info request type (value "
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index e423aba..67bdedc 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -50,8 +50,10 @@
                             return StreamEventResponse.createFromParcelBody(source);
                         case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
                             return DsmccResponse.createFromParcelBody(source);
-                        case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+                        case TvInputManager.BROADCAST_INFO_TYPE_COMMAND:
                             return CommandResponse.createFromParcelBody(source);
+                        case TvInputManager.BROADCAST_INFO_TYPE_TIMELINE:
+                            return TimelineResponse.createFromParcelBody(source);
                         default:
                             throw new IllegalStateException(
                                     "Unexpected broadcast info response type (value "
diff --git a/media/java/android/media/tv/CommandRequest.java b/media/java/android/media/tv/CommandRequest.java
index 2391fa3..d61c858 100644
--- a/media/java/android/media/tv/CommandRequest.java
+++ b/media/java/android/media/tv/CommandRequest.java
@@ -23,7 +23,7 @@
 /** @hide */
 public final class CommandRequest extends BroadcastInfoRequest implements Parcelable {
     public static final @TvInputManager.BroadcastInfoType int requestType =
-            TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
+            TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
 
     public static final @NonNull Parcelable.Creator<CommandRequest> CREATOR =
             new Parcelable.Creator<CommandRequest>() {
diff --git a/media/java/android/media/tv/CommandResponse.java b/media/java/android/media/tv/CommandResponse.java
index d34681f..af3d00c 100644
--- a/media/java/android/media/tv/CommandResponse.java
+++ b/media/java/android/media/tv/CommandResponse.java
@@ -23,7 +23,7 @@
 /** @hide */
 public final class CommandResponse extends BroadcastInfoResponse implements Parcelable {
     public static final @TvInputManager.BroadcastInfoType int responseType =
-            TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
+            TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
 
     public static final @NonNull Parcelable.Creator<CommandResponse> CREATOR =
             new Parcelable.Creator<CommandResponse>() {
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index e43d31a..3ca63e3 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -17,15 +17,42 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
+import android.annotation.StringDef;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
 /** @hide */
 public final class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
     public static final @TvInputManager.BroadcastInfoType int responseType =
             TvInputManager.BROADCAST_INFO_TYPE_DSMCC;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "BIOP_MESSAGE_TYPE_", value = {
+            BIOP_MESSAGE_TYPE_DIRECTORY,
+            BIOP_MESSAGE_TYPE_FILE,
+            BIOP_MESSAGE_TYPE_STREAM,
+            BIOP_MESSAGE_TYPE_SERVICE_GATEWAY,
+
+    })
+    public @interface BiopMessageType {}
+
+    /** Broadcast Inter-ORB Protocol (BIOP) message types */
+    /** BIOP directory message */
+    public static final String BIOP_MESSAGE_TYPE_DIRECTORY = "directory";
+    /** BIOP file message */
+    public static final String BIOP_MESSAGE_TYPE_FILE = "file";
+    /** BIOP stream message */
+    public static final String BIOP_MESSAGE_TYPE_STREAM = "stream";
+    /** BIOP service gateway message */
+    public static final String BIOP_MESSAGE_TYPE_SERVICE_GATEWAY = "service_gateway";
+
     public static final @NonNull Parcelable.Creator<DsmccResponse> CREATOR =
             new Parcelable.Creator<DsmccResponse>() {
                 @Override
@@ -40,30 +67,173 @@
                 }
             };
 
+    private final @BiopMessageType String mBiopMessageType;
     private final ParcelFileDescriptor mFileDescriptor;
+    private final List<String> mChildList;
+    private final int[] mEventIds;
+    private final String[] mEventNames;
 
     public static DsmccResponse createFromParcelBody(Parcel in) {
         return new DsmccResponse(in);
     }
 
+    /**
+     * Constructs a BIOP file message response.
+     */
     public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
-            ParcelFileDescriptor file) {
+            @NonNull ParcelFileDescriptor file) {
         super(responseType, requestId, sequence, responseResult);
+        mBiopMessageType = BIOP_MESSAGE_TYPE_FILE;
         mFileDescriptor = file;
+        mChildList = null;
+        mEventIds = null;
+        mEventNames = null;
     }
 
-    protected DsmccResponse(Parcel source) {
+    /**
+     * Constructs a BIOP service gateway or directory message response.
+     */
+    public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
+            boolean isServiceGateway, @NonNull List<String> childList) {
+        super(responseType, requestId, sequence, responseResult);
+        if (isServiceGateway) {
+            mBiopMessageType = BIOP_MESSAGE_TYPE_SERVICE_GATEWAY;
+        } else {
+            mBiopMessageType = BIOP_MESSAGE_TYPE_DIRECTORY;
+        }
+        mFileDescriptor = null;
+        mChildList = childList;
+        mEventIds = null;
+        mEventNames = null;
+    }
+
+    /**
+     * Constructs a BIOP stream message response.
+     *
+     * <p>The current stream message response does not support other stream messages types than
+     * stream event message type.
+     */
+    public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
+            @NonNull int[] eventIds, @NonNull String[] eventNames) {
+        super(responseType, requestId, sequence, responseResult);
+        mBiopMessageType = BIOP_MESSAGE_TYPE_STREAM;
+        mFileDescriptor = null;
+        mChildList = null;
+        mEventIds = eventIds;
+        mEventNames = eventNames;
+        if (mEventIds.length != eventNames.length) {
+            throw new IllegalStateException("The size of eventIds and eventNames must be equal");
+        }
+    }
+
+    private DsmccResponse(@NonNull Parcel source) {
         super(responseType, source);
-        mFileDescriptor = source.readFileDescriptor();
+
+        mBiopMessageType = source.readString();
+        switch (mBiopMessageType) {
+            case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
+            case BIOP_MESSAGE_TYPE_DIRECTORY:
+                int childNum = source.readInt();
+                mChildList = new ArrayList<>();
+                for (int i = 0; i < childNum; i++) {
+                    mChildList.add(source.readString());
+                }
+                mFileDescriptor = null;
+                mEventIds = null;
+                mEventNames = null;
+                break;
+            case BIOP_MESSAGE_TYPE_FILE:
+                mFileDescriptor = source.readFileDescriptor();
+                mChildList = null;
+                mEventIds = null;
+                mEventNames = null;
+                break;
+            case BIOP_MESSAGE_TYPE_STREAM:
+                int eventNum = source.readInt();
+                mEventIds = new int[eventNum];
+                mEventNames = new String[eventNum];
+                for (int i = 0; i < eventNum; i++) {
+                    mEventIds[i] = source.readInt();
+                    mEventNames[i] = source.readString();
+                }
+                mChildList = null;
+                mFileDescriptor = null;
+                break;
+            default:
+                throw new IllegalStateException("unexpected BIOP message type");
+        }
     }
 
+    /** Returns the BIOP message type */
+    @NonNull
+    public @BiopMessageType String getBiopMessageType() {
+        return mBiopMessageType;
+    }
+
+    /** Returns the file descriptor for a given file message response */
+    @NonNull
     public ParcelFileDescriptor getFile() {
+        if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_FILE)) {
+            throw new IllegalStateException("Not file object");
+        }
         return mFileDescriptor;
     }
 
+    /**
+     * Returns a list of subobject names for the given service gateway or directory message
+     * response.
+     */
+    @NonNull
+    public List<String> getChildList() {
+        if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_DIRECTORY)
+                && !mBiopMessageType.equals(BIOP_MESSAGE_TYPE_SERVICE_GATEWAY)) {
+            throw new IllegalStateException("Not directory object");
+        }
+        return new ArrayList<String>(mChildList);
+    }
+
+    /** Returns all event IDs carried in a given stream message response. */
+    @NonNull
+    public int[] getStreamEventIds() {
+        if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
+            throw new IllegalStateException("Not stream event object");
+        }
+        return mEventIds;
+    }
+
+    /** Returns all event names carried in a given stream message response */
+    @NonNull
+    public String[] getStreamEventNames() {
+        if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
+            throw new IllegalStateException("Not stream event object");
+        }
+        return mEventNames;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
-        mFileDescriptor.writeToParcel(dest, flags);
+        dest.writeString(mBiopMessageType);
+        switch (mBiopMessageType) {
+            case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
+            case BIOP_MESSAGE_TYPE_DIRECTORY:
+                dest.writeInt(mChildList.size());
+                for (String child : mChildList) {
+                    dest.writeString(child);
+                }
+                break;
+            case BIOP_MESSAGE_TYPE_FILE:
+                dest.writeFileDescriptor(mFileDescriptor.getFileDescriptor());
+                break;
+            case BIOP_MESSAGE_TYPE_STREAM:
+                dest.writeInt(mEventIds.length);
+                for (int i = 0; i < mEventIds.length; i++) {
+                    dest.writeInt(mEventIds[i]);
+                    dest.writeString(mEventNames[i]);
+                }
+                break;
+            default:
+                throw new IllegalStateException("unexpected BIOP message type");
+        }
     }
 }
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index f4f55e4..49148ce 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -47,6 +47,7 @@
     void onTimeShiftStartPositionChanged(long timeMs, int seq);
     void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
     void onAitInfoUpdated(in AitInfo aitInfo, int seq);
+    void onSignalStrength(int stength, int seq);
 
     void onTuned(in Uri channelUri, int seq);
     // For the recording session
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index d34e636..2a33ee6 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -66,6 +66,7 @@
             int seq, int userId);
     void releaseSession(in IBinder sessionToken, int userId);
     int getClientPid(in String sessionId);
+    int getClientPriority(int useCase, in String sessionId);
 
     void setMainSession(in IBinder sessionToken, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
@@ -76,7 +77,7 @@
     void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
     void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
 
-    void setIAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
+    void setInteractiveAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
 
     void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
             int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index f427501..9820034 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -42,7 +42,7 @@
     void setCaptionEnabled(boolean enabled);
     void selectTrack(int type, in String trackId);
 
-    void setIAppNotificationEnabled(boolean enable);
+    void setInteractiveAppNotificationEnabled(boolean enable);
 
     void appPrivateCommand(in String action, in Bundle data);
 
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 9830e78..9dfdb78 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -44,6 +44,7 @@
     void onTimeShiftStartPositionChanged(long timeMs);
     void onTimeShiftCurrentPositionChanged(long timeMs);
     void onAitInfoUpdated(in AitInfo aitInfo);
+    void onSignalStrength(int strength);
 
     // For the recording session
     void onTuned(in Uri channelUri);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 418ab2c..8911f6c 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -247,7 +247,7 @@
                 break;
             }
             case DO_SET_IAPP_NOTIFICATION_ENABLED: {
-                mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj);
+                mTvInputSessionImpl.setInteractiveAppNotificationEnabled((Boolean) msg.obj);
                 break;
             }
             case DO_REQUEST_AD: {
@@ -322,7 +322,7 @@
     }
 
     @Override
-    public void setIAppNotificationEnabled(boolean enabled) {
+    public void setInteractiveAppNotificationEnabled(boolean enabled) {
         mCaller.executeOrSendMessage(
                 mCaller.obtainMessageO(DO_SET_IAPP_NOTIFICATION_ENABLED, enabled));
     }
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 33acd0d..fa04293 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -1,6 +1,6 @@
[email protected]
 [email protected]
 [email protected]
[email protected]
 
 # For android remote service
 per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
index fd75801..903fab5c2 100644
--- a/media/java/android/media/tv/StreamEventResponse.java
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -39,54 +39,53 @@
                 }
             };
 
-    private final String mName;
-    private final String mText;
-    private final String mData;
-    private final String mStatus;
+    private final int mEventId;
+    private final long mNpt;
+    private final byte[] mData;
 
     public static StreamEventResponse createFromParcelBody(Parcel in) {
         return new StreamEventResponse(in);
     }
 
     public StreamEventResponse(int requestId, int sequence, @ResponseResult int responseResult,
-            String name, String text, String data, String status) {
+            int eventId, long npt, @NonNull byte[] data) {
         super(responseType, requestId, sequence, responseResult);
-        mName = name;
-        mText = text;
+        mEventId = eventId;
+        mNpt = npt;
         mData = data;
-        mStatus = status;
     }
 
-    protected StreamEventResponse(Parcel source) {
+    private StreamEventResponse(@NonNull Parcel source) {
         super(responseType, source);
-        mName = source.readString();
-        mText = source.readString();
-        mData = source.readString();
-        mStatus = source.readString();
+        mEventId = source.readInt();
+        mNpt = source.readLong();
+        int dataLength = source.readInt();
+        mData = new byte[dataLength];
+        source.readByteArray(mData);
     }
 
-    public String getName() {
-        return mName;
+    /** Returns the event ID */
+    public int getEventId() {
+        return mEventId;
     }
 
-    public String getText() {
-        return mText;
+    /** Returns the NPT(Normal Play Time) value when the event occurred or will occur */
+    public long getNpt() {
+        return mNpt;
     }
 
-    public String getData() {
+    /** Returns the application specific data */
+    @NonNull
+    public byte[] getData() {
         return mData;
     }
 
-    public String getStatus() {
-        return mStatus;
-    }
-
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
-        dest.writeString(mName);
-        dest.writeString(mText);
-        dest.writeString(mData);
-        dest.writeString(mStatus);
+        dest.writeInt(mEventId);
+        dest.writeLong(mNpt);
+        dest.writeInt(mData.length);
+        dest.writeByteArray(mData);
     }
 }
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index 912cbce..68d5f8a 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -17,9 +17,9 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.net.Uri;
 
 /** @hide */
 public final class TableResponse extends BroadcastInfoResponse implements Parcelable {
diff --git a/media/java/android/media/tv/TimelineRequest.java b/media/java/android/media/tv/TimelineRequest.java
new file mode 100644
index 0000000..0714972
--- /dev/null
+++ b/media/java/android/media/tv/TimelineRequest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class TimelineRequest extends BroadcastInfoRequest implements Parcelable {
+    private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE =
+            TvInputManager.BROADCAST_INFO_TYPE_TIMELINE;
+
+    public static final @NonNull Parcelable.Creator<TimelineRequest> CREATOR =
+            new Parcelable.Creator<TimelineRequest>() {
+                @Override
+                public TimelineRequest createFromParcel(Parcel source) {
+                    source.readInt();
+                    return createFromParcelBody(source);
+                }
+
+                @Override
+                public TimelineRequest[] newArray(int size) {
+                    return new TimelineRequest[size];
+                }
+            };
+
+    private final int mIntervalMs;
+
+    static TimelineRequest createFromParcelBody(Parcel in) {
+        return new TimelineRequest(in);
+    }
+
+    public TimelineRequest(int requestId, @RequestOption int option, int intervalMs) {
+        super(REQUEST_TYPE, requestId, option);
+        mIntervalMs = intervalMs;
+    }
+
+    protected TimelineRequest(Parcel source) {
+        super(REQUEST_TYPE, source);
+        mIntervalMs = source.readInt();
+    }
+
+    public int getIntervalMs() {
+        return mIntervalMs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mIntervalMs);
+    }
+}
diff --git a/media/java/android/media/tv/TimelineResponse.java b/media/java/android/media/tv/TimelineResponse.java
new file mode 100644
index 0000000..fee10b4
--- /dev/null
+++ b/media/java/android/media/tv/TimelineResponse.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class TimelineResponse extends BroadcastInfoResponse implements Parcelable {
+    private static final @TvInputManager.BroadcastInfoType int RESPONSE_TYPE =
+            TvInputManager.BROADCAST_INFO_TYPE_TIMELINE;
+
+    public static final @NonNull Parcelable.Creator<TimelineResponse> CREATOR =
+            new Parcelable.Creator<TimelineResponse>() {
+                @Override
+                public TimelineResponse createFromParcel(Parcel source) {
+                    source.readInt();
+                    return createFromParcelBody(source);
+                }
+
+                @Override
+                public TimelineResponse[] newArray(int size) {
+                    return new TimelineResponse[size];
+                }
+            };
+
+    private final String mSelector;
+    private final int mUnitsPerTick;
+    private final int mUnitsPerSecond;
+    private final long mWallClock;
+    private final long mTicks;
+
+    static TimelineResponse createFromParcelBody(Parcel in) {
+        return new TimelineResponse(in);
+    }
+
+    public TimelineResponse(int requestId, int sequence,
+            @ResponseResult int responseResult, String selector, int unitsPerTick,
+            int unitsPerSecond, long wallClock, long ticks) {
+        super(RESPONSE_TYPE, requestId, sequence, responseResult);
+        mSelector = selector;
+        mUnitsPerTick = unitsPerTick;
+        mUnitsPerSecond = unitsPerSecond;
+        mWallClock = wallClock;
+        mTicks = ticks;
+    }
+
+    protected TimelineResponse(Parcel source) {
+        super(RESPONSE_TYPE, source);
+        mSelector = source.readString();
+        mUnitsPerTick = source.readInt();
+        mUnitsPerSecond = source.readInt();
+        mWallClock = source.readLong();
+        mTicks = source.readLong();
+    }
+
+    public String getSelector() {
+        return mSelector;
+    }
+
+    public int getUnitsPerTick() {
+        return mUnitsPerTick;
+    }
+
+    public int getUnitsPerSecond() {
+        return mUnitsPerSecond;
+    }
+
+    public long getWallClock() {
+        return mWallClock;
+    }
+
+    public long getTicks() {
+        return mTicks;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mSelector);
+        dest.writeInt(mUnitsPerTick);
+        dest.writeInt(mUnitsPerSecond);
+        dest.writeLong(mWallClock);
+        dest.writeLong(mTicks);
+    }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index ad86002..f438d29 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -27,8 +28,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat.Encoding;
 import android.media.PlaybackParams;
-import android.media.tv.interactive.TvIAppManager;
+import android.media.tv.interactive.TvInteractiveAppManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -60,6 +63,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -359,9 +363,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
+    @IntDef(prefix = "BROADCAST_INFO_TYPE_", value =
+            {BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
             BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC,
-            BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION})
+            BROADCAST_INFO_TYPE_COMMAND, BROADCAST_INFO_TYPE_TIMELINE})
     public @interface BroadcastInfoType {}
 
     /** @hide */
@@ -377,7 +382,31 @@
     /** @hide */
     public static final int BROADCAST_INFO_TYPE_DSMCC = 6;
     /** @hide */
-    public static final int BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION = 7;
+    public static final int BROADCAST_INFO_TYPE_COMMAND = 7;
+    /** @hide */
+    public static final int BROADCAST_INFO_TYPE_TIMELINE = 8;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SIGNAL_STRENGTH_",
+            value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG})
+    public @interface SignalStrength {}
+
+    /**
+     * Signal lost.
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_LOST = 1;
+    /**
+     * Weak signal.
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_WEAK = 2;
+    /**
+     * Strong signal.
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_STRONG = 3;
 
     /**
      * An unknown state of the client pid gets from the TvInputManager. Client gets this value when
@@ -658,6 +687,14 @@
         }
 
         /**
+         * This is called when signal strength is updated.
+         * @param session A {@link TvInputManager.Session} associated with this callback.
+         * @param strength The current signal strength.
+         */
+        public void onSignalStrength(Session session, @SignalStrength int strength) {
+        }
+
+        /**
          * This is called when the session has been tuned to the given channel.
          *
          * @param channelUri The URI of a channel.
@@ -731,8 +768,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onTracksChanged(mSession, tracks);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyTracksChanged(tracks);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyTracksChanged(tracks);
                     }
                 }
             });
@@ -743,8 +781,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onTrackSelected(mSession, type, trackId);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyTrackSelected(type, trackId);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyTrackSelected(type, trackId);
                     }
                 }
             });
@@ -764,6 +803,10 @@
                 @Override
                 public void run() {
                     mSessionCallback.onVideoAvailable(mSession);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyVideoAvailable();
+                    }
                 }
             });
         }
@@ -773,6 +816,10 @@
                 @Override
                 public void run() {
                     mSessionCallback.onVideoUnavailable(mSession, reason);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyVideoUnavailable(reason);
+                    }
                 }
             });
         }
@@ -782,6 +829,10 @@
                 @Override
                 public void run() {
                     mSessionCallback.onContentAllowed(mSession);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyContentAllowed();
+                    }
                 }
             });
         }
@@ -791,6 +842,10 @@
                 @Override
                 public void run() {
                     mSessionCallback.onContentBlocked(mSession, rating);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyContentBlocked(rating);
+                    }
                 }
             });
         }
@@ -850,13 +905,27 @@
             });
         }
 
+        void postSignalStrength(final int strength) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSignalStrength(mSession, strength);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifySignalStrength(strength);
+                    }
+                }
+            });
+        }
+
         void postTuned(final Uri channelUri) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     mSessionCallback.onTuned(mSession, channelUri);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyTuned(channelUri);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyTuned(channelUri);
                     }
                 }
             });
@@ -887,8 +956,9 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        if (mSession.getIAppSession() != null) {
-                            mSession.getIAppSession().notifyBroadcastInfoResponse(response);
+                        if (mSession.getInteractiveAppSession() != null) {
+                            mSession.getInteractiveAppSession()
+                                    .notifyBroadcastInfoResponse(response);
                         }
                     }
                 });
@@ -900,8 +970,8 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        if (mSession.getIAppSession() != null) {
-                            mSession.getIAppSession().notifyAdResponse(response);
+                        if (mSession.getInteractiveAppSession() != null) {
+                            mSession.getInteractiveAppSession().notifyAdResponse(response);
                         }
                     }
                 });
@@ -1283,6 +1353,18 @@
             }
 
             @Override
+            public void onSignalStrength(int strength, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postSignalStrength(strength);
+                }
+            }
+
+            @Override
             public void onTuned(Uri channelUri, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -1786,6 +1868,29 @@
     };
 
     /**
+     * Returns a priority for the given use case type and the client's foreground or background
+     * status.
+     *
+     * @param useCase the use case type of the client. When the given use case type is invalid,
+     *        the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}.
+     * @param sessionId the unique id of the session owned by the client. When {@code null},
+     *        the caller will be used as a client. When the session is invalid, background status
+     *        will be used as a client's status. Otherwise, TV app corresponding to the given
+     *        session id will be used as a client.
+     *        {@see TvInputService#onCreateSession(String, String)}.
+     *
+     * @return the use case priority value for the given use case type and the client's foreground
+     *         or background status.
+     *
+     * @hide
+     */
+    @SystemApi
+    public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase,
+            @Nullable String sessionId) {
+        return getClientPriorityInternal(useCase, sessionId);
+    };
+
+    /**
      * Creates a recording {@link Session} for a given TV input.
      *
      * <p>The number of sessions that can be created at the same time is limited by the capability
@@ -1829,6 +1934,14 @@
         return clientPid;
     }
 
+    private int getClientPriorityInternal(int useCase, String sessionId) {
+        try {
+            return mService.getClientPriority(useCase, sessionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Returns the TvStreamConfig list of the given TV input.
      *
@@ -2205,7 +2318,7 @@
         // @GuardedBy("mMetadataLock")
         private int mVideoHeight;
 
-        private TvIAppManager.Session mIAppSession;
+        private TvInteractiveAppManager.Session mIAppSession;
         private boolean mIAppNotificationEnabled = false;
 
         private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
@@ -2218,12 +2331,12 @@
             mSessionCallbackRecordMap = sessionCallbackRecordMap;
         }
 
-        public TvIAppManager.Session getIAppSession() {
+        public TvInteractiveAppManager.Session getInteractiveAppSession() {
             return mIAppSession;
         }
 
-        public void setIAppSession(TvIAppManager.Session IAppSession) {
-            this.mIAppSession = IAppSession;
+        public void setInteractiveAppSession(TvInteractiveAppManager.Session iAppSession) {
+            this.mIAppSession = iAppSession;
         }
 
         /**
@@ -2480,17 +2593,17 @@
 
         /**
          * Enables interactive app notification.
+         *
          * @param enabled {@code true} if you want to enable interactive app notifications.
          *                {@code false} otherwise.
-         * @hide
          */
-        public void setIAppNotificationEnabled(boolean enabled) {
+        public void setInteractiveAppNotificationEnabled(boolean enabled) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.setIAppNotificationEnabled(mToken, enabled, mUserId);
+                mService.setInteractiveAppNotificationEnabled(mToken, enabled, mUserId);
                 mIAppNotificationEnabled = enabled;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -3178,6 +3291,16 @@
             return false;
         }
 
+        /**
+         * Override default audio sink from audio policy.
+         *
+         * @param audioType device type of the audio sink to override with.
+         * @param audioAddress device address of the audio sink to override with.
+         * @param samplingRate desired sampling rate. Use default when it's 0.
+         * @param channelMask desired channel mask. Use default when it's
+         *        AudioFormat.CHANNEL_OUT_DEFAULT.
+         * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+         */
         public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
                 int channelMask, int format) {
             try {
@@ -3187,5 +3310,27 @@
                 throw new RuntimeException(e);
             }
         }
+
+        /**
+         * Override default audio sink from audio policy.
+         *
+         * @param device {@link android.media.AudioDeviceInfo} to use.
+         * @param samplingRate desired sampling rate. Use default when it's 0.
+         * @param channelMask desired channel mask. Use default when it's
+         *        AudioFormat.CHANNEL_OUT_DEFAULT.
+         * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+         */
+        public void overrideAudioSink(@NonNull AudioDeviceInfo device,
+                @IntRange(from = 0) int samplingRate,
+                int channelMask, @Encoding int format) {
+            Objects.requireNonNull(device);
+            try {
+                mInterface.overrideAudioSink(
+                        AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
+                        device.getAddress(), samplingRate, channelMask, format);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index bd5a343..9bc7367 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -592,6 +592,24 @@
             });
         }
 
+        /** @hide */
+        public void notifyTuned(@NonNull Uri channelUri) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyTuned");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onTuned(channelUri);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyTuned", e);
+                    }
+                }
+            });
+        }
+
         /**
          * Sends the list of all audio/video/subtitle tracks. The is used by the framework to
          * maintain the track information for a given session, which in turn is used by
@@ -927,7 +945,13 @@
         }
 
         /**
-         * Notifies AIT info updated.
+         * Informs the app that the AIT (Application Information Table) is updated.
+         *
+         * <p>This method should also be call when
+         * {@link #onSetInteractiveAppNotificationEnabled(boolean)} is called to send the first AIT
+         * info.
+         *
+         * @see #onSetInteractiveAppNotificationEnabled(boolean)
          * @hide
          */
         public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) {
@@ -948,6 +972,27 @@
         }
 
         /**
+         * Notifies signal strength.
+         * @hide
+         */
+        public void notifySignalStrength(@TvInputManager.SignalStrength final int strength) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifySignalStrength");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onSignalStrength(strength);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifySignalStrength", e);
+                    }
+                }
+            });
+        }
+
+        /**
          * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
          * is relative to the overlay view that sits on top of this surface.
          *
@@ -1159,10 +1204,19 @@
 
         /**
          * Enables or disables interactive app notification.
+         *
+         * <p>This method enables or disables the event detection from the corresponding TV input.
+         * When it's enabled, the TV input service detects events related to interactive app, such
+         * as AIT (Application Information Table) and sends to TvView or the linked TV interactive
+         * app service.
+         *
          * @param enabled {@code true} to enable, {@code false} to disable.
+         *
+         * @see TvView#setInteractiveAppNotificationEnabled(boolean)
+         * @see Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
          * @hide
          */
-        public void onSetIAppNotificationEnabled(boolean enabled) {
+        public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
         }
 
         /**
@@ -1514,10 +1568,10 @@
         }
 
         /**
-         * Calls {@link #onSetIAppNotificationEnabled}.
+         * Calls {@link #onSetInteractiveAppNotificationEnabled}.
          */
-        void setIAppNotificationEnabled(boolean enabled) {
-            onSetIAppNotificationEnabled(enabled);
+        void setInteractiveAppNotificationEnabled(boolean enabled) {
+            onSetInteractiveAppNotificationEnabled(enabled);
         }
 
         /**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 4a12cd7..d2086c5 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -481,14 +481,23 @@
     }
 
     /**
-     * Enables interactive app notification.
+     * Enables or disables interactive app notification.
+     *
+     * <p>This method enables or disables the event detection from the corresponding TV input. When
+     * it's enabled, the TV input service detects events related to interactive app, such as
+     * AIT (Application Information Table) and sends to TvView or the linked TV interactive app
+     * service.
+     *
      * @param enabled {@code true} if you want to enable interactive app notifications.
      *                {@code false} otherwise.
+     *
+     * @see TvInputService.Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
+     * @see android.media.tv.interactive.TvInteractiveAppView#setTvView(TvView)
      * @hide
      */
-    public void setIAppNotificationEnabled(boolean enabled) {
+    public void setInteractiveAppNotificationEnabled(boolean enabled) {
         if (mSession != null) {
-            mSession.setIAppNotificationEnabled(enabled);
+            mSession.setInteractiveAppNotificationEnabled(enabled);
         }
     }
 
@@ -1062,12 +1071,22 @@
         }
 
         /**
-         * This is called when the AIT info has been updated.
+         * This is called when the AIT (Application Information Table) info has been updated.
          *
          * @param aitInfo The current AIT info.
          * @hide
          */
-        public void onAitInfoUpdated(String inputId, AitInfo aitInfo) {
+        public void onAitInfoUpdated(@NonNull String inputId, @NonNull AitInfo aitInfo) {
+        }
+
+        /**
+         * This is called when signal strength is updated.
+         * @param inputId The ID of the TV input bound to this view.
+         * @param strength The current signal strength.
+         *
+         * @hide
+         */
+        public void onSignalStrength(String inputId, @TvInputManager.SignalStrength int strength) {
         }
 
         /**
@@ -1390,6 +1409,20 @@
         }
 
         @Override
+        public void onSignalStrength(Session session, int strength) {
+            if (DEBUG) {
+                Log.d(TAG, "onSignalStrength(strength=" + strength + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSignalStrength - session not created");
+                return;
+            }
+            if (mCallback != null) {
+                mCallback.onSignalStrength(mInputId, strength);
+            }
+        }
+
+        @Override
         public void onTuned(Session session, Uri channelUri) {
             if (DEBUG) {
                 Log.d(TAG, "onTuned(channelUri=" + channelUri + ")");
diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.aidl b/media/java/android/media/tv/interactive/AppLinkInfo.aidl
new file mode 100644
index 0000000..7c52d01
--- /dev/null
+++ b/media/java/android/media/tv/interactive/AppLinkInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+parcelable AppLinkInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.java b/media/java/android/media/tv/interactive/AppLinkInfo.java
new file mode 100644
index 0000000..5cce443
--- /dev/null
+++ b/media/java/android/media/tv/interactive/AppLinkInfo.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * App link information used by TV interactive app to launch Android apps.
+ * @hide
+ */
+public final class AppLinkInfo implements Parcelable {
+    private @NonNull String mPackageName;
+    private @NonNull String mClassName;
+    private @Nullable String mUriScheme;
+    private @Nullable String mUriHost;
+    private @Nullable String mUriPrefix;
+
+
+    /**
+     * Creates a new AppLinkInfo.
+     */
+    private AppLinkInfo(
+            @NonNull String packageName,
+            @NonNull String className,
+            @Nullable String uriScheme,
+            @Nullable String uriHost,
+            @Nullable String uriPrefix) {
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mClassName = className;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mClassName);
+        this.mUriScheme = uriScheme;
+        this.mUriHost = uriHost;
+        this.mUriPrefix = uriPrefix;
+    }
+
+    /**
+     * Gets package name of the App link.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Gets package class of the App link.
+     */
+    @NonNull
+    public String getClassName() {
+        return mClassName;
+    }
+
+    /**
+     * Gets URI scheme of the App link.
+     */
+    @Nullable
+    public String getUriScheme() {
+        return mUriScheme;
+    }
+
+    /**
+     * Gets URI host of the App link.
+     */
+    @Nullable
+    public String getUriHost() {
+        return mUriHost;
+    }
+
+    /**
+     * Gets URI prefix of the App link.
+     */
+    @Nullable
+    public String getUriPrefix() {
+        return mUriPrefix;
+    }
+
+    @Override
+    public String toString() {
+        return "AppLinkInfo { "
+                + "packageName = " + mPackageName + ", "
+                + "className = " + mClassName + ", "
+                + "uriScheme = " + mUriScheme + ", "
+                + "uriHost = " + mUriHost + ", "
+                + "uriPrefix = " + mUriPrefix
+                + " }";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mPackageName);
+        dest.writeString(mClassName);
+        dest.writeString(mUriScheme);
+        dest.writeString(mUriHost);
+        dest.writeString(mUriPrefix);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /* package-private */ AppLinkInfo(@NonNull Parcel in) {
+        String packageName = in.readString();
+        String className = in.readString();
+        String uriScheme = in.readString();
+        String uriHost = in.readString();
+        String uriPrefix = in.readString();
+
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mClassName = className;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mClassName);
+        this.mUriScheme = uriScheme;
+        this.mUriHost = uriHost;
+        this.mUriPrefix = uriPrefix;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<AppLinkInfo> CREATOR =
+            new Parcelable.Creator<AppLinkInfo>() {
+                @Override
+                public AppLinkInfo[] newArray(int size) {
+                    return new AppLinkInfo[size];
+                }
+
+                @Override
+                public AppLinkInfo createFromParcel(@NonNull Parcel in) {
+                    return new AppLinkInfo(in);
+                }
+            };
+
+    /**
+     * A builder for {@link AppLinkInfo}
+     */
+    public static final class Builder {
+        private @NonNull String mPackageName;
+        private @NonNull String mClassName;
+        private @Nullable String mUriScheme;
+        private @Nullable String mUriHost;
+        private @Nullable String mUriPrefix;
+
+        /**
+         * Creates a new Builder.
+         */
+        public Builder(
+                @NonNull String packageName,
+                @NonNull String className) {
+            mPackageName = packageName;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, mPackageName);
+            mClassName = className;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, mClassName);
+        }
+
+        /**
+         * Sets package name of the App link.
+         */
+        @NonNull
+        public Builder setPackageName(@NonNull String value) {
+            mPackageName = value;
+            return this;
+        }
+
+        /**
+         * Sets app name of the App link.
+         */
+        @NonNull
+        public Builder setClassName(@NonNull String value) {
+            mClassName = value;
+            return this;
+        }
+
+        /**
+         * Sets URI scheme of the App link.
+         */
+        @NonNull
+        public Builder setUriScheme(@Nullable String value) {
+            mUriScheme = value;
+            return this;
+        }
+
+        /**
+         * Sets URI host of the App link.
+         */
+        @NonNull
+        public Builder setUriHost(@Nullable String value) {
+            mUriHost = value;
+            return this;
+        }
+
+        /**
+         * Sets URI prefix of the App link.
+         */
+        @NonNull
+        public Builder setUriPrefix(@Nullable String value) {
+            mUriPrefix = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        @NonNull
+        public AppLinkInfo build() {
+            AppLinkInfo o = new AppLinkInfo(
+                    mPackageName,
+                    mClassName,
+                    mUriScheme,
+                    mUriHost,
+                    mUriPrefix);
+            return o;
+        }
+    }
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
deleted file mode 100644
index 892a800..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.graphics.Rect;
-import android.media.tv.AdRequest;
-import android.media.tv.BroadcastInfoRequest;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.InputChannel;
-
-/**
- * Interface a client of the ITvIAppManager implements, to identify itself and receive information
- * about changes to the state of each TV interactive application service.
- * @hide
- */
-oneway interface ITvIAppClient {
-    void onSessionCreated(in String iAppServiceId, IBinder token, in InputChannel channel, int seq);
-    void onSessionReleased(int seq);
-    void onLayoutSurface(int left, int top, int right, int bottom, int seq);
-    void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
-    void onRemoveBroadcastInfo(int id, int seq);
-    void onSessionStateChanged(int state, int seq);
-    void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
-    void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
-    void onSetVideoBounds(in Rect rect, int seq);
-    void onRequestCurrentChannelUri(int seq);
-    void onRequestCurrentChannelLcn(int seq);
-    void onRequestStreamVolume(int seq);
-    void onRequestTrackInfoList(int seq);
-    void onAdRequest(in AdRequest request, int Seq);
-}
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
deleted file mode 100644
index 23201fa..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.graphics.Rect;
-import android.media.tv.AdResponse;
-import android.media.tv.BroadcastInfoResponse;
-import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppClient;
-import android.media.tv.interactive.ITvIAppManagerCallback;
-import android.media.tv.interactive.TvIAppInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.Surface;
-
-/**
- * Interface to the TV interactive app service.
- * @hide
- */
-interface ITvIAppManager {
-    List<TvIAppInfo> getTvIAppServiceList(int userId);
-    void prepare(String tiasId, int type, int userId);
-    void notifyAppLinkInfo(String tiasId, in Bundle info, int userId);
-    void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
-    void startIApp(in IBinder sessionToken, int userId);
-    void stopIApp(in IBinder sessionToken, int userId);
-    void createBiInteractiveApp(
-            in IBinder sessionToken, in Uri biIAppUri, in Bundle params, int userId);
-    void destroyBiInteractiveApp(in IBinder sessionToken, in String biIAppId, int userId);
-    void sendCurrentChannelUri(in IBinder sessionToken, in Uri channelUri, int userId);
-    void sendCurrentChannelLcn(in IBinder sessionToken, int lcn, int userId);
-    void sendStreamVolume(in IBinder sessionToken, float volume, int userId);
-    void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
-    void createSession(
-            in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
-    void releaseSession(in IBinder sessionToken, int userId);
-    void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId);
-    void notifyTrackSelected(in IBinder sessionToken, int type, in String trackId, int userId);
-    void notifyTracksChanged(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
-    void setSurface(in IBinder sessionToken, in Surface surface, int userId);
-    void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
-            int userId);
-    void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
-            int UserId);
-    void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId);
-
-    void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
-            int userId);
-    void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId);
-    void removeMediaView(in IBinder sessionToken, int userId);
-
-    void registerCallback(in ITvIAppManagerCallback callback, int userId);
-    void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
-}
diff --git a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl
deleted file mode 100644
index d5e0c63..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.media.tv.interactive.TvIAppInfo;
-
-/**
- * Interface to receive callbacks from ITvIAppManager regardless of sessions.
- * @hide
- */
-interface ITvIAppManagerCallback {
-    void onIAppServiceAdded(in String iAppServiceId);
-    void onIAppServiceRemoved(in String iAppServiceId);
-    void onIAppServiceUpdated(in String iAppServiceId);
-    void onTvIAppInfoUpdated(in TvIAppInfo tvIAppInfo);
-    void onStateChanged(in String iAppServiceId, int type, int state);
-}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
deleted file mode 100644
index 8acb75f..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.media.tv.interactive.ITvIAppServiceCallback;
-import android.media.tv.interactive.ITvIAppSessionCallback;
-import android.os.Bundle;
-import android.view.InputChannel;
-
-/**
- * Top-level interface to a TV IApp component (implemented in a Service). It's used for
- * TvIAppManagerService to communicate with TvIAppService.
- * @hide
- */
-oneway interface ITvIAppService {
-    void registerCallback(in ITvIAppServiceCallback callback);
-    void unregisterCallback(in ITvIAppServiceCallback callback);
-    void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
-            in String iAppServiceId, int type);
-    void prepare(int type);
-    void notifyAppLinkInfo(in Bundle info);
-    void sendAppLinkCommand(in Bundle command);
-}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
deleted file mode 100644
index fec7d78..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-/**
- * Helper interface for ITvIAppService to allow the TvIAppService to notify the
- * TvIAppManagerService.
- * @hide
- */
-oneway interface ITvIAppServiceCallback {
-    void onStateChanged(int type, int state);
-}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
deleted file mode 100644
index 52f9a87..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.graphics.Rect;
-import android.media.tv.BroadcastInfoResponse;
-import android.net.Uri;
-import android.media.tv.AdResponse;
-import android.media.tv.BroadcastInfoResponse;
-import android.media.tv.TvTrackInfo;
-import android.os.Bundle;
-import android.view.Surface;
-
-/**
- * Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
- * @hide
- */
-oneway interface ITvIAppSession {
-    void startIApp();
-    void stopIApp();
-    void createBiInteractiveApp(in Uri biIAppUri, in Bundle params);
-    void destroyBiInteractiveApp(in String biIAppId);
-    void sendCurrentChannelUri(in Uri channelUri);
-    void sendCurrentChannelLcn(int lcn);
-    void sendStreamVolume(float volume);
-    void sendTrackInfoList(in List<TvTrackInfo> tracks);
-    void release();
-    void notifyTuned(in Uri channelUri);
-    void notifyTrackSelected(int type, in String trackId);
-    void notifyTracksChanged(in List<TvTrackInfo> tracks);
-    void setSurface(in Surface surface);
-    void dispatchSurfaceChanged(int format, int width, int height);
-    void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
-    void notifyAdResponse(in AdResponse response);
-
-    void createMediaView(in IBinder windowToken, in Rect frame);
-    void relayoutMediaView(in Rect frame);
-    void removeMediaView();
-}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
deleted file mode 100644
index 9b9e6af..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.graphics.Rect;
-import android.media.tv.AdRequest;
-import android.media.tv.BroadcastInfoRequest;
-import android.media.tv.interactive.ITvIAppSession;
-import android.net.Uri;
-import android.os.Bundle;
-
-/**
- * Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
- * there is a related event.
- * @hide
- */
-oneway interface ITvIAppSessionCallback {
-    void onSessionCreated(in ITvIAppSession session);
-    void onLayoutSurface(int left, int top, int right, int bottom);
-    void onBroadcastInfoRequest(in BroadcastInfoRequest request);
-    void onRemoveBroadcastInfo(int id);
-    void onSessionStateChanged(int state);
-    void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
-    void onCommandRequest(in String cmdType, in Bundle parameters);
-    void onSetVideoBounds(in Rect rect);
-    void onRequestCurrentChannelUri();
-    void onRequestCurrentChannelLcn();
-    void onRequestStreamVolume();
-    void onRequestTrackInfoList();
-    void onAdRequest(in AdRequest request);
-}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
new file mode 100644
index 0000000..a3e58d1
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.BroadcastInfoRequest;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.InputChannel;
+
+/**
+ * Interface a client of the ITvInteractiveAppManager implements, to identify itself and receive
+ * information about changes to the state of each TV interactive application service.
+ * @hide
+ */
+oneway interface ITvInteractiveAppClient {
+    void onSessionCreated(in String iAppServiceId, IBinder token, in InputChannel channel, int seq);
+    void onSessionReleased(int seq);
+    void onLayoutSurface(int left, int top, int right, int bottom, int seq);
+    void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
+    void onRemoveBroadcastInfo(int id, int seq);
+    void onSessionStateChanged(int state, int err, int seq);
+    void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
+    void onTeletextAppStateChanged(int state, int seq);
+    void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
+    void onSetVideoBounds(in Rect rect, int seq);
+    void onRequestCurrentChannelUri(int seq);
+    void onRequestCurrentChannelLcn(int seq);
+    void onRequestStreamVolume(int seq);
+    void onRequestTrackInfoList(int seq);
+    void onRequestCurrentTvInputId(int seq);
+    void onAdRequest(in AdRequest request, int Seq);
+}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
new file mode 100644
index 0000000..aaabe34
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.graphics.Rect;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.AppLinkInfo;
+import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
+import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Surface;
+
+/**
+ * Interface to the TV interactive app service.
+ * @hide
+ */
+interface ITvInteractiveAppManager {
+    List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
+    void prepare(String tiasId, int type, int userId);
+    void registerAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
+    void unregisterAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
+    void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
+    void startInteractiveApp(in IBinder sessionToken, int userId);
+    void stopInteractiveApp(in IBinder sessionToken, int userId);
+    void resetInteractiveApp(in IBinder sessionToken, int userId);
+    void createBiInteractiveApp(
+            in IBinder sessionToken, in Uri biIAppUri, in Bundle params, int userId);
+    void destroyBiInteractiveApp(in IBinder sessionToken, in String biIAppId, int userId);
+    void setTeletextAppEnabled(in IBinder sessionToken, boolean enable, int userId);
+    void sendCurrentChannelUri(in IBinder sessionToken, in Uri channelUri, int userId);
+    void sendCurrentChannelLcn(in IBinder sessionToken, int lcn, int userId);
+    void sendStreamVolume(in IBinder sessionToken, float volume, int userId);
+    void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
+    void sendCurrentTvInputId(in IBinder sessionToken, in String inputId, int userId);
+    void createSession(in ITvInteractiveAppClient client, in String iAppServiceId, int type,
+            int seq, int userId);
+    void releaseSession(in IBinder sessionToken, int userId);
+    void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId);
+    void notifyTrackSelected(in IBinder sessionToken, int type, in String trackId, int userId);
+    void notifyTracksChanged(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
+    void notifyVideoAvailable(in IBinder sessionToken, int userId);
+    void notifyVideoUnavailable(in IBinder sessionToken, int reason, int userId);
+    void notifyContentAllowed(in IBinder sessionToken, int userId);
+    void notifyContentBlocked(in IBinder sessionToken, in String rating, int userId);
+    void notifySignalStrength(in IBinder sessionToken, int stength, int userId);
+    void setSurface(in IBinder sessionToken, in Surface surface, int userId);
+    void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
+            int userId);
+    void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
+            int UserId);
+    void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId);
+
+    void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
+            int userId);
+    void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId);
+    void removeMediaView(in IBinder sessionToken, int userId);
+
+    void registerCallback(in ITvInteractiveAppManagerCallback callback, int userId);
+    void unregisterCallback(in ITvInteractiveAppManagerCallback callback, int userId);
+}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
new file mode 100644
index 0000000..23be4c6
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.media.tv.interactive.TvInteractiveAppInfo;
+
+/**
+ * Interface to receive callbacks from ITvInteractiveAppManager regardless of sessions.
+ * @hide
+ */
+interface ITvInteractiveAppManagerCallback {
+    void onInteractiveAppServiceAdded(in String iAppServiceId);
+    void onInteractiveAppServiceRemoved(in String iAppServiceId);
+    void onInteractiveAppServiceUpdated(in String iAppServiceId);
+    void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo);
+    void onStateChanged(in String iAppServiceId, int type, int state, int err);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
new file mode 100644
index 0000000..b6d518f
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.media.tv.interactive.AppLinkInfo;
+import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
+import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
+import android.os.Bundle;
+import android.view.InputChannel;
+
+/**
+ * Top-level interface to a TV Interactive App component (implemented in a Service). It's used for
+ * TvInteractiveAppManagerService to communicate with TvInteractiveAppService.
+ * @hide
+ */
+oneway interface ITvInteractiveAppService {
+    void registerCallback(in ITvInteractiveAppServiceCallback callback);
+    void unregisterCallback(in ITvInteractiveAppServiceCallback callback);
+    void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback,
+            in String iAppServiceId, int type);
+    void prepare(int type);
+    void registerAppLinkInfo(in AppLinkInfo info);
+    void unregisterAppLinkInfo(in AppLinkInfo info);
+    void sendAppLinkCommand(in Bundle command);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
new file mode 100644
index 0000000..970b943
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+/**
+ * Helper interface for ITvInteractiveAppService to allow the TvInteractiveAppService to notify the
+ * TvInteractiveAppManagerService.
+ * @hide
+ */
+oneway interface ITvInteractiveAppServiceCallback {
+    void onStateChanged(int type, int state, int error);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
new file mode 100644
index 0000000..c449d2475
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.graphics.Rect;
+import android.media.tv.BroadcastInfoResponse;
+import android.net.Uri;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvTrackInfo;
+import android.os.Bundle;
+import android.view.Surface;
+
+/**
+ * Sub-interface of ITvInteractiveAppService.aidl which is created per session and has its own
+ * context.
+ * @hide
+ */
+oneway interface ITvInteractiveAppSession {
+    void startInteractiveApp();
+    void stopInteractiveApp();
+    void resetInteractiveApp();
+    void createBiInteractiveApp(in Uri biIAppUri, in Bundle params);
+    void destroyBiInteractiveApp(in String biIAppId);
+    void setTeletextAppEnabled(boolean enable);
+    void sendCurrentChannelUri(in Uri channelUri);
+    void sendCurrentChannelLcn(int lcn);
+    void sendStreamVolume(float volume);
+    void sendTrackInfoList(in List<TvTrackInfo> tracks);
+    void sendCurrentTvInputId(in String inputId);
+    void release();
+    void notifyTuned(in Uri channelUri);
+    void notifyTrackSelected(int type, in String trackId);
+    void notifyTracksChanged(in List<TvTrackInfo> tracks);
+    void notifyVideoAvailable();
+    void notifyVideoUnavailable(int reason);
+    void notifyContentAllowed();
+    void notifyContentBlocked(in String rating);
+    void notifySignalStrength(int strength);
+    void setSurface(in Surface surface);
+    void dispatchSurfaceChanged(int format, int width, int height);
+    void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
+    void notifyAdResponse(in AdResponse response);
+
+    void createMediaView(in IBinder windowToken, in Rect frame);
+    void relayoutMediaView(in Rect frame);
+    void removeMediaView();
+}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
new file mode 100644
index 0000000..385f0d4
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.interactive.ITvInteractiveAppSession;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Helper interface for ITvInteractiveAppSession to allow TvInteractiveAppService to notify the
+ * system service when there is a related event.
+ * @hide
+ */
+oneway interface ITvInteractiveAppSessionCallback {
+    void onSessionCreated(in ITvInteractiveAppSession session);
+    void onLayoutSurface(int left, int top, int right, int bottom);
+    void onBroadcastInfoRequest(in BroadcastInfoRequest request);
+    void onRemoveBroadcastInfo(int id);
+    void onSessionStateChanged(int state, int err);
+    void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
+    void onTeletextAppStateChanged(int state);
+    void onCommandRequest(in String cmdType, in Bundle parameters);
+    void onSetVideoBounds(in Rect rect);
+    void onRequestCurrentChannelUri();
+    void onRequestCurrentChannelLcn();
+    void onRequestStreamVolume();
+    void onRequestTrackInfoList();
+    void onRequestCurrentTvInputId();
+    void onAdRequest(in AdRequest request);
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/media/java/android/media/tv/interactive/TvIAppInfo.aidl
deleted file mode 100644
index 6041460..0000000
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-parcelable TvIAppInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java
deleted file mode 100644
index 79d94cc..0000000
--- a/media/java/android/media/tv/interactive/TvIAppInfo.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.annotation.NonNull;
-import android.annotation.StringDef;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is used to specify meta information of a TV interactive app.
- * @hide
- */
-public final class TvIAppInfo implements Parcelable {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "TvIAppInfo";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
-            INTERACTIVE_APP_TYPE_HBBTV,
-            INTERACTIVE_APP_TYPE_ATSC,
-            INTERACTIVE_APP_TYPE_GINGA,
-    })
-    @interface InteractiveAppType {}
-
-    /** HbbTV interactive app type */
-    public static final String INTERACTIVE_APP_TYPE_HBBTV = "hbbtv";
-    /** ATSC interactive app type */
-    public static final String INTERACTIVE_APP_TYPE_ATSC = "atsc";
-    /** Ginga interactive app type */
-    public static final String INTERACTIVE_APP_TYPE_GINGA = "ginga";
-
-    private final ResolveInfo mService;
-    private final String mId;
-    private List<String> mTypes = new ArrayList<>();
-
-    private TvIAppInfo(ResolveInfo service, String id, List<String> types) {
-        mService = service;
-        mId = id;
-        mTypes = types;
-    }
-
-    private TvIAppInfo(@NonNull Parcel in) {
-        mService = ResolveInfo.CREATOR.createFromParcel(in);
-        mId = in.readString();
-        in.readStringList(mTypes);
-    }
-
-    public static final @NonNull Creator<TvIAppInfo> CREATOR = new Creator<TvIAppInfo>() {
-        @Override
-        public TvIAppInfo createFromParcel(Parcel in) {
-            return new TvIAppInfo(in);
-        }
-
-        @Override
-        public TvIAppInfo[] newArray(int size) {
-            return new TvIAppInfo[size];
-        }
-    };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        mService.writeToParcel(dest, flags);
-        dest.writeString(mId);
-        dest.writeStringList(mTypes);
-    }
-
-    @NonNull
-    public String getId() {
-        return mId;
-    }
-
-    /**
-     * Returns the component of the TV IApp service.
-     * @hide
-     */
-    public ComponentName getComponent() {
-        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
-    }
-
-    /**
-     * Returns the information of the service that implements this TV IApp service.
-     */
-    public ServiceInfo getServiceInfo() {
-        return mService.serviceInfo;
-    }
-
-    /**
-     * Gets supported interactive app types
-     */
-    public List<String> getSupportedTypes() {
-        return new ArrayList<>(mTypes);
-    }
-
-    /**
-     * A convenience builder for creating {@link TvIAppInfo} objects.
-     */
-    public static final class Builder {
-        private static final String XML_START_TAG_NAME = "tv-iapp";
-        private final Context mContext;
-        private final ResolveInfo mResolveInfo;
-        private final List<String> mTypes = new ArrayList<>();
-
-        /**
-         * Constructs a new builder for {@link TvIAppInfo}.
-         *
-         * @param context A Context of the application package implementing this class.
-         * @param component The name of the application component to be used for the
-         *                  {@link TvIAppService}.
-         */
-        public Builder(@NonNull Context context, @NonNull ComponentName component) {
-            if (context == null) {
-                throw new IllegalArgumentException("context cannot be null.");
-            }
-            Intent intent = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
-            mResolveInfo = context.getPackageManager().resolveService(intent,
-                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
-            if (mResolveInfo == null) {
-                throw new IllegalArgumentException("Invalid component. Can't find the service.");
-            }
-            mContext = context;
-        }
-
-        /**
-         * Creates a {@link TvIAppInfo} instance with the specified fields. Most of the information
-         * is obtained by parsing the AndroidManifest and {@link TvIAppService#SERVICE_META_DATA}
-         * for the {@link TvIAppService} this TV IApp implements.
-         *
-         * @return TvIAppInfo containing information about this TV IApp service.
-         */
-        @NonNull
-        public TvIAppInfo build() {
-            ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
-                    mResolveInfo.serviceInfo.name);
-            String id;
-            id = generateIAppServiceId(componentName);
-            parseServiceMetadata();
-            return new TvIAppInfo(mResolveInfo, id, mTypes);
-        }
-
-        private static String generateIAppServiceId(ComponentName name) {
-            return name.flattenToShortString();
-        }
-
-        private void parseServiceMetadata() {
-            ServiceInfo si = mResolveInfo.serviceInfo;
-            PackageManager pm = mContext.getPackageManager();
-            try (XmlResourceParser parser =
-                         si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
-                if (parser == null) {
-                    throw new IllegalStateException("No " + TvIAppService.SERVICE_META_DATA
-                            + " meta-data found for " + si.name);
-                }
-
-                Resources res = pm.getResourcesForApplication(si.applicationInfo);
-                AttributeSet attrs = Xml.asAttributeSet(parser);
-
-                int type;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && type != XmlPullParser.START_TAG) {
-                    // move to the START_TAG
-                }
-
-                String nodeName = parser.getName();
-                if (!XML_START_TAG_NAME.equals(nodeName)) {
-                    throw new IllegalStateException("Meta-data does not start with "
-                            + XML_START_TAG_NAME + " tag for " + si.name);
-                }
-
-                TypedArray sa = res.obtainAttributes(attrs,
-                        com.android.internal.R.styleable.TvIAppService);
-                CharSequence[] types = sa.getTextArray(
-                        com.android.internal.R.styleable.TvIAppService_supportedTypes);
-                for (CharSequence cs : types) {
-                    mTypes.add(cs.toString().toLowerCase());
-                }
-
-                sa.recycle();
-            } catch (IOException | XmlPullParserException e) {
-                throw new IllegalStateException(
-                        "Failed reading meta-data for " + si.packageName, e);
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new IllegalStateException("No resources found for " + si.packageName, e);
-            }
-        }
-    }
-}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
deleted file mode 100644
index d1fd1df..0000000
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ /dev/null
@@ -1,1495 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemService;
-import android.content.Context;
-import android.graphics.Rect;
-import android.media.tv.AdRequest;
-import android.media.tv.AdResponse;
-import android.media.tv.BroadcastInfoRequest;
-import android.media.tv.BroadcastInfoResponse;
-import android.media.tv.TvInputManager;
-import android.media.tv.TvTrackInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Pools;
-import android.util.SparseArray;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventSender;
-import android.view.Surface;
-import android.view.View;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Central system API to the overall TV interactive application framework (TIAF) architecture, which
- * arbitrates interaction between applications and interactive apps.
- */
-@SystemService(Context.TV_IAPP_SERVICE)
-public final class TvIAppManager {
-    // TODO: cleanup and unhide public APIs
-    private static final String TAG = "TvIAppManager";
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = false, prefix = "TV_IAPP_RTE_STATE_", value = {
-            TV_IAPP_RTE_STATE_UNREALIZED,
-            TV_IAPP_RTE_STATE_PREPARING,
-            TV_IAPP_RTE_STATE_READY,
-            TV_IAPP_RTE_STATE_ERROR})
-    public @interface TvIAppRteState {}
-
-    /**
-     * Unrealized state of interactive app RTE.
-     * @hide
-     */
-    public static final int TV_IAPP_RTE_STATE_UNREALIZED = 1;
-    /**
-     * Preparing state of interactive app RTE.
-     * @hide
-     */
-    public static final int TV_IAPP_RTE_STATE_PREPARING = 2;
-    /**
-     * Ready state of interactive app RTE.
-     * @hide
-     */
-    public static final int TV_IAPP_RTE_STATE_READY = 3;
-    /**
-     * Error state of interactive app RTE.
-     * @hide
-     */
-    public static final int TV_IAPP_RTE_STATE_ERROR = 4;
-
-    /**
-     * Key for package name in app link.
-     * <p>Type: String
-     *
-     * @see #notifyAppLinkInfo(String, Bundle)
-     * @see #sendAppLinkCommand(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_PACKAGE_NAME = "package_name";
-
-    /**
-     * Key for class name in app link.
-     * <p>Type: String
-     *
-     * @see #notifyAppLinkInfo(String, Bundle)
-     * @see #sendAppLinkCommand(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_CLASS_NAME = "class_name";
-
-    /**
-     * Key for URI scheme in app link.
-     * <p>Type: String
-     *
-     * @see #notifyAppLinkInfo(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_URI_SCHEME = "uri_scheme";
-
-    /**
-     * Key for URI host in app link.
-     * <p>Type: String
-     *
-     * @see #notifyAppLinkInfo(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_URI_HOST = "uri_host";
-
-    /**
-     * Key for URI prefix in app link.
-     * <p>Type: String
-     *
-     * @see #notifyAppLinkInfo(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_URI_PREFIX = "uri_prefix";
-
-    /**
-     * Key for command type in app link command.
-     * <p>Type: String
-     *
-     * @see #sendAppLinkCommand(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_COMMAND_TYPE = "command_type";
-
-    /**
-     * Key for service ID in app link command.
-     * <p>Type: String
-     *
-     * @see #sendAppLinkCommand(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_SERVICE_ID = "service_id";
-
-    /**
-     * Key for back URI in app link command.
-     * <p>Type: String
-     *
-     * @see #sendAppLinkCommand(String, Bundle)
-     * @hide
-     */
-    public static final String KEY_BACK_URI = "back_uri";
-
-    private final ITvIAppManager mService;
-    private final int mUserId;
-
-    // A mapping from the sequence number of a session to its SessionCallbackRecord.
-    private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
-            new SparseArray<>();
-
-    // @GuardedBy("mLock")
-    private final List<TvIAppCallbackRecord> mCallbackRecords = new LinkedList<>();
-
-    // A sequence number for the next session to be created. Should be protected by a lock
-    // {@code mSessionCallbackRecordMap}.
-    private int mNextSeq;
-
-    private final Object mLock = new Object();
-
-    private final ITvIAppClient mClient;
-
-    /** @hide */
-    public TvIAppManager(ITvIAppManager service, int userId) {
-        mService = service;
-        mUserId = userId;
-        mClient = new ITvIAppClient.Stub() {
-            @Override
-            public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel,
-                    int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for " + token);
-                        return;
-                    }
-                    Session session = null;
-                    if (token != null) {
-                        session = new Session(token, channel, mService, mUserId, seq,
-                                mSessionCallbackRecordMap);
-                    } else {
-                        mSessionCallbackRecordMap.delete(seq);
-                    }
-                    record.postSessionCreated(session);
-                }
-            }
-
-            @Override
-            public void onSessionReleased(int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    mSessionCallbackRecordMap.delete(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq:" + seq);
-                        return;
-                    }
-                    record.mSession.releaseInternal();
-                    record.postSessionReleased();
-                }
-            }
-
-            @Override
-            public void onLayoutSurface(int left, int top, int right, int bottom, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postLayoutSurface(left, top, right, bottom);
-                }
-            }
-
-            @Override
-            public void onBroadcastInfoRequest(BroadcastInfoRequest request, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postBroadcastInfoRequest(request);
-                }
-            }
-
-            @Override
-            public void onRemoveBroadcastInfo(int requestId, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postRemoveBroadcastInfo(requestId);
-                }
-            }
-
-            @Override
-            public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
-                    Bundle parameters, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postCommandRequest(cmdType, parameters);
-                }
-            }
-
-            @Override
-            public void onSetVideoBounds(Rect rect, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postSetVideoBounds(rect);
-                }
-            }
-
-            @Override
-            public void onAdRequest(AdRequest request, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postAdRequest(request);
-                }
-            }
-
-            @Override
-            public void onRequestCurrentChannelUri(int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postRequestCurrentChannelUri();
-                }
-            }
-
-            @Override
-            public void onRequestCurrentChannelLcn(int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postRequestCurrentChannelLcn();
-                }
-            }
-
-            @Override
-            public void onRequestStreamVolume(int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postRequestStreamVolume();
-                }
-            }
-
-            @Override
-            public void onRequestTrackInfoList(int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postRequestTrackInfoList();
-                }
-            }
-
-            @Override
-            public void onSessionStateChanged(int state, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postSessionStateChanged(state);
-                }
-            }
-
-            @Override
-            public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId, int seq) {
-                synchronized (mSessionCallbackRecordMap) {
-                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
-                    if (record == null) {
-                        Log.e(TAG, "Callback not found for seq " + seq);
-                        return;
-                    }
-                    record.postBiInteractiveAppCreated(biIAppUri, biIAppId);
-                }
-            }
-        };
-        ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() {
-            @Override
-            public void onIAppServiceAdded(String iAppServiceId) {
-                synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postIAppServiceAdded(iAppServiceId);
-                    }
-                }
-            }
-
-            @Override
-            public void onIAppServiceRemoved(String iAppServiceId) {
-                synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postIAppServiceRemoved(iAppServiceId);
-                    }
-                }
-            }
-
-            @Override
-            public void onIAppServiceUpdated(String iAppServiceId) {
-                synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postIAppServiceUpdated(iAppServiceId);
-                    }
-                }
-            }
-
-            @Override
-            public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) {
-                // TODO: add public API updateIAppInfo()
-                synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postTvIAppInfoUpdated(iAppInfo);
-                    }
-                }
-            }
-
-            @Override
-            public void onStateChanged(String iAppServiceId, int type, int state) {
-                synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postStateChanged(iAppServiceId, type, state);
-                    }
-                }
-            }
-        };
-        try {
-            if (mService != null) {
-                mService.registerCallback(managerCallback, mUserId);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Callback used to monitor status of the TV IApp.
-     * @hide
-     */
-    public abstract static class TvIAppCallback {
-        /**
-         * This is called when a TV IApp service is added to the system.
-         *
-         * <p>Normally it happens when the user installs a new TV IApp service package that
-         * implements {@link TvIAppService} interface.
-         *
-         * @param iAppServiceId The ID of the TV IApp service.
-         */
-        public void onIAppServiceAdded(@NonNull String iAppServiceId) {
-        }
-
-        /**
-         * This is called when a TV IApp service is removed from the system.
-         *
-         * <p>Normally it happens when the user uninstalls the previously installed TV IApp service
-         * package.
-         *
-         * @param iAppServiceId The ID of the TV IApp service.
-         */
-        public void onIAppServiceRemoved(@NonNull String iAppServiceId) {
-        }
-
-        /**
-         * This is called when a TV IApp service is updated on the system.
-         *
-         * <p>Normally it happens when a previously installed TV IApp service package is
-         * re-installed or a newer version of the package exists becomes available/unavailable.
-         *
-         * @param iAppServiceId The ID of the TV IApp service.
-         */
-        public void onIAppServiceUpdated(@NonNull String iAppServiceId) {
-        }
-
-        /**
-         * This is called when the information about an existing TV IApp service has been updated.
-         *
-         * <p>Because the system automatically creates a <code>TvIAppInfo</code> object for each TV
-         * IApp service based on the information collected from the
-         * <code>AndroidManifest.xml</code>, this method is only called back when such information
-         * has changed dynamically.
-         *
-         * @param iAppInfo The <code>TvIAppInfo</code> object that contains new information.
-         */
-        public void onTvIAppInfoUpdated(@NonNull TvIAppInfo iAppInfo) {
-        }
-
-        /**
-         * This is called when the state of the interactive app service is changed.
-         * @hide
-         */
-        public void onTvIAppServiceStateChanged(
-                @NonNull String iAppServiceId, int type, @TvIAppRteState int state) {
-        }
-    }
-
-    private static final class TvIAppCallbackRecord {
-        private final TvIAppCallback mCallback;
-        private final Handler mHandler;
-
-        TvIAppCallbackRecord(TvIAppCallback callback, Handler handler) {
-            mCallback = callback;
-            mHandler = handler;
-        }
-
-        public TvIAppCallback getCallback() {
-            return mCallback;
-        }
-
-        public void postIAppServiceAdded(final String iAppServiceId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mCallback.onIAppServiceAdded(iAppServiceId);
-                }
-            });
-        }
-
-        public void postIAppServiceRemoved(final String iAppServiceId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mCallback.onIAppServiceRemoved(iAppServiceId);
-                }
-            });
-        }
-
-        public void postIAppServiceUpdated(final String iAppServiceId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mCallback.onIAppServiceUpdated(iAppServiceId);
-                }
-            });
-        }
-
-        public void postTvIAppInfoUpdated(final TvIAppInfo iAppInfo) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mCallback.onTvIAppInfoUpdated(iAppInfo);
-                }
-            });
-        }
-
-        public void postStateChanged(String iAppServiceId, int type, int state) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mCallback.onTvIAppServiceStateChanged(iAppServiceId, type, state);
-                }
-            });
-        }
-    }
-
-    /**
-     * Creates a {@link Session} for a given TV interactive application.
-     *
-     * <p>The number of sessions that can be created at the same time is limited by the capability
-     * of the given interactive application.
-     *
-     * @param iAppServiceId The ID of the interactive application.
-     * @param type the type of the interactive application.
-     * @param callback A callback used to receive the created session.
-     * @param handler A {@link Handler} that the session creation will be delivered to.
-     * @hide
-     */
-    public void createSession(@NonNull String iAppServiceId, int type,
-            @NonNull final SessionCallback callback, @NonNull Handler handler) {
-        createSessionInternal(iAppServiceId, type, callback, handler);
-    }
-
-    private void createSessionInternal(String iAppServiceId, int type, SessionCallback callback,
-            Handler handler) {
-        Preconditions.checkNotNull(iAppServiceId);
-        Preconditions.checkNotNull(callback);
-        Preconditions.checkNotNull(handler);
-        SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
-        synchronized (mSessionCallbackRecordMap) {
-            int seq = mNextSeq++;
-            mSessionCallbackRecordMap.put(seq, record);
-            try {
-                mService.createSession(mClient, iAppServiceId, type, seq, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
-     * Returns the complete list of TV IApp service on the system.
-     *
-     * @return List of {@link TvIAppInfo} for each TV IApp service that describes its meta
-     *         information.
-     * @hide
-     */
-    @NonNull
-    public List<TvIAppInfo> getTvIAppServiceList() {
-        try {
-            return mService.getTvIAppServiceList(mUserId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Prepares TV IApp service for the given type.
-     * @hide
-     */
-    public void prepare(@NonNull String tvIAppServiceId, int type) {
-        try {
-            mService.prepare(tvIAppServiceId, type, mUserId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Notifies app link info.
-     * @hide
-     */
-    public void notifyAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
-        try {
-            mService.notifyAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Sends app link command.
-     * @hide
-     */
-    public void sendAppLinkCommand(String tvIAppServiceId, Bundle command) {
-        try {
-            mService.sendAppLinkCommand(tvIAppServiceId, command, mUserId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Registers a {@link TvIAppManager.TvIAppCallback}.
-     *
-     * @param callback A callback used to monitor status of the TV IApp services.
-     * @param handler A {@link Handler} that the status change will be delivered to.
-     * @hide
-     */
-    public void registerCallback(@NonNull TvIAppCallback callback, @NonNull Handler handler) {
-        Preconditions.checkNotNull(callback);
-        Preconditions.checkNotNull(handler);
-        synchronized (mLock) {
-            mCallbackRecords.add(new TvIAppCallbackRecord(callback, handler));
-        }
-    }
-
-    /**
-     * Unregisters the existing {@link TvIAppManager.TvIAppCallback}.
-     *
-     * @param callback The existing callback to remove.
-     * @hide
-     */
-    public void unregisterCallback(@NonNull final TvIAppCallback callback) {
-        Preconditions.checkNotNull(callback);
-        synchronized (mLock) {
-            for (Iterator<TvIAppCallbackRecord> it = mCallbackRecords.iterator();
-                    it.hasNext(); ) {
-                TvIAppCallbackRecord record = it.next();
-                if (record.getCallback() == callback) {
-                    it.remove();
-                    break;
-                }
-            }
-        }
-    }
-
-    /**
-     * The Session provides the per-session functionality of interactive app.
-     * @hide
-     */
-    public static final class Session {
-        static final int DISPATCH_IN_PROGRESS = -1;
-        static final int DISPATCH_NOT_HANDLED = 0;
-        static final int DISPATCH_HANDLED = 1;
-
-        private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
-
-        private final ITvIAppManager mService;
-        private final int mUserId;
-        private final int mSeq;
-        private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
-
-        // For scheduling input event handling on the main thread. This also serves as a lock to
-        // protect pending input events and the input channel.
-        private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
-
-        private TvInputManager.Session mInputSession;
-        private final Pools.Pool<PendingEvent> mPendingEventPool = new Pools.SimplePool<>(20);
-        private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
-
-        private IBinder mToken;
-        private TvInputEventSender mSender;
-        private InputChannel mInputChannel;
-
-        private Session(IBinder token, InputChannel channel, ITvIAppManager service, int userId,
-                int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
-            mToken = token;
-            mInputChannel = channel;
-            mService = service;
-            mUserId = userId;
-            mSeq = seq;
-            mSessionCallbackRecordMap = sessionCallbackRecordMap;
-        }
-
-        public TvInputManager.Session getInputSession() {
-            return mInputSession;
-        }
-
-        public void setInputSession(TvInputManager.Session inputSession) {
-            mInputSession = inputSession;
-        }
-
-        void startIApp() {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.startIApp(mToken, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void stopIApp() {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.stopIApp(mToken, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void createBiInteractiveApp(Uri biIAppUri, Bundle params) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.createBiInteractiveApp(mToken, biIAppUri, params, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void destroyBiInteractiveApp(String biIAppId) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.destroyBiInteractiveApp(mToken, biIAppId, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void sendCurrentChannelUri(@Nullable Uri channelUri) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.sendCurrentChannelUri(mToken, channelUri, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void sendCurrentChannelLcn(int lcn) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.sendCurrentChannelLcn(mToken, lcn, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void sendStreamVolume(float volume) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.sendStreamVolume(mToken, volume, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.sendTrackInfoList(mToken, tracks, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Sets the {@link android.view.Surface} for this session.
-         *
-         * @param surface A {@link android.view.Surface} used to render video.
-         */
-        public void setSurface(Surface surface) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            // surface can be null.
-            try {
-                mService.setSurface(mToken, surface, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Creates a media view. Once the media view is created, {@link #relayoutMediaView}
-         * should be called whenever the layout of its containing view is changed.
-         * {@link #removeMediaView()} should be called to remove the media view.
-         * Since a session can have only one media view, this method should be called only once
-         * or it can be called again after calling {@link #removeMediaView()}.
-         *
-         * @param view A view for interactive app.
-         * @param frame A position of the media view.
-         * @throws IllegalStateException if {@code view} is not attached to a window.
-         */
-        void createMediaView(@NonNull View view, @NonNull Rect frame) {
-            Preconditions.checkNotNull(view);
-            Preconditions.checkNotNull(frame);
-            if (view.getWindowToken() == null) {
-                throw new IllegalStateException("view must be attached to a window");
-            }
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.createMediaView(mToken, view.getWindowToken(), frame, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Relayouts the current media view.
-         *
-         * @param frame A new position of the media view.
-         */
-        void relayoutMediaView(@NonNull Rect frame) {
-            Preconditions.checkNotNull(frame);
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.relayoutMediaView(mToken, frame, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Removes the current media view.
-         */
-        void removeMediaView() {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.removeMediaView(mToken, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Notifies of any structural changes (format or size) of the surface passed in
-         * {@link #setSurface}.
-         *
-         * @param format The new PixelFormat of the surface.
-         * @param width The new width of the surface.
-         * @param height The new height of the surface.
-         */
-        public void dispatchSurfaceChanged(int format, int width, int height) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Dispatches an input event to this session.
-         *
-         * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
-         * @param token A token used to identify the input event later in the callback.
-         * @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
-         * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
-         *            {@code null}.
-         * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
-         *         {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
-         *         {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
-         *         be invoked later.
-         * @hide
-         */
-        public int dispatchInputEvent(@NonNull InputEvent event, Object token,
-                @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
-            Preconditions.checkNotNull(event);
-            Preconditions.checkNotNull(callback);
-            Preconditions.checkNotNull(handler);
-            synchronized (mHandler) {
-                if (mInputChannel == null) {
-                    return DISPATCH_NOT_HANDLED;
-                }
-                PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
-                if (Looper.myLooper() == Looper.getMainLooper()) {
-                    // Already running on the main thread so we can send the event immediately.
-                    return sendInputEventOnMainLooperLocked(p);
-                }
-
-                // Post the event to the main thread.
-                Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
-                msg.setAsynchronous(true);
-                mHandler.sendMessage(msg);
-                return DISPATCH_IN_PROGRESS;
-            }
-        }
-
-        /**
-         * Notifies of any broadcast info response passed in from TIS.
-         *
-         * @param response response passed in from TIS.
-         */
-        public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.notifyBroadcastInfoResponse(mToken, response, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Notifies of any advertisement response passed in from TIS.
-         *
-         * @param response response passed in from TIS.
-         */
-        public void notifyAdResponse(AdResponse response) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.notifyAdResponse(mToken, response, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Releases this session.
-         */
-        public void release() {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.releaseSession(mToken, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-
-            releaseInternal();
-        }
-
-        /**
-         * Notifies IAPP session when a channel is tuned.
-         */
-        public void notifyTuned(Uri channelUri) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.notifyTuned(mToken, channelUri, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Notifies IAPP session when a track is selected.
-         */
-        public void notifyTrackSelected(int type, String trackId) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.notifyTrackSelected(mToken, type, trackId, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
-         * Notifies IAPP session when tracks are changed.
-         */
-        public void notifyTracksChanged(List<TvTrackInfo> tracks) {
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.notifyTracksChanged(mToken, tracks, mUserId);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        private void flushPendingEventsLocked() {
-            mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
-
-            final int count = mPendingEvents.size();
-            for (int i = 0; i < count; i++) {
-                int seq = mPendingEvents.keyAt(i);
-                Message msg = mHandler.obtainMessage(
-                        InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
-                msg.setAsynchronous(true);
-                msg.sendToTarget();
-            }
-        }
-
-        private void releaseInternal() {
-            mToken = null;
-            synchronized (mHandler) {
-                if (mInputChannel != null) {
-                    if (mSender != null) {
-                        flushPendingEventsLocked();
-                        mSender.dispose();
-                        mSender = null;
-                    }
-                    mInputChannel.dispose();
-                    mInputChannel = null;
-                }
-            }
-            synchronized (mSessionCallbackRecordMap) {
-                mSessionCallbackRecordMap.delete(mSeq);
-            }
-        }
-
-        private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
-                FinishedInputEventCallback callback, Handler handler) {
-            PendingEvent p = mPendingEventPool.acquire();
-            if (p == null) {
-                p = new PendingEvent();
-            }
-            p.mEvent = event;
-            p.mEventToken = token;
-            p.mCallback = callback;
-            p.mEventHandler = handler;
-            return p;
-        }
-
-        // Assumes the event has already been removed from the queue.
-        void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
-            p.mHandled = handled;
-            if (p.mEventHandler.getLooper().isCurrentThread()) {
-                // Already running on the callback handler thread so we can send the callback
-                // immediately.
-                p.run();
-            } else {
-                // Post the event to the callback handler thread.
-                // In this case, the callback will be responsible for recycling the event.
-                Message msg = Message.obtain(p.mEventHandler, p);
-                msg.setAsynchronous(true);
-                msg.sendToTarget();
-            }
-        }
-
-        // Must be called on the main looper
-        private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
-            synchronized (mHandler) {
-                int result = sendInputEventOnMainLooperLocked(p);
-                if (result == DISPATCH_IN_PROGRESS) {
-                    return;
-                }
-            }
-
-            invokeFinishedInputEventCallback(p, false);
-        }
-
-        private int sendInputEventOnMainLooperLocked(PendingEvent p) {
-            if (mInputChannel != null) {
-                if (mSender == null) {
-                    mSender = new TvInputEventSender(mInputChannel, mHandler.getLooper());
-                }
-
-                final InputEvent event = p.mEvent;
-                final int seq = event.getSequenceNumber();
-                if (mSender.sendInputEvent(seq, event)) {
-                    mPendingEvents.put(seq, p);
-                    Message msg = mHandler.obtainMessage(
-                            InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
-                    msg.setAsynchronous(true);
-                    mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
-                    return DISPATCH_IN_PROGRESS;
-                }
-
-                Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
-                        + event);
-            }
-            return DISPATCH_NOT_HANDLED;
-        }
-
-        void finishedInputEvent(int seq, boolean handled, boolean timeout) {
-            final PendingEvent p;
-            synchronized (mHandler) {
-                int index = mPendingEvents.indexOfKey(seq);
-                if (index < 0) {
-                    return; // spurious, event already finished or timed out
-                }
-
-                p = mPendingEvents.valueAt(index);
-                mPendingEvents.removeAt(index);
-
-                if (timeout) {
-                    Log.w(TAG, "Timeout waiting for session to handle input event after "
-                            + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
-                } else {
-                    mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
-                }
-            }
-
-            invokeFinishedInputEventCallback(p, handled);
-        }
-
-        private void recyclePendingEventLocked(PendingEvent p) {
-            p.recycle();
-            mPendingEventPool.release(p);
-        }
-
-        /**
-         * Callback that is invoked when an input event that was dispatched to this session has been
-         * finished.
-         *
-         * @hide
-         */
-        public interface FinishedInputEventCallback {
-            /**
-             * Called when the dispatched input event is finished.
-             *
-             * @param token A token passed to {@link #dispatchInputEvent}.
-             * @param handled {@code true} if the dispatched input event was handled properly.
-             *            {@code false} otherwise.
-             */
-            void onFinishedInputEvent(Object token, boolean handled);
-        }
-
-        private final class InputEventHandler extends Handler {
-            public static final int MSG_SEND_INPUT_EVENT = 1;
-            public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
-            public static final int MSG_FLUSH_INPUT_EVENT = 3;
-
-            InputEventHandler(Looper looper) {
-                super(looper, null, true);
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case MSG_SEND_INPUT_EVENT: {
-                        sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
-                        return;
-                    }
-                    case MSG_TIMEOUT_INPUT_EVENT: {
-                        finishedInputEvent(msg.arg1, false, true);
-                        return;
-                    }
-                    case MSG_FLUSH_INPUT_EVENT: {
-                        finishedInputEvent(msg.arg1, false, false);
-                        return;
-                    }
-                }
-            }
-        }
-
-        private final class TvInputEventSender extends InputEventSender {
-            TvInputEventSender(InputChannel inputChannel, Looper looper) {
-                super(inputChannel, looper);
-            }
-
-            @Override
-            public void onInputEventFinished(int seq, boolean handled) {
-                finishedInputEvent(seq, handled, false);
-            }
-        }
-
-        private final class PendingEvent implements Runnable {
-            public InputEvent mEvent;
-            public Object mEventToken;
-            public FinishedInputEventCallback mCallback;
-            public Handler mEventHandler;
-            public boolean mHandled;
-
-            public void recycle() {
-                mEvent = null;
-                mEventToken = null;
-                mCallback = null;
-                mEventHandler = null;
-                mHandled = false;
-            }
-
-            @Override
-            public void run() {
-                mCallback.onFinishedInputEvent(mEventToken, mHandled);
-
-                synchronized (mEventHandler) {
-                    recyclePendingEventLocked(this);
-                }
-            }
-        }
-    }
-
-    private static final class SessionCallbackRecord {
-        private final SessionCallback mSessionCallback;
-        private final Handler mHandler;
-        private Session mSession;
-
-        SessionCallbackRecord(SessionCallback sessionCallback, Handler handler) {
-            mSessionCallback = sessionCallback;
-            mHandler = handler;
-        }
-
-        void postSessionCreated(final Session session) {
-            mSession = session;
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onSessionCreated(session);
-                }
-            });
-        }
-
-        void postSessionReleased() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onSessionReleased(mSession);
-                }
-            });
-        }
-
-        void postLayoutSurface(final int left, final int top, final int right,
-                final int bottom) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom);
-                }
-            });
-        }
-
-        void postBroadcastInfoRequest(final BroadcastInfoRequest request) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSession.getInputSession().requestBroadcastInfo(request);
-                }
-            });
-        }
-
-        void postRemoveBroadcastInfo(final int requestId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSession.getInputSession().removeBroadcastInfo(requestId);
-                }
-            });
-        }
-
-        void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType,
-                final Bundle parameters) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onCommandRequest(mSession, cmdType, parameters);
-                }
-            });
-        }
-
-        void postSetVideoBounds(Rect rect) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onSetVideoBounds(mSession, rect);
-                }
-            });
-        }
-
-        void postRequestCurrentChannelUri() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onRequestCurrentChannelUri(mSession);
-                }
-            });
-        }
-
-        void postRequestCurrentChannelLcn() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onRequestCurrentChannelLcn(mSession);
-                }
-            });
-        }
-
-        void postRequestStreamVolume() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onRequestStreamVolume(mSession);
-                }
-            });
-        }
-
-        void postRequestTrackInfoList() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onRequestTrackInfoList(mSession);
-                }
-            });
-        }
-
-        void postAdRequest(final AdRequest request) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (mSession.getInputSession() != null) {
-                        mSession.getInputSession().requestAd(request);
-                    }
-                }
-            });
-        }
-
-        void postSessionStateChanged(int state) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onSessionStateChanged(mSession, state);
-                }
-            });
-        }
-
-        void postBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mSessionCallback.onBiInteractiveAppCreated(mSession, biIAppUri, biIAppId);
-                }
-            });
-        }
-    }
-
-    /**
-     * Interface used to receive the created session.
-     * @hide
-     */
-    public abstract static class SessionCallback {
-        /**
-         * This is called after {@link TvIAppManager#createSession} has been processed.
-         *
-         * @param session A {@link TvIAppManager.Session} instance created. This can be {@code null}
-         *                if the creation request failed.
-         */
-        public void onSessionCreated(@Nullable Session session) {
-        }
-
-        /**
-         * This is called when {@link TvIAppManager.Session} is released.
-         * This typically happens when the process hosting the session has crashed or been killed.
-         *
-         * @param session the {@link TvIAppManager.Session} instance released.
-         */
-        public void onSessionReleased(@NonNull Session session) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#layoutSurface} is called to change the
-         * layout of surface.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         * @param left Left position.
-         * @param top Top position.
-         * @param right Right position.
-         * @param bottom Bottom position.
-         */
-        public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#requestCommand} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         * @param cmdType type of the command.
-         * @param parameters parameters of the command.
-         */
-        public void onCommandRequest(Session session,
-                @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         */
-        public void onSetVideoBounds(Session session, Rect rect) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         */
-        public void onRequestCurrentChannelUri(Session session) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         */
-        public void onRequestCurrentChannelLcn(Session session) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         */
-        public void onRequestStreamVolume(Session session) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         */
-        public void onRequestTrackInfoList(Session session) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         * @param state the current state.
-         */
-        public void onSessionStateChanged(Session session, int state) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated} is
-         * called.
-         *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
-         * @param biIAppUri URI associated this BI interactive app. This is the same URI in
-         *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
-         * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
-         *                 app.
-         */
-        public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
-        }
-    }
-}
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
deleted file mode 100644
index 1480ff64..0000000
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ /dev/null
@@ -1,1306 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.media.tv.AdRequest;
-import android.media.tv.AdResponse;
-import android.media.tv.BroadcastInfoRequest;
-import android.media.tv.BroadcastInfoResponse;
-import android.media.tv.TvTrackInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-
-import com.android.internal.os.SomeArgs;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The TvIAppService class represents a TV interactive applications RTE.
- */
-public abstract class TvIAppService extends Service {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "TvIAppService";
-
-    private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
-
-    // TODO: cleanup and unhide APIs.
-
-    /**
-     * This is the interface name that a service implementing a TV IApp service should say that it
-     * supports -- that is, this is the action it uses for its intent filter. To be supported, the
-     * service must also require the android.Manifest.permission#BIND_TV_IAPP permission so
-     * that other applications cannot abuse it.
-     */
-    public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
-
-    /**
-     * Name under which a TvIAppService component publishes information about itself. This meta-data
-     * must reference an XML resource containing an
-     * <code>&lt;{@link android.R.styleable#TvIAppService tv-iapp}&gt;</code>
-     * tag.
-     */
-    public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = {
-            IAPP_SERVICE_COMMAND_TYPE_TUNE,
-            IAPP_SERVICE_COMMAND_TYPE_TUNE_NEXT,
-            IAPP_SERVICE_COMMAND_TYPE_TUNE_PREV,
-            IAPP_SERVICE_COMMAND_TYPE_STOP,
-            IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME,
-            IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK
-    })
-    public @interface IAppServiceCommandType {}
-
-    /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "tune";
-    /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE_NEXT = "tune_next";
-    /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE_PREV = "tune_previous";
-    /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "stop";
-    /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME = "set_stream_volume";
-    /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track";
-
-    private final Handler mServiceHandler = new ServiceHandler();
-    private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
-            new RemoteCallbackList<>();
-
-    /** @hide */
-    @Override
-    public final IBinder onBind(Intent intent) {
-        ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
-            @Override
-            public void registerCallback(ITvIAppServiceCallback cb) {
-                if (cb != null) {
-                    mCallbacks.register(cb);
-                }
-            }
-
-            @Override
-            public void unregisterCallback(ITvIAppServiceCallback cb) {
-                if (cb != null) {
-                    mCallbacks.unregister(cb);
-                }
-            }
-
-            @Override
-            public void createSession(InputChannel channel, ITvIAppSessionCallback cb,
-                    String iAppServiceId, int type) {
-                if (cb == null) {
-                    return;
-                }
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = channel;
-                args.arg2 = cb;
-                args.arg3 = iAppServiceId;
-                args.arg4 = type;
-                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
-                        .sendToTarget();
-            }
-
-            @Override
-            public void prepare(int type) {
-                onPrepare(type);
-            }
-
-            @Override
-            public void notifyAppLinkInfo(Bundle appLinkInfo) {
-                onAppLinkInfo(appLinkInfo);
-            }
-
-            @Override
-            public void sendAppLinkCommand(Bundle command) {
-                onAppLinkCommand(command);
-            }
-        };
-        return tvIAppServiceBinder;
-    }
-
-    /**
-     * Prepares TV IApp service for the given type.
-     * @hide
-     */
-    public void onPrepare(int type) {
-        // TODO: make it abstract when unhide
-    }
-
-    /**
-     * Registers App link info.
-     * @hide
-     */
-    public void onAppLinkInfo(Bundle appLinkInfo) {
-        // TODO: make it abstract when unhide
-    }
-
-    /**
-     * Sends App link info.
-     * @hide
-     */
-    public void onAppLinkCommand(Bundle command) {
-        // TODO: make it abstract when unhide
-    }
-
-
-    /**
-     * Returns a concrete implementation of {@link Session}.
-     *
-     * <p>May return {@code null} if this TV IApp service fails to create a session for some
-     * reason.
-     *
-     * @param iAppServiceId The ID of the TV IApp associated with the session.
-     * @param type The type of the TV IApp associated with the session.
-     * @hide
-     */
-    @Nullable
-    public Session onCreateSession(@NonNull String iAppServiceId, int type) {
-        // TODO: make it abstract when unhide
-        return null;
-    }
-
-    /**
-     * Notifies the system when the state of the interactive app has been changed.
-     * @param state the current state
-     * @hide
-     */
-    public final void notifyStateChanged(int type, @TvIAppManager.TvIAppRteState int state) {
-        mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED,
-                type, state).sendToTarget();
-    }
-
-    /**
-     * Base class for derived classes to implement to provide a TV interactive app session.
-     * @hide
-     */
-    public abstract static class Session implements KeyEvent.Callback {
-        private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
-
-        private final Object mLock = new Object();
-        // @GuardedBy("mLock")
-        private ITvIAppSessionCallback mSessionCallback;
-        // @GuardedBy("mLock")
-        private final List<Runnable> mPendingActions = new ArrayList<>();
-
-        private final Context mContext;
-        final Handler mHandler;
-        private final WindowManager mWindowManager;
-        private WindowManager.LayoutParams mWindowParams;
-        private Surface mSurface;
-        private FrameLayout mMediaViewContainer;
-        private View mMediaView;
-        private MediaViewCleanUpTask mMediaViewCleanUpTask;
-        private boolean mMediaViewEnabled;
-        private IBinder mWindowToken;
-        private Rect mMediaFrame;
-
-        /**
-         * Creates a new Session.
-         *
-         * @param context The context of the application
-         */
-        public Session(@NonNull Context context) {
-            mContext = context;
-            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-            mHandler = new Handler(context.getMainLooper());
-        }
-
-        /**
-         * Enables or disables the media view.
-         *
-         * <p>By default, the media view is disabled. Must be called explicitly after the
-         * session is created to enable the media view.
-         *
-         * <p>The TV IApp service can disable its media view when needed.
-         *
-         * @param enable {@code true} if you want to enable the media view. {@code false}
-         *            otherwise.
-         */
-        public void setMediaViewEnabled(final boolean enable) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (enable == mMediaViewEnabled) {
-                        return;
-                    }
-                    mMediaViewEnabled = enable;
-                    if (enable) {
-                        if (mWindowToken != null) {
-                            createMediaView(mWindowToken, mMediaFrame);
-                        }
-                    } else {
-                        removeMediaView(false);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Starts TvIAppService session.
-         * @hide
-         */
-        public void onStartIApp() {
-        }
-
-        /**
-         * Stops TvIAppService session.
-         * @hide
-         */
-        public void onStopIApp() {
-        }
-
-        /**
-         * Creates broadcast-independent(BI) interactive application.
-         *
-         * @see #onDestroyBiInteractiveApp(String)
-         * @hide
-         */
-        public void onCreateBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
-        }
-
-
-        /**
-         * Destroys broadcast-independent(BI) interactive application.
-         *
-         * @param biIAppId the BI interactive app ID from
-         *        {@link #createBiInteractiveApp(Uri, Bundle)}
-         *
-         * @see #onCreateBiInteractiveApp(Uri, Bundle)
-         * @hide
-         */
-        public void onDestroyBiInteractiveApp(@NonNull String biIAppId) {
-        }
-
-        /**
-         * Receives current channel URI.
-         * @hide
-         */
-        public void onCurrentChannelUri(@Nullable Uri channelUri) {
-        }
-
-        /**
-         * Receives logical channel number (LCN) of current channel.
-         * @hide
-         */
-        public void onCurrentChannelLcn(int lcn) {
-        }
-
-        /**
-         * Receives stream volume.
-         * @hide
-         */
-        public void onStreamVolume(float volume) {
-        }
-
-        /**
-         * Receives track list.
-         * @hide
-         */
-        public void onTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
-        }
-
-        /**
-         * Called when the application sets the surface.
-         *
-         * <p>The TV IApp service should render interactive app UI onto the given surface. When
-         * called with {@code null}, the IApp service should immediately free any references to the
-         * currently set surface and stop using it.
-         *
-         * @param surface The surface to be used for interactive app UI rendering. Can be
-         *                {@code null}.
-         * @return {@code true} if the surface was set successfully, {@code false} otherwise.
-         */
-        public abstract boolean onSetSurface(@Nullable Surface surface);
-
-        /**
-         * Called after any structural changes (format or size) have been made to the surface passed
-         * in {@link #onSetSurface}. This method is always called at least once, after
-         * {@link #onSetSurface} is called with non-null surface.
-         *
-         * @param format The new PixelFormat of the surface.
-         * @param width The new width of the surface.
-         * @param height The new height of the surface.
-         */
-        public void onSurfaceChanged(int format, int width, int height) {
-        }
-
-        /**
-         * Called when the size of the media view is changed by the application.
-         *
-         * <p>This is always called at least once when the session is created regardless of whether
-         * the media view is enabled or not. The media view container size is the same as the
-         * containing {@link TvIAppView}. Note that the size of the underlying surface can be
-         * different if the surface was changed by calling {@link #layoutSurface}.
-         *
-         * @param width The width of the media view.
-         * @param height The height of the media view.
-         */
-        public void onMediaViewSizeChanged(int width, int height) {
-        }
-
-        /**
-         * Called when the application requests to create an media view. Each session
-         * implementation can override this method and return its own view.
-         *
-         * @return a view attached to the media window
-         */
-        @Nullable
-        public View onCreateMediaView() {
-            return null;
-        }
-
-        /**
-         * Releases TvIAppService session.
-         * @hide
-         */
-        public void onRelease() {
-        }
-
-        /**
-         * Called when the corresponding TV input tuned to a channel.
-         * @hide
-         */
-        public void onTuned(@NonNull Uri channelUri) {
-        }
-
-        /**
-         * Called when the corresponding TV input selected to a track.
-         * @hide
-         */
-        public void onTrackSelected(int type, String trackId) {
-        }
-
-        /**
-         * Called when the tracks are changed.
-         * @hide
-         */
-        public void onTracksChanged(List<TvTrackInfo> tracks) {
-        }
-
-        /**
-         * Called when a broadcast info response is received.
-         * @hide
-         */
-        public void onBroadcastInfoResponse(@NonNull BroadcastInfoResponse response) {
-        }
-
-        /**
-         * Called when an advertisement response is received.
-         * @hide
-         */
-        public void onAdResponse(AdResponse response) {
-        }
-
-        /**
-         * TODO: JavaDoc of APIs related to input events.
-         * @hide
-         */
-        @Override
-        public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        @Override
-        public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        @Override
-        public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) {
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        @Override
-        public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public boolean onTouchEvent(@NonNull MotionEvent event) {
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public boolean onTrackballEvent(@NonNull MotionEvent event) {
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
-            return false;
-        }
-
-        /**
-         * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
-         * is relative to the overlay view that sits on top of this surface.
-         *
-         * @param left Left position in pixels, relative to the overlay view.
-         * @param top Top position in pixels, relative to the overlay view.
-         * @param right Right position in pixels, relative to the overlay view.
-         * @param bottom Bottom position in pixels, relative to the overlay view.
-         */
-        public void layoutSurface(final int left, final int top, final int right,
-                final int bottom) {
-            if (left > right || top > bottom) {
-                throw new IllegalArgumentException("Invalid parameter");
-            }
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top
-                                    + ", r=" + right + ", b=" + bottom + ",)");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onLayoutSurface(left, top, right, bottom);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in layoutSurface", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Requests broadcast related information from the related TV input.
-         * @param request the request for broadcast info
-         */
-        public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestBroadcastInfo (requestId="
-                                    + request.getRequestId() + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onBroadcastInfoRequest(request);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestBroadcastInfo", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Remove broadcast information request from the related TV input.
-         * @param requestId the ID of the request
-         */
-        public void removeBroadcastInfo(final int requestId) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "removeBroadcastInfo (requestId="
-                                    + requestId + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onRemoveBroadcastInfo(requestId);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in removeBroadcastInfo", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * requests a specific command to be processed by the related TV input.
-         * @param cmdType type of the specific command
-         * @param parameters parameters of the specific command
-         */
-        public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters="
-                                    + parameters.toString() + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onCommandRequest(cmdType, parameters);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestCommand", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Sets broadcast video bounds.
-         */
-        public void setVideoBounds(Rect rect) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "setVideoBounds (rect=" + rect + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onSetVideoBounds(rect);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in setVideoBounds", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Requests the URI of the current channel.
-         */
-        public void requestCurrentChannelUri() {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestCurrentChannelUri");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onRequestCurrentChannelUri();
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestCurrentChannelUri", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Requests the logic channel number (LCN) of the current channel.
-         */
-        public void requestCurrentChannelLcn() {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestCurrentChannelLcn");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onRequestCurrentChannelLcn();
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestCurrentChannelLcn", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Requests stream volume.
-         */
-        public void requestStreamVolume() {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestStreamVolume");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onRequestStreamVolume();
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestStreamVolume", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Requests the list of {@link TvTrackInfo}.
-         */
-        public void requestTrackInfoList() {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestTrackInfoList");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onRequestTrackInfoList();
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestTrackInfoList", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * requests an advertisement request to be processed by the related TV input.
-         * @param request advertisement request
-         */
-        public void requestAd(@NonNull final AdRequest request) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "requestAd (id=" + request.getId() + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onAdRequest(request);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in requestAd", e);
-                    }
-                }
-            });
-        }
-
-        void startIApp() {
-            onStartIApp();
-        }
-
-        void stopIApp() {
-            onStopIApp();
-        }
-
-        void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
-            onCreateBiInteractiveApp(biIAppUri, params);
-        }
-
-        void destroyBiInteractiveApp(@NonNull String biIAppId) {
-            onDestroyBiInteractiveApp(biIAppId);
-        }
-
-        void sendCurrentChannelUri(@Nullable Uri channelUri) {
-            onCurrentChannelUri(channelUri);
-        }
-
-        void sendCurrentChannelLcn(int lcn) {
-            onCurrentChannelLcn(lcn);
-        }
-
-        void sendStreamVolume(float volume) {
-            onStreamVolume(volume);
-        }
-
-        void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
-            onTrackInfoList(tracks);
-        }
-
-        void release() {
-            onRelease();
-            if (mSurface != null) {
-                mSurface.release();
-                mSurface = null;
-            }
-            synchronized (mLock) {
-                mSessionCallback = null;
-                mPendingActions.clear();
-            }
-            // Removes the media view lastly so that any hanging on the main thread can be handled
-            // in {@link #scheduleMediaViewCleanup}.
-            removeMediaView(true);
-        }
-
-        void notifyTuned(Uri channelUri) {
-            if (DEBUG) {
-                Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")");
-            }
-            onTuned(channelUri);
-        }
-
-        void notifyTrackSelected(int type, String trackId) {
-            if (DEBUG) {
-                Log.d(TAG, "notifyTrackSelected (type=" + type + "trackId=" + trackId + ")");
-            }
-            onTrackSelected(type, trackId);
-        }
-
-        void notifyTracksChanged(List<TvTrackInfo> tracks) {
-            if (DEBUG) {
-                Log.d(TAG, "notifyTracksChanged (tracks=" + tracks + ")");
-            }
-            onTracksChanged(tracks);
-        }
-
-
-        /**
-         * Calls {@link #onBroadcastInfoResponse}.
-         */
-        void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
-            if (DEBUG) {
-                Log.d(TAG, "notifyBroadcastInfoResponse (requestId="
-                        + response.getRequestId() + ")");
-            }
-            onBroadcastInfoResponse(response);
-        }
-
-        /**
-         * Calls {@link #onAdResponse}.
-         */
-        void notifyAdResponse(AdResponse response) {
-            if (DEBUG) {
-                Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")");
-            }
-            onAdResponse(response);
-        }
-
-        /**
-         * Notifies when the session state is changed.
-         * @param state the current state.
-         */
-        public void notifySessionStateChanged(@TvIAppManager.TvIAppRteState int state) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "notifySessionStateChanged (state="
-                                    + state + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onSessionStateChanged(state);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in notifySessionStateChanged", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Notifies the broadcast-independent(BI) interactive application has been created.
-         * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
-         *                 app.
-         * @hide
-         */
-        public final void notifyBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
-            executeOrPostRunnableOnMainThread(new Runnable() {
-                @MainThread
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "notifyBiInteractiveAppCreated (biIAppId="
-                                    + biIAppId + ")");
-                        }
-                        if (mSessionCallback != null) {
-                            mSessionCallback.onBiInteractiveAppCreated(biIAppUri, biIAppId);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "error in notifyBiInteractiveAppCreated", e);
-                    }
-                }
-            });
-        }
-
-        /**
-         * Takes care of dispatching incoming input events and tells whether the event was handled.
-         */
-        int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
-            if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
-            if (event instanceof KeyEvent) {
-                KeyEvent keyEvent = (KeyEvent) event;
-                if (keyEvent.dispatch(this, mDispatcherState, this)) {
-                    return TvIAppManager.Session.DISPATCH_HANDLED;
-                }
-
-                // TODO: special handlings of navigation keys and media keys
-            } else if (event instanceof MotionEvent) {
-                MotionEvent motionEvent = (MotionEvent) event;
-                final int source = motionEvent.getSource();
-                if (motionEvent.isTouchEvent()) {
-                    if (onTouchEvent(motionEvent)) {
-                        return TvIAppManager.Session.DISPATCH_HANDLED;
-                    }
-                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    if (onTrackballEvent(motionEvent)) {
-                        return TvIAppManager.Session.DISPATCH_HANDLED;
-                    }
-                } else {
-                    if (onGenericMotionEvent(motionEvent)) {
-                        return TvIAppManager.Session.DISPATCH_HANDLED;
-                    }
-                }
-            }
-            // TODO: handle overlay view
-            return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
-        }
-
-        private void initialize(ITvIAppSessionCallback callback) {
-            synchronized (mLock) {
-                mSessionCallback = callback;
-                for (Runnable runnable : mPendingActions) {
-                    runnable.run();
-                }
-                mPendingActions.clear();
-            }
-        }
-
-        /**
-         * Calls {@link #onSetSurface}.
-         */
-        void setSurface(Surface surface) {
-            onSetSurface(surface);
-            if (mSurface != null) {
-                mSurface.release();
-            }
-            mSurface = surface;
-            // TODO: Handle failure.
-        }
-
-        /**
-         * Calls {@link #onSurfaceChanged}.
-         */
-        void dispatchSurfaceChanged(int format, int width, int height) {
-            if (DEBUG) {
-                Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
-                        + ", height=" + height + ")");
-            }
-            onSurfaceChanged(format, width, height);
-        }
-
-        private void executeOrPostRunnableOnMainThread(Runnable action) {
-            synchronized (mLock) {
-                if (mSessionCallback == null) {
-                    // The session is not initialized yet.
-                    mPendingActions.add(action);
-                } else {
-                    if (mHandler.getLooper().isCurrentThread()) {
-                        action.run();
-                    } else {
-                        // Posts the runnable if this is not called from the main thread
-                        mHandler.post(action);
-                    }
-                }
-            }
-        }
-
-        /**
-         * Creates an media view. This calls {@link #onCreateMediaView} to get a view to attach
-         * to the media window.
-         *
-         * @param windowToken A window token of the application.
-         * @param frame A position of the media view.
-         */
-        void createMediaView(IBinder windowToken, Rect frame) {
-            if (mMediaViewContainer != null) {
-                removeMediaView(false);
-            }
-            if (DEBUG) Log.d(TAG, "create media view(" + frame + ")");
-            mWindowToken = windowToken;
-            mMediaFrame = frame;
-            onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
-            if (!mMediaViewEnabled) {
-                return;
-            }
-            mMediaView = onCreateMediaView();
-            if (mMediaView == null) {
-                return;
-            }
-            if (mMediaViewCleanUpTask != null) {
-                mMediaViewCleanUpTask.cancel(true);
-                mMediaViewCleanUpTask = null;
-            }
-            // Creates a container view to check hanging on the media view detaching.
-            // Adding/removing the media view to/from the container make the view attach/detach
-            // logic run on the main thread.
-            mMediaViewContainer = new FrameLayout(mContext.getApplicationContext());
-            mMediaViewContainer.addView(mMediaView);
-
-            int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
-            // We make the overlay view non-focusable and non-touchable so that
-            // the application that owns the window token can decide whether to consume or
-            // dispatch the input events.
-            int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-            if (ActivityManager.isHighEndGfx()) {
-                flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-            }
-            mWindowParams = new WindowManager.LayoutParams(
-                    frame.right - frame.left, frame.bottom - frame.top,
-                    frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT);
-            mWindowParams.privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-            mWindowParams.gravity = Gravity.START | Gravity.TOP;
-            mWindowParams.token = windowToken;
-            mWindowManager.addView(mMediaViewContainer, mWindowParams);
-        }
-
-        /**
-         * Relayouts the current media view.
-         *
-         * @param frame A new position of the media view.
-         */
-        void relayoutMediaView(Rect frame) {
-            if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")");
-            if (mMediaFrame == null || mMediaFrame.width() != frame.width()
-                    || mMediaFrame.height() != frame.height()) {
-                // Note: relayoutMediaView is called whenever TvIAppView's layout is changed
-                // regardless of setMediaViewEnabled.
-                onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
-            }
-            mMediaFrame = frame;
-            if (!mMediaViewEnabled || mMediaViewContainer == null) {
-                return;
-            }
-            mWindowParams.x = frame.left;
-            mWindowParams.y = frame.top;
-            mWindowParams.width = frame.right - frame.left;
-            mWindowParams.height = frame.bottom - frame.top;
-            mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams);
-        }
-
-        /**
-         * Removes the current media view.
-         */
-        void removeMediaView(boolean clearWindowToken) {
-            if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")");
-            if (clearWindowToken) {
-                mWindowToken = null;
-                mMediaFrame = null;
-            }
-            if (mMediaViewContainer != null) {
-                // Removes the media view from the view hierarchy in advance so that it can be
-                // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is
-                // hanging.
-                mMediaViewContainer.removeView(mMediaView);
-                mMediaView = null;
-                mWindowManager.removeView(mMediaViewContainer);
-                mMediaViewContainer = null;
-                mWindowParams = null;
-            }
-        }
-
-        /**
-         * Schedules a task which checks whether the media view is detached and kills the process
-         * if it is not. Note that this method is expected to be called in a non-main thread.
-         */
-        void scheduleMediaViewCleanup() {
-            View mediaViewParent = mMediaViewContainer;
-            if (mediaViewParent != null) {
-                mMediaViewCleanUpTask = new MediaViewCleanUpTask();
-                mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
-                        mediaViewParent);
-            }
-        }
-    }
-
-    private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> {
-        @Override
-        protected Void doInBackground(View... views) {
-            View mediaViewParent = views[0];
-            try {
-                Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS);
-            } catch (InterruptedException e) {
-                return null;
-            }
-            if (isCancelled()) {
-                return null;
-            }
-            if (mediaViewParent.isAttachedToWindow()) {
-                Log.e(TAG, "Time out on releasing media view. Killing "
-                        + mediaViewParent.getContext().getPackageName());
-                android.os.Process.killProcess(Process.myPid());
-            }
-            return null;
-        }
-    }
-
-    /**
-     * Implements the internal ITvIAppSession interface.
-     * @hide
-     */
-    public static class ITvIAppSessionWrapper extends ITvIAppSession.Stub {
-        // TODO: put ITvIAppSessionWrapper in a separate Java file
-        private final Session mSessionImpl;
-        private InputChannel mChannel;
-        private TvIAppEventReceiver mReceiver;
-
-        public ITvIAppSessionWrapper(Context context, Session mSessionImpl, InputChannel channel) {
-            this.mSessionImpl = mSessionImpl;
-            mChannel = channel;
-            if (channel != null) {
-                mReceiver = new TvIAppEventReceiver(channel, context.getMainLooper());
-            }
-        }
-
-        @Override
-        public void startIApp() {
-            mSessionImpl.startIApp();
-        }
-
-        @Override
-        public void stopIApp() {
-            mSessionImpl.stopIApp();
-        }
-
-        @Override
-        public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
-            mSessionImpl.createBiInteractiveApp(biIAppUri, params);
-        }
-
-        @Override
-        public void destroyBiInteractiveApp(@NonNull String biIAppId) {
-            mSessionImpl.destroyBiInteractiveApp(biIAppId);
-        }
-
-        @Override
-        public void sendCurrentChannelUri(@Nullable Uri channelUri) {
-            mSessionImpl.sendCurrentChannelUri(channelUri);
-        }
-
-        @Override
-        public void sendCurrentChannelLcn(int lcn) {
-            mSessionImpl.sendCurrentChannelLcn(lcn);
-        }
-
-        @Override
-        public void sendStreamVolume(float volume) {
-            mSessionImpl.sendStreamVolume(volume);
-        }
-
-        @Override
-        public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
-            mSessionImpl.sendTrackInfoList(tracks);
-        }
-
-        @Override
-        public void release() {
-            mSessionImpl.scheduleMediaViewCleanup();
-            mSessionImpl.release();
-        }
-
-        @Override
-        public void notifyTuned(Uri channelUri) {
-            mSessionImpl.notifyTuned(channelUri);
-        }
-
-        @Override
-        public void notifyTrackSelected(int type, final String trackId) {
-            mSessionImpl.notifyTrackSelected(type, trackId);
-        }
-
-        @Override
-        public void notifyTracksChanged(List<TvTrackInfo> tracks) {
-            mSessionImpl.notifyTracksChanged(tracks);
-        }
-
-        @Override
-        public void setSurface(Surface surface) {
-            mSessionImpl.setSurface(surface);
-        }
-
-        @Override
-        public void dispatchSurfaceChanged(int format, int width, int height) {
-            mSessionImpl.dispatchSurfaceChanged(format, width, height);
-        }
-
-        @Override
-        public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
-            mSessionImpl.notifyBroadcastInfoResponse(response);
-        }
-
-        @Override
-        public void notifyAdResponse(AdResponse response) {
-            mSessionImpl.notifyAdResponse(response);
-        }
-
-        @Override
-        public void createMediaView(IBinder windowToken, Rect frame) {
-            mSessionImpl.createMediaView(windowToken, frame);
-        }
-
-        @Override
-        public void relayoutMediaView(Rect frame) {
-            mSessionImpl.relayoutMediaView(frame);
-        }
-
-        @Override
-        public void removeMediaView() {
-            mSessionImpl.removeMediaView(true);
-        }
-
-        private final class TvIAppEventReceiver extends InputEventReceiver {
-            TvIAppEventReceiver(InputChannel inputChannel, Looper looper) {
-                super(inputChannel, looper);
-            }
-
-            @Override
-            public void onInputEvent(InputEvent event) {
-                if (mSessionImpl == null) {
-                    // The session has been finished.
-                    finishInputEvent(event, false);
-                    return;
-                }
-
-                int handled = mSessionImpl.dispatchInputEvent(event, this);
-                if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
-                    finishInputEvent(event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
-                }
-            }
-        }
-    }
-
-    @SuppressLint("HandlerLeak")
-    private final class ServiceHandler extends Handler {
-        private static final int DO_CREATE_SESSION = 1;
-        private static final int DO_NOTIFY_SESSION_CREATED = 2;
-        private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3;
-
-        private void broadcastRteStateChanged(int type, int state) {
-            int n = mCallbacks.beginBroadcast();
-            for (int i = 0; i < n; ++i) {
-                try {
-                    mCallbacks.getBroadcastItem(i).onStateChanged(type, state);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "error in broadcastRteStateChanged", e);
-                }
-            }
-            mCallbacks.finishBroadcast();
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case DO_CREATE_SESSION: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    InputChannel channel = (InputChannel) args.arg1;
-                    ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg2;
-                    String iAppServiceId = (String) args.arg3;
-                    int type = (int) args.arg4;
-                    args.recycle();
-                    Session sessionImpl = onCreateSession(iAppServiceId, type);
-                    if (sessionImpl == null) {
-                        try {
-                            // Failed to create a session.
-                            cb.onSessionCreated(null);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "error in onSessionCreated", e);
-                        }
-                        return;
-                    }
-                    ITvIAppSession stub = new ITvIAppSessionWrapper(
-                            TvIAppService.this, sessionImpl, channel);
-
-                    SomeArgs someArgs = SomeArgs.obtain();
-                    someArgs.arg1 = sessionImpl;
-                    someArgs.arg2 = stub;
-                    someArgs.arg3 = cb;
-                    mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
-                            someArgs).sendToTarget();
-                    return;
-                }
-                case DO_NOTIFY_SESSION_CREATED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    Session sessionImpl = (Session) args.arg1;
-                    ITvIAppSession stub = (ITvIAppSession) args.arg2;
-                    ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg3;
-                    try {
-                        cb.onSessionCreated(stub);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "error in onSessionCreated", e);
-                    }
-                    if (sessionImpl != null) {
-                        sessionImpl.initialize(cb);
-                    }
-                    args.recycle();
-                    return;
-                }
-                case DO_NOTIFY_RTE_STATE_CHANGED: {
-                    int type = msg.arg1;
-                    int state = msg.arg2;
-                    broadcastRteStateChanged(type, state);
-                    return;
-                }
-                default: {
-                    Log.w(TAG, "Unhandled message code: " + msg.what);
-                    return;
-                }
-            }
-        }
-
-    }
-}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
deleted file mode 100644
index 1ce14ae..0000000
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ /dev/null
@@ -1,708 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.media.tv.TvInputManager;
-import android.media.tv.TvTrackInfo;
-import android.media.tv.TvView;
-import android.media.tv.interactive.TvIAppManager.Session;
-import android.media.tv.interactive.TvIAppManager.SessionCallback;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Xml;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.List;
-
-/**
- * Displays contents of interactive TV applications.
- * @hide
- */
-public class TvIAppView extends ViewGroup {
-    private static final String TAG = "TvIAppView";
-    private static final boolean DEBUG = false;
-
-    private static final int SET_TVVIEW_SUCCESS = 1;
-    private static final int SET_TVVIEW_FAIL = 2;
-    private static final int UNSET_TVVIEW_SUCCESS = 3;
-    private static final int UNSET_TVVIEW_FAIL = 4;
-
-    private final TvIAppManager mTvIAppManager;
-    private final Handler mHandler = new Handler();
-    private Session mSession;
-    private MySessionCallback mSessionCallback;
-    private TvIAppCallback mCallback;
-    private SurfaceView mSurfaceView;
-    private Surface mSurface;
-
-    private boolean mSurfaceChanged;
-    private int mSurfaceFormat;
-    private int mSurfaceWidth;
-    private int mSurfaceHeight;
-
-    private boolean mUseRequestedSurfaceLayout;
-    private int mSurfaceViewLeft;
-    private int mSurfaceViewRight;
-    private int mSurfaceViewTop;
-    private int mSurfaceViewBottom;
-
-    private boolean mMediaViewCreated;
-    private Rect mMediaViewFrame;
-
-    private final AttributeSet mAttrs;
-    private final int mDefStyleAttr;
-    private final XmlResourceParser mParser;
-
-    private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
-        @Override
-        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-            if (DEBUG) {
-                Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format
-                        + ", width=" + width + ", height=" + height + ")");
-            }
-            mSurfaceFormat = format;
-            mSurfaceWidth = width;
-            mSurfaceHeight = height;
-            mSurfaceChanged = true;
-            dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
-        }
-
-        @Override
-        public void surfaceCreated(SurfaceHolder holder) {
-            mSurface = holder.getSurface();
-            setSessionSurface(mSurface);
-        }
-
-        @Override
-        public void surfaceDestroyed(SurfaceHolder holder) {
-            mSurface = null;
-            mSurfaceChanged = false;
-            setSessionSurface(null);
-        }
-    };
-
-    public TvIAppView(@NonNull Context context) {
-        this(context, null, 0);
-    }
-
-    public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        int sourceResId = Resources.getAttributeSetSourceResId(attrs);
-        if (sourceResId != Resources.ID_NULL) {
-            Log.d(TAG, "Build local AttributeSet");
-            mParser  = context.getResources().getXml(sourceResId);
-            mAttrs = Xml.asAttributeSet(mParser);
-        } else {
-            Log.d(TAG, "Use passed in AttributeSet");
-            mParser = null;
-            mAttrs = attrs;
-        }
-        mDefStyleAttr = defStyleAttr;
-        resetSurfaceView();
-        mTvIAppManager = (TvIAppManager) getContext().getSystemService(Context.TV_IAPP_SERVICE);
-    }
-
-    /**
-     * Sets the callback to be invoked when an event is dispatched to this TvIAppView.
-     *
-     * @param callback The callback to receive events. A value of {@code null} removes the existing
-     *            callback.
-     */
-    public void setCallback(@Nullable TvIAppCallback callback) {
-        mCallback = callback;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        createSessionMediaView();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        removeSessionMediaView();
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (DEBUG) {
-            Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
-                    + ", bottom=" + bottom + ",)");
-        }
-        if (mUseRequestedSurfaceLayout) {
-            mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
-                    mSurfaceViewBottom);
-        } else {
-            mSurfaceView.layout(0, 0, right - left, bottom - top);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
-        int width = mSurfaceView.getMeasuredWidth();
-        int height = mSurfaceView.getMeasuredHeight();
-        int childState = mSurfaceView.getMeasuredState();
-        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
-                resolveSizeAndState(height, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        mSurfaceView.setVisibility(visibility);
-        if (visibility == View.VISIBLE) {
-            createSessionMediaView();
-        } else {
-            removeSessionMediaView();
-        }
-    }
-
-    private void resetSurfaceView() {
-        if (mSurfaceView != null) {
-            mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
-            removeView(mSurfaceView);
-        }
-        mSurface = null;
-        mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
-            @Override
-            protected void updateSurface() {
-                super.updateSurface();
-                relayoutSessionMediaView();
-            }};
-        // The surface view's content should be treated as secure all the time.
-        mSurfaceView.setSecure(true);
-        mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
-        addView(mSurfaceView);
-    }
-
-    /**
-     * Resets this TvIAppView.
-     */
-    public void reset() {
-        if (DEBUG) Log.d(TAG, "reset()");
-        resetInternal();
-    }
-
-    private void createSessionMediaView() {
-        // TODO: handle z-order
-        if (mSession == null || !isAttachedToWindow() || mMediaViewCreated) {
-            return;
-        }
-        mMediaViewFrame = getViewFrameOnScreen();
-        mSession.createMediaView(this, mMediaViewFrame);
-        mMediaViewCreated = true;
-    }
-
-    private void removeSessionMediaView() {
-        if (mSession == null || !mMediaViewCreated) {
-            return;
-        }
-        mSession.removeMediaView();
-        mMediaViewCreated = false;
-        mMediaViewFrame = null;
-    }
-
-    private void relayoutSessionMediaView() {
-        if (mSession == null || !isAttachedToWindow() || !mMediaViewCreated) {
-            return;
-        }
-        Rect viewFrame = getViewFrameOnScreen();
-        if (viewFrame.equals(mMediaViewFrame)) {
-            return;
-        }
-        mSession.relayoutMediaView(viewFrame);
-        mMediaViewFrame = viewFrame;
-    }
-
-    private Rect getViewFrameOnScreen() {
-        Rect frame = new Rect();
-        getGlobalVisibleRect(frame);
-        RectF frameF = new RectF(frame);
-        getMatrix().mapRect(frameF);
-        frameF.round(frame);
-        return frame;
-    }
-
-    private void setSessionSurface(Surface surface) {
-        if (mSession == null) {
-            return;
-        }
-        mSession.setSurface(surface);
-    }
-
-    private void dispatchSurfaceChanged(int format, int width, int height) {
-        if (mSession == null) {
-            return;
-        }
-        mSession.dispatchSurfaceChanged(format, width, height);
-    }
-
-    /**
-     * Prepares the interactive application.
-     */
-    public void prepareIApp(@NonNull String iAppServiceId, int type) {
-        // TODO: document and handle the cases that this method is called multiple times.
-        if (DEBUG) {
-            Log.d(TAG, "prepareIApp");
-        }
-        mSessionCallback = new MySessionCallback(iAppServiceId, type);
-        if (mTvIAppManager != null) {
-            mTvIAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
-        }
-    }
-
-    /**
-     * Starts the interactive application.
-     */
-    public void startIApp() {
-        if (DEBUG) {
-            Log.d(TAG, "startIApp");
-        }
-        if (mSession != null) {
-            mSession.startIApp();
-        }
-    }
-
-    /**
-     * Stops the interactive application.
-     */
-    public void stopIApp() {
-        if (DEBUG) {
-            Log.d(TAG, "stopIApp");
-        }
-        if (mSession != null) {
-            mSession.stopIApp();
-        }
-    }
-
-    /**
-     * Sends current channel URI to related TV interactive app.
-     */
-    public void sendCurrentChannelUri(Uri channelUri) {
-        if (DEBUG) {
-            Log.d(TAG, "sendCurrentChannelUri");
-        }
-        if (mSession != null) {
-            mSession.sendCurrentChannelUri(channelUri);
-        }
-    }
-
-    /**
-     * Sends current channel logical channel number (LCN) to related TV interactive app.
-     */
-    public void sendCurrentChannelLcn(int lcn) {
-        if (DEBUG) {
-            Log.d(TAG, "sendCurrentChannelLcn");
-        }
-        if (mSession != null) {
-            mSession.sendCurrentChannelLcn(lcn);
-        }
-    }
-
-    /**
-     * Sends stream volume to related TV interactive app.
-     */
-    public void sendStreamVolume(float volume) {
-        if (DEBUG) {
-            Log.d(TAG, "sendStreamVolume");
-        }
-        if (mSession != null) {
-            mSession.sendStreamVolume(volume);
-        }
-    }
-
-    /**
-     * Sends track info list to related TV interactive app.
-     */
-    public void sendTrackInfoList(List<TvTrackInfo> tracks) {
-        if (DEBUG) {
-            Log.d(TAG, "sendTrackInfoList");
-        }
-        if (mSession != null) {
-            mSession.sendTrackInfoList(tracks);
-        }
-    }
-
-    private void resetInternal() {
-        mSessionCallback = null;
-        if (mSession != null) {
-            setSessionSurface(null);
-            removeSessionMediaView();
-            mUseRequestedSurfaceLayout = false;
-            mSession.release();
-            mSession = null;
-            resetSurfaceView();
-        }
-    }
-
-    /**
-     * Creates broadcast-independent(BI) interactive application.
-     *
-     * @see #destroyBiInteractiveApp(String)
-     * @hide
-     */
-    public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
-        if (DEBUG) {
-            Log.d(TAG, "createBiInteractiveApp Uri=" + biIAppUri + ", params=" + params);
-        }
-        if (mSession != null) {
-            mSession.createBiInteractiveApp(biIAppUri, params);
-        }
-    }
-
-    /**
-     * Destroys broadcast-independent(BI) interactive application.
-     *
-     * @param biIAppId the BI interactive app ID from {@link #createBiInteractiveApp(Uri, Bundle)}
-     *
-     * @see #createBiInteractiveApp(Uri, Bundle)
-     * @hide
-     */
-    public void destroyBiInteractiveApp(@NonNull String biIAppId) {
-        if (DEBUG) {
-            Log.d(TAG, "destroyBiInteractiveApp biIAppId=" + biIAppId);
-        }
-        if (mSession != null) {
-            mSession.destroyBiInteractiveApp(biIAppId);
-        }
-    }
-
-    public Session getIAppSession() {
-        return mSession;
-    }
-
-    /**
-     * Sets the TvIAppView to receive events from TIS. This method links the session of
-     * TvIAppManager to TvInputManager session, so the TIAS can get the TIS events.
-     *
-     * @param tvView the TvView to be linked to this TvIAppView via linking of Sessions.
-     * @return to be added
-     */
-    public int setTvView(@Nullable TvView tvView) {
-        if (tvView == null) {
-            return unsetTvView();
-        }
-        TvInputManager.Session inputSession = tvView.getInputSession();
-        if (inputSession == null || mSession == null) {
-            return SET_TVVIEW_FAIL;
-        }
-        mSession.setInputSession(inputSession);
-        inputSession.setIAppSession(mSession);
-        return SET_TVVIEW_SUCCESS;
-    }
-
-    private int unsetTvView() {
-        if (mSession == null || mSession.getInputSession() == null) {
-            return UNSET_TVVIEW_FAIL;
-        }
-        mSession.getInputSession().setIAppSession(null);
-        mSession.setInputSession(null);
-        return UNSET_TVVIEW_SUCCESS;
-    }
-
-    /**
-     * Callback used to receive various status updates on the {@link TvIAppView}.
-     */
-    public abstract static class TvIAppCallback {
-
-        /**
-         * This is called when a command is requested to be processed by the related TV input.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @param cmdType type of the command
-         * @param parameters parameters of the command
-         */
-        public void onCommandRequest(
-                @NonNull String iAppServiceId,
-                @NonNull @TvIAppService.IAppServiceCommandType String cmdType,
-                @Nullable Bundle parameters) {
-        }
-
-        /**
-         * This is called when the session state is changed.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @param state current session state.
-         */
-        public void onSessionStateChanged(@NonNull String iAppServiceId, int state) {
-        }
-
-        /**
-         * This is called when broadcast-independent (BI) interactive app is created.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @param biIAppUri URI associated this BI interactive app. This is the same URI in
-         *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
-         * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
-         *                 app.
-         */
-        public void onBiInteractiveAppCreated(@NonNull String iAppServiceId, @NonNull Uri biIAppUri,
-                @Nullable String biIAppId) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         */
-        public void onSetVideoBounds(@NonNull String iAppServiceId, @NonNull Rect rect) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is called.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         */
-        public void onRequestCurrentChannelUri(@NonNull String iAppServiceId) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is called.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         */
-        public void onRequestCurrentChannelLcn(@NonNull String iAppServiceId) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is called.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         */
-        public void onRequestStreamVolume(@NonNull String iAppServiceId) {
-        }
-
-        /**
-         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is called.
-         *
-         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         */
-        public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
-        }
-    }
-
-    private class MySessionCallback extends SessionCallback {
-        final String mIAppServiceId;
-        int mType;
-
-        MySessionCallback(String iAppServiceId, int type) {
-            mIAppServiceId = iAppServiceId;
-            mType = type;
-        }
-
-        @Override
-        public void onSessionCreated(Session session) {
-            if (DEBUG) {
-                Log.d(TAG, "onSessionCreated()");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onSessionCreated - session already created");
-                // This callback is obsolete.
-                if (session != null) {
-                    session.release();
-                }
-                return;
-            }
-            mSession = session;
-            if (session != null) {
-                // mSurface may not be ready yet as soon as starting an application.
-                // In the case, we don't send Session.setSurface(null) unnecessarily.
-                // setSessionSurface will be called in surfaceCreated.
-                if (mSurface != null) {
-                    setSessionSurface(mSurface);
-                    if (mSurfaceChanged) {
-                        dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
-                    }
-                }
-                createSessionMediaView();
-            } else {
-                // Failed to create
-                // Todo: forward error to Tv App
-                mSessionCallback = null;
-            }
-        }
-
-        @Override
-        public void onSessionReleased(Session session) {
-            if (DEBUG) {
-                Log.d(TAG, "onSessionReleased()");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onSessionReleased - session not created");
-                return;
-            }
-            mMediaViewCreated = false;
-            mMediaViewFrame = null;
-            mSessionCallback = null;
-            mSession = null;
-        }
-
-        @Override
-        public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
-            if (DEBUG) {
-                Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right="
-                        + right + ", bottom=" + bottom + ",)");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onLayoutSurface - session not created");
-                return;
-            }
-            mSurfaceViewLeft = left;
-            mSurfaceViewTop = top;
-            mSurfaceViewRight = right;
-            mSurfaceViewBottom = bottom;
-            mUseRequestedSurfaceLayout = true;
-            requestLayout();
-        }
-
-        @Override
-        public void onCommandRequest(Session session,
-                @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
-            if (DEBUG) {
-                Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
-                        + parameters.toString() + ")");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onCommandRequest - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
-            }
-        }
-
-        @Override
-        public void onSessionStateChanged(Session session, int state) {
-            if (DEBUG) {
-                Log.d(TAG, "onSessionStateChanged (state=" + state +  ")");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onSessionStateChanged - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onSessionStateChanged(mIAppServiceId, state);
-            }
-        }
-
-        @Override
-        public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
-            if (DEBUG) {
-                Log.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri + ", biIAppId="
-                        + biIAppId + ")");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onBiInteractiveAppCreated - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onBiInteractiveAppCreated(mIAppServiceId, biIAppUri, biIAppId);
-            }
-        }
-
-        @Override
-        public void onSetVideoBounds(Session session, Rect rect) {
-            if (DEBUG) {
-                Log.d(TAG, "onSetVideoBounds (rect=" + rect + ")");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onSetVideoBounds - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onSetVideoBounds(mIAppServiceId, rect);
-            }
-        }
-
-        @Override
-        public void onRequestCurrentChannelUri(Session session) {
-            if (DEBUG) {
-                Log.d(TAG, "onRequestCurrentChannelUri");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onRequestCurrentChannelUri - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onRequestCurrentChannelUri(mIAppServiceId);
-            }
-        }
-
-        @Override
-        public void onRequestCurrentChannelLcn(Session session) {
-            if (DEBUG) {
-                Log.d(TAG, "onRequestCurrentChannelLcn");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onRequestCurrentChannelLcn - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
-            }
-        }
-
-        @Override
-        public void onRequestStreamVolume(Session session) {
-            if (DEBUG) {
-                Log.d(TAG, "onRequestStreamVolume");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onRequestStreamVolume - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onRequestStreamVolume(mIAppServiceId);
-            }
-        }
-
-        @Override
-        public void onRequestTrackInfoList(Session session) {
-            if (DEBUG) {
-                Log.d(TAG, "onRequestTrackInfoList");
-            }
-            if (this != mSessionCallback) {
-                Log.w(TAG, "onRequestTrackInfoList - session not created");
-                return;
-            }
-            if (mCallback != null) {
-                mCallback.onRequestTrackInfoList(mIAppServiceId);
-            }
-        }
-    }
-}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
new file mode 100644
index 0000000..5e15016
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+parcelable TvInteractiveAppInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
new file mode 100644
index 0000000..e1f535c
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV interactive app.
+ */
+public final class TvInteractiveAppInfo implements Parcelable {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvInteractiveAppInfo";
+
+    private static final String XML_START_TAG_NAME = "tv-interactive-app";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
+            INTERACTIVE_APP_TYPE_HBBTV,
+            INTERACTIVE_APP_TYPE_ATSC,
+            INTERACTIVE_APP_TYPE_GINGA,
+    })
+    public @interface InteractiveAppType {}
+
+    /** HbbTV interactive app type */
+    public static final int INTERACTIVE_APP_TYPE_HBBTV = 0x1;
+    /** ATSC interactive app type */
+    public static final int INTERACTIVE_APP_TYPE_ATSC = 0x2;
+    /** Ginga interactive app type */
+    public static final int INTERACTIVE_APP_TYPE_GINGA = 0x4;
+
+    private final ResolveInfo mService;
+    private final String mId;
+    private int mTypes;
+
+    public TvInteractiveAppInfo(@NonNull Context context, @NonNull ComponentName component) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null.");
+        }
+        Intent intent =
+                new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
+        ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        if (resolveInfo == null) {
+            throw new IllegalArgumentException("Invalid component. Can't find the service.");
+        }
+
+        ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+                resolveInfo.serviceInfo.name);
+        String id;
+        id = generateInteractiveAppServiceId(componentName);
+        List<String> types = new ArrayList<>();
+        parseServiceMetadata(resolveInfo, context, types);
+
+        mService = resolveInfo;
+        mId = id;
+        mTypes = toTypesFlag(types);
+    }
+    private TvInteractiveAppInfo(ResolveInfo service, String id, int types) {
+        mService = service;
+        mId = id;
+        mTypes = types;
+    }
+
+    private TvInteractiveAppInfo(@NonNull Parcel in) {
+        mService = ResolveInfo.CREATOR.createFromParcel(in);
+        mId = in.readString();
+        mTypes = in.readInt();
+    }
+
+    public static final @NonNull Creator<TvInteractiveAppInfo> CREATOR =
+            new Creator<TvInteractiveAppInfo>() {
+                @Override
+                public TvInteractiveAppInfo createFromParcel(Parcel in) {
+                    return new TvInteractiveAppInfo(in);
+                }
+
+                @Override
+                public TvInteractiveAppInfo[] newArray(int size) {
+                    return new TvInteractiveAppInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mId);
+        dest.writeInt(mTypes);
+    }
+
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the component of the TV Interactive App service.
+     * @hide
+     */
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+    }
+
+    /**
+     * Returns the information of the service that implements this TV Interactive App service.
+     */
+    @Nullable
+    public ServiceInfo getServiceInfo() {
+        return mService.serviceInfo;
+    }
+
+    /**
+     * Gets supported interactive app types
+     */
+    @InteractiveAppType
+    @NonNull
+    public int getSupportedTypes() {
+        return mTypes;
+    }
+
+    private static String generateInteractiveAppServiceId(ComponentName name) {
+        return name.flattenToShortString();
+    }
+
+    private static void parseServiceMetadata(
+            ResolveInfo resolveInfo, Context context, List<String> types) {
+        ServiceInfo si = resolveInfo.serviceInfo;
+        PackageManager pm = context.getPackageManager();
+        try (XmlResourceParser parser =
+                     si.loadXmlMetaData(pm, TvInteractiveAppService.SERVICE_META_DATA)) {
+            if (parser == null) {
+                throw new IllegalStateException(
+                        "No " + TvInteractiveAppService.SERVICE_META_DATA
+                        + " meta-data found for " + si.name);
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // move to the START_TAG
+            }
+
+            String nodeName = parser.getName();
+            if (!XML_START_TAG_NAME.equals(nodeName)) {
+                throw new IllegalStateException("Meta-data does not start with "
+                        + XML_START_TAG_NAME + " tag for " + si.name);
+            }
+
+            TypedArray sa = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.TvInteractiveAppService);
+            CharSequence[] textArr = sa.getTextArray(
+                    com.android.internal.R.styleable.TvInteractiveAppService_supportedTypes);
+            for (CharSequence cs : textArr) {
+                types.add(cs.toString().toLowerCase());
+            }
+
+            sa.recycle();
+        } catch (IOException | XmlPullParserException e) {
+            throw new IllegalStateException(
+                    "Failed reading meta-data for " + si.packageName, e);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("No resources found for " + si.packageName, e);
+        }
+    }
+
+    private static int toTypesFlag(List<String> types) {
+        int flag = 0;
+        for (String type : types) {
+            switch (type) {
+                case "hbbtv":
+                    flag |= INTERACTIVE_APP_TYPE_HBBTV;
+                    break;
+                case "atsc":
+                    flag |= INTERACTIVE_APP_TYPE_ATSC;
+                    break;
+                case "ginga":
+                    flag |= INTERACTIVE_APP_TYPE_GINGA;
+                    break;
+                default:
+                    break;
+            }
+        }
+        return flag;
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
new file mode 100755
index 0000000..39be501
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -0,0 +1,1821 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pools;
+import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventSender;
+import android.view.Surface;
+import android.view.View;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Central system API to the overall TV interactive application framework (TIAF) architecture, which
+ * arbitrates interaction between applications and interactive apps.
+ */
+@SystemService(Context.TV_INTERACTIVE_APP_SERVICE)
+public final class TvInteractiveAppManager {
+    // TODO: cleanup and unhide public APIs
+    private static final String TAG = "TvInteractiveAppManager";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "SERVICE_STATE_", value = {
+            SERVICE_STATE_UNREALIZED,
+            SERVICE_STATE_PREPARING,
+            SERVICE_STATE_READY,
+            SERVICE_STATE_ERROR})
+    public @interface ServiceState {}
+
+    /**
+     * Unrealized state of interactive app service.
+     * @hide
+     */
+    public static final int SERVICE_STATE_UNREALIZED = 1;
+    /**
+     * Preparing state of interactive app service.
+     * @hide
+     */
+    public static final int SERVICE_STATE_PREPARING = 2;
+    /**
+     * Ready state of interactive app service.
+     * @hide
+     */
+    public static final int SERVICE_STATE_READY = 3;
+    /**
+     * Error state of interactive app service.
+     * @hide
+     */
+    public static final int SERVICE_STATE_ERROR = 4;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "INTERACTIVE_APP_STATE_", value = {
+            INTERACTIVE_APP_STATE_STOPPED,
+            INTERACTIVE_APP_STATE_RUNNING,
+            INTERACTIVE_APP_STATE_ERROR})
+    public @interface InteractiveAppState {}
+
+    /**
+     * Stopped (or not started) state of interactive application.
+     * @hide
+     */
+    public static final int INTERACTIVE_APP_STATE_STOPPED = 1;
+    /**
+     * Running state of interactive application.
+     * @hide
+     */
+    public static final int INTERACTIVE_APP_STATE_RUNNING = 2;
+    /**
+     * Error state of interactive application.
+     * @hide
+     */
+    public static final int INTERACTIVE_APP_STATE_ERROR = 3;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "ERROR_", value = {
+            ERROR_NONE,
+            ERROR_UNKNOWN,
+            ERROR_NOT_SUPPORTED,
+            ERROR_WEAK_SIGNAL,
+            ERROR_RESOURCE_UNAVAILABLE,
+            ERROR_BLOCKED,
+            ERROR_ENCRYPTED,
+            ERROR_UNKNOWN_CHANNEL,
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * No error.
+     * @hide
+     */
+    public static final int ERROR_NONE = 0;
+    /**
+     * Unknown error code.
+     * @hide
+     */
+    public static final int ERROR_UNKNOWN = 1;
+    /**
+     * Error code for an unsupported channel.
+     * @hide
+     */
+    public static final int ERROR_NOT_SUPPORTED = 2;
+    /**
+     * Error code for weak signal.
+     * @hide
+     */
+    public static final int ERROR_WEAK_SIGNAL = 3;
+    /**
+     * Error code when resource (e.g. tuner) is unavailable.
+     * @hide
+     */
+    public static final int ERROR_RESOURCE_UNAVAILABLE = 4;
+    /**
+     * Error code for blocked contents.
+     * @hide
+     */
+    public static final int ERROR_BLOCKED = 5;
+    /**
+     * Error code when the key or module is missing for the encrypted channel.
+     * @hide
+     */
+    public static final int ERROR_ENCRYPTED = 6;
+    /**
+     * Error code when the current channel is an unknown channel.
+     * @hide
+     */
+    public static final int ERROR_UNKNOWN_CHANNEL = 7;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "TELETEXT_APP_STATE_", value = {
+            TELETEXT_APP_STATE_SHOW,
+            TELETEXT_APP_STATE_HIDE,
+            TELETEXT_APP_STATE_ERROR})
+    public @interface TeletextAppState {}
+
+    /**
+     * Show state of Teletext app.
+     * @hide
+     */
+    public static final int TELETEXT_APP_STATE_SHOW = 1;
+    /**
+     * Hide state of Teletext app.
+     * @hide
+     */
+    public static final int TELETEXT_APP_STATE_HIDE = 2;
+    /**
+     * Error state of Teletext app.
+     * @hide
+     */
+    public static final int TELETEXT_APP_STATE_ERROR = 3;
+
+    /**
+     * Key for package name in app link.
+     * <p>Type: String
+     *
+     * @see #registerAppLinkInfo(String, Bundle)
+     * @see #sendAppLinkCommand(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_PACKAGE_NAME = "package_name";
+
+    /**
+     * Key for class name in app link.
+     * <p>Type: String
+     *
+     * @see #registerAppLinkInfo(String, Bundle)
+     * @see #sendAppLinkCommand(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_CLASS_NAME = "class_name";
+
+    /**
+     * Key for URI scheme in app link.
+     * <p>Type: String
+     *
+     * @see #registerAppLinkInfo(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_URI_SCHEME = "uri_scheme";
+
+    /**
+     * Key for URI host in app link.
+     * <p>Type: String
+     *
+     * @see #registerAppLinkInfo(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_URI_HOST = "uri_host";
+
+    /**
+     * Key for URI prefix in app link.
+     * <p>Type: String
+     *
+     * @see #registerAppLinkInfo(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_URI_PREFIX = "uri_prefix";
+
+    /**
+     * Key for command type in app link command.
+     * <p>Type: String
+     *
+     * @see #sendAppLinkCommand(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_COMMAND_TYPE = "command_type";
+
+    /**
+     * Key for service ID in app link command.
+     * <p>Type: String
+     *
+     * @see #sendAppLinkCommand(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_SERVICE_ID = "service_id";
+
+    /**
+     * Key for back URI in app link command.
+     * <p>Type: String
+     *
+     * @see #sendAppLinkCommand(String, Bundle)
+     * @hide
+     */
+    public static final String KEY_BACK_URI = "back_uri";
+
+    private final ITvInteractiveAppManager mService;
+    private final int mUserId;
+
+    // A mapping from the sequence number of a session to its SessionCallbackRecord.
+    private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
+            new SparseArray<>();
+
+    // @GuardedBy("mLock")
+    private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new LinkedList<>();
+
+    // A sequence number for the next session to be created. Should be protected by a lock
+    // {@code mSessionCallbackRecordMap}.
+    private int mNextSeq;
+
+    private final Object mLock = new Object();
+
+    private final ITvInteractiveAppClient mClient;
+
+    /** @hide */
+    public TvInteractiveAppManager(ITvInteractiveAppManager service, int userId) {
+        mService = service;
+        mUserId = userId;
+        mClient = new ITvInteractiveAppClient.Stub() {
+            @Override
+            public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel,
+                    int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for " + token);
+                        return;
+                    }
+                    Session session = null;
+                    if (token != null) {
+                        session = new Session(token, channel, mService, mUserId, seq,
+                                mSessionCallbackRecordMap);
+                    } else {
+                        mSessionCallbackRecordMap.delete(seq);
+                    }
+                    record.postSessionCreated(session);
+                }
+            }
+
+            @Override
+            public void onSessionReleased(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    mSessionCallbackRecordMap.delete(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq:" + seq);
+                        return;
+                    }
+                    record.mSession.releaseInternal();
+                    record.postSessionReleased();
+                }
+            }
+
+            @Override
+            public void onLayoutSurface(int left, int top, int right, int bottom, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postLayoutSurface(left, top, right, bottom);
+                }
+            }
+
+            @Override
+            public void onBroadcastInfoRequest(BroadcastInfoRequest request, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postBroadcastInfoRequest(request);
+                }
+            }
+
+            @Override
+            public void onRemoveBroadcastInfo(int requestId, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRemoveBroadcastInfo(requestId);
+                }
+            }
+
+            @Override
+            public void onCommandRequest(
+                    @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
+                    Bundle parameters,
+                    int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postCommandRequest(cmdType, parameters);
+                }
+            }
+
+            @Override
+            public void onSetVideoBounds(Rect rect, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postSetVideoBounds(rect);
+                }
+            }
+
+            @Override
+            public void onAdRequest(AdRequest request, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postAdRequest(request);
+                }
+            }
+
+            @Override
+            public void onRequestCurrentChannelUri(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestCurrentChannelUri();
+                }
+            }
+
+            @Override
+            public void onRequestCurrentChannelLcn(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestCurrentChannelLcn();
+                }
+            }
+
+            @Override
+            public void onRequestStreamVolume(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestStreamVolume();
+                }
+            }
+
+            @Override
+            public void onRequestTrackInfoList(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestTrackInfoList();
+                }
+            }
+
+            @Override
+            public void onRequestCurrentTvInputId(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestCurrentTvInputId();
+                }
+            }
+
+            @Override
+            public void onSessionStateChanged(int state, int err, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postSessionStateChanged(state, err);
+                }
+            }
+
+            @Override
+            public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postBiInteractiveAppCreated(biIAppUri, biIAppId);
+                }
+            }
+
+            @Override
+            public void onTeletextAppStateChanged(int state, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postTeletextAppStateChanged(state);
+                }
+            }
+        };
+        ITvInteractiveAppManagerCallback managerCallback =
+                new ITvInteractiveAppManagerCallback.Stub() {
+            @Override
+            public void onInteractiveAppServiceAdded(String iAppServiceId) {
+                synchronized (mLock) {
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postInteractiveAppServiceAdded(iAppServiceId);
+                    }
+                }
+            }
+
+            @Override
+            public void onInteractiveAppServiceRemoved(String iAppServiceId) {
+                synchronized (mLock) {
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postInteractiveAppServiceRemoved(iAppServiceId);
+                    }
+                }
+            }
+
+            @Override
+            public void onInteractiveAppServiceUpdated(String iAppServiceId) {
+                synchronized (mLock) {
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postInteractiveAppServiceUpdated(iAppServiceId);
+                    }
+                }
+            }
+
+            @Override
+            public void onTvInteractiveAppInfoUpdated(TvInteractiveAppInfo iAppInfo) {
+                // TODO: add public API updateInteractiveAppInfo()
+                synchronized (mLock) {
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postTvInteractiveAppInfoUpdated(iAppInfo);
+                    }
+                }
+            }
+
+            @Override
+            public void onStateChanged(String iAppServiceId, int type, int state, int err) {
+                synchronized (mLock) {
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postStateChanged(iAppServiceId, type, state, err);
+                    }
+                }
+            }
+        };
+        try {
+            if (mService != null) {
+                mService.registerCallback(managerCallback, mUserId);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Callback used to monitor status of the TV Interactive App.
+     * @hide
+     */
+    public abstract static class TvInteractiveAppCallback {
+        /**
+         * This is called when a TV Interactive App service is added to the system.
+         *
+         * <p>Normally it happens when the user installs a new TV Interactive App service package
+         * that implements {@link TvInteractiveAppService} interface.
+         *
+         * @param iAppServiceId The ID of the TV Interactive App service.
+         * @hide
+         */
+        public void onInteractiveAppServiceAdded(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when a TV Interactive App service is removed from the system.
+         *
+         * <p>Normally it happens when the user uninstalls the previously installed TV Interactive
+         * App service package.
+         *
+         * @param iAppServiceId The ID of the TV Interactive App service.
+         * @hide
+         */
+        public void onInteractiveAppServiceRemoved(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when a TV Interactive App service is updated on the system.
+         *
+         * <p>Normally it happens when a previously installed TV Interactive App service package is
+         * re-installed or a newer version of the package exists becomes available/unavailable.
+         *
+         * @param iAppServiceId The ID of the TV Interactive App service.
+         * @hide
+         */
+        public void onInteractiveAppServiceUpdated(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when the information about an existing TV Interactive App service has been
+         * updated.
+         *
+         * <p>Because the system automatically creates a <code>TvInteractiveAppInfo</code> object
+         * for each TV Interactive App service based on the information collected from the
+         * <code>AndroidManifest.xml</code>, this method is only called back when such information
+         * has changed dynamically.
+         *
+         * @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new
+         *                 information.
+         * @hide
+         */
+        public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) {
+        }
+
+        /**
+         * This is called when the state of the interactive app service is changed.
+         *
+         * @param type the interactive app type
+         * @param state the current state of the service of the given type
+         * @param err the error code for error state. {@link #ERROR_NONE} is used when the state is
+         *            not {@link #SERVICE_STATE_ERROR}.
+         */
+        public void onTvInteractiveAppServiceStateChanged(
+                @NonNull String iAppServiceId,
+                @TvInteractiveAppInfo.InteractiveAppType int type,
+                @ServiceState int state,
+                @ErrorCode int err) {
+        }
+    }
+
+    private static final class TvInteractiveAppCallbackRecord {
+        private final TvInteractiveAppCallback mCallback;
+        private final Executor mExecutor;
+
+        TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        public TvInteractiveAppCallback getCallback() {
+            return mCallback;
+        }
+
+        public void postInteractiveAppServiceAdded(final String iAppServiceId) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onInteractiveAppServiceAdded(iAppServiceId);
+                }
+            });
+        }
+
+        public void postInteractiveAppServiceRemoved(final String iAppServiceId) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onInteractiveAppServiceRemoved(iAppServiceId);
+                }
+            });
+        }
+
+        public void postInteractiveAppServiceUpdated(final String iAppServiceId) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onInteractiveAppServiceUpdated(iAppServiceId);
+                }
+            });
+        }
+
+        public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onTvInteractiveAppInfoUpdated(iAppInfo);
+                }
+            });
+        }
+
+        public void postStateChanged(String iAppServiceId, int type, int state, int err) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onTvInteractiveAppServiceStateChanged(
+                            iAppServiceId, type, state, err);
+                }
+            });
+        }
+    }
+
+    /**
+     * Creates a {@link Session} for a given TV interactive application.
+     *
+     * <p>The number of sessions that can be created at the same time is limited by the capability
+     * of the given interactive application.
+     *
+     * @param iAppServiceId The ID of the interactive application.
+     * @param type the type of the interactive application.
+     * @param callback A callback used to receive the created session.
+     * @param handler A {@link Handler} that the session creation will be delivered to.
+     * @hide
+     */
+    public void createSession(@NonNull String iAppServiceId, int type,
+            @NonNull final SessionCallback callback, @NonNull Handler handler) {
+        createSessionInternal(iAppServiceId, type, callback, handler);
+    }
+
+    private void createSessionInternal(String iAppServiceId, int type, SessionCallback callback,
+            Handler handler) {
+        Preconditions.checkNotNull(iAppServiceId);
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(handler);
+        SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
+        synchronized (mSessionCallbackRecordMap) {
+            int seq = mNextSeq++;
+            mSessionCallbackRecordMap.put(seq, record);
+            try {
+                mService.createSession(mClient, iAppServiceId, type, seq, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the complete list of TV Interactive App service on the system.
+     *
+     * @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that
+     *         describes its meta information.
+     */
+    @NonNull
+    public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() {
+        try {
+            return mService.getTvInteractiveAppServiceList(mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Prepares TV Interactive App service for the given type.
+     * @hide
+     */
+    public void prepare(@NonNull String tvIAppServiceId, int type) {
+        try {
+            mService.prepare(tvIAppServiceId, type, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers app link info.
+     * @hide
+     */
+    public void registerAppLinkInfo(
+            @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) {
+        try {
+            mService.registerAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters app link info.
+     * @hide
+     */
+    public void unregisterAppLinkInfo(
+            @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) {
+        try {
+            mService.unregisterAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sends app link command.
+     * @hide
+     */
+    public void sendAppLinkCommand(String tvIAppServiceId, Bundle command) {
+        try {
+            mService.sendAppLinkCommand(tvIAppServiceId, command, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a {@link TvInteractiveAppCallback}.
+     *
+     * @param callback A callback used to monitor status of the TV Interactive App services.
+     * @param executor A {@link Executor} that the status change will be delivered to.
+     * @hide
+     */
+    public void registerCallback(
+            @NonNull TvInteractiveAppCallback callback,
+            @CallbackExecutor @NonNull Executor executor) {
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(executor);
+        synchronized (mLock) {
+            mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, executor));
+        }
+    }
+
+    /**
+     * Unregisters the existing {@link TvInteractiveAppCallback}.
+     *
+     * @param callback The existing callback to remove.
+     * @hide
+     */
+    public void unregisterCallback(@NonNull final TvInteractiveAppCallback callback) {
+        Preconditions.checkNotNull(callback);
+        synchronized (mLock) {
+            for (Iterator<TvInteractiveAppCallbackRecord> it = mCallbackRecords.iterator();
+                    it.hasNext(); ) {
+                TvInteractiveAppCallbackRecord record = it.next();
+                if (record.getCallback() == callback) {
+                    it.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * The Session provides the per-session functionality of interactive app.
+     * @hide
+     */
+    public static final class Session {
+        static final int DISPATCH_IN_PROGRESS = -1;
+        static final int DISPATCH_NOT_HANDLED = 0;
+        static final int DISPATCH_HANDLED = 1;
+
+        private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
+
+        private final ITvInteractiveAppManager mService;
+        private final int mUserId;
+        private final int mSeq;
+        private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
+
+        // For scheduling input event handling on the main thread. This also serves as a lock to
+        // protect pending input events and the input channel.
+        private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
+
+        private TvInputManager.Session mInputSession;
+        private final Pools.Pool<PendingEvent> mPendingEventPool = new Pools.SimplePool<>(20);
+        private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
+
+        private IBinder mToken;
+        private TvInputEventSender mSender;
+        private InputChannel mInputChannel;
+
+        private Session(IBinder token, InputChannel channel, ITvInteractiveAppManager service,
+                int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
+            mToken = token;
+            mInputChannel = channel;
+            mService = service;
+            mUserId = userId;
+            mSeq = seq;
+            mSessionCallbackRecordMap = sessionCallbackRecordMap;
+        }
+
+        public TvInputManager.Session getInputSession() {
+            return mInputSession;
+        }
+
+        public void setInputSession(TvInputManager.Session inputSession) {
+            mInputSession = inputSession;
+        }
+
+        void startInteractiveApp() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.startInteractiveApp(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void stopInteractiveApp() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.stopInteractiveApp(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void resetInteractiveApp() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.resetInteractiveApp(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void createBiInteractiveApp(Uri biIAppUri, Bundle params) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.createBiInteractiveApp(mToken, biIAppUri, params, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void destroyBiInteractiveApp(String biIAppId) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.destroyBiInteractiveApp(mToken, biIAppId, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void setTeletextAppEnabled(boolean enable) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.setTeletextAppEnabled(mToken, enable, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void sendCurrentChannelUri(@Nullable Uri channelUri) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.sendCurrentChannelUri(mToken, channelUri, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void sendCurrentChannelLcn(int lcn) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.sendCurrentChannelLcn(mToken, lcn, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void sendStreamVolume(float volume) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.sendStreamVolume(mToken, volume, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.sendTrackInfoList(mToken, tracks, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void sendCurrentTvInputId(@Nullable String inputId) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.sendCurrentTvInputId(mToken, inputId, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Sets the {@link android.view.Surface} for this session.
+         *
+         * @param surface A {@link android.view.Surface} used to render video.
+         */
+        public void setSurface(Surface surface) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            // surface can be null.
+            try {
+                mService.setSurface(mToken, surface, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Creates a media view. Once the media view is created, {@link #relayoutMediaView}
+         * should be called whenever the layout of its containing view is changed.
+         * {@link #removeMediaView()} should be called to remove the media view.
+         * Since a session can have only one media view, this method should be called only once
+         * or it can be called again after calling {@link #removeMediaView()}.
+         *
+         * @param view A view for interactive app.
+         * @param frame A position of the media view.
+         * @throws IllegalStateException if {@code view} is not attached to a window.
+         */
+        void createMediaView(@NonNull View view, @NonNull Rect frame) {
+            Preconditions.checkNotNull(view);
+            Preconditions.checkNotNull(frame);
+            if (view.getWindowToken() == null) {
+                throw new IllegalStateException("view must be attached to a window");
+            }
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.createMediaView(mToken, view.getWindowToken(), frame, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Relayouts the current media view.
+         *
+         * @param frame A new position of the media view.
+         */
+        void relayoutMediaView(@NonNull Rect frame) {
+            Preconditions.checkNotNull(frame);
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.relayoutMediaView(mToken, frame, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Removes the current media view.
+         */
+        void removeMediaView() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.removeMediaView(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies of any structural changes (format or size) of the surface passed in
+         * {@link #setSurface}.
+         *
+         * @param format The new PixelFormat of the surface.
+         * @param width The new width of the surface.
+         * @param height The new height of the surface.
+         */
+        public void dispatchSurfaceChanged(int format, int width, int height) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Dispatches an input event to this session.
+         *
+         * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
+         * @param token A token used to identify the input event later in the callback.
+         * @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
+         * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
+         *            {@code null}.
+         * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
+         *         {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
+         *         {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
+         *         be invoked later.
+         * @hide
+         */
+        public int dispatchInputEvent(@NonNull InputEvent event, Object token,
+                @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
+            Preconditions.checkNotNull(event);
+            Preconditions.checkNotNull(callback);
+            Preconditions.checkNotNull(handler);
+            synchronized (mHandler) {
+                if (mInputChannel == null) {
+                    return DISPATCH_NOT_HANDLED;
+                }
+                PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
+                if (Looper.myLooper() == Looper.getMainLooper()) {
+                    // Already running on the main thread so we can send the event immediately.
+                    return sendInputEventOnMainLooperLocked(p);
+                }
+
+                // Post the event to the main thread.
+                Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
+                msg.setAsynchronous(true);
+                mHandler.sendMessage(msg);
+                return DISPATCH_IN_PROGRESS;
+            }
+        }
+
+        /**
+         * Notifies of any broadcast info response passed in from TIS.
+         *
+         * @param response response passed in from TIS.
+         */
+        public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyBroadcastInfoResponse(mToken, response, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies of any advertisement response passed in from TIS.
+         *
+         * @param response response passed in from TIS.
+         */
+        public void notifyAdResponse(AdResponse response) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyAdResponse(mToken, response, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Releases this session.
+         */
+        public void release() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.releaseSession(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+            releaseInternal();
+        }
+
+        /**
+         * Notifies Interactive APP session when a channel is tuned.
+         */
+        public void notifyTuned(Uri channelUri) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyTuned(mToken, channelUri, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when a track is selected.
+         */
+        public void notifyTrackSelected(int type, String trackId) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyTrackSelected(mToken, type, trackId, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when tracks are changed.
+         */
+        public void notifyTracksChanged(List<TvTrackInfo> tracks) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyTracksChanged(mToken, tracks, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when video is available.
+         */
+        public void notifyVideoAvailable() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyVideoAvailable(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when video is unavailable.
+         */
+        public void notifyVideoUnavailable(int reason) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyVideoUnavailable(mToken, reason, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when content is allowed.
+         */
+        public void notifyContentAllowed() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyContentAllowed(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when content is blocked.
+         */
+        public void notifyContentBlocked(TvContentRating rating) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyContentBlocked(mToken, rating.flattenToString(), mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies Interactive APP session when signal strength is changed.
+         */
+        public void notifySignalStrength(int strength) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifySignalStrength(mToken, strength, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        private void flushPendingEventsLocked() {
+            mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
+
+            final int count = mPendingEvents.size();
+            for (int i = 0; i < count; i++) {
+                int seq = mPendingEvents.keyAt(i);
+                Message msg = mHandler.obtainMessage(
+                        InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
+                msg.setAsynchronous(true);
+                msg.sendToTarget();
+            }
+        }
+
+        private void releaseInternal() {
+            mToken = null;
+            synchronized (mHandler) {
+                if (mInputChannel != null) {
+                    if (mSender != null) {
+                        flushPendingEventsLocked();
+                        mSender.dispose();
+                        mSender = null;
+                    }
+                    mInputChannel.dispose();
+                    mInputChannel = null;
+                }
+            }
+            synchronized (mSessionCallbackRecordMap) {
+                mSessionCallbackRecordMap.delete(mSeq);
+            }
+        }
+
+        private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+                FinishedInputEventCallback callback, Handler handler) {
+            PendingEvent p = mPendingEventPool.acquire();
+            if (p == null) {
+                p = new PendingEvent();
+            }
+            p.mEvent = event;
+            p.mEventToken = token;
+            p.mCallback = callback;
+            p.mEventHandler = handler;
+            return p;
+        }
+
+        // Assumes the event has already been removed from the queue.
+        void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+            p.mHandled = handled;
+            if (p.mEventHandler.getLooper().isCurrentThread()) {
+                // Already running on the callback handler thread so we can send the callback
+                // immediately.
+                p.run();
+            } else {
+                // Post the event to the callback handler thread.
+                // In this case, the callback will be responsible for recycling the event.
+                Message msg = Message.obtain(p.mEventHandler, p);
+                msg.setAsynchronous(true);
+                msg.sendToTarget();
+            }
+        }
+
+        // Must be called on the main looper
+        private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+            synchronized (mHandler) {
+                int result = sendInputEventOnMainLooperLocked(p);
+                if (result == DISPATCH_IN_PROGRESS) {
+                    return;
+                }
+            }
+
+            invokeFinishedInputEventCallback(p, false);
+        }
+
+        private int sendInputEventOnMainLooperLocked(PendingEvent p) {
+            if (mInputChannel != null) {
+                if (mSender == null) {
+                    mSender = new TvInputEventSender(mInputChannel, mHandler.getLooper());
+                }
+
+                final InputEvent event = p.mEvent;
+                final int seq = event.getSequenceNumber();
+                if (mSender.sendInputEvent(seq, event)) {
+                    mPendingEvents.put(seq, p);
+                    Message msg = mHandler.obtainMessage(
+                            InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+                    msg.setAsynchronous(true);
+                    mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
+                    return DISPATCH_IN_PROGRESS;
+                }
+
+                Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
+                        + event);
+            }
+            return DISPATCH_NOT_HANDLED;
+        }
+
+        void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+            final PendingEvent p;
+            synchronized (mHandler) {
+                int index = mPendingEvents.indexOfKey(seq);
+                if (index < 0) {
+                    return; // spurious, event already finished or timed out
+                }
+
+                p = mPendingEvents.valueAt(index);
+                mPendingEvents.removeAt(index);
+
+                if (timeout) {
+                    Log.w(TAG, "Timeout waiting for session to handle input event after "
+                            + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
+                } else {
+                    mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+                }
+            }
+
+            invokeFinishedInputEventCallback(p, handled);
+        }
+
+        private void recyclePendingEventLocked(PendingEvent p) {
+            p.recycle();
+            mPendingEventPool.release(p);
+        }
+
+        /**
+         * Callback that is invoked when an input event that was dispatched to this session has been
+         * finished.
+         *
+         * @hide
+         */
+        public interface FinishedInputEventCallback {
+            /**
+             * Called when the dispatched input event is finished.
+             *
+             * @param token A token passed to {@link #dispatchInputEvent}.
+             * @param handled {@code true} if the dispatched input event was handled properly.
+             *            {@code false} otherwise.
+             */
+            void onFinishedInputEvent(Object token, boolean handled);
+        }
+
+        private final class InputEventHandler extends Handler {
+            public static final int MSG_SEND_INPUT_EVENT = 1;
+            public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
+            public static final int MSG_FLUSH_INPUT_EVENT = 3;
+
+            InputEventHandler(Looper looper) {
+                super(looper, null, true);
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_SEND_INPUT_EVENT: {
+                        sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
+                        return;
+                    }
+                    case MSG_TIMEOUT_INPUT_EVENT: {
+                        finishedInputEvent(msg.arg1, false, true);
+                        return;
+                    }
+                    case MSG_FLUSH_INPUT_EVENT: {
+                        finishedInputEvent(msg.arg1, false, false);
+                        return;
+                    }
+                }
+            }
+        }
+
+        private final class TvInputEventSender extends InputEventSender {
+            TvInputEventSender(InputChannel inputChannel, Looper looper) {
+                super(inputChannel, looper);
+            }
+
+            @Override
+            public void onInputEventFinished(int seq, boolean handled) {
+                finishedInputEvent(seq, handled, false);
+            }
+        }
+
+        private final class PendingEvent implements Runnable {
+            public InputEvent mEvent;
+            public Object mEventToken;
+            public FinishedInputEventCallback mCallback;
+            public Handler mEventHandler;
+            public boolean mHandled;
+
+            public void recycle() {
+                mEvent = null;
+                mEventToken = null;
+                mCallback = null;
+                mEventHandler = null;
+                mHandled = false;
+            }
+
+            @Override
+            public void run() {
+                mCallback.onFinishedInputEvent(mEventToken, mHandled);
+
+                synchronized (mEventHandler) {
+                    recyclePendingEventLocked(this);
+                }
+            }
+        }
+    }
+
+    private static final class SessionCallbackRecord {
+        private final SessionCallback mSessionCallback;
+        private final Handler mHandler;
+        private Session mSession;
+
+        SessionCallbackRecord(SessionCallback sessionCallback, Handler handler) {
+            mSessionCallback = sessionCallback;
+            mHandler = handler;
+        }
+
+        void postSessionCreated(final Session session) {
+            mSession = session;
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionCreated(session);
+                }
+            });
+        }
+
+        void postSessionReleased() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionReleased(mSession);
+                }
+            });
+        }
+
+        void postLayoutSurface(final int left, final int top, final int right,
+                final int bottom) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom);
+                }
+            });
+        }
+
+        void postBroadcastInfoRequest(final BroadcastInfoRequest request) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSession.getInputSession().requestBroadcastInfo(request);
+                }
+            });
+        }
+
+        void postRemoveBroadcastInfo(final int requestId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSession.getInputSession().removeBroadcastInfo(requestId);
+                }
+            });
+        }
+
+        void postCommandRequest(
+                final @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
+                final Bundle parameters) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onCommandRequest(mSession, cmdType, parameters);
+                }
+            });
+        }
+
+        void postSetVideoBounds(Rect rect) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSetVideoBounds(mSession, rect);
+                }
+            });
+        }
+
+        void postRequestCurrentChannelUri() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestCurrentChannelUri(mSession);
+                }
+            });
+        }
+
+        void postRequestCurrentChannelLcn() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestCurrentChannelLcn(mSession);
+                }
+            });
+        }
+
+        void postRequestStreamVolume() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestStreamVolume(mSession);
+                }
+            });
+        }
+
+        void postRequestTrackInfoList() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestTrackInfoList(mSession);
+                }
+            });
+        }
+
+        void postRequestCurrentTvInputId() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestCurrentTvInputId(mSession);
+                }
+            });
+        }
+
+        void postAdRequest(final AdRequest request) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mSession.getInputSession() != null) {
+                        mSession.getInputSession().requestAd(request);
+                    }
+                }
+            });
+        }
+
+        void postSessionStateChanged(int state, int err) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionStateChanged(mSession, state, err);
+                }
+            });
+        }
+
+        void postBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onBiInteractiveAppCreated(mSession, biIAppUri, biIAppId);
+                }
+            });
+        }
+
+        void postTeletextAppStateChanged(int state) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onTeletextAppStateChanged(mSession, state);
+                }
+            });
+        }
+    }
+
+    /**
+     * Interface used to receive the created session.
+     * @hide
+     */
+    public abstract static class SessionCallback {
+        /**
+         * This is called after {@link TvInteractiveAppManager#createSession} has been processed.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} instance created. This can be
+         *                {@code null} if the creation request failed.
+         */
+        public void onSessionCreated(@Nullable Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppManager.Session} is released.
+         * This typically happens when the process hosting the session has crashed or been killed.
+         *
+         * @param session the {@link TvInteractiveAppManager.Session} instance released.
+         */
+        public void onSessionReleased(@NonNull Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#layoutSurface} is called to
+         * change the layout of surface.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         * @param left Left position.
+         * @param top Top position.
+         * @param right Right position.
+         * @param bottom Bottom position.
+         */
+        public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#requestCommand} is called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         * @param cmdType type of the command.
+         * @param parameters parameters of the command.
+         */
+        public void onCommandRequest(
+                Session session,
+                @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
+                Bundle parameters) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         */
+        public void onSetVideoBounds(Session session, Rect rect) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         */
+        public void onRequestCurrentChannelUri(Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         */
+        public void onRequestCurrentChannelLcn(Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         */
+        public void onRequestStreamVolume(Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         */
+        public void onRequestTrackInfoList(Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentTvInputId} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+         * @hide
+         */
+        public void onRequestCurrentTvInputId(Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         * @param state the current state.
+         */
+        public void onSessionStateChanged(
+                Session session,
+                @InteractiveAppState int state,
+                @ErrorCode int err) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#notifyBiInteractiveAppCreated}
+         * is called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         * @param biIAppUri URI associated this BI interactive app. This is the same URI in
+         *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
+         * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
+         *                 app.
+         */
+        public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
+        }
+
+        /**
+         * This is called when {@link TvIAppService.Session#notifyTeletextAppStateChanged} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
+         * @param state the current state.
+         */
+        public void onTeletextAppStateChanged(
+                Session session, @TvInteractiveAppManager.TeletextAppState int state) {
+        }
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
new file mode 100755
index 0000000..d599d0a
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -0,0 +1,1573 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.os.SomeArgs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The TvInteractiveAppService class represents a TV interactive applications RTE.
+ */
+public abstract class TvInteractiveAppService extends Service {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvInteractiveAppService";
+
+    private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
+
+    // TODO: cleanup and unhide APIs.
+
+    /**
+     * This is the interface name that a service implementing a TV Interactive App service should
+     * say that it supports -- that is, this is the action it uses for its intent filter. To be
+     * supported, the service must also require the
+     * android.Manifest.permission#BIND_TV_INTERACTIVE_APP permission so that other applications
+     * cannot abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.media.tv.interactive.TvInteractiveAppService";
+
+    /**
+     * Name under which a TvInteractiveAppService component publishes information about itself. This
+     * meta-data must reference an XML resource containing an
+     * <code>&lt;{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}&gt;</code>
+     * tag.
+     */
+    public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "INTERACTIVE_APP_SERVICE_COMMAND_TYPE_", value = {
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_NEXT,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_PREV,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_STOP,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SELECT_TRACK
+    })
+    public @interface InteractiveAppServiceCommandType {}
+
+    /** @hide */
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE = "tune";
+    /** @hide */
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_NEXT = "tune_next";
+    /** @hide */
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_PREV = "tune_previous";
+    /** @hide */
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_STOP = "stop";
+    /** @hide */
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME =
+            "set_stream_volume";
+    /** @hide */
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track";
+    /** @hide */
+    public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri";
+    /** @hide */
+    public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id";
+    /** @hide */
+    public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume";
+    /** @hide */
+    public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type";
+    /** @hide */
+    public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id";
+    /** @hide */
+    public static final String COMMAND_PARAMETER_KEY_TRACK_SELECT_MODE =
+            "command_track_select_mode";
+    /**
+     * Command to quiet channel change. No channel banner or channel info is shown.
+     * <p>Refer to HbbTV Spec 2.0.4 chapter A.2.4.3.
+     * @hide
+     */
+    public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY =
+            "command_change_channel_quietly";
+
+    private final Handler mServiceHandler = new ServiceHandler();
+    private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
+            new RemoteCallbackList<>();
+
+    /** @hide */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        ITvInteractiveAppService.Stub tvIAppServiceBinder = new ITvInteractiveAppService.Stub() {
+            @Override
+            public void registerCallback(ITvInteractiveAppServiceCallback cb) {
+                if (cb != null) {
+                    mCallbacks.register(cb);
+                }
+            }
+
+            @Override
+            public void unregisterCallback(ITvInteractiveAppServiceCallback cb) {
+                if (cb != null) {
+                    mCallbacks.unregister(cb);
+                }
+            }
+
+            @Override
+            public void createSession(InputChannel channel, ITvInteractiveAppSessionCallback cb,
+                    String iAppServiceId, int type) {
+                if (cb == null) {
+                    return;
+                }
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = channel;
+                args.arg2 = cb;
+                args.arg3 = iAppServiceId;
+                args.arg4 = type;
+                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
+                        .sendToTarget();
+            }
+
+            @Override
+            public void prepare(int type) {
+                onPrepare(type);
+            }
+
+            @Override
+            public void registerAppLinkInfo(AppLinkInfo appLinkInfo) {
+                onRegisterAppLinkInfo(appLinkInfo);
+            }
+
+            @Override
+            public void unregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
+                onUnregisterAppLinkInfo(appLinkInfo);
+            }
+
+            @Override
+            public void sendAppLinkCommand(Bundle command) {
+                onAppLinkCommand(command);
+            }
+        };
+        return tvIAppServiceBinder;
+    }
+
+    /**
+     * Prepares TV Interactive App service for the given type.
+     * @hide
+     */
+    public void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type) {
+    }
+
+    /**
+     * Registers App link info.
+     * @hide
+     */
+    public void onRegisterAppLinkInfo(AppLinkInfo appLinkInfo) {
+        // TODO: make it abstract when unhide
+    }
+
+    /**
+     * Unregisters App link info.
+     * @hide
+     */
+    public void onUnregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
+        // TODO: make it abstract when unhide
+    }
+
+    /**
+     * Sends App link info.
+     * @hide
+     */
+    public void onAppLinkCommand(Bundle command) {
+        // TODO: make it abstract when unhide
+    }
+
+
+    /**
+     * Returns a concrete implementation of {@link Session}.
+     *
+     * <p>May return {@code null} if this TV Interactive App service fails to create a session for
+     * some reason.
+     *
+     * @param iAppServiceId The ID of the TV Interactive App associated with the session.
+     * @param type The type of the TV Interactive App associated with the session.
+     * @hide
+     */
+    @Nullable
+    public Session onCreateSession(
+            @NonNull String iAppServiceId,
+            @TvInteractiveAppInfo.InteractiveAppType int type) {
+        return null;
+    }
+
+    /**
+     * Notifies the system when the state of the interactive app RTE has been changed.
+     *
+     * @param type the interactive app type
+     * @param state the current state of the service of the given type
+     * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
+     *              used when the state is not
+     *              {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
+     * @hide
+     */
+    public final void notifyStateChanged(
+            @TvInteractiveAppInfo.InteractiveAppType int type,
+            @TvInteractiveAppManager.ServiceState int state,
+            @TvInteractiveAppManager.ErrorCode int error) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = type;
+        args.arg2 = state;
+        args.arg3 = error;
+        mServiceHandler
+                .obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget();
+    }
+
+    /**
+     * Base class for derived classes to implement to provide a TV interactive app session.
+     * @hide
+     */
+    public abstract static class Session implements KeyEvent.Callback {
+        private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
+
+        private final Object mLock = new Object();
+        // @GuardedBy("mLock")
+        private ITvInteractiveAppSessionCallback mSessionCallback;
+        // @GuardedBy("mLock")
+        private final List<Runnable> mPendingActions = new ArrayList<>();
+
+        private final Context mContext;
+        final Handler mHandler;
+        private final WindowManager mWindowManager;
+        private WindowManager.LayoutParams mWindowParams;
+        private Surface mSurface;
+        private FrameLayout mMediaViewContainer;
+        private View mMediaView;
+        private MediaViewCleanUpTask mMediaViewCleanUpTask;
+        private boolean mMediaViewEnabled;
+        private IBinder mWindowToken;
+        private Rect mMediaFrame;
+
+        /**
+         * Creates a new Session.
+         *
+         * @param context The context of the application
+         */
+        public Session(@NonNull Context context) {
+            mContext = context;
+            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+            mHandler = new Handler(context.getMainLooper());
+        }
+
+        /**
+         * Enables or disables the media view.
+         *
+         * <p>By default, the media view is disabled. Must be called explicitly after the
+         * session is created to enable the media view.
+         *
+         * <p>The TV Interactive App service can disable its media view when needed.
+         *
+         * @param enable {@code true} if you want to enable the media view. {@code false}
+         *            otherwise.
+         * @hide
+         */
+        public void setMediaViewEnabled(final boolean enable) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (enable == mMediaViewEnabled) {
+                        return;
+                    }
+                    mMediaViewEnabled = enable;
+                    if (enable) {
+                        if (mWindowToken != null) {
+                            createMediaView(mWindowToken, mMediaFrame);
+                        }
+                    } else {
+                        removeMediaView(false);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Starts TvInteractiveAppService session.
+         */
+        public void onStartInteractiveApp() {
+        }
+
+        /**
+         * Stops TvInteractiveAppService session.
+         */
+        public void onStopInteractiveApp() {
+        }
+
+        /**
+         * Resets TvIAppService session.
+         * @hide
+         */
+        public void onResetInteractiveApp() {
+        }
+
+        /**
+         * Creates broadcast-independent(BI) interactive application.
+         *
+         * @see #onDestroyBiInteractiveApp(String)
+         * @hide
+         */
+        public void onCreateBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+        }
+
+
+        /**
+         * Destroys broadcast-independent(BI) interactive application.
+         *
+         * @param biIAppId the BI interactive app ID from
+         *        {@link #createBiInteractiveApp(Uri, Bundle)}
+         *
+         * @see #onCreateBiInteractiveApp(Uri, Bundle)
+         * @hide
+         */
+        public void onDestroyBiInteractiveApp(@NonNull String biIAppId) {
+        }
+
+        /**
+         * To toggle Digital Teletext Application if there is one in AIT app list.
+         * @param enable
+         * @hide
+         */
+        public void onSetTeletextAppEnabled(boolean enable) {
+        }
+
+        /**
+         * Receives current channel URI.
+         * @hide
+         */
+        public void onCurrentChannelUri(@Nullable Uri channelUri) {
+        }
+
+        /**
+         * Receives logical channel number (LCN) of current channel.
+         * @hide
+         */
+        public void onCurrentChannelLcn(int lcn) {
+        }
+
+        /**
+         * Receives stream volume.
+         * @hide
+         */
+        public void onStreamVolume(float volume) {
+        }
+
+        /**
+         * Receives track list.
+         * @hide
+         */
+        public void onTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+        }
+
+        /**
+         * Receives current TV input ID.
+         * @hide
+         */
+        public void onCurrentTvInputId(@Nullable String inputId) {
+        }
+
+        /**
+         * Called when the application sets the surface.
+         *
+         * <p>The TV Interactive App service should render interactive app UI onto the given
+         * surface. When called with {@code null}, the Interactive App service should immediately
+         * free any references to the currently set surface and stop using it.
+         *
+         * @param surface The surface to be used for interactive app UI rendering. Can be
+         *                {@code null}.
+         * @return {@code true} if the surface was set successfully, {@code false} otherwise.
+         */
+        public abstract boolean onSetSurface(@Nullable Surface surface);
+
+        /**
+         * Called after any structural changes (format or size) have been made to the surface passed
+         * in {@link #onSetSurface}. This method is always called at least once, after
+         * {@link #onSetSurface} is called with non-null surface.
+         *
+         * @param format The new PixelFormat of the surface.
+         * @param width The new width of the surface.
+         * @param height The new height of the surface.
+         */
+        public void onSurfaceChanged(int format, int width, int height) {
+        }
+
+        /**
+         * Called when the size of the media view is changed by the application.
+         *
+         * <p>This is always called at least once when the session is created regardless of whether
+         * the media view is enabled or not. The media view container size is the same as the
+         * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can
+         * be different if the surface was changed by calling {@link #layoutSurface}.
+         *
+         * @param width The width of the media view.
+         * @param height The height of the media view.
+         * @hide
+         */
+        public void onMediaViewSizeChanged(int width, int height) {
+        }
+
+        /**
+         * Called when the application requests to create an media view. Each session
+         * implementation can override this method and return its own view.
+         *
+         * @return a view attached to the media window
+         * @hide
+         */
+        @Nullable
+        public View onCreateMediaView() {
+            return null;
+        }
+
+        /**
+         * Releases TvInteractiveAppService session.
+         * @hide
+         */
+        public void onRelease() {
+        }
+
+        /**
+         * Called when the corresponding TV input tuned to a channel.
+         * @hide
+         */
+        public void onTuned(@NonNull Uri channelUri) {
+        }
+
+        /**
+         * Called when the corresponding TV input selected to a track.
+         * @hide
+         */
+        public void onTrackSelected(int type, String trackId) {
+        }
+
+        /**
+         * Called when the tracks are changed.
+         * @hide
+         */
+        public void onTracksChanged(List<TvTrackInfo> tracks) {
+        }
+
+        /**
+         * Called when video is available.
+         * @hide
+         */
+        public void onVideoAvailable() {
+        }
+
+        /**
+         * Called when video is unavailable.
+         * @hide
+         */
+        public void onVideoUnavailable(int reason) {
+        }
+
+        /**
+         * Called when content is allowed.
+         * @hide
+         */
+        public void onContentAllowed() {
+        }
+
+        /**
+         * Called when content is blocked.
+         * @hide
+         */
+        public void onContentBlocked(TvContentRating rating) {
+        }
+
+        /**
+         * Called when signal strength is changed.
+         * @hide
+         */
+        public void onSignalStrength(@TvInputManager.SignalStrength int strength) {
+        }
+
+        /**
+         * Called when a broadcast info response is received.
+         * @hide
+         */
+        public void onBroadcastInfoResponse(@NonNull BroadcastInfoResponse response) {
+        }
+
+        /**
+         * Called when an advertisement response is received.
+         * @hide
+         */
+        public void onAdResponse(AdResponse response) {
+        }
+
+        /**
+         * TODO: JavaDoc of APIs related to input events.
+         * @hide
+         */
+        @Override
+        public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        public boolean onTouchEvent(@NonNull MotionEvent event) {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        public boolean onTrackballEvent(@NonNull MotionEvent event) {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
+            return false;
+        }
+
+        /**
+         * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
+         * is relative to the overlay view that sits on top of this surface.
+         *
+         * @param left Left position in pixels, relative to the overlay view.
+         * @param top Top position in pixels, relative to the overlay view.
+         * @param right Right position in pixels, relative to the overlay view.
+         * @param bottom Bottom position in pixels, relative to the overlay view.
+         */
+        public void layoutSurface(final int left, final int top, final int right,
+                final int bottom) {
+            if (left > right || top > bottom) {
+                throw new IllegalArgumentException("Invalid parameter");
+            }
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top
+                                    + ", r=" + right + ", b=" + bottom + ",)");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onLayoutSurface(left, top, right, bottom);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in layoutSurface", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Requests broadcast related information from the related TV input.
+         * @param request the request for broadcast info
+         * @hide
+         */
+        public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestBroadcastInfo (requestId="
+                                    + request.getRequestId() + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onBroadcastInfoRequest(request);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestBroadcastInfo", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Remove broadcast information request from the related TV input.
+         * @param requestId the ID of the request
+         * @hide
+         */
+        public void removeBroadcastInfo(final int requestId) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "removeBroadcastInfo (requestId="
+                                    + requestId + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRemoveBroadcastInfo(requestId);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in removeBroadcastInfo", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * requests a specific command to be processed by the related TV input.
+         * @param cmdType type of the specific command
+         * @param parameters parameters of the specific command
+         * @hide
+         */
+        public void requestCommand(
+                @InteractiveAppServiceCommandType String cmdType, Bundle parameters) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters="
+                                    + parameters.toString() + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onCommandRequest(cmdType, parameters);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestCommand", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Sets broadcast video bounds.
+         * @hide
+         */
+        public void setVideoBounds(Rect rect) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "setVideoBounds (rect=" + rect + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onSetVideoBounds(rect);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in setVideoBounds", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Requests the URI of the current channel.
+         * @hide
+         */
+        public void requestCurrentChannelUri() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestCurrentChannelUri");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRequestCurrentChannelUri();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestCurrentChannelUri", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Requests the logic channel number (LCN) of the current channel.
+         * @hide
+         */
+        public void requestCurrentChannelLcn() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestCurrentChannelLcn");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRequestCurrentChannelLcn();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestCurrentChannelLcn", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Requests stream volume.
+         * @hide
+         */
+        public void requestStreamVolume() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestStreamVolume");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRequestStreamVolume();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestStreamVolume", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Requests the list of {@link TvTrackInfo}.
+         * @hide
+         */
+        public void requestTrackInfoList() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestTrackInfoList");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRequestTrackInfoList();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestTrackInfoList", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Requests current TV input ID.
+         *
+         * @see android.media.tv.TvInputInfo
+         * @hide
+         */
+        public void requestCurrentTvInputId() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestCurrentTvInputId");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRequestCurrentTvInputId();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestCurrentTvInputId", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * requests an advertisement request to be processed by the related TV input.
+         * @param request advertisement request
+         * @hide
+         */
+        public void requestAd(@NonNull final AdRequest request) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestAd (id=" + request.getId() + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onAdRequest(request);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestAd", e);
+                    }
+                }
+            });
+        }
+
+        void startInteractiveApp() {
+            onStartInteractiveApp();
+        }
+
+        void stopInteractiveApp() {
+            onStopInteractiveApp();
+        }
+
+        void resetInteractiveApp() {
+            onResetInteractiveApp();
+        }
+
+        void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+            onCreateBiInteractiveApp(biIAppUri, params);
+        }
+
+        void destroyBiInteractiveApp(@NonNull String biIAppId) {
+            onDestroyBiInteractiveApp(biIAppId);
+        }
+
+        void setTeletextAppEnabled(boolean enable) {
+            onSetTeletextAppEnabled(enable);
+        }
+
+        void sendCurrentChannelUri(@Nullable Uri channelUri) {
+            onCurrentChannelUri(channelUri);
+        }
+
+        void sendCurrentChannelLcn(int lcn) {
+            onCurrentChannelLcn(lcn);
+        }
+
+        void sendStreamVolume(float volume) {
+            onStreamVolume(volume);
+        }
+
+        void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+            onTrackInfoList(tracks);
+        }
+
+        void sendCurrentTvInputId(@Nullable String inputId) {
+            onCurrentTvInputId(inputId);
+        }
+
+        void release() {
+            onRelease();
+            if (mSurface != null) {
+                mSurface.release();
+                mSurface = null;
+            }
+            synchronized (mLock) {
+                mSessionCallback = null;
+                mPendingActions.clear();
+            }
+            // Removes the media view lastly so that any hanging on the main thread can be handled
+            // in {@link #scheduleMediaViewCleanup}.
+            removeMediaView(true);
+        }
+
+        void notifyTuned(Uri channelUri) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")");
+            }
+            onTuned(channelUri);
+        }
+
+        void notifyTrackSelected(int type, String trackId) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyTrackSelected (type=" + type + "trackId=" + trackId + ")");
+            }
+            onTrackSelected(type, trackId);
+        }
+
+        void notifyTracksChanged(List<TvTrackInfo> tracks) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyTracksChanged (tracks=" + tracks + ")");
+            }
+            onTracksChanged(tracks);
+        }
+
+        void notifyVideoAvailable() {
+            if (DEBUG) {
+                Log.d(TAG, "notifyVideoAvailable");
+            }
+            onVideoAvailable();
+        }
+
+        void notifyVideoUnavailable(int reason) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyVideoAvailable (reason=" + reason + ")");
+            }
+            onVideoUnavailable(reason);
+        }
+
+        void notifyContentAllowed() {
+            if (DEBUG) {
+                Log.d(TAG, "notifyContentAllowed");
+            }
+            notifyContentAllowed();
+        }
+
+        void notifyContentBlocked(TvContentRating rating) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyContentBlocked (rating=" + rating.flattenToString() + ")");
+            }
+            onContentBlocked(rating);
+        }
+
+        void notifySignalStrength(int strength) {
+            if (DEBUG) {
+                Log.d(TAG, "notifySignalStrength (strength=" + strength + ")");
+            }
+            onSignalStrength(strength);
+        }
+
+        /**
+         * Calls {@link #onBroadcastInfoResponse}.
+         */
+        void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyBroadcastInfoResponse (requestId="
+                        + response.getRequestId() + ")");
+            }
+            onBroadcastInfoResponse(response);
+        }
+
+        /**
+         * Calls {@link #onAdResponse}.
+         */
+        void notifyAdResponse(AdResponse response) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")");
+            }
+            onAdResponse(response);
+        }
+
+        /**
+         * Notifies when the session state is changed.
+         *
+         * @param state the current session state.
+         * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
+         *            used when the state is not
+         *            {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
+         */
+        public void notifySessionStateChanged(
+                @TvInteractiveAppManager.InteractiveAppState int state,
+                @TvInteractiveAppManager.ErrorCode int err) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "notifySessionStateChanged (state="
+                                    + state + "; err=" + err + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onSessionStateChanged(state, err);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifySessionStateChanged", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Notifies the broadcast-independent(BI) interactive application has been created.
+         * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
+         *                 app.
+         * @hide
+         */
+        public final void notifyBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "notifyBiInteractiveAppCreated (biIAppId="
+                                    + biIAppId + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onBiInteractiveAppCreated(biIAppUri, biIAppId);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyBiInteractiveAppCreated", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Notifies when the digital teletext app state is changed.
+         * @param state the current state.
+         * @hide
+         */
+        public final void notifyTeletextAppStateChanged(
+                @TvInteractiveAppManager.TeletextAppState int state) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "notifyTeletextAppState (state="
+                                    + state + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onTeletextAppStateChanged(state);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyTeletextAppState", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Takes care of dispatching incoming input events and tells whether the event was handled.
+         */
+        int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
+            if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
+            if (event instanceof KeyEvent) {
+                KeyEvent keyEvent = (KeyEvent) event;
+                if (keyEvent.dispatch(this, mDispatcherState, this)) {
+                    return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
+                }
+
+                // TODO: special handlings of navigation keys and media keys
+            } else if (event instanceof MotionEvent) {
+                MotionEvent motionEvent = (MotionEvent) event;
+                final int source = motionEvent.getSource();
+                if (motionEvent.isTouchEvent()) {
+                    if (onTouchEvent(motionEvent)) {
+                        return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
+                    }
+                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    if (onTrackballEvent(motionEvent)) {
+                        return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
+                    }
+                } else {
+                    if (onGenericMotionEvent(motionEvent)) {
+                        return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
+                    }
+                }
+            }
+            // TODO: handle overlay view
+            return TvInteractiveAppManager.Session.DISPATCH_NOT_HANDLED;
+        }
+
+        private void initialize(ITvInteractiveAppSessionCallback callback) {
+            synchronized (mLock) {
+                mSessionCallback = callback;
+                for (Runnable runnable : mPendingActions) {
+                    runnable.run();
+                }
+                mPendingActions.clear();
+            }
+        }
+
+        /**
+         * Calls {@link #onSetSurface}.
+         */
+        void setSurface(Surface surface) {
+            onSetSurface(surface);
+            if (mSurface != null) {
+                mSurface.release();
+            }
+            mSurface = surface;
+            // TODO: Handle failure.
+        }
+
+        /**
+         * Calls {@link #onSurfaceChanged}.
+         */
+        void dispatchSurfaceChanged(int format, int width, int height) {
+            if (DEBUG) {
+                Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
+                        + ", height=" + height + ")");
+            }
+            onSurfaceChanged(format, width, height);
+        }
+
+        private void executeOrPostRunnableOnMainThread(Runnable action) {
+            synchronized (mLock) {
+                if (mSessionCallback == null) {
+                    // The session is not initialized yet.
+                    mPendingActions.add(action);
+                } else {
+                    if (mHandler.getLooper().isCurrentThread()) {
+                        action.run();
+                    } else {
+                        // Posts the runnable if this is not called from the main thread
+                        mHandler.post(action);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Creates an media view. This calls {@link #onCreateMediaView} to get a view to attach
+         * to the media window.
+         *
+         * @param windowToken A window token of the application.
+         * @param frame A position of the media view.
+         */
+        void createMediaView(IBinder windowToken, Rect frame) {
+            if (mMediaViewContainer != null) {
+                removeMediaView(false);
+            }
+            if (DEBUG) Log.d(TAG, "create media view(" + frame + ")");
+            mWindowToken = windowToken;
+            mMediaFrame = frame;
+            onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
+            if (!mMediaViewEnabled) {
+                return;
+            }
+            mMediaView = onCreateMediaView();
+            if (mMediaView == null) {
+                return;
+            }
+            if (mMediaViewCleanUpTask != null) {
+                mMediaViewCleanUpTask.cancel(true);
+                mMediaViewCleanUpTask = null;
+            }
+            // Creates a container view to check hanging on the media view detaching.
+            // Adding/removing the media view to/from the container make the view attach/detach
+            // logic run on the main thread.
+            mMediaViewContainer = new FrameLayout(mContext.getApplicationContext());
+            mMediaViewContainer.addView(mMediaView);
+
+            int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+            // We make the overlay view non-focusable and non-touchable so that
+            // the application that owns the window token can decide whether to consume or
+            // dispatch the input events.
+            int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+            if (ActivityManager.isHighEndGfx()) {
+                flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+            }
+            mWindowParams = new WindowManager.LayoutParams(
+                    frame.right - frame.left, frame.bottom - frame.top,
+                    frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT);
+            mWindowParams.privateFlags |=
+                    WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+            mWindowParams.gravity = Gravity.START | Gravity.TOP;
+            mWindowParams.token = windowToken;
+            mWindowManager.addView(mMediaViewContainer, mWindowParams);
+        }
+
+        /**
+         * Relayouts the current media view.
+         *
+         * @param frame A new position of the media view.
+         */
+        void relayoutMediaView(Rect frame) {
+            if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")");
+            if (mMediaFrame == null || mMediaFrame.width() != frame.width()
+                    || mMediaFrame.height() != frame.height()) {
+                // Note: relayoutMediaView is called whenever TvInteractiveAppView's layout is
+                // changed regardless of setMediaViewEnabled.
+                onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
+            }
+            mMediaFrame = frame;
+            if (!mMediaViewEnabled || mMediaViewContainer == null) {
+                return;
+            }
+            mWindowParams.x = frame.left;
+            mWindowParams.y = frame.top;
+            mWindowParams.width = frame.right - frame.left;
+            mWindowParams.height = frame.bottom - frame.top;
+            mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams);
+        }
+
+        /**
+         * Removes the current media view.
+         */
+        void removeMediaView(boolean clearWindowToken) {
+            if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")");
+            if (clearWindowToken) {
+                mWindowToken = null;
+                mMediaFrame = null;
+            }
+            if (mMediaViewContainer != null) {
+                // Removes the media view from the view hierarchy in advance so that it can be
+                // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is
+                // hanging.
+                mMediaViewContainer.removeView(mMediaView);
+                mMediaView = null;
+                mWindowManager.removeView(mMediaViewContainer);
+                mMediaViewContainer = null;
+                mWindowParams = null;
+            }
+        }
+
+        /**
+         * Schedules a task which checks whether the media view is detached and kills the process
+         * if it is not. Note that this method is expected to be called in a non-main thread.
+         */
+        void scheduleMediaViewCleanup() {
+            View mediaViewParent = mMediaViewContainer;
+            if (mediaViewParent != null) {
+                mMediaViewCleanUpTask = new MediaViewCleanUpTask();
+                mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
+                        mediaViewParent);
+            }
+        }
+    }
+
+    private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> {
+        @Override
+        protected Void doInBackground(View... views) {
+            View mediaViewParent = views[0];
+            try {
+                Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+            if (isCancelled()) {
+                return null;
+            }
+            if (mediaViewParent.isAttachedToWindow()) {
+                Log.e(TAG, "Time out on releasing media view. Killing "
+                        + mediaViewParent.getContext().getPackageName());
+                android.os.Process.killProcess(Process.myPid());
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Implements the internal ITvInteractiveAppSession interface.
+     * @hide
+     */
+    public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub {
+        // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file
+        private final Session mSessionImpl;
+        private InputChannel mChannel;
+        private TvInteractiveAppEventReceiver mReceiver;
+
+        public ITvInteractiveAppSessionWrapper(
+                Context context, Session mSessionImpl, InputChannel channel) {
+            this.mSessionImpl = mSessionImpl;
+            mChannel = channel;
+            if (channel != null) {
+                mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
+            }
+        }
+
+        @Override
+        public void startInteractiveApp() {
+            mSessionImpl.startInteractiveApp();
+        }
+
+        @Override
+        public void stopInteractiveApp() {
+            mSessionImpl.stopInteractiveApp();
+        }
+
+        @Override
+        public void resetInteractiveApp() {
+            mSessionImpl.resetInteractiveApp();
+        }
+
+        @Override
+        public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+            mSessionImpl.createBiInteractiveApp(biIAppUri, params);
+        }
+
+        @Override
+        public void setTeletextAppEnabled(boolean enable) {
+            mSessionImpl.setTeletextAppEnabled(enable);
+        }
+
+        @Override
+        public void destroyBiInteractiveApp(@NonNull String biIAppId) {
+            mSessionImpl.destroyBiInteractiveApp(biIAppId);
+        }
+
+        @Override
+        public void sendCurrentChannelUri(@Nullable Uri channelUri) {
+            mSessionImpl.sendCurrentChannelUri(channelUri);
+        }
+
+        @Override
+        public void sendCurrentChannelLcn(int lcn) {
+            mSessionImpl.sendCurrentChannelLcn(lcn);
+        }
+
+        @Override
+        public void sendStreamVolume(float volume) {
+            mSessionImpl.sendStreamVolume(volume);
+        }
+
+        @Override
+        public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+            mSessionImpl.sendTrackInfoList(tracks);
+        }
+
+        @Override
+        public void sendCurrentTvInputId(@Nullable String inputId) {
+            mSessionImpl.sendCurrentTvInputId(inputId);
+        }
+
+        @Override
+        public void release() {
+            mSessionImpl.scheduleMediaViewCleanup();
+            mSessionImpl.release();
+        }
+
+        @Override
+        public void notifyTuned(Uri channelUri) {
+            mSessionImpl.notifyTuned(channelUri);
+        }
+
+        @Override
+        public void notifyTrackSelected(int type, final String trackId) {
+            mSessionImpl.notifyTrackSelected(type, trackId);
+        }
+
+        @Override
+        public void notifyTracksChanged(List<TvTrackInfo> tracks) {
+            mSessionImpl.notifyTracksChanged(tracks);
+        }
+
+        @Override
+        public void notifyVideoAvailable() {
+            mSessionImpl.notifyVideoAvailable();
+        }
+
+        @Override
+        public void notifyVideoUnavailable(int reason) {
+            mSessionImpl.notifyVideoUnavailable(reason);
+        }
+
+        @Override
+        public void notifyContentAllowed() {
+            mSessionImpl.notifyContentAllowed();
+        }
+
+        @Override
+        public void notifyContentBlocked(String rating) {
+            mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating));
+        }
+
+        @Override
+        public void notifySignalStrength(int strength) {
+            mSessionImpl.notifySignalStrength(strength);
+        }
+
+        @Override
+        public void setSurface(Surface surface) {
+            mSessionImpl.setSurface(surface);
+        }
+
+        @Override
+        public void dispatchSurfaceChanged(int format, int width, int height) {
+            mSessionImpl.dispatchSurfaceChanged(format, width, height);
+        }
+
+        @Override
+        public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+            mSessionImpl.notifyBroadcastInfoResponse(response);
+        }
+
+        @Override
+        public void notifyAdResponse(AdResponse response) {
+            mSessionImpl.notifyAdResponse(response);
+        }
+
+        @Override
+        public void createMediaView(IBinder windowToken, Rect frame) {
+            mSessionImpl.createMediaView(windowToken, frame);
+        }
+
+        @Override
+        public void relayoutMediaView(Rect frame) {
+            mSessionImpl.relayoutMediaView(frame);
+        }
+
+        @Override
+        public void removeMediaView() {
+            mSessionImpl.removeMediaView(true);
+        }
+
+        private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
+            TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
+                super(inputChannel, looper);
+            }
+
+            @Override
+            public void onInputEvent(InputEvent event) {
+                if (mSessionImpl == null) {
+                    // The session has been finished.
+                    finishInputEvent(event, false);
+                    return;
+                }
+
+                int handled = mSessionImpl.dispatchInputEvent(event, this);
+                if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
+                    finishInputEvent(
+                            event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
+                }
+            }
+        }
+    }
+
+    @SuppressLint("HandlerLeak")
+    private final class ServiceHandler extends Handler {
+        private static final int DO_CREATE_SESSION = 1;
+        private static final int DO_NOTIFY_SESSION_CREATED = 2;
+        private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3;
+
+        private void broadcastRteStateChanged(int type, int state, int error) {
+            int n = mCallbacks.beginBroadcast();
+            for (int i = 0; i < n; ++i) {
+                try {
+                    mCallbacks.getBroadcastItem(i).onStateChanged(type, state, error);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "error in broadcastRteStateChanged", e);
+                }
+            }
+            mCallbacks.finishBroadcast();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DO_CREATE_SESSION: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    InputChannel channel = (InputChannel) args.arg1;
+                    ITvInteractiveAppSessionCallback cb =
+                            (ITvInteractiveAppSessionCallback) args.arg2;
+                    String iAppServiceId = (String) args.arg3;
+                    int type = (int) args.arg4;
+                    args.recycle();
+                    Session sessionImpl = onCreateSession(iAppServiceId, type);
+                    if (sessionImpl == null) {
+                        try {
+                            // Failed to create a session.
+                            cb.onSessionCreated(null);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "error in onSessionCreated", e);
+                        }
+                        return;
+                    }
+                    ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper(
+                            TvInteractiveAppService.this, sessionImpl, channel);
+
+                    SomeArgs someArgs = SomeArgs.obtain();
+                    someArgs.arg1 = sessionImpl;
+                    someArgs.arg2 = stub;
+                    someArgs.arg3 = cb;
+                    mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
+                            someArgs).sendToTarget();
+                    return;
+                }
+                case DO_NOTIFY_SESSION_CREATED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Session sessionImpl = (Session) args.arg1;
+                    ITvInteractiveAppSession stub = (ITvInteractiveAppSession) args.arg2;
+                    ITvInteractiveAppSessionCallback cb =
+                            (ITvInteractiveAppSessionCallback) args.arg3;
+                    try {
+                        cb.onSessionCreated(stub);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "error in onSessionCreated", e);
+                    }
+                    if (sessionImpl != null) {
+                        sessionImpl.initialize(cb);
+                    }
+                    args.recycle();
+                    return;
+                }
+                case DO_NOTIFY_RTE_STATE_CHANGED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    int type = (int) args.arg1;
+                    int state = (int) args.arg2;
+                    int error = (int) args.arg3;
+                    broadcastRteStateChanged(type, state, error);
+                    return;
+                }
+                default: {
+                    Log.w(TAG, "Unhandled message code: " + msg.what);
+                    return;
+                }
+            }
+        }
+
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
new file mode 100755
index 0000000..12e2199
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.TvView;
+import android.media.tv.interactive.TvInteractiveAppManager.Session;
+import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
+import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Displays contents of interactive TV applications.
+ */
+public class TvInteractiveAppView extends ViewGroup {
+    private static final String TAG = "TvInteractiveAppView";
+    private static final boolean DEBUG = false;
+
+    private static final int SET_TVVIEW_SUCCESS = 1;
+    private static final int SET_TVVIEW_FAIL = 2;
+    private static final int UNSET_TVVIEW_SUCCESS = 3;
+    private static final int UNSET_TVVIEW_FAIL = 4;
+
+    private final TvInteractiveAppManager mTvInteractiveAppManager;
+    private final Handler mHandler = new Handler();
+    private final Object mCallbackLock = new Object();
+    private Session mSession;
+    private MySessionCallback mSessionCallback;
+    private TvInteractiveAppCallback mCallback;
+    private Executor mCallbackExecutor;
+    private SurfaceView mSurfaceView;
+    private Surface mSurface;
+
+    private boolean mSurfaceChanged;
+    private int mSurfaceFormat;
+    private int mSurfaceWidth;
+    private int mSurfaceHeight;
+
+    private boolean mUseRequestedSurfaceLayout;
+    private int mSurfaceViewLeft;
+    private int mSurfaceViewRight;
+    private int mSurfaceViewTop;
+    private int mSurfaceViewBottom;
+
+    private boolean mMediaViewCreated;
+    private Rect mMediaViewFrame;
+
+    private final AttributeSet mAttrs;
+    private final int mDefStyleAttr;
+    private final XmlResourceParser mParser;
+    private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
+
+    private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            if (DEBUG) {
+                Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format
+                        + ", width=" + width + ", height=" + height + ")");
+            }
+            mSurfaceFormat = format;
+            mSurfaceWidth = width;
+            mSurfaceHeight = height;
+            mSurfaceChanged = true;
+            dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            mSurface = holder.getSurface();
+            setSessionSurface(mSurface);
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mSurface = null;
+            mSurfaceChanged = false;
+            setSessionSurface(null);
+        }
+    };
+
+    public TvInteractiveAppView(@NonNull Context context) {
+        this(context, null, 0);
+    }
+
+    public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+        if (sourceResId != Resources.ID_NULL) {
+            Log.d(TAG, "Build local AttributeSet");
+            mParser  = context.getResources().getXml(sourceResId);
+            mAttrs = Xml.asAttributeSet(mParser);
+        } else {
+            Log.d(TAG, "Use passed in AttributeSet");
+            mParser = null;
+            mAttrs = attrs;
+        }
+        mDefStyleAttr = defStyleAttr;
+        resetSurfaceView();
+        mTvInteractiveAppManager = (TvInteractiveAppManager) getContext().getSystemService(
+                Context.TV_INTERACTIVE_APP_SERVICE);
+    }
+
+    /**
+     * Sets the callback to be invoked when an event is dispatched to this TvInteractiveAppView.
+     *
+     * @param callback The callback to receive events. A value of {@code null} removes the existing
+     *                 callback.
+     */
+    public void setCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull TvInteractiveAppCallback callback) {
+        synchronized (mCallbackLock) {
+            mCallbackExecutor = executor;
+            mCallback = callback;
+        }
+    }
+
+    /**
+     * Clears the callback.
+     */
+    public void clearCallback() {
+        synchronized (mCallbackLock) {
+            mCallback = null;
+            mCallbackExecutor = null;
+        }
+    }
+
+    /** @hide */
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        createSessionMediaView();
+    }
+
+    /** @hide */
+    @Override
+    protected void onDetachedFromWindow() {
+        removeSessionMediaView();
+        super.onDetachedFromWindow();
+    }
+
+    /** @hide */
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (DEBUG) {
+            Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
+                    + ", bottom=" + bottom + ",)");
+        }
+        if (mUseRequestedSurfaceLayout) {
+            mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
+                    mSurfaceViewBottom);
+        } else {
+            mSurfaceView.layout(0, 0, right - left, bottom - top);
+        }
+    }
+
+    /** @hide */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
+        int width = mSurfaceView.getMeasuredWidth();
+        int height = mSurfaceView.getMeasuredHeight();
+        int childState = mSurfaceView.getMeasuredState();
+        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+                resolveSizeAndState(height, heightMeasureSpec,
+                        childState << MEASURED_HEIGHT_STATE_SHIFT));
+    }
+
+    /** @hide */
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mSurfaceView.setVisibility(visibility);
+        if (visibility == View.VISIBLE) {
+            createSessionMediaView();
+        } else {
+            removeSessionMediaView();
+        }
+    }
+
+    private void resetSurfaceView() {
+        if (mSurfaceView != null) {
+            mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
+            removeView(mSurfaceView);
+        }
+        mSurface = null;
+        mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
+            @Override
+            protected void updateSurface() {
+                super.updateSurface();
+                relayoutSessionMediaView();
+            }};
+        // The surface view's content should be treated as secure all the time.
+        mSurfaceView.setSecure(true);
+        mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+        mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+        addView(mSurfaceView);
+    }
+
+    /**
+     * Resets this TvInteractiveAppView.
+     * @hide
+     */
+    public void reset() {
+        if (DEBUG) Log.d(TAG, "reset()");
+        resetInternal();
+    }
+
+    private void createSessionMediaView() {
+        // TODO: handle z-order
+        if (mSession == null || !isAttachedToWindow() || mMediaViewCreated) {
+            return;
+        }
+        mMediaViewFrame = getViewFrameOnScreen();
+        mSession.createMediaView(this, mMediaViewFrame);
+        mMediaViewCreated = true;
+    }
+
+    private void removeSessionMediaView() {
+        if (mSession == null || !mMediaViewCreated) {
+            return;
+        }
+        mSession.removeMediaView();
+        mMediaViewCreated = false;
+        mMediaViewFrame = null;
+    }
+
+    private void relayoutSessionMediaView() {
+        if (mSession == null || !isAttachedToWindow() || !mMediaViewCreated) {
+            return;
+        }
+        Rect viewFrame = getViewFrameOnScreen();
+        if (viewFrame.equals(mMediaViewFrame)) {
+            return;
+        }
+        mSession.relayoutMediaView(viewFrame);
+        mMediaViewFrame = viewFrame;
+    }
+
+    private Rect getViewFrameOnScreen() {
+        Rect frame = new Rect();
+        getGlobalVisibleRect(frame);
+        RectF frameF = new RectF(frame);
+        getMatrix().mapRect(frameF);
+        frameF.round(frame);
+        return frame;
+    }
+
+    private void setSessionSurface(Surface surface) {
+        if (mSession == null) {
+            return;
+        }
+        mSession.setSurface(surface);
+    }
+
+    private void dispatchSurfaceChanged(int format, int width, int height) {
+        if (mSession == null) {
+            return;
+        }
+        mSession.dispatchSurfaceChanged(format, width, height);
+    }
+
+    private final FinishedInputEventCallback mFinishedInputEventCallback =
+            new FinishedInputEventCallback() {
+                @Override
+                public void onFinishedInputEvent(Object token, boolean handled) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled="
+                                + handled + ")");
+                    }
+                    if (handled) {
+                        return;
+                    }
+                    // TODO: Re-order unhandled events.
+                    InputEvent event = (InputEvent) token;
+                    if (dispatchUnhandledInputEvent(event)) {
+                        return;
+                    }
+                    ViewRootImpl viewRootImpl = getViewRootImpl();
+                    if (viewRootImpl != null) {
+                        viewRootImpl.dispatchUnhandledInputEvent(event);
+                    }
+                }
+            };
+
+    /**
+     * Dispatches an unhandled input event to the next receiver.
+     * @hide
+     */
+    public boolean dispatchUnhandledInputEvent(@NonNull InputEvent event) {
+        if (mOnUnhandledInputEventListener != null) {
+            if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
+                return true;
+            }
+        }
+        return onUnhandledInputEvent(event);
+    }
+
+    /**
+     * Called when an unhandled input event also has not been handled by the user provided
+     * callback. This is the last chance to handle the unhandled input event in the
+     * TvInteractiveAppView.
+     *
+     * @param event The input event.
+     * @return If you handled the event, return {@code true}. If you want to allow the event to be
+     *         handled by the next receiver, return {@code false}.
+     * @hide
+     */
+    public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
+        return false;
+    }
+
+    /**
+     * Registers a callback to be invoked when an input event is not handled
+     * by the TV Interactive App.
+     *
+     * @param listener The callback to be invoked when the unhandled input event is received.
+     * @hide
+     */
+    public void setOnUnhandledInputEventListener(@NonNull OnUnhandledInputEventListener listener) {
+        mOnUnhandledInputEventListener = listener;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+        if (super.dispatchKeyEvent(event)) {
+            return true;
+        }
+        if (mSession == null) {
+            return false;
+        }
+        InputEvent copiedEvent = event.copy();
+        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+                mHandler);
+        return ret != Session.DISPATCH_NOT_HANDLED;
+    }
+
+    /**
+     * Prepares the interactive application.
+     *
+     * @param iAppServiceId the interactive app service ID, which can be found in
+     *                      {@link TvInteractiveAppInfo#getId()}.
+     *
+     * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList()
+     * @hide
+     */
+    public void prepareInteractiveApp(
+            @NonNull String iAppServiceId,
+            @TvInteractiveAppInfo.InteractiveAppType int type) {
+        // TODO: document and handle the cases that this method is called multiple times.
+        if (DEBUG) {
+            Log.d(TAG, "prepareInteractiveApp");
+        }
+        mSessionCallback = new MySessionCallback(iAppServiceId, type);
+        if (mTvInteractiveAppManager != null) {
+            mTvInteractiveAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
+        }
+    }
+
+    /**
+     * Starts the interactive application.
+     */
+    public void startInteractiveApp() {
+        if (DEBUG) {
+            Log.d(TAG, "startInteractiveApp");
+        }
+        if (mSession != null) {
+            mSession.startInteractiveApp();
+        }
+    }
+
+    /**
+     * Stops the interactive application.
+     * @hide
+     */
+    public void stopInteractiveApp() {
+        if (DEBUG) {
+            Log.d(TAG, "stopInteractiveApp");
+        }
+        if (mSession != null) {
+            mSession.stopInteractiveApp();
+        }
+    }
+
+    /**
+     * Resets the interactive application.
+     * @hide
+     */
+    public void resetInteractiveApp() {
+        if (DEBUG) {
+            Log.d(TAG, "resetInteractiveApp");
+        }
+        if (mSession != null) {
+            mSession.resetInteractiveApp();
+        }
+    }
+
+    /**
+     * Sends current channel URI to related TV interactive app.
+     * @hide
+     */
+    public void sendCurrentChannelUri(Uri channelUri) {
+        if (DEBUG) {
+            Log.d(TAG, "sendCurrentChannelUri");
+        }
+        if (mSession != null) {
+            mSession.sendCurrentChannelUri(channelUri);
+        }
+    }
+
+    /**
+     * Sends current channel logical channel number (LCN) to related TV interactive app.
+     * @hide
+     */
+    public void sendCurrentChannelLcn(int lcn) {
+        if (DEBUG) {
+            Log.d(TAG, "sendCurrentChannelLcn");
+        }
+        if (mSession != null) {
+            mSession.sendCurrentChannelLcn(lcn);
+        }
+    }
+
+    /**
+     * Sends stream volume to related TV interactive app.
+     * @hide
+     */
+    public void sendStreamVolume(float volume) {
+        if (DEBUG) {
+            Log.d(TAG, "sendStreamVolume");
+        }
+        if (mSession != null) {
+            mSession.sendStreamVolume(volume);
+        }
+    }
+
+    /**
+     * Sends track info list to related TV interactive app.
+     * @hide
+     */
+    public void sendTrackInfoList(List<TvTrackInfo> tracks) {
+        if (DEBUG) {
+            Log.d(TAG, "sendTrackInfoList");
+        }
+        if (mSession != null) {
+            mSession.sendTrackInfoList(tracks);
+        }
+    }
+
+    /**
+     * Sends current TV input ID to related TV interactive app.
+     *
+     * @param inputId The current TV input ID whose channel is tuned. {@code null} if no channel is
+     *                tuned.
+     * @see android.media.tv.TvInputInfo
+     * @hide
+     */
+    public void sendCurrentTvInputId(@Nullable String inputId) {
+        if (DEBUG) {
+            Log.d(TAG, "sendCurrentTvInputId");
+        }
+        if (mSession != null) {
+            mSession.sendCurrentTvInputId(inputId);
+        }
+    }
+
+    private void resetInternal() {
+        mSessionCallback = null;
+        if (mSession != null) {
+            setSessionSurface(null);
+            removeSessionMediaView();
+            mUseRequestedSurfaceLayout = false;
+            mSession.release();
+            mSession = null;
+            resetSurfaceView();
+        }
+    }
+
+    /**
+     * Creates broadcast-independent(BI) interactive application.
+     *
+     * @see #destroyBiInteractiveApp(String)
+     * @hide
+     */
+    public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+        if (DEBUG) {
+            Log.d(TAG, "createBiInteractiveApp Uri=" + biIAppUri + ", params=" + params);
+        }
+        if (mSession != null) {
+            mSession.createBiInteractiveApp(biIAppUri, params);
+        }
+    }
+
+    /**
+     * Destroys broadcast-independent(BI) interactive application.
+     *
+     * @param biIAppId the BI interactive app ID from {@link #createBiInteractiveApp(Uri, Bundle)}
+     *
+     * @see #createBiInteractiveApp(Uri, Bundle)
+     * @hide
+     */
+    public void destroyBiInteractiveApp(@NonNull String biIAppId) {
+        if (DEBUG) {
+            Log.d(TAG, "destroyBiInteractiveApp biIAppId=" + biIAppId);
+        }
+        if (mSession != null) {
+            mSession.destroyBiInteractiveApp(biIAppId);
+        }
+    }
+
+    /** @hide */
+    public Session getInteractiveAppSession() {
+        return mSession;
+    }
+
+    /**
+     * Sets the TvInteractiveAppView to receive events from TIS. This method links the session of
+     * TvInteractiveAppManager to TvInputManager session, so the TIAS can get the TIS events.
+     *
+     * @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
+     * @return The result of the operation.
+     * @hide
+     */
+    public int setTvView(@Nullable TvView tvView) {
+        if (tvView == null) {
+            return unsetTvView();
+        }
+        TvInputManager.Session inputSession = tvView.getInputSession();
+        if (inputSession == null || mSession == null) {
+            return SET_TVVIEW_FAIL;
+        }
+        mSession.setInputSession(inputSession);
+        inputSession.setInteractiveAppSession(mSession);
+        return SET_TVVIEW_SUCCESS;
+    }
+
+    private int unsetTvView() {
+        if (mSession == null || mSession.getInputSession() == null) {
+            return UNSET_TVVIEW_FAIL;
+        }
+        mSession.getInputSession().setInteractiveAppSession(null);
+        mSession.setInputSession(null);
+        return UNSET_TVVIEW_SUCCESS;
+    }
+
+    /**
+     * To toggle Digital Teletext Application if there is one in AIT app list.
+     * @param enable
+     * @hide
+     */
+    public void setTeletextAppEnabled(boolean enable) {
+        if (DEBUG) {
+            Log.d(TAG, "setTeletextAppEnabled enable=" + enable);
+        }
+        if (mSession != null) {
+            mSession.setTeletextAppEnabled(enable);
+        }
+    }
+
+    /**
+     * Callback used to receive various status updates on the {@link TvInteractiveAppView}.
+     */
+    public abstract static class TvInteractiveAppCallback {
+        // TODO: unhide the following public APIs
+
+        /**
+         * This is called when a command is requested to be processed by the related TV input.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @param cmdType type of the command
+         * @param parameters parameters of the command
+         * @hide
+         */
+        public void onCommandRequest(
+                @NonNull String iAppServiceId,
+                @NonNull @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
+                @Nullable Bundle parameters) {
+        }
+
+        /**
+         * This is called when the session state is changed.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @param state the current state.
+         * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE}
+         *              is used when the state is not
+         *              {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
+         * @hide
+         */
+        public void onStateChanged(
+                @NonNull String iAppServiceId,
+                @TvInteractiveAppManager.InteractiveAppState int state,
+                @TvInteractiveAppManager.ErrorCode int err) {
+        }
+
+        /**
+         * This is called when broadcast-independent (BI) interactive app is created.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @param biIAppUri URI associated this BI interactive app. This is the same URI in
+         *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
+         * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
+         *                 app.
+         * @hide
+         */
+        public void onBiInteractiveAppCreated(@NonNull String iAppServiceId, @NonNull Uri biIAppUri,
+                @Nullable String biIAppId) {
+        }
+
+        /**
+         * This is called when the digital teletext app state is changed.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @param state digital teletext app current state.
+         * @hide
+         */
+        public void onTeletextAppStateChanged(
+                @NonNull String iAppServiceId,
+                @TvInteractiveAppManager.TeletextAppState int state) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onSetVideoBounds(@NonNull String iAppServiceId, @NonNull Rect rect) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
+         * called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onRequestCurrentChannelUri(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
+         * called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onRequestCurrentChannelLcn(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
+         * called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onRequestStreamVolume(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
+         * called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
+        }
+
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the unhandled input event is received.
+     * @hide
+     */
+    public interface OnUnhandledInputEventListener {
+        /**
+         * Called when an input event was not handled by the TV Interactive App.
+         *
+         * <p>This is called asynchronously from where the event is dispatched. It gives the host
+         * application a chance to handle the unhandled input events.
+         *
+         * @param event The input event.
+         * @return If you handled the event, return {@code true}. If you want to allow the event to
+         *         be handled by the next receiver, return {@code false}.
+         */
+        boolean onUnhandledInputEvent(@NonNull InputEvent event);
+    }
+
+    private class MySessionCallback extends SessionCallback {
+        final String mIAppServiceId;
+        int mType;
+
+        MySessionCallback(String iAppServiceId, int type) {
+            mIAppServiceId = iAppServiceId;
+            mType = type;
+        }
+
+        @Override
+        public void onSessionCreated(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionCreated()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionCreated - session already created");
+                // This callback is obsolete.
+                if (session != null) {
+                    session.release();
+                }
+                return;
+            }
+            mSession = session;
+            if (session != null) {
+                // mSurface may not be ready yet as soon as starting an application.
+                // In the case, we don't send Session.setSurface(null) unnecessarily.
+                // setSessionSurface will be called in surfaceCreated.
+                if (mSurface != null) {
+                    setSessionSurface(mSurface);
+                    if (mSurfaceChanged) {
+                        dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+                    }
+                }
+                createSessionMediaView();
+            } else {
+                // Failed to create
+                // Todo: forward error to Tv App
+                mSessionCallback = null;
+            }
+        }
+
+        @Override
+        public void onSessionReleased(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionReleased()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionReleased - session not created");
+                return;
+            }
+            mMediaViewCreated = false;
+            mMediaViewFrame = null;
+            mSessionCallback = null;
+            mSession = null;
+        }
+
+        @Override
+        public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+            if (DEBUG) {
+                Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right="
+                        + right + ", bottom=" + bottom + ",)");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onLayoutSurface - session not created");
+                return;
+            }
+            mSurfaceViewLeft = left;
+            mSurfaceViewTop = top;
+            mSurfaceViewRight = right;
+            mSurfaceViewBottom = bottom;
+            mUseRequestedSurfaceLayout = true;
+            requestLayout();
+        }
+
+        @Override
+        public void onCommandRequest(
+                Session session,
+                @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
+                Bundle parameters) {
+            if (DEBUG) {
+                Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+                        + parameters.toString() + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onCommandRequest - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onSessionStateChanged(
+                Session session,
+                @TvInteractiveAppManager.InteractiveAppState int state,
+                @TvInteractiveAppManager.ErrorCode int err) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionStateChanged (state=" + state + "; err=" + err + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionStateChanged - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onStateChanged(mIAppServiceId, state, err);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
+            if (DEBUG) {
+                Log.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri + ", biIAppId="
+                        + biIAppId + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onBiInteractiveAppCreated - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onBiInteractiveAppCreated(
+                                        mIAppServiceId, biIAppUri, biIAppId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onTeletextAppStateChanged(Session session, int state) {
+            if (DEBUG) {
+                Log.d(TAG, "onTeletextAppStateChanged (state=" + state +  ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onTeletextAppStateChanged - session not created");
+                return;
+            }
+            if (mCallback != null) {
+                mCallback.onTeletextAppStateChanged(mIAppServiceId, state);
+            }
+        }
+
+        @Override
+        public void onSetVideoBounds(Session session, Rect rect) {
+            if (DEBUG) {
+                Log.d(TAG, "onSetVideoBounds (rect=" + rect + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSetVideoBounds - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onSetVideoBounds(mIAppServiceId, rect);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentChannelUri(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestCurrentChannelUri");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestCurrentChannelUri - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestCurrentChannelUri(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentChannelLcn(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestCurrentChannelLcn");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestCurrentChannelLcn - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onRequestStreamVolume(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestStreamVolume");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestStreamVolume - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestStreamVolume(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onRequestTrackInfoList(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestTrackInfoList");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestTrackInfoList - session not created");
+                return;
+            }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestTrackInfoList(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentTvInputId(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestCurrentTvInputId");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestCurrentTvInputId - session not created");
+                return;
+            }
+            if (mCallback != null) {
+                mCallback.onRequestCurrentTvInputId(mIAppServiceId);
+            }
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 6a6a22c..50a2083 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -28,6 +28,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -145,9 +148,9 @@
 
     private static final String TAG = "Lnb";
 
-    LnbCallback mCallback;
-    Executor mExecutor;
-    Tuner mTuner;
+    Map<LnbCallback, Executor> mCallbackMap =
+            new HashMap<LnbCallback, Executor>();
+    Tuner mOwner;
     private final Object mCallbackLock = new Object();
 
 
@@ -164,38 +167,82 @@
 
     private Lnb() {}
 
-    void setCallback(Executor executor, @Nullable LnbCallback callback, Tuner tuner) {
+    void setCallbackAndOwner(Executor executor, @Nullable LnbCallback callback, Tuner tuner) {
         synchronized (mCallbackLock) {
-            mCallback = callback;
-            mExecutor = executor;
-            mTuner = tuner;
+            if (callback != null && executor != null) {
+                addCallback(callback, executor);
+            }
+        }
+        setOwner(tuner);
+    }
+
+    /**
+     * Adds LnbCallback
+     *
+     * @param callback the callback to receive notifications from LNB.
+     * @param executor the executor on which callback will be invoked. Cannot be null.
+     */
+    public void addCallback(@NonNull  LnbCallback callback, @NonNull Executor executor) {
+        Objects.requireNonNull(callback, "callback must not be null");
+        Objects.requireNonNull(executor, "executor must not be null");
+        synchronized (mCallbackLock) {
+            mCallbackMap.put(callback, executor);
+        }
+    }
+
+    /**
+     * Removes LnbCallback
+     *
+     * @param callback the callback be removed for callback
+     *
+     * @return {@code true} when successful. {@code false} otherwise.
+     */
+    public boolean removeCallback(@NonNull LnbCallback callback) {
+        Objects.requireNonNull(callback, "callback must not be null");
+        synchronized (mCallbackLock) {
+            boolean result = (mCallbackMap.remove(callback) != null);
+            return result;
+        }
+    }
+
+    // allow owner transfer independent of whether callback is registered or not
+    /* package */ void setOwner(@NonNull Tuner newOwner) {
+        Objects.requireNonNull(newOwner, "newOwner must not be null");
+        synchronized (mLock) {
+            mOwner = newOwner;
         }
     }
 
     private void onEvent(int eventType) {
         synchronized (mCallbackLock) {
-            if (mExecutor != null && mCallback != null) {
-                mExecutor.execute(() -> {
-                    synchronized (mCallbackLock) {
-                        if (mCallback != null) {
-                            mCallback.onEvent(eventType);
+            for (LnbCallback callback : mCallbackMap.keySet()) {
+                Executor executor = mCallbackMap.get(callback);
+                if (callback != null && executor != null) {
+                    executor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (callback != null) {
+                                callback.onEvent(eventType);
+                            }
                         }
-                    }
-                });
+                    });
+                }
             }
         }
     }
 
     private void onDiseqcMessage(byte[] diseqcMessage) {
         synchronized (mCallbackLock) {
-            if (mExecutor != null && mCallback != null) {
-                mExecutor.execute(() -> {
-                    synchronized (mCallbackLock) {
-                        if (mCallback != null) {
-                            mCallback.onDiseqcMessage(diseqcMessage);
+            for (LnbCallback callback : mCallbackMap.keySet()) {
+                Executor executor = mCallbackMap.get(callback);
+                if (callback != null && executor != null) {
+                    executor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (callback != null) {
+                                callback.onDiseqcMessage(diseqcMessage);
+                            }
                         }
-                    }
-                });
+                    });
+                }
             }
         }
     }
@@ -279,7 +326,11 @@
                 TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
             } else {
                 mIsClosed = true;
-                mTuner.releaseLnb();
+                if (mOwner != null) {
+                    mOwner.releaseLnb();
+                    mOwner = null;
+                }
+                mCallbackMap.clear();
             }
         }
     }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 300aa15..3157375 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -241,7 +241,7 @@
 
 
     private static final String TAG = "MediaTvTuner";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int MSG_RESOURCE_LOST = 1;
     private static final int MSG_ON_FILTER_EVENT = 2;
@@ -250,7 +250,6 @@
 
     private static final int FILTER_CLEANUP_THRESHOLD = 256;
 
-
     /** @hide */
     @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
     @Retention(RetentionPolicy.SOURCE)
@@ -455,6 +454,260 @@
     }
 
     /**
+     * Transfers the ownership of shared frontend and its associated resources.
+     *
+     * @param newOwner the Tuner instance to be the new owner.
+     *
+     * @return result status of tune operation.
+     */
+    public int transferOwner(@NonNull Tuner newOwner) {
+        acquireTRMSLock("transferOwner()");
+        mFrontendLock.lock();
+        mFrontendCiCamLock.lock();
+        mLnbLock.lock();
+        try {
+
+            if (!isFrontendOwner() || !isNewOwnerQualifiedForTransfer(newOwner)) {
+                return RESULT_INVALID_STATE;
+            }
+
+            int res = transferFeOwner(newOwner);
+            if (res != RESULT_SUCCESS) {
+                return res;
+            }
+
+            res = transferCiCamOwner(newOwner);
+            if (res != RESULT_SUCCESS) {
+                return res;
+            }
+
+            res = transferLnbOwner(newOwner);
+            if (res != RESULT_SUCCESS) {
+                return res;
+            }
+        } finally {
+            mFrontendLock.unlock();
+            mFrontendCiCamLock.unlock();
+            mLnbLock.unlock();
+            releaseTRMSLock();
+        }
+        return RESULT_SUCCESS;
+    }
+
+    /**
+     * Resets or copies Frontend related settings.
+     */
+    private void replicateFrontendSettings(@Nullable Tuner src) {
+        mFrontendLock.lock();
+        try {
+            if (src == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "resetting Frontend params for " + mClientId);
+                }
+                mFrontend = null;
+                mFrontendHandle = null;
+                mFrontendInfo = null;
+                mFrontendType = FrontendSettings.TYPE_UNDEFINED;
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "copying Frontend params from " + src.mClientId
+                            + " to " + mClientId);
+                }
+                mFrontend = src.mFrontend;
+                mFrontendHandle = src.mFrontendHandle;
+                mFrontendInfo = src.mFrontendInfo;
+                mFrontendType = src.mFrontendType;
+            }
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
+     * Sets the frontend owner. mFeOwnerTuner should be null for the owner Tuner instance.
+     */
+    private void setFrontendOwner(Tuner owner) {
+        mFrontendLock.lock();
+        try {
+            mFeOwnerTuner = owner;
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
+     * Resets or copies the CiCam related settings.
+     */
+    private void replicateCiCamSettings(@Nullable Tuner src) {
+        mFrontendCiCamLock.lock();
+        try {
+            if (src == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "resetting CiCamParams: " + mClientId);
+                }
+                mFrontendCiCamHandle = null;
+                mFrontendCiCamId = null;
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "copying CiCamParams from " + src.mClientId + " to " + mClientId);
+                    Log.d(TAG, "mFrontendCiCamHandle:" + src.mFrontendCiCamHandle + ", "
+                            + "mFrontendCiCamId:" + src.mFrontendCiCamId);
+                }
+                mFrontendCiCamHandle = src.mFrontendCiCamHandle;
+                mFrontendCiCamId = src.mFrontendCiCamId;
+            }
+        } finally {
+            mFrontendCiCamLock.unlock();
+        }
+    }
+
+    /**
+     * Resets or copies Lnb related settings.
+     */
+    private void replicateLnbSettings(@Nullable Tuner src) {
+        mLnbLock.lock();
+        try {
+            if (src == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "resetting Lnb params");
+                }
+                mLnb = null;
+                mLnbHandle = null;
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "copying Lnb params from " + src.mClientId + " to " + mClientId);
+                }
+                mLnb = src.mLnb;
+                mLnbHandle = src.mLnbHandle;
+            }
+        } finally {
+            mLnbLock.unlock();
+        }
+    }
+
+    /**
+     * Checks if it is a frontend resource owner.
+     * Proper mutex must be held prior to calling this.
+     */
+    private boolean isFrontendOwner() {
+        boolean notAnOwner = (mFeOwnerTuner != null);
+        if (notAnOwner) {
+            Log.e(TAG, "transferOwner() - cannot be called on the non-owner");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks if the newOwner is qualified.
+     * Proper mutex must be held prior to calling this.
+     */
+    private boolean isNewOwnerQualifiedForTransfer(@NonNull Tuner newOwner) {
+        // new owner must be the current sharee
+        boolean newOwnerIsTheCurrentSharee = (newOwner.mFeOwnerTuner == this)
+                && (newOwner.mFrontendHandle.equals(mFrontendHandle));
+        if (!newOwnerIsTheCurrentSharee) {
+            Log.e(TAG, "transferOwner() - new owner must be the current sharee");
+            return false;
+        }
+
+        // new owner must not be holding any of the to-be-shared resources
+        boolean newOwnerAlreadyHoldsToBeSharedResource =
+                (newOwner.mFrontendCiCamHandle != null || newOwner.mLnb != null);
+        if (newOwnerAlreadyHoldsToBeSharedResource) {
+            Log.e(TAG, "transferOwner() - new owner cannot be holding CiCam"
+                    + " nor Lnb resource");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Transfers the ownership of the already held frontend resource.
+     * Proper mutex must be held prior to calling this.
+     */
+    private int transferFeOwner(@NonNull Tuner newOwner) {
+        // handle native resource first
+        newOwner.nativeUpdateFrontend(getNativeContext());
+        nativeUpdateFrontend(0);
+
+        // transfer frontend related settings
+        newOwner.replicateFrontendSettings(this);
+
+        // transfer the frontend owner info
+        setFrontendOwner(newOwner);
+        newOwner.setFrontendOwner(null);
+
+        // handle TRM
+        if (mTunerResourceManager.transferOwner(
+                TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
+                mClientId, newOwner.mClientId)) {
+            return RESULT_SUCCESS;
+        } else {
+            return RESULT_UNKNOWN_ERROR;
+        }
+    }
+
+    /**
+     * Transfers the ownership of CiCam resource.
+     * This is a no-op if the CiCam resource is not held.
+     * Proper mutex must be held prior to calling this.
+     */
+    private int transferCiCamOwner(Tuner newOwner) {
+        boolean notAnOwner = (mFrontendCiCamHandle == null);
+        if (notAnOwner) {
+            // There is nothing to do here if there is no CiCam
+            return RESULT_SUCCESS;
+        }
+
+        // no need to handle at native level
+
+        // transfer the CiCam info at Tuner level
+        newOwner.replicateCiCamSettings(this);
+        replicateCiCamSettings(null);
+
+        // handle TRM
+        if (mTunerResourceManager.transferOwner(
+                TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM,
+                mClientId, newOwner.mClientId)) {
+            return RESULT_SUCCESS;
+        } else {
+            return RESULT_UNKNOWN_ERROR;
+        }
+    }
+
+    /**
+     * Transfers the ownership of Lnb resource.
+     * This is a no-op if the Lnb resource is not held.
+     * Proper mutex must be held prior to calling this.
+     */
+    private int transferLnbOwner(Tuner newOwner) {
+        boolean notAnOwner = (mLnb == null);
+        if (notAnOwner) {
+            // There is nothing to do here if there is no Lnb
+            return RESULT_SUCCESS;
+        }
+
+        // no need to handle at native level
+
+        // set the new owner
+        mLnb.setOwner(newOwner);
+
+        newOwner.replicateLnbSettings(this);
+        replicateLnbSettings(null);
+
+        // handle TRM
+        if (mTunerResourceManager.transferOwner(
+                TunerResourceManager.TUNER_RESOURCE_TYPE_LNB,
+                mClientId, newOwner.mClientId)) {
+            return RESULT_SUCCESS;
+        } else {
+            return RESULT_UNKNOWN_ERROR;
+        }
+    }
+
+    /**
      * Updates client priority with an arbitrary value along with a nice value.
      *
      * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
@@ -547,59 +800,114 @@
         }
     }
 
+    /**
+     * Either unshares the frontend resource (for sharee) or release Frontend (for owner)
+     */
+    public void closeFrontend() {
+        acquireTRMSLock("closeFrontend()");
+        try {
+            releaseFrontend();
+        } finally {
+            releaseTRMSLock();
+        }
+    }
+
+    /**
+     * Releases frontend resource for the owner. Unshares frontend resource for the sharee.
+     */
     private void releaseFrontend() {
+        if (DEBUG) {
+            Log.d(TAG, "Tuner#releaseFrontend");
+        }
         mFrontendLock.lock();
         try {
             if (mFrontendHandle != null) {
+                if (DEBUG) {
+                    Log.d(TAG, "mFrontendHandle not null");
+                }
                 if (mFeOwnerTuner != null) {
+                    if (DEBUG) {
+                        Log.d(TAG, "mFeOwnerTuner not null - sharee");
+                    }
                     // unregister self from the Frontend callback
                     mFeOwnerTuner.unregisterFrontendCallbackListener(this);
                     mFeOwnerTuner = null;
+                    nativeUnshareFrontend();
                 } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "mFeOwnerTuner null - owner");
+                    }
                     // close resource as owner
                     int res = nativeCloseFrontend(mFrontendHandle);
                     if (res != Tuner.RESULT_SUCCESS) {
                         TunerUtils.throwExceptionForResult(res, "failed to close frontend");
                     }
-                    mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
                 }
+                if (DEBUG) {
+                    Log.d(TAG, "call TRM#releaseFrontend :" + mFrontendHandle + ", " + mClientId);
+                }
+                mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
                 FrameworkStatsLog
                         .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
                         FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
-                mFrontendHandle = null;
-                mFrontend = null;
+                replicateFrontendSettings(null);
             }
         } finally {
             mFrontendLock.unlock();
         }
     }
 
+    /**
+     * Releases CiCam resource if held. No-op otherwise.
+     */
+    private void releaseCiCam() {
+        mFrontendCiCamLock.lock();
+        try {
+            if (mFrontendCiCamHandle != null) {
+                if (DEBUG) {
+                    Log.d(TAG, "unlinking CiCam : " + mFrontendCiCamHandle + " for " +  mClientId);
+                }
+                int result = nativeUnlinkCiCam(mFrontendCiCamId);
+                if (result == RESULT_SUCCESS) {
+                    mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+                    replicateCiCamSettings(null);
+                } else {
+                    Log.e(TAG, "nativeUnlinkCiCam(" + mFrontendCiCamHandle + ") for mClientId:"
+                            + mClientId + "failed with result:" + result);
+                }
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "NOT unlinking CiCam : " + mClientId);
+                }
+            }
+        } finally {
+            mFrontendCiCamLock.unlock();
+        }
+    }
+
     private void releaseAll() {
+        // release CiCam before frontend because frontend handle is needed to unlink CiCam
+        releaseCiCam();
+
         releaseFrontend();
 
         mLnbLock.lock();
         try {
             // mLnb will be non-null only for owner tuner
             if (mLnb != null) {
+                if (DEBUG) {
+                    Log.d(TAG, "calling mLnb.close() : " + mClientId);
+                }
                 mLnb.close();
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "NOT calling mLnb.close() : " + mClientId);
+                }
             }
         } finally {
             mLnbLock.unlock();
         }
 
-        mFrontendCiCamLock.lock();
-        try {
-            if (mFrontendCiCamHandle != null) {
-                int result = nativeUnlinkCiCam(mFrontendCiCamId);
-                if (result == RESULT_SUCCESS) {
-                    mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
-                    mFrontendCiCamId = null;
-                    mFrontendCiCamHandle = null;
-                }
-            }
-        } finally {
-            mFrontendCiCamLock.unlock();
-        }
 
         synchronized (mDescramblers) {
             if (!mDescramblers.isEmpty()) {
@@ -669,8 +977,11 @@
      */
     private native Frontend nativeOpenFrontendByHandle(int handle);
     private native int nativeShareFrontend(int id);
+    private native int nativeUnshareFrontend();
     private native void nativeRegisterFeCbListener(long nativeContext);
     private native void nativeUnregisterFeCbListener(long nativeContext);
+    // nativeUpdateFrontend must be called on the new owner first
+    private native void nativeUpdateFrontend(long nativeContext);
     @Result
     private native int nativeTune(int type, FrontendSettings settings);
     private native int nativeStopTune();
@@ -691,7 +1002,7 @@
     private native String nativeGetFrontendHardwareInfo();
     private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
     private native int nativeGetMaxNumberOfFrontends(int frontendType);
-
+    private native int nativeRemoveOutputPid(int pid);
     private native Lnb nativeOpenLnbByHandle(int handle);
     private native Lnb nativeOpenLnbByName(String name);
 
@@ -997,6 +1308,21 @@
             mFrontendHandle = feHandle[0];
             mFrontend = nativeOpenFrontendByHandle(mFrontendHandle);
         }
+
+        // For satellite type, set Lnb if valid handle exists.
+        // This is necessary as now that we support closeFrontend().
+        if (mFrontendType == FrontendSettings.TYPE_DVBS
+                || mFrontendType == FrontendSettings.TYPE_ISDBS
+                || mFrontendType == FrontendSettings.TYPE_ISDBS3) {
+            mLnbLock.lock();
+            try {
+                if (mLnbHandle != null && mLnb != null) {
+                    nativeSetLnb(mLnb);
+                }
+            } finally {
+                mLnbLock.unlock();
+            }
+        }
         return granted;
     }
 
@@ -1239,6 +1565,36 @@
     }
 
     /**
+     * Filter out unnecessary PID (packet identifier) from frontend output.
+     *
+     * <p>It is used by the client to remove some video or audio PIDs of other program to reduce the
+     * total amount of recorded TS.
+     *
+     * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would cause
+     * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     *
+     * @return result status of the operation. Unsupported version or if current active frontend
+     *         doesn’t support PID filtering out would return {@link #RESULT_UNAVAILABLE}.
+     * @throws IllegalStateException if there is no active frontend currently.
+     */
+    @Result
+    public int removeOutputPid(@IntRange(from = 0) int pid) {
+        mFrontendLock.lock();
+        try {
+            if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
+                return RESULT_UNAVAILABLE;
+            }
+            if (mFrontend == null) {
+                throw new IllegalStateException("frontend is not initialized");
+            }
+            return nativeRemoveOutputPid(pid);
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
      * Gets the currently initialized and activated frontend information. To get all the available
      * frontend info on the device, use {@link getAvailableFrontendInfos()}.
      *
@@ -1294,7 +1650,7 @@
      * @throws IllegalStateException if there is no active frontend currently.
      */
     @Nullable
-    public String getCurrentFrontendHardwardInfo() {
+    public String getCurrentFrontendHardwareInfo() {
         mFrontendLock.lock();
         try {
             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
@@ -1434,7 +1790,7 @@
         }
     }
 
-    private void onUnLocked() {
+    private void onUnlocked() {
         Log.d(TAG, "Wrote Stats Log for unlocked event from scanning.");
         FrameworkStatsLog.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
                 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
@@ -1444,7 +1800,7 @@
                 mScanCallbackExecutor.execute(() -> {
                     synchronized (mScanCallbackLock) {
                         if (mScanCallback != null) {
-                            mScanCallback.onUnLocked();
+                            mScanCallback.onUnlocked();
                         }
                     }
                 });
@@ -1756,12 +2112,12 @@
             Objects.requireNonNull(executor, "executor must not be null");
             Objects.requireNonNull(cb, "LnbCallback must not be null");
             if (mLnb != null) {
-                mLnb.setCallback(executor, cb, this);
+                mLnb.setCallbackAndOwner(executor, cb, this);
                 return mLnb;
             }
             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, mLnbLock)
                     && mLnb != null) {
-                mLnb.setCallback(executor, cb, this);
+                mLnb.setCallbackAndOwner(executor, cb, this);
                 setLnb(mLnb);
                 return mLnb;
             }
@@ -1795,7 +2151,7 @@
                     mLnbHandle = null;
                 }
                 mLnb = newLnb;
-                mLnb.setCallback(executor, cb, this);
+                mLnb.setCallbackAndOwner(executor, cb, this);
                 setLnb(mLnb);
             }
             return mLnb;
@@ -2081,8 +2437,15 @@
         try {
             if (mLnbHandle != null) {
                 // LNB handle can be null if it's opened by name.
+                if (DEBUG) {
+                    Log.d(TAG, "releasing Lnb");
+                }
                 mTunerResourceManager.releaseLnb(mLnbHandle, mClientId);
                 mLnbHandle = null;
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "NOT releasing Lnb because mLnbHandle is null");
+                }
             }
             mLnb = null;
         } finally {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f9fc17f..9f44236 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -283,9 +283,21 @@
                     synchronized (mCallbackLock) {
                         if (mCallback != null) {
                             mCallback.onFilterEvent(this, events);
+                        } else {
+                            for (FilterEvent event : events) {
+                                if (event instanceof MediaEvent) {
+                                    ((MediaEvent)event).release();
+                                }
+                            }
                         }
                     }
                 });
+            } else {
+                for (FilterEvent event : events) {
+                    if (event instanceof MediaEvent) {
+                        ((MediaEvent)event).release();
+                    }
+                }
             }
         }
     }
@@ -558,6 +570,8 @@
             if (res != Tuner.RESULT_SUCCESS) {
                 TunerUtils.throwExceptionForResult(res, "Failed to close filter.");
             } else {
+                mCallback = null;
+                mExecutor = null;
                 mIsStarted = false;
                 mIsClosed = true;
             }
@@ -612,8 +626,12 @@
      * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
      *
      * @param delayInMs specifies the duration of the delay in milliseconds.
+     * @return one of the following results: {@link Tuner#RESULT_SUCCESS},
+     * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
+     * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
+     * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
      */
-    public int delayCallbackUntilTimeMillis(long delayInMs) {
+    public int delayCallbackUntilMillisElapsed(long delayInMs) {
         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
                   TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
             return Tuner.RESULT_UNAVAILABLE;
@@ -638,8 +656,12 @@
      * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
      *
      * @param delayInBytes specifies the duration of the delay in bytes.
+     * @return one of the following results: {@link Tuner#RESULT_SUCCESS},
+     * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
+     * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
+     * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
      */
-    public int delayCallbackUntilBufferFilled(int delayInBytes) {
+    public int delayCallbackUntilBytesAccumulated(int delayInBytes) {
         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
                   TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
             return Tuner.RESULT_UNAVAILABLE;
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 94fda30..83ed8e8 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,12 +16,13 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.media.tv.tuner.TunerUtils;
 
 /**
- * Filter Settings for Section data according to ISO/IEC 13818-1.
+ * Filter Settings for Section data according to ISO/IEC 13818-1 and ISO/IEC 23008-1.
  *
  * @hide
  */
@@ -30,12 +31,15 @@
     final boolean mCrcEnabled;
     final boolean mIsRepeat;
     final boolean mIsRaw;
+    final int mBitWidthOfLengthField;
 
-    SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw) {
+    SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw,
+            int bitWidthOfLengthField) {
         super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
         mCrcEnabled = crcEnabled;
         mIsRepeat = isRepeat;
         mIsRaw = isRaw;
+        mBitWidthOfLengthField = bitWidthOfLengthField;
     }
 
     /**
@@ -62,6 +66,7 @@
     public boolean isRepeat() {
         return mIsRepeat;
     }
+
     /**
      * Returns whether the filter sends {@link FilterCallback#onFilterStatusChanged} instead of
      * {@link FilterCallback#onFilterEvent}.
@@ -71,6 +76,17 @@
     }
 
     /**
+     * Returns the bit width of the MMTP (MPEG Media Transport Protocol) section message's length
+     * field according to ISO/IEC 23008-1.
+     *
+     * The section filter uses this for CRC (Cyclic redundancy check) checking when
+     * {@link #isCrcEnabled()} is {@code true}.
+     */
+    public int getLengthFieldBitWidth() {
+        return mBitWidthOfLengthField;
+    }
+
+    /**
      * Builder for {@link SectionSettings}.
      *
      * @param <T> The subclass to be built.
@@ -80,6 +96,7 @@
         boolean mCrcEnabled;
         boolean mIsRepeat;
         boolean mIsRaw;
+        int mBitWidthOfLengthField;
 
         Builder(int mainType) {
             mMainType = mainType;
@@ -114,6 +131,7 @@
             mIsRepeat = isRepeat;
             return self();
         }
+
         /**
          * Sets whether the filter send onFilterStatus instead of
          * {@link FilterCallback#onFilterEvent}.
@@ -124,6 +142,23 @@
             return self();
         }
 
+        /**
+         * Sets the bit width for the MMTP(MPEG Media Transport Protocol) section message's length
+         * field according to ISO/IEC 23008-1.
+         *
+         * The section filter uses this for CRC (Cyclic redundancy check) checking when
+         * {@link #isCrcEnabled()} is {@code true}.
+         *
+         * <p>This field is only supported in Tuner 2.0 or higher version. Unsupported version will
+         * cause no-op. Use {@link android.media.tv.tuner.TunerVersionChecker#getTunerVersion()}
+         * to get the version information.
+         */
+        @NonNull
+        public T setBitWidthOfLengthField(@IntRange(from = 0) int bitWidthOfLengthField) {
+            mBitWidthOfLengthField = bitWidthOfLengthField;
+            return self();
+        }
+
         /* package */ abstract T self();
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index edfe85e..766a5c9 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -30,10 +30,9 @@
     private final byte[] mMask;
     private final byte[] mMode;
 
-
     private SectionSettingsWithSectionBits(int mainType, boolean isCheckCrc, boolean isRepeat,
-            boolean isRaw, byte[] filter, byte[] mask, byte[] mode) {
-        super(mainType, isCheckCrc, isRepeat, isRaw);
+            boolean isRaw, int bitWidthOfLengthField, byte[] filter, byte[] mask, byte[] mode) {
+        super(mainType, isCheckCrc, isRepeat, isRaw, bitWidthOfLengthField);
         mFilter = filter;
         mMask = mask;
         mMode = mode;
@@ -126,8 +125,8 @@
          */
         @NonNull
         public SectionSettingsWithSectionBits build() {
-            return new SectionSettingsWithSectionBits(
-                    mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mFilter, mMask, mMode);
+            return new SectionSettingsWithSectionBits(mMainType, mCrcEnabled, mIsRepeat, mIsRaw,
+                    mBitWidthOfLengthField, mFilter, mMask, mMode);
         }
 
         @Override
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index ff8f796..eddef40 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -37,8 +37,8 @@
     private final int mVersion;
 
     private SectionSettingsWithTableInfo(int mainType, boolean isCheckCrc, boolean isRepeat,
-            boolean isRaw, int tableId, int version) {
-        super(mainType, isCheckCrc, isRepeat, isRaw);
+            boolean isRaw, int bitWidthOfLengthField, int tableId, int version) {
+        super(mainType, isCheckCrc, isRepeat, isRaw, bitWidthOfLengthField);
         mTableId = tableId;
         mVersion = version;
     }
@@ -99,8 +99,8 @@
          */
         @NonNull
         public SectionSettingsWithTableInfo build() {
-            return new SectionSettingsWithTableInfo(
-                    mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mTableId, mVersion);
+            return new SectionSettingsWithTableInfo(mMainType, mCrcEnabled, mIsRepeat, mIsRaw,
+                    mBitWidthOfLengthField, mTableId, mVersion);
         }
 
         @Override
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 8cedd04..c1e9b38a 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -54,7 +54,7 @@
             FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
             FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
             FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_IDS,
-            FRONTEND_STATUS_TYPE_DVBT_CELL_IDS})
+            FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendStatusType {}
 
@@ -165,7 +165,7 @@
     public static final int FRONTEND_STATUS_TYPE_RF_LOCK =
             android.hardware.tv.tuner.FrontendStatusType.RF_LOCK;
     /**
-     * PLP information in a frequency band for ATSC-3.0 frontend.
+     * Current tuned PLP information in a frequency band for ATSC-3.0 frontend.
      */
     public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
             android.hardware.tv.tuner.FrontendStatusType.ATSC3_PLP_INFO;
@@ -267,6 +267,13 @@
     public static final int FRONTEND_STATUS_TYPE_DVBT_CELL_IDS =
             android.hardware.tv.tuner.FrontendStatusType.DVBT_CELL_IDS;
 
+    /**
+     * All PLP information in a frequency band for ATSC-3.0 frontend, which includes both tuned and
+     * not tuned PLPs for currently watching service.
+     */
+    public static final int FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO =
+            android.hardware.tv.tuner.FrontendStatusType.ATSC3_ALL_PLP_INFO;
+
     /** @hide */
     @IntDef(value = {
             AtscFrontendSettings.MODULATION_UNDEFINED,
@@ -508,6 +515,7 @@
     private Integer mIsdbtPartialReceptionFlag;
     private int[] mStreamIds;
     private int[] mDvbtCellIds;
+    private Atsc3PlpInfo[] mAllPlpInfo;
 
     // Constructed and fields set by JNI code.
     private FrontendStatus() {
@@ -1078,6 +1086,25 @@
     }
 
     /**
+     * Gets an array of all PLPs information of ATSC3 frontend, which includes both tuned and not
+     * tuned PLPs for currently watching service.
+     *
+     * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+     * doesn't return all PLPs information will throw IllegalStateException. Use
+     * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     */
+    @SuppressLint("ArrayReturn")
+    @NonNull
+    public Atsc3PlpInfo[] getAllAtsc3PlpInfo() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status");
+        if (mAllPlpInfo == null) {
+            throw new IllegalStateException("Atsc3PlpInfo all status is empty");
+        }
+        return mAllPlpInfo;
+    }
+
+    /**
      * Information of each tuning Physical Layer Pipes.
      */
     public static class Atsc3PlpTuningInfo {
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 6bbc13fe..68f3c1b 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -37,7 +37,7 @@
     void onLocked();
 
     /** Scan unlocked the signal. */
-    default void onUnLocked() {}
+    default void onUnlocked() {}
 
     /** Scan stopped. */
     void onScanStopped();
diff --git a/media/java/android/media/tv/tunerresourcemanager/OWNER b/media/java/android/media/tv/tunerresourcemanager/OWNER
index 76b84d9..0eb1c31 100644
--- a/media/java/android/media/tv/tunerresourcemanager/OWNER
+++ b/media/java/android/media/tv/tunerresourcemanager/OWNER
@@ -1,4 +1,3 @@
[email protected]
[email protected]
 [email protected]
[email protected]
\ No newline at end of file
[email protected]
[email protected]
\ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index fe611c7..5ada89e 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -21,9 +21,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
+import android.annotation.SuppressLint;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.tv.TvInputService;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -415,6 +418,25 @@
     }
 
     /**
+     * Transfers the ownership of shared resource.
+     *
+     * <p><strong>Note:</strong> Only the existing frontend sharee can be the new owner.
+     *
+     * @param resourceType the type of the resource to transfer the ownership for.
+     * @param currentOwnerId the id of the current owner client.
+     * @param newOwnerId the id of the new owner client.
+     *
+     * @return true if successful and false otherwise.
+     */
+    public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) {
+        try {
+            return mService.transferOwner(resourceType, currentOwnerId, newOwnerId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Requests a Tuner Demux resource.
      *
      * <p>There are three possible scenarios:
@@ -702,6 +724,49 @@
     }
 
     /**
+     * Returns a priority for the given use case type and the client's foreground or background
+     * status.
+     *
+     * @param useCase the use case type of the client. When the given use case type is invalid,
+     *        the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}.
+     * @param pid the pid of the client. When the pid is invalid, background status will be used as
+     *        a client's status. Otherwise, client's app corresponding to the given session id will
+     *        be used as a client. {@see TvInputService#onCreateSession(String, String)}.
+     *
+     * @return the client priority..
+     */
+    public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase, int pid) {
+        try {
+            return mService.getClientPriority(useCase, pid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a config priority for the given use case type and the foreground or background
+     * status.
+     *
+     * @param useCase the use case type of the client. When the given use case type is invalid,
+     *        the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}.
+     * @param isForeground {@code true} if foreground, {@code false} otherwise.
+     *
+     * @return the config priority.
+     *
+     * @hide
+     */
+    @TestApi
+    @SuppressLint("ShowingMemberInHiddenClass")
+    public int getConfigPriority(@TvInputService.PriorityHintUseCaseType int useCase,
+            boolean isForeground) {
+        try {
+            return mService.getConfigPriority(useCase, isForeground);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Interface used to receive events from TunerResourceManager.
      */
     public abstract static class ResourcesReclaimListener {
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 5f35820..d16fc6c 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -177,6 +177,19 @@
     void shareFrontend(in int selfClientId, in int targetClientId);
 
     /*
+     * Transfers the ownership of the shared resource.
+     *
+     * <p><strong>Note:</strong> Only the existing frontend sharee can be the new owner.
+     *
+     * @param resourceType the type of resource to transfer the ownership for.
+     * @param currentOwnerId the id of the current owner client.
+     * @param newOwnerId the id of the new owner client.
+     *
+     * @return true if successful. false otherwise.
+     */
+    boolean transferOwner(in int resourceType, in int currentOwnerId, in int newOwnerId);
+
+    /*
      * This API is used by the Tuner framework to request an available demux from the TunerHAL.
      *
      * <p>There are three possible scenarios:
@@ -442,4 +455,30 @@
      * guaranteed to work and may be unrecoverrable. (This should not happen.)
      */
     boolean releaseLock(in int clientId);
+
+    /**
+     * Returns a priority for the given use case type and the client's foreground or background
+     * status.
+     *
+     * @param useCase the use case type of the client. When the given use case type is invalid,
+     *        the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}.
+     * @param pid the pid of the client. When the pid is invalid, background status will be used as
+     *        a client's status. Otherwise, client's app corresponding to the given session id will
+     *        be used as a client. {@see TvInputService#onCreateSession(String, String)}.
+     *
+     * @return the client priority..
+     */
+    int getClientPriority(int useCase, int pid);
+
+    /**
+     * Returns a config priority for the given use case type and the foreground or background
+     * status.
+     *
+     * @param useCase the use case type of the client. When the given use case type is invalid,
+     *        the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}.
+     * @param isForeground {@code true} if foreground, {@code false} otherwise.
+     *
+     * @return the config priority.
+     */
+    int getConfigPriority(int useCase, boolean isForeground);
 }
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e817f2d..feae606 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -39,6 +39,7 @@
         "android_media_MediaProfiles.cpp",
         "android_media_MediaRecorder.cpp",
         "android_media_MediaSync.cpp",
+        "android_media_PublicFormatUtils.cpp",
         "android_media_ResampleInputStream.cpp",
         "android_media_Streams.cpp",
         "android_media_SyncParams.cpp",
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 021507c..6002e28 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -375,18 +375,13 @@
 }
 
 static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint width, jint height,
-                             jint format, jint maxImages, jlong ndkUsage)
-{
+                             jint maxImages, jlong ndkUsage, jint nativeFormat, jlong dataSpace) {
     status_t res;
-    int nativeFormat;
-    android_dataspace nativeDataspace;
 
-    ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d",
-          __FUNCTION__, width, height, format, maxImages);
+    ALOGV("%s: width:%d, height: %d, nativeFormat: %d, maxImages:%d",
+          __FUNCTION__, width, height, nativeFormat, maxImages);
 
-    PublicFormat publicFormat = static_cast<PublicFormat>(format);
-    nativeFormat = mapPublicFormatToHalFormat(publicFormat);
-    nativeDataspace = mapPublicFormatToHalDataspace(publicFormat);
+    android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace);
 
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
@@ -400,7 +395,7 @@
     BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
     sp<BufferItemConsumer> bufferConsumer;
     String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
-            width, height, format, maxImages, getpid(),
+            width, height, nativeFormat, maxImages, getpid(),
             createProcessUniqueId());
     uint64_t consumerUsage =
             android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
@@ -527,7 +522,8 @@
     ALOGV("%s: Image (format: 0x%x) has been released", __FUNCTION__, ctx->getBufferFormat());
 }
 
-static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) {
+static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image,
+                                   jboolean legacyValidateImageFormat) {
     ALOGV("%s:", __FUNCTION__);
     JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
     if (ctx == NULL) {
@@ -590,7 +586,7 @@
             ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
                     __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
         }
-        if (imgReaderFmt != bufferFormat) {
+        if (legacyValidateImageFormat && imgReaderFmt != bufferFormat) {
             if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
                     isPossiblyYUV(bufferFormat)) {
                 // Treat formats that are compatible with flexible YUV
@@ -958,10 +954,10 @@
 
 static const JNINativeMethod gImageReaderMethods[] = {
     {"nativeClassInit",        "()V",                        (void*)ImageReader_classInit },
-    {"nativeInit",             "(Ljava/lang/Object;IIIIJ)V",  (void*)ImageReader_init },
+    {"nativeInit",             "(Ljava/lang/Object;IIIJIJ)V",   (void*)ImageReader_init },
     {"nativeClose",            "()V",                        (void*)ImageReader_close },
     {"nativeReleaseImage",     "(Landroid/media/Image;)V",   (void*)ImageReader_imageRelease },
-    {"nativeImageSetup",       "(Landroid/media/Image;)I",   (void*)ImageReader_imageSetup },
+    {"nativeImageSetup",       "(Landroid/media/Image;Z)I",   (void*)ImageReader_imageSetup },
     {"nativeGetSurface",       "()Landroid/view/Surface;",   (void*)ImageReader_getSurface },
     {"nativeDetachImage",      "(Landroid/media/Image;)I",   (void*)ImageReader_detachImage },
     {"nativeCreateImagePlanes",
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 0a5490d..2e419a6 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -375,7 +375,8 @@
 }
 
 static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
-        jint maxImages, jint userFormat, jint userWidth, jint userHeight) {
+        jint maxImages, jint userWidth, jint userHeight, jboolean useSurfaceImageFormatInfo,
+        jint hardwareBufferFormat, jlong dataSpace, jlong ndkUsage) {
     status_t res;
 
     ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
@@ -450,7 +451,7 @@
 
     // Query surface format if no valid user format is specified, otherwise, override surface format
     // with user format.
-    if (userFormat == IMAGE_FORMAT_UNKNOWN) {
+    if (useSurfaceImageFormatInfo) {
         if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) {
             ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
             jniThrowRuntimeException(env, "Failed to query Surface format");
@@ -458,13 +459,13 @@
         }
     } else {
         // Set consumer buffer format to user specified format
-        PublicFormat publicFormat = static_cast<PublicFormat>(userFormat);
-        int nativeFormat = mapPublicFormatToHalFormat(publicFormat);
-        android_dataspace nativeDataspace = mapPublicFormatToHalDataspace(publicFormat);
-        res = native_window_set_buffers_format(anw.get(), nativeFormat);
+        android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace);
+        int userFormat = static_cast<int>(mapHalFormatDataspaceToPublicFormat(
+            hardwareBufferFormat, nativeDataspace));
+        res = native_window_set_buffers_format(anw.get(), hardwareBufferFormat);
         if (res != OK) {
             ALOGE("%s: Unable to configure consumer native buffer format to %#x",
-                    __FUNCTION__, nativeFormat);
+                    __FUNCTION__, hardwareBufferFormat);
             jniThrowRuntimeException(env, "Failed to set Surface format");
             return 0;
         }
@@ -484,15 +485,13 @@
     env->SetIntField(thiz,
             gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
 
-    if (!isFormatOpaque(surfaceFormat)) {
-        res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
-        if (res != OK) {
-            ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
-                  __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN),
-                  surfaceFormat, strerror(-res), res);
-            jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
-            return 0;
-        }
+    res = native_window_set_usage(anw.get(), ndkUsage);
+    if (res != OK) {
+        ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
+              __FUNCTION__, static_cast<unsigned int>(ndkUsage),
+              surfaceFormat, strerror(-res), res);
+        jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
+        return 0;
     }
 
     int minUndequeuedBufferCount = 0;
@@ -1093,7 +1092,7 @@
 
 static JNINativeMethod gImageWriterMethods[] = {
     {"nativeClassInit",         "()V",                        (void*)ImageWriter_classInit },
-    {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;IIII)J",
+    {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;IIIZIJJ)J",
                                                               (void*)ImageWriter_init },
     {"nativeClose",              "(J)V",                      (void*)ImageWriter_close },
     {"nativeAttachAndQueueImage",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 8dcdc98..a548a47 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1454,6 +1454,7 @@
 extern int register_android_media_MediaMuxer(JNIEnv *env);
 extern int register_android_media_MediaRecorder(JNIEnv *env);
 extern int register_android_media_MediaSync(JNIEnv *env);
+extern int register_android_media_PublicFormatUtils(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
 extern int register_android_media_MediaProfiles(JNIEnv *env);
 extern int register_android_mtp_MtpDatabase(JNIEnv *env);
@@ -1501,6 +1502,11 @@
         goto bail;
     }
 
+    if (register_android_media_PublicFormatUtils(env) < 0) {
+        ALOGE("ERROR: PublicFormatUtils native registration failed\n");
+        goto bail;
+    }
+
     if (register_android_media_ResampleInputStream(env) < 0) {
         ALOGE("ERROR: ResampleInputStream native registration failed\n");
         goto bail;
diff --git a/media/jni/android_media_PublicFormatUtils.cpp b/media/jni/android_media_PublicFormatUtils.cpp
new file mode 100644
index 0000000..09ebdee
--- /dev/null
+++ b/media/jni/android_media_PublicFormatUtils.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "PublicFormatUtils_JNI"
+
+#include <utils/misc.h>
+#include <ui/PublicFormat.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <jni.h>
+
+using namespace android;
+
+static jint android_media_PublicFormatUtils_getHalFormat(JNIEnv* /*env*/, jobject /*thiz*/,
+                                                         jint imageFormat) {
+    PublicFormat publicFormat = static_cast<PublicFormat>(imageFormat);
+    int nativeFormat = mapPublicFormatToHalFormat(publicFormat);
+    return static_cast<jint>(nativeFormat);
+}
+
+static jlong android_media_PublicFormatUtils_getHalDataspace(JNIEnv* /*env*/, jobject /*thiz*/,
+                                                             jint imageFormat) {
+    PublicFormat publicFormat = static_cast<PublicFormat>(imageFormat);
+    android_dataspace
+        nativeDataspace = mapPublicFormatToHalDataspace(publicFormat);
+    return static_cast<jlong>(nativeDataspace);
+}
+
+static jint android_media_PublicFormatUtils_getPublicFormat(JNIEnv* /*env*/, jobject /*thiz*/,
+                                                            jint hardwareBufferFormat,
+                                                            jlong dataspace) {
+    PublicFormat nativeFormat = mapHalFormatDataspaceToPublicFormat(
+            hardwareBufferFormat, static_cast<android_dataspace>(dataspace));
+    return static_cast<jint>(nativeFormat);
+}
+
+static const JNINativeMethod gMethods[] = {
+    {"nativeGetHalFormat",    "(I)I", (void*)android_media_PublicFormatUtils_getHalFormat},
+    {"nativeGetHalDataspace", "(I)J", (void*)android_media_PublicFormatUtils_getHalDataspace},
+    {"nativeGetPublicFormat", "(IJ)I",(void*)android_media_PublicFormatUtils_getPublicFormat}
+};
+
+int register_android_media_PublicFormatUtils(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(env,
+             "android/media/PublicFormatUtils", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 8ccc4fb..41f3a678 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -461,6 +461,7 @@
 }
 
 MediaEvent::~MediaEvent() {
+    android::Mutex::Autolock autoLock(mLock);
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->DeleteWeakGlobalRef(mMediaEventObj);
     mMediaEventObj = nullptr;
@@ -977,7 +978,8 @@
 void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jweak listenerRef = env->NewWeakGlobalRef(listener);
-    ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this);
+    ALOGV("addCallbackListener() with listener:%p and ref:%p @%p",
+              listener, listenerRef, this);
     std::scoped_lock<std::mutex> lock(mMutex);
     mListenersMap[jtuner] = listenerRef;
 }
@@ -1044,7 +1046,7 @@
             } else {
                 env->CallVoidMethod(
                         frontend,
-                        env->GetMethodID(clazz, "onUnLocked", "()V"));
+                        env->GetMethodID(clazz, "onUnlocked", "()V"));
             }
             break;
         }
@@ -1344,18 +1346,43 @@
     return (int)Result::SUCCESS;
 }
 
+int JTuner::unshareFrontend() {
+    if (mFeClient != nullptr) {
+        ALOGE("Cannot unshare frontend because this session is already holding %d"
+              " as an owner instead of as a sharee", mFeClient->getId());
+        return (int)Result::INVALID_STATE;
+    }
+
+    mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
+    return (int)Result::SUCCESS;
+}
+
 void JTuner::registerFeCbListener(JTuner* jtuner) {
+    ALOGV("registerFeCbListener: %p", jtuner);
     if (mFeClientCb != nullptr && jtuner != nullptr) {
         mFeClientCb->addCallbackListener(jtuner, jtuner->getObject());
     }
 }
 
 void JTuner::unregisterFeCbListener(JTuner* jtuner) {
+    ALOGV("unregisterFeCbListener: %p", jtuner);
     if (mFeClientCb != nullptr && jtuner != nullptr) {
         mFeClientCb->removeCallbackListener(jtuner);
     }
 }
 
+void JTuner::updateFrontend(JTuner* jtuner) {
+    if (jtuner == nullptr) {
+        ALOGV("JTuner::updateFrontend(null) called for previous owner: %p", this);
+        mFeClient = nullptr;
+        mFeClientCb = nullptr;
+    } else {
+        ALOGV("JTuner::updateFrontend(%p) called for new owner: %p", jtuner, this);
+        mFeClient = jtuner->mFeClient;
+        mFeClientCb = jtuner->mFeClientCb;
+    }
+}
+
 jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
     jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
     jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
@@ -1595,6 +1622,15 @@
     return mTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
 }
 
+jint JTuner::removeOutputPid(int32_t pid) {
+    if (mFeClient == nullptr) {
+        ALOGE("frontend is not initialized");
+        return (jint)Result::INVALID_STATE;
+    }
+
+    return (jint)mFeClient->removeOutputPid(pid);
+}
+
 jobject JTuner::openLnbByHandle(int handle) {
     if (mTunerClient == nullptr) {
         return nullptr;
@@ -2583,6 +2619,24 @@
                 env->SetObjectField(statusObj, field, valObj);
                 break;
             }
+            case FrontendStatus::Tag::allPlpInfo: {
+                jfieldID field = env->GetFieldID(clazz, "mAllPlpInfo",
+                                                 "[Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;");
+                jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo");
+                jmethodID initPlp = env->GetMethodID(plpClazz, "<init>", "(IZ)V");
+
+                vector<FrontendScanAtsc3PlpInfo> plpInfos =
+                        s.get<FrontendStatus::Tag::allPlpInfo>();
+                jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
+                for (int i = 0; i < plpInfos.size(); i++) {
+                    jobject plpObj = env->NewObject(plpClazz, initPlp, plpInfos[i].plpId,
+                                                    plpInfos[i].bLlsFlag);
+                    env->SetObjectArrayElement(valObj, i, plpObj);
+                }
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
         }
     }
     return statusObj;
@@ -3318,6 +3372,12 @@
     return tuner->shareFrontend(id);
 }
 
+static int android_media_tv_Tuner_unshare_frontend(
+        JNIEnv *env, jobject thiz) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->unshareFrontend();
+}
+
 static void android_media_tv_Tuner_register_fe_cb_listener(
         JNIEnv *env, jobject thiz, jlong shareeJTuner) {
     sp<JTuner> tuner = getTuner(env, thiz);
@@ -3332,6 +3392,17 @@
     tuner->unregisterFeCbListener(jtuner);
 }
 
+static void android_media_tv_Tuner_update_frontend(JNIEnv *env, jobject thiz, jlong jtunerPtr) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    JTuner *jtuner;
+    if (jtunerPtr == 0) {
+        jtuner = nullptr;
+    } else {
+        jtuner = (JTuner *) jtunerPtr;
+    }
+    tuner->updateFrontend(jtuner);
+}
+
 static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
     sp<JTuner> tuner = getTuner(env, thiz);
     FrontendSettings setting = getFrontendSettings(env, type, settings);
@@ -3515,11 +3586,14 @@
     bool isCheckCrc = env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z"));
     bool isRepeat = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z"));
     bool isRaw = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"));
+    int32_t bitWidthOfLengthField =
+            env->GetIntField(settings, env->GetFieldID(clazz, "mBitWidthOfLengthField", "I"));
 
     DemuxFilterSectionSettings filterSectionSettings {
         .isCheckCrc = isCheckCrc,
         .isRepeat = isRepeat,
         .isRaw = isRaw,
+        .bitWidthOfLengthField = bitWidthOfLengthField,
     };
     if (env->IsInstanceOf(
             settings,
@@ -4310,6 +4384,11 @@
     return tuner->getMaxNumberOfFrontends(type);
 }
 
+static jint android_media_tv_Tuner_remove_output_pid(JNIEnv *env, jobject thiz, jint pid) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->removeOutputPid(pid);
+}
+
 static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->closeFrontend();
@@ -4570,10 +4649,14 @@
             (void *)android_media_tv_Tuner_open_frontend_by_handle },
     { "nativeShareFrontend", "(I)I",
             (void *)android_media_tv_Tuner_share_frontend },
+    { "nativeUnshareFrontend", "()I",
+            (void *)android_media_tv_Tuner_unshare_frontend },
     { "nativeRegisterFeCbListener", "(J)V",
             (void*)android_media_tv_Tuner_register_fe_cb_listener },
     { "nativeUnregisterFeCbListener", "(J)V",
             (void*)android_media_tv_Tuner_unregister_fe_cb_listener },
+    { "nativeUpdateFrontend", "(J)V",
+            (void*)android_media_tv_Tuner_update_frontend },
     { "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
             (void *)android_media_tv_Tuner_tune },
     { "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -4625,6 +4708,8 @@
              (void *)android_media_tv_Tuner_set_maximum_frontends },
     { "nativeGetMaxNumberOfFrontends", "(I)I",
             (void *)android_media_tv_Tuner_get_maximum_frontends },
+    { "nativeRemoveOutputPid", "(I)I",
+            (void *)android_media_tv_Tuner_remove_output_pid },
 };
 
 static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index f1b31e3..e9475dc 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -178,8 +178,10 @@
     jobject getFrontendIds();
     jobject openFrontendByHandle(int feHandle);
     int shareFrontend(int feId);
+    int unshareFrontend();
     void registerFeCbListener(JTuner* jtuner);
     void unregisterFeCbListener(JTuner* jtuner);
+    void updateFrontend(JTuner* jtuner);
     jint closeFrontendById(int id);
     jobject getFrontendInfo(int id);
     int tune(const FrontendSettings& settings);
@@ -203,6 +205,7 @@
     Result getFrontendHardwareInfo(string& info);
     jint setMaxNumberOfFrontends(int32_t frontendType, int32_t maxNumber);
     int32_t getMaxNumberOfFrontends(int32_t frontendType);
+    jint removeOutputPid(int32_t pid);
 
     jweak getObject();
 
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 773cdc9..50bb79c 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -275,118 +275,104 @@
         float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
         std::vector<std::any>& garbage)
 {
-    // oldTrack and newTrack are placeholders to be released by garbage without the lock.
-    sp<AudioTrack> oldTrack;
-    sp<AudioTrack> newTrack;
-    status_t status = NO_ERROR;
+    ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
+            " priority=%d, loop=%d, rate=%f)",
+            __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
+            priority, loop, rate);
 
-    {
-        ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
-                " priority=%d, loop=%d, rate=%f)",
-                __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
-                priority, loop, rate);
+    // initialize track
+    const audio_stream_type_t streamType =
+            AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
+    const int32_t channelCount = sound->getChannelCount();
+    const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
+    size_t frameCount = 0;
 
-        // initialize track
-        const audio_stream_type_t streamType =
-                AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
-        const int32_t channelCount = sound->getChannelCount();
-        const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
-        size_t frameCount = 0;
+    if (loop) {
+        const audio_format_t format = sound->getFormat();
+        const size_t frameSize = audio_is_linear_pcm(format)
+                ? channelCount * audio_bytes_per_sample(format) : 1;
+        frameCount = sound->getSizeInBytes() / frameSize;
+    }
 
-        if (loop) {
-            const audio_format_t format = sound->getFormat();
-            const size_t frameSize = audio_is_linear_pcm(format)
-                    ? channelCount * audio_bytes_per_sample(format) : 1;
-            frameCount = sound->getSizeInBytes() / frameSize;
-        }
-
-        // check if the existing track has the same sound id.
-        if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
+    if (mAudioTrack != nullptr) {
+        if (mSoundID == sound->getSoundID()
+                && mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+            // Reuse the old track if the soundID matches.
             // the sample rate may fail to change if the audio track is a fast track.
-            if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
-                newTrack = mAudioTrack;
-                ALOGV("%s: reusing track %p for sound %d",
-                        __func__, mAudioTrack.get(), sound->getSoundID());
-            }
-        }
-        if (newTrack == nullptr) {
-            // mToggle toggles each time a track is started on a given stream.
-            // The toggle is concatenated with the Stream address and passed to AudioTrack
-            // as callback user data. This enables the detection of callbacks received from the old
-            // audio track while the new one is being started and avoids processing them with
-            // wrong audio audio buffer size  (mAudioBufferSize)
-            auto toggle = mToggle ^ 1;
-            // NOLINTNEXTLINE(performance-no-int-to-ptr)
-            void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
-            audio_channel_mask_t soundChannelMask = sound->getChannelMask();
-            // When sound contains a valid channel mask, use it as is.
-            // Otherwise, use stream count to calculate channel mask.
-            audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
-                    ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
-
-            // do not create a new audio track if current track is compatible with sound parameters
-
-            android::content::AttributionSourceState attributionSource;
-            attributionSource.packageName = mStreamManager->getOpPackageName();
-            attributionSource.token = sp<BBinder>::make();
-            // TODO b/182469354 make consistent with AudioRecord, add util for native source
-            newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
-                    channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
-                    staticCallback, userData,
-                    0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
-                    AudioTrack::TRANSFER_DEFAULT,
-                    nullptr /*offloadInfo*/, attributionSource,
-                    mStreamManager->getAttributes(),
-                    false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
-            // Set caller name so it can be logged in destructor.
-            // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
-            newTrack->setCallerName("soundpool");
-            oldTrack = mAudioTrack;
-            status = newTrack->initCheck();
-            if (status != NO_ERROR) {
-                ALOGE("%s: error creating AudioTrack", __func__);
-                // newTrack goes out of scope, so reference count drops to zero
-                goto exit;
-            }
-            // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
-            mToggle = toggle;
-            mAudioTrack = newTrack;
-            ALOGV("%s: using new track %p for sound %d",
-                    __func__, newTrack.get(), sound->getSoundID());
-        }
-        if (mMuted) {
-            newTrack->setVolume(0.0f, 0.0f);
+            ALOGV("%s: reusing track %p for sound %d",
+                    __func__, mAudioTrack.get(), sound->getSoundID());
         } else {
-            newTrack->setVolume(leftVolume, rightVolume);
+            // If reuse not possible, move mAudioTrack to garbage, set to nullptr.
+            garbage.emplace_back(std::move(mAudioTrack));
+            mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
         }
-        newTrack->setLoop(0, frameCount, loop);
-        mAudioTrack->start();
-        mSound = sound;
-        mSoundID = sound->getSoundID();
-        mPriority = priority;
-        mLoop = loop;
-        mLeftVolume = leftVolume;
-        mRightVolume = rightVolume;
-        mRate = rate;
-        mState = PLAYING;
-        mStopTimeNs = 0;
-        mStreamID = nextStreamID;  // prefer this to be the last, as it is an atomic sync point
     }
+    if (mAudioTrack == nullptr) {
+        // mToggle toggles each time a track is started on a given stream.
+        // The toggle is concatenated with the Stream address and passed to AudioTrack
+        // as callback user data. This enables the detection of callbacks received from the old
+        // audio track while the new one is being started and avoids processing them with
+        // wrong audio audio buffer size  (mAudioBufferSize)
+        auto toggle = mToggle ^ 1;
+        // NOLINTNEXTLINE(performance-no-int-to-ptr)
+        void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
+        audio_channel_mask_t soundChannelMask = sound->getChannelMask();
+        // When sound contains a valid channel mask, use it as is.
+        // Otherwise, use stream count to calculate channel mask.
+        audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
+                ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
 
-exit:
-    ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
-    if (status != NO_ERROR) {
-        // TODO: should we consider keeping the soundID if the old track is OK?
-        // Do not attempt to restart this track (should we remove the stream id?)
-        mState = IDLE;
-        mSoundID = 0;
-        mSound.reset();
-        mAudioTrack.clear();  // actual release from garbage
+        // do not create a new audio track if current track is compatible with sound parameters
+
+        android::content::AttributionSourceState attributionSource;
+        attributionSource.packageName = mStreamManager->getOpPackageName();
+        attributionSource.token = sp<BBinder>::make();
+        // TODO b/182469354 make consistent with AudioRecord, add util for native source
+        mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
+                channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+                staticCallback, userData,
+                0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
+                AudioTrack::TRANSFER_DEFAULT,
+                nullptr /*offloadInfo*/, attributionSource,
+                mStreamManager->getAttributes(),
+                false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
+        // Set caller name so it can be logged in destructor.
+        // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
+        mAudioTrack->setCallerName("soundpool");
+
+        if (status_t status = mAudioTrack->initCheck();
+            status != NO_ERROR) {
+            ALOGE("%s: error %d creating AudioTrack", __func__, status);
+            // TODO: should we consider keeping the soundID and reusing the old track?
+            mState = IDLE;
+            mSoundID = 0;
+            mSound.reset();
+            garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack.
+            mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
+            return;
+        }
+        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+        mToggle = toggle;
+        ALOGV("%s: using new track %p for sound %d",
+                __func__, mAudioTrack.get(), sound->getSoundID());
     }
-
-    // move tracks to garbage to be released later outside of lock.
-    if (newTrack) garbage.emplace_back(std::move(newTrack));
-    if (oldTrack) garbage.emplace_back(std::move(oldTrack));
+    if (mMuted) {
+        mAudioTrack->setVolume(0.f, 0.f);
+    } else {
+        mAudioTrack->setVolume(leftVolume, rightVolume);
+    }
+    mAudioTrack->setLoop(0, frameCount, loop);
+    mAudioTrack->start();
+    mSound = sound;
+    mSoundID = sound->getSoundID();
+    mPriority = priority;
+    mLoop = loop;
+    mLeftVolume = leftVolume;
+    mRightVolume = rightVolume;
+    mRate = rate;
+    mState = PLAYING;
+    mStopTimeNs = 0;
+    mStreamID = nextStreamID;  // prefer this to be the last, as it is an atomic sync point
 }
 
 /* static */
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 8568383..959e756 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -43,6 +43,7 @@
 }
 
 FilterClient::~FilterClient() {
+    Mutex::Autolock _l(mLock);
     mTunerFilter = nullptr;
     mAvSharedHandle = nullptr;
     mAvSharedMemSize = 0;
@@ -74,6 +75,7 @@
     Result res;
     checkIsPassthroughFilter(configure);
 
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configure(configure);
         res = ClientHelper::getServiceSpecificErrorCode(s);
@@ -87,6 +89,7 @@
 }
 
 Result FilterClient::configureMonitorEvent(int32_t monitorEventType) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -96,6 +99,7 @@
 }
 
 Result FilterClient::configureIpFilterContextId(int32_t cid) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configureIpFilterContextId(cid);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -105,6 +109,7 @@
 }
 
 Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configureAvStreamType(avStreamType);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -114,6 +119,7 @@
 }
 
 Result FilterClient::start() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->start();
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -123,6 +129,7 @@
 }
 
 Result FilterClient::stop() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->stop();
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -132,6 +139,7 @@
 }
 
 Result FilterClient::flush() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->flush();
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -141,6 +149,7 @@
 }
 
 Result FilterClient::getId(int32_t& id) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->getId(&id);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -150,6 +159,7 @@
 }
 
 Result FilterClient::getId64Bit(int64_t& id) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->getId64Bit(&id);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -159,6 +169,7 @@
 }
 
 Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->releaseAvHandle(dupToAidl(handle), avDataId);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -168,6 +179,7 @@
 }
 
 Result FilterClient::setDataSource(sp<FilterClient> filterClient){
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -177,6 +189,7 @@
 }
 
 Result FilterClient::close() {
+    Mutex::Autolock _l(mLock);
     if (mFilterMQEventFlag != nullptr) {
         EventFlag::deleteEventFlag(&mFilterMQEventFlag);
         mFilterMQEventFlag = nullptr;
@@ -197,6 +210,7 @@
 }
 
 string FilterClient::acquireSharedFilterToken() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         string filterToken;
         if (mTunerFilter->acquireSharedFilterToken(&filterToken).isOk()) {
@@ -208,6 +222,7 @@
 }
 
 Result FilterClient::freeSharedFilterToken(const string& filterToken) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->freeSharedFilterToken(filterToken);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -237,6 +252,7 @@
 }
 
 Result FilterClient::getFilterMq() {
+    Mutex::Autolock _l(mLock);
     if (mFilterMQ != nullptr) {
         return Result::SUCCESS;
     }
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 20e5610..9e9b233 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -21,6 +21,7 @@
 #include <aidl/android/media/tv/tuner/BnTunerFilterCallback.h>
 #include <aidl/android/media/tv/tuner/ITunerFilter.h>
 #include <fmq/AidlMessageQueue.h>
+#include <utils/Mutex.h>
 
 #include "ClientHelper.h"
 #include "FilterClientCallback.h"
@@ -37,6 +38,7 @@
 using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
 using ::aidl::android::media::tv::tuner::ITunerFilter;
 using ::android::hardware::EventFlag;
+using ::android::Mutex;
 
 using namespace std;
 
@@ -179,6 +181,7 @@
     uint64_t mAvSharedMemSize;
     bool mIsMediaFilter;
     bool mIsPassthroughFilter;
+    Mutex mLock;
 };
 }  // namespace android
 
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 0fdd8d8..bea0342 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -143,6 +143,15 @@
     return Result::INVALID_STATE;
 }
 
+Result FrontendClient::removeOutputPid(int32_t pid) {
+    if (mTunerFrontend != nullptr) {
+        Status s = mTunerFrontend->removeOutputPid(pid);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
+
+    return Result::INVALID_STATE;
+}
+
 shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
     return mTunerFrontend;
 }
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 77d9098..c6838c8 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -120,6 +120,11 @@
      */
     Result getHardwareInfo(string& info);
 
+    /**
+     * Filter out unnecessary PID from frontend output.
+     */
+    Result removeOutputPid(int32_t pid);
+
     int32_t getId();
 
     shared_ptr<ITunerFrontend> getAidlFrontend();
diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp
index 8a573fb..1452488 100644
--- a/media/native/midi/MidiDeviceInfo.cpp
+++ b/media/native/midi/MidiDeviceInfo.cpp
@@ -64,6 +64,7 @@
     RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
     RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
     RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
+    RETURN_IF_FAILED(parcel->writeInt32(mDefaultProtocol));
     RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
     // This corresponds to "extra" properties written by Java code
     RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
@@ -83,6 +84,7 @@
     int32_t isPrivate;
     RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
     mIsPrivate = isPrivate == 1;
+    RETURN_IF_FAILED(parcel->readInt32(&mDefaultProtocol));
     RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
     // Ignore "extra" properties as they may contain Java Parcelables
     return OK;
@@ -130,7 +132,8 @@
             areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
             areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
             lhs.mProperties == rhs.mProperties &&
-            lhs.mIsPrivate == rhs.mIsPrivate);
+            lhs.mIsPrivate == rhs.mIsPrivate &&
+            lhs.mDefaultProtocol == rhs.mDefaultProtocol);
 }
 
 }  // namespace midi
diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h
index 5b4a241..23e1cb4 100644
--- a/media/native/midi/MidiDeviceInfo.h
+++ b/media/native/midi/MidiDeviceInfo.h
@@ -38,6 +38,7 @@
     int getType() const { return mType; }
     int getUid() const { return mId; }
     bool isPrivate() const { return mIsPrivate; }
+    int getDefaultProtocol() const { return mDefaultProtocol; }
     const Vector<String16>& getInputPortNames() const { return mInputPortNames; }
     const Vector<String16>&  getOutputPortNames() const { return mOutputPortNames; }
     String16 getProperty(const char* propertyName);
@@ -48,6 +49,18 @@
         TYPE_VIRTUAL = 2,
         TYPE_BLUETOOTH = 3,
     };
+
+    enum {
+        PROTOCOL_UMP_USE_MIDI_CI = 0,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4,
+        PROTOCOL_UMP_MIDI_2_0 = 17,
+        PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18,
+        PROTOCOL_UNKNOWN = -1,
+    };
+
     static const char* const PROPERTY_NAME;
     static const char* const PROPERTY_MANUFACTURER;
     static const char* const PROPERTY_PRODUCT;
@@ -72,6 +85,7 @@
     Vector<String16> mOutputPortNames;
     os::PersistableBundle mProperties;
     bool mIsPrivate;
+    int32_t mDefaultProtocol;
 };
 
 }  // namespace midi
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index f90796e..aa076e8 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -138,6 +138,7 @@
     outDeviceInfoPtr->type = deviceInfo.getType();
     outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
     outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
+    outDeviceInfoPtr->defaultProtocol = deviceInfo.getDefaultProtocol();
 
     return AMEDIA_OK;
 }
@@ -238,6 +239,13 @@
     return device->deviceInfo.outputPortCount;
 }
 
+AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device) {
+    if (device == nullptr) {
+        return AMIDI_DEVICE_PROTOCOL_UNKNOWN;
+    }
+    return static_cast<AMidiDevice_Protocol>(device->deviceInfo.defaultProtocol);
+}
+
 /*
  * Port Helpers
  */
diff --git a/media/native/midi/amidi_internal.h b/media/native/midi/amidi_internal.h
index fce8596..023a6f5 100644
--- a/media/native/midi/amidi_internal.h
+++ b/media/native/midi/amidi_internal.h
@@ -25,6 +25,7 @@
     int32_t type;            /* one of AMIDI_DEVICE_TYPE_* constants */
     int32_t inputPortCount;  /* number of input (send) ports associated with the device */
     int32_t outputPortCount; /* number of output (received) ports associated with the device */
+    int32_t defaultProtocol; /* one of the AMIDI_DEVICE_PROTOCOL_* constants */
 } AMidiDeviceInfo;
 
 struct AMidiDevice {
diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h
index 742db34..fbb7fb3 100644
--- a/media/native/midi/include/amidi/AMidi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -62,6 +62,78 @@
 };
 
 /*
+ * Protocol IDs for various MIDI devices.
+ *
+ * Introduced in API 33.
+ */
+enum AMidiDevice_Protocol : int32_t {
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use UMP to negotiate with the device with MIDI-CI.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_USE_MIDI_CI = 0,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0 = 17,
+
+     /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18,
+
+    /**
+     * Constant representing a device with an unknown default protocol.
+     * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     */
+    AMIDI_DEVICE_PROTOCOL_UNKNOWN = -1
+};
+
+/*
  * Device API
  */
 /**
@@ -134,6 +206,30 @@
  */
 ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
+/**
+ * Gets the MIDI device default protocol.
+ *
+ * @param device Specifies the MIDI device.
+ *
+ * @return The identifier of the MIDI device default protocol:
+ * AMIDI_DEVICE_PROTOCOL_UMP_USE_MIDI_CI
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UNKNOWN
+ *
+ * Most devices should return PROTOCOL_UNKNOWN (-1). This is intentional as devices
+ * with default UMP support are not backwards compatible. When the device is null,
+ * return AMIDI_DEVICE_PROTOCOL_UNKNOWN.
+ *
+ * Available since API 33.
+ */
+AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device)
+        __INTRODUCED_IN(33);
+
 /*
  * API for receiving data from the Output port of a device.
  */
diff --git a/media/native/midi/libamidi.map.txt b/media/native/midi/libamidi.map.txt
index 62627f8..f25f977 100644
--- a/media/native/midi/libamidi.map.txt
+++ b/media/native/midi/libamidi.map.txt
@@ -2,6 +2,7 @@
   global:
     AMidiDevice_fromJava;
     AMidiDevice_release;
+    AMidiDevice_getDefaultProtocol; # introduced=Tiramisu
     AMidiDevice_getType;
     AMidiDevice_getNumInputPorts;
     AMidiDevice_getNumOutputPorts;
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 03606ba..9039011 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -20,7 +20,7 @@
         xmlns:tools="http://schemas.android.com/tools"
         package="com.android.bluetoothmidiservice"
         >
-    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
 
     <uses-feature android:name="android.hardware.bluetooth_le"
          android:required="true"/>
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index bfb0546..5a900c7 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -19,7 +19,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.bluetoothmidiservice"
           >
-    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
     <application
         android:label="BluetoothMidi"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 62c313a..4c3b689 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -24,6 +24,7 @@
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.media.midi.MidiDevice;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
 import android.media.midi.MidiDeviceStatus;
@@ -63,6 +64,7 @@
             "00002902-0000-1000-8000-00805f9b34fb");
 
     private final BluetoothDevice mBluetoothDevice;
+    private final Context mContext;
     private final BluetoothMidiService mService;
     private final MidiManager mMidiManager;
     private MidiReceiver mOutputReceiver;
@@ -136,6 +138,8 @@
                         // switch to receiving notifications
                         mBluetoothGatt.readCharacteristic(characteristic);
                     }
+
+                    openBluetoothDevice(mBluetoothDevice);
                 }
             } else {
                 Log.e(TAG, "onServicesDiscovered received: " + status);
@@ -249,6 +253,7 @@
 
         mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);
 
+        mContext = context;
         mMidiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
 
         Bundle properties = new Bundle();
@@ -260,7 +265,8 @@
         inputPortReceivers[0] = mEventScheduler.getReceiver();
 
         mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
-                null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
+                null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH,
+                MidiDeviceInfo.PROTOCOL_UNKNOWN, mDeviceServerCallback);
 
         mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
 
@@ -309,6 +315,18 @@
         }
     }
 
+    void openBluetoothDevice(BluetoothDevice btDevice) {
+        Log.d(TAG, "openBluetoothDevice() device: " + btDevice);
+
+        MidiManager midiManager = mContext.getSystemService(MidiManager.class);
+        midiManager.openBluetoothDevice(btDevice,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice device) {
+                    }
+                }, null);
+    }
+
     public IBinder getBinder() {
         return mDeviceServer.asBinder();
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
new file mode 100644
index 0000000..fd66d3b
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothProfile;
+import android.media.BtProfileConnectionInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BtProfileConnectionInfoTest {
+
+    @Test
+    public void testCoverageA2dp() {
+        final boolean supprNoisy = false;
+        final int volume = 42;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume);
+        assertEquals(info.getProfile(), BluetoothProfile.A2DP);
+        assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+        assertEquals(info.getVolume(), volume);
+    }
+
+    @Test
+    public void testCoverageA2dpSink() {
+        final int volume = 42;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume);
+        assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK);
+        assertEquals(info.getVolume(), volume);
+    }
+
+    @Test
+    public void testCoveragehearingAid() {
+        final boolean supprNoisy = true;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy);
+        assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID);
+        assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+    }
+
+    @Test
+    public void testCoverageLeAudio() {
+        final boolean supprNoisy = false;
+        final boolean isLeOutput = true;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy,
+                isLeOutput);
+        assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO);
+        assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+        assertEquals(info.getIsLeOutput(), isLeOutput);
+    }
+}
+
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
index 73ea663..7554889 100644
--- a/media/tests/TunerTest/OWNERS
+++ b/media/tests/TunerTest/OWNERS
@@ -1,4 +1,4 @@
[email protected]
[email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
\ No newline at end of file
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index 5f18e1a..e22580d 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -71,11 +71,12 @@
         const AChoreographerFrameCallbackData* data, size_t index) {
     return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(data, index);
+    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(data,
+                                                                                         index);
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(data, index);
+    return AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(data, index);
 }
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3c1aa44..35c794e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -34,8 +34,8 @@
     AChoreographerFrameCallbackData_getFrameTimelinesLength;  # introduced=33
     AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineVsyncId;  # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime;  # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineDeadline;  # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos;  # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos;  # introduced=33
     AConfiguration_copy;
     AConfiguration_delete;
     AConfiguration_diff;
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index a6c1b50..32fd734 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -18,6 +18,10 @@
     android_getprocnetwork; # llndk
     android_setprocdns; # llndk
     android_getprocdns; # llndk
+    # These functions have been part of the NDK since API 33.
+    android_tag_socket_with_uid; # llndk
+    android_tag_socket;  # llndk
+    android_untag_socket; # llndk
   local:
     *;
 };
diff --git a/native/android/net.c b/native/android/net.c
index e2f36a7..d7c22e1 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -161,3 +161,15 @@
 void android_res_cancel(int nsend_fd) {
     resNetworkCancel(nsend_fd);
 }
+
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) {
+    return tagSocket(sockfd, tag, uid);
+}
+
+int android_tag_socket(int sockfd, int tag) {
+    return tagSocket(sockfd, tag, -1);
+}
+
+int android_untag_socket(int sockfd) {
+    return untagSocket(sockfd);
+}
diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
new file mode 100644
index 0000000..a1855fd
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/activity_confirmation"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:background="@drawable/dialog_background"
+              android:elevation="16dp"
+              android:maxHeight="400dp"
+              android:orientation="vertical"
+              android:padding="18dp"
+              android:layout_gravity="center">
+
+    <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
+
+    <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:paddingHorizontal="12dp"
+            style="@*android:style/TextAppearance.Widget.Toolbar.Title"/>
+
+    <TextView
+            android:id="@+id/summary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="12dp"
+            android:gravity="center"
+            android:textColor="?android:attr/textColorSecondary"
+            android:textSize="14sp" />
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="end">
+
+        <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+        <Button
+                android:id="@+id/btn_negative"
+                style="@android:style/Widget.Material.Button.Borderless.Colored"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/consent_no"
+                android:textColor="?android:attr/textColorSecondary" />
+
+        <Button
+                android:id="@+id/btn_positive"
+                style="@android:style/Widget.Material.Button.Borderless.Colored"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/consent_yes" />
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 377e0c3..d424359 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,8 +20,8 @@
     <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
-    <string name="summary_watch" product="default" msgid="7113724443198337683">"Jakinarazpenekin interakzioan aritzeko eta telefonoa, SMSak, kontaktuak eta egutegia erabiltzeko baimena izango du <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak."</string>
-    <string name="summary_watch" product="tablet" msgid="7113724443198337683">"Jakinarazpenekin interakzioan aritzeko eta telefonoa, SMSak, kontaktuak eta egutegia erabiltzeko baimena izango du <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak."</string>
+    <string name="summary_watch" product="default" msgid="7113724443198337683">"Jakinarazpenekin interakzioan aritzeko eta telefonoa, SMSak, kontaktuak eta egutegia erabiltzeko baimenak izango ditu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak."</string>
+    <string name="summary_watch" product="tablet" msgid="7113724443198337683">"Jakinarazpenekin interakzioan aritzeko eta telefonoa, SMSak, kontaktuak eta egutegia erabiltzeko baimenak izango ditu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak."</string>
     <string name="title_app_streaming" msgid="4459136600249308574">"Aplikazioak igortzeko baimena eman nahi diozu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari?"</string>
     <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Utzi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; urrunetik atzitzen, telefonoa konektatuta dagoenean bertan instalatuta dauden aplikazioetarako sarbidea izateko."</string>
     <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Utzi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; urrunetik atzitzen, tableta konektatuta dagoenean bertan instalatuta dauden aplikazioetarako sarbidea izateko."</string>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index cb8b616..25ec9606 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -73,4 +73,15 @@
 
     <!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] -->
     <string name="consent_no">Don\u2019t allow</string>
+
+    <!-- ================== System data transfer ==================== -->
+    <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=60] -->
+    <string name="permission_sync_confirmation_title">Transfer app permissions to your
+        watch</string>
+
+    <!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=400] -->
+    <string name="permission_sync_summary">To make it easier to set up your watch,
+        apps installed on your watch during setup will use the same permissions as your phone.\n\n
+        These permissions may include access to your watch\u2019s microphone and location.</string>
+
 </resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
new file mode 100644
index 0000000..67efa03
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.companiondevicemanager;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.Activity;
+import android.companion.SystemDataTransferRequest;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.text.Html;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * This activity manages the UI of companion device data transfer.
+ */
+public class CompanionDeviceDataTransferActivity extends Activity {
+
+    private static final String LOG_TAG = CompanionDeviceDataTransferActivity.class.getSimpleName();
+
+    // UI -> SystemDataTransferProcessor
+    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0;
+    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1;
+    private static final String EXTRA_SYSTEM_DATA_TRANSFER_REQUEST = "system_data_transfer_request";
+    private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER =
+            "system_data_transfer_result_receiver";
+
+    private SystemDataTransferRequest mRequest;
+    private ResultReceiver mCdmServiceReceiver;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Log.i(LOG_TAG, "Creating UI for data transfer confirmation.");
+
+        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
+        setContentView(R.layout.data_transfer_confirmation);
+
+        TextView titleView = findViewById(R.id.title);
+        TextView summaryView = findViewById(R.id.summary);
+        ListView listView = findViewById(R.id.device_list);
+        listView.setVisibility(View.GONE);
+        Button allowButton = findViewById(R.id.btn_positive);
+        Button disallowButton = findViewById(R.id.btn_negative);
+
+        final Intent intent = getIntent();
+        mRequest = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST);
+        mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER);
+
+        requireNonNull(mRequest);
+        requireNonNull(mCdmServiceReceiver);
+
+        if (mRequest.isPermissionSyncAllPackages()
+                || !mRequest.getPermissionSyncPackages().isEmpty()) {
+            titleView.setText(Html.fromHtml(getString(
+                    R.string.permission_sync_confirmation_title), 0));
+            summaryView.setText(getString(R.string.permission_sync_summary));
+            allowButton.setOnClickListener(v -> allow());
+            disallowButton.setOnClickListener(v -> disallow());
+        }
+    }
+
+    private void allow() {
+        Log.i(LOG_TAG, "allow()");
+
+        sendDataToReceiver(RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED);
+
+        setResultAndFinish(RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED);
+    }
+
+    private void disallow() {
+        Log.i(LOG_TAG, "disallow()");
+
+        sendDataToReceiver(RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED);
+
+        setResultAndFinish(RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED);
+    }
+
+    private void sendDataToReceiver(int cdmResultCode) {
+        Bundle data = new Bundle();
+        data.putParcelable(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST, mRequest);
+        mCdmServiceReceiver.send(cdmResultCode, data);
+    }
+
+    private void setResultAndFinish(int cdmResultCode) {
+        setResult(cdmResultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED
+                ? RESULT_OK : RESULT_CANCELED);
+        finish();
+    }
+}
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 38bac1c..223bdcdd 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -25,15 +25,21 @@
     name: "framework-connectivity-netstats-internal-sources",
     srcs: [
         "src/android/app/usage/*.java",
-        "src/android/net/DataUsage*.*",
-        "src/android/net/INetworkStats*.*",
-        "src/android/net/NetworkIdentity*.java",
+        "src/android/net/DataUsageRequest.*",
+        "src/android/net/INetworkStatsService.aidl",
+        "src/android/net/INetworkStatsSession.aidl",
+        "src/android/net/NetworkIdentity.java",
+        "src/android/net/NetworkIdentitySet.java",
         "src/android/net/NetworkStateSnapshot.*",
-        "src/android/net/NetworkStats*.*",
+        "src/android/net/NetworkStats.*",
+        "src/android/net/NetworkStatsAccess.*",
+        "src/android/net/NetworkStatsCollection.*",
+        "src/android/net/NetworkStatsHistory.*",
         "src/android/net/NetworkTemplate.*",
         "src/android/net/TrafficStats.java",
         "src/android/net/UnderlyingNetworkInfo.*",
         "src/android/net/netstats/**/*.*",
+        "src/com/android/server/NetworkManagementSocketTagger.java",
     ],
     path: "src",
     visibility: [
@@ -123,6 +129,11 @@
         "src/android/net/EthernetNetworkSpecifier.java",
         "src/android/net/IEthernetManager.aidl",
         "src/android/net/IEthernetServiceListener.aidl",
+        "src/android/net/IInternalNetworkManagementListener.aidl",
+        "src/android/net/InternalNetworkUpdateRequest.java",
+        "src/android/net/InternalNetworkUpdateRequest.aidl",
+        "src/android/net/InternalNetworkManagementException.java",
+        "src/android/net/InternalNetworkManagementException.aidl",
         "src/android/net/ITetheredInterfaceCallback.aidl",
     ],
     path: "src",
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
index f684a4d..2b6570a 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
@@ -545,9 +545,18 @@
     }
 
     /**
+     * Collects tagged summary results and sets summary enumeration mode.
+     * @throws RemoteException
+     */
+    void startTaggedSummaryEnumeration() throws RemoteException {
+        mSummary = mSession.getTaggedSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp);
+        mEnumerationIndex = 0;
+    }
+
+    /**
      * Collects history results for uid and resets history enumeration index.
      */
-    void startHistoryEnumeration(int uid, int tag, int state) {
+    void startHistoryUidEnumeration(int uid, int tag, int state) {
         mHistory = null;
         try {
             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
@@ -562,6 +571,20 @@
     }
 
     /**
+     * Collects history results for network and resets history enumeration index.
+     */
+    void startHistoryDeviceEnumeration() {
+        try {
+            mHistory = mSession.getHistoryIntervalForNetwork(
+                    mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+            mHistory = null;
+        }
+        mEnumerationIndex = 0;
+    }
+
+    /**
      * Starts uid enumeration for current user.
      * @throws RemoteException
      */
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index a316b8a..8813f98 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -17,7 +17,10 @@
 package android.app.usage;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -123,6 +126,19 @@
     private final Context mContext;
     private final INetworkStatsService mService;
 
+    /**
+     * Type constants for reading different types of Data Usage.
+     * @hide
+     */
+    // @SystemApi(client = MODULE_LIBRARIES)
+    public static final String PREFIX_DEV = "dev";
+    /** @hide */
+    public static final String PREFIX_XT = "xt";
+    /** @hide */
+    public static final String PREFIX_UID = "uid";
+    /** @hide */
+    public static final String PREFIX_UID_TAG = "uid_tag";
+
     /** @hide */
     public static final int FLAG_POLL_ON_OPEN = 1 << 0;
     /** @hide */
@@ -142,6 +158,25 @@
     }
 
     /** @hide */
+    public INetworkStatsService getBinder() {
+        return mService;
+    }
+
+    /**
+     * Set poll on open flag to indicate the poll is needed before service gets statistics
+     * result. This is default enabled. However, for any non-privileged caller, the poll might
+     * be omitted in case of rate limiting.
+     *
+     * @param pollOnOpen true if poll is needed.
+     * @hide
+     */
+    // The system will ignore any non-default values for non-privileged
+    // processes, so processes that don't hold the appropriate permissions
+    // can make no use of this API.
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
     public void setPollOnOpen(boolean pollOnOpen) {
         if (pollOnOpen) {
             mFlags |= FLAG_POLL_ON_OPEN;
@@ -195,9 +230,10 @@
      */
     @NonNull
     @WorkerThread
-    // @SystemApi(client = MODULE_LIBRARIES)
+    @SystemApi(client = MODULE_LIBRARIES)
     public Bucket querySummaryForDevice(@NonNull NetworkTemplate template,
             long startTime, long endTime) {
+        Objects.requireNonNull(template);
         try {
             NetworkStats stats =
                     new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
@@ -368,11 +404,12 @@
      * @return Statistics which is described above.
      * @hide
      */
-    @Nullable
-    // @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    @SystemApi(client = MODULE_LIBRARIES)
     @WorkerThread
     public NetworkStats querySummary(@NonNull NetworkTemplate template, long startTime,
             long endTime) throws SecurityException {
+        Objects.requireNonNull(template);
         try {
             NetworkStats result =
                     new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
@@ -385,6 +422,77 @@
     }
 
     /**
+     * Query tagged network usage statistics summaries.
+     *
+     * The results will only include tagged traffic made by UIDs belonging to the calling user
+     * profile. The results are aggregated over time, so that all buckets will have the same
+     * start and end timestamps as the passed arguments. Not aggregated over state, uid,
+     * default network, metered, or roaming.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *            {@link System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *            {@link System#currentTimeMillis}.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats queryTaggedSummary(@NonNull NetworkTemplate template, long startTime,
+            long endTime) throws SecurityException {
+        Objects.requireNonNull(template);
+        try {
+            NetworkStats result =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startTaggedSummaryEnumeration();
+            return result;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null; // To make the compiler happy.
+    }
+
+    /**
+     * Query usage statistics details for networks matching a given {@link NetworkTemplate}.
+     *
+     * Result is not aggregated over time. This means buckets' start and
+     * end timestamps will be between 'startTime' and 'endTime' parameters.
+     * <p>Only includes buckets whose entire time period is included between
+     * startTime and endTime. Doesn't interpolate or return partial buckets.
+     * Since bucket length is in the order of hours, this
+     * method cannot be used to measure data usage on a fine grained time scale.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *                  {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *                {@link java.lang.System#currentTimeMillis}.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats queryDetailsForDevice(@NonNull NetworkTemplate template,
+            long startTime, long endTime) {
+        Objects.requireNonNull(template);
+        try {
+            final NetworkStats result =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startHistoryDeviceEnumeration();
+            return result;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+
+        return null; // To make the compiler happy.
+    }
+
+    /**
      * Query network usage statistics details for a given uid.
      * This may take a long time, and apps should avoid calling this on their main thread.
      *
@@ -450,7 +558,8 @@
      * @param endTime End of period. Defined in terms of "Unix time", see
      *            {@link java.lang.System#currentTimeMillis}.
      * @param uid UID of app
-     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags.
+     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+     *            across all the tags.
      * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
      *            traffic from all states.
      * @return Statistics object or null if an error happened during statistics collection.
@@ -465,21 +574,52 @@
         return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state);
     }
 
-    /** @hide */
-    public NetworkStats queryDetailsForUidTagState(NetworkTemplate template,
+    /**
+     * Query network usage statistics details for a given template, uid, tag, and state.
+     *
+     * Only usable for uids belonging to calling user. Result is not aggregated over time.
+     * This means buckets' start and end timestamps are going to be between 'startTime' and
+     * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+     * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
+     * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+     * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
+     * interpolate across partial buckets. Since bucket length is in the order of hours, this
+     * method cannot be used to measure data usage on a fine grained time scale.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *                  {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *                {@link java.lang.System#currentTimeMillis}.
+     * @param uid UID of app
+     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+     *            across all the tags.
+     * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
+     *            traffic from all states.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats queryDetailsForUidTagState(@NonNull NetworkTemplate template,
             long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
-
-        NetworkStats result;
+        Objects.requireNonNull(template);
         try {
-            result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
-            result.startHistoryEnumeration(uid, tag, state);
+            final NetworkStats result = new NetworkStats(
+                    mContext, template, mFlags, startTime, endTime, mService);
+            result.startHistoryUidEnumeration(uid, tag, state);
+            return result;
         } catch (RemoteException e) {
             Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
                     + " state=" + state, e);
-            return null;
+            e.rethrowFromSystemServer();
         }
 
-        return result;
+        return null; // To make the compiler happy.
     }
 
     /**
@@ -535,6 +675,54 @@
         return result;
     }
 
+    /**
+     * Query realtime mobile network usage statistics.
+     *
+     * Return a snapshot of current UID network statistics, as it applies
+     * to the mobile radios of the device. The snapshot will include any
+     * tethering traffic, video calling data usage and count of
+     * network operations set by {@link TrafficStats#incrementOperationCount}
+     * made over a mobile radio.
+     * The snapshot will not include any statistics that cannot be seen by
+     * the kernel, e.g. statistics reported by {@link NetworkStatsProvider}s.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    @NonNull public android.net.NetworkStats getMobileUidStats() {
+        try {
+            return mService.getUidStatsForTransport(TRANSPORT_CELLULAR);
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when get Mobile uid stats");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Query realtime Wi-Fi network usage statistics.
+     *
+     * Return a snapshot of current UID network statistics, as it applies
+     * to the Wi-Fi radios of the device. The snapshot will include any
+     * tethering traffic, video calling data usage and count of
+     * network operations set by {@link TrafficStats#incrementOperationCount}
+     * made over a Wi-Fi radio.
+     * The snapshot will not include any statistics that cannot be seen by
+     * the kernel, e.g. statistics reported by {@link NetworkStatsProvider}s.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    @NonNull public android.net.NetworkStats getWifiUidStats() {
+        try {
+            return mService.getUidStatsForTransport(TRANSPORT_WIFI);
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when get WiFi uid stats");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public void registerUsageCallback(NetworkTemplate template, int networkType,
             long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
@@ -804,4 +992,83 @@
             return msg.getData().getParcelable(key);
         }
     }
+
+    /**
+     * Mark given UID as being in foreground for stats purposes.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setUidForeground(int uid, boolean uidForeground) {
+        try {
+            mService.setUidForeground(uid, uidForeground);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set default value of global alert bytes, the value will be clamped to [128kB, 2MB].
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            Manifest.permission.NETWORK_STACK})
+    public void setDefaultGlobalAlert(long alertBytes) {
+        try {
+            // TODO: Sync internal naming with the API surface.
+            mService.advisePersistThreshold(alertBytes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Force update of statistics.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void forceUpdate() {
+        try {
+            mService.forceUpdate();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     *
+     * Asynchronicity notes : because traffic may be happening on the device at the same time, it
+     * doesn't make sense to wait for the warning and limit to be set – a caller still wouldn't
+     * know when exactly it was effective. All that can matter is that it's done quickly. Also,
+     * this method can't fail, so there is no status to return. All providers will see the new
+     * values soon.
+     * As such, this method returns immediately and sends the warning and limit to all providers
+     * as soon as possible through a one-way binder call.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+            long limit) {
+        try {
+            mService.setStatsProviderWarningAndLimitAsync(iface, warning, limit);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
index 62c5761..925d12b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
@@ -23,8 +23,6 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Objects;
 
 /**
@@ -47,7 +45,9 @@
      * @param interfaceName Name of the ethernet interface the specifier refers to.
      */
     public EthernetNetworkSpecifier(@NonNull String interfaceName) {
-        Preconditions.checkStringNotEmpty(interfaceName);
+        if (TextUtils.isEmpty(interfaceName)) {
+            throw new IllegalArgumentException();
+        }
         mInterfaceName = interfaceName;
     }
 
diff --git a/core/java/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
similarity index 100%
rename from core/java/android/net/IInternalNetworkManagementListener.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index 12937b5..da0aa99 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -49,14 +49,8 @@
     @UnsupportedAppUsage
     NetworkStats getDataLayerSnapshotForUid(int uid);
 
-    /** Get a detailed snapshot of stats since boot for all UIDs.
-    *
-    * <p>Results will not always be limited to stats on requiredIfaces when specified: stats for
-    * interfaces stacked on the specified interfaces, or for interfaces on which the specified
-    * interfaces are stacked on, will also be included.
-    * @param requiredIfaces Interface names to get data for, or {@link NetworkStats#INTERFACES_ALL}.
-    */
-    NetworkStats getDetailedUidStats(in String[] requiredIfaces);
+    /** Get the transport NetworkStats for all UIDs since boot. */
+    NetworkStats getUidStatsForTransport(int transport);
 
     /** Return set of any ifaces associated with mobile networks since boot. */
     @UnsupportedAppUsage
@@ -94,4 +88,16 @@
     /** Registers a network stats provider */
     INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
             in INetworkStatsProvider provider);
+
+    /** Mark given UID as being in foreground for stats purposes. */
+    void setUidForeground(int uid, boolean uidForeground);
+
+    /** Advise persistence threshold; may be overridden internally. */
+    void advisePersistThreshold(long thresholdBytes);
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+     void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
index dfedf66..ab70be8 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -32,6 +32,11 @@
     /** Return historical network layer stats for traffic that matches template. */
     @UnsupportedAppUsage
     NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
+    /**
+     * Return historical network layer stats for traffic that matches template, start and end
+     * timestamp.
+     */
+    NetworkStatsHistory getHistoryIntervalForNetwork(in NetworkTemplate template, int fields, long start, long end);
 
     /**
      * Return network layer usage summary per UID for traffic that matches template.
@@ -46,6 +51,10 @@
      */
     @UnsupportedAppUsage
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
+
+    /** Return network layer usage summary per UID for tagged traffic that matches template. */
+    NetworkStats getTaggedSummaryForAllUid(in NetworkTemplate template, long start, long end);
+
     /** Return historical network layer stats for specific UID traffic that matches template. */
     @UnsupportedAppUsage
     NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
diff --git a/core/java/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
similarity index 100%
rename from core/java/android/net/InternalNetworkManagementException.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
diff --git a/core/java/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
similarity index 100%
rename from core/java/android/net/InternalNetworkManagementException.java
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
similarity index 100%
rename from core/java/android/net/InternalNetworkUpdateRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
new file mode 100644
index 0000000..f42c4b7
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** @hide */
+public final class InternalNetworkUpdateRequest implements Parcelable {
+    @NonNull
+    private final StaticIpConfiguration mIpConfig;
+    @NonNull
+    private final NetworkCapabilities mNetworkCapabilities;
+
+    @NonNull
+    public StaticIpConfiguration getIpConfig() {
+        return new StaticIpConfiguration(mIpConfig);
+    }
+
+    @NonNull
+    public NetworkCapabilities getNetworkCapabilities() {
+        return new NetworkCapabilities(mNetworkCapabilities);
+    }
+
+    /** @hide */
+    public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+            @NonNull final NetworkCapabilities networkCapabilities) {
+        Objects.requireNonNull(ipConfig);
+        Objects.requireNonNull(networkCapabilities);
+        mIpConfig = new StaticIpConfiguration(ipConfig);
+        mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
+    }
+
+    private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
+        Objects.requireNonNull(source);
+        mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
+        mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
+    }
+
+    @Override
+    public String toString() {
+        return "InternalNetworkUpdateRequest{"
+                + "mIpConfig=" + mIpConfig
+                + ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o;
+
+        return Objects.equals(that.getIpConfig(), mIpConfig)
+                && Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIpConfig, mNetworkCapabilities);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mIpConfig.writeToParcel(dest, flags);
+        mNetworkCapabilities.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR =
+            new Parcelable.Creator<InternalNetworkUpdateRequest>() {
+                @Override
+                public InternalNetworkUpdateRequest[] newArray(int size) {
+                    return new InternalNetworkUpdateRequest[size];
+                }
+
+                @Override
+                public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
+                    return new InternalNetworkUpdateRequest(source);
+                }
+            };
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
index a84e7a9..10a22ac 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
@@ -343,7 +343,7 @@
         // Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
         // the resource are not allowed.
         final String[] resourceAlgos = systemResources.getStringArray(
-                com.android.internal.R.array.config_optionalIpSecAlgorithms);
+                android.R.array.config_optionalIpSecAlgorithms);
         for (String str : resourceAlgos) {
             if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
                 // This error should be caught by CTS and never be thrown to API callers
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 0d15dff..a423783 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -25,8 +26,8 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.net.annotations.PolicyDirection;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -41,6 +42,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -88,6 +91,11 @@
     @SystemApi(client = MODULE_LIBRARIES)
     public static final int DIRECTION_FWD = 2;
 
+    /** @hide */
+    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PolicyDirection {}
+
     /**
      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
      *
@@ -981,6 +989,29 @@
     }
 
     /**
+     * @hide
+     */
+    public IpSecTransformResponse createTransform(IpSecConfig config, IBinder binder,
+            String callingPackage) {
+        try {
+            return mService.createTransform(config, binder, callingPackage);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void deleteTransform(int resourceId) {
+        try {
+            mService.deleteTransform(resourceId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Construct an instance of IpSecManager within an application context.
      *
      * @param context the application context for this manager
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
index 36199a0..68ae5de 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
@@ -26,9 +26,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.util.Log;
 
@@ -93,16 +90,9 @@
         mResourceId = INVALID_RESOURCE_ID;
     }
 
-    private IIpSecService getIpSecService() {
-        IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
-        if (b == null) {
-            throw new RemoteException("Failed to connect to IpSecService")
-                    .rethrowAsRuntimeException();
-        }
-
-        return IIpSecService.Stub.asInterface(b);
+    private IpSecManager getIpSecManager(Context context) {
+        return context.getSystemService(IpSecManager.class);
     }
-
     /**
      * Checks the result status and throws an appropriate exception if the status is not Status.OK.
      */
@@ -130,8 +120,7 @@
                     IpSecManager.SpiUnavailableException {
         synchronized (this) {
             try {
-                IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result = svc.createTransform(
+                IpSecTransformResponse result = getIpSecManager(mContext).createTransform(
                         mConfig, new Binder(), mContext.getOpPackageName());
                 int status = result.status;
                 checkResultStatus(status);
@@ -140,8 +129,6 @@
                 mCloseGuard.open("build");
             } catch (ServiceSpecificException e) {
                 throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
             }
         }
 
@@ -177,10 +164,7 @@
             return;
         }
         try {
-            IIpSecService svc = getIpSecService();
-            svc.deleteTransform(mResourceId);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
+            getIpSecManager(mContext).deleteTransform(mResourceId);
         } catch (Exception e) {
             // On close we swallow all random exceptions since failure to close is not
             // actionable by the user.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 8f1115e..d3d5a08 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -16,18 +16,29 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.service.NetworkIdentityProto;
-import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation;
+import android.telephony.TelephonyManager;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -37,11 +48,24 @@
  *
  * @hide
  */
-public class NetworkIdentity implements Comparable<NetworkIdentity> {
+@SystemApi(client = MODULE_LIBRARIES)
+public class NetworkIdentity {
     private static final String TAG = "NetworkIdentity";
 
+    /** @hide */
+    // TODO: Remove this after migrating all callers to use
+    //  {@link NetworkTemplate#NETWORK_TYPE_ALL} instead.
     public static final int SUBTYPE_COMBINED = -1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "OEM_MANAGED_" }, flag = true, value = {
+            NetworkTemplate.OEM_MANAGED_NO,
+            NetworkTemplate.OEM_MANAGED_PAID,
+            NetworkTemplate.OEM_MANAGED_PRIVATE
+    })
+    public @interface OemManaged{}
+
     /**
      * Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}.
      * @hide
@@ -51,29 +75,32 @@
      * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
      * @hide
      */
-    public static final int OEM_PAID = 0x1;
+    public static final int OEM_PAID = 1 << 0;
     /**
      * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
      * @hide
      */
-    public static final int OEM_PRIVATE = 0x2;
+    public static final int OEM_PRIVATE = 1 << 1;
+
+    private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE;
 
     final int mType;
-    final int mSubType;
+    final int mRatType;
     final String mSubscriberId;
-    final String mNetworkId;
+    final String mWifiNetworkKey;
     final boolean mRoaming;
     final boolean mMetered;
     final boolean mDefaultNetwork;
     final int mOemManaged;
 
+    /** @hide */
     public NetworkIdentity(
-            int type, int subType, String subscriberId, String networkId, boolean roaming,
-            boolean metered, boolean defaultNetwork, int oemManaged) {
+            int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey,
+            boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged) {
         mType = type;
-        mSubType = subType;
+        mRatType = ratType;
         mSubscriberId = subscriberId;
-        mNetworkId = networkId;
+        mWifiNetworkKey = wifiNetworkKey;
         mRoaming = roaming;
         mMetered = metered;
         mDefaultNetwork = defaultNetwork;
@@ -82,7 +109,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
+        return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered,
                 mDefaultNetwork, mOemManaged);
     }
 
@@ -90,9 +117,9 @@
     public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkIdentity) {
             final NetworkIdentity ident = (NetworkIdentity) obj;
-            return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
+            return mType == ident.mType && mRatType == ident.mRatType && mRoaming == ident.mRoaming
                     && Objects.equals(mSubscriberId, ident.mSubscriberId)
-                    && Objects.equals(mNetworkId, ident.mNetworkId)
+                    && Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey)
                     && mMetered == ident.mMetered
                     && mDefaultNetwork == ident.mDefaultNetwork
                     && mOemManaged == ident.mOemManaged;
@@ -104,18 +131,18 @@
     public String toString() {
         final StringBuilder builder = new StringBuilder("{");
         builder.append("type=").append(mType);
-        builder.append(", subType=");
-        if (mSubType == SUBTYPE_COMBINED) {
+        builder.append(", ratType=");
+        if (mRatType == NETWORK_TYPE_ALL) {
             builder.append("COMBINED");
         } else {
-            builder.append(mSubType);
+            builder.append(mRatType);
         }
         if (mSubscriberId != null) {
             builder.append(", subscriberId=")
                     .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
         }
-        if (mNetworkId != null) {
-            builder.append(", networkId=").append(mNetworkId);
+        if (mWifiNetworkKey != null) {
+            builder.append(", wifiNetworkKey=").append(mWifiNetworkKey);
         }
         if (mRoaming) {
             builder.append(", ROAMING");
@@ -153,18 +180,14 @@
         }
     }
 
+    /** @hide */
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
 
         proto.write(NetworkIdentityProto.TYPE, mType);
 
-        // Not dumping mSubType, subtypes are no longer supported.
+        // TODO: dump mRatType as well.
 
-        if (mSubscriberId != null) {
-            proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
-                    NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
-        }
-        proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
         proto.write(NetworkIdentityProto.METERED, mMetered);
         proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
@@ -173,77 +196,99 @@
         proto.end(start);
     }
 
+    /** Get the network type of this instance. */
     public int getType() {
         return mType;
     }
 
-    public int getSubType() {
-        return mSubType;
+    /** Get the Radio Access Technology(RAT) type of this instance. */
+    public int getRatType() {
+        return mRatType;
     }
 
+    /** Get the Subscriber Id of this instance. */
+    @Nullable
     public String getSubscriberId() {
         return mSubscriberId;
     }
 
-    public String getNetworkId() {
-        return mNetworkId;
+    /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getNetworkKey()}. */
+    @Nullable
+    public String getWifiNetworkKey() {
+        return mWifiNetworkKey;
     }
 
+    /** @hide */
+    // TODO: Remove this function after all callers are removed.
     public boolean getRoaming() {
         return mRoaming;
     }
 
+    /** Return whether this network is roaming. */
+    public boolean isRoaming() {
+        return mRoaming;
+    }
+
+    /** @hide */
+    // TODO: Remove this function after all callers are removed.
     public boolean getMetered() {
         return mMetered;
     }
 
+    /** Return whether this network is metered. */
+    public boolean isMetered() {
+        return mMetered;
+    }
+
+    /** @hide */
+    // TODO: Remove this function after all callers are removed.
     public boolean getDefaultNetwork() {
         return mDefaultNetwork;
     }
 
+    /** Return whether this network is the default network. */
+    public boolean isDefaultNetwork() {
+        return mDefaultNetwork;
+    }
+
+    /** Get the OEM managed type of this instance. */
     public int getOemManaged() {
         return mOemManaged;
     }
 
     /**
-     * Build a {@link NetworkIdentity} from the given {@link NetworkStateSnapshot} and
-     * {@code subType}, assuming that any mobile networks are using the current IMSI.
-     * The subType if applicable, should be set as one of the TelephonyManager.NETWORK_TYPE_*
-     * constants, or {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
+     * Assemble a {@link NetworkIdentity} from the passed arguments.
+     *
+     * This methods builds an identity based on the capabilities of the network in the
+     * snapshot and other passed arguments. The identity is used as a key to record data usage.
+     *
+     * @param snapshot the snapshot of network state. See {@link NetworkStateSnapshot}.
+     * @param defaultNetwork whether the network is a default network.
+     * @param ratType the Radio Access Technology(RAT) type of the network. Or
+     *                {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if not applicable.
+     *                See {@code TelephonyManager.NETWORK_TYPE_*}.
+     * @hide
+     * @deprecated See {@link NetworkIdentity.Builder}.
      */
+    // TODO: Remove this after all callers are migrated to use new Api.
+    @Deprecated
+    @NonNull
     public static NetworkIdentity buildNetworkIdentity(Context context,
-            NetworkStateSnapshot snapshot, boolean defaultNetwork, @NetworkType int subType) {
-        final int legacyType = snapshot.getLegacyType();
-
-        final String subscriberId = snapshot.getSubscriberId();
-        String networkId = null;
-        boolean roaming = !snapshot.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
-        boolean metered = !(snapshot.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
-                || snapshot.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-
-        final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
-
-        if (legacyType == TYPE_WIFI) {
-            final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
-                    .getTransportInfo();
-            if (transportInfo instanceof WifiInfo) {
-                final WifiInfo info = (WifiInfo) transportInfo;
-                networkId = info != null ? info.getCurrentNetworkKey() : null;
-            }
+            @NonNull NetworkStateSnapshot snapshot,
+            boolean defaultNetwork, @Annotation.NetworkType int ratType) {
+        final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
+                .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
+        if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
+            builder.setRatType(ratType);
         }
-
-        return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
-                defaultNetwork, oemManaged);
+        return builder.build();
     }
 
     /**
      * Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}.
      * @hide
      */
-    public static int getOemBitfield(NetworkCapabilities nc) {
+    public static int getOemBitfield(@NonNull NetworkCapabilities nc) {
         int oemManaged = OEM_NONE;
 
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) {
@@ -256,30 +301,265 @@
         return oemManaged;
     }
 
-    @Override
-    public int compareTo(NetworkIdentity another) {
-        int res = Integer.compare(mType, another.mType);
+    /** @hide */
+    public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) {
+        Objects.requireNonNull(right);
+        int res = Integer.compare(left.mType, right.mType);
         if (res == 0) {
-            res = Integer.compare(mSubType, another.mSubType);
+            res = Integer.compare(left.mRatType, right.mRatType);
         }
-        if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
-            res = mSubscriberId.compareTo(another.mSubscriberId);
+        if (res == 0 && left.mSubscriberId != null && right.mSubscriberId != null) {
+            res = left.mSubscriberId.compareTo(right.mSubscriberId);
         }
-        if (res == 0 && mNetworkId != null && another.mNetworkId != null) {
-            res = mNetworkId.compareTo(another.mNetworkId);
+        if (res == 0 && left.mWifiNetworkKey != null && right.mWifiNetworkKey != null) {
+            res = left.mWifiNetworkKey.compareTo(right.mWifiNetworkKey);
         }
         if (res == 0) {
-            res = Boolean.compare(mRoaming, another.mRoaming);
+            res = Boolean.compare(left.mRoaming, right.mRoaming);
         }
         if (res == 0) {
-            res = Boolean.compare(mMetered, another.mMetered);
+            res = Boolean.compare(left.mMetered, right.mMetered);
         }
         if (res == 0) {
-            res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
+            res = Boolean.compare(left.mDefaultNetwork, right.mDefaultNetwork);
         }
         if (res == 0) {
-            res = Integer.compare(mOemManaged, another.mOemManaged);
+            res = Integer.compare(left.mOemManaged, right.mOemManaged);
         }
         return res;
     }
+
+    /**
+     * Builder class for {@link NetworkIdentity}.
+     */
+    public static final class Builder {
+        // Need to be synchronized with ConnectivityManager.
+        // TODO: Use {@link ConnectivityManager#MAX_NETWORK_TYPE} when this file is in the module.
+        private static final int MAX_NETWORK_TYPE = 18; // TYPE_TEST
+        private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
+        private int mType;
+        private int mRatType;
+        private String mSubscriberId;
+        private String mWifiNetworkKey;
+        private boolean mRoaming;
+        private boolean mMetered;
+        private boolean mDefaultNetwork;
+        private int mOemManaged;
+
+        /**
+         * Creates a new Builder.
+         */
+        public Builder() {
+            // Initialize with default values. Will be overwritten by setters.
+            mType = ConnectivityManager.TYPE_NONE;
+            mRatType = NetworkTemplate.NETWORK_TYPE_ALL;
+            mSubscriberId = null;
+            mWifiNetworkKey = null;
+            mRoaming = false;
+            mMetered = false;
+            mDefaultNetwork = false;
+            mOemManaged = NetworkTemplate.OEM_MANAGED_NO;
+        }
+
+        /**
+         * Add an {@link NetworkStateSnapshot} into the {@link NetworkIdentity} instance.
+         * This is a useful shorthand that will read from the snapshot and set the
+         * following fields, if they are set in the snapshot :
+         *  - type
+         *  - subscriberId
+         *  - roaming
+         *  - metered
+         *  - oemManaged
+         *  - wifiNetworkKey
+         *
+         * @param snapshot The target {@link NetworkStateSnapshot} object.
+         * @return The builder object.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setNetworkStateSnapshot(@NonNull NetworkStateSnapshot snapshot) {
+            setType(snapshot.getLegacyType());
+
+            setSubscriberId(snapshot.getSubscriberId());
+            setRoaming(!snapshot.getNetworkCapabilities().hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING));
+            setMetered(!(snapshot.getNetworkCapabilities().hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+                    || snapshot.getNetworkCapabilities().hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)));
+
+            setOemManaged(getOemBitfield(snapshot.getNetworkCapabilities()));
+
+            if (mType == TYPE_WIFI) {
+                final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
+                        .getTransportInfo();
+                if (transportInfo instanceof WifiInfo) {
+                    final WifiInfo info = (WifiInfo) transportInfo;
+                    setWifiNetworkKey(info.getNetworkKey());
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Set the network type of the network.
+         *
+         * @param type the network type. See {@link ConnectivityManager#TYPE_*}.
+         *
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setType(int type) {
+            // Include TYPE_NONE for compatibility, type field might not be filled by some
+            // networks such as test networks.
+            if ((type < MIN_NETWORK_TYPE || MAX_NETWORK_TYPE < type)
+                    && type != ConnectivityManager.TYPE_NONE) {
+                throw new IllegalArgumentException("Invalid network type: " + type);
+            }
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Set the Radio Access Technology(RAT) type of the network.
+         *
+         * No RAT type is specified by default. Call clearRatType to reset.
+         *
+         * @param ratType the Radio Access Technology(RAT) type if applicable. See
+         *                {@code TelephonyManager.NETWORK_TYPE_*}.
+         *
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setRatType(@Annotation.NetworkType int ratType) {
+            if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
+                    && ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+                throw new IllegalArgumentException("Invalid ratType " + ratType);
+            }
+            mRatType = ratType;
+            return this;
+        }
+
+        /**
+         * Clear the Radio Access Technology(RAT) type of the network.
+         *
+         * @return this builder.
+         */
+        @NonNull
+        public Builder clearRatType() {
+            mRatType = NetworkTemplate.NETWORK_TYPE_ALL;
+            return this;
+        }
+
+        /**
+         * Set the Subscriber Id.
+         *
+         * @param subscriberId the Subscriber Id of the network. Or null if not applicable.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setSubscriberId(@Nullable String subscriberId) {
+            mSubscriberId = subscriberId;
+            return this;
+        }
+
+        /**
+         * Set the Wifi Network Key.
+         *
+         * @param wifiNetworkKey Wifi Network Key of the network,
+         *                        see {@link WifiInfo#getNetworkKey()}.
+         *                        Or null if not applicable.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
+            mWifiNetworkKey = wifiNetworkKey;
+            return this;
+        }
+
+        /**
+         * Set whether this network is roaming.
+         *
+         * This field is false by default. Call with false to reset.
+         *
+         * @param roaming the roaming status of the network.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setRoaming(boolean roaming) {
+            mRoaming = roaming;
+            return this;
+        }
+
+        /**
+         * Set whether this network is metered.
+         *
+         * This field is false by default. Call with false to reset.
+         *
+         * @param metered the meteredness of the network.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setMetered(boolean metered) {
+            mMetered = metered;
+            return this;
+        }
+
+        /**
+         * Set whether this network is the default network.
+         *
+         * This field is false by default. Call with false to reset.
+         *
+         * @param defaultNetwork the default network status of the network.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setDefaultNetwork(boolean defaultNetwork) {
+            mDefaultNetwork = defaultNetwork;
+            return this;
+        }
+
+        /**
+         * Set the OEM managed type.
+         *
+         * @param oemManaged Type of OEM managed network or unmanaged networks.
+         *                   See {@code NetworkTemplate#OEM_MANAGED_*}.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setOemManaged(@OemManaged int oemManaged) {
+            // Assert input does not contain illegal oemManage bits.
+            if ((~SUPPORTED_OEM_MANAGED_TYPES & oemManaged) != 0) {
+                throw new IllegalArgumentException("Invalid value for OemManaged : " + oemManaged);
+            }
+            mOemManaged = oemManaged;
+            return this;
+        }
+
+        private void ensureValidParameters() {
+            // Assert non-mobile network cannot have a ratType.
+            if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
+                throw new IllegalArgumentException(
+                        "Invalid ratType " + mRatType + " for type " + mType);
+            }
+
+            // Assert non-wifi network cannot have a wifi network key.
+            if (mType != TYPE_WIFI && mWifiNetworkKey != null) {
+                throw new IllegalArgumentException("Invalid wifi network key for type " + mType);
+            }
+        }
+
+        /**
+         * Builds the instance of the {@link NetworkIdentity}.
+         *
+         * @return the built instance of {@link NetworkIdentity}.
+         */
+        @NonNull
+        public NetworkIdentity build() {
+            ensureValidParameters();
+            return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
+                    mRoaming, mMetered, mDefaultNetwork, mOemManaged);
+        }
+    }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index abbebef..dfa347f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -18,6 +18,7 @@
 
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 
+import android.annotation.NonNull;
 import android.service.NetworkIdentitySetProto;
 import android.util.proto.ProtoOutputStream;
 
@@ -25,6 +26,8 @@
 import java.io.DataOutput;
 import java.io.IOException;
 import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
@@ -32,8 +35,7 @@
  *
  * @hide
  */
-public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
-        Comparable<NetworkIdentitySet> {
+public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
     private static final int VERSION_INIT = 1;
     private static final int VERSION_ADD_ROAMING = 2;
     private static final int VERSION_ADD_NETWORK_ID = 3;
@@ -41,9 +43,19 @@
     private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
     private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
 
+    /**
+     * Construct a {@link NetworkIdentitySet} object.
+     */
     public NetworkIdentitySet() {
+        super();
     }
 
+    /** @hide */
+    public NetworkIdentitySet(@NonNull Set<NetworkIdentity> ident) {
+        super(ident);
+    }
+
+    /** @hide */
     public NetworkIdentitySet(DataInput in) throws IOException {
         final int version = in.readInt();
         final int size = in.readInt();
@@ -52,7 +64,7 @@
                 final int ignored = in.readInt();
             }
             final int type = in.readInt();
-            final int subType = in.readInt();
+            final int ratType = in.readInt();
             final String subscriberId = readOptionalString(in);
             final String networkId;
             if (version >= VERSION_ADD_NETWORK_ID) {
@@ -91,63 +103,73 @@
                 oemNetCapabilities = NetworkIdentity.OEM_NONE;
             }
 
-            add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+            add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered,
                     defaultNetwork, oemNetCapabilities));
         }
     }
 
     /**
      * Method to serialize this object into a {@code DataOutput}.
+     * @hide
      */
     public void writeToStream(DataOutput out) throws IOException {
         out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
         out.writeInt(size());
         for (NetworkIdentity ident : this) {
             out.writeInt(ident.getType());
-            out.writeInt(ident.getSubType());
+            out.writeInt(ident.getRatType());
             writeOptionalString(out, ident.getSubscriberId());
-            writeOptionalString(out, ident.getNetworkId());
-            out.writeBoolean(ident.getRoaming());
-            out.writeBoolean(ident.getMetered());
-            out.writeBoolean(ident.getDefaultNetwork());
+            writeOptionalString(out, ident.getWifiNetworkKey());
+            out.writeBoolean(ident.isRoaming());
+            out.writeBoolean(ident.isMetered());
+            out.writeBoolean(ident.isDefaultNetwork());
             out.writeInt(ident.getOemManaged());
         }
     }
 
-    /** @return whether any {@link NetworkIdentity} in this set is considered metered. */
+    /**
+     * @return whether any {@link NetworkIdentity} in this set is considered metered.
+     * @hide
+     */
     public boolean isAnyMemberMetered() {
         if (isEmpty()) {
             return false;
         }
         for (NetworkIdentity ident : this) {
-            if (ident.getMetered()) {
+            if (ident.isMetered()) {
                 return true;
             }
         }
         return false;
     }
 
-    /** @return whether any {@link NetworkIdentity} in this set is considered roaming. */
+    /**
+     * @return whether any {@link NetworkIdentity} in this set is considered roaming.
+     * @hide
+     */
     public boolean isAnyMemberRoaming() {
         if (isEmpty()) {
             return false;
         }
         for (NetworkIdentity ident : this) {
-            if (ident.getRoaming()) {
+            if (ident.isRoaming()) {
                 return true;
             }
         }
         return false;
     }
 
-    /** @return whether any {@link NetworkIdentity} in this set is considered on the default
-            network. */
+    /**
+     * @return whether any {@link NetworkIdentity} in this set is considered on the default
+     *         network.
+     * @hide
+     */
     public boolean areAllMembersOnDefaultNetwork() {
         if (isEmpty()) {
             return true;
         }
         for (NetworkIdentity ident : this) {
-            if (!ident.getDefaultNetwork()) {
+            if (!ident.isDefaultNetwork()) {
                 return false;
             }
         }
@@ -171,18 +193,20 @@
         }
     }
 
-    @Override
-    public int compareTo(NetworkIdentitySet another) {
-        if (isEmpty()) return -1;
-        if (another.isEmpty()) return 1;
+    public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) {
+        Objects.requireNonNull(left);
+        Objects.requireNonNull(right);
+        if (left.isEmpty()) return -1;
+        if (right.isEmpty()) return 1;
 
-        final NetworkIdentity ident = iterator().next();
-        final NetworkIdentity anotherIdent = another.iterator().next();
-        return ident.compareTo(anotherIdent);
+        final NetworkIdentity leftIdent = left.iterator().next();
+        final NetworkIdentity rightIdent = right.iterator().next();
+        return NetworkIdentity.compare(leftIdent, rightIdent);
     }
 
     /**
      * Method to dump this object into proto debug file.
+     * @hide
      */
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 181a594..9175809 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -41,6 +41,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -57,7 +58,7 @@
  */
 // @NotThreadSafe
 @SystemApi
-public final class NetworkStats implements Parcelable {
+public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Entry> {
     private static final String TAG = "NetworkStats";
 
     /**
@@ -83,10 +84,7 @@
      */
     // TODO: Rename TAG_ALL to TAG_ANY.
     public static final int TAG_ALL = -1;
-    /**
-     * {@link #set} value for all sets combined, not including debug sets.
-     * @hide
-     */
+    /** {@link #set} value for all sets combined, not including debug sets. */
     public static final int SET_ALL = -1;
     /** {@link #set} value where background data is accounted. */
     public static final int SET_DEFAULT = 0;
@@ -114,9 +112,6 @@
             SET_ALL,
             SET_DEFAULT,
             SET_FOREGROUND,
-            SET_DEBUG_START,
-            SET_DBG_VPN_IN,
-            SET_DBG_VPN_OUT
     })
     public @interface State {
     }
@@ -131,10 +126,7 @@
     // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
 
-    /**
-     * {@link #metered} value to account for all metered states.
-     * @hide
-     */
+    /** {@link #metered} value to account for all metered states. */
     public static final int METERED_ALL = -1;
     /** {@link #metered} value where native, unmetered data is accounted. */
     public static final int METERED_NO = 0;
@@ -152,10 +144,7 @@
     }
 
 
-    /**
-     * {@link #roaming} value to account for all roaming states.
-     * @hide
-     */
+    /** {@link #roaming} value to account for all roaming states. */
     public static final int ROAMING_ALL = -1;
     /** {@link #roaming} value where native, non-roaming data is accounted. */
     public static final int ROAMING_NO = 0;
@@ -172,10 +161,7 @@
     public @interface Roaming {
     }
 
-    /**
-     * {@link #onDefaultNetwork} value to account for all default network states.
-     * @hide
-     */
+    /** {@link #onDefaultNetwork} value to account for all default network states. */
     public static final int DEFAULT_NETWORK_ALL = -1;
     /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
     public static final int DEFAULT_NETWORK_NO = 0;
@@ -398,6 +384,95 @@
             this.operations += another.operations;
         }
 
+        /**
+         * @return interface name of this entry.
+         * @hide
+         */
+        @Nullable public String getIface() {
+            return iface;
+        }
+
+        /**
+         * @return the uid of this entry.
+         */
+        public int getUid() {
+            return uid;
+        }
+
+        /**
+         * @return the set state of this entry. Should be one of the following
+         * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+         */
+        @State public int getSet() {
+            return set;
+        }
+
+        /**
+         * @return the tag value of this entry.
+         */
+        public int getTag() {
+            return tag;
+        }
+
+        /**
+         * @return the metered state. Should be one of the following
+         * values: {link #METERED_YES}, {link #METERED_NO}.
+         */
+        @Meteredness public int getMetered() {
+            return metered;
+        }
+
+        /**
+         * @return the roaming state. Should be one of the following
+         * values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+         */
+        @Roaming public int getRoaming() {
+            return roaming;
+        }
+
+        /**
+         * @return the default network state. Should be one of the following
+         * values: {link #DEFAULT_NETWORK_YES}, {link #DEFAULT_NETWORK_NO}.
+         */
+        @DefaultNetwork public int getDefaultNetwork() {
+            return defaultNetwork;
+        }
+
+        /**
+         * @return the number of received bytes.
+         */
+        public long getRxBytes() {
+            return rxBytes;
+        }
+
+        /**
+         * @return the number of received packets.
+         */
+        public long getRxPackets() {
+            return rxPackets;
+        }
+
+        /**
+         * @return the number of transmitted bytes.
+         */
+        public long getTxBytes() {
+            return txBytes;
+        }
+
+        /**
+         * @return the number of transmitted packets.
+         */
+        public long getTxPackets() {
+            return txPackets;
+        }
+
+        /**
+         * @return the count of network operations performed for this entry.
+         */
+        public long getOperations() {
+            return operations;
+        }
+
         @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder();
@@ -604,11 +679,40 @@
     }
 
     /**
+     * Iterate over Entry objects.
+     *
+     * Return an iterator of this object that will iterate through all contained Entry objects.
+     *
+     * This iterator does not support concurrent modification and makes no guarantee of fail-fast
+     * behavior. If any method that can mutate the contents of this object is called while
+     * iteration is in progress, either inside the loop or in another thread, then behavior is
+     * undefined.
+     * The remove() method is not implemented and will throw UnsupportedOperationException.
+     * @hide
+     */
+    @SystemApi
+    @NonNull public Iterator<Entry> iterator() {
+        return new Iterator<Entry>() {
+            int mIndex = 0;
+
+            @Override
+            public boolean hasNext() {
+                return mIndex < size;
+            }
+
+            @Override
+            public Entry next() {
+                return getValues(mIndex++, null);
+            }
+        };
+    }
+
+    /**
      * Return specific stats entry.
      * @hide
      */
     @UnsupportedAppUsage
-    public Entry getValues(int i, Entry recycle) {
+    public Entry getValues(int i, @Nullable Entry recycle) {
         final Entry entry = recycle != null ? recycle : new Entry();
         entry.iface = iface[i];
         entry.uid = uid[i];
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 7935d28..58ca21f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -32,6 +32,8 @@
 
 import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Binder;
 import android.service.NetworkStatsCollectionKeyProto;
 import android.service.NetworkStatsCollectionProto;
@@ -46,8 +48,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
 import com.android.internal.util.FileRotator;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkStatsUtils;
@@ -58,6 +58,7 @@
 import java.io.DataInput;
 import java.io.DataInputStream;
 import java.io.DataOutput;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -71,6 +72,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Collection of {@link NetworkStatsHistory}, stored based on combined key of
@@ -78,14 +80,12 @@
  *
  * @hide
  */
+// @SystemApi(client = MODULE_LIBRARIES)
 public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
     private static final String TAG = NetworkStatsCollection.class.getSimpleName();
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
-    /** Default buffer size from BufferedInputStream */
-    private static final int BUFFER_SIZE = 8192;
-
     private static final int VERSION_NETWORK_INIT = 1;
 
     private static final int VERSION_UID_INIT = 1;
@@ -104,15 +104,23 @@
     private long mTotalBytes;
     private boolean mDirty;
 
+    /**
+     * Construct a {@link NetworkStatsCollection} object.
+     *
+     * @param bucketDuration duration of the buckets in this object, in milliseconds.
+     * @hide
+     */
     public NetworkStatsCollection(long bucketDuration) {
         mBucketDuration = bucketDuration;
         reset();
     }
 
+    /** @hide */
     public void clear() {
         reset();
     }
 
+    /** @hide */
     public void reset() {
         mStats.clear();
         mStartMillis = Long.MAX_VALUE;
@@ -121,6 +129,7 @@
         mDirty = false;
     }
 
+    /** @hide */
     public long getStartMillis() {
         return mStartMillis;
     }
@@ -128,6 +137,7 @@
     /**
      * Return first atomic bucket in this collection, which is more conservative
      * than {@link #mStartMillis}.
+     * @hide
      */
     public long getFirstAtomicBucketMillis() {
         if (mStartMillis == Long.MAX_VALUE) {
@@ -137,26 +147,32 @@
         }
     }
 
+    /** @hide */
     public long getEndMillis() {
         return mEndMillis;
     }
 
+    /** @hide */
     public long getTotalBytes() {
         return mTotalBytes;
     }
 
+    /** @hide */
     public boolean isDirty() {
         return mDirty;
     }
 
+    /** @hide */
     public void clearDirty() {
         mDirty = false;
     }
 
+    /** @hide */
     public boolean isEmpty() {
         return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
     }
 
+    /** @hide */
     @VisibleForTesting
     public long roundUp(long time) {
         if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
@@ -172,6 +188,7 @@
         }
     }
 
+    /** @hide */
     @VisibleForTesting
     public long roundDown(long time) {
         if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
@@ -186,10 +203,12 @@
         }
     }
 
+    /** @hide */
     public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
         return getRelevantUids(accessLevel, Binder.getCallingUid());
     }
 
+    /** @hide */
     public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
                 final int callerUid) {
         final ArrayList<Integer> uids = new ArrayList<>();
@@ -210,6 +229,7 @@
     /**
      * Combine all {@link NetworkStatsHistory} in this collection which match
      * the requested parameters.
+     * @hide
      */
     public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan,
             int uid, int set, int tag, int fields, long start, long end,
@@ -335,6 +355,7 @@
      * @param end - end of the range, timestamp in milliseconds since the epoch.
      * @param accessLevel - caller access level.
      * @param callerUid - caller UID.
+     * @hide
      */
     public NetworkStats getSummary(NetworkTemplate template, long start, long end,
             @NetworkStatsAccess.Level int accessLevel, int callerUid) {
@@ -381,6 +402,7 @@
 
     /**
      * Record given {@link android.net.NetworkStats.Entry} into this collection.
+     * @hide
      */
     public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
             long end, NetworkStats.Entry entry) {
@@ -391,8 +413,12 @@
 
     /**
      * Record given {@link NetworkStatsHistory} into this collection.
+     *
+     * @hide
      */
-    private void recordHistory(Key key, NetworkStatsHistory history) {
+    public void recordHistory(@NonNull Key key, @NonNull NetworkStatsHistory history) {
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(history);
         if (history.size() == 0) return;
         noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
 
@@ -407,8 +433,11 @@
     /**
      * Record all {@link NetworkStatsHistory} contained in the given collection
      * into this collection.
+     *
+     * @hide
      */
-    public void recordCollection(NetworkStatsCollection another) {
+    public void recordCollection(@NonNull NetworkStatsCollection another) {
+        Objects.requireNonNull(another);
         for (int i = 0; i < another.mStats.size(); i++) {
             final Key key = another.mStats.keyAt(i);
             final NetworkStatsHistory value = another.mStats.valueAt(i);
@@ -437,10 +466,10 @@
         }
     }
 
+    /** @hide */
     @Override
     public void read(InputStream in) throws IOException {
-        final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
-        read(dataIn);
+        read((DataInput) new DataInputStream(in));
     }
 
     private void read(DataInput in) throws IOException {
@@ -477,11 +506,11 @@
         }
     }
 
+    /** @hide */
     @Override
     public void write(OutputStream out) throws IOException {
-        final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
-        write(dataOut);
-        dataOut.flush();
+        write((DataOutput) new DataOutputStream(out));
+        out.flush();
     }
 
     private void write(DataOutput out) throws IOException {
@@ -520,6 +549,7 @@
      * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
      *
      * @deprecated
+     * @hide
      */
     @Deprecated
     public void readLegacyNetwork(File file) throws IOException {
@@ -565,6 +595,7 @@
      * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
      *
      * @deprecated
+     * @hide
      */
     @Deprecated
     public void readLegacyUid(File file, boolean onlyTags) throws IOException {
@@ -635,6 +666,7 @@
      * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
      * moving any {@link NetworkStats#TAG_NONE} series to
      * {@link TrafficStats#UID_REMOVED}.
+     * @hide
      */
     public void removeUids(int[] uids) {
         final ArrayList<Key> knownKeys = new ArrayList<>();
@@ -671,10 +703,11 @@
     private ArrayList<Key> getSortedKeys() {
         final ArrayList<Key> keys = new ArrayList<>();
         keys.addAll(mStats.keySet());
-        Collections.sort(keys);
+        Collections.sort(keys, (left, right) -> Key.compare(left, right));
         return keys;
     }
 
+    /** @hide */
     public void dump(IndentingPrintWriter pw) {
         for (Key key : getSortedKeys()) {
             pw.print("ident="); pw.print(key.ident.toString());
@@ -689,6 +722,7 @@
         }
     }
 
+    /** @hide */
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
 
@@ -712,6 +746,7 @@
         proto.end(start);
     }
 
+    /** @hide */
     public void dumpCheckin(PrintWriter pw, long start, long end) {
         dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
         dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
@@ -774,16 +809,37 @@
         return false;
     }
 
-    private static class Key implements Comparable<Key> {
+    /**
+     * the identifier that associate with the {@link NetworkStatsHistory} object to identify
+     * a certain record in the {@link NetworkStatsCollection} object.
+     */
+    public static class Key {
+        /** @hide */
         public final NetworkIdentitySet ident;
+        /** @hide */
         public final int uid;
+        /** @hide */
         public final int set;
+        /** @hide */
         public final int tag;
 
         private final int mHashCode;
 
-        Key(NetworkIdentitySet ident, int uid, int set, int tag) {
-            this.ident = ident;
+        /**
+         * Construct a {@link Key} object.
+         *
+         * @param ident a Set of {@link NetworkIdentity} that associated with the record.
+         * @param uid Uid of the record.
+         * @param set Set of the record, see {@code NetworkStats#SET_*}.
+         * @param tag Tag of the record, see {@link TrafficStats#setThreadStatsTag(int)}.
+         */
+        public Key(@NonNull Set<NetworkIdentity> ident, int uid, int set, int tag) {
+            this(new NetworkIdentitySet(Objects.requireNonNull(ident)), uid, set, tag);
+        }
+
+        /** @hide */
+        public Key(@NonNull NetworkIdentitySet ident, int uid, int set, int tag) {
+            this.ident = Objects.requireNonNull(ident);
             this.uid = uid;
             this.set = set;
             this.tag = tag;
@@ -796,7 +852,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (obj instanceof Key) {
                 final Key key = (Key) obj;
                 return uid == key.uid && set == key.set && tag == key.tag
@@ -805,20 +861,22 @@
             return false;
         }
 
-        @Override
-        public int compareTo(Key another) {
+        /** @hide */
+        public static int compare(@NonNull Key left, @NonNull Key right) {
+            Objects.requireNonNull(left);
+            Objects.requireNonNull(right);
             int res = 0;
-            if (ident != null && another.ident != null) {
-                res = ident.compareTo(another.ident);
+            if (left.ident != null && right.ident != null) {
+                res = NetworkIdentitySet.compare(left.ident, right.ident);
             }
             if (res == 0) {
-                res = Integer.compare(uid, another.uid);
+                res = Integer.compare(left.uid, right.uid);
             }
             if (res == 0) {
-                res = Integer.compare(set, another.set);
+                res = Integer.compare(left.set, right.set);
             }
             if (res == 0) {
-                res = Integer.compare(tag, another.tag);
+                res = Integer.compare(left.tag, right.tag);
             }
             return res;
         }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index 428bc6d..78c1370 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.TAG_NONE;
@@ -30,6 +31,8 @@
 
 import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -50,7 +53,9 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.ProtocolException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Random;
 
 /**
@@ -64,18 +69,25 @@
  *
  * @hide
  */
-public class NetworkStatsHistory implements Parcelable {
+@SystemApi(client = MODULE_LIBRARIES)
+public final class NetworkStatsHistory implements Parcelable {
     private static final int VERSION_INIT = 1;
     private static final int VERSION_ADD_PACKETS = 2;
     private static final int VERSION_ADD_ACTIVE = 3;
 
+    /** @hide */
     public static final int FIELD_ACTIVE_TIME = 0x01;
+    /** @hide */
     public static final int FIELD_RX_BYTES = 0x02;
+    /** @hide */
     public static final int FIELD_RX_PACKETS = 0x04;
+    /** @hide */
     public static final int FIELD_TX_BYTES = 0x08;
+    /** @hide */
     public static final int FIELD_TX_PACKETS = 0x10;
+    /** @hide */
     public static final int FIELD_OPERATIONS = 0x20;
-
+    /** @hide */
     public static final int FIELD_ALL = 0xFFFFFFFF;
 
     private long bucketDuration;
@@ -89,34 +101,171 @@
     private long[] operations;
     private long totalBytes;
 
-    public static class Entry {
-        public static final long UNKNOWN = -1;
-
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public long bucketDuration;
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public long bucketStart;
-        public long activeTime;
-        @UnsupportedAppUsage
-        public long rxBytes;
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public long rxPackets;
-        @UnsupportedAppUsage
-        public long txBytes;
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public long txPackets;
-        public long operations;
+    /** @hide */
+    public NetworkStatsHistory(long bucketDuration, long[] bucketStart, long[] activeTime,
+            long[] rxBytes, long[] rxPackets, long[] txBytes, long[] txPackets,
+            long[] operations, int bucketCount, long totalBytes) {
+        this.bucketDuration = bucketDuration;
+        this.bucketStart = bucketStart;
+        this.activeTime = activeTime;
+        this.rxBytes = rxBytes;
+        this.rxPackets = rxPackets;
+        this.txBytes = txBytes;
+        this.txPackets = txPackets;
+        this.operations = operations;
+        this.bucketCount = bucketCount;
+        this.totalBytes = totalBytes;
     }
 
+    /**
+     * An instance to represent a single record in a {@link NetworkStatsHistory} object.
+     */
+    public static final class Entry {
+        /** @hide */
+        public static final long UNKNOWN = -1;
+
+        /** @hide */
+        // TODO: Migrate all callers to get duration from the history object and remove this field.
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public long bucketDuration;
+        /** @hide */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public long bucketStart;
+        /** @hide */
+        public long activeTime;
+        /** @hide */
+        @UnsupportedAppUsage
+        public long rxBytes;
+        /** @hide */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public long rxPackets;
+        /** @hide */
+        @UnsupportedAppUsage
+        public long txBytes;
+        /** @hide */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public long txPackets;
+        /** @hide */
+        public long operations;
+        /** @hide */
+        Entry() {}
+
+        /**
+         * Construct a {@link Entry} instance to represent a single record in a
+         * {@link NetworkStatsHistory} object.
+         *
+         * @param bucketStart Start of period for this {@link Entry}, in milliseconds since the
+         *                    Unix epoch, see {@link java.lang.System#currentTimeMillis}.
+         * @param activeTime Active time for this {@link Entry}, in milliseconds.
+         * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+         *                represent the contents of IP packets, including IP headers.
+         * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+         *                  represent the contents of IP packets, including IP headers.
+         * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+         *                represent the contents of IP packets, including IP headers.
+         * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+         *                  represent the contents of IP packets, including IP headers.
+         * @param operations count of network operations performed for this {@link Entry}. This can
+         *                   be used to derive bytes-per-operation.
+         */
+        public Entry(long bucketStart, long activeTime, long rxBytes,
+                long rxPackets, long txBytes, long txPackets, long operations) {
+            this.bucketStart = bucketStart;
+            this.activeTime = activeTime;
+            this.rxBytes = rxBytes;
+            this.rxPackets = rxPackets;
+            this.txBytes = txBytes;
+            this.txPackets = txPackets;
+            this.operations = operations;
+        }
+
+        /**
+         * Get start timestamp of the bucket's time interval, in milliseconds since the Unix epoch.
+         */
+        public long getBucketStart() {
+            return bucketStart;
+        }
+
+        /**
+         * Get active time of the bucket's time interval, in milliseconds.
+         */
+        public long getActiveTime() {
+            return activeTime;
+        }
+
+        /** Get number of bytes received for this {@link Entry}. */
+        public long getRxBytes() {
+            return rxBytes;
+        }
+
+        /** Get number of packets received for this {@link Entry}. */
+        public long getRxPackets() {
+            return rxPackets;
+        }
+
+        /** Get number of bytes transmitted for this {@link Entry}. */
+        public long getTxBytes() {
+            return txBytes;
+        }
+
+        /** Get number of packets transmitted for this {@link Entry}. */
+        public long getTxPackets() {
+            return txPackets;
+        }
+
+        /** Get count of network operations performed for this {@link Entry}. */
+        public long getOperations() {
+            return operations;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o.getClass() != getClass()) return false;
+            Entry entry = (Entry) o;
+            return bucketStart == entry.bucketStart
+                    && activeTime == entry.activeTime && rxBytes == entry.rxBytes
+                    && rxPackets == entry.rxPackets && txBytes == entry.txBytes
+                    && txPackets == entry.txPackets && operations == entry.operations;
+        }
+
+        @Override
+        public int hashCode() {
+            return (int) (bucketStart * 2
+                    + activeTime * 3
+                    + rxBytes * 5
+                    + rxPackets * 7
+                    + txBytes * 11
+                    + txPackets * 13
+                    + operations * 17);
+        }
+
+        @Override
+        public String toString() {
+            return "Entry{"
+                    + "bucketStart=" + bucketStart
+                    + ", activeTime=" + activeTime
+                    + ", rxBytes=" + rxBytes
+                    + ", rxPackets=" + rxPackets
+                    + ", txBytes=" + txBytes
+                    + ", txPackets=" + txPackets
+                    + ", operations=" + operations
+                    + "}";
+        }
+    }
+
+    /** @hide */
     @UnsupportedAppUsage
     public NetworkStatsHistory(long bucketDuration) {
         this(bucketDuration, 10, FIELD_ALL);
     }
 
+    /** @hide */
     public NetworkStatsHistory(long bucketDuration, int initialSize) {
         this(bucketDuration, initialSize, FIELD_ALL);
     }
 
+    /** @hide */
     public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
         this.bucketDuration = bucketDuration;
         bucketStart = new long[initialSize];
@@ -130,11 +279,13 @@
         totalBytes = 0;
     }
 
+    /** @hide */
     public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
         this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
         recordEntireHistory(existing);
     }
 
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public NetworkStatsHistory(Parcel in) {
         bucketDuration = in.readLong();
@@ -150,7 +301,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, activeTime, bucketCount);
@@ -162,6 +313,7 @@
         out.writeLong(totalBytes);
     }
 
+    /** @hide */
     public NetworkStatsHistory(DataInput in) throws IOException {
         final int version = in.readInt();
         switch (version) {
@@ -204,6 +356,7 @@
         }
     }
 
+    /** @hide */
     public void writeToStream(DataOutput out) throws IOException {
         out.writeInt(VERSION_ADD_ACTIVE);
         out.writeLong(bucketDuration);
@@ -221,15 +374,18 @@
         return 0;
     }
 
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public int size() {
         return bucketCount;
     }
 
+    /** @hide */
     public long getBucketDuration() {
         return bucketDuration;
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public long getStart() {
         if (bucketCount > 0) {
@@ -239,6 +395,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public long getEnd() {
         if (bucketCount > 0) {
@@ -250,6 +407,7 @@
 
     /**
      * Return total bytes represented by this history.
+     * @hide
      */
     public long getTotalBytes() {
         return totalBytes;
@@ -258,6 +416,7 @@
     /**
      * Return index of bucket that contains or is immediately before the
      * requested time.
+     * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public int getIndexBefore(long time) {
@@ -273,6 +432,7 @@
     /**
      * Return index of bucket that contains or is immediately after the
      * requested time.
+     * @hide
      */
     public int getIndexAfter(long time) {
         int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
@@ -286,6 +446,7 @@
 
     /**
      * Return specific stats entry.
+     * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public Entry getValues(int i, Entry recycle) {
@@ -301,6 +462,23 @@
         return entry;
     }
 
+    /**
+     * Get List of {@link Entry} of the {@link NetworkStatsHistory} instance.
+     *
+     * @return
+     */
+    @NonNull
+    public List<Entry> getEntries() {
+        // TODO: Return a wrapper that uses this list instead, to prevent the returned result
+        //  from being changed.
+        final ArrayList<Entry> ret = new ArrayList<>(size());
+        for (int i = 0; i < size(); i++) {
+            ret.add(getValues(i, null /* recycle */));
+        }
+        return ret;
+    }
+
+    /** @hide */
     public void setValues(int i, Entry entry) {
         // Unwind old values
         if (rxBytes != null) totalBytes -= rxBytes[i];
@@ -322,6 +500,7 @@
     /**
      * Record that data traffic occurred in the given time range. Will
      * distribute across internal buckets, creating new buckets as needed.
+     * @hide
      */
     @Deprecated
     public void recordData(long start, long end, long rxBytes, long txBytes) {
@@ -332,6 +511,7 @@
     /**
      * Record that data traffic occurred in the given time range. Will
      * distribute across internal buckets, creating new buckets as needed.
+     * @hide
      */
     public void recordData(long start, long end, NetworkStats.Entry entry) {
         long rxBytes = entry.rxBytes;
@@ -392,6 +572,7 @@
     /**
      * Record an entire {@link NetworkStatsHistory} into this history. Usually
      * for combining together stats for external reporting.
+     * @hide
      */
     @UnsupportedAppUsage
     public void recordEntireHistory(NetworkStatsHistory input) {
@@ -402,6 +583,7 @@
      * Record given {@link NetworkStatsHistory} into this history, copying only
      * buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets.
+     * @hide
      */
     public void recordHistory(NetworkStatsHistory input, long start, long end) {
         final NetworkStats.Entry entry = new NetworkStats.Entry(
@@ -483,6 +665,7 @@
 
     /**
      * Clear all data stored in this object.
+     * @hide
      */
     public void clear() {
         bucketStart = EmptyArray.LONG;
@@ -498,9 +681,10 @@
 
     /**
      * Remove buckets older than requested cutoff.
+     * @hide
      */
-    @Deprecated
     public void removeBucketsBefore(long cutoff) {
+        // TODO: Consider use getIndexBefore.
         int i;
         for (i = 0; i < bucketCount; i++) {
             final long curStart = bucketStart[i];
@@ -522,7 +706,9 @@
             if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
             bucketCount -= i;
 
-            // TODO: subtract removed values from totalBytes
+            totalBytes = 0;
+            if (rxBytes != null) totalBytes += CollectionUtils.total(rxBytes);
+            if (txBytes != null) totalBytes += CollectionUtils.total(txBytes);
         }
     }
 
@@ -536,6 +722,7 @@
      * @param start - start of the range, timestamp in milliseconds since the epoch.
      * @param end - end of the range, timestamp in milliseconds since the epoch.
      * @param recycle - entry instance for performance, could be null.
+     * @hide
      */
     @UnsupportedAppUsage
     public Entry getValues(long start, long end, Entry recycle) {
@@ -550,6 +737,7 @@
      * @param end - end of the range, timestamp in milliseconds since the epoch.
      * @param now - current timestamp in milliseconds since the epoch (wall clock).
      * @param recycle - entry instance for performance, could be null.
+     * @hide
      */
     @UnsupportedAppUsage
     public Entry getValues(long start, long end, long now, Entry recycle) {
@@ -613,6 +801,7 @@
 
     /**
      * @deprecated only for temporary testing
+     * @hide
      */
     @Deprecated
     public void generateRandom(long start, long end, long bytes) {
@@ -631,6 +820,7 @@
 
     /**
      * @deprecated only for temporary testing
+     * @hide
      */
     @Deprecated
     public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
@@ -660,12 +850,14 @@
         }
     }
 
+    /** @hide */
     public static long randomLong(Random r, long start, long end) {
         return (long) (start + (r.nextFloat() * (end - start)));
     }
 
     /**
      * Quickly determine if this history intersects with given window.
+     * @hide
      */
     public boolean intersects(long start, long end) {
         final long dataStart = getStart();
@@ -677,6 +869,7 @@
         return false;
     }
 
+    /** @hide */
     public void dump(IndentingPrintWriter pw, boolean fullHistory) {
         pw.print("NetworkStatsHistory: bucketDuration=");
         pw.println(bucketDuration / SECOND_IN_MILLIS);
@@ -700,6 +893,7 @@
         pw.decreaseIndent();
     }
 
+    /** @hide */
     public void dumpCheckin(PrintWriter pw) {
         pw.print("d,");
         pw.print(bucketDuration / SECOND_IN_MILLIS);
@@ -717,6 +911,7 @@
         }
     }
 
+    /** @hide */
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
 
@@ -776,6 +971,7 @@
         if (array != null) array[i] += value;
     }
 
+    /** @hide */
     public int estimateResizeBuckets(long newBucketDuration) {
         return (int) (size() * getBucketDuration() / newBucketDuration);
     }
@@ -783,6 +979,7 @@
     /**
      * Utility methods for interacting with {@link DataInputStream} and
      * {@link DataOutputStream}, mostly dealing with writing partial arrays.
+     * @hide
      */
     public static class DataStreamUtils {
         @Deprecated
@@ -857,6 +1054,7 @@
     /**
      * Utility methods for interacting with {@link Parcel} structures, mostly
      * dealing with writing partial arrays.
+     * @hide
      */
     public static class ParcelUtils {
         public static long[] readLongArray(Parcel in) {
@@ -884,4 +1082,80 @@
         }
     }
 
+    /**
+     * Builder class for {@link NetworkStatsHistory}.
+     */
+    public static final class Builder {
+        private final long mBucketDuration;
+        private final List<Long> mBucketStart;
+        private final List<Long> mActiveTime;
+        private final List<Long> mRxBytes;
+        private final List<Long> mRxPackets;
+        private final List<Long> mTxBytes;
+        private final List<Long> mTxPackets;
+        private final List<Long> mOperations;
+
+        /**
+         * Creates a new Builder with given bucket duration and initial capacity to construct
+         * {@link NetworkStatsHistory} objects.
+         *
+         * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+         * @param initialCapacity Estimated number of records.
+         */
+        public Builder(long bucketDuration, int initialCapacity) {
+            mBucketDuration = bucketDuration;
+            mBucketStart = new ArrayList<>(initialCapacity);
+            mActiveTime = new ArrayList<>(initialCapacity);
+            mRxBytes = new ArrayList<>(initialCapacity);
+            mRxPackets = new ArrayList<>(initialCapacity);
+            mTxBytes = new ArrayList<>(initialCapacity);
+            mTxPackets = new ArrayList<>(initialCapacity);
+            mOperations = new ArrayList<>(initialCapacity);
+        }
+
+        /**
+         * Add an {@link Entry} into the {@link NetworkStatsHistory} instance.
+         *
+         * @param entry The target {@link Entry} object.
+         * @return The builder object.
+         */
+        @NonNull
+        public Builder addEntry(@NonNull Entry entry) {
+            mBucketStart.add(entry.bucketStart);
+            mActiveTime.add(entry.activeTime);
+            mRxBytes.add(entry.rxBytes);
+            mRxPackets.add(entry.rxPackets);
+            mTxBytes.add(entry.txBytes);
+            mTxPackets.add(entry.txPackets);
+            mOperations.add(entry.operations);
+            return this;
+        }
+
+        private static long sum(@NonNull List<Long> list) {
+            long sum = 0;
+            for (long entry : list) {
+                sum += entry;
+            }
+            return sum;
+        }
+
+        /**
+         * Builds the instance of the {@link NetworkStatsHistory}.
+         *
+         * @return the built instance of {@link NetworkStatsHistory}.
+         */
+        @NonNull
+        public NetworkStatsHistory build() {
+            return new NetworkStatsHistory(mBucketDuration,
+                    CollectionUtils.toLongArray(mBucketStart),
+                    CollectionUtils.toLongArray(mActiveTime),
+                    CollectionUtils.toLongArray(mRxBytes),
+                    CollectionUtils.toLongArray(mRxPackets),
+                    CollectionUtils.toLongArray(mTxBytes),
+                    CollectionUtils.toLongArray(mTxPackets),
+                    CollectionUtils.toLongArray(mOperations),
+                    mBucketStart.size(),
+                    sum(mRxBytes) + sum(mTxBytes));
+        }
+    }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 659ad06..cad8075 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -39,7 +40,9 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.wifi.WifiInfo;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -70,7 +73,7 @@
  *
  * @hide
  */
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
 public final class NetworkTemplate implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -260,7 +263,7 @@
      * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
      * given key of the wifi network.
      *
-     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
      *                  to know details about the key.
      * @hide
      */
@@ -280,7 +283,7 @@
      * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
      * of key of the wifi network.
      *
-     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
      *                  to know details about the key.
      * @param subscriberId the IMSI associated to this wifi network.
      *
@@ -361,7 +364,7 @@
     private final int mMetered;
     private final int mRoaming;
     private final int mDefaultNetwork;
-    private final int mSubType;
+    private final int mRatType;
     /**
      * The subscriber Id match rule defines how the template should match networks with
      * specific subscriberId(s). See NetworkTemplate#SUBSCRIBER_ID_MATCH_RULE_* for more detail.
@@ -410,18 +413,18 @@
     /** @hide */
     // TODO: Remove it after updating all of the caller.
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType,
+            String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int ratType,
             int oemManaged) {
         this(matchRule, subscriberId, matchSubscriberIds,
                 wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
-                metered, roaming, defaultNetwork, subType, oemManaged,
+                metered, roaming, defaultNetwork, ratType, oemManaged,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
     /** @hide */
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String[] matchWifiNetworkKeys, int metered, int roaming,
-            int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) {
+            int defaultNetwork, int ratType, int oemManaged, int subscriberIdMatchRule) {
         Objects.requireNonNull(matchWifiNetworkKeys);
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
@@ -432,7 +435,7 @@
         mMetered = metered;
         mRoaming = roaming;
         mDefaultNetwork = defaultNetwork;
-        mSubType = subType;
+        mRatType = ratType;
         mOemManaged = oemManaged;
         mSubscriberIdMatchRule = subscriberIdMatchRule;
         checkValidSubscriberIdMatchRule(matchRule, subscriberIdMatchRule);
@@ -450,7 +453,7 @@
         mMetered = in.readInt();
         mRoaming = in.readInt();
         mDefaultNetwork = in.readInt();
-        mSubType = in.readInt();
+        mRatType = in.readInt();
         mOemManaged = in.readInt();
         mSubscriberIdMatchRule = in.readInt();
     }
@@ -464,7 +467,7 @@
         dest.writeInt(mMetered);
         dest.writeInt(mRoaming);
         dest.writeInt(mDefaultNetwork);
-        dest.writeInt(mSubType);
+        dest.writeInt(mRatType);
         dest.writeInt(mOemManaged);
         dest.writeInt(mSubscriberIdMatchRule);
     }
@@ -497,8 +500,8 @@
             builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
                     mDefaultNetwork));
         }
-        if (mSubType != NETWORK_TYPE_ALL) {
-            builder.append(", subType=").append(mSubType);
+        if (mRatType != NETWORK_TYPE_ALL) {
+            builder.append(", ratType=").append(mRatType);
         }
         if (mOemManaged != OEM_MANAGED_ALL) {
             builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
@@ -511,7 +514,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys),
-                mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
+                mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged, mSubscriberIdMatchRule);
     }
 
     @Override
@@ -523,7 +526,7 @@
                     && mMetered == other.mMetered
                     && mRoaming == other.mRoaming
                     && mDefaultNetwork == other.mDefaultNetwork
-                    && mSubType == other.mSubType
+                    && mRatType == other.mRatType
                     && mOemManaged == other.mOemManaged
                     && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule
                     && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys);
@@ -572,6 +575,7 @@
 
     /**
      * Get subscriber Id of the template.
+     * @hide
      */
     @Nullable
     @UnsupportedAppUsage
@@ -588,26 +592,19 @@
     }
 
     /**
-     * Get Wifi Network Key of the template. See {@link WifiInfo#getCurrentNetworkKey()}.
+     * Get the set of Wifi Network Keys of the template.
+     * See {@link WifiInfo#getNetworkKey()}.
      */
-    @Nullable
-    public String getWifiNetworkKey() {
-        return CollectionUtils.isEmpty(mMatchWifiNetworkKeys) ? null : mMatchWifiNetworkKeys[0];
-    }
-
-    /**
-     * Get set of Wifi Network Keys of the template.
-     */
-    @Nullable
+    @NonNull
     public Set<String> getWifiNetworkKeys() {
         return new ArraySet<>(Arrays.asList(mMatchWifiNetworkKeys));
     }
 
     /** @hide */
-    // TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
+    // TODO: Remove this and replace all callers with {@link #getWifiNetworkKeys()}.
     @Nullable
     public String getNetworkId() {
-        return getWifiNetworkKey();
+        return getWifiNetworkKeys().isEmpty() ? null : getWifiNetworkKeys().iterator().next();
     }
 
     /**
@@ -638,7 +635,7 @@
      * Get the Radio Access Technology(RAT) type filter of the template.
      */
     public int getRatType() {
-        return mSubType;
+        return mRatType;
     }
 
     /**
@@ -711,8 +708,8 @@
     }
 
     private boolean matchesCollapsedRatType(NetworkIdentity ident) {
-        return mSubType == NETWORK_TYPE_ALL
-                || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType);
+        return mRatType == NETWORK_TYPE_ALL
+                || getCollapsedRatType(mRatType) == getCollapsedRatType(ident.mRatType);
     }
 
     /**
@@ -732,7 +729,7 @@
      * Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
      * empty.
      *
-     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
      *                  to know details about the key.
      */
     private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
@@ -840,7 +837,7 @@
         switch (ident.mType) {
             case TYPE_WIFI:
                 return matchesSubscriberId(ident.mSubscriberId)
-                        && matchesWifiNetworkKey(ident.mNetworkId);
+                        && matchesWifiNetworkKey(ident.mWifiNetworkKey);
             default:
                 return false;
         }
@@ -1062,9 +1059,9 @@
          * the intention of matching any Wifi Network Key.
          *
          * @param wifiNetworkKeys the list of Wifi Network Key,
-         *                        see {@link WifiInfo#getCurrentNetworkKey()}.
+         *                        see {@link WifiInfo#getNetworkKey()}.
          *                        Or an empty list to match all networks.
-         *                        Note that {@code getCurrentNetworkKey()} might get null key
+         *                        Note that {@code getNetworkKey()} might get null key
          *                        when wifi disconnects. However, the caller should never invoke
          *                        this function with a null Wifi Network Key since such statistics
          *                        never exists.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index d8feb88..c803a72 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -26,9 +26,9 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.MediaPlayer;
+import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 
 import com.android.server.NetworkManagementSocketTagger;
 
@@ -53,6 +53,7 @@
  * use {@link NetworkStatsManager} instead.
  */
 public class TrafficStats {
+    private static final String TAG = TrafficStats.class.getSimpleName();
     /**
      * The return value to indicate that the device does not support the statistic.
      */
@@ -173,8 +174,8 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
     private synchronized static INetworkStatsService getStatsService() {
         if (sStatsService == null) {
-            sStatsService = INetworkStatsService.Stub.asInterface(
-                    ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+            throw new IllegalStateException("TrafficStats not initialized, uid="
+                    + Binder.getCallingUid());
         }
         return sStatsService;
     }
@@ -193,6 +194,26 @@
     private static final String LOOPBACK_IFACE = "lo";
 
     /**
+     * Initialization {@link TrafficStats} with the context, to
+     * allow {@link TrafficStats} to fetch the needed binder.
+     *
+     * @param context a long-lived context, such as the application context or system
+     *                server context.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("VisiblySynchronized")
+    public static synchronized void init(@NonNull final Context context) {
+        if (sStatsService != null) {
+            throw new IllegalStateException("TrafficStats is already initialized, uid="
+                    + Binder.getCallingUid());
+        }
+        final NetworkStatsManager statsManager =
+                context.getSystemService(NetworkStatsManager.class);
+        sStatsService = statsManager.getBinder();
+    }
+
+    /**
      * Set active tag to use when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * <p>
@@ -265,6 +286,18 @@
     }
 
     /**
+     * Set active tag to use when accounting {@link Socket} traffic originating
+     * from the current thread. The tag used internally is well-defined to
+     * distinguish all download provider traffic.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void setThreadStatsTagDownload() {
+        setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
+    }
+
+    /**
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
new file mode 100644
index 0000000..1eb52fb
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 android.os.StrictMode;
+import android.util.Log;
+
+import dalvik.system.SocketTagger;
+
+import java.io.FileDescriptor;
+import java.net.SocketException;
+
+/**
+ * Assigns tags to sockets for traffic stats.
+ * @hide
+ */
+public final class NetworkManagementSocketTagger extends SocketTagger {
+    private static final String TAG = "NetworkManagementSocketTagger";
+    private static final boolean LOGD = false;
+
+    private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
+        @Override
+        protected SocketTags initialValue() {
+            return new SocketTags();
+        }
+    };
+
+    public static void install() {
+        SocketTagger.set(new NetworkManagementSocketTagger());
+    }
+
+    public static int setThreadSocketStatsTag(int tag) {
+        final int old = threadSocketTags.get().statsTag;
+        threadSocketTags.get().statsTag = tag;
+        return old;
+    }
+
+    public static int getThreadSocketStatsTag() {
+        return threadSocketTags.get().statsTag;
+    }
+
+    public static int setThreadSocketStatsUid(int uid) {
+        final int old = threadSocketTags.get().statsUid;
+        threadSocketTags.get().statsUid = uid;
+        return old;
+    }
+
+    public static int getThreadSocketStatsUid() {
+        return threadSocketTags.get().statsUid;
+    }
+
+    @Override
+    public void tag(FileDescriptor fd) throws SocketException {
+        final SocketTags options = threadSocketTags.get();
+        if (LOGD) {
+            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
+                    + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
+        }
+        if (options.statsTag == -1) {
+            StrictMode.noteUntaggedSocket();
+        }
+        // TODO: skip tagging when options would be no-op
+        tagSocketFd(fd, options.statsTag, options.statsUid);
+    }
+
+    private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
+        if (tag == -1 && uid == -1) return;
+
+        final int errno = native_tagSocketFd(fd, tag, uid);
+        if (errno < 0) {
+            Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
+                    + tag + ", "
+                    + uid + ") failed with errno" + errno);
+        }
+    }
+
+    @Override
+    public void untag(FileDescriptor fd) throws SocketException {
+        if (LOGD) {
+            Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
+        }
+        unTagSocketFd(fd);
+    }
+
+    private void unTagSocketFd(FileDescriptor fd) {
+        final SocketTags options = threadSocketTags.get();
+        if (options.statsTag == -1 && options.statsUid == -1) return;
+
+        final int errno = native_untagSocketFd(fd);
+        if (errno < 0) {
+            Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
+        }
+    }
+
+    public static class SocketTags {
+        public int statsTag = -1;
+        public int statsUid = -1;
+    }
+
+    public static void setKernelCounterSet(int uid, int counterSet) {
+        final int errno = native_setCounterSet(counterSet, uid);
+        if (errno < 0) {
+            Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
+                    + errno);
+        }
+    }
+
+    public static void resetKernelUidStats(int uid) {
+        int errno = native_deleteTagData(0, uid);
+        if (errno < 0) {
+            Log.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
+        }
+    }
+
+    /**
+     * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
+     * format like {@code 0x7fffffff00000000}.
+     */
+    public static int kernelToTag(String string) {
+        int length = string.length();
+        if (length > 10) {
+            return Long.decode(string.substring(0, length - 8)).intValue();
+        } else {
+            return 0;
+        }
+    }
+
+    private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
+    private static native int native_untagSocketFd(FileDescriptor fd);
+    private static native int native_setCounterSet(int uid, int counterSetNum);
+    private static native int native_deleteTagData(int tag, int uid);
+}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index b261e16..36dd200 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -66,6 +66,7 @@
 filegroup {
     name: "services.connectivity-ethernet-sources",
     srcs: [
+        "src/com/android/server/net/DelayedDiskWrite.java",
         "src/com/android/server/net/IpConfigStore.java",
     ],
     path: "src",
diff --git a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
index d1e432e..179d945 100644
--- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
@@ -1236,37 +1236,53 @@
         int callingUid = Binder.getCallingUid();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
         final int resourceId = mNextResourceId++;
-        FileDescriptor sockFd = null;
+
+        ParcelFileDescriptor pFd = null;
         try {
             if (!userRecord.mSocketQuotaTracker.isAvailable()) {
                 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
             }
 
-            sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-            mUidFdTagger.tag(sockFd, callingUid);
+            FileDescriptor sockFd = null;
+            try {
+                sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+                pFd = ParcelFileDescriptor.dup(sockFd);
+            } finally {
+                IoUtils.closeQuietly(sockFd);
+            }
 
+            mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
             // This code is common to both the unspecified and specified port cases
             Os.setsockoptInt(
-                    sockFd,
+                    pFd.getFileDescriptor(),
                     OsConstants.IPPROTO_UDP,
                     OsConstants.UDP_ENCAP,
                     OsConstants.UDP_ENCAP_ESPINUDP);
 
-            mNetd.ipSecSetEncapSocketOwner(new ParcelFileDescriptor(sockFd), callingUid);
+            mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
             if (port != 0) {
                 Log.v(TAG, "Binding to port " + port);
-                Os.bind(sockFd, INADDR_ANY, port);
+                Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
             } else {
-                port = bindToRandomPort(sockFd);
+                port = bindToRandomPort(pFd.getFileDescriptor());
             }
 
             userRecord.mEncapSocketRecords.put(
                     resourceId,
                     new RefcountedResource<EncapSocketRecord>(
-                            new EncapSocketRecord(resourceId, sockFd, port), binder));
-            return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
+                            new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
+                            binder));
+            return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
+                    pFd.getFileDescriptor());
         } catch (IOException | ErrnoException e) {
-            IoUtils.closeQuietly(sockFd);
+            try {
+                if (pFd != null) {
+                    pFd.close();
+                }
+            } catch (IOException ex) {
+                // Nothing can be done at this point
+                Log.e(TAG, "Failed to close pFd.");
+            }
         }
         // If we make it to here, then something has gone wrong and we couldn't open a socket.
         // The only reasonable condition that would cause that is resource unavailable.
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
new file mode 100644
index 0000000..35dc455
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * This class provides APIs to do a delayed data write to a given {@link OutputStream}.
+ */
+public class DelayedDiskWrite {
+    private static final String TAG = "DelayedDiskWrite";
+
+    private HandlerThread mDiskWriteHandlerThread;
+    private Handler mDiskWriteHandler;
+    /* Tracks multiple writes on the same thread */
+    private int mWriteSequence = 0;
+
+    /**
+     * Used to do a delayed data write to a given {@link OutputStream}.
+     */
+    public interface Writer {
+        /**
+         * write data to a given {@link OutputStream}.
+         */
+        void onWriteCalled(DataOutputStream out) throws IOException;
+    }
+
+    /**
+     * Do a delayed data write to a given output stream opened from filePath.
+     */
+    public void write(final String filePath, final Writer w) {
+        write(filePath, w, true);
+    }
+
+    /**
+     * Do a delayed data write to a given output stream opened from filePath.
+     */
+    public void write(final String filePath, final Writer w, final boolean open) {
+        if (TextUtils.isEmpty(filePath)) {
+            throw new IllegalArgumentException("empty file path");
+        }
+
+        /* Do a delayed write to disk on a separate handler thread */
+        synchronized (this) {
+            if (++mWriteSequence == 1) {
+                mDiskWriteHandlerThread = new HandlerThread("DelayedDiskWriteThread");
+                mDiskWriteHandlerThread.start();
+                mDiskWriteHandler = new Handler(mDiskWriteHandlerThread.getLooper());
+            }
+        }
+
+        mDiskWriteHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                doWrite(filePath, w, open);
+            }
+        });
+    }
+
+    private void doWrite(String filePath, Writer w, boolean open) {
+        DataOutputStream out = null;
+        try {
+            if (open) {
+                out = new DataOutputStream(new BufferedOutputStream(
+                        new FileOutputStream(filePath)));
+            }
+            w.onWriteCalled(out);
+        } catch (IOException e) {
+            loge("Error writing data file " + filePath);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (Exception e) { }
+            }
+
+            // Quit if no more writes sent
+            synchronized (this) {
+                if (--mWriteSequence == 0) {
+                    mDiskWriteHandler.getLooper().quit();
+                    mDiskWriteHandler = null;
+                    mDiskWriteHandlerThread = null;
+                }
+            }
+        }
+    }
+
+    private void loge(String s) {
+        Log.e(TAG, s);
+    }
+}
+
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
deleted file mode 100644
index 0e9a9da..0000000
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import android.annotation.NonNull;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-
-public abstract class NetworkStatsManagerInternal {
-    /** Return network layer usage total for traffic that matches template. */
-    public abstract long getNetworkTotalBytes(NetworkTemplate template, long start, long end);
-
-    /** Return network layer usage per-UID for traffic that matches template. */
-    public abstract NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end);
-
-    /** Mark given UID as being in foreground for stats purposes. */
-    public abstract void setUidForeground(int uid, boolean uidForeground);
-
-    /** Advise persistance threshold; may be overridden internally. */
-    public abstract void advisePersistThreshold(long thresholdBytes);
-
-    /** Force update of statistics. */
-    public abstract void forceUpdate();
-
-    /**
-     * Set the warning and limit to all registered custom network stats providers.
-     * Note that invocation of any interface will be sent to all providers.
-     */
-    public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
-            long limit);
-}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index ef84ce0..1105de3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -19,12 +19,16 @@
 import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.app.usage.NetworkStatsManager.PREFIX_DEV;
+import static android.app.usage.NetworkStatsManager.PREFIX_UID;
+import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
+import static android.app.usage.NetworkStatsManager.PREFIX_XT;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.IFACE_VT;
@@ -42,28 +46,11 @@
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
-import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.UID_TETHERING;
 import static android.net.TrafficStats.UNSUPPORTED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
-import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE;
-import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES;
-import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_UID_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_UID_ROTATE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -71,7 +58,7 @@
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
@@ -90,13 +77,13 @@
 import android.database.ContentObserver;
 import android.net.DataUsageRequest;
 import android.net.INetd;
-import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkIdentitySet;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
@@ -107,26 +94,26 @@
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
+import android.net.TetherStatsParcel;
+import android.net.TetheringManager;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
 import android.net.Uri;
 import android.net.netstats.provider.INetworkStatsProvider;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.net.netstats.provider.NetworkStatsProvider;
-import android.os.BestClock;
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -149,12 +136,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FileRotator;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BestClock;
 import com.android.net.module.util.BinderUtils;
 import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
-import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -207,8 +195,19 @@
 
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+      */
+    private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
+    private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+
+    // TODO: Replace the hardcoded string and move it into ConnectivitySettingsManager.
+    private static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
+            "netstats_combine_subtype_enabled";
+
     private final Context mContext;
-    private final INetworkManagementService mNetworkManager;
     private final NetworkStatsFactory mStatsFactory;
     private final AlarmManager mAlarmManager;
     private final Clock mClock;
@@ -223,6 +222,9 @@
     private final ContentObserver mContentObserver;
     private final ContentResolver mContentResolver;
 
+    protected INetd mNetd;
+    private final AlertObserver mAlertObserver = new AlertObserver();
+
     @VisibleForTesting
     public static final String ACTION_NETWORK_STATS_POLL =
             "com.android.server.action.NETWORK_STATS_POLL";
@@ -231,11 +233,6 @@
 
     private PendingIntent mPollIntent;
 
-    private static final String PREFIX_DEV = "dev";
-    private static final String PREFIX_XT = "xt";
-    private static final String PREFIX_UID = "uid";
-    private static final String PREFIX_UID_TAG = "uid_tag";
-
     /**
      * Settings that can be changed externally.
      */
@@ -245,9 +242,9 @@
         boolean getSampleEnabled();
         boolean getAugmentEnabled();
         /**
-         * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}.
-         * When disabled, mobile data is broken down by a granular subtype representative of the
-         * actual subtype. {@see NetworkTemplate#getCollapsedRatType}.
+         * When enabled, all mobile data is reported under {@link NetworkTemplate#NETWORK_TYPE_ALL}.
+         * When disabled, mobile data is broken down by a granular ratType representative of the
+         * actual ratType. {@see NetworkTemplate#getCollapsedRatType}.
          * Enabling this decreases the level of detail but saves performance, disk space and
          * amount of data logged.
          */
@@ -294,6 +291,9 @@
     /** Set of any ifaces associated with mobile networks since boot. */
     private volatile String[] mMobileIfaces = new String[0];
 
+    /** Set of any ifaces associated with wifi networks since boot. */
+    private volatile String[] mWifiIfaces = new String[0];
+
     /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
     @GuardedBy("mStatsLock")
     private Network[] mDefaultNetworks = new Network[0];
@@ -354,6 +354,9 @@
     @NonNull
     private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
 
+    @NonNull
+    private final LocationPermissionChecker mLocationPermissionChecker;
+
     private static @NonNull File getDefaultSystemDir() {
         return new File(Environment.getDataDirectory(), "system");
     }
@@ -405,20 +408,20 @@
         }
     }
 
-    public static NetworkStatsService create(Context context,
-                INetworkManagementService networkManager) {
+    /** Creates a new NetworkStatsService */
+    public static NetworkStatsService create(Context context) {
         AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         final INetd netd = INetd.Stub.asInterface(
                 (IBinder) context.getSystemService(Context.NETD_SERVICE));
-        final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+        final NetworkStatsService service = new NetworkStatsService(context,
+                INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
                 alarmManager, wakeLock, getDefaultClock(),
-                new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
+                new DefaultNetworkStatsSettings(), new NetworkStatsFactory(netd),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
-        service.registerLocalService();
 
         return service;
     }
@@ -426,14 +429,12 @@
     // This must not be called outside of tests, even within the same package, as this constructor
     // does not register the local service. Use the create() helper above.
     @VisibleForTesting
-    NetworkStatsService(Context context, INetworkManagementService networkManager,
-            AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
-            NetworkStatsSettings settings, NetworkStatsFactory factory,
-            NetworkStatsObservers statsObservers, File systemDir, File baseDir,
-            @NonNull Dependencies deps) {
+    NetworkStatsService(Context context, INetd netd, AlarmManager alarmManager,
+            PowerManager.WakeLock wakeLock, Clock clock, NetworkStatsSettings settings,
+            NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
+            File baseDir, @NonNull Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing Context");
-        mNetworkManager = Objects.requireNonNull(networkManager,
-                "missing INetworkManagementService");
+        mNetd = Objects.requireNonNull(netd, "missing Netd");
         mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
         mClock = Objects.requireNonNull(clock, "missing Clock");
         mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
@@ -448,10 +449,11 @@
         handlerThread.start();
         mHandler = new NetworkStatsHandler(handlerThread.getLooper());
         mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
-                mHandler.getLooper(), new HandlerExecutor(mHandler), this);
+                (command) -> mHandler.post(command) , this);
         mContentResolver = mContext.getContentResolver();
         mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
                 mNetworkStatsSubscriptionsMonitor);
+        mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
     }
 
     /**
@@ -474,11 +476,10 @@
          */
         @NonNull
         public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(@NonNull Context context,
-                @NonNull Looper looper, @NonNull Executor executor,
-                @NonNull NetworkStatsService service) {
+                @NonNull Executor executor, @NonNull NetworkStatsService service) {
             // TODO: Update RatType passively in NSS, instead of querying into the monitor
             //  when notifyNetworkStatus.
-            return new NetworkStatsSubscriptionsMonitor(context, looper, executor,
+            return new NetworkStatsSubscriptionsMonitor(context, executor,
                     (subscriberId, type) -> service.handleOnCollapsedRatTypeChanged());
         }
 
@@ -500,11 +501,33 @@
                 }
             };
         }
+
+        /**
+         * @see LocationPermissionChecker
+         */
+        public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+            return new LocationPermissionChecker(context);
+        }
     }
 
-    private void registerLocalService() {
-        LocalServices.addService(NetworkStatsManagerInternal.class,
-                new NetworkStatsManagerInternalImpl());
+    /**
+     * Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
+     */
+    @VisibleForTesting
+    public class AlertObserver extends BaseNetdUnsolicitedEventListener {
+        @Override
+        public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) {
+            PermissionUtils.enforceNetworkStackPermission(mContext);
+
+            if (LIMIT_GLOBAL_ALERT.equals(alertName)) {
+                // kick off background poll to collect network stats unless there is already
+                // such a call pending; UID stats are handled during normal polling interval.
+                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+                            mSettings.getPollDelay());
+                }
+            }
+        }
     }
 
     public void systemReady() {
@@ -531,8 +554,9 @@
         }
 
         // watch for tethering changes
-        final IntentFilter tetherFilter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
-        mContext.registerReceiver(mTetherReceiver, tetherFilter, null, mHandler);
+        final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
+        tetheringManager.registerTetheringEventCallback(
+                (command) -> mHandler.post(command), mTetherListener);
 
         // listen for periodic polling events
         final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -551,9 +575,9 @@
         mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
 
         try {
-            mNetworkManager.registerObserver(mAlertObserver);
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
+            mNetd.registerUnsolicitedEventListener(mAlertObserver);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Log.wtf(TAG, "Error registering event listener :", e);
         }
 
         //  schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
@@ -566,13 +590,13 @@
                 mSettings.getPollInterval(), pollIntent);
 
         mContentResolver.registerContentObserver(Settings.Global
-                .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED),
+                .getUriFor(NETSTATS_COMBINE_SUBTYPE_ENABLED),
                         false /* notifyForDescendants */, mContentObserver);
 
         // Post a runnable on handler thread to call onChange(). It's for getting current value of
         // NETSTATS_COMBINE_SUBTYPE_ENABLED to decide start or stop monitoring RAT type changes.
         mHandler.post(() -> mContentObserver.onChange(false, Settings.Global
-                .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED)));
+                .getUriFor(NETSTATS_COMBINE_SUBTYPE_ENABLED)));
 
         registerGlobalAlert();
     }
@@ -588,7 +612,8 @@
 
     @GuardedBy("mStatsLock")
     private void shutdownLocked() {
-        mContext.unregisterReceiver(mTetherReceiver);
+        final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
+        tetheringManager.unregisterTetheringEventCallback(mTetherListener);
         mContext.unregisterReceiver(mPollReceiver);
         mContext.unregisterReceiver(mRemovedReceiver);
         mContext.unregisterReceiver(mUserReceiver);
@@ -640,13 +665,13 @@
     }
 
     /**
-     * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+     * Register for a global alert that is delivered through {@link AlertObserver}
      * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
      * been transferred.
      */
     private void registerGlobalAlert() {
         try {
-            mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
+            mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes);
         } catch (IllegalStateException e) {
             Log.w(TAG, "problem registering for global alert: " + e);
         } catch (RemoteException e) {
@@ -682,12 +707,25 @@
         return now - lastCallTime < POLL_RATE_LIMIT_MS;
     }
 
-    private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
+    private int restrictFlagsForCaller(int flags) {
+        // All non-privileged callers are not allowed to turn off POLL_ON_OPEN.
+        final boolean isPrivileged = PermissionUtils.checkAnyPermissionOf(mContext,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                android.Manifest.permission.NETWORK_STACK);
+        if (!isPrivileged) {
+            flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN;
+        }
+        // Non-system uids are rate limited for POLL_ON_OPEN.
         final int callingUid = Binder.getCallingUid();
-        final int usedFlags = isRateLimitedForPoll(callingUid)
+        flags = isRateLimitedForPoll(callingUid)
                 ? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
                 : flags;
-        if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
+        return flags;
+    }
+
+    private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
+        final int restrictedFlags = restrictFlagsForCaller(flags);
+        if ((restrictedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
                 | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -701,7 +739,7 @@
         // for its lifetime; when caller closes only weak references remain.
 
         return new INetworkStatsSession.Stub() {
-            private final int mCallingUid = callingUid;
+            private final int mCallingUid = Binder.getCallingUid();
             private final String mCallingPackage = callingPackage;
             private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel(
                     callingPackage);
@@ -735,26 +773,41 @@
             @Override
             public NetworkStats getDeviceSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
-                return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
-                        mCallingUid);
+                enforceTemplatePermissions(template, callingPackage);
+                return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+                        mAccessLevel, mCallingUid);
             }
 
             @Override
             public NetworkStats getSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
-                return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
-                        mCallingUid);
+                enforceTemplatePermissions(template, callingPackage);
+                return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+                        mAccessLevel, mCallingUid);
+            }
+
+            // TODO: Remove this after all callers are removed.
+            @Override
+            public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
+                enforceTemplatePermissions(template, callingPackage);
+                return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+                        mAccessLevel, mCallingUid, Long.MIN_VALUE, Long.MAX_VALUE);
             }
 
             @Override
-            public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
-                return internalGetHistoryForNetwork(template, usedFlags, fields, mAccessLevel,
-                        mCallingUid);
+            public NetworkStatsHistory getHistoryIntervalForNetwork(NetworkTemplate template,
+                    int fields, long start, long end) {
+                enforceTemplatePermissions(template, callingPackage);
+                // TODO(b/200768422): Redact returned history if the template is location
+                //  sensitive but the caller is not privileged.
+                return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+                        mAccessLevel, mCallingUid, start, end);
             }
 
             @Override
             public NetworkStats getSummaryForAllUid(
                     NetworkTemplate template, long start, long end, boolean includeTags) {
+                enforceTemplatePermissions(template, callingPackage);
                 try {
                     final NetworkStats stats = getUidComplete()
                             .getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -765,8 +818,19 @@
                     }
                     return stats;
                 } catch (NullPointerException e) {
-                    // TODO: Track down and fix the cause of this crash and remove this catch block.
-                    Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+                    throw e;
+                }
+            }
+
+            @Override
+            public NetworkStats getTaggedSummaryForAllUid(
+                    NetworkTemplate template, long start, long end) {
+                enforceTemplatePermissions(template, callingPackage);
+                try {
+                    final NetworkStats tagStats = getUidTagComplete()
+                            .getSummary(template, start, end, mAccessLevel, mCallingUid);
+                    return tagStats;
+                } catch (NullPointerException e) {
                     throw e;
                 }
             }
@@ -774,6 +838,7 @@
             @Override
             public NetworkStatsHistory getHistoryForUid(
                     NetworkTemplate template, int uid, int set, int tag, int fields) {
+                enforceTemplatePermissions(template, callingPackage);
                 // NOTE: We don't augment UID-level statistics
                 if (tag == TAG_NONE) {
                     return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -788,6 +853,9 @@
             public NetworkStatsHistory getHistoryIntervalForUid(
                     NetworkTemplate template, int uid, int set, int tag, int fields,
                     long start, long end) {
+                enforceTemplatePermissions(template, callingPackage);
+                // TODO(b/200768422): Redact returned history if the template is location
+                //  sensitive but the caller is not privileged.
                 // NOTE: We don't augment UID-level statistics
                 if (tag == TAG_NONE) {
                     return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -809,6 +877,26 @@
         };
     }
 
+    private void enforceTemplatePermissions(@NonNull NetworkTemplate template,
+            @NonNull String callingPackage) {
+        // For a template with wifi network keys, it is possible for a malicious
+        // client to track the user locations via querying data usage. Thus, enforce
+        // fine location permission check.
+        if (!template.getWifiNetworkKeys().isEmpty()) {
+            final boolean canAccessFineLocation = mLocationPermissionChecker
+                    .checkCallersLocationPermission(callingPackage,
+                    null /* featureId */,
+                            Binder.getCallingUid(),
+                            false /* coarseForTargetSdkLessThanQ */,
+                            null /* message */);
+            if (!canAccessFineLocation) {
+                throw new SecurityException("Access fine location is required when querying"
+                        + " with wifi network keys, make sure the app has the necessary"
+                        + "permissions and the location toggle is on.");
+            }
+        }
+    }
+
     private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
         return NetworkStatsAccess.checkAccessLevel(
                 mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
@@ -826,7 +914,7 @@
             if (LOGD) Log.d(TAG, "Resolving plan for " + template);
             final long token = Binder.clearCallingIdentity();
             try {
-                plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+                plan = mContext.getSystemService(NetworkPolicyManager.class)
                         .getSubscriptionPlan(template);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -845,7 +933,7 @@
         // We've been using pure XT stats long enough that we no longer need to
         // splice DEV and XT together.
         final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL,
-                accessLevel, callingUid);
+                accessLevel, callingUid, start, end);
 
         final long now = System.currentTimeMillis();
         final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
@@ -862,14 +950,14 @@
      * appropriate.
      */
     private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template,
-            int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
+            int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid,
+            long start, long end) {
         // We've been using pure XT stats long enough that we no longer need to
         // splice DEV and XT together.
         final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags);
         synchronized (mStatsLock) {
             return mXtStatsCached.getHistory(template, augmentPlan,
-                    UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
-                    accessLevel, callingUid);
+                    UID_ALL, SET_ALL, TAG_NONE, fields, start, end, accessLevel, callingUid);
         }
     }
 
@@ -920,10 +1008,15 @@
     }
 
     @Override
-    public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+    public NetworkStats getUidStatsForTransport(int transport) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         try {
+            final String[] relevantIfaces =
+                    transport == TRANSPORT_WIFI ? mWifiIfaces : mMobileIfaces;
+            // TODO(b/215633405) : mMobileIfaces and mWifiIfaces already contain the stacked
+            // interfaces, so this is not useful, remove it.
             final String[] ifacesToQuery =
-                    mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
+                    mStatsFactory.augmentWithStackedInterfaces(relevantIfaces);
             return getNetworkStatsUidDetail(ifacesToQuery);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error compiling UID stats", e);
@@ -964,7 +1057,8 @@
     }
 
     @VisibleForTesting
-    void setUidForeground(int uid, boolean uidForeground) {
+    public void setUidForeground(int uid, boolean uidForeground) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
             final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
@@ -1000,7 +1094,7 @@
 
     @Override
     public void forceUpdate() {
-        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1010,7 +1104,9 @@
         }
     }
 
-    private void advisePersistThreshold(long thresholdBytes) {
+    /** Advise persistence threshold; may be overridden internally. */
+    public void advisePersistThreshold(long thresholdBytes) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         // clamp threshold into safe range
         mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
                 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
@@ -1152,14 +1248,15 @@
     }
 
     /**
-     * Receiver that watches for {@link Tethering} to claim interface pairs.
+     * Listener that watches for {@link TetheringManager} to claim interface pairs.
      */
-    private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            performPoll(FLAG_PERSIST_NETWORK);
-        }
-    };
+    private final TetheringManager.TetheringEventCallback mTetherListener =
+            new TetheringManager.TetheringEventCallback() {
+                @Override
+                public void onUpstreamChanged(@Nullable Network network) {
+                    performPoll(FLAG_PERSIST_NETWORK);
+                }
+            };
 
     private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
         @Override
@@ -1224,26 +1321,6 @@
     };
 
     /**
-     * Observer that watches for {@link INetworkManagementService} alerts.
-     */
-    private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
-        @Override
-        public void limitReached(String limitName, String iface) {
-            // only someone like NMS should be calling us
-            PermissionUtils.enforceNetworkStackPermission(mContext);
-
-            if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
-                // kick off background poll to collect network stats unless there is already
-                // such a call pending; UID stats are handled during normal polling interval.
-                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
-                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
-                            mSettings.getPollDelay());
-                }
-            }
-        }
-    };
-
-    /**
      * Handle collapsed RAT type changed event.
      */
     @VisibleForTesting
@@ -1298,16 +1375,18 @@
 
         final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
         final ArraySet<String> mobileIfaces = new ArraySet<>();
+        final ArraySet<String> wifiIfaces = new ArraySet<>();
         for (NetworkStateSnapshot snapshot : snapshots) {
             final int displayTransport =
                     getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
             final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
+            final boolean isWifi = (NetworkCapabilities.TRANSPORT_WIFI == displayTransport);
             final boolean isDefault = CollectionUtils.contains(
                     mDefaultNetworks, snapshot.getNetwork());
-            final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
-                    : getSubTypeForStateSnapshot(snapshot);
+            final int ratType = combineSubtypeEnabled ? NetworkTemplate.NETWORK_TYPE_ALL
+                    : getRatTypeForStateSnapshot(snapshot);
             final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
-                    isDefault, subType);
+                    isDefault, ratType);
 
             // Traffic occurring on the base interface is always counted for
             // both total usage and UID details.
@@ -1322,12 +1401,12 @@
                 // VT is considered always metered in framework's layer. If VT is not metered
                 // per carrier's policy, modem will report 0 usage for VT calls.
                 if (snapshot.getNetworkCapabilities().hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+                        NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.isMetered()) {
 
                     // Copy the identify from IMS one but mark it as metered.
                     NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
-                            ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
-                            ident.getRoaming(), true /* metered */,
+                            ident.getRatType(), ident.getSubscriberId(), ident.getWifiNetworkKey(),
+                            ident.isRoaming(), true /* metered */,
                             true /* onDefaultNetwork */, ident.getOemManaged());
                     final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
                     findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
@@ -1337,6 +1416,9 @@
                 if (isMobile) {
                     mobileIfaces.add(baseIface);
                 }
+                if (isWifi) {
+                    wifiIfaces.add(baseIface);
+                }
             }
 
             // Traffic occurring on stacked interfaces is usually clatd.
@@ -1378,6 +1460,9 @@
                     if (isMobile) {
                         mobileIfaces.add(iface);
                     }
+                    if (isWifi) {
+                        wifiIfaces.add(iface);
+                    }
 
                     mStatsFactory.noteStackedIface(iface, baseIface);
                 }
@@ -1385,11 +1470,16 @@
         }
 
         mMobileIfaces = mobileIfaces.toArray(new String[0]);
+        mWifiIfaces = wifiIfaces.toArray(new String[0]);
         // TODO (b/192758557): Remove debug log.
         if (CollectionUtils.contains(mMobileIfaces, null)) {
             throw new NullPointerException(
                     "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
         }
+        if (CollectionUtils.contains(mWifiIfaces, null)) {
+            throw new NullPointerException(
+                    "null element in mWifiIfaces: " + Arrays.toString(mWifiIfaces));
+        }
     }
 
     private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
@@ -1407,11 +1497,11 @@
     }
 
     /**
-     * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through
+     * For networks with {@code TRANSPORT_CELLULAR}, get ratType that was obtained through
      * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
      * transport types do not actually fill this value.
      */
-    private int getSubTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) {
+    private int getRatTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) {
         if (!state.getNetworkCapabilities().hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
             return 0;
         }
@@ -1608,7 +1698,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsMobileSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_MOBILE_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1620,7 +1710,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsWifiSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_WIFI_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1666,52 +1756,19 @@
         removeUidsLocked(CollectionUtils.toIntArray(uids));
     }
 
-    private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
-        @Override
-        public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
-            try {
-                return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+    public void setStatsProviderWarningAndLimitAsync(
+            @NonNull String iface, long warning, long limit) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        if (LOGV) {
+            Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+                    + iface + "," + warning + "," + limit + ")");
         }
-
-        @Override
-        public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
-            try {
-                return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
-        }
-
-        @Override
-        public void setUidForeground(int uid, boolean uidForeground) {
-            NetworkStatsService.this.setUidForeground(uid, uidForeground);
-        }
-
-        @Override
-        public void advisePersistThreshold(long thresholdBytes) {
-            NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
-        }
-
-        @Override
-        public void forceUpdate() {
-            NetworkStatsService.this.forceUpdate();
-        }
-
-        @Override
-        public void setStatsProviderWarningAndLimitAsync(
-                @NonNull String iface, long warning, long limit) {
-            if (LOGV) {
-                Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
-                        + iface + "," + warning + "," + limit + ")");
-            }
-            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
-                    warning, limit));
-        }
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
+                warning, limit));
     }
 
     @Override
@@ -1954,13 +2011,29 @@
      */
     // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
     //  tethering stats.
-    private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+    private @NonNull NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+         // We only need to return per-UID stats. Per-device stats are already counted by
+        // interface counters.
+        if (how != STATS_PER_UID) {
+            return new NetworkStats(SystemClock.elapsedRealtime(), 0);
+        }
+
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
         try {
-            return mNetworkManager.getNetworkStatsTethering(how);
+            final TetherStatsParcel[] tetherStatsParcels = mNetd.tetherGetStats();
+            for (TetherStatsParcel tetherStats : tetherStatsParcels) {
+                try {
+                    stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING,
+                            SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets,
+                            tetherStats.txBytes, tetherStats.txPackets, 0L));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    throw new IllegalStateException("invalid tethering stats " + e);
+                }
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem reading network stats", e);
-            return new NetworkStats(0L, 10);
         }
+        return stats;
     }
 
     // TODO: It is copied from ConnectivityService, consider refactor these check permission
@@ -1999,10 +2072,12 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         Objects.requireNonNull(provider, "provider is null");
         Objects.requireNonNull(tag, "tag is null");
+        final NetworkPolicyManager netPolicyManager = mContext
+                .getSystemService(NetworkPolicyManager.class);
         try {
             NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
                     tag, provider, mStatsProviderSem, mAlertObserver,
-                    mStatsProviderCbList);
+                    mStatsProviderCbList, netPolicyManager);
             mStatsProviderCbList.add(callback);
             Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
                     + getCallingUid() + "/" + getCallingPid());
@@ -2042,8 +2117,9 @@
 
         @NonNull final INetworkStatsProvider mProvider;
         @NonNull private final Semaphore mSemaphore;
-        @NonNull final INetworkManagementEventObserver mAlertObserver;
+        @NonNull final AlertObserver mAlertObserver;
         @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+        @NonNull final NetworkPolicyManager mNetworkPolicyManager;
 
         @NonNull private final Object mProviderStatsLock = new Object();
 
@@ -2056,8 +2132,9 @@
         NetworkStatsProviderCallbackImpl(
                 @NonNull String tag, @NonNull INetworkStatsProvider provider,
                 @NonNull Semaphore semaphore,
-                @NonNull INetworkManagementEventObserver alertObserver,
-                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
+                @NonNull AlertObserver alertObserver,
+                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList,
+                @NonNull NetworkPolicyManager networkPolicyManager)
                 throws RemoteException {
             mTag = tag;
             mProvider = provider;
@@ -2065,6 +2142,7 @@
             mSemaphore = semaphore;
             mAlertObserver = alertObserver;
             mStatsProviderCbList = cbList;
+            mNetworkPolicyManager = networkPolicyManager;
         }
 
         @NonNull
@@ -2104,15 +2182,14 @@
             // This binder object can only have been obtained by a process that holds
             // NETWORK_STATS_PROVIDER. Thus, no additional permission check is required.
             BinderUtils.withCleanCallingIdentity(() ->
-                    mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
+                    mAlertObserver.onQuotaLimitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
         }
 
         @Override
         public void notifyWarningOrLimitReached() {
             Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
             BinderUtils.withCleanCallingIdentity(() ->
-                    LocalServices.getService(NetworkPolicyManagerInternal.class)
-                            .onStatsProviderWarningOrLimitReached(mTag));
+                    mNetworkPolicyManager.notifyStatsProviderWarningOrLimitReached());
         }
 
         @Override
@@ -2171,24 +2248,11 @@
      * {@link android.provider.Settings.Global}.
      */
     private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
-        private final ContentResolver mResolver;
-
-        public DefaultNetworkStatsSettings(Context context) {
-            mResolver = Objects.requireNonNull(context.getContentResolver());
-            // TODO: adjust these timings for production builds
-        }
-
-        private long getGlobalLong(String name, long def) {
-            return Settings.Global.getLong(mResolver, name, def);
-        }
-        private boolean getGlobalBoolean(String name, boolean def) {
-            final int defInt = def ? 1 : 0;
-            return Settings.Global.getInt(mResolver, name, defInt) != 0;
-        }
+        DefaultNetworkStatsSettings() {}
 
         @Override
         public long getPollInterval() {
-            return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
+            return 30 * MINUTE_IN_MILLIS;
         }
         @Override
         public long getPollDelay() {
@@ -2196,25 +2260,23 @@
         }
         @Override
         public long getGlobalAlertBytes(long def) {
-            return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
+            return def;
         }
         @Override
         public boolean getSampleEnabled() {
-            return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true);
+            return true;
         }
         @Override
         public boolean getAugmentEnabled() {
-            return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true);
+            return true;
         }
         @Override
         public boolean getCombineSubtypeEnabled() {
-            return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false);
+            return false;
         }
         @Override
         public Config getDevConfig() {
-            return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
-                    getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
-                    getGlobalLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
+            return new Config(HOUR_IN_MILLIS, 15 * DAY_IN_MILLIS, 90 * DAY_IN_MILLIS);
         }
         @Override
         public Config getXtConfig() {
@@ -2222,31 +2284,27 @@
         }
         @Override
         public Config getUidConfig() {
-            return new Config(getGlobalLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
-                    getGlobalLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
-                    getGlobalLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
+            return new Config(2 * HOUR_IN_MILLIS, 15 * DAY_IN_MILLIS, 90 * DAY_IN_MILLIS);
         }
         @Override
         public Config getUidTagConfig() {
-            return new Config(getGlobalLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
-                    getGlobalLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
-                    getGlobalLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
+            return new Config(2 * HOUR_IN_MILLIS, 5 * DAY_IN_MILLIS, 15 * DAY_IN_MILLIS);
         }
         @Override
         public long getDevPersistBytes(long def) {
-            return getGlobalLong(NETSTATS_DEV_PERSIST_BYTES, def);
+            return def;
         }
         @Override
         public long getXtPersistBytes(long def) {
-            return getDevPersistBytes(def);
+            return def;
         }
         @Override
         public long getUidPersistBytes(long def) {
-            return getGlobalLong(NETSTATS_UID_PERSIST_BYTES, def);
+            return def;
         }
         @Override
         public long getUidTagPersistBytes(long def) {
-            return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
+            return def;
         }
     }
 
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 93d0ae7..4875f1c 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -18,15 +18,16 @@
 
 import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA;
 import static android.net.NetworkTemplate.getCollapsedRatType;
+import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED;
+import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.os.Looper;
 import android.telephony.Annotation;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -64,7 +65,7 @@
     private final Delegate mDelegate;
 
     /**
-     * Receivers that watches for {@link ServiceState} changes for each subscription, to
+     * Receivers that watches for {@link TelephonyDisplayInfo} changes for each subscription, to
      * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
      */
     @NonNull
@@ -79,9 +80,9 @@
     @NonNull
     private final Executor mExecutor;
 
-    NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Looper looper,
+    NetworkStatsSubscriptionsMonitor(@NonNull Context context,
             @NonNull Executor executor, @NonNull Delegate delegate) {
-        super(looper);
+        super();
         mSubscriptionManager = (SubscriptionManager) context.getSystemService(
                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
         mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -101,7 +102,8 @@
         // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
         final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
         for (final int subId : newSubs) {
-            final String subscriberId = mTeleManager.getSubscriberId(subId);
+            final String subscriberId =
+                    mTeleManager.createForSubscriptionId(subId).getSubscriberId();
             if (!TextUtils.isEmpty(subscriberId)) {
                 filteredNewSubs.add(new Pair(subId, subscriberId));
             }
@@ -115,13 +117,12 @@
                 continue;
             }
 
-            final RatTypeListener listener =
-                    new RatTypeListener(mExecutor, this, sub.first, sub.second);
+            final RatTypeListener listener = new RatTypeListener(this, sub.first, sub.second);
             mRatListeners.add(listener);
 
             // Register listener to the telephony manager that associated with specific sub.
             mTeleManager.createForSubscriptionId(sub.first)
-                    .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+                    .registerTelephonyCallback(mExecutor, listener);
             Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
         }
 
@@ -175,7 +176,7 @@
 
     private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
         mTeleManager.createForSubscriptionId(listener.mSubId)
-                .listen(listener, PhoneStateListener.LISTEN_NONE);
+                .unregisterTelephonyCallback(listener);
         Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId);
         mRatListeners.remove(listener);
 
@@ -185,7 +186,8 @@
                 listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
     }
 
-    static class RatTypeListener extends PhoneStateListener {
+    static class RatTypeListener extends TelephonyCallback
+            implements TelephonyCallback.DisplayInfoListener {
         // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
         @NonNull
         private final int mSubId;
@@ -199,29 +201,27 @@
         @NonNull
         private final NetworkStatsSubscriptionsMonitor mMonitor;
 
-        RatTypeListener(@NonNull Executor executor,
-                @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
+        RatTypeListener(@NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
                 @NonNull String subscriberId) {
-            super(executor);
             mSubId = subId;
             mSubscriberId = subscriberId;
             mMonitor = monitor;
         }
 
         @Override
-        public void onServiceStateChanged(@NonNull ServiceState ss) {
+        public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) {
             // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
             // would report RAT = 5G_NR.
             // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
             // network allocates a secondary 5G cell so telephony reports RAT = LTE along with
             // NR state as connected. In such case, attributes the data usage to NR.
             // See b/160727498.
-            final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE
-                    || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA)
-                    && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+            final boolean is5GNsa = displayInfo.getNetworkType() == NETWORK_TYPE_LTE
+                    && (displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_NSA
+                    || displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
 
             final int networkType =
-                    (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType());
+                    (is5GNsa ? NETWORK_TYPE_5G_NSA : displayInfo.getNetworkType());
             final int collapsedRatType = getCollapsedRatType(networkType);
             if (collapsedRatType == mLastCollapsedRatType) return;
 
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index a3eb0ecc..ce58ff6 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -236,7 +236,8 @@
                 root.flags |= Root.FLAG_REMOVABLE_USB;
             }
 
-            if (volume.getType() != VolumeInfo.TYPE_EMULATED) {
+            if (volume.getType() != VolumeInfo.TYPE_EMULATED
+                    && volume.getType() != VolumeInfo.TYPE_STUB) {
                 root.flags |= Root.FLAG_SUPPORTS_EJECT;
             }
 
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 7a239af..068074a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -44,6 +44,7 @@
 
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /** Basic fused location provider implementation. */
 public class FusedLocationProvider extends LocationProviderBase {
@@ -69,6 +70,12 @@
     private final BroadcastReceiver mUserChangeReceiver;
 
     @GuardedBy("mLock")
+    boolean mGpsPresent;
+
+    @GuardedBy("mLock")
+    boolean mNlpPresent;
+
+    @GuardedBy("mLock")
     private ProviderRequest mRequest;
 
     @GuardedBy("mLock")
@@ -119,19 +126,28 @@
 
     @Override
     public void onFlush(OnFlushCompleteCallback callback) {
-        OnFlushCompleteCallback wrapper = new OnFlushCompleteCallback() {
-            private int mFlushCount = 2;
+        synchronized (mLock) {
+            AtomicInteger flushCount = new AtomicInteger(0);
+            if (mGpsPresent) {
+                flushCount.incrementAndGet();
+            }
+            if (mNlpPresent) {
+                flushCount.incrementAndGet();
+            }
 
-            @Override
-            public void onFlushComplete() {
-                if (--mFlushCount == 0) {
+            OnFlushCompleteCallback wrapper = () -> {
+                if (flushCount.decrementAndGet() == 0) {
                     callback.onFlushComplete();
                 }
-            }
-        };
+            };
 
-        mGpsListener.flush(wrapper);
-        mNetworkListener.flush(wrapper);
+            if (mGpsPresent) {
+                mGpsListener.flush(wrapper);
+            }
+            if (mNlpPresent) {
+                mNetworkListener.flush(wrapper);
+            }
+        }
     }
 
     @Override
@@ -139,9 +155,19 @@
 
     @GuardedBy("mLock")
     private void updateRequirementsLocked() {
-        long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getIntervalMillis()
-                : INTERVAL_DISABLED;
-        long networkInterval = mRequest.getIntervalMillis();
+        // it's possible there might be race conditions on device start where a provider doesn't
+        // appear to be present yet, but once a provider is present it shouldn't go away.
+        if (!mGpsPresent) {
+            mGpsPresent = mLocationManager.hasProvider(GPS_PROVIDER);
+        }
+        if (!mNlpPresent) {
+            mNlpPresent = mLocationManager.hasProvider(NETWORK_PROVIDER);
+        }
+
+        long gpsInterval =
+                mGpsPresent && (!mNlpPresent || mRequest.getQuality() < QUALITY_LOW_POWER)
+                        ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
+        long networkInterval = mNlpPresent ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
 
         mGpsListener.resetProviderRequest(gpsInterval);
         mNetworkListener.resetProviderRequest(networkInterval);
diff --git a/packages/PrintSpooler/Android.bp b/packages/PrintSpooler/Android.bp
index 772c69f..6af3c66 100644
--- a/packages/PrintSpooler/Android.bp
+++ b/packages/PrintSpooler/Android.bp
@@ -34,18 +34,23 @@
 android_app {
     name: "PrintSpooler",
     defaults: ["platform_app_defaults"],
+    resource_dirs: [],
+    platform_apis: true,
+    jni_libs: ["libprintspooler_jni"],
+    static_libs: [
+        "PrintSpoolerLib",
+    ],
+}
 
+android_library {
+    name: "PrintSpoolerLib",
     resource_dirs: ["res"],
-
     srcs: [
         "src/**/*.java",
         "src/com/android/printspooler/renderer/IPdfRenderer.aidl",
         "src/com/android/printspooler/renderer/IPdfEditor.aidl",
     ],
-
     platform_apis: true,
-
-    jni_libs: ["libprintspooler_jni"],
     static_libs: [
         "android-support-v7-recyclerview",
         "android-support-compat",
@@ -55,4 +60,5 @@
         "android-support-fragment",
         "android-support-annotations",
     ],
+    manifest: "AndroidManifest.xml",
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index cf73aac..0c4cb8e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -314,16 +314,15 @@
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.string.print_select_printer: {
-                PrinterInfo printer = item.getIntent().getParcelableExtra(EXTRA_PRINTER);
-                onPrinterSelected(printer);
-            } return true;
-
-            case R.string.print_forget_printer: {
-                PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
-                mPrinterRegistry.forgetFavoritePrinter(printerId);
-            } return true;
+        final int itemId = item.getItemId();
+        if (itemId == R.string.print_select_printer) {
+            PrinterInfo printer = item.getIntent().getParcelableExtra(EXTRA_PRINTER);
+            onPrinterSelected(printer);
+            return true;
+        } else if (itemId == R.string.print_forget_printer) {
+            PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
+            mPrinterRegistry.forgetFavoritePrinter(printerId);
+            return true;
         }
         return false;
     }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index b266df5..fcf2282 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -49,6 +49,7 @@
         "SettingsLibTwoTargetPreference",
         "SettingsLibSettingsTransition",
         "SettingsLibActivityEmbedding",
+        "SettingsLibButtonPreference",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
new file mode 100644
index 0000000..39f804f
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibButtonPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.preference_preference",
+        "SettingsLibSettingsTheme",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ButtonPreference/AndroidManifest.xml b/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
new file mode 100644
index 0000000..2d35c331
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_background_material.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_background_material.xml
new file mode 100644
index 0000000..51ca4ac
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_background_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Used for the text of a bordered colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/colorButtonNormal" />
+    <item android:color="?android:attr/colorAccent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_text_material.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_text_material.xml
new file mode 100644
index 0000000..8dca4db
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_text_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Used for the text of a bordered colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/textColorPrimary" />
+    <item android:color="?android:attr/textColorPrimaryInverse" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_dark.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_dark.xml
new file mode 100644
index 0000000..1e930ea
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="@dimen/settingslib_highlight_alpha_material_dark"
+          android:color="@android:color/white" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_light.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_light.xml
new file mode 100644
index 0000000..378fc16
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="@dimen/settingslib_highlight_alpha_material_light"
+          android:color="@android:color/black" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/drawable/settingslib_btn_colored_material.xml b/packages/SettingsLib/ButtonPreference/res/drawable/settingslib_btn_colored_material.xml
new file mode 100644
index 0000000..bb0597d
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/drawable/settingslib_btn_colored_material.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="4dp"
+       android:insetTop="6dp"
+       android:insetRight="4dp"
+       android:insetBottom="6dp">
+    <ripple android:color="?attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle"
+                   android:tint="@color/settingslib_btn_colored_background_material">
+                <corners android:radius="2dp" />
+                <solid android:color="@android:color/white" />
+                <padding android:left="8dp"
+                         android:top="4dp"
+                         android:right="8dp"
+                         android:bottom="4dp" />
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
new file mode 100644
index 0000000..1ff0990
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <Button
+        android:id="@+id/settingslib_button"
+        android:drawablePadding="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:layout_marginStart="-4dp"
+        style="@style/SettingsLibButtonStyle" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/ButtonPreference/res/values-night/colors.xml b/packages/SettingsLib/ButtonPreference/res/values-night/colors.xml
new file mode 100644
index 0000000..6be7b93
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-night/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Material inverse ripple color, useful for inverted backgrounds. -->
+    <color name="settingslib_button_ripple">@color/settingslib_ripple_material_light</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v23/styles.xml b/packages/SettingsLib/ButtonPreference/res/values-v23/styles.xml
new file mode 100644
index 0000000..202645f
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-v23/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibButtonStyle" parent="android:Widget.Material.Button.Colored">
+        <item name="android:theme">@style/SettingsLibRoundedCornerThemeOverlay</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/settingslib_btn_colored_text_material</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v28/styles.xml b/packages/SettingsLib/ButtonPreference/res/values-v28/styles.xml
new file mode 100644
index 0000000..d8c6ac3f
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-v28/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibButtonStyle" parent="android:Widget.DeviceDefault.Button.Colored">
+        <item name="android:theme">@style/SettingsLibRoundedCornerThemeOverlay</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v31/styles.xml b/packages/SettingsLib/ButtonPreference/res/values-v31/styles.xml
new file mode 100644
index 0000000..12dcbbf
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-v31/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibRoundedCornerThemeOverlay">
+        <item name="android:buttonCornerRadius">24dp</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">16dp</item>
+        <item name="android:colorControlHighlight">@color/settingslib_button_ripple</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/res/values/attrs.xml b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
new file mode 100644
index 0000000..9a4312a
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <declare-styleable name="ButtonPreference">
+        <attr name="android:gravity" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/colors.xml b/packages/SettingsLib/ButtonPreference/res/values/colors.xml
new file mode 100644
index 0000000..45baeeb
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Material inverse ripple color, useful for inverted backgrounds. -->
+    <color name="settingslib_button_ripple">@color/settingslib_ripple_material_dark</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/dimens.xml b/packages/SettingsLib/ButtonPreference/res/values/dimens.xml
new file mode 100644
index 0000000..3d7831e8
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <item name="settingslib_highlight_alpha_material_light" format="float" type="dimen">0.10</item>
+    <item name="settingslib_highlight_alpha_material_dark" format="float" type="dimen">0.10</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/styles.xml b/packages/SettingsLib/ButtonPreference/res/values/styles.xml
new file mode 100644
index 0000000..3963732
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibRoundedCornerThemeOverlay">
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">16dp</item>
+        <item name="android:colorControlHighlight">@color/settingslib_button_ripple</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle" parent="android:Widget.Material.Button">
+        <item name="android:theme">@style/SettingsLibRoundedCornerThemeOverlay</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/settingslib_btn_colored_text_material</item>
+        <item name="android:background">@drawable/settingslib_btn_colored_material</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
new file mode 100644
index 0000000..56d2967
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.annotation.GravityInt;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference handled a button
+ */
+public class ButtonPreference extends Preference {
+
+    private static final int ICON_SIZE = 24;
+
+    private View.OnClickListener mClickListener;
+    private Button mButton;
+    private CharSequence mTitle;
+    private Drawable mIcon;
+    @GravityInt
+    private int mGravity;
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme, the supplied
+     * attribute set, and default style attribute.
+     *
+     * @param context      The Context the view is running in, through which it can
+     *                     access the current theme, resources, etc.
+     * @param attrs        The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     */
+    public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and the supplied
+     * attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs   The attributes of the XML tag that is inflating the view.
+     */
+    public ButtonPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, 0 /* defStyleAttr */);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and a customized view.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     */
+    public ButtonPreference(Context context) {
+        this(context, null);
+    }
+
+    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+        setLayoutResource(R.layout.settingslib_button_layout);
+
+        if (attrs != null) {
+            TypedArray a = context.obtainStyledAttributes(attrs,
+                    androidx.preference.R.styleable.Preference, defStyleAttr,
+                    0 /*defStyleRes*/);
+            mTitle = a.getText(
+                    androidx.preference.R.styleable.Preference_android_title);
+            mIcon = a.getDrawable(
+                    androidx.preference.R.styleable.Preference_android_icon);
+            a.recycle();
+
+            a = context.obtainStyledAttributes(attrs,
+                    R.styleable.ButtonPreference, defStyleAttr,
+                    0 /*defStyleRes*/);
+            mGravity = a.getInt(R.styleable.ButtonPreference_android_gravity, Gravity.START);
+            a.recycle();
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        mButton = (Button) holder.findViewById(R.id.settingslib_button);
+        setTitle(mTitle);
+        setIcon(mIcon);
+        setGravity(mGravity);
+        setOnClickListener(mClickListener);
+
+        if (mButton != null) {
+            final boolean selectable = isSelectable();
+            mButton.setFocusable(selectable);
+            mButton.setClickable(selectable);
+
+            mButton.setEnabled(isEnabled());
+        }
+
+        holder.setDividerAllowedAbove(false);
+        holder.setDividerAllowedBelow(false);
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitle = title;
+        if (mButton != null) {
+            mButton.setText(title);
+        }
+    }
+
+    @Override
+    public void setIcon(Drawable icon) {
+        mIcon = icon;
+        if (mButton == null || icon == null) {
+            return;
+        }
+        //get pixel from dp
+        int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, ICON_SIZE,
+                getContext().getResources().getDisplayMetrics());
+        icon.setBounds(0, 0, size, size);
+
+        //set drawableStart
+        mButton.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null/* top */, null/* end */,
+                null/* bottom */);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        if (mButton != null) {
+            mButton.setEnabled(enabled);
+        }
+    }
+
+    /**
+     * Return Button
+     */
+    public Button getButton() {
+        return mButton;
+    }
+
+
+    /**
+     * Set a listener for button click
+     */
+    public void setOnClickListener(View.OnClickListener listener) {
+        mClickListener = listener;
+        if (mButton != null) {
+            mButton.setOnClickListener(listener);
+        }
+    }
+
+    /**
+     * Set the gravity of button
+     *
+     * @param gravity The {@link Gravity} supported CENTER_HORIZONTAL
+     *                and the default value is START
+     */
+    public void setGravity(@GravityInt int gravity) {
+        if (gravity == Gravity.CENTER_HORIZONTAL || gravity == Gravity.CENTER_VERTICAL
+                || gravity == Gravity.CENTER) {
+            mGravity = Gravity.CENTER_HORIZONTAL;
+        } else {
+            mGravity = Gravity.START;
+        }
+
+        if (mButton != null) {
+            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mButton.getLayoutParams();
+            lp.gravity = mGravity;
+            mButton.setLayoutParams(lp);
+        }
+    }
+}
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index e586dbb..b127630 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -24,7 +24,6 @@
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:background="?android:attr/selectableItemBackground"
     android:orientation="vertical"
-    android:importantForAccessibility = "no"
     android:clipToPadding="false">
 
     <LinearLayout
diff --git a/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
index 990860ad..23192b6 100644
--- a/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout/preference_footer.xml
@@ -23,7 +23,6 @@
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:background="?android:attr/selectableItemBackground"
-    android:importantForAccessibility = "no"
     android:clipToPadding="false">
 
     <LinearLayout
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index fe7988f..e51bb45 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -61,6 +61,7 @@
         title.setMovementMethod(new LinkMovementMethod());
         title.setClickable(false);
         title.setLongClickable(false);
+        title.setFocusable(false);
         if (!TextUtils.isEmpty(mContentDescription)) {
             title.setContentDescription(mContentDescription);
         }
@@ -79,6 +80,7 @@
             if (!TextUtils.isEmpty(mLearnMoreContentDescription)) {
                 learnMore.setContentDescription(mLearnMoreContentDescription);
             }
+            learnMore.setFocusable(false);
         } else {
             learnMore.setVisibility(View.GONE);
         }
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index c206903..3f2b8ac 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -43,4 +43,8 @@
     <color name="settingslib_accent_primary_variant">@android:color/system_accent1_300</color>
 
     <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_50</color>
+
+    <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_200</color>
+
+    <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index 0401098..ec3c336 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -65,4 +65,8 @@
     <color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color>
 
     <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_900</color>
+
+    <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_700</color>
+
+    <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_600</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index 1c33f1a..11546c8 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -20,4 +20,9 @@
     <dimen name="app_icon_min_width">52dp</dimen>
     <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen>
     <dimen name="settingslib_dialogCornerRadius">28dp</dimen>
+
+    <!-- Left padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen>
+    <!-- Right padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 5800636..8e7226b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -17,13 +17,19 @@
 <resources>
     <style name="TextAppearance.PreferenceTitle.SettingsLib"
            parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
         <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20sp</item>
     </style>
 
+    <style name="TextAppearance.PreferenceSummary.SettingsLib"
+           parent="@android:style/TextAppearance.DeviceDefault.Small">
+        <item name="android:textColor">@color/settingslib_text_color_secondary_device_default</item>
+    </style>
+
     <style name="TextAppearance.CategoryTitle.SettingsLib"
            parent="@android:style/TextAppearance.DeviceDefault.Medium">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">@color/settingslib_text_color_preference_category_title</item>
         <item name="android:textSize">14sp</item>
     </style>
 
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 6bf288b..7bf75bc 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -19,8 +19,8 @@
     <!-- Only using in Settings application -->
     <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" >
         <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item>
-        <item name="android:listPreferredItemPaddingStart">24dp</item>
-        <item name="android:listPreferredItemPaddingLeft">24dp</item>
+        <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item>
+        <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item>
         <item name="android:listPreferredItemPaddingEnd">16dp</item>
         <item name="android:listPreferredItemPaddingRight">16dp</item>
         <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/config.xml b/packages/SettingsLib/SettingsTheme/res/values/config.xml
new file mode 100644
index 0000000..e73dcc0
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <bool name="settingslib_config_icon_space_reserved">true</bool>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
index 18af1f9..f7e0144 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
@@ -21,4 +21,11 @@
     <dimen name="app_icon_min_width">56dp</dimen>
     <dimen name="two_target_min_width">72dp</dimen>
     <dimen name="settingslib_dialogCornerRadius">8dp</dimen>
+
+    <!-- Left padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingStart">16dp</dimen>
+    <!-- Right padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingEnd">16dp</dimen>
+    <!-- Icon size of the preference -->
+    <dimen name="settingslib_preferenceIconSize">24dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
new file mode 100644
index 0000000..aaab0f0
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+<resources>
+    <style name="TextAppearance.PreferenceTitle.SettingsLib"
+           parent="@android:style/TextAppearance.Material.Subhead">
+    </style>
+
+    <style name="TextAppearance.PreferenceSummary.SettingsLib"
+           parent="@style/PreferenceSummaryTextStyle">
+    </style>
+
+    <style name="TextAppearance.CategoryTitle.SettingsLib"
+           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+    </style>
+</resources>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index f3fb48f..13a5caf 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vra elke keer"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Foonluidspreker"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Hierdie foon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 647d052..7ad455a 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ሁልጊዜ ጠይቅ"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"የስልክ ድምጽ ማጉያ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ይህ ስልክ"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 6357f3c..a4646f1 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -556,7 +556,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"السؤال في كل مرة"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"هذا الهاتف"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index cb74e1f..c304921 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্ৰতিবাৰতে সোধক"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফ’নৰ স্পীকাৰ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফ’নটো"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 979c957..6e3947e 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Həmişə soruşulsun"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Deaktiv edilənə qədər"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon dinamiki"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 5af3006..a05dae9 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -553,7 +553,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d1b6226..e3b0567 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Заўсёды пытацца"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Дынамік тэлефона"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Гэты тэлефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 375879a..a8eaaf0 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Да се пита винаги"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"До изключване"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Високоговорител"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Този телефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 89fcbd3..c28e927 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্রতিবার জিজ্ঞেস করা হবে"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফোনের স্পিকার"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফোন"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index e33978f..232c22f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -553,7 +553,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 2b10a30..bc393c3 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pregunta sempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altaveu del telèfon"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7a94bb4..f758365 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pokaždé se zeptat"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dokud funkci nevypnete"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefonu"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index ff5c6ae..0fd2569 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spørg hver gang"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens højttaler"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index defc223..f75685b 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Jedes Mal fragen"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Smartphone-Lautsprecher"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Dieses Smartphone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus &amp; und wieder ein."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 402e489..5115728 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Να ερωτώμαι κάθε φορά"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Μέχρι την απενεργοποίηση"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Ηχείο τηλεφώνου"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφωνο"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Πρόβλημα κατά τη σύνδεση. Απενεργοποιήστε τη συσκευή και ενεργοποιήστε την ξανά"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ενσύρματη συσκευή ήχου"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index fb3bc7a..1481cee 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index e33af37..b3dd58a 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index fb3bc7a..1481cee 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index fb3bc7a..1481cee 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 2c58236..2a6c82c 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎Ask every time‎‏‎‎‏‎"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‏‏‎Until you turn off‎‏‎‎‏‎"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎Just now‎‏‎‎‏‎"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎Phone speaker‎‏‎‎‏‎"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‎This phone‎‏‎‎‏‎"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‏‎‏‏‎This phone‎‏‎‎‏‎"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‎Problem connecting. Turn device off &amp; back on‎‏‎‎‏‎"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎Wired audio device‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 471e715..ee0e0bc 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -219,7 +219,7 @@
     <item msgid="3075292553049300105">"Normal"</item>
     <item msgid="1158955023692670059">"Ligera"</item>
     <item msgid="5664310435707146591">"Muy ligera"</item>
-    <item msgid="5491266922147715962">"A velocidad muy alta"</item>
+    <item msgid="5491266922147715962">"Muy rápida"</item>
     <item msgid="7659240015901486196">"Rápida"</item>
     <item msgid="7147051179282410945">"Muy rápida"</item>
     <item msgid="581904787661470707">"A velocidad máxima"</item>
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index bd687e4..1f64bad 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index aaf3201..ed7b9dc 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Küsi iga kord"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoni kõlar"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"See telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 0f929de..5222d8a 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -250,7 +250,7 @@
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Konektatuta daudenak"</string>
     <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Gailuaren xehetasunak"</string>
     <string name="adb_device_forget" msgid="193072400783068417">"Ahaztu"</string>
-    <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Gailuaren erreferentzia-gako digitala: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
+    <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Gailuaren aztarna digitala: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
     <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Ezin izan da konektatu"</string>
     <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Ziurtatu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> sare berera konektatuta dagoela"</string>
     <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Parekatu gailuarekin"</string>
@@ -552,9 +552,9 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Galdetu beti"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonoaren bozgorailua"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefono hau"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string>
-    <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazoren bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
+    <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
     <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
     <string name="storage_category" msgid="2287342585424631813">"Biltegiratzea"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 3dce25d..1b5f979 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"هربار پرسیده شود"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانی‌که آن را خاموش کنید"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"هم‌اکنون"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"بلندگوی تلفن"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"این تلفن"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 373f6a4..4103a9f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -209,7 +209,7 @@
     <string name="tts_status_checking" msgid="8026559918948285013">"Tarkistetaan…"</string>
     <string name="tts_engine_settings_title" msgid="7849477533103566291">"Asetukset: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
     <string name="tts_engine_settings_button" msgid="477155276199968948">"Käynnistä moottorin asetukset"</string>
-    <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ensisijainen kone"</string>
+    <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ensisijainen moottori"</string>
     <string name="tts_general_section_title" msgid="8919671529502364567">"Yleiset"</string>
     <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Palauta äänenkorkeus"</string>
     <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Palauta tekstin lukemisen oletusäänenkorkeus"</string>
@@ -220,8 +220,8 @@
     <item msgid="1158955023692670059">"Nopea"</item>
     <item msgid="5664310435707146591">"Nopeampi"</item>
     <item msgid="5491266922147715962">"Hyvin nopea"</item>
-    <item msgid="7659240015901486196">"Nopea"</item>
-    <item msgid="7147051179282410945">"Erittäin nopea"</item>
+    <item msgid="7659240015901486196">"Vauhdikas"</item>
+    <item msgid="7147051179282410945">"Erittäin vauhdikas"</item>
     <item msgid="581904787661470707">"Nopein"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Valitse profiili"</string>
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Kysy aina"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Puhelimen kaiutin"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tämä puhelin"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index ebaf4b1..ab36e11 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 5f635b0..35f2fce 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index cb33a51..8d23864 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar sempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altofalante do teléfono"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 79ca4ab..ac38a64 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"દર વખતે પૂછો"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ફોન સ્પીકર"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"આ ફોન"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index fcf04bc..5c008f0 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"हर बार पूछें"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"जब तक आप इसे बंद नहीं करते"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फ़ोन का स्पीकर"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यह फ़ोन"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 1ef94fb..d681a1e 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -553,7 +553,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index deb0f81..57894a0 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Mindig kérdezzen rá"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hangszórója"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ez a telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 485af54..e6ed2b0 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ամեն անգամ հարցնել"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև անջատեք"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Հեռախոսի բարձրախոս"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Այս հեռախոսը"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index f82798a4..6fbab2b 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Selalu tanya"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ponsel"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ponsel ini"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat &amp; aktifkan kembali"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index a43eb14..fa11dfb 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spyrja í hvert skipti"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Símahátalari"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Þessi sími"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 0a96f59..0db0448 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Chiedi ogni volta"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlante telefono"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Questo telefono"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 6f3cfb9..7ac5919 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"יש לשאול בכל פעם"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"הרמקול של הטלפון"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"הטלפון הזה"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 620aca6..d82ca5d 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"毎回確認"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"OFF にするまで"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"たった今"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"スマートフォンのスピーカー"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"このスマートフォン"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"このスマートフォン"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index daa0fae..02c1084 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ყოველთვის მკითხეთ"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ტელეფონის დინამიკი"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ეს ტელეფონი"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 6305891..6c55b62 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Әрдайым сұрау"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефон динамигі"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index fb00aa0..58a2c7c 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"សួរគ្រប់ពេល"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"រហូតទាល់តែ​អ្នកបិទ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ឧបករណ៍​បំពង​សំឡេង​ទូរសព្ទ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ទូរសព្ទនេះ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មាន​បញ្ហា​ក្នុងការ​ភ្ជាប់។ បិទ រួច​បើក​ឧបករណ៍​វិញ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍​សំឡេងប្រើខ្សែ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a1f0825..91e76e2 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ಈ ಫೋನ್"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 1ff18c6..d470560 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"항상 확인"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"사용 중지할 때까지"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"휴대전화 스피커"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"이 휴대전화"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"이 휴대전화"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"연결 중에 문제가 발생했습니다. 기기를 껐다가 다시 켜 보세요."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"유선 오디오 기기"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 58e3107..e38bb51a 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ар дайым суралсын"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефондун динамиги"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ушул телефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index a2b8c6b..cc02292 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ຖາມທຸກເທື່ອ"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ລຳໂພງໂທລະສັບ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index d8c1a45..fb645a0 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Klausti kaskart"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefono garsiakalbis"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis telefonas"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 9ac8c65..694ff0a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -553,7 +553,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vaicāt katru reizi"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Tālruņa skaļrunis"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis tālrunis"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index c08fd24..9d412fce 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Прашувај секогаш"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефонски звучник"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овој телефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 27693e6..82df16c0 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"എപ്പോഴും ചോദിക്കുക"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ഫോൺ സ്‌പീക്കർ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ഈ ഫോൺ"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്‌റ്റ് ചെയ്യുന്നതിൽ പ്രശ്‌നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index b62d103..027b722 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Тухай бүрд асуух"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Таныг унтраах хүртэл"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Утасны чанга яригч"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Энэ утас"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Энэ утас"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Холбогдоход асуудал гарлаа. Төхөөрөмжийг унтраагаад дахин асаана уу"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Утастай аудио төхөөрөмж"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index d90c08a..176d764 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक वेळी विचारा"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनचा स्पीकर"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"हा फोन"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्‍ट करण्‍यात समस्‍या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 7011aec..6bf15fe 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Tanya setiap kali"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Sehingga anda matikan"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Pembesar suara telefon"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefon ini"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefon ini"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan &amp; hidupkan kembali peranti"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3de8cf9..13fc6ad 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"အမြဲမေးရန်"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ဖုန်းစပီကာ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ဤဖုန်း"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index e7f70d1..c2e449b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spør hver gang"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhøyttaler"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefonen"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 8fc329f8..85860f0 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक पटक सोधियोस्"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले अफ नगरेसम्म"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनको स्पिकर"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यो फोन"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि सक्रिय गर्नुहोस्"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 35e651b..2ffa559 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -222,7 +222,7 @@
     <item msgid="5491266922147715962">"Nog sneller"</item>
     <item msgid="7659240015901486196">"Heel erg snel"</item>
     <item msgid="7147051179282410945">"Snelst"</item>
-    <item msgid="581904787661470707">"Allerallersnelst"</item>
+    <item msgid="581904787661470707">"Allersnelst"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Profiel kiezen"</string>
     <string name="category_personal" msgid="6236798763159385225">"Persoonlijk"</string>
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vraag altijd"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat je uitzet"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoonspeaker"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Deze telefoon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Deze telefoon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 49018be..e2fee13 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ଫୋନ୍ ସ୍ପିକର୍"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ଏହି ଫୋନ"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 9efc4c4..11cd481 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 1eb2dd9..0c670bc 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Zawsze pytaj"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Głośnik telefonu"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ten telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 38d637f..10568a1 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este smartphone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 5a0ec2e..54582790e 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altifalante do telemóvel"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telemóvel"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 38d637f..10568a1 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este smartphone"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index ce58f1c..00fc8ff 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -553,7 +553,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Întreabă de fiecare dată"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivați"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Difuzorul telefonului"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Acest telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Opriți și reporniți dispozitivul."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 14db225..15e3456 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Всегда спрашивать"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Встроен. динамик"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Этот смартфон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 41f10c9..d56dd34 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"සෑම විටම ඉල්ලන්න"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"ඔබ ක්‍රියාවිරහිත කරන තුරු"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"දුරකථන ස්පීකරය"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"මෙම දුරකථනය"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"මෙම දුරකථනය"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"සම්බන්ධ කිරීමේ ගැටලුවකි උපාංගය ක්‍රියාවිරහිත කර &amp; ආපසු ක්‍රියාත්මක කරන්න"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"රැහැන්ගත කළ ඕඩියෝ උපාංගය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 801c213..e7ea915 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vždy sa opýtať"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefónu"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefón"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index a331018..95a6fb9 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vedno vprašaj"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvočnik telefona"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ta telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index cb9bea8..8524164 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pyet çdo herë"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Derisa ta çaktivizosh"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlanti i telefonit"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 9e90333..b19198a 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -553,7 +553,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Питај сваки пут"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Док не искључите"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Звучник телефона"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овај телефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овај телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем при повезивању. Искључите уређај, па га поново укључите"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичани аудио уређај"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 215d9ad..59cde86 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Fråga varje gång"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhögtalare"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Den här telefonen"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index f51825b..64c2e07 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Uliza kila wakati"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Hadi utakapoizima"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Spika ya simu"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Simu hii"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Simu hii"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kuna tatizo la kuunganisha kwenye Intaneti. Zima kisha uwashe kifaa"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kifaa cha sauti kinachotumia waya"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 8128683..ccef6b9 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ஒவ்வொரு முறையும் கேள்"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"மொபைல் ஸ்பீக்கர்"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"இந்த மொபைல்"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 298f850..4013620 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ప్రతిసారి అడుగు"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్‌ చేసే వరకు"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ఈ ఫోన్"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index e18c649..c42295f 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ถามทุกครั้ง"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"จนกว่าคุณจะปิด"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ลำโพงโทรศัพท์"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"โทรศัพท์เครื่องนี้"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"เกิดปัญหาในการเชื่อมต่อ ปิดอุปกรณ์แล้วเปิดใหม่อีกครั้ง"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"อุปกรณ์เสียงแบบมีสาย"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 628bacc..ab36a55 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Magtanong palagi"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Hanggang sa i-off mo"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ng telepono"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ang teleponong ito"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ang teleponong ito"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 3e30d43..ce71496 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Her zaman sor"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hoparlörü"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 8683953..b7d370d2 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -554,7 +554,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Запитувати щоразу"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Динамік"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Цей телефон"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 09e50f6..c3f4d56 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ہر بار پوچھیں"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"فون اسپیکر"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"یہ فون"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index d0b9506..aa3d8826 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Har safar so‘ralsin"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Rejimdan chiqilgunicha"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon karnayi"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Shu telefon"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Shu telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index bd97cc5..7291d06 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Luôn hỏi"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Cho đến khi bạn tắt"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Loa điện thoại"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Điện thoại này"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Điện thoại này"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 3ed1860..3f1b9ae 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都询问"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手机扬声器"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 965e78f..c91a13f 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"直至您關閉為止"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e3ca344..8b469ea 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"直到你關閉為止"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這支手機"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連線,請關閉裝置後再重新開啟"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音訊裝置"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 1af1fa1..9eac12c 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -552,7 +552,7 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Buza njalo"</string>
     <string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string>
-    <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Isipikha sefoni"</string>
+    <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Le foni"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 45253bb..b150e01 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -28,4 +28,9 @@
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
+
+    <integer-array name="config_supportedDreamComplications">
+    </integer-array>
+    <integer-array name="config_dreamComplicationsEnabledByDefault">
+    </integer-array>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index c439cf0..9bccc3f 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -22,7 +22,7 @@
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
 
-    <dimen name="circle_avatar_size">40dp</dimen>
+    <dimen name="circle_avatar_size">190dp</dimen>
 
     <!-- Height of a user icon view -->
     <dimen name="user_icon_view_height">24dp</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 47b0744..f9ac01d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1271,10 +1271,13 @@
     <string name="wifi_status_mac_randomized">MAC is randomized</string>
 
     <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
-    <plurals name="wifi_tether_connected_summary">
-        <item quantity="one">%1$d device connected</item>
-        <item quantity="other">%1$d devices connected</item>
-    </plurals>
+    <string name="wifi_tether_connected_summary">
+        {count, plural,
+            =0    {0 device connected}
+            =1    {1 device connected}
+            other {# devices connected}
+        }
+    </string>
 
     <!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_manual_zen_more_time">More time.</string>
diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
index de45ea5..d3fe4a7 100644
--- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
+++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
@@ -47,7 +47,7 @@
  * Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources}
  * subclasses.
  */
-@SupportedSourceVersion(SourceVersion.RELEASE_8)
+@SupportedSourceVersion(SourceVersion.RELEASE_9)
 @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
 public class IndexableProcessor extends AbstractProcessor {
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 2b357c5..1e8cb9f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -38,6 +38,7 @@
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.ImageSpan;
+import android.util.Log;
 import android.view.MenuItem;
 import android.widget.TextView;
 
@@ -54,6 +55,7 @@
 public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
 
     private static final String LOG_TAG = "RestrictedLockUtils";
+    private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
 
     /**
      * @return drawables for displaying with settings that are locked by a device admin.
@@ -92,14 +94,25 @@
         }
 
         final UserManager um = UserManager.get(context);
+        final UserHandle userHandle = UserHandle.of(userId);
         final List<UserManager.EnforcingUser> enforcingUsers =
-                um.getUserRestrictionSources(userRestriction, UserHandle.of(userId));
+                um.getUserRestrictionSources(userRestriction, userHandle);
 
         if (enforcingUsers.isEmpty()) {
             // Restriction is not enforced.
             return null;
-        } else if (enforcingUsers.size() > 1) {
-            return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
+        }
+        final int size = enforcingUsers.size();
+        if (size > 1) {
+            final EnforcedAdmin enforcedAdmin = EnforcedAdmin
+                    .createDefaultEnforcedAdminWithRestriction(userRestriction);
+            enforcedAdmin.user = userHandle;
+            if (DEBUG) {
+                Log.d(LOG_TAG, "Multiple (" + size + ") enforcing users for restriction '"
+                        + userRestriction + "' on user " + userHandle + "; returning default admin "
+                        + "(" + enforcedAdmin + ")");
+            }
+            return enforcedAdmin;
         }
 
         final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource();
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
index 5f2bef7..64a0781 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -31,9 +31,8 @@
     private int mLastDensity;
 
     public InterestingConfigChanges() {
-        this(ActivityInfo.CONFIG_LOCALE
-                | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT
-                | ActivityInfo.CONFIG_ASSETS_PATHS);
+        this(ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_LAYOUT_DIRECTION
+                | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_ASSETS_PATHS);
     }
 
     public InterestingConfigChanges(int flags) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 8ac4e38..389892e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -170,12 +170,6 @@
     }
 
     @VisibleForTesting
-    void registerIntentReceiver() {
-        mContext.registerReceiverAsUser(mBroadcastReceiver, mUserHandle, mAdapterIntentFilter,
-                null, mReceiverHandler);
-    }
-
-    @VisibleForTesting
     void addProfileHandler(String action, Handler handler) {
         mHandlerMap.put(action, handler);
         mProfileIntentFilter.addAction(action);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 9dd329e..b11bbde 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -138,13 +138,6 @@
         return mService.getActiveDevice();
     }
 
-    public boolean isAudioOn() {
-        if (mService == null) {
-            return false;
-        }
-        return mService.isAudioOn();
-    }
-
     public int getAudioState(BluetoothDevice device) {
         if (mService == null) {
             return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
index c6552f7..d3934bf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
@@ -73,7 +73,8 @@
         }
 
         mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter,
-                android.Manifest.permission.CHANGE_NETWORK_STATE, null);
+                android.Manifest.permission.CHANGE_NETWORK_STATE, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     protected abstract String[] getConnectivityIntents();
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 6b9b750..7168f3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -38,6 +38,8 @@
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.settingslib.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -45,13 +47,17 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 public class DreamBackend {
     private static final String TAG = "DreamBackend";
     private static final boolean DEBUG = false;
+    private final Drawable mDreamPreviewDefault;
 
     public static class DreamInfo {
         public CharSequence caption;
@@ -77,19 +83,41 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WHILE_CHARGING, WHILE_DOCKED, EITHER, NEVER})
-    public @interface WhenToDream{}
+    public @interface WhenToDream {}
 
     public static final int WHILE_CHARGING = 0;
     public static final int WHILE_DOCKED = 1;
     public static final int EITHER = 2;
     public static final int NEVER = 3;
 
+    /**
+     * The type of dream complications which can be provided by a
+     * {@link com.android.systemui.dreams.ComplicationProvider}.
+     */
+    @IntDef(prefix = {"COMPLICATION_TYPE_"}, value = {
+            COMPLICATION_TYPE_TIME,
+            COMPLICATION_TYPE_DATE,
+            COMPLICATION_TYPE_WEATHER,
+            COMPLICATION_TYPE_AIR_QUALITY,
+            COMPLICATION_TYPE_CAST_INFO
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComplicationType {}
+
+    public static final int COMPLICATION_TYPE_TIME = 1;
+    public static final int COMPLICATION_TYPE_DATE = 2;
+    public static final int COMPLICATION_TYPE_WEATHER = 3;
+    public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
+    public static final int COMPLICATION_TYPE_CAST_INFO = 5;
+
     private final Context mContext;
     private final IDreamManager mDreamManager;
     private final DreamInfoComparator mComparator;
     private final boolean mDreamsEnabledByDefault;
     private final boolean mDreamsActivatedOnSleepByDefault;
     private final boolean mDreamsActivatedOnDockByDefault;
+    private final Set<Integer> mSupportedComplications;
+    private final Set<Integer> mDefaultEnabledComplications;
 
     private static DreamBackend sInstance;
 
@@ -102,15 +130,31 @@
 
     public DreamBackend(Context context) {
         mContext = context.getApplicationContext();
+        final Resources resources = mContext.getResources();
+
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.getService(DreamService.DREAM_SERVICE));
         mComparator = new DreamInfoComparator(getDefaultDream());
-        mDreamsEnabledByDefault = mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault);
-        mDreamsActivatedOnSleepByDefault = mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
-        mDreamsActivatedOnDockByDefault = mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+        mDreamsEnabledByDefault = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsEnabledByDefault);
+        mDreamsActivatedOnSleepByDefault = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
+        mDreamsActivatedOnDockByDefault = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+        mDreamPreviewDefault = resources.getDrawable(
+                com.android.internal.R.drawable.default_dream_preview);
+
+        mSupportedComplications =
+                Arrays.stream(resources.getIntArray(R.array.config_supportedDreamComplications))
+                        .boxed()
+                        .collect(Collectors.toSet());
+
+        mDefaultEnabledComplications = Arrays.stream(
+                        resources.getIntArray(R.array.config_dreamComplicationsEnabledByDefault))
+                .boxed()
+                // A complication can only be enabled by default if it is also supported.
+                .filter(mSupportedComplications::contains)
+                .collect(Collectors.toSet());
     }
 
     public List<DreamInfo> getDreamInfos() {
@@ -133,11 +177,11 @@
             dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
 
             final DreamMetadata dreamMetadata = getDreamMetadata(pm, resolveInfo);
-            if (dreamMetadata != null) {
-                dreamInfo.settingsComponentName = dreamMetadata.mSettingsActivity;
-                dreamInfo.previewImage = dreamMetadata.mPreviewImage;
+            dreamInfo.settingsComponentName = dreamMetadata.mSettingsActivity;
+            dreamInfo.previewImage = dreamMetadata.mPreviewImage;
+            if (dreamInfo.previewImage == null) {
+                dreamInfo.previewImage = mDreamPreviewDefault;
             }
-
             dreamInfos.add(dreamInfo);
         }
         Collections.sort(dreamInfos, mComparator);
@@ -239,7 +283,57 @@
             default:
                 break;
         }
+    }
 
+    /** Gets all complications which have been enabled by the user. */
+    public Set<Integer> getEnabledComplications() {
+        final String enabledComplications = Settings.Secure.getString(
+                mContext.getContentResolver(),
+                Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS);
+
+        if (enabledComplications == null) {
+            return mDefaultEnabledComplications;
+        }
+
+        return parseFromString(enabledComplications);
+    }
+
+    /** Gets all dream complications which are supported on this device. **/
+    public Set<Integer> getSupportedComplications() {
+        return mSupportedComplications;
+    }
+
+    /**
+     * Enables or disables a particular dream complication.
+     *
+     * @param complicationType The dream complication to be enabled/disabled.
+     * @param value            If true, the complication is enabled. Otherwise it is disabled.
+     */
+    public void setComplicationEnabled(@ComplicationType int complicationType, boolean value) {
+        if (!mSupportedComplications.contains(complicationType)) return;
+
+        Set<Integer> enabledComplications = getEnabledComplications();
+        if (value) {
+            enabledComplications.add(complicationType);
+        } else {
+            enabledComplications.remove(complicationType);
+        }
+
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
+                convertToString(enabledComplications));
+    }
+
+    private static String convertToString(Set<Integer> set) {
+        return set.stream()
+                .map(String::valueOf)
+                .collect(Collectors.joining(","));
+    }
+
+    private static Set<Integer> parseFromString(String string) {
+        return Arrays.stream(string.split(","))
+                .map(Integer::parseInt)
+                .collect(Collectors.toSet());
     }
 
     public boolean isEnabled() {
@@ -308,7 +402,10 @@
         if (dreamInfo == null || dreamInfo.settingsComponentName == null) {
             return;
         }
-        uiContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
+        final Intent intent = new Intent()
+                .setComponent(dreamInfo.settingsComponentName)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        uiContext.startActivity(intent);
     }
 
     public void preview(DreamInfo dreamInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 011ca0b..cff45c6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,8 +16,6 @@
 
 package com.android.settingslib.net;
 
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.TelephonyManager.SIM_STATE_READY;
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
@@ -27,12 +25,9 @@
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
-import android.os.ServiceManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
@@ -51,25 +46,20 @@
 
     private static final String TAG = "DataUsageController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
             PERIOD_BUILDER, Locale.getDefault());
 
     private final Context mContext;
-    private final INetworkStatsService mStatsService;
     private final NetworkPolicyManager mPolicyManager;
     private final NetworkStatsManager mNetworkStatsManager;
 
-    private INetworkStatsSession mSession;
     private Callback mCallback;
     private NetworkNameProvider mNetworkController;
     private int mSubscriptionId;
 
     public DataUsageController(Context context) {
         mContext = context;
-        mStatsService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         mPolicyManager = NetworkPolicyManager.from(mContext);
         mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -112,8 +102,7 @@
     }
 
     public DataUsageInfo getWifiDataUsageInfo() {
-        NetworkTemplate template = NetworkTemplate.buildTemplateWifi(
-                NetworkTemplate.WIFI_NETWORKID_ALL, null);
+        NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
         return getDataUsageInfo(template);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index 3f95a07..afd44d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.net;
 
 import android.content.Context;
+import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -26,6 +27,7 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Utils class for data usage
@@ -73,10 +75,15 @@
 
     private static NetworkTemplate getMobileTemplateForSubId(
             TelephonyManager telephonyManager, int subId) {
-        // The null subscriberId means that no any mobile/carrier network will be matched.
-        // Using old API: buildTemplateMobileAll for the null subscriberId to avoid NPE.
+        // Create template that matches any mobile network when the subscriberId is null.
         String subscriberId = telephonyManager.getSubscriberId(subId);
-        return subscriberId != null ? NetworkTemplate.buildTemplateCarrierMetered(subscriberId)
-                : NetworkTemplate.buildTemplateMobileAll(subscriberId);
+        return subscriberId != null
+                ? new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setSubscriberIds(Set.of(subscriberId))
+                .setMeteredness(NetworkStats.METERED_YES)
+                .build()
+                : new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+                        .setMeteredness(NetworkStats.METERED_YES)
+                        .build();
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 3e95b01..5e9ac5a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -16,23 +16,16 @@
 
 package com.android.settingslib.net;
 
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-
+import android.annotation.NonNull;
 import android.app.usage.NetworkStats;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
-import android.net.TrafficStats;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.text.format.DateUtils;
 import android.util.Pair;
+import android.util.Range;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.loader.content.AsyncTaskLoader;
@@ -52,8 +45,6 @@
     protected final NetworkTemplate mNetworkTemplate;
     private final NetworkPolicy mPolicy;
     private final ArrayList<Long> mCycles;
-    @VisibleForTesting
-    final INetworkStatsService mNetworkStatsService;
 
     protected NetworkCycleDataLoader(Builder<?> builder) {
         super(builder.mContext);
@@ -61,8 +52,6 @@
         mCycles = builder.mCycles;
         mNetworkStatsManager = (NetworkStatsManager)
             builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
-        mNetworkStatsService = INetworkStatsService.Stub.asInterface(
-            ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         final NetworkPolicyEditor policyEditor =
             new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
         policyEditor.read();
@@ -112,23 +101,20 @@
 
     @VisibleForTesting
     void loadFourWeeksData() {
+        if (mNetworkTemplate == null) return;
+        final NetworkStats stats = mNetworkStatsManager.queryDetailsForDevice(
+                mNetworkTemplate, Long.MIN_VALUE, Long.MAX_VALUE);
         try {
-            final INetworkStatsSession networkSession = mNetworkStatsService.openSession();
-            final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork(
-                mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES);
-            final long historyStart = networkHistory.getStart();
-            final long historyEnd = networkHistory.getEnd();
+            final Range<Long> historyTimeRange = getTimeRangeOf(stats);
 
-            long cycleEnd = historyEnd;
-            while (cycleEnd > historyStart) {
+            long cycleEnd = historyTimeRange.getUpper();
+            while (cycleEnd > historyTimeRange.getLower()) {
                 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
                 recordUsage(cycleStart, cycleEnd);
                 cycleEnd = cycleStart;
             }
-
-            TrafficStats.closeQuietly(networkSession);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
+        } catch (IllegalArgumentException e) {
+            // Empty history, ignore.
         }
     }
 
@@ -169,6 +155,32 @@
         return bytes;
     }
 
+    @NonNull
+    @VisibleForTesting
+    Range getTimeRangeOf(@NonNull NetworkStats stats) {
+        long start = Long.MAX_VALUE;
+        long end = Long.MIN_VALUE;
+        while (hasNextBucket(stats)) {
+            final NetworkStats.Bucket bucket = getNextBucket(stats);
+            start = Math.min(start, bucket.getStartTimeStamp());
+            end = Math.max(end, bucket.getEndTimeStamp());
+        }
+        return new Range(start, end);
+    }
+
+    @VisibleForTesting
+    boolean hasNextBucket(@NonNull NetworkStats stats) {
+        return stats.hasNextBucket();
+    }
+
+    @NonNull
+    @VisibleForTesting
+    NetworkStats.Bucket getNextBucket(@NonNull NetworkStats stats) {
+        NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+        stats.getNextBucket(bucket);
+        return bucket;
+    }
+
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     public ArrayList<Long> getCycles() {
         return mCycles;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
index 21a4ac6..fa4ae67 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
@@ -60,6 +60,22 @@
         return true;
     }
 
+    /**
+     * Confirm Wi-Fi Config is allowed to add according to whether user restriction is set
+     *
+     * @param context A context
+     * @return whether the device is permitted to add new Wi-Fi config
+     */
+    public static boolean isAddWifiConfigAllowed(Context context) {
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        final Bundle restrictions = userManager.getUserRestrictions();
+        if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)) {
+            Log.i(TAG, "Wi-Fi Add network isn't available due to user restriction.");
+            return false;
+        }
+        return true;
+    }
+
     @ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU)
     private static boolean isAtLeastT() {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 56454e9..4ab6542 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
+import android.icu.text.MessageFormat;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
@@ -33,6 +34,8 @@
 
 import com.android.settingslib.R;
 
+import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 public class WifiUtils {
@@ -333,4 +336,20 @@
         intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
         return intent;
     }
+
+    /**
+     * Returns the string of Wi-Fi tethering summary for connected devices.
+     *
+     * @param context          The application context
+     * @param connectedDevices The count of connected devices
+     */
+    public static String getWifiTetherSummaryForConnectedDevices(Context context,
+            int connectedDevices) {
+        MessageFormat msgFormat = new MessageFormat(
+                context.getResources().getString(R.string.wifi_tether_connected_summary),
+                Locale.getDefault());
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("count", connectedDevices);
+        return msgFormat.format(arguments);
+    }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
index 7e389a1..919f602 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
@@ -20,6 +20,7 @@
 
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 
 import androidx.test.InstrumentationRegistry;
@@ -32,6 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NetworkPolicyEditorTest {
@@ -44,7 +47,9 @@
 
     @Before
     public void setUp() {
-        mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered("123456789123456");
+        mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of("123456789123456")).build();
         NetworkPolicyManager policyManager = NetworkPolicyManager.from(InstrumentationRegistry
                 .getContext());
         mNetworkPolicyEditor = new NetworkPolicyEditor(policyManager);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 63153f8..10ccd22 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -708,11 +708,11 @@
             throws RemoteException {
 
         if (ownerApps != null) {
-            when(mApplicationsState.mIpm.getInstalledApplications(anyInt(), eq(0)))
+            when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(0)))
                 .thenReturn(new ParceledListSlice<>(ownerApps));
         }
         if (profileApps != null) {
-            when(mApplicationsState.mIpm.getInstalledApplications(anyInt(), eq(PROFILE_USERID)))
+            when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(PROFILE_USERID)))
                 .thenReturn(new ParceledListSlice<>(profileApps));
         }
         final InterestingConfigChanges configChanges = mock(InterestingConfigChanges.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index bee466d..852ac5c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -129,7 +129,6 @@
     @Test
     public void intentWithExtraState_audioStateChangedShouldDispatchToRegisterCallback() {
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
 
         mContext.sendBroadcast(mIntent);
@@ -143,7 +142,6 @@
     @Test
     public void intentWithExtraState_phoneStateChangedShouldDispatchToRegisterCallback() {
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
 
         mContext.sendBroadcast(mIntent);
@@ -169,7 +167,6 @@
     @Test
     public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
 
@@ -182,7 +179,6 @@
     @Test
     public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
 
@@ -196,7 +192,6 @@
     public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
         when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
 
@@ -210,7 +205,6 @@
     public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
         when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
 
@@ -224,7 +218,6 @@
     public void dispatchAclConnectionStateChanged_findDeviceReturnNull_shouldNotDispatchCallback() {
         when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(null);
         mBluetoothEventManager.registerCallback(mBluetoothCallback);
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
 
@@ -361,7 +354,6 @@
 
     @Test
     public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
         mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -377,7 +369,6 @@
 
     @Test
     public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() {
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
         mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -394,7 +385,6 @@
 
     @Test
     public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() {
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
         mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -410,7 +400,6 @@
 
     @Test
     public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() {
-        mBluetoothEventManager.registerIntentReceiver();
         mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
         mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index 30182c4..f5ce664 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -55,15 +55,6 @@
     }
 
     @Test
-    public void bluetoothProfile_shouldReturnTheAudioStatusFromBlueToothHeadsetService() {
-        when(mService.isAudioOn()).thenReturn(true);
-        assertThat(mProfile.isAudioOn()).isTrue();
-
-        when(mService.isAudioOn()).thenReturn(false);
-        assertThat(mProfile.isAudioOn()).isFalse();
-    }
-
-    @Test
     public void testHeadsetProfile_shouldReturnAudioState() {
         when(mService.getAudioState(mBluetoothDevice)).
                 thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index 4444e63..c1cc3ae 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -33,6 +33,7 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -54,6 +55,7 @@
     }
 
     @Test
+    @Ignore
     public void testBroadcastReceiver() {
         final AbstractConnectivityPreferenceController preferenceController =
                 spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
new file mode 100644
index 0000000..53d4653
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.dream;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.settingslib.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowSettings;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSettings.ShadowSecure.class})
+public final class DreamBackendTest {
+    private static final int[] SUPPORTED_DREAM_COMPLICATIONS = {1, 2, 3};
+    private static final int[] DEFAULT_DREAM_COMPLICATIONS = {1, 3, 4};
+
+    @Mock
+    private Context mContext;
+    private DreamBackend mBackend;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getApplicationContext()).thenReturn(mContext);
+
+        final Resources res = mock(Resources.class);
+        when(mContext.getResources()).thenReturn(res);
+        when(res.getIntArray(R.array.config_supportedDreamComplications)).thenReturn(
+                SUPPORTED_DREAM_COMPLICATIONS);
+        when(res.getIntArray(R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
+                DEFAULT_DREAM_COMPLICATIONS);
+        mBackend = new DreamBackend(mContext);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowSettings.ShadowSecure.reset();
+    }
+
+    @Test
+    public void testSupportedComplications() {
+        assertThat(mBackend.getSupportedComplications()).containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void testGetEnabledDreamComplications_default() {
+        assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+    }
+
+    @Test
+    public void testEnableComplication() {
+        mBackend.setComplicationEnabled(/* complicationType= */ 2, true);
+        assertThat(mBackend.getEnabledComplications()).containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void testEnableComplication_notSupported() {
+        mBackend.setComplicationEnabled(/* complicationType= */ 5, true);
+        assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+    }
+
+    @Test
+    public void testDisableComplication() {
+        mBackend.setComplicationEnabled(/* complicationType= */ 1, false);
+        assertThat(mBackend.getEnabledComplications()).containsExactly(3);
+    }
+}
+
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
index 8ec577e..06b6fc8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -22,7 +22,6 @@
 
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertSame;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -67,7 +66,7 @@
 
     @Test
     public void buttonClicked() {
-        ComponentName componentName = mock(ComponentName.class);
+        ComponentName componentName = new ComponentName("com.android.test", "AThing");
         RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
                 componentName, new UserHandle(UserHandle.myUserId()));
 
@@ -83,6 +82,6 @@
         assertEquals(Settings.SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS,
                 intentCaptor.getValue().getStringExtra(
                         Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY));
-        assertSame(componentName, intentCaptor.getValue().getComponent());
+        assertEquals(componentName.getPackageName(), intentCaptor.getValue().getPackage());
     }
 }
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 2d53831..aa0ef91 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
@@ -46,6 +46,7 @@
 import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -731,6 +732,7 @@
     }
 
     @Test
+    @Ignore
     public void shouldDisableMediaOutput_infosSizeEqual1_returnsTrue() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
         final List<MediaRoute2Info> infos = new ArrayList<>();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 9be783d..f0456b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -18,26 +18,21 @@
 
 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.anyLong;
 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.app.usage.NetworkStats;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.RemoteException;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,6 +42,8 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.shadows.ShadowSubscriptionManager;
 
+import java.util.Set;
+
 @RunWith(RobolectricTestRunner.class)
 public class DataUsageControllerTest {
 
@@ -54,8 +51,6 @@
     private static final String SUB_ID_2 = "Test Subscriber 2";
 
     @Mock
-    private INetworkStatsSession mSession;
-    @Mock
     private TelephonyManager mTelephonyManager;
     @Mock
     private SubscriptionManager mSubscriptionManager;
@@ -68,7 +63,6 @@
     private NetworkTemplate mWifiNetworkTemplate;
 
     private DataUsageController mController;
-    private NetworkStatsHistory mNetworkStatsHistory;
     private final int mDefaultSubscriptionId = 1234;
 
     @Before
@@ -80,17 +74,16 @@
                 .thenReturn(mSubscriptionManager);
         when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
         mController = new DataUsageController(mContext);
-        mNetworkStatsHistory = spy(
-                new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
-        doReturn(mNetworkStatsHistory)
-                .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
         ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId);
         doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId();
 
-        mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID);
-        mNetworkTemplate2 = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID_2);
-        mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifi(
-                NetworkTemplate.WIFI_NETWORKID_ALL, null);
+        mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(android.net.NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of(SUB_ID)).build();
+        mNetworkTemplate2 = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(android.net.NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of(SUB_ID_2)).build();
+        mWifiNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index e8d5844..5b0f659 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.text.format.DateUtils;
 
@@ -40,6 +41,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.Set;
+
 @RunWith(RobolectricTestRunner.class)
 public class NetworkCycleDataForUidLoaderTest {
     private static final String SUB_ID = "Test Subscriber";
@@ -62,7 +65,9 @@
         when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
                 .thenReturn(mNetworkPolicyManager);
         when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
-        mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID);
+        mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of(SUB_ID)).build();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 74b9151..c79440e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,24 +16,24 @@
 
 package com.android.settingslib.net;
 
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.nullable;
+import static android.app.usage.NetworkStats.Bucket.UID_ALL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.doNothing;
 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.annotation.NonNull;
+import android.app.usage.NetworkStats;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
-import android.os.RemoteException;
 import android.text.format.DateUtils;
 import android.util.Range;
 
@@ -49,6 +49,8 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
 
 @RunWith(RobolectricTestRunner.class)
 public class NetworkCycleDataLoaderTest {
@@ -63,8 +65,6 @@
     private NetworkPolicy mPolicy;
     @Mock
     private Iterator<Range<ZonedDateTime>> mIterator;
-    @Mock
-    private INetworkStatsService mNetworkStatsService;
 
     private NetworkCycleDataTestLoader mLoader;
 
@@ -132,20 +132,24 @@
         verify(mLoader).recordUsage(nowInMs, nowInMs);
     }
 
+    private NetworkStats.Bucket makeMockBucket(int uid, long rxBytes, long txBytes,
+            long start, long end) {
+        NetworkStats.Bucket ret = mock(NetworkStats.Bucket.class);
+        when(ret.getUid()).thenReturn(uid);
+        when(ret.getRxBytes()).thenReturn(rxBytes);
+        when(ret.getTxBytes()).thenReturn(txBytes);
+        when(ret.getStartTimeStamp()).thenReturn(start);
+        when(ret.getEndTimeStamp()).thenReturn(end);
+        return ret;
+    }
+
     @Test
-    public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException {
+    public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() {
         mLoader = spy(new NetworkCycleDataTestLoader(mContext));
-        ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService);
-        final INetworkStatsSession networkSession = mock(INetworkStatsSession.class);
-        when(mNetworkStatsService.openSession()).thenReturn(networkSession);
-        final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class);
-        when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt()))
-            .thenReturn(networkHistory);
         final long now = System.currentTimeMillis();
         final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
         final long twoDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 2);
-        when(networkHistory.getStart()).thenReturn(twoDaysAgo);
-        when(networkHistory.getEnd()).thenReturn(now);
+        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, twoDaysAgo, now));
 
         mLoader.loadFourWeeksData();
 
@@ -173,10 +177,31 @@
         verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo);
     }
 
+    @Test
+    public void getTimeRangeOf() {
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+        // If empty, new Range(MAX_VALUE, MIN_VALUE) will be constructed. Hence, the function
+        // should throw.
+        assertThrows(IllegalArgumentException.class,
+                () -> mLoader.getTimeRangeOf(mock(NetworkStats.class)));
+
+        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10));
+        // Feed the function with unused NetworkStats. The actual data injection is
+        // done by addBucket.
+        assertEquals(new Range(0L, 10L), mLoader.getTimeRangeOf(mock(NetworkStats.class)));
+
+        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10));
+        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 30, 40));
+        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 10, 25));
+        assertEquals(new Range(0L, 40L), mLoader.getTimeRangeOf(mock(NetworkStats.class)));
+    }
+
     public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
+        private final Queue<NetworkStats.Bucket> mMockedBuckets = new LinkedBlockingQueue<>();
 
         private NetworkCycleDataTestLoader(Context context) {
-            super(NetworkCycleDataLoader.builder(mContext));
+            super(NetworkCycleDataLoader.builder(mContext)
+                    .setNetworkTemplate(mock(NetworkTemplate.class)));
             mContext = context;
         }
 
@@ -188,5 +213,19 @@
         List<NetworkCycleData> getCycleUsage() {
             return null;
         }
+
+        public void addBucket(NetworkStats.Bucket bucket) {
+            mMockedBuckets.add(bucket);
+        }
+
+        @Override
+        public boolean hasNextBucket(@NonNull NetworkStats unused) {
+            return !mMockedBuckets.isEmpty();
+        }
+
+        @Override
+        public NetworkStats.Bucket getNextBucket(@NonNull NetworkStats unused) {
+            return mMockedBuckets.remove();
+        }
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
new file mode 100644
index 0000000..625b214
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.R;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowDrawable;
+
+@RunWith(RobolectricTestRunner.class)
+public class ButtonPreferenceTest {
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private ButtonPreference mPreference;
+    private PreferenceViewHolder mHolder;
+
+    private boolean mClickListenerCalled;
+    private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true;
+
+    @Before
+    public void setUp() {
+        mClickListenerCalled = false;
+        mPreference = new ButtonPreference(mContext);
+        setUpViewHolder();
+    }
+
+    @Test
+    public void onBindViewHolder_whenTitleSet_shouldSetButtonText() {
+        final String testTitle = "Test title";
+        mPreference.setTitle(testTitle);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        assertThat(button.getText().toString()).isEqualTo(testTitle);
+    }
+
+    @Test
+    public void onBindViewHolder_whenIconSet_shouldSetIcon() {
+        mPreference.setIcon(R.drawable.settingslib_ic_cross);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final Drawable icon = button.getCompoundDrawablesRelative()[0];
+        final ShadowDrawable shadowDrawable = shadowOf(icon);
+        assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.settingslib_ic_cross);
+    }
+
+    @Test
+    public void onBindViewHolder_setEnable_shouldSetButtonEnabled() {
+        mPreference.setEnabled(true);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        assertThat(button.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_setDisable_shouldSetButtonDisabled() {
+        mPreference.setEnabled(false);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        assertThat(button.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_default_shouldReturnButtonGravityStart() {
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.START);
+    }
+
+    @Test
+    public void onBindViewHolder_setGravityStart_shouldReturnButtonGravityStart() {
+        mPreference.setGravity(Gravity.START);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.START);
+    }
+
+    @Test
+    public void onBindViewHolder_setGravityCenter_shouldReturnButtonGravityCenterHorizontal() {
+        mPreference.setGravity(Gravity.CENTER_HORIZONTAL);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+
+        mPreference.setGravity(Gravity.CENTER_VERTICAL);
+        mPreference.onBindViewHolder(mHolder);
+        assertThat(lp.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+
+        mPreference.setGravity(Gravity.CENTER);
+        mPreference.onBindViewHolder(mHolder);
+        assertThat(lp.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+    }
+
+    @Test
+    public void onBindViewHolder_setUnsupportedGravity_shouldReturnButtonGravityStart() {
+        mPreference.setGravity(Gravity.END);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.START);
+    }
+
+    @Test
+    public void setButtonOnClickListener_setsClickListener() {
+        mPreference.setOnClickListener(mClickListener);
+
+        mPreference.onBindViewHolder(mHolder);
+        final Button button = mPreference.getButton();
+        button.callOnClick();
+
+        assertThat(mClickListenerCalled).isTrue();
+    }
+
+    private void setUpViewHolder() {
+        final View rootView =
+                View.inflate(mContext, mPreference.getLayoutResource(), null /* parent */);
+        mHolder = PreferenceViewHolder.createInstanceForTests(rootView);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 2bd20a9..30267f7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -19,11 +19,15 @@
 
 import static org.mockito.ArgumentMatchers.anyInt;
 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.content.Context;
 import android.graphics.drawable.ColorDrawable;
+import android.net.wifi.WifiManager;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,7 +40,7 @@
 @RunWith(RobolectricTestRunner.class)
 public class AccessPointPreferenceTest {
 
-    private Context mContext = RuntimeEnvironment.application;
+    private Context mContext;
 
     @Mock
     private AccessPoint mockAccessPoint;
@@ -53,6 +57,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mock(WifiManager.class));
 
         when(mockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
index 3c339de..f6af09a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
@@ -103,4 +103,30 @@
 
         assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue();
     }
+
+    @Test
+    public void isAddWifiConfigAllowed_setSDKForS_shouldReturnTrue() {
+        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
+        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAddWifiConfigAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() {
+        ReflectionHelpers.setStaticField(
+            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isFalse();
+    }
+
+    @Test
+    public void isAddWifiConfigAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() {
+        ReflectionHelpers.setStaticField(
+            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(false);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 7c2b904..e7b3fe9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,6 +33,7 @@
 import android.net.WifiKey;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -74,6 +76,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mock(WifiManager.class));
     }
 
     @Test
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 16cece9..8e35ee96 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+        Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
         Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
         Settings.Secure.ONE_HANDED_MODE_ENABLED,
         Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 71accc4..00b5f50 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -78,6 +78,8 @@
         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,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 688c48d..5f549fd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -229,6 +229,7 @@
         VALIDATORS.put(Secure.SKIP_DIRECTION, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SILENCE_GESTURE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, JSON_OBJECT_VALIDATOR);
+        VALIDATORS.put(Secure.NAV_BAR_KIDS_MODE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"}));
         VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT,
@@ -266,6 +267,7 @@
                 new InclusiveIntegerRangeValidator(
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL));
+        VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
@@ -294,6 +296,7 @@
         VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.LOCATION_SHOW_SYSTEM_OPS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DEVICE_STATE_ROTATION_LOCK, value -> {
             if (TextUtils.isEmpty(value)) {
                 return true;
@@ -322,6 +325,7 @@
             return true;
         });
         VALIDATORS.put(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.FAST_PAIR_SCAN_ENABLED, BOOLEAN_VALIDATOR);
 
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 84e9d28..6bcb769 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -118,6 +118,8 @@
         VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.APPLY_RAMPING_RINGER, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+        VALIDATORS.put(System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
         VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
         VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 103e141..7381e05 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -244,9 +244,6 @@
 
         final long autofillToken = p.start(GlobalSettingsProto.AUTOFILL);
         dumpSetting(s, p,
-                Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
-                GlobalSettingsProto.Autofill.COMPAT_MODE_ALLOWED_PACKAGES);
-        dumpSetting(s, p,
                 Settings.Global.AUTOFILL_LOGGING_LEVEL,
                 GlobalSettingsProto.Autofill.LOGGING_LEVEL);
         dumpSetting(s, p,
@@ -1381,9 +1378,6 @@
                 Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
                 GlobalSettingsProto.Sys.STORAGE_CACHE_PERCENTAGE);
         dumpSetting(s, p,
-                Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
-                GlobalSettingsProto.Sys.STORAGE_CACHE_MAX_BYTES);
-        dumpSetting(s, p,
                 Settings.Global.SYS_UIDCPUPOWER,
                 GlobalSettingsProto.Sys.UIDCPUPOWER);
         p.end(sysToken);
@@ -1816,6 +1810,10 @@
         dumpSetting(s, p,
                 Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
                 SecureSettingsProto.Accessibility.ODI_CAPTIONS_VOLUME_UI_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+                SecureSettingsProto.Accessibility
+                        .ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
         p.end(accessibilityToken);
 
         final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
@@ -2247,6 +2245,10 @@
                 SecureSettingsProto.MULTI_PRESS_TIMEOUT);
 
         dumpSetting(s, p,
+                Settings.Secure.NAV_BAR_KIDS_MODE,
+                SecureSettingsProto.NAV_BAR_KIDS_MODE);
+
+        dumpSetting(s, p,
                 Settings.Secure.NAVIGATION_MODE,
                 SecureSettingsProto.NAVIGATION_MODE);
 
@@ -2911,6 +2913,18 @@
         dumpSetting(s, p,
                 Settings.System.VIBRATE_WHEN_RINGING,
                 SystemSettingsProto.Vibrate.WHEN_RINGING);
+
+        // NOTIFICATION_VIBRATION_INTENSITY is already logged at Notification.vibration_intensity
+        // HAPTIC_FEEDBACK_INTENSITY is already logged at HapticFeedback.intensity
+        dumpSetting(s, p,
+                Settings.System.ALARM_VIBRATION_INTENSITY,
+                SystemSettingsProto.Vibrate.ALARM_INTENSITY);
+        dumpSetting(s, p,
+                Settings.System.MEDIA_VIBRATION_INTENSITY,
+                SystemSettingsProto.Vibrate.MEDIA_INTENSITY);
+        dumpSetting(s, p,
+                Settings.System.RING_VIBRATION_INTENSITY,
+                SystemSettingsProto.Vibrate.RING_INTENSITY);
         p.end(vibrateToken);
 
         final long volumeToken = p.start(SystemSettingsProto.VOLUME);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 368dda1..720fb6c 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -134,7 +134,6 @@
                     Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE,
                     Settings.Global.ASSISTED_GPS_ENABLED,
                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
-                    Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
                     Settings.Global.AUTOFILL_LOGGING_LEVEL,
                     Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
                     Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
@@ -463,7 +462,6 @@
                     Settings.Global.SYNC_MANAGER_CONSTANTS,
                     Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
                     Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
-                    Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
                     Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
                     Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 10252ee..46e24fa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
         >
 
         <!-- Standard permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP" />
     <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.READ_SMS" />
@@ -249,6 +250,7 @@
     <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
     <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
     <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
+    <uses-permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" />
     <uses-permission android:name="android.permission.MANAGE_UI_TRANSLATION" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
@@ -340,6 +342,9 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />
 
+    <!-- Permission needed to test wallpaper dimming -->
+    <uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
+
     <!-- Permission required to test ContentResolver caching. -->
     <uses-permission android:name="android.permission.CACHE_CONTENT" />
 
@@ -349,6 +354,9 @@
     <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
 
+    <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
+    <uses-permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"/>
+
     <!-- permissions required for CTS test - PhoneStateListenerTest -->
     <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
 
@@ -541,6 +549,10 @@
     <!-- Permission required for CTS test - PeopleManagerTest -->
     <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
 
+    <!-- Permissions required for CTS test - TrustTestCases -->
+    <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
+    <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+
     <!-- Permission required for CTS test - CtsGameManagerTestCases -->
     <uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
 
@@ -607,15 +619,17 @@
     <!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
     <uses-permission android:name="android.permission.LOCK_DEVICE" />
 
-    <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
+    <!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
     <uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
-
-    <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
     <uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
+    <uses-permission android:name="android.permission.MANAGE_SAFETY_CENTER" />
 
-    <!-- Permission required for CTS test - CommunalManagerTest -->
-    <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
-    <uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
+
+    <!-- Permission required for CTS test - Notification test suite -->
+    <uses-permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" />
+
+    <!-- Permission required for CTS test - CaptioningManagerTest -->
+    <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
 
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/res/values-watch/strings.xml b/packages/Shell/res/values-watch/strings.xml
new file mode 100644
index 0000000..5f7bfcb
--- /dev/null
+++ b/packages/Shell/res/values-watch/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <!-- Title for Bug report notification indicating the number of the bug report and the
+       percentage complete. Example: "Bug report #3 is 20% complete" [CHAR LIMIT=50] -->
+    <string name="bugreport_in_progress_title">Bug report <xliff:g id="id" example="#3">#%1$d</xliff:g> is <xliff:g id="percentage" example="20%">%2$s</xliff:g> complete</string>
+</resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index ee9d430..c5a01a1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -199,6 +199,15 @@
      */
     private static final String BUGREPORT_DIR = "bugreports";
 
+    /**
+     * The directory in which System Trace files from the native System Tracing app are stored for
+     * Wear devices.
+     */
+    private static final String WEAR_SYSTEM_TRACES_DIRECTORY_ON_DEVICE = "data/local/traces/";
+
+    /** The directory that contains System Traces in bugreports that include System Traces. */
+    private static final String WEAR_SYSTEM_TRACES_DIRECTORY_IN_BUGREPORT = "systraces/";
+
     private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
 
     /**
@@ -724,14 +733,16 @@
         nf.setMaximumFractionDigits(2);
         final String percentageText = nf.format((double) info.progress.intValue() / 100);
 
-        String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
-
-        // TODO: Remove this workaround when notification progress is implemented on Wear.
+        final String title;
         if (mIsWatch) {
+            // TODO: Remove this workaround when notification progress is implemented on Wear.
             nf.setMinimumFractionDigits(0);
             nf.setMaximumFractionDigits(0);
             final String watchPercentageText = nf.format((double) info.progress.intValue() / 100);
-            title = title + "\n" + watchPercentageText;
+            title = mContext.getString(
+                R.string.bugreport_in_progress_title, info.id, watchPercentageText);
+        } else {
+            title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
         }
 
         final String name =
@@ -1456,6 +1467,16 @@
         }
     }
 
+    /** Returns an array of the system trace files collected by the System Tracing native app. */
+    private static File[] getSystemTraceFiles() {
+        try {
+            return new File(WEAR_SYSTEM_TRACES_DIRECTORY_ON_DEVICE).listFiles();
+        } catch (SecurityException e) {
+            Log.e(TAG, "Error getting system trace files.", e);
+            return new File[]{};
+        }
+    }
+
     /**
      * Adds the user-provided info into the bugreport zip file.
      * <p>
@@ -1475,8 +1496,17 @@
             Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
             return;
         }
-        if (TextUtils.isEmpty(info.getTitle()) && TextUtils.isEmpty(info.getDescription())) {
-            Log.d(TAG, "Not touching zip file since neither title nor description are set");
+
+        File[] systemTracesToIncludeInBugreport = new File[] {};
+        if (mIsWatch) {
+            systemTracesToIncludeInBugreport = getSystemTraceFiles();
+            Log.d(TAG, "Found " + systemTracesToIncludeInBugreport.length + " system traces.");
+        }
+
+        if (TextUtils.isEmpty(info.getTitle())
+                    && TextUtils.isEmpty(info.getDescription())
+                    && systemTracesToIncludeInBugreport.length == 0) {
+            Log.d(TAG, "Not touching zip file: no detail to add.");
             return;
         }
         if (info.addedDetailsToZip || info.addingDetailsToZip) {
@@ -1487,7 +1517,10 @@
 
         // It's not possible to add a new entry into an existing file, so we need to create a new
         // zip, copy all entries, then rename it.
-        sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time
+        if (!mIsWatch) {
+            // TODO(b/184854609): re-introduce this notification for Wear.
+            sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time
+        }
 
         final File dir = info.bugreportFile.getParentFile();
         final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
@@ -1508,6 +1541,13 @@
             }
 
             // Then add the user-provided info.
+            if (systemTracesToIncludeInBugreport.length != 0) {
+                for (File trace : systemTracesToIncludeInBugreport) {
+                    addEntry(zos,
+                            WEAR_SYSTEM_TRACES_DIRECTORY_IN_BUGREPORT + trace.getName(),
+                            new FileInputStream(trace));
+                }
+            }
             addEntry(zos, "title.txt", info.getTitle());
             addEntry(zos, "description.txt", info.getDescription());
         } catch (IOException e) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b19ef3a..137a1fd 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -129,8 +129,15 @@
 }
 
 filegroup {
+    name: "AAA-src",
+    srcs: ["tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java"],
+    path: "tests/src",
+}
+
+filegroup {
     name: "SystemUI-tests-utils",
     srcs: [
+        "tests/src/com/android/systemui/SysuiBaseFragmentTest.java",
         "tests/src/com/android/systemui/SysuiTestCase.java",
         "tests/src/com/android/systemui/TestableDependency.java",
         "tests/src/com/android/systemui/classifier/FalsingManagerFake.java",
@@ -138,6 +145,7 @@
         "tests/src/com/android/systemui/statusbar/RankingBuilder.java",
         "tests/src/com/android/systemui/statusbar/SbnBuilder.java",
         "tests/src/com/android/systemui/SysuiTestableContext.java",
+        "tests/src/com/android/systemui/util/**/*Fake.java",
         "tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java",
         "tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java",
         "tests/src/com/android/systemui/**/Fake*.java",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e907efb..f35f5dd 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -150,12 +150,12 @@
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
     <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
 
-    <!-- Communal mode -->
-    <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
-
     <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
     <uses-permission android:name="android.permission.SET_WALLPAPER"/>
 
+    <!-- Needed for WallpaperManager.getWallpaperDimAmount in StatusBar.updateTheme -->
+    <uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
+
     <!-- Wifi Display -->
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
 
@@ -299,6 +299,9 @@
 
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
 
+    <!-- For clipboard overlay -->
+    <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
+
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -348,6 +351,7 @@
         <!-- started from PhoneWindowManager
              TODO: Should have an android:permission attribute -->
         <service android:name=".screenshot.TakeScreenshotService"
+            android:permission="com.android.systemui.permission.SELF"
             android:process=":screenshot"
             android:exported="false" />
 
@@ -760,6 +764,12 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".clipboardoverlay.EditTextActivity"
+                  android:theme="@style/EditTextActivity"
+                  android:exported="false"
+                  android:excludeFromRecents="true"
+                  />
+
         <activity android:name=".controls.management.ControlsProviderSelectorActivity"
                   android:label="@string/controls_providers_title"
                   android:theme="@style/Theme.ControlsManagement"
@@ -845,6 +855,12 @@
             android:singleUser="true"
             android:permission="android.permission.BIND_DREAM_SERVICE" />
 
+        <!-- Service for external clients to do media transfer -->
+        <!-- TODO(b/203800643): Export and guard with a permission. -->
+        <service
+            android:name=".media.taptotransfer.sender.MediaTttSenderService"
+           />
+
         <receiver
             android:name=".tuner.TunerService$ClearReceiver"
             android:exported="false">
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e1da744..3ae85e7 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,8 +11,10 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 [email protected]
 [email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
@@ -43,6 +45,8 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 092758e..dee4ff5 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -15,12 +15,6 @@
             "exclude-annotation": "org.junit.Ignore"
         },
         {
-            "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-            "exclude-annotation": "android.platform.helpers.Staging"
-        },
-        {
             "exclude-annotation": "android.platform.test.annotations.Postsubmit"
         },
         {
@@ -99,9 +93,6 @@
             "exclude-annotation": "androidx.test.filters.FlakyTest"
         },
         {
-            "exclude-annotation": "android.platform.helpers.Staging"
-        },
-        {
             "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
         },
         {
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 1b15d20..46adfeb 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -39,5 +39,5 @@
     ],
 
     manifest: "AndroidManifest.xml",
-
+    kotlincflags: ["-Xjvm-default=enable"],
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index a0d335d..08d217d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -95,6 +95,9 @@
      */
     var callback: Callback? = null
 
+    /** The set of [Listener] that should be notified of any animation started by this animator. */
+    private val listeners = LinkedHashSet<Listener>()
+
     /**
      * Start an intent and animate the opening window. The intent will be started by running
      * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch
@@ -214,6 +217,16 @@
         }
     }
 
+    /** Add a [Listener] that can listen to launch animations. */
+    fun addListener(listener: Listener) {
+        listeners.add(listener)
+    }
+
+    /** Remove a [Listener]. */
+    fun removeListener(listener: Listener) {
+        listeners.remove(listener)
+    }
+
     /** Create a new animation [Runner] controlled by [controller]. */
     @VisibleForTesting
     fun createRunner(controller: Controller): Runner = Runner(controller)
@@ -234,13 +247,27 @@
         /** Hide the keyguard and animate using [runner]. */
         fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
 
-        /** Enable/disable window blur so they don't overlap with the window launch animation **/
-        fun setBlursDisabledForAppLaunch(disabled: Boolean)
-
         /* Get the background color of [task]. */
         fun getBackgroundColor(task: TaskInfo): Int
     }
 
+    interface Listener {
+        /** Called when an activity launch animation started. */
+        @JvmDefault
+        fun onLaunchAnimationStart() {}
+
+        /**
+         * Called when an activity launch animation is finished. This will be called if and only if
+         * [onLaunchAnimationStart] was called earlier.
+         */
+        @JvmDefault
+        fun onLaunchAnimationEnd() {}
+
+        /** Called when an activity launch animation made progress. */
+        @JvmDefault
+        fun onLaunchAnimationProgress(linearProgress: Float) {}
+    }
+
     /**
      * A controller that takes care of applying the animation to an expanding view.
      *
@@ -396,12 +423,12 @@
             val delegate = this.controller
             val controller = object : LaunchAnimator.Controller by delegate {
                 override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
-                    callback.setBlursDisabledForAppLaunch(true)
+                    listeners.forEach { it.onLaunchAnimationStart() }
                     delegate.onLaunchAnimationStart(isExpandingFullyAbove)
                 }
 
                 override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
-                    callback.setBlursDisabledForAppLaunch(false)
+                    listeners.forEach { it.onLaunchAnimationEnd() }
                     iCallback?.invoke()
                     delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
                 }
@@ -413,6 +440,7 @@
                 ) {
                     applyStateToWindow(window, state)
                     navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+                    listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
                     delegate.onLaunchAnimationProgress(state, progress, linearProgress)
                 }
             }
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 0b3eccf..29221aa 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -28,18 +28,88 @@
 const val TAG = "ColorScheme"
 
 const val ACCENT1_CHROMA = 48.0f
-const val ACCENT2_CHROMA = 16.0f
-const val ACCENT3_CHROMA = 32.0f
-const val ACCENT3_HUE_SHIFT = 60.0f
-
-const val NEUTRAL1_CHROMA = 4.0f
-const val NEUTRAL2_CHROMA = 8.0f
-
 const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
-
 const val MIN_CHROMA = 5
 
-public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
+internal enum class ChromaStrategy {
+    EQ, GTE
+}
+
+internal enum class HueStrategy {
+    SOURCE, ADD, SUBTRACT
+}
+
+internal class Chroma(val strategy: ChromaStrategy, val value: Double) {
+    fun get(sourceChroma: Double): Double {
+        return when (strategy) {
+            ChromaStrategy.EQ -> value
+            ChromaStrategy.GTE -> sourceChroma.coerceAtLeast(value)
+        }
+    }
+}
+
+internal class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0.0) {
+    fun get(sourceHue: Double): Double {
+        return when (strategy) {
+            HueStrategy.SOURCE -> sourceHue
+            HueStrategy.ADD -> ColorScheme.wrapDegreesDouble(sourceHue + value)
+            HueStrategy.SUBTRACT -> ColorScheme.wrapDegreesDouble(sourceHue - value)
+        }
+    }
+}
+
+internal class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) {
+    fun shades(sourceColor: Cam): List<Int> {
+        val hue = hue.get(sourceColor.hue.toDouble())
+        val chroma = chroma.get(sourceColor.chroma.toDouble())
+        return Shades.of(hue.toFloat(), chroma.toFloat()).toList()
+    }
+}
+
+internal class CoreSpec(
+    val a1: TonalSpec,
+    val a2: TonalSpec,
+    val a3: TonalSpec,
+    val n1: TonalSpec,
+    val n2: TonalSpec
+)
+
+enum class Style(internal val coreSpec: CoreSpec) {
+    SPRITZ(CoreSpec(
+            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+            a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+            a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+            n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0))
+    )),
+    TONAL_SPOT(CoreSpec(
+            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+            a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+            a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)),
+            n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0))
+    )),
+    VIBRANT(CoreSpec(
+            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+            a2 = TonalSpec(Hue(HueStrategy.ADD, 10.0), Chroma(ChromaStrategy.EQ, 24.0)),
+            a3 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.GTE, 32.0)),
+            n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
+            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+    )),
+    EXPRESSIVE(CoreSpec(
+            a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 40.0), Chroma(ChromaStrategy.GTE, 64.0)),
+            a2 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.EQ, 24.0)),
+            a3 = TonalSpec(Hue(HueStrategy.SUBTRACT, 80.0), Chroma(ChromaStrategy.GTE, 64.0)),
+            n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0))
+    )),
+}
+
+class ColorScheme(
+    @ColorInt seed: Int,
+    val darkTheme: Boolean,
+    val style: Style = Style.TONAL_SPOT
+) {
 
     val accent1: List<Int>
     val accent2: List<Int>
@@ -47,6 +117,9 @@
     val neutral1: List<Int>
     val neutral2: List<Int>
 
+    constructor(@ColorInt seed: Int, darkTheme: Boolean):
+            this(seed, darkTheme, Style.TONAL_SPOT)
+
     constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
             this(getSeedColor(wallpaperColors), darkTheme)
 
@@ -83,14 +156,11 @@
             seed
         }
         val camSeed = Cam.fromInt(seedArgb)
-        val hue = camSeed.hue
-        val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
-        val tertiaryHue = wrapDegrees((hue + ACCENT3_HUE_SHIFT).toInt())
-        accent1 = Shades.of(hue, chroma).toList()
-        accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
-        accent3 = Shades.of(tertiaryHue.toFloat(), ACCENT3_CHROMA).toList()
-        neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
-        neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
+        accent1 = style.coreSpec.a1.shades(camSeed)
+        accent2 = style.coreSpec.a2.shades(camSeed)
+        accent3 = style.coreSpec.a3.shades(camSeed)
+        neutral1 = style.coreSpec.n1.shades(camSeed)
+        neutral2 = style.coreSpec.n2.shades(camSeed)
     }
 
     override fun toString(): String {
@@ -100,6 +170,7 @@
                 "  accent1: ${humanReadable(accent1)}\n" +
                 "  accent2: ${humanReadable(accent2)}\n" +
                 "  accent3: ${humanReadable(accent3)}\n" +
+                "  style: $style\n" +
                 "}"
     }
 
@@ -225,6 +296,20 @@
             }
         }
 
+        public fun wrapDegreesDouble(degrees: Double): Double {
+            return when {
+                degrees < 0 -> {
+                    (degrees % 360) + 360
+                }
+                degrees >= 360 -> {
+                    degrees % 360
+                }
+                else -> {
+                    degrees
+                }
+            }
+        }
+
         private fun hueDiff(a: Float, b: Float): Float {
             return 180f - ((a - b).absoluteValue - 180f).absoluteValue
         }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index ffac26b..1ef5324 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -159,9 +159,9 @@
         public Supplier<Icon> iconSupplier;
         public int state = DEFAULT_STATE;
         public CharSequence label;
-        public CharSequence secondaryLabel;
+        @Nullable public CharSequence secondaryLabel;
         public CharSequence contentDescription;
-        public CharSequence stateDescription;
+        @Nullable public CharSequence stateDescription;
         public CharSequence dualLabelContentDescription;
         public boolean disabledByPolicy;
         public boolean dualTarget = false;
@@ -170,6 +170,7 @@
         public SlashState slash;
         public boolean handlesLongClick = true;
         public boolean showRippleEffect = true;
+        @Nullable
         public Drawable sideViewCustomDrawable;
         public String spec;
 
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index dfc3e63..ecb3cb3 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -22,21 +22,6 @@
     android:layout_height="48dp"
     android:gravity="center_vertical">
 
-    <com.android.systemui.statusbar.AlphaOptimizedImageView
-        android:id="@android:id/edit"
-        android:layout_width="0dp"
-        android:layout_height="@dimen/qs_footer_action_button_size"
-        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
-        android:layout_weight="1"
-        android:background="@drawable/qs_footer_action_chip_background"
-        android:clickable="true"
-        android:clipToPadding="false"
-        android:contentDescription="@string/accessibility_quick_settings_edit"
-        android:focusable="true"
-        android:padding="@dimen/qs_footer_icon_padding"
-        android:src="@*android:drawable/ic_mode_edit"
-        android:tint="?android:attr/textColorPrimary" />
-
     <com.android.systemui.statusbar.phone.MultiUserSwitch
         android:id="@+id/multi_user_switch"
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 1e142ea..e64b586 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -46,8 +46,6 @@
             android:clipChildren="false"
             android:clipToPadding="false"
             android:paddingTop="@dimen/keyguard_security_view_top_margin"
-            android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
-            android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
             android:layout_gravity="center"
             android:gravity="center">
         </com.android.keyguard.KeyguardSecurityViewFlipper>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index e1550aa..e77e084 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -25,6 +25,7 @@
     android:layout_height="match_parent"
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
     androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+    android:layout_gravity="center_horizontal|bottom"
     android:gravity="bottom"
     >
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index f613a19..231ead8 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -28,6 +28,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+    android:layout_gravity="center_horizontal|bottom"
     android:clipChildren="false"
     android:clipToPadding="false">
 
@@ -65,6 +66,7 @@
         android:orientation="vertical"
         android:layout_gravity="bottom|center_horizontal"
         android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+        android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
         android:gravity="center_horizontal" />
 
 </com.android.keyguard.KeyguardPatternView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 94566c7..7ce6f0e 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -25,6 +25,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+        android:layout_gravity="center_horizontal|bottom"
         android:orientation="vertical"
         >
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 3e34e4b..b765f49 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -25,7 +25,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
-        android:gravity="center_horizontal">
+        android:layout_gravity="center_horizontal|bottom">
 
   <Space
       android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index d5510e9..917ea6b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -26,7 +26,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
-        android:gravity="center_horizontal">
+        android:layout_gravity="center_horizontal|bottom">
 
   <Space
       android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 74a7123..9cf1793 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Toestel is handmatig gesluit"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nie herken nie"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie herken nie"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Skakel "<b>"kameratoegang"</b>" in Instellings &gt; Privaatheid aan om Gesigslot te gebruik"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
       <item quantity="one">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.</item>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index ef97693..2d436c8 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"መሣሪያ በተጠቃሚው ራሱ ተቆልፏል"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"አልታወቀም"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"አልታወቀም"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"በመልክ መክፈትን ለመጠቀም "<b>"የካሜራ መዳረሻ"</b>"ን በቅንብሮች እና ግላዊነት ውስጥ ያብሩ"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
       <item quantity="other">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index d44a861..9c73b9d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -105,6 +105,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"تم حظر الجهاز يدويًا"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"لم يتم التعرف عليها."</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"لم يتم التعرّف عليه."</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"‏لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات &gt; الخصوصية."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="zero">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
       <item quantity="two">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index ab50326..e9c20b5 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইচটো মেনুৱেলভাৱে লক কৰা হৈছিল"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"চিনাক্ত কৰিব পৰা নাই"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"চিনাক্ত কৰিব পৰা নাই"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ফেচ আনলক সুবিধাটো ব্যৱহাৰ কৰিবলৈ ছেটিং &gt; গোপনীয়তাত "<b>"কেমেৰাৰ এক্সেছ"</b>" অন কৰক"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
       <item quantity="other">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index 6153591..f9c67cb 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihaz əl ilə kilidləndi"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tanınmır"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmır"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Üz ilə Kiliddən Açma funksiyasını istifadə etmək üçün Ayarlar &gt; Məxfilik bölməsində "<b>"Kameraya girişi"</b>" aktiv edin"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN-ni daxil edin. <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
       <item quantity="one">SIM PIN-ni daxil edin. Cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamadan öncə <xliff:g id="NUMBER_0">%d</xliff:g> cəhdiniz qalır.</item>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 52bbd4b..647e786e 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -96,6 +96,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nije prepoznat"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznat"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja &gt; Privatnost"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
       <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index d18fefa3..adcedff 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Прылада была заблакіравана ўручную"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не распазнана"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распазнана"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Каб выкарыстоўваць распазнаванне твару, уключыце "<b>"доступ да камеры"</b>" праз раздзел \"Налады &gt; Прыватнасць\""</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Увядзіце PIN-код SIM-карты. У вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
       <item quantity="few">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index 256672b..348b46c 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройството бе заключено ръчно"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не е разпознато"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не е разпознато"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"За да използвате функцията „Отключване с лице“, включете "<b>"достъпа до камерата"</b>" от „Настройки &gt; Поверителност“"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Въведете ПИН кода за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
       <item quantity="one">Въведете ПИН кода за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да се наложи да се свържете с оператора си, за да отключите устройството.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 74fed29..0dc7052 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইসটিকে ম্যানুয়ালি লক করা হয়েছে"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"শনাক্ত করা যায়নি"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"শনাক্ত করা যায়নি"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"\'ফেস আনলক\' ফিচার ব্যবহার করতে \'সেটিংস ও গোপনীয়তা\' বিকল্পে গিয়ে "<b>"ক্যামেরায় অ্যাক্সেস দিন"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
       <item quantity="other">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index ef15ce3..86238b1 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -96,6 +96,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nije prepoznato"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Da koristite otključavanje licem, uključite "<b>"Pristup kameri"</b>" u meniju Postavke &gt; Privatnost"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
       <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 5a71f37..5c315db 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositiu s\'ha bloquejat manualment"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"No s\'ha reconegut"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No s\'ha reconegut"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Per utilitzar Desbloqueig facial, activa "<b>"Accés a la càmera"</b>" a Configuració &gt; Privadesa"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Introdueix el PIN de la SIM. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
       <item quantity="one">Introdueix el PIN de la SIM. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 41300fc..f5ef1bb 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zařízení bylo ručně uzamčeno"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nerozpoznáno"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznáno"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pokud chcete používat odemknutí obličejem, v Nastavení &gt; Soukromí zapnetě "<b>"přístup k fotoaparátu"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="few">Zadejte PIN SIM karty. Zbývají <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
       <item quantity="many">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index 01ea136..c008f74 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheden blev låst manuelt"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ikke genkendt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke genkendt"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Hvis du vil bruge ansigtslåsen, skal du aktivere "<b>"Kameraadgang"</b>" under Indstillinger &gt; Privatliv"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
       <item quantity="other">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 0d6728a..87c1bf4 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Gerät manuell gesperrt"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nicht erkannt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nicht erkannt"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Wenn du die Entsperrung per Gesichtserkennung verwenden möchtest, aktiviere in den Einstellungen unter „Datenschutz“ die Option "<b>"Kamerazugriff"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
       <item quantity="one">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.</item>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 518f2e2..f04747f 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Η συσκευή κλειδώθηκε με μη αυτόματο τρόπο"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Δεν αναγνωρίστηκε"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Δεν αναγνωρίστηκε"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Για να χρησιμοποιήσετε τη λειτουργία Ξεκλείδωμα με το πρόσωπο, ενεργοποιήστε την επιλογή "<b>"Πρόσβαση στην κάμερα"</b>" από το μενού Ρυθμίσεις &gt; Απόρρητο"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες.</item>
       <item quantity="one">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index e2db3b1..eb3a5be 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 6c9ddb8..9cb8227 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index e2db3b1..eb3a5be 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index e2db3b1..eb3a5be 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 9c32604..cc61057 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎Device was locked manually‎‏‎‎‏‎"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎Not recognized‎‏‎‎‏‎"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎Not recognized‎‏‎‎‏‎"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎To use Face Unlock, turn on ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Camera access‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ in Settings &gt; Privacy‎‏‎‎‏‎"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎Enter SIM PIN. You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts.‎‏‎‎‏‎</item>
       <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎Enter SIM PIN. You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempt before you must contact your carrier to unlock your device.‎‏‎‎‏‎</item>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 7be89b6..74db06d 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se bloqueó de forma manual"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"No se reconoció"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoció"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar Desbloqueo facial, activa el "<b>"Acceso a la cámara"</b>" en Configuración y privacidad"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Ingresa el PIN de la SIM. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
       <item quantity="one">Ingresa el PIN de la SIM. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index b0f9f33..d833b06 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se ha bloqueado manualmente"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"No se reconoce"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoce"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar Desbloqueo Facial, habilita el "<b>"acceso a la cámara"</b>" en Ajustes y privacidad"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Introduce el PIN de la tarjeta SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
       <item quantity="one">Introduce el PIN de la tarjeta SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 58b870f..0cd86dd 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Seade lukustati käsitsi"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ei tuvastatud"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tuvastatud"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Näoga avamise kasutamiseks lülitage menüüs Seaded &gt; Privaatsus sisse "<b>"juurdepääs kaamerale"</b>"."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
       <item quantity="one">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks ühendust võtma operaatoriga.</item>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 2366156..580399d 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Eskuz blokeatu da gailua"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ez da ezagutu"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera atzitzeko baimena"</b>" Ezarpenak &gt; Pribatutasuna atalean"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Idatzi SIMaren PINa. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
       <item quantity="one">Idatzi SIMaren PINa. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index e110d08..a21507e 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"دستگاه به‌صورت دستی قفل شده است"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"شناسایی نشد"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"شناسایی نشد"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"‏برای استفاده از «قفل‌گشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات &gt; حریم‌خصوصی» روشن کنید"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">پین سیم‌کارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
       <item quantity="other">پین سیم‌کارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 82927e1..f0826e5 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Laite lukittiin manuaalisesti"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ei tunnistettu"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tunnistettu"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Jos haluat käyttää kasvojentunnistusavausta, valitse Asetukset &gt; Yksityisyys ja laita "<b>"Pääsy kameraan"</b>" päälle"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Anna SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
       <item quantity="one">Anna SIM-kortin PIN-koodi. <xliff:g id="NUMBER_0">%d</xliff:g> yrityksen jälkeen laite lukittuu, ja vain operaattori voi avata sen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 3646394..dada709 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"L\'appareil a été verrouillé manuellement"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Doigt non reconnu"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Doigt non reconnu"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pour utiliser le déverrouillage par reconnaissance faciale, activez l\'"<b>"accès à l\'appareil photo"</b>" dans Paramètres &gt; Confidentialité"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
       <item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index 6e1ee96..d19db43 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Appareil verrouillé manuellement"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non reconnu"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Non reconnu"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pour utiliser Face Unlock, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres &gt; Confidentialité"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
       <item quantity="other">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 123b8c1..bfbcf9d7 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo bloqueouse manualmente"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non se recoñeceu"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Non se recoñeceu"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar o desbloqueo facial, activa "<b>"Acceso á cámara"</b>" en Configuración &gt; Privacidade"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Introduce o código PIN da SIM. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
       <item quantity="one">Introduce o código PIN da SIM. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 1ca39d7..c804ec0 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ઉપકરણ મેન્યુઅલી લૉક કર્યું હતું"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ઓળખાયેલ નથી"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ઓળખાયેલ નથી"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ફેસ અનલૉક સુવિધાનો ઉપયોગ કરવા માટે, સેટિંગ &gt; પ્રાઇવસીમાં જઈને "<b>"કૅમેરા ઍક્સેસ"</b>" ચાલુ કરો"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
       <item quantity="other">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index e017a6b..a571a2a 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिवाइस को मैन्युअल रूप से लॉक किया गया था"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"पहचान नहीं हो पाई"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"पहचान नहीं हो पाई"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"फ़ेस अनलॉक की सुविधा का इस्तेमाल करने के लिए, सेटिंग और निजता में जाकर, "<b>"कैमरे का ऐक्सेस"</b>" चालू करें"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
       <item quantity="other">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 123f423..1a40d46 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -96,6 +96,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nije prepoznat"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Da biste koristili otključavanje licem, uključite opciju "<b>"Pristup kameri"</b>" u odjeljku Postavke &gt; Privatnost"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
       <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index 47b49a4..47a1c61 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Az eszközt manuálisan lezárták"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nem ismerhető fel"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nem ismerhető fel"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Az Arcalapú feloldás funkció használatához kapcsolja be a "<b>"Hozzáférés a kamerához"</b>" beállítást a Beállítások &gt; Adatvédelem szakaszban."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
       <item quantity="one">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 16bbb07..923d762 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Սարքը կողպվել է ձեռքով"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Չհաջողվեց ճանաչել"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Չհաջողվեց ճանաչել"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Դեմքով ապակողպումն օգտագործելու համար անցեք Կարգավորումներ &gt; Գաղտնիություն և տրամադրեք "<b>"տեսախցիկն օգտագործելու թույլտվություն"</b>"։"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
       <item quantity="other">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index 757e6a5..559069b 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Perangkat dikunci secara manual"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tidak dikenali"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Untuk menggunakan Face Unlock, aktifkan "<b>"Akses kamera"</b>" di Setelan &gt; Privasi"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
       <item quantity="one">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 6dc8246..bec957f 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Tækinu var læst handvirkt"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Þekktist ekki"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Þekktist ekki"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Þú verður að kveikja á "<b>"aðgangi að myndavél"</b>" í „Stillingar &gt; persónuvernd“ til að nota andlitskenni"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
       <item quantity="other">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 337d433..656a8bc 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Il dispositivo è stato bloccato manualmente"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non riconosciuto"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Non riconosciuto"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Per utilizzare lo sblocco con il volto, attiva "<b>"l\'accesso alla fotocamera"</b>" in Impostazioni &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="other">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 1ba6f83..e241beb 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"המכשיר ננעל באופן ידני"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"לא זוהתה"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"לא זוהתה"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"‏כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות &gt; פרטיות"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="two">‏יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
       <item quantity="many">‏יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 1acc14c..6e6adba 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"デバイスは手動でロックされました"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"認識されませんでした"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"認識されませんでした"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"顔認証を使用するには、[設定] &gt; [プライバシー] で"<b>"カメラへのアクセス"</b>"を有効にしてください"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。</item>
       <item quantity="one">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えた場合は、携帯通信会社にお問い合わせください。</item>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 2f38e64..c728471 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"მოწყობილობა ხელით ჩაიკეტა"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"არ არის ამოცნობილი"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"არ არის ამოცნობილი"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"იმისთვის, რომ სახით განბლოკვით ისარგებლოთ, ჩართეთ "<b>"კამერაზე წვდომა"</b>" პარამეტრებსა და კონფიდენციალურობაში"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა.</item>
       <item quantity="one">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც მოწყობილობის განსაბლოკად დაგჭირდებათ თქვენს ოპერატორთან დაკავშირება.</item>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index c7a1713..b192f02 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Құрылғы қолмен құлыпталды"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Танылмады"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Танылмады"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Face Unlock функциясын пайдалану үшін \"Параметрлер &gt; Құпиялылық\" бөлімінен "<b>"Камераны пайдалану рұқсатын"</b>" қосыңыз."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
       <item quantity="one">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 388d4fc..e31621e 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ឧបករណ៍ត្រូវបានចាក់សោដោយអ្នកប្រើផ្ទាល់"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"មិនអាចសម្គាល់បានទេ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"មិនអាចសម្គាល់បានទេ"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ដើម្បីប្រើមុខងារដោះសោតាមទម្រង់មុខ សូមបើក"<b>"ការចូលប្រើកាមេរ៉ា"</b>"នៅក្នុងការកំណត់ &gt; ឯកជនភាព"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">បញ្ចូល​កូដ PIN របស់ស៊ីម។ អ្នកនៅ​សល់ការ​ព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
       <item quantity="one">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នក​នៅសល់​ការព្យាយាម <xliff:g id="NUMBER_0">%d</xliff:g> ដង​ទៀត មុន​ពេល​ដែលអ្នក​ត្រូវទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​របស់អ្នក​ដើម្បី​ដោះសោ​ឧបករណ៍​របស់អ្នក។</item>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 9279fad..eeb8cbf 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ಸಾಧನವನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಲಾಕ್‌ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳು &gt; ಗೌಪ್ಯತೆ ಎಂಬಲ್ಲಿ "<b>"ಕ್ಯಾಮರಾ ಪ್ರವೇಶವನ್ನು"</b>" ಆನ್ ಮಾಡಿ"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
       <item quantity="other">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 761ccfa..e9b83d1 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"기기가 수동으로 잠금 설정되었습니다."</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"인식할 수 없음"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"인식할 수 없음"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"얼굴 인식 잠금 해제를 사용하려면 설정 &gt; 개인 정보 보호에서 "<b>"카메라 액세스"</b>"를 사용 설정하세요."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN을 입력하세요. 입력은 <xliff:g id="NUMBER_1">%d</xliff:g>번 더 시도할 수 있습니다.</item>
       <item quantity="one">SIM PIN을 입력하세요. 입력에 <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패하면 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 216c978..dd2c2f7 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Түзмөк кол менен кулпуланды"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Таанылган жок"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Таанылган жок"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр &gt; Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
       <item quantity="one">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды, андан кийин түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуна кайрылышыңыз керек болот.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index a9dd139..611c666 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ອຸປະກອນຖືກສັ່ງໃຫ້ລັອກ"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ບໍ່ຮູ້ຈັກ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ບໍ່ຮູ້ຈັກ"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ກະລຸນາເປີດໃຊ້ "<b>"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</b>" ໃນການຕັ້ງຄ່າ &gt; ຄວາມເປັນສ່ວນຕົວ"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">ລະຫັດ SIM PIN ບໍ່ຖືກຕ້ອງ. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ.</item>
       <item quantity="one">ໃສ່ລະຫັດ SIM PIN. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ຈະຕ້ອງຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການເພື່ອປົດລັອກ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 20164a1..08ddb74 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Įrenginys užrakintas neautomatiškai"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Neatpažinta"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Neatpažinta"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Jei norite naudoti atrakinimą pagal veidą, įjunkite parinktį "<b>"Prieiga prie fotoaparato"</b>" skiltyje „Nustatymai“ &gt; „Privatumas“"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
       <item quantity="few">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 238b706..13fcfae 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -96,6 +96,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Ierīce tika bloķēta manuāli."</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nav atpazīts"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nav atpazīts"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Lai izmantotu autorizāciju pēc sejas, sadaļā Iestatījumi &gt; Konfidencialitāte ieslēdziet opciju "<b>"Piekļuve kamerai"</b>"."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="zero">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
       <item quantity="one">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index f4c3dde..5cf025c 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уредот е заклучен рачно"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Непознат"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Непознат"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"За да користите „Отклучување со лик“, вклучете "<b>"Пристап до камерата"</b>" во „Поставки &gt; Приватност“"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Внесете PIN-код за SIM-картичката. Ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
       <item quantity="other">Внесете PIN-код за SIM-картичката. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index 06228c6c..30e2081 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ഉപകരണം നേരിട്ട് ലോക്കുചെയ്തു"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"തിരിച്ചറിയുന്നില്ല"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"തിരിച്ചറിയുന്നില്ല"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ഫെയ്‌സ് അൺലോക്ക് ഉപയോഗിക്കാൻ, ക്രമീകരണം &gt; സ്വകാര്യത എന്നതിൽ "<b>"ക്യാമറാ ആക്‌സസ്"</b>" ഓണാക്കുക"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">സിം പിൻ നൽകുക. നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
       <item quantity="one">സിം പിൻ നൽകുക. ഉപകരണം അൺലോക്ക് ചെയ്യാൻ കാരിയറുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിന് മുമ്പ് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index db1396b..0ea710c 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Төхөөрөмжийг гараар түгжсэн"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Таньж чадсангүй"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Таньж чадсангүй"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо &gt; Нууцлал хэсэгт "<b>" Камерын хандалтыг "</b>" асаана уу"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM-н ПИН кодыг оруулна уу. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
       <item quantity="one">SIM-н ПИН кодыг оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index b83ef6bb..647d132 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिव्हाइस मॅन्युअली लॉक केले होते"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ओळखले नाही"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ओळखले नाही"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"फेस अनलॉक वापरण्यासाठी, सेटिंग्ज &gt; गोपनीयता येथे "<b>"कॅमेरा अ‍ॅक्सेस"</b>" सुरू करा"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
       <item quantity="one">सिम पिन एंटर करा. तुम्ही तुमचे डिव्‍हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधण्‍यापूर्वी, तुमच्याकडे <xliff:g id="NUMBER_0">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 4205a2d..88dfd68 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Peranti telah dikunci secara manual"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tidak dikenali"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Untuk menggunakan Buka Kunci Wajah, hidupkan "<b>"akses Kamera"</b>" dalam Tetapan &gt; Privasi"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan lagi.</item>
       <item quantity="one">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan lagi sebelum anda perlu menghubungi pembawa anda untuk membuka kunci peranti.</item>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 6ce5c05..de879b1 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"စက်ပစ္စည်းကို ကိုယ်တိုင်ကိုယ်ကျ လော့ခ်ချထားခဲ့သည်"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"မသိ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"မသိ"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံးရန် "<b>"ကင်မရာ သုံးခွင့်"</b>" ကို ‘ဆက်တင်များ &gt; ကန့်သတ်ဆက်တင်’ တွင်ဖွင့်ပါ"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">ဆင်းမ်ကတ် ပင်နံပါတ် ထည့်ပါ။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
       <item quantity="one">ဆင်းမ်ကတ် ပင်နံပါတ် ထည့်ပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ပေးရန်အတွက် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 890afc0..c7d6613 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten ble låst manuelt"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ikke gjenkjent"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke gjenkjent"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"For å bruke ansiktslås, slå på "<b>"Kameratilgang"</b>" i Innstillinger &gt; Personvern"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
       <item quantity="one">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 57d9d95..16ece5d 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"यन्त्रलाई म्यानुअल तरिकाले लक गरिएको थियो"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"पहिचान भएन"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"पहिचान भएन"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"फेस अनलक प्रयोग गर्न \"सेटिङ तथा गोपनीयता\" मा गई "<b>"क्यामेरा प्रयोग गर्ने अनुमति"</b>" दिनुहोस्"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g>  प्रयासहरू बाँकी छन्।</item>
       <item quantity="one">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो डिभाइस अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 11f29ef..bf1906d 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Apparaat is handmatig vergrendeld"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Niet herkend"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Niet herkend"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Als je Ontgrendelen via gezichtsherkenning wilt gebruiken, zet je "<b>"Cameratoegang"</b>" aan via Instellingen &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
       <item quantity="one">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om het apparaat te ontgrendelen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index 8a6c2449..3b20dcb 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ଡିଭାଇସ୍‍ ମାନୁଆଲ ଭାବେ ଲକ୍‍ କରାଗଲା"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ଫେସ ଅନଲକ ବ୍ୟବହାର କରିବା ପାଇଁ, ସେଟିଂସ ଏବଂ ଗୋପନୀୟତାରେ "<b>"କ୍ୟାମେରା ଆକ୍ସେସ"</b>"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ପାଇଁ <xliff:g id="NUMBER_1">%d</xliff:g>ଟି ପ୍ରୟାସ ବଳକା ଅଛି।</item>
       <item quantity="one">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ଅନଲକ୍ କରିବା ପାଇଁ ପାଖରେ ବଳକା ଥିବା <xliff:g id="NUMBER_0">%d</xliff:g>ଟି ପ୍ରୟାସର ବ୍ୟବହାର କରିବା ପୂର୍ବରୁ ନିଜର କେରିଅର୍‍ଙ୍କୁ ସମ୍ପର୍କ କରନ୍ତୁ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index f40e09e..afc8897 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ਡੀਵਾਈਸ ਨੂੰ ਹੱਥੀਂ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ਫ਼ੇਸ ਅਣਲਾਕ ਨੂੰ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ &gt; ਪਰਦੇਦਾਰੀ ਵਿੱਚ ਜਾ ਕੇ "<b>"ਕੈਮਰਾ ਪਹੁੰਚ"</b>" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
       <item quantity="other">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 2d53dcc..0b45e36 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Urządzenie zostało zablokowane ręcznie"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nie rozpoznano"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie rozpoznano"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Aby używać rozpoznawania twarzy, włącz "<b>"dostęp do aparatu"</b>" w Ustawieniach i prywatności"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="few">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
       <item quantity="many">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 9fd9b1f..d69b62b 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Não reconhecido"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações &gt; Privacidade"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
       <item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index da663f0..d3f5802 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Não reconhecido."</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido."</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para utilizar o Desbloqueio facial, ative o "<b>"Acesso à câmara"</b>" em Definições &gt; Privacidade"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.</item>
       <item quantity="other">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 9fd9b1f..d69b62b 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Não reconhecido"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações &gt; Privacidade"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
       <item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index be6aea0..2671076 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -96,6 +96,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Dispozitivul a fost blocat manual"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nu este recunoscută"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nu este recunoscut"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="few">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
       <item quantity="other">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index 6e11d61..a91eef5 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройство было заблокировано вручную"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не распознано"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распознано"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Чтобы использовать фейсконтроль, разрешите "<b>"доступ к камере"</b>". Для этого перейдите в настройки и нажмите \"Конфиденциальность\"."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Введите PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
       <item quantity="few">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 902127d..d06c4a9 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"උපාංගය හස්තීයව අගුලු දමන ලදී"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"හඳුනා නොගන්නා ලදී"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"හඳුනා නොගන්නා ලදී"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"මුහුණෙන් අගුලු හැරීම භාවිත කිරීමට, සැකසීම් &gt; පෞද්ගලිකත්වය තුළ "<b>"කැමරා ප්‍රවේශය"</b>" ක්‍රියාත්මක කරන්න"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
       <item quantity="other">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 5e4c248..a48123e 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zariadenie bolo uzamknuté ručne"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nerozpoznané"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznané"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Ak chcete používať odomknutie tvárou, v sekcii Nastavenia &gt; Ochrana súkromia zapnite "<b>"prístup ku kamere"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="few">Zadajte kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
       <item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 149ac8e..4af9fe5 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Naprava je bila ročno zaklenjena"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ni prepoznano"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ni prepoznano"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Če želite uporabljati odklepanje z obrazom, v meniju »Nastavitve« &gt; »Zasebnost« vklopite možnost "<b>"Dostop do fotoaparata"</b>"."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
       <item quantity="two">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index 5fca45bb..524f1c3 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Pajisja është kyçur manualisht"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nuk njihet"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nuk njihet"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Për të përdorur \"Shkyçjen me fytyrë\", aktivizo "<b>"Qasjen te kamera"</b>" te \"Cilësimet\" &gt; \"Privatësia\""</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Fut kodin PIN të kartës SIM. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
       <item quantity="one">Fut kodin PIN të kartës SIM. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të duhet të kontaktosh me operatorin tënd celular për ta shkyçur pajisjen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 7facbfb..0bb2cb4 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -96,6 +96,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уређај је ручно закључан"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Није препознат"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Није препознат"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања &gt; Приватност"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
       <item quantity="few">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index f271dda..2af9bb5 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten har låsts manuellt"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Identifierades inte"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Identifierades inte"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Om du vill använda ansiktslås aktiverar du "<b>"Kameraåtkomst"</b>" i Inställningar &gt; Integritet"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
       <item quantity="one">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index c84bef0..aaad8b9 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Umefunga kifaa mwenyewe"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Haitambuliwi"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Haitambuliwi"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Ili utumie kipengele cha kufungua kwa uso, washa kipengele cha "<b>"ufikiaji wa Kamera"</b>" katika Mipangilio na Faragha"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Weka PIN ya SIM. Zimesalia mara <xliff:g id="NUMBER_1">%d</xliff:g> za kujaribu.</item>
       <item quantity="one">Weka PIN ya SIM. Ukijaribu tena mara <xliff:g id="NUMBER_0">%d</xliff:g> bila kufaulu, kifaa chako kitafungwa na utalazimika uwasiliane na mtoa huduma wako ili akifungue.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
deleted file mode 100644
index 54bb1fc..0000000
--- a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<resources>
-    <!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
-         switch sides -->
-    <bool name="can_use_one_handed_bouncer">false</bool>
-
-    <!-- Will display the bouncer on one side of the display, and the current user icon and
-         user switcher on the other side -->
-    <bool name="bouncer_display_user_switcher">true</bool>
-</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 151279e..ed9a5d4 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"பயனர் சாதனத்தைப் பூட்டியுள்ளார்"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"அடையாளங்காணபடவில்லை"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"அடையாளங்காணபடவில்லை"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகள் &gt; தனியுரிமை என்பதற்குச் சென்று "<b>"கேமரா அணுகலை"</b>" இயக்கவும்"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">சிம் பின்னை உள்ளிடவும். மேலும், <xliff:g id="NUMBER_1">%d</xliff:g> வாய்ப்புகள் மீதமுள்ளன.</item>
       <item quantity="one">சிம் பின்னை உள்ளிடவும். மீதமுள்ள <xliff:g id="NUMBER_0">%d</xliff:g> வாய்ப்பில் தவறுதலான பின் உள்ளிடப்பட்டால், உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்பு கொண்டு மட்டுமே சாதனத்தை அன்லாக் செய்ய முடியும்.</item>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index dfb0c81..0c184ab 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"పరికరం మాన్యువల్‌గా లాక్ చేయబడింది"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"గుర్తించలేదు"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"గుర్తించలేదు"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ఫేస్ అన్‌లాక్‌ను ఉపయోగించడానికి, సెట్టింగ్‌లు &gt; గోప్యతలో "<b>"కెమెరా యాక్సెస్"</b>"ను ఆన్ చేయండి"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM పిన్‌ని నమోదు చేయండి. మీకు <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నలు మిగిలి ఉన్నాయి.</item>
       <item quantity="one">SIM పిన్‌ని నమోదు చేయండి, మీరు మీ పరికరాన్ని అన్‌లాక్ చేయడానికి తప్పనిసరిగా మీ క్యారియర్‌ను సంప్రదించడానికి ముందు మీకు <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది.</item>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 25968b7..4d3c0b5 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"มีการล็อกอุปกรณ์ด้วยตัวเอง"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ไม่รู้จัก"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จัก"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"หากต้องการใช้ปลดล็อกด้วยใบหน้า ให้เปิด"<b>"การเข้าถึงกล้อง"</b>"ในการตั้งค่าและความเป็นส่วนตัว"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
       <item quantity="one">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์</item>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index 8927c9e..4c391e5 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Manual na na-lock ang device"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Hindi nakilala"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Hindi nakilala"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para magamit ang Pag-unlock Gamit ang Mukha, i-on ang "<b>"Access sa camera"</b>" sa Mga Setting &gt; Privacy"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
       <item quantity="other">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 5ba351a..100f074d 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihazın manuel olarak kilitlendi"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tanınmadı"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmadı"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Yüz Tanıma Kilidi\'ni kullanmak için Ayarlar &gt; Gizlilik bölümünden "<b>"Kamera erişimi"</b>"\'ni açın"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN\'inizi girin. <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
       <item quantity="one">SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 96f2ea2..a915f57 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -99,6 +99,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Пристрій заблоковано вручну"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не розпізнано"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не розпізнано"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Щоб використовувати фейсконтроль, увімкніть "<b>"Доступ до камери"</b>" в розділі \"Налаштування\" &gt; \"Конфіденційність\""</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Введіть PIN-код SIM-карти. Залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
       <item quantity="few">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 3ae43f9..66bc9d6 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"آلہ کو دستی طور پر مقفل کیا گیا تھا"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"تسلیم شدہ نہیں ہے"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"تسلیم شدہ نہیں ہے"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"فیس اَنلاک کا استعمال کرنے کے لیے، ترتیبات اور رازداری میں "<b>"کیمرے تک رسائی"</b>" کو آن کریں"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">‏SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔</item>
       <item quantity="one">‏SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد آپ کو اپنا آلہ غیر مقفل کرنے کے لیے اپنے کیریئر سے رابطہ کرنا ہوگا۔</item>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 5d281fa..1046dbd 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Qurilma qo‘lda qulflangan"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Aniqlanmadi"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Aniqlanmadi"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Yuz bilan ochish uchun Sozlamalar va maxfiylik orqali "<b>"kameraga kirishga ruxsat bering"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">SIM PIN kodini kiriting, sizda <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish bor.</item>
       <item quantity="one">SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish bor.</item>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 52d64b9..569b99e 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Thiết bị đã bị khóa theo cách thủ công"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Không nhận dạng được"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Không nhận dạng được"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Để dùng tính năng Mở khoá bằng khuôn mặt, hãy bật tuỳ chọn "<b>"Truy cập máy ảnh"</b>" trong phần Cài đặt &gt; Quyền riêng tư"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
       <item quantity="one">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi phải liên hệ với nhà mạng để mở khóa thiết bị của mình.</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index 4be7908..e429994 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"此设备已手动锁定"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"无法识别"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"无法识别"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"如需使用人脸解锁功能,请在“设置”&gt;“隐私权”中开启"<b>"摄像头使用权限"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
       <item quantity="one">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍不正确,则需要联系运营商帮您解锁设备。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 7ca7677..d870304 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"使用者已手動將裝置上鎖"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"未能識別"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"未能識別"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"如要使用「面孔解鎖」,請在 [設定] &gt; [私隱] 開啟"<b>"相機存取權"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
       <item quantity="one">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index d3802c5..99a00fd 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"裝置已手動鎖定"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"無法識別"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"無法識別"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"如要使用人臉解鎖功能,請前往「設定」&gt;「隱私權」開啟"<b>"攝影機存取權"</b></string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="other">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
       <item quantity="one">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,就必須請電信業者為裝置解鎖。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 5440b00..4c1ca1c 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -93,6 +93,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Idivayisi ikhiywe ngokwenza"</string>
     <string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Akwaziwa"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Akwaziwa"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Ukuze usebenzise Ukuvula ngobuso, vula "<b>"Ukufinyelela kwekhamera"</b>" kokuthi Amasethingi &gt; Ubumfihlo"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
       <item quantity="one">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
       <item quantity="other">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6194aa0..e824443 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -27,6 +27,6 @@
     <bool name="can_use_one_handed_bouncer">false</bool>
     <!-- Will display the bouncer on one side of the display, and the current user icon and
          user switcher on the other side -->
-    <bool name="bouncer_display_user_switcher">false</bool>
+    <bool name="config_enableBouncerUserSwitcher">false</bool>
 
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index c8bb8e9..cbf4f83 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -37,7 +37,6 @@
 
     <!-- Margin around the various security views -->
     <dimen name="keyguard_security_view_top_margin">8dp</dimen>
-    <dimen name="keyguard_security_view_lateral_margin">20dp</dimen>
 
     <dimen name="keyguard_eca_top_margin">18dp</dimen>
     <dimen name="keyguard_eca_bottom_margin">12dp</dimen>
diff --git a/packages/SystemUI/res-product/values-zu/strings.xml b/packages/SystemUI/res-product/values-zu/strings.xml
index e6c140a..8b79a22 100644
--- a/packages/SystemUI/res-product/values-zu/strings.xml
+++ b/packages/SystemUI/res-product/values-zu/strings.xml
@@ -40,7 +40,7 @@
     <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Uzame ngokungalungile ukuvula ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Iphrofayela yomsebenzi izosuswa, okuzosusa yonke idatha yephrofayela."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Udwebe ngokungalungile iphethini yakho yokuvula ngezikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kwemizamo engaphumelelanga kaningi engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuthi uvule ithebulethi yakho usebenzisa i-akhawunti ye-imeyili.\n\nZama futhi kumasekhondi angu-<xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%1$d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%3$d</xliff:g> imizuzwana."</string>
-    <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Vula ifoni yakho ukuthola izinketho ezengeziwe"</string>
-    <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Vula ithebulethi yakho ukuthola izinketho ezengeziwe"</string>
-    <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Vula idivayisi yakho ukuthola izinketho ezengeziwe"</string>
+    <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Vula ifoni yakho ukuthola okunye okungakhethwa"</string>
+    <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Vula ithebulethi yakho ukuthola okunye okungakhethwa"</string>
+    <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Vula idivayisi yakho ukuthola okunye okungakhethwa"</string>
 </resources>
diff --git a/packages/SystemUI/res/drawable/ic_baseline_devices_24.xml b/packages/SystemUI/res/drawable/ic_baseline_devices_24.xml
new file mode 100644
index 0000000..61c32b22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_baseline_devices_24.xml
@@ -0,0 +1,27 @@
+<!--
+  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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4,6h18L22,4L4,4c-1.1,0 -2,0.9 -2,2v11L0,17v3h14v-3L4,17L4,6zM23,8h-6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1L24,9c0,-0.55 -0.45,-1 -1,-1zM22,17h-4v-7h4v7z"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_qs_color_correction.xml b/packages/SystemUI/res/drawable/ic_qs_color_correction.xml
new file mode 100644
index 0000000..f83cabd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_color_correction.xml
@@ -0,0 +1,24 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M3,21v-4.75l8.95,-8.95 -1.45,-1.4 1.45,-1.4 1.9,1.9 3.1,-3.1q0.275,-0.275 0.7,-0.275 0.425,0 0.7,0.275l2.35,2.35q0.275,0.275 0.275,0.7 0,0.425 -0.275,0.7l-3.075,3.075 1.9,1.95L18.1,13.5l-1.4,-1.45L7.75,21zM5,19h1.95l8.3,-8.35 -1.9,-1.9L5,17.05z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_warning.xml b/packages/SystemUI/res/drawable/ic_warning.xml
new file mode 100644
index 0000000..fbed779
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_warning.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
+    <path android:fillColor="@android:color/white" android:pathData="M12,12.5zM1,21L12,2l11,19zM11,15h2v-5h-2zM12,18q0.425,0 0.713,-0.288Q13,17.425 13,17t-0.287,-0.712Q12.425,16 12,16t-0.713,0.288Q11,16.575 11,17t0.287,0.712Q11.575,18 12,18zM4.45,19h15.1L12,6z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
index ed8f61a..6fa9eac 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
@@ -15,7 +15,7 @@
 -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android">
     <shape>
-        <solid android:color="@color/qs_detail_transition"/>
+        <solid android:color="@android:color/transparent"/>
         <corners android:radius="?android:attr/dialogCornerRadius" />
     </shape>
 </inset>
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
index e5c7352..c23649d 100644
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ b/packages/SystemUI/res/drawable/qs_detail_background.xml
@@ -17,7 +17,7 @@
     <item>
         <inset>
             <shape>
-                <solid android:color="@color/qs_detail_transition"/>
+                <solid android:color="@android:color/transparent"/>
                 <corners android:radius="@dimen/qs_footer_action_corner_radius" />
             </shape>
         </inset>
diff --git a/packages/SystemUI/res/layout/clipboard_content_preview.xml b/packages/SystemUI/res/layout/clipboard_content_preview.xml
new file mode 100644
index 0000000..7317a94
--- /dev/null
+++ b/packages/SystemUI/res/layout/clipboard_content_preview.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:app="http://schemas.android.com/apk/res-auto"
+             android:id="@+id/preview_border"
+             android:elevation="9dp"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:layout_marginStart="@dimen/screenshot_offset_x"
+             android:layout_marginBottom="@dimen/screenshot_offset_y"
+             android:layout_gravity="bottom|start"
+             app:layout_constraintStart_toStartOf="parent"
+             app:layout_constraintBottom_toBottomOf="parent"
+             android:clipToPadding="false"
+             android:clipChildren="false"
+             android:padding="4dp"
+             android:background="@drawable/screenshot_border"
+             >
+    <FrameLayout
+        android:elevation="0dp"
+        android:background="@drawable/screenshot_preview_background"
+        android:clipChildren="true"
+        android:clipToOutline="true"
+        android:clipToPadding="true"
+        android:layout_width="@dimen/screenshot_x_scale"
+        android:layout_height="wrap_content">
+        <TextView android:id="@+id/text_preview"
+                  android:textFontWeight="500"
+                  android:padding="8dp"
+                  android:gravity="center|start"
+                  android:ellipsize="end"
+                  android:autoSizeTextType="uniform"
+                  android:autoSizeMinTextSize="10sp"
+                  android:autoSizeMaxTextSize="200sp"
+                  android:textColor="?android:attr/textColorPrimary"
+                  android:layout_width="@dimen/screenshot_x_scale"
+                  android:layout_height="@dimen/screenshot_x_scale"/>
+        <ImageView
+            android:id="@+id/image_preview"
+            android:scaleType="fitCenter"
+            android:adjustViewBounds="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+    </FrameLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
new file mode 100644
index 0000000..8f6753a
--- /dev/null
+++ b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/copy_button"
+        style="@android:style/Widget.DeviceDefault.Button.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:layout_marginTop="8dp"
+        android:text="@string/clipboard_edit_text_copy"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/attribution"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        app:layout_constraintStart_toStartOf="@+id/copy_button"
+        app:layout_constraintTop_toBottomOf="@+id/copy_button" />
+
+    <ImageButton
+        android:id="@+id/share"
+        style="@android:style/Widget.Material.Button.Borderless"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginEnd="8dp"
+        android:padding="12dp"
+        android:scaleType="fitCenter"
+        android:contentDescription="@*android:string/share"
+        android:tooltipText="@*android:string/share"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/copy_button"
+        android:src="@drawable/ic_screenshot_share" />
+
+    <ScrollView
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginTop="8dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/copy_button"
+        app:layout_constraintTop_toBottomOf="@+id/attribution">
+
+        <EditText
+            android:id="@+id/edit_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="start|top"
+            android:textSize="24sp" />
+    </ScrollView>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
new file mode 100644
index 0000000..76280d8
--- /dev/null
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<com.android.systemui.clipboardoverlay.DraggableConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_gravity="bottom"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <ImageView
+        android:id="@+id/actions_container_background"
+        android:visibility="gone"
+        android:layout_height="0dp"
+        android:layout_width="0dp"
+        android:elevation="1dp"
+        android:background="@drawable/action_chip_container_background"
+        android:layout_marginStart="@dimen/screenshot_action_container_margin_horizontal"
+        app:layout_constraintBottom_toBottomOf="@+id/actions_container"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/actions_container"
+        app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
+    <HorizontalScrollView
+        android:id="@+id/actions_container"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal"
+        android:paddingEnd="@dimen/screenshot_action_container_padding_right"
+        android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
+        android:elevation="1dp"
+        android:scrollbars="none"
+        app:layout_constraintHorizontal_bias="0"
+        app:layout_constraintWidth_percent="1.0"
+        app:layout_constraintWidth_max="wrap"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/preview_border"
+        app:layout_constraintEnd_toEndOf="parent">
+        <LinearLayout
+            android:id="@+id/actions"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+            <include layout="@layout/screenshot_action_chip"
+                     android:id="@+id/remote_copy_chip"/>
+            <include layout="@layout/screenshot_action_chip"
+                     android:id="@+id/edit_chip"/>
+        </LinearLayout>
+    </HorizontalScrollView>
+    <include layout="@layout/clipboard_content_preview" />
+</com.android.systemui.clipboardoverlay.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/contaminant_dialog.xml b/packages/SystemUI/res/layout/contaminant_dialog.xml
index ea6d900..5f8c305 100644
--- a/packages/SystemUI/res/layout/contaminant_dialog.xml
+++ b/packages/SystemUI/res/layout/contaminant_dialog.xml
@@ -63,7 +63,8 @@
 
         <TextView
             android:id="@+id/learnMore"
-            style="@style/USBContaminant.UserAction" />
+            style="@style/USBContaminant.UserAction"
+            android:visibility="gone" />
 
         <View
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
new file mode 100644
index 0000000..f898ef6
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/dream_overlay_complications_layer"
+    android:padding="20dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextClock
+        android:id="@+id/time_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:fontFamily="sans-serif-thin"
+        android:format12Hour="h:mm"
+        android:format24Hour="kk:mm"
+        android:shadowColor="#B2000000"
+        android:shadowRadius="2.0"
+        android:singleLine="true"
+        android:textSize="72sp"
+        app:layout_constraintBottom_toTopOf="@+id/date_view"
+        app:layout_constraintStart_toStartOf="parent" />
+    <TextClock
+        android:id="@+id/date_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:shadowColor="#B2000000"
+        android:shadowRadius="2.0"
+        android:format12Hour="EEE, MMM d"
+        android:format24Hour="EEE, MMM d"
+        android:singleLine="true"
+        android:textSize="18sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@+id/time_view"
+        app:layout_constraintStart_toStartOf="@+id/time_view" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4929f50..c6b502ec 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -28,6 +28,8 @@
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
 
+    <include layout="@layout/dream_overlay_complications_layer" />
+
     <com.android.systemui.dreams.DreamOverlayStatusBarView
         android:id="@+id/dream_overlay_status_bar"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index e69582f..f72a8dc 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -378,7 +378,8 @@
                         android:clickable="true"/>
                 </LinearLayout>
             </LinearLayout>
-            <FrameLayout
+
+            <LinearLayout
                 android:id="@+id/button_layout"
                 android:orientation="horizontal"
                 android:layout_width="match_parent"
@@ -390,9 +391,10 @@
                 android:clickable="false"
                 android:focusable="false">
 
-                <FrameLayout
+                <LinearLayout
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
+                    android:layout_weight="1"
                     android:layout_gravity="start|center_vertical"
                     android:orientation="vertical">
                     <Button
@@ -401,12 +403,13 @@
                         android:layout_height="wrap_content"
                         android:text="@string/turn_off_airplane_mode"
                         android:ellipsize="end"
+                        android:maxLines="1"
                         style="@style/Widget.Dialog.Button.BorderButton"
                         android:clickable="true"
                         android:focusable="true"/>
-                </FrameLayout>
+                </LinearLayout>
 
-                <FrameLayout
+                <LinearLayout
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_marginStart="16dp"
@@ -417,10 +420,13 @@
                         android:layout_height="wrap_content"
                         android:text="@string/inline_done_button"
                         style="@style/Widget.Dialog.Button"
+                        android:maxLines="1"
+                        android:ellipsize="end"
                         android:clickable="true"
                         android:focusable="true"/>
-                </FrameLayout>
-            </FrameLayout>
+                </LinearLayout>
+            </LinearLayout>
+
         </LinearLayout>
     </androidx.core.widget.NestedScrollView>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_media_container.xml b/packages/SystemUI/res/layout/keyguard_media_container.xml
new file mode 100644
index 0000000..c717e37
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_media_container.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- Layout for media controls on the lockscreen -->
+<com.android.systemui.statusbar.notification.stack.MediaContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="0dp"
+    android:paddingEnd="0dp"
+    android:focusable="true"
+    android:clickable="true"
+/>
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
deleted file mode 100644
index 63a878f..0000000
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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
-  -->
-
-<!-- Layout for media controls on the lockscreen -->
-<com.android.systemui.statusbar.notification.stack.MediaHeaderView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingStart="0dp"
-    android:paddingEnd="0dp"
-    android:focusable="true"
-    android:clickable="true"
-/>
diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml
index 2d082dc..a5fdcd9 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip.xml
@@ -28,8 +28,8 @@
 
     <com.android.internal.widget.CachingIconView
         android:id="@+id/app_icon"
-        android:layout_width="@dimen/media_ttt_icon_size"
-        android:layout_height="@dimen/media_ttt_icon_size"
+        android:layout_width="@dimen/media_ttt_app_icon_size"
+        android:layout_height="@dimen/media_ttt_app_icon_size"
         android:layout_marginEnd="12dp"
         />
 
@@ -41,23 +41,34 @@
         android:textColor="?android:attr/textColorPrimary"
         />
 
+    <!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
+
     <ProgressBar
         android:id="@+id/loading"
         android:indeterminate="true"
-        android:layout_width="@dimen/media_ttt_loading_size"
-        android:layout_height="@dimen/media_ttt_loading_size"
-        android:layout_marginStart="12dp"
+        android:layout_width="@dimen/media_ttt_status_icon_size"
+        android:layout_height="@dimen/media_ttt_status_icon_size"
+        android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
         android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
         style="?android:attr/progressBarStyleSmall"
         />
 
+    <ImageView
+        android:id="@+id/failure_icon"
+        android:layout_width="@dimen/media_ttt_status_icon_size"
+        android:layout_height="@dimen/media_ttt_status_icon_size"
+        android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
+        android:src="@drawable/ic_warning"
+        android:tint="@color/GM2_red_500"
+        />
+
     <TextView
         android:id="@+id/undo"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/media_transfer_undo"
         android:textColor="?androidprv:attr/textColorOnAccent"
-        android:layout_marginStart="12dp"
+        android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
         android:textSize="@dimen/media_ttt_text_size"
         android:paddingStart="@dimen/media_ttt_chip_outer_padding"
         android:paddingEnd="@dimen/media_ttt_chip_outer_padding"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index e70084b..5cd9e94 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -43,7 +43,6 @@
                 android:id="@+id/build"
                 android:layout_width="0dp"
                 android:layout_height="match_parent"
-                android:paddingStart="@dimen/qs_tile_margin_horizontal"
                 android:paddingEnd="4dp"
                 android:layout_weight="1"
                 android:clickable="true"
@@ -61,10 +60,23 @@
                 android:layout_gravity="center_vertical"
                 android:visibility="gone" />
 
-            <View
+            <FrameLayout
                 android:layout_width="0dp"
                 android:layout_height="match_parent"
-                android:layout_weight="1" />
+                android:layout_weight="1">
+                <com.android.systemui.statusbar.AlphaOptimizedImageView
+                    android:id="@android:id/edit"
+                    android:layout_width="@dimen/qs_footer_action_button_size"
+                    android:layout_height="@dimen/qs_footer_action_button_size"
+                    android:layout_gravity="center_vertical|end"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:clickable="true"
+                    android:contentDescription="@string/accessibility_quick_settings_edit"
+                    android:focusable="true"
+                    android:padding="@dimen/qs_footer_icon_padding"
+                    android:src="@*android:drawable/ic_mode_edit"
+                    android:tint="?android:attr/textColorPrimary" />
+            </FrameLayout>
 
         </LinearLayout>
 
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index f083b85..10b8ef4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Sluitskerm."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Werksluitskerm"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Maak toe"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi afgeskakel."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi aangeskakel."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Vliegtuigmodus afgeskakel."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Vliegtuigmodus aangeskakel."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"volkome stilte"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"net wekkers"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Moenie Steur Nie."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Moenie Steur Nie is afgeskakel."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Moenie Steur Nie is aangeskakel."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth aan."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth afgeskakel."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth aangeskakel."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Liggingverslaggewing afgeskakel."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Liggingverslaggewing aangeskakel."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Wekker gestel vir <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Meer tyd."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Minder tyd."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Flitslig afgeskakel."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Flitslig aangeskakel."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Kleuromkering afgeskakel."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Kleuromkering aangeskakel."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele warmkol afgeskakel."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele warmkol aangeskakel."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Uitsaai van skerm gestaak."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databespaarder is afgeskakel."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databespaarder is aangeskakel."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Skermhelderheid"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobiele data is laat wag"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data is onderbreek"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi is nie gekoppel nie"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleuromkering"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurregstelling"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellings"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikerinstellings"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skandeer QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik om \'n QR-kode te skandeer"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-kode"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tik om te skandeer"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Jy sal nie jou volgende wekker <xliff:g id="WHEN">%1$s</xliff:g> hoor nie"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Jy moet Bluetooth aanskakel om jou sleutelbord aan jou tablet te koppel."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Skakel aan"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kragkennisgewingkontroles"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan – gesiggegrond"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Met kragkennisgewingkontroles kan jy \'n belangrikheidvlak van 0 tot 5 vir \'n program se kennisgewings stel. \n\n"<b>"Vlak 5"</b>" \n- Wys aan die bokant van die kennisgewinglys \n- Laat volskermonderbreking toe \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 4"</b>" \n- Verhoed volskermonderbreking \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 3"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n\n"<b>"Vlak 2"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n\n"<b>"Vlak 1"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n- Versteek van sluitskerm en statusbalk \n- Wys aan die onderkant van die kennisgewinglys \n\n"<b>"Vlak 0"</b>" \n- Blokkeer alle kennisgewings van die program af"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Pas toe"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> deur <xliff:g id="ARTIST_NAME">%2$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ontdoen"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Beweeg nader om op <xliff:g id="DEVICENAME">%1$s</xliff:g> te speel"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Speel tans op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Programme wat op die agtergrond werk"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 2059490..08e60c5 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Af"</item>
     <item msgid="2075645297847971154">"Aan"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Onbeskikbaar"</item>
+    <item msgid="1909756493418256167">"Af"</item>
+    <item msgid="4531508423703413340">"Aan"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Onbeskikbaar"</item>
     <item msgid="9103697205127645916">"Af"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 839b8d2..6fe6f53 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ማያ ገጽ ቆልፍ።"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"የስራ ማያ ገጽ ቁልፍ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ዝጋ"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi በርቷል።"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"የአውሮፕላን ሁነታ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"የአውሮፕላን ሁነታ በርቷል።"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ሙሉ ለሙሉ ጸጥታ"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ማንቂያዎች ብቻ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"አትረብሽ።"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"አትረብሽ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"አትረብሽ በርቷል።"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ብሉቱዝ።"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ብሉቱዝ በርቷል።"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ብሉቱዝ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ብሉቱዝ በርቷል።"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"አካባቢን ሪፖርት ማድረግ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"አካባቢን ሪፖርት ማድረግ በርቷል።"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"ማንቂያ ለ<xliff:g id="TIME">%s</xliff:g> ተዋቅሯል።"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ተጨማሪ ጊዜ።"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"ያነሰ ጊዜ።"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"የባትሪ ብርሃን ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"የባትሪ ብርሃን በርቷል።"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"የቀለም ግልበጣ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"የቀለም ግልበጣ በርቷል።"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"የተንቀሳቃሽ ስልክ መገናኛ ነጥብ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"የተንቀሳቃሽ ስልክ መገናኛ ነጥብ በርቷል።"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ማያ ገጽ መውሰድ ቆሟል።"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ውሂብ ቆጣቢ ጠፍቷል።"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ውሂብ ቆጣቢ በርቷል።"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ብሩህነት ያሳዩ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"የተንቀሳቃሽ ስልክ ውሂብ ባለበት ቆሟል"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ውሂብ ላፍታ ቆሟል"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi አልተገናኘም"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ብሩህነት"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ተቃራኒ ቀለም"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"የቀለም ማስተካከያ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"ተጨማሪ ቅንብሮች"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"የተጠቃሚ ቅንብሮች"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ተከናውኗል"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ለማየት ይክፈቱ"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"የእርስዎን ካርዶች ማግኘት ላይ ችግር ነበር፣ እባክዎ ቆይተው እንደገና ይሞክሩ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"የገጽ መቆለፊያ ቅንብሮች"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ቃኝ"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ኮድን ለመቃኘት ጠቅ ያድርጉ"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"ለመቃኘት መታ ያድርጉ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"የእርስዎን ቀጣይ ማንቂያ <xliff:g id="WHEN">%1$s</xliff:g> አይሰሙም"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"የቁልፍ ሰሌዳዎን ከእርስዎ ጡባዊ ጋር ለማገናኘት በመጀመሪያ ብሉቱዝን ማብራት አለብዎት።"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"አብራ"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"የኃይል ማሳወቂያ መቆጣጠሪያዎች"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"በርቷል - መልክ ላይ የተመሠረተ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"በኃይል ማሳወቂያ መቆጣጠሪያዎች አማካኝነት የአንድ መተግበሪያ ማሳወቂያዎች የአስፈላጊነት ደረጃ ከ0 እስከ 5 ድረስ ማዘጋጀት ይችላሉ። \n\n"<b>"ደረጃ 5"</b>" \n- በማሳወቂያ ዝርዝሩ አናት ላይ አሳይ \n- የሙሉ ማያ ገጽ ማቋረጥን ፍቀድ \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 4"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 3"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- በፍጹም አጮልቀው አይምልከቱ \n\n"<b>"ደረጃ 2"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ እና ንዝረትን በፍጹም አይኑር \n\n"<b>"ደረጃ 1"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ ወይም ንዝረትን በፍጹም አያደርጉ \n- ከመቆለፊያ ገጽ እና የሁኔታ አሞሌ ይደብቁ \n- በማሳወቂያ ዝርዝር ግርጌ ላይ አሳይ \n\n"<b>"ደረጃ 0"</b>" \n- ሁሉንም የመተግበሪያው ማሳወቂያዎች ያግዱ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ተከናውኗል"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ተግብር"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> በ<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ከ<xliff:g id="APP_LABEL">%3$s</xliff:g> ያጫውቱ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ከ<xliff:g id="APP_LABEL">%2$s</xliff:g> ያጫውቱ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ቀልብስ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ ለማጫወት ጠጋ ያድርጉ"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ በማጫወት ላይ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"አልተገኘም"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ተጠቃሚን ይምረጡ"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ከበስተጀርባ የሚሠሩ መተግበሪያዎች"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"መቆሚያ"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index 7fdfd11..c464f9a 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"አብራ"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"አይገኝም"</item>
     <item msgid="9103697205127645916">"ጠፍቷል"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 6110793..5a0c7a1 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"شاشة القفل."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"شاشة قفل بيانات العمل"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"إغلاق"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"‏تم إيقاف Wifi."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"‏تم تفعيل Wifi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطيران."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطيران."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"كتم الصوت تمامًا"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"المنبِّهات فقط"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"عدم الإزعاج"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"تم إيقاف \"عدم الإزعاج\"."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"تم تفعيل \"عدم الإزعاج\"."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"البلوتوث."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"تفعيل البلوتوث."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"تم إيقاف البلوتوث."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"تم تفعيل البلوتوث."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"تم إيقاف الإبلاغ عن الموقع."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"تم تفعيل ميزة الإبلاغ عن الموقع الجغرافي."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"تم ضبط المنبّه على <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"وقت أكثر."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"وقت أقل."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"تم إيقاف الفلاش."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"تم تفعيل الفلاش."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"تم إيقاف \"قلب الألوان\"."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"تم تفعيل \"قلب الألوان\"."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"تم إيقاف نقطة اتصال الجوّال."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"تم تفعيل نقطة اتصال الجوّال."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"توقف إرسال الشاشة."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"تم إيقاف توفير البيانات."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"تم تفعيل توفير البيانات."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"سطوع الشاشة"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"تم إيقاف بيانات الجوّال مؤقتًا"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"تم إيقاف البيانات مؤقتًا"</string>
@@ -255,6 +237,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏لم يتم الاتصال بشبكة Wi-Fi."</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"السطوع"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"قلب الألوان"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحيح الألوان"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"المزيد من الإعدادات"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"إعدادات المستخدم"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"تم"</string>
@@ -478,8 +461,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"فتح القفل للاستخدام"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"حدثت مشكلة أثناء الحصول على البطاقات، يُرجى إعادة المحاولة لاحقًا."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"مسح رمز الاستجابة السريعة ضوئيًا"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"انقر لمسح رمز الاستجابة السريعة ضوئيًا."</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -499,6 +484,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"لتوصيل لوحة المفاتيح بالجهاز اللوحي، يلزمك تفعيل بلوتوث أولاً."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"تفعيل"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"عناصر التحكم في إشعارات التشغيل"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"تفعيل - استنادًا للوجه"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك ضبط مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"تمّ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"تطبيق"</string>
@@ -831,7 +817,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> للفنان <xliff:g id="ARTIST_NAME">%2$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"تراجع"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"عليك الاقتراب لتشغيل الموسيقى على <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"جارٍ تشغيل الموسيقى على <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"لم يتم العثور عليه."</string>
@@ -919,4 +906,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"اختيار المستخدم"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"التطبيقات التي يتم تشغيلها في الخلفية"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"إيقاف"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index d7026c7..2bfcf7c 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"مفعّل"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"الميزة غير متاحة"</item>
     <item msgid="9103697205127645916">"الميزة غير مفعّلة"</item>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 3fe47cb..bf35ad2 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"বন্ধ স্ক্ৰীন।"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীন"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ কৰক"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ৱাই-ফাই অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ৱাই-ফাই অন কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"এয়াৰপ্লেইন ম\'ড অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"এয়াৰপ্লেইন ম\'ড অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"সম্পূৰ্ণ নিৰৱতা"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"কেৱল এলাৰ্মবোৰৰ বাবে"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"অসুবিধা নিদিব"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"অসুবিধা নিদিব বন্ধ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"অসুবিধা নিদিব অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ব্লুটুথ।"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ব্লুটুথ অন হৈ আছে।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ব্লুটুথ অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ব্লুটুথ অন কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"অৱস্থান সবিশেষ অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"অৱস্থান সবিশেষ অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g>ৰ বাবে এলাৰ্ম ছেট কৰা হৈছে।"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"অধিক সময়।"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"কম সময়।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ফ্লাশ্বলাইট অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ফ্লাশ্বলাইট অন কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"ৰং বিপৰীতকৰণ অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ৰং বিপৰীতকৰণ অন কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ম’বাইল হটস্পট  অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ম’বাইল হটস্পট  অন কৰা হ’ল।"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"স্ক্ৰীন কাষ্টিং বন্ধ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ডেটা সঞ্চয়কাৰী সুবিধা অফ কৰা হ’ল।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ডেটা সঞ্চয়কাৰী সুবিধা অন কৰা হ’ল।"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ডিছপ্লেৰ উজ্জ্বলতা"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ম’বাইল ডেটা পজ কৰা হৈছে"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ডেটা পজ কৰা হৈছে"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ৱাই-ফাইৰ সৈতে সংযোগ হৈ থকা নাই"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ৰং বিপৰীতকৰণ"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ৰং শুধৰণী"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"অধিক ছেটিং"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যৱহাৰকাৰীৰ ছেটিং"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন কৰা হ’ল"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"আপোনাৰ কাৰ্ড লাভ কৰোঁতে এটা সমস্যা হৈছে, অনুগ্ৰহ কৰি পাছত পুনৰ চেষ্টা কৰক"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্ৰীনৰ ছেটিং"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"কিউআৰ স্কেন কৰক"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"এটা কিউআৰ ক’ড স্কেন কৰিবলৈ ক্লিক কৰক"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লেইন ম\'ড"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"আপোনাৰ টেবলেটত আপোনাৰ কীব\'ৰ্ড সংযোগ কৰিবলৈ আপুনি প্ৰথমে ব্লুটুথ অন কৰিব লাগিব।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"অন কৰক"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"জাননী নিয়ন্ত্ৰণৰ অধিক কৰ্তৃত্ব"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"অন আছে - মুখাৱয়ব ভিত্তিক"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"জাননী নিয়ন্ত্ৰণৰ অধিক কৰ্তৃত্বৰ সৈতে আপুনি এটা এপৰ জাননীৰ গুৰুত্বৰ স্তৰ ০ৰ পৰা ৫লৈ ছেট কৰিব পাৰে।\n\n"<b>"স্তৰ ৫"</b>" \n- জাননী তালিকাৰ একেবাৰে ওপৰত দেখুৱাওক \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ অনুমতি দিয়ক\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৪"</b>" \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৩"</b>" \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n\n"<b>"স্তৰ ২"</b>" \n- সম্পূর্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব \n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n- কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব\n\n"<b>" স্তৰ ১"</b>" \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n-কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব \n- লক স্ক্ৰীন আৰু স্থিতি দণ্ডৰ পৰা লুকুৱাই ৰাখক \n- জাননী তালিকাৰ একেবাৰে তলত দেখুৱাওক\n\n"<b>"স্তৰ ০"</b>" \n- এই এপৰ আটাইবোৰ জাননী অৱৰোধ কৰক"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"কৰা হ’ল"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"প্ৰয়োগ কৰক"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ত <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ৰ <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ কৰক"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ত <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ কৰক"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"আনডু কৰক"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে\' কৰিবলৈ ওপৰলৈ যাওক"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে\' কৰি থকা হৈছে"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্‌টো পৰীক্ষা কৰক"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"নেপথ্যত চলি থকা এপ্"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"বন্ধ কৰক"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index efb2351..ba66f8c 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"অন কৰা আছে"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"উপলব্ধ নহয়"</item>
     <item msgid="9103697205127645916">"অফ আছে"</item>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 255ce58..d491724 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kilid ekranı."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ekran kilidi"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Qapadın"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi deaktivdir."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi aktivdir."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Təyyarə rejimi deaktiv edildi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Təyyarə rejimi aktiv edildi."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"tam sakitlik"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"bildirişlər"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Narahat Etməyin."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"Narahat etməyin\" deaktivdir."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"Narahat etməyin\" aktivdir."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth aktiv."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth deaktivdir."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth aktivdir."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Məkan xəbərdarlığı deaktivdir."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Məkan xəbərdarlığı aktivdir."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm <xliff:g id="TIME">%s</xliff:g> üçün qurulub."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Daha çox vaxt."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Daha az vaxt."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Fənər deaktivdir."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Fənər aktivdir."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Rəng inversiyası deaktivdir."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Rəng inversiyası aktivdir."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil hotspot deaktivdir."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil hotspot aktivdir."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekran yayımı dayandırıldı."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Trafikə qənaət edilmir."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Trafikə qənaət edilir."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Display brightness"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobil dataya fasilə verildi"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Məlumatlara fasilə verildi"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi qoşulu deyil"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaqlıq"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rəng inversiyası"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Rəng korreksiyası"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Digər ayarlar"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"İstifadəçi ayarları"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hazır"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"İstifadə etmək üçün kiliddən çıxarın"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Kartların əldə edilməsində problem oldu, sonra yenidən cəhd edin"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilid ekranı ayarları"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodunu skan edin"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodu skan etmək üçün tıklayın"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Skanlamaq üçün toxunun"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> zaman növbəti xəbərdarlığınızı eşitməyəcəksiniz"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Tabletinizlə klaviaturaya bağlanmaq üçün ilk olaraq Bluetooth\'u aktivləşdirməlisiniz."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivləşdirin"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Enerji bildiriş nəzarəti"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Üz əsaslı"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Enerji bildiriş nəzarəti ilə, tətbiq bildirişləri üçün əhəmiyyət səviyyəsini 0-dan 5-ə kimi ayarlaya bilərsiniz. \n\n"<b>"Səviyyə 5"</b>" \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n- Tam ekran kəsintisinə icazə verin \n- Hər zaman izləyin \n\n"<b>"Səviyyə 4"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Hər zaman izləyin \n\n"<b>"Level 3"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n\n"<b>"Level 2"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n\n"<b>"Səviyyə 1"</b>" \n- Prevent full screen interruption \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n- Ekran kilidi və ya status panelindən gizlədin \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n\n"<b>"Səviyyə 0"</b>" \n- Bütün bildirişləri tətbiqdən blok edin"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hazırdır"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tətbiq edin"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> tərəfindən <xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%3$s</xliff:g> tətbiqindən oxudun"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%2$s</xliff:g> tətbiqindən oxudun"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Geri qaytarın"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxutmaq üçün yaxınlaşın"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxudulur"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Tapılmadı"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"İstifadəçi seçin"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Arxa fonda işləyən tətbiqlər"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Dayandırın"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index 7c47203..3689660 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Deaktiv"</item>
     <item msgid="2075645297847971154">"Aktiv"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Əlçatmazdır"</item>
+    <item msgid="1909756493418256167">"Deaktiv"</item>
+    <item msgid="4531508423703413340">"Aktiv"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Əlçatan deyil"</item>
     <item msgid="9103697205127645916">"Deaktiv"</item>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f29d9de..955096e 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključan ekran."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan ekran za posao"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvori"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi je isključen."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi je uključen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Režim rada u avionu je isključen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Režim rada u avionu je uključen."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"potpuna tišina"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"samo alarmi"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne uznemiravaj."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Podešavanje Ne uznemiravaj je isključeno."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Podešavanje Ne uznemiravaj je uključeno."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth je uključen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth je isključen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth je uključen."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Izveštavanje o lokaciji je isključeno."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Izveštavanje o lokaciji je uključeno."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm je podešen za <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Više vremena."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Manje vremena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Baterijska lampa je isključena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Baterijska lampa je uključena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inverzija boja je isključena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inverzija boja je uključena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilni hotspot je isključen."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilni hotspot je uključen."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prebacivanje ekrana je zaustavljeno."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Ušteda podataka je isključena."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Ušteda podataka je uključena."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Osvetljenost ekrana"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobilni podaci su pauzirani"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Podaci su pauzirani"</string>
@@ -252,6 +234,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi nije povezan"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Još podešavanja"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisnička podešavanja"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -469,8 +452,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključaj radi korišćenja"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema pri preuzimanju kartica. Probajte ponovo kasnije"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Podešavanja zaključanog ekrana"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR kôd"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da biste skenirali QR kôd"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kôd"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Dodirnite da biste skenirali"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -490,6 +473,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Da biste povezali tastaturu sa tabletom, prvo morate da uključite Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Uključi"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Napredne kontrole za obaveštenja"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Pomoću naprednih kontrola za obaveštenja možete da podesite nivo važnosti od 0. do 5. za obaveštenja aplikacije. \n\n"<b>"5. nivo"</b>" \n– Prikazuju se u vrhu liste obaveštenja \n- Dozvoli prekid režima celog ekrana \n– Uvek zaviruj \n\n"<b>"4. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Uvek zaviruj \n\n"<b>"3. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n\n"<b>"2. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n– Nikada ne proizvodi zvuk ili vibraciju \n\n"<b>"1. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n– Nikada ne proizvodi zvuk ili vibraciju \n– Sakrij na zaključanom ekranu i statusnoj traci \n– Prikazuju se u dnu liste obaveštenja \n\n"<b>"0. nivo"</b>" \n– Blokiraj sva obaveštenja iz aplikacije"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Primeni"</string>
@@ -813,7 +797,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Opozovi"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Približite da biste puštali muziku na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Pušta se na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -901,4 +886,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izaberite korisnika"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije pokrenute u pozadini"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index 88caf97..6e2b7d1 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Isključeno"</item>
     <item msgid="2075645297847971154">"Uključeno"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nedostupno"</item>
+    <item msgid="1909756493418256167">"Isključeno"</item>
+    <item msgid="4531508423703413340">"Uključeno"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nedostupno"</item>
     <item msgid="9103697205127645916">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 3b0c1a3..4d11111 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Экран блакіроўкі."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Экран блакіроўкі дзейнасці"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Закрыць"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi выключаны."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi уключаны."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Рэжым палёту выключаецца."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Рэжым палёту ўключаецца."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"поўная цішыня"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"толькі будзільнікі"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Не турбаваць."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Рэжым \"Не турбаваць\" выключаны."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Рэжым \"Не турбаваць\" уключаны."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth уключаны."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth выключаны."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth уключаны."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Адпраўка даных аб месцазнаходжанні выключаецца."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Адпраўка даных аб месцазнаходжанні ўключаецца."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Наладжаны будзiльнiк: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Больш часу."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Менш часу."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Ліхтарык выключаецца."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Ліхтарык уключаецца."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Інверсія колераў адключаецца."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Інверсія колераў уключаецца."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мабільны хот-спот выключаецца."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Хот-спот уключаны."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Трансляцыя экрана спынена."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Эканомія трафіка адключана."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Эканомія трафіка ўключана."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Яркасць дысплэя"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мабільная перадача даных прыпынена"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Перадача даных прыпынена"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Няма падключэння да Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркасць"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія колераў"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Карэкцыя колераў"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Дадатковыя налады"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налады карыстальніка"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Гатова"</string>
@@ -370,7 +353,7 @@
     <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="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Гэта прылада знаходзіцца пад кантролем вашых бацькоў"</string>
+    <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Гэта прылада знаходзіцца пад кантролем бацькоў"</string>
     <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ваша арганізацыя валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
     <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
     <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
@@ -409,7 +392,7 @@
     <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Ваш асабісты профіль падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" ,"</string>
     <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"Адкрыйце налады VPN"</string>
-    <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Гэта прылада знаходзіцца пад кантролем вашых бацькоў. Бацькі могуць праглядаць і кантраляваць вашу інфармацыю, напрыклад пра праграмы, якія вы выкарыстоўваеце, даныя пра ваша месцазнаходжанне і час карыстання прыладай."</string>
+    <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Гэта прылада знаходзіцца пад кантролем бацькоў. Бацькі могуць праглядаць і кантраляваць тваю інфармацыю, напрыклад пра праграмы, якія ты выкарыстоўваеш, даныя пра тваё месцазнаходжанне і час карыстання прыладай."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблакіравана з дапамогай TrustAgent"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -472,8 +455,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблакіраваць для выкарыстання"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Узнікла праблема з загрузкай вашых карт. Паўтарыце спробу пазней"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Налады экрана блакіроўкі"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Адсканіраваць QR-код"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Націсніце, каб адсканіраваць QR-код"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Вы не пачуеце наступны будзільнік <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -493,6 +478,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Для падлучэння клавіятуры да планшэта трэба спачатку ўключыць Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Уключыць"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Пашыранае кіраванне апавяшчэннямі"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Уключана – З улікам паставы галавы"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"З дапамогай пашыранага кіравання апавяшчэннямі вы можаце задаваць узровень важнасці апавяшчэнняў праграмы ад 0 да 5. \n\n"<b>"Узровень 5"</b>" \n- Паказваць уверсе спіса апавяшчэнняў \n- Дазваляць перапыняць рэжым поўнага экрана \n- Заўсёды дазваляць кароткі паказ \n\n"<b>"Узровень 4"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Заўсёды дазваляць кароткі паказ \n\n"<b>"Узровень 3"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n\n"<b>"Узровень 2"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n- Ніколі не прайграваць гук і не вібрыраваць \n\n"<b>"Узровень 1"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n- Ніколі не прайграваць гук і не вібрыраваць \n- Хаваць з экрана блакіроўкі і панэлі стану \n- Паказваць унізе спіса апавяшчэнняў \n\n"<b>"Узровень 0"</b>" \n- Блакіраваць усе апавяшчэнні ад праграмы"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Гатова"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Прымяніць"</string>
@@ -819,7 +805,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Прайграйце кампазіцыю \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (выканаўца – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) з дапамогай праграмы \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\""</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Прайграйце кампазіцыю \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" з дапамогай праграмы \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Адрабіць"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Наблізьцеся, каб прайграць музыку на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Прайграецца на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Не знойдзена"</string>
@@ -907,4 +894,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выбар карыстальніка"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Праграмы працуюць у фонавым рэжыме"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Спыніць"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index ecfde0b..aef519f 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Уключана"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Недаступна"</item>
     <item msgid="9103697205127645916">"Выключана"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6881fcf..aaa1f3a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Заключване на екрана."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Заключен екран на служебния профил"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Затваряне"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Функцията за Wi-Fi се изключи."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Функцията за Wi-Fi се включи."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Самолетният режим се изключи."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Самолетният режим се включи."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"пълна тишина"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"само будилници"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Не безпокойте."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Режимът „Не безпокойте“ е изключен."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Режимът „Не безпокойте“ е включен."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Функцията за Bluetooth е включена."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Функцията за Bluetooth се изключи."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Функцията за Bluetooth се включи."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Функцията „Отчитане на местоположението“ се изключи."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Функцията „Отчитане на местоположението“ се включи."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Будилникът е навит за <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Повече време."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"По-малко време."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Фенерчето е изключено."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Фенерчето е включено."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Функцията за инвертиране на цветовете се изключи."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Функцията за инвертиране на цветовете се включи."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобилната точка за достъп се изключи."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобилната точка за достъп се включи."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Предаването на съдържанието от екрана спря."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Функцията „Икономия на данни“ е изключена."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Функцията „Икономия на данни“ е включена."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Яркост на екрана"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобилните данни са поставени на пауза"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Данните са поставени на пауза"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"не е установена връзка с Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркост"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инвертиране на цветовете"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекция на цветове"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Още настройки"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Потребителски настройки"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отключване с цел използване"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"При извличането на картите ви възникна проблем. Моля, опитайте отново по-късно"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки за заключения екран"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканиране на QR код"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликнете, за да сканирате QR код"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Няма да чуете следващия си будилник в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"За да свържете клавиатурата с таблета си, първо трябва да включите Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Включване"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Контроли за известията"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вкл. – въз основа на лицето"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"С помощта на контролите за известията можете да зададете ниво на важност от 0 до 5 за известията от дадено приложение. \n\n"<b>"Ниво 5"</b>" \n– Показване най-горе в списъка с известия. \n– Разрешаване на прекъсването на цял екран. \n– Известията винаги се показват мимолетно. \n\n"<b>"Ниво 4"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията винаги се показват мимолетно. \n\n"<b>"Ниво 3"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n\n"<b>"Ниво 2"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n– Без издаване на звуков сигнал и вибриране. \n\n"<b>"Ниво 1"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n– Без издаване на звуков сигнал и вибриране. \n– Скриване от заключения екран и лентата на състоянието. \n– Показване най-долу в списъка с известия. \n\n"<b>"Ниво 0"</b>" \n– Блокиране на всички известия от приложението."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Прилагане"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Пускане на <xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="ARTIST_NAME">%2$s</xliff:g> от <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пускане на <xliff:g id="SONG_NAME">%1$s</xliff:g> от <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Отмяна"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Преместете се по-близо, за да се възпроизведе на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Възпроизвежда се на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Не е намерено"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Избор на потребител"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Приложения, които се изпълняват на заден план"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Спиране"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index 7424f81..0900c52 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Вкл."</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Не е налице"</item>
     <item msgid="9103697205127645916">"Изкл."</item>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 571118a..be290af 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"লক স্ক্রিন।"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কর্মস্থলের স্ক্রিন লক"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ করুন"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ওয়াই ফাই বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ওয়াই ফাই চালু হয়েছে।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"বিমান মোড বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"বিমান মোড চালু হয়েছে।"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"সম্পূর্ণ নীরব"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"শুধুমাত্র অ্যালার্ম"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"বিরক্ত করবে না।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'বিরক্ত করবে না\' বন্ধ আছে।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\'বিরক্ত করবে না\' চালু করা হয়েছে।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ব্লুটুথ"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ব্লুটুথ চালু আছে।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ব্লুটুথ বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ব্লুটুথ চালু হয়েছে।"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"লোকেশন জানানো বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"লোকেশন জানানো চালু হয়েছে।"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> এ অ্যালার্ম সেট করা হয়েছে৷"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"বেশি সময়।"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"কম সময়।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ফ্ল্যাশলাইট বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ফ্ল্যাশলাইট চালু হয়েছে।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"রঙ বিলোমক্রিয়া বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"রঙ বিলোমক্রিয়া চালু হয়েছে।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"মোবাইল হটস্পট বন্ধ হয়েছে।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"মোবাইল হটস্পট চালু হয়েছে।"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"স্ক্রিন কাস্ট করা থেমেছে।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ডেটা সেভার বন্ধ আছে।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ডেটা সেভার চালু আছে।"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"প্রদর্শনের উজ্জ্বলতা"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"মোবাইল ডেটা সাময়িক ভাবে বন্ধ করা হয়েছে"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ডেট বিরতি দেওয়া হয়েছে"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ওয়াই-ফাই কানেক্ট করা নেই"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"কালার ইনভার্সন"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"রঙ সংশোধন"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"আরও সেটিংস"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যবহারকারী সেটিংস"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন হয়েছে"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যবহার করতে আনলক করুন"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"আপনার কার্ড সংক্রান্ত তথ্য পেতে সমস্যা হয়েছে, পরে আবার চেষ্টা করুন"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্রিন সেটিংস"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR কোড স্ক্যান করুন"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR কোড স্ক্যান করতে ক্লিক করুন"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"স্ক্যান করতে ট্যাপ করুন"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"আপনার ট্যাবলেটের সাথে আপনার কীবোর্ড সংযুক্ত করতে, আপনাকে প্রথমে ব্লুটুথ চালু করতে হবে।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"চালু করুন"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"পাওয়ার বিজ্ঞপ্তির নিয়ন্ত্রণগুলি"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"চালু আছে - মুখের হিসেবে"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"পাওয়ার বিজ্ঞপ্তির নিয়ন্ত্রণগুলি ব্যহবার করে, আপনি কোনও অ্যাপ্লিকেশনের বিজ্ঞপ্তির জন্য ০ থেকে ৫ পর্যন্ত একটি গুরুত্বের লেভেলকে সেট করতে পারবেন৷ \n\n"<b>"লেভেল ৫"</b>" \n- বিজ্ঞপ্তি তালিকার শীর্ষে দেখায় \n- পূর্ণ স্ক্রিনের বাধাকে অনুমতি দেয় \n- সর্বদা স্ক্রিনে উপস্থিত হয় \n\n"<b>"লেভেল ৪"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- সর্বদা স্ক্রিনে উপস্থিত হয় \n\n"<b>"লেভেল ৩"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n\n"<b>"লেভেল ২"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n- কখনওই শব্দ এবং কম্পন করে না \n\n"<b>"লেভেল ১"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n- কখনওই শব্দ এবং কম্পন করে না \n- লক স্ক্রিন এবং স্ট্যাটাস বার থেকে লুকায় \n- বিজ্ঞপ্তি তালিকার নীচের দিকে দেখায় \n\n"<b>"লেভেল ০"</b>" \n- অ্যাপ্লিকেশন থেকে সমস্ত বিজ্ঞপ্তিকে অবরূদ্ধ করে"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"হয়ে গেছে"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"প্রয়োগ করুন"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-এর <xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%3$s</xliff:g> অ্যাপে চালান"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%2$s</xliff:g> অ্যাপে চালান"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"আগের অবস্থায় ফিরুন"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ চালাতে আরও কাছে আনুন"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ ভিডিও চালানো হচ্ছে"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"বন্ধ আছে, অ্যাপ চেক করুন"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"খুঁজে পাওয়া যায়নি"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যবহারকারী বেছে নিন"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ব্যাকগ্রাউন্ডে অ্যাপ চলছে"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"বন্ধ করুন"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index eddf851..5358e5d 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"চালু আছে"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"উপলভ্য নেই"</item>
     <item msgid="9103697205127645916">"বন্ধ আছে"</item>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6d4326b..eb45708 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključan ekran."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan ekran radnog profila"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvori"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi je isključen."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi je uključen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Način rada u avionu je isključen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Način rada u avionu je uključen."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"potpuna tišina"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"samo alarmi"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne ometaj."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Način rada Ne ometaj je isključen."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Način rada Ne ometaj je uključen."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth uključen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth je isključen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth je uključen."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Javljanje lokacije je isključeno."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Javljanje lokacije je uključeno."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm je podešen na <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Više vremena."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Manje vremena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Svjetiljka je isključena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Svjetiljka je uključena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inverzija boja je isključena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inverzija boja je uključena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilna pristupna tačka je isključena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilna pristupna tačka je uključena."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prebacivanje ekrana je zaustavljeno."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Ušteda podataka je isključena."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Ušteda podataka je uključena."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Osvjetljenje ekrana"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Prijenos podataka je pauziran"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Prijenos podataka je pauziran"</string>
@@ -252,6 +234,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi mreža nije povezana"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvjetljenje"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ispravka boje"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Više postavki"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -469,8 +452,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da koristite"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema prilikom preuzimanja vaših kartica. Pokušajte ponovo kasnije"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključavanja ekrana"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da skenirate QR kôd"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kôd"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Dodirnite da skenirate"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil za posao"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -490,6 +473,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Da povežete tastaturu sa tabletom, prvo morate uključiti Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Uključi"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kontrole obavještenja o napajanju"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Uz kontrolu obavještenja o napajanju, možete postaviti nivo značaja obavještenja iz aplikacije, i to od nivoa 0 do 5. \n\n"<b>"Nivo 5"</b>" \n- Prikaži na vrhu liste obavještenja \n- Dopusti prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nvio 4"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nivo 3"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n\n"<b>"Nivo 2"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n\n"<b>"Nivo 1"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikada ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n- Sakrij sa ekrana za zaključavanje i statusne trake \n- Prikaži na dnu liste obavještenja \n\n"<b>"Nivo 0"</b>" \n- Blokiraj sva obavještenja iz aplikacije"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
@@ -813,7 +797,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Približite se da reproducirate na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -901,4 +886,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odaberite korisnika"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije su aktivne u pozadini"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index 88caf97..6e2b7d1 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Isključeno"</item>
     <item msgid="2075645297847971154">"Uključeno"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nedostupno"</item>
+    <item msgid="1909756493418256167">"Isključeno"</item>
+    <item msgid="4531508423703413340">"Uključeno"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nedostupno"</item>
     <item msgid="9103697205127645916">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e6c7cfb..06799a2 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueig"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla de bloqueig per a la feina"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Tanca"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"La xarxa Wi-Fi està desactivada."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"La xarxa Wi-Fi està activada."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"S\'ha desactivat el Mode d\'avió."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"S\'ha activat el Mode d\'avió."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silenci total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"només alarmes"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"No molestis."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"S\'ha desactivat el mode No molestis."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"S\'ha activat el mode No molestis."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth activat."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth desactivat."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth activat."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Els informes d\'ubicació estan desactivats."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Els informes d\'ubicació estan activats."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"S\'ha configurat l\'alarma (<xliff:g id="TIME">%s</xliff:g>)."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Més temps"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menys temps"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Llanterna apagada."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Llanterna encesa."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"La inversió de colors està desactivada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"La inversió de colors està activada."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"El punt d\'accés mòbil està desactivat."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"El punt d\'accés mòbil està activat."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"S\'ha aturat l\'emissió de la pantalla."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"S\'ha desactivat l\'Economitzador de dades."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"S\'ha activat l\'Economitzador de dades."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brillantor de la pantalla"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"S\'han posat en pausa les dades mòbils"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Les dades estan aturades"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversió de colors"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correcció de color"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuració d\'usuari"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloqueja per utilitzar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Hi ha hagut un problema en obtenir les teves targetes; torna-ho a provar més tard"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuració de la pantalla de bloqueig"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escaneja un codi QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fes clic per escanejar un codi QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> no sentiràs la pròxima alarma"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activa"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controls millorats per a notificacions"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activat: basat en cares"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Amb els controls de notificació millorats, pots establir un nivell d\'importància d\'entre 0 i 5 per a les notificacions d\'una aplicació. \n\n"<b>"Nivell 5"</b>" \n- Mostra les notificacions a la part superior de la llista \n- Permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 4"</b>" \n- No permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 3"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n\n"<b>"Nivell 2"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- Les notificacions no poden emetre sons ni vibracions \n\n"<b>"Nivell 1"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- No activa mai el so ni la vibració \n- Amaga les notificacions de la pantalla de bloqueig i de la barra d\'estat \n- Mostra les notificacions a la part inferior de la llista \n\n"<b>"Nivell 0"</b>" \n- Bloqueja totes les notificacions de l\'aplicació"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Fet"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplica"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) des de l\'aplicació <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> des de l\'aplicació <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfés"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Mou més a prop per reproduir a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"S\'està reproduint a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"No s\'ha trobat"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicacions que s\'executen en segon pla"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Atura"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index e3538fa..2738ecf 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desactivat"</item>
     <item msgid="2075645297847971154">"Activat"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"No disponible"</item>
+    <item msgid="1909756493418256167">"Desactivada"</item>
+    <item msgid="4531508423703413340">"Activada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"No disponible"</item>
     <item msgid="9103697205127645916">"Desactivat"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index b2becd8..4b63cd8 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Obrazovka uzamčení"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Obrazovka uzamčení pracovního profilu"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zavřít"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Připojení Wi-Fi je vypnuto."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Připojení Wi-Fi je zapnuto."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Režim Letadlo je vypnutý."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Režim Letadlo je zapnutý."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"úplné ticho"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"pouze budíky"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Nerušit."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Režim Nerušit je vypnutý."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Režim Nerušit je zapnutý."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Rozhraní Bluetooth je zapnuto."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Rozhraní Bluetooth je vypnuto."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Rozhraní Bluetooth je zapnuto."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Hlášení polohy je vypnuto."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Hlášení polohy je zapnuto."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Budík je nastaven na <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Delší doba"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Kratší doba"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Svítilna je vypnutá."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Svítilna je zapnutá."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Převrácení barev je vypnuto."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Převrácení barev je zapnuto."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobile hotspot je vypnutý."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobile hotspot je zapnutý."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Odesílání obrazovky zastaveno."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Spořič dat byl vypnut."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Spořič dat byl zapnut."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Jas displeje"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobilní data byla pozastavena"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data jsou pozastavena"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Není připojena Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Převrácení barev"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekce barev"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Další nastavení"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uživatelské nastavení"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
@@ -472,8 +455,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odemknout a použít"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Při načítání karet došlo k problému, zkuste to později"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavení obrazovky uzamčení"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Naskenovat QR kód"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknutím naskenujete QR kód"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kód"</string>
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Svůj další budík <xliff:g id="WHEN">%1$s</xliff:g> neuslyšíte"</string>
@@ -493,6 +477,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Chcete-li klávesnici připojit k tabletu, nejdříve musíte zapnout Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Zapnout"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Rozšířené ovládací prvky oznámení"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuto – podle obličeje"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Rozšířené ovládací prvky oznámení umožňují nastavit úroveň důležitosti oznámení aplikace od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazit na začátku seznamu oznámení \n– Povolit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 4"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 3"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n\n"<b>"Úroveň 2"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat žádný zvukový signál ani nevibrovat \n\n"<b>"Úroveň 1"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat zvukový signál ani nevibrovat \n– Skrýt na obrazovce uzamčení a stavového řádku \n– Zobrazovat na konci seznamu oznámení \n\n"<b>";Úroveň 0"</b>" \n– Blokovat všechna oznámení z aplikace"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Použít"</string>
@@ -819,7 +804,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Přehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Přehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Vrátit zpět"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Pokud chcete přehrávat na zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>, přibližte se k němu"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Přehrává se na zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nenalezeno"</string>
@@ -907,4 +893,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zvolte uživatele"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikace běžící na pozadí"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 3f40ab6..cd667cb 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Vyp"</item>
     <item msgid="2075645297847971154">"Zap"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nedostupné"</item>
+    <item msgid="1909756493418256167">"Vyp"</item>
+    <item msgid="4531508423703413340">"Zap"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nedostupné"</item>
     <item msgid="9103697205127645916">"Vyp"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index d559c150..ad92cad 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Låseskærm."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Låseskærm til arbejde"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Luk"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi er slået fra."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi er slået til."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Flytilstand er slået fra."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Flytilstand er slået til."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"total stilhed"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"kun alarmer"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Forstyr ikke."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Forstyr ikke er slået fra."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Forstyr ikke er slået til."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth er slået til."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth er slået fra."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth er slået til."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Lokationsrapportering er slået fra."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Lokationsrapportering er slået til."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarmen er indstillet til <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mere tid."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mindre tid."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lommelygten er slukket."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lommelygten er tændt."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Ombytning af farver er slået fra."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Ombytning af farver er slået til."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilhotspot er slået fra."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilhotspot er slået til."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casting af din skærm er stoppet."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datasparefunktionen er slået fra."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datasparefunktionen er aktiveret."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Skærmens lysstyrke"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobildata er sat på pause"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data er sat på pause"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Manglende Wi-Fi-forbindelse"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ombytning af farver"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farvekorrigering"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Flere indstillinger"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brugerindstillinger"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR-kode"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik for at scanne en QR-kode"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Slå til"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kontrolelementer til notifikation om strøm"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Til – ansigtsbaseret"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Med kontrolelementer til notifikationer om strøm kan du konfigurere et vigtighedsniveau fra 0 til 5 for en apps notifikationer. \n\n"<b>"Niveau 5"</b>\n"- Vis øverst på listen over notifikationer \n- Tillad afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 4"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 3"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n\n"<b>"Niveau 2"</b>\n"- Ingen afbrydelse af fuld skærm \n Se aldrig smugkig \n- Ingen lyd og vibration \n\n"<b>"Niveau 1"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n- Ingen lyd eller vibration \n- Skjul fra låseskærm og statusbjælke \n- Vis nederst på listen over notifikationer \n\n"<b>"Niveau 0"</b>\n"- Bloker alle notifikationer fra appen."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Udfør"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Anvend"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> af <xliff:g id="ARTIST_NAME">%2$s</xliff:g> via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Fortryd"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Flyt enheden tættere på for at afspille på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Afspilles på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ikke fundet"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vælg bruger"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps, der kører i baggrunden"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index b479a44..5ec01fe 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Fra"</item>
     <item msgid="2075645297847971154">"Til"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Ikke tilgængelig"</item>
+    <item msgid="1909756493418256167">"Fra"</item>
+    <item msgid="4531508423703413340">"Til"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Ikke tilgængelig"</item>
     <item msgid="9103697205127645916">"Fra"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 801d9e0..8b51482 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Sperrbildschirm"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Sperrbildschirm für Arbeitsprofil"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Schließen"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WLAN ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WLAN ist aktiviert."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Der Flugmodus ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Der Flugmodus ist aktiviert."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"lautlos"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"nur Weckrufe"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Bitte nicht stören."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"„Bitte nicht stören“ deaktiviert."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"„Bitte nicht stören“ aktiviert"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth aktiviert"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth ist aktiviert."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Der Standortbericht ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Der Standortbericht ist aktiviert."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Wecker gestellt für <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mehr Zeit"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Weniger Zeit"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Die Taschenlampe ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Die Taschenlampe ist aktiviert."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Die Farbumkehr ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Die Farbumkehr ist aktiviert."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Der mobile Hotspot ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Der mobile Hotspot ist aktiviert."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Die Bildschirmübertragung wurde angehalten."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Der Datensparmodus ist deaktiviert."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Der Datensparmodus ist aktiviert."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Helligkeit des Displays"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobile Datennutzung pausiert"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Daten pausiert"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WLAN nicht verbunden"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helligkeit"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farbumkehr"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farbkorrektur"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Weitere Einstellungen"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Nutzereinstellungen"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Fertig"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Zum Verwenden entsperren"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Beim Abrufen deiner Karten ist ein Fehler aufgetreten – bitte versuch es später noch einmal"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Einstellungen für den Sperrbildschirm"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-Code scannen"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klicken, um einen QR-Code zu scannen"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Zum Scannen tippen"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Lautloser Weckruf <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Zum Verbinden von Tastatur und Tablet muss Bluetooth aktiviert sein."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivieren"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Erweiterte Benachrichtigungseinstellungen"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"An – gesichtsbasiert"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Mit den erweiterten Benachrichtigungseinstellungen kannst du für App-Benachrichtigungen eine Wichtigkeitsstufe von 0 bis 5 festlegen. \n\n"<b>"Stufe 5"</b>" \n- Auf der Benachrichtigungsleiste ganz oben anzeigen \n- Vollbildunterbrechung zulassen \n- Immer kurz einblenden \n\n"<b>"Stufe 4"</b>" \n- Keine Vollbildunterbrechung \n- Immer kurz einblenden \n\n"<b>"Stufe 3"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n\n"<b>"Stufe 2"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n\n"<b>"Stufe 1"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n- Auf Sperrbildschirm und Statusleiste verbergen \n- Auf der Benachrichtigungsleiste ganz unten anzeigen \n\n"<b>"Stufe 0"</b>" \n- Alle Benachrichtigungen der App sperren"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Fertig"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Anwenden"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergeben"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> über <xliff:g id="APP_LABEL">%2$s</xliff:g> wiedergeben"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Rückgängig machen"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Gehe für die Wiedergabe näher an <xliff:g id="DEVICENAME">%1$s</xliff:g> heran"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Wird auf <xliff:g id="DEVICENAME">%1$s</xliff:g> abgespielt"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nicht gefunden"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Nutzer auswählen"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps, die im Hintergrund ausgeführt werden"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Beenden"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 81f030c..7247645 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Aus"</item>
     <item msgid="2075645297847971154">"An"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nicht verfügbar"</item>
+    <item msgid="1909756493418256167">"Aus"</item>
+    <item msgid="4531508423703413340">"An"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nicht verfügbar"</item>
     <item msgid="9103697205127645916">"Aus"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index bc7d3b0..9e61f1a 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Οθόνη κλειδώματος"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Οθόνη κλειδωμένης εργασίας"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Κλείσιμο"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Το Wi-fi απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Το  Wi-fi ενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Η λειτουργία πτήσης απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Ενεργή λειτουργία πτήσης."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"πλήρης σίγαση"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"μόνο ξυπνητήρια"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Μην ενοχλείτε."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Η λειτουργία Μην ενοχλείτε απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Η λειτουργία Μην ενοχλείτε ενεργοποιήθηκε."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Ενεργό Bluetooth."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Το Bluetooth απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Το Bluetooth ενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Η Αναφορά τοποθεσίας απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Η Αναφορά τοποθεσίας ενεργοποιήθηκε."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Το ξυπνητήρι έχει οριστεί στις <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Περισσότερη ώρα."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Λιγότερη ώρα."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Ο φακός απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Ο φακός ενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Η αντιστροφή χρωμάτων απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Η αντιστροφή χρωμάτων ενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Το σημείο πρόσβασης κινητής συσκευής απενεργοποιήθηκε."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Το σημείο πρόσβασης κινητής συσκευής ενεργοποιήθηκε."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Η μετάδοση της οθόνης διακόπηκε."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Η Εξοικονόμηση δεδομένων είναι ανενεργή."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Η Εξοικονόμηση δεδομένων είναι ενεργή."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Φωτεινότητα οθόνης"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Τα δεδομένα κινητής τηλεφωνίας τέθηκαν σε παύση"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Τα δεδομένα τέθηκαν σε παύση"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Το Wi-Fi δεν είναι συνδεδεμένο"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Φωτεινότητα"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Αντιστροφή χρωμάτων"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Διόρθωση χρωμάτων"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Περισσότερες ρυθμίσεις"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ρυθμίσεις χρήστη"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Τέλος"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ξεκλείδωμα για χρήση"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Παρουσιάστηκε πρόβλημα με τη λήψη των καρτών σας. Δοκιμάστε ξανά αργότερα"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ρυθμίσεις κλειδώματος οθόνης"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Σάρωση κωδικού QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Κάντε κλικ για σάρωση κωδικού QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Πατήστε για σάρωση"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Δεν θα ακούσετε το επόμενο ξυπνητήρι σας <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Για να συνδέσετε το πληκτρολόγιο με το tablet σας, θα πρέπει πρώτα να ενεργοποιήσετε το Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ενεργοποίηση"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Στοιχεία ελέγχου ειδοποίησης ισχύος"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ενεργό - Βάσει προσώπου"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Με τα στοιχεία ελέγχου ειδοποίησης ισχύος, μπορείτε να ορίσετε ένα επίπεδο βαρύτητας από 0 έως 5 για τις ειδοποιήσεις μιας εφαρμογής. \n\n"<b>"Επίπεδο 5"</b>" \n- Εμφάνιση στην κορυφή της λίστας ειδοποιήσεων \n- Να επιτρέπεται η διακοπή πλήρους οθόνης \n- Να γίνεται πάντα σύντομη προβολή \n\n"<b>"Επίπεδο 4"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να γίνεται πάντα σύντομη προβολή \n\n"<b>"Επίπεδο 3"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n\n"<b>"Επίπεδο 2"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n- Να μην χρησιμοποιείται ποτέ ήχος και δόνηση \n\n"<b>"Επίπεδο 1"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n- Να μην χρησιμοποιείται ποτέ ήχος και δόνηση \n- Απόκρυψη από την οθόνη κλειδώματος και τη γραμμή κατάστασης \n- Εμφάνιση στο κάτω μέρος της λίστας ειδοποιήσεων \n\n"<b>"Επίπεδο 0"</b>" \n- Αποκλεισμός όλων των ειδοποιήσεων από την εφαρμογή"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> από <xliff:g id="ARTIST_NAME">%2$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Αναίρεση"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Πλησιάστε για αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογής"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Δεν βρέθηκε."</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Επιλογή χρήστη"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Οι εφαρμογές βρίσκονται σε εξέλιξη στο παρασκήνιο"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Διακοπή"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 422f2aa..4dca192 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Ενεργή"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Μη διαθέσιμο"</item>
     <item msgid="9103697205127645916">"Ανενεργό"</item>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 3427442..79c9ca2 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lock screen."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Close"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi turned off."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi turned on."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Aeroplane mode turned off."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Aeroplane mode turned on."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"total silence"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarms only"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Do Not Disturb."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Do Not Disturb turned off."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Do Not Disturb turned on."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth on."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth turned off."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth turned on."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Location reporting turned off."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Location reporting turned on."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"More time."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Less time."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Torch turned off."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Torch turned on."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Colour inversion turned off."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Colour inversion turned on."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobile hotspot turned off."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobile hotspot turned on."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Display brightness"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data is paused"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Power notification controls"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 9b00c8b..13f236b 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Off"</item>
     <item msgid="2075645297847971154">"On"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Unavailable"</item>
+    <item msgid="1909756493418256167">"Off"</item>
+    <item msgid="4531508423703413340">"On"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Unavailable"</item>
     <item msgid="9103697205127645916">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index b6d1b1d..605811d 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lock screen."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Close"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi turned off."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi turned on."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Airplane mode off."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Airplane mode turned on."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"total silence"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarms only"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Do Not Disturb."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Do Not Disturb turned off."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Do Not Disturb turned on."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth on."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth turned off."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth turned on."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Location reporting turned off."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Location reporting turned on."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"More time."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Less time."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Flashlight turned off."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Flashlight turned on."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Colour inversion turned off."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Colour inversion turned on."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobile hotspot turned off."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobile hotspot turned on."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Display brightness"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data is paused"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Power notification controls"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 9b00c8b..13f236b 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Off"</item>
     <item msgid="2075645297847971154">"On"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Unavailable"</item>
+    <item msgid="1909756493418256167">"Off"</item>
+    <item msgid="4531508423703413340">"On"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Unavailable"</item>
     <item msgid="9103697205127645916">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 3427442..79c9ca2 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lock screen."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Close"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi turned off."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi turned on."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Aeroplane mode turned off."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Aeroplane mode turned on."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"total silence"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarms only"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Do Not Disturb."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Do Not Disturb turned off."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Do Not Disturb turned on."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth on."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth turned off."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth turned on."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Location reporting turned off."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Location reporting turned on."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"More time."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Less time."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Torch turned off."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Torch turned on."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Colour inversion turned off."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Colour inversion turned on."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobile hotspot turned off."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobile hotspot turned on."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Display brightness"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data is paused"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Power notification controls"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 9b00c8b..13f236b 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Off"</item>
     <item msgid="2075645297847971154">"On"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Unavailable"</item>
+    <item msgid="1909756493418256167">"Off"</item>
+    <item msgid="4531508423703413340">"On"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Unavailable"</item>
     <item msgid="9103697205127645916">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 3427442..79c9ca2 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lock screen."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Close"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi turned off."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi turned on."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Aeroplane mode turned off."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Aeroplane mode turned on."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"total silence"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarms only"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Do Not Disturb."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Do Not Disturb turned off."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Do Not Disturb turned on."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth on."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth turned off."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth turned on."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Location reporting turned off."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Location reporting turned on."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"More time."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Less time."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Torch turned off."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Torch turned on."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Colour inversion turned off."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Colour inversion turned on."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobile hotspot turned off."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobile hotspot turned on."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Display brightness"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data is paused"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Turn on"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Power notification controls"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 9b00c8b..13f236b 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Off"</item>
     <item msgid="2075645297847971154">"On"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Unavailable"</item>
+    <item msgid="1909756493418256167">"Off"</item>
+    <item msgid="4531508423703413340">"On"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Unavailable"</item>
     <item msgid="9103697205127645916">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index a4e9ab4..3895548 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎Lock screen.‎‏‎‎‏‎"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎Work lock screen‎‏‎‎‏‎"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎Close‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‎Wifi turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎Wifi turned on.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎Airplane mode turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‎Airplane mode turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎total silence‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎alarms only‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‎Do Not Disturb.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎Do Not Disturb turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎Do Not Disturb turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎Bluetooth.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‎Bluetooth on.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎Bluetooth turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎Bluetooth turned on.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎Location reporting turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎Location reporting turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‎Alarm set for ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎More time.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‎‎Less time.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎Flashlight turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎Flashlight turned on.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎Color inversion turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‎Color inversion turned on.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎Mobile hotspot turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎Mobile hotspot turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎Screen casting stopped.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎Data Saver turned off.‎‏‎‎‏‎"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎Data Saver turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎Display brightness‎‏‎‎‏‎"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎Mobile data is paused‎‏‎‎‏‎"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎Data is paused‎‏‎‎‏‎"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎Wi‑Fi not connected‎‏‎‎‏‎"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎Brightness‎‏‎‎‏‎"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎Color inversion‎‏‎‎‏‎"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎Color correction‎‏‎‎‏‎"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎More settings‎‏‎‎‏‎"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎User settings‎‏‎‎‏‎"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎Done‎‏‎‎‏‎"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎Unlock to use‎‏‎‎‏‎"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎There was a problem getting your cards, please try again later‎‏‎‎‏‎"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎Lock screen settings‎‏‎‎‏‎"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎Scan QR‎‏‎‎‏‎"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎Click to scan a QR code‎‏‎‎‏‎"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎QR code‎‏‎‎‏‎"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎Tap to scan‎‏‎‎‏‎"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎Work profile‎‏‎‎‏‎"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎Airplane mode‎‏‎‎‏‎"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎You won\'t hear your next alarm ‎‏‎‎‏‏‎<xliff:g id="WHEN">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎To connect your keyboard with your tablet, you first have to turn on Bluetooth.‎‏‎‎‏‎"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‏‎‎‎Turn on‎‏‎‎‏‎"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎Power notification controls‎‏‎‎‏‎"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎On - Face-based‎‏‎‎‏‎"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Level 5‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Show at the top of the notification list ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Allow full screen interruption ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Always peek ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Level 4‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Prevent full screen interruption ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Always peek ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Level 3‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Prevent full screen interruption ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Never peek ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Level 2‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Prevent full screen interruption ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Never peek ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Never make sound and vibration ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Level 1‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Prevent full screen interruption ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Never peek ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Never make sound or vibrate ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Hide from lock screen and status bar ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Show at the bottom of the notification list ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Level 0‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎- Block all notifications from the app‎‏‎‎‏‎"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎Done‎‏‎‎‏‎"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎Apply‎‏‎‎‏‎"</string>
@@ -807,7 +791,7 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ by ‎‏‎‎‏‏‎<xliff:g id="ARTIST_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎Undo‎‏‎‎‏‎"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎Move closer to play on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎Move closer to play on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="media_transfer_playing" msgid="3760048096352107789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎Playing on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎Not found‎‏‎‎‏‎"</string>
@@ -895,4 +879,6 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎Select user‎‏‎‎‏‎"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‎‏‎Apps running in the background‎‏‎‎‏‎"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎Stop‎‏‎‎‏‎"</string>
+    <string name="clipboard_edit_text_copy" msgid="770856373439969178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎Copy‎‏‎‎‏‎"</string>
+    <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‎Copied‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
index 0012b57..e6baa31 100644
--- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎Off‎‏‎‎‏‎"</item>
     <item msgid="2075645297847971154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎On‎‏‎‎‏‎"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎Unavailable‎‏‎‎‏‎"</item>
+    <item msgid="1909756493418256167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎Off‎‏‎‎‏‎"</item>
+    <item msgid="4531508423703413340">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‎On‎‏‎‎‏‎"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‎Unavailable‎‏‎‎‏‎"</item>
     <item msgid="9103697205127645916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎Off‎‏‎‎‏‎"</item>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 9065e7f..f748e9d 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueo"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla bloqueada del perfil de trabajo"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Cerrar"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi desactivado"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi activado"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Modo de avión desactivado"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Modo de avión activado"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silencio total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"solo alarmas"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"No interrumpir."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"La función No interrumpir está desactivada."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Se activó la opción No interrumpir."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth activado"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth desactivado"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth activado"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Informes de Ubicación desactivados"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Informes de Ubicación activados"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarma: <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Más tiempo"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menos tiempo"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Linterna desactivada"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Linterna activada"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversión de color desactivada"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversión de color activada"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Zona móvil desactivada"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona móvil activada"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmisión de pantalla detenida"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Se desactivó el Ahorro de datos."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Se activó el Ahorro de datos."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brillo de pantalla"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Se detuvo el uso de datos móviles"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Datos pausados"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Red Wi-Fi no conectada"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión de color"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de colores"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Más configuraciones"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración del usuario"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Haz clic para escanear un código QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Presiona para escanear"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar el teclado con la tablet, primero debes activar Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activar"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controles de activación de notificaciones"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activa - En función del rostro"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Con los controles de activación de notificaciones, puedes establecer un nivel de importancia para las notificaciones de una app. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones. \n- Permitir interrupción en la pantalla completa. \n- Mostrar siempre. \n\n"<b>"Nivel 4"</b>" \n- No permitir interrupción en la pantalla completa. \n- Mostrar siempre. \n\n"<b>"Nivel 3"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n\n"<b>"Nivel 2"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n- No sonar ni vibrar. \n\n"<b>"Nivel 1"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n- No sonar ni vibrar. \n- Ocultar de la pantalla bloqueada y la barra de estado. \n- Mostrar al final de la lista de notificaciones. \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la app."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Listo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducir <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Acércate para reproducir en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"No se encontró"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps en ejecución en segundo plano"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index afb0e72..6e425ee 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desactivado"</item>
     <item msgid="2075645297847971154">"Activado"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"No disponible"</item>
+    <item msgid="1909756493418256167">"Desactivada"</item>
+    <item msgid="4531508423703413340">"Activada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"No disponible"</item>
     <item msgid="9103697205127645916">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index cf3b2f7..0494780 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueo."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla de bloqueo para el perfil de trabajo"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Cerrar"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi desactivado."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi activado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Modo avión desactivado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Modo avión activado."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silencio total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"solo alarmas"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"No molestar."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"No molestar desactivado."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"No molestar activado."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth activado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth desactivado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth activado."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Informes de ubicación desactivados."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Informes de ubicación activados."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"La alarma sonará a la(s) <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Más tiempo."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menos tiempo."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Linterna desactivada."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Linterna activada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversión de color desactivada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversión de color activada."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Punto de acceso móvil desactivado."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Punto de acceso móvil activado."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Envío de pantalla detenido."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Ahorro de datos desactivado."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Ahorro de datos activado."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brillo de la pantalla"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Datos móviles en pausa"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Datos pausados"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi no conectado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de color"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Más ajustes"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ajustes de usuario"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hecho"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Se ha producido un problema al obtener tus tarjetas. Inténtalo de nuevo más tarde."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Haz clic para escanear un código QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Para poder conectar tu teclado a tu tablet, debes activar el Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activar"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controles de energía de las notificaciones"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activado: basado en caras"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Los controles de energía de las notificaciones permiten establecer un nivel de importancia de 0 a 5 para las notificaciones de las aplicaciones. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones \n- Permitir interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 4"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 3"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n\n"<b>"Nivel 2"</b>" \n- Evitar interrumpir en el modo de pantalla completa\n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n\n"<b>"Nivel 1"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n- Ocultar de la pantalla de bloqueo y de la barra de estado \n- Mostrar en la parte inferior de la lista de notificaciones \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la aplicación"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hecho"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para que se reproduzca en ese dispositivo"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"No se ha encontrado"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicaciones ejecutándose en segundo plano"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index dd1ed43..3ac10ec 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desactivado"</item>
     <item msgid="2075645297847971154">"Activado"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"No disponible"</item>
+    <item msgid="1909756493418256167">"Desactivada"</item>
+    <item msgid="4531508423703413340">"Activada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"No disponible"</item>
     <item msgid="9103697205127645916">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 1494f0f..15c10be 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kuva lukustamine."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Töö lukustuskuva"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Sulgemine"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi on sisse lülitatud."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Lennurežiim on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Lennukirežiim on sisse lülitatud."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"täielik vaikus"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ainult alarmid"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Mitte segada."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Funktsioon Mitte segada on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Funktsioon Mitte segada on sisse lülitatud."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth on sees."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth on sisse lülitatud."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Asukohateavitus on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Asukohateavitus on sisse lülitatud."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Määratud äratus: <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Pikem aeg."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Lühem aeg."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Taskulamp on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Taskulamp on sisse lülitatud."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Värvi ümberpööramine on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Värvi ümberpööramine on sisse lülitatud."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiilside kuumkoht on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiilside kuumkoht on sisse lülitatud."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekraanikuva ülekandmine on peatatud."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Andmemahu säästja on välja lülitatud."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Andmemahu säästja on sisse lülitatud."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ekraani heledus"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobiilne andmeside on peatatud"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Andmekasutus on peatatud"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi-ühendus puudub"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Heledus"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Värvide ümberpööramine"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värviparandus"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Rohkem seadeid"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kasutaja seaded"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avage kasutamiseks"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Teie kaartide hankimisel ilmnes probleem, proovige hiljem uuesti"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukustuskuva seaded"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-koodi skannimine"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klõpsake QR-koodi skannimiseks"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Te ei kuule järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Klaviatuuri ühendamiseks tahvelarvutiga peate esmalt Bluetoothi sisse lülitama."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Lülita sisse"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Toite märguannete juhtnupud"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Sees – näopõhine"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Toite märguannete juhtnuppudega saate määrata rakenduse märguannete tähtsuse taseme vahemikus 0–5. \n\n"<b>"5. tase"</b>" \n- Kuva märguannete loendi ülaosas\n- Luba täisekraanil häirimine \n- Kuva alati ekraani servas \n\n"<b>"4. tase"</b>" \n- Keela täisekraanil häirimine \n- Kuva alati ekraani servas \n\n"<b>"3. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n\n"<b>"2. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n- Ära kunagi helise ega vibreeri \n\n"<b>"1. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n- Ära kunagi helise ega vibreeri \n- Peida lukustuskuval ja olekuribal \n- Kuva märguannete loendi allosas \n\n"<b>"Tase 0"</b>" \n- Blokeeri kõik rakenduse märguanded"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Rakenda"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> esitajalt <xliff:g id="ARTIST_NAME">%2$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Võta tagasi"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Minge lähemale, et seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g> esitada"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Esitatakse seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ei leitud"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kasutaja valimine"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Taustal töötavad rakendused"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Peata"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 26fbe80..27edd17 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Väljas"</item>
     <item msgid="2075645297847971154">"Sees"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Pole saadaval"</item>
+    <item msgid="1909756493418256167">"Väljas"</item>
+    <item msgid="4531508423703413340">"Sees"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Pole saadaval"</item>
     <item msgid="9103697205127645916">"Väljas"</item>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 565bc1b..454ad59 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantaila blokeatzeko aukera."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Laneko pantaila blokeatua"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Itxi"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi konexioa desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi konexioa aktibatu egin da."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Hegaldi modua desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Hegaldi modua aktibatu egin da."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"isiltasun osoa"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarmak soilik"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ez molestatzeko modua."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Desaktibatu egin da ez molestatzeko modua."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Aktibatu egin da ez molestatzeko modua."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth-a."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth bidezko konexioa aktibatuta dago."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth bidezko konexioa desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth bidezko konexioa aktibatu egin da."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Kokapena hautemateko aukera desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Kokapena hautemateko aukera aktibatu egin da."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarma ordu honetarako ezarri da: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Denbora gehiago."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Denbora gutxiago."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Flasha desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Flasha aktibatu egin da."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Koloreen alderantzikatzea desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Koloreen alderantzikatzea aktibatu egin da."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Wifi-gune mugikorra desaktibatu egin da."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Wifi-gune mugikorra aktibatu egin da."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Pantaila igortzeari utzi zaio."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Desaktibatuta dago datu-aurrezlea."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Aktibatuta dago datu-aurrezlea."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Bistaratu distira"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Datu-konexioa pausatu egin da"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Datuen erabilera pausatu da"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Ez zaude konektatuta wifi-sarera"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Distira"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kolore-alderantzikatzea"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koloreen zuzenketa"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Ezarpen gehiago"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Erabiltzaile-ezarpenak"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Eginda"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desblokeatu erabiltzeko"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Arazo bat izan da txartelak eskuratzean. Saiatu berriro geroago."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Pantaila blokeatuaren ezarpenak"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Eskaneatu QR kode bat"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kode bat eskaneatzeko, sakatu hau"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Sakatu eskaneatzeko"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profila"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ez duzu entzungo hurrengo alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Teklatua tabletara konektatzeko, Bluetooth eginbidea aktibatu behar duzu."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktibatu"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Bateria-mailaren arabera jakinarazpenak kontrolatzeko aukerak"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktibatuta: aurpegian oinarrituta"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Bateria-mailaren arabera jakinarazpenak kontrolatzeko aukerekin, 0 eta 5 bitarteko garrantzi-mailetan sailka ditzakezu aplikazioen jakinarazpenak. \n\n"<b>"5. maila"</b>" \n- Erakutsi jakinarazpenen zerrendaren goialdean. \n- Baimendu etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"4. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"3. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n\n"<b>"2. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n\n"<b>"1. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n- Ezkutatu pantaila blokeatutik eta egoera-barratik. \n- Erakutsi jakinarazpenen zerrendaren behealdean. \n\n"<b>"0. maila"</b>" \n- Blokeatu aplikazioaren jakinarazpen guztiak."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Eginda"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplikatu"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> bidez"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> bidez"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desegin"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Gerturatu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzeko"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> pantailan erreproduzitzen"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ez da aurkitu"</string>
@@ -863,7 +849,7 @@
     <string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak irudi bat bidali du"</string>
     <string name="new_status_content_description" msgid="6046637888641308327">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak egoera eguneratu du: <xliff:g id="STATUS">%2$s</xliff:g>"</string>
     <string name="person_available" msgid="2318599327472755472">"Konektatuta"</string>
-    <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Arazo bat gertatu da bateria-neurgailua irakurtzean"</string>
+    <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Arazo bat izan da bateria-neurgailua irakurtzean"</string>
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Informazio gehiago lortzeko, sakatu hau"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ez da ezarri alarmarik"</string>
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Hatz-marken sentsorea"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Hautatu erabiltzaile bat"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Atzeko planoan exekutatzen ari diren aplikazioak"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Gelditu"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index a2a08db..eb13a12 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desaktibatuta"</item>
     <item msgid="2075645297847971154">"Aktibatuta"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Ez dago erabilgarri"</item>
+    <item msgid="1909756493418256167">"Desaktibatuta"</item>
+    <item msgid="4531508423703413340">"Aktibatuta"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Ez dago erabilgarri"</item>
     <item msgid="9103697205127645916">"Desaktibatuta"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 674101d6..c2c1ffb 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"صفحه قفل."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"صفحه قفل کاری"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"بستن"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"‏Wi-Fi خاموش شد."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"‏Wi-Fi روشن شد."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"حالت هواپیما خاموش شد."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"حالت هواپیما روشن شد."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"سکوت کامل"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"فقط زنگ ساعت"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"مزاحم نشوید."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"«مزاحم نشوید» خاموش شد."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"«مزاحم نشوید» روشن شد."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"بلوتوث."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"بلوتوث روشن است."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"بلوتوث خاموش شد."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"بلوتوث روشن شد."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"گزارش موقعیت مکانی خاموش شد."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"گزارش موقعیت مکانی روشن شد."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"زنگ برای <xliff:g id="TIME">%s</xliff:g> تنظیم شد."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"زمان بیشتر."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"زمان کمتر."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"چراغ قوه خاموش شد."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"چراغ قوه روشن شد."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"وارونگی رنگ خاموش شد."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"وارونگی رنگ روشن شد."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"نقطه اتصال دستگاه همراه خاموش شد."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"نقطه اتصال دستگاه همراه روشن شد."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"فرستادن صفحه نمایش متوقف شد."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفه‌جویی داده خاموش شد."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفه‌جویی داده روشن شد."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"روشنایی نمایشگر"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"داده تلفن همراه موقتاً متوقف شده است"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"داده موقتاً متوقف شده است"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏Wi-Fi وصل نیست"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"روشنایی"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"وارونگی رنگ"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحیح رنگ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"تنظیمات بیشتر"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"تنظیمات کاربر"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"تمام"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارت‌ها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"اسکن رمزینه پاسخ‌سریع"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"برای اسکن رمزینه پاسخ‌سریع، کلیک کنید"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"در ساعت <xliff:g id="WHEN">%1$s</xliff:g>، دیگر صدای زنگ ساعت را نمی‌شنوید"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"برای مرتبط کردن صفحه‌کلید با رایانه لوحی، ابتدا باید بلوتوث را روشن کنید."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"روشن کردن"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"کنترل‌های قدرتمند اعلان"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"روشن - براساس چهره"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"با کنترل‌های قدرتمند اعلان می‌توانید سطح اهمیت اعلان‌های هر برنامه را از ۰ تا ۵ تعیین کنید. \n\n"<b>"سطح ۵"</b>" \n- در صدر فهرست اعلان‌ها نشان داده می‌شود \n- وقفه برای نمایش تمام‌صفحه مجاز است \n- همیشه اجمالی نشان داده می‌شود \n\n"<b>"سطح ۴"</b>" \n- وقفه برای نمایش تمام‌صفحه مجاز نیست \n- همیشه اجمالی نشان داده می‌شود \n\n"<b>"سطح ۳"</b>" \n- وقفه برای نمایش تمام‌صفحه مجاز نیست \n- هیچ‌وقت اجمالی نشان داده نمی‌شود \n\n"<b>"سطح ۲"</b>" \n- وقفه برای نمایش تمام‌صفحه مجاز نیست \n- هیچ‌وقت اجمالی نشان داده نمی‌شود \n- هیچ‌وقت صدا و لرزش ایجاد نمی‌کند \n\n"<b>"سطح ۱"</b>" \n- نمایش تمام صفحه مجاز نیست \n- هیچ‌وقت اجمالی نشان داده نمی‌شود \n- هیچ‌وقت صدا یا لرزش ایجاد نمی‌کند \n- در صفحه قفل و نوار وضعیت پنهان است \n- در پایین فهرست اعلان‌ها نشان داده می‌شود \n\n"<b>"سطح ۰"</b>" \n- همه اعلان‌های این برنامه مسدود است"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"تمام"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"اعمال"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> از <xliff:g id="ARTIST_NAME">%2$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%3$s</xliff:g> پخش کنید"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%2$s</xliff:g> پخش کنید"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"واگرد"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"برای پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g>، به دستگاه نزدیک‌تر شوید"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"درحال پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"غیرفعال، برنامه را بررسی کنید"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"پیدا نشد"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"انتخاب کاربر"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"برنامه‌هایی که در پس‌زمینه اجرا می‌شود"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"توقف"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index 8c40e71..dcde4d3 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"روشن"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"دردسترس نیست"</item>
     <item msgid="9103697205127645916">"خاموش"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 432d951..cd3844f 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -100,7 +100,7 @@
     <string name="accessibility_back" msgid="6530104400086152611">"Takaisin"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Aloitus"</string>
     <string name="accessibility_menu" msgid="2701163794470513040">"Valikko"</string>
-    <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Esteettömyys"</string>
+    <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Saavutettavuus"</string>
     <string name="accessibility_rotate_button" msgid="1238584767612362586">"Näytön kääntäminen"</string>
     <string name="accessibility_recent" msgid="901641734769533575">"Viimeisimmät"</string>
     <string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lukitse näyttö."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Työlukitusnäyttö"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Sulje"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi otettiin käyttöön."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Lentokonetila poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Lentokonetila otettiin käyttöön."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"hiljennä kaikki"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"vain herätykset"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Älä häiritse."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Älä häiritse -tila poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Älä häiritse -tila otettiin käyttöön."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth on päällä."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth otettiin käyttöön."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Sijainnin ilmoittaminen poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Sijainnin ilmoittaminen otettiin käyttöön."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Hälytys asetettu, aika: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Lisää aikaa."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Vähennä aikaa."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Taskulamppu poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Taskulamppu otettiin käyttöön."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Käänteiset värit poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Käänteiset värit otettiin käyttöön."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiiliyhteyden hotspot poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiiliyhteyden hotspot otettiin käyttöön."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ruudun lähetys pysäytettiin."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver poistettiin käytöstä."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver otettiin käyttöön."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Näytön kirkkaus"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobiilidatan käyttö on keskeytetty"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Tiedonsiirto keskeytettiin"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fiä ei ole yhdistetty"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kirkkaus"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Käänteiset värit"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värinkorjaus"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Lisäasetukset"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Käyttäjäasetukset"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avaa lukitus ja käytä"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Korttien noutamisessa oli ongelma, yritä myöhemmin uudelleen"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukitusnäytön asetukset"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skannaa QR-koodi"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Skannaa QR-koodi klikkaamalla"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Et kuule seuraavaa hälytystäsi (<xliff:g id="WHEN">%1$s</xliff:g>)."</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Jotta voit yhdistää näppäimistön tablettiisi, sinun on ensin otettava Bluetooth käyttöön."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ota käyttöön"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Ilmoitusten tehohallinta"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Päällä – kasvojen perusteella"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Ilmoitusten tehohallinnan avulla voit määrittää sovelluksen ilmoituksille tärkeystason väliltä 0–5. \n\n"<b>"Taso 5"</b>" \n– Ilmoitukset näytetään ilmoitusluettelon yläosassa \n– Näkyminen koko näytön tilassa sallitaan \n– Ilmoitukset kurkistavat aina näytölle\n\n"<b>"Taso 4"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ilmoitukset kurkistavat aina näytölle \n\n"<b>"Taso 3"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n\n"<b>"Taso 2"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n\n"<b>"Taso 1"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n– Ilmoitukset piilotetaan lukitusnäytöltä ja tilapalkista \n– Ilmoitukset näytetään ilmoitusluettelon alaosassa \n\n"<b>"Taso 0"</b>" \n– Kaikki sovelluksen ilmoitukset estetään"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Käytä"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) sovelluksessa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="APP_LABEL">%2$s</xliff:g>)"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Kumoa"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Siirry lähemmäs, jotta <xliff:g id="DEVICENAME">%1$s</xliff:g> voi toistaa tämän"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> toistaa tämän"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ei löydy"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Valitse käyttäjä"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Sovellukset jotka ovat käynnissä taustalla"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Lopeta"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index cb9e07c..d838cf8 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Poissa päältä"</item>
     <item msgid="2075645297847971154">"Päällä"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Ei saatavilla"</item>
+    <item msgid="1909756493418256167">"Pois päältä"</item>
+    <item msgid="4531508423703413340">"Päällä"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Ei saatavilla"</item>
     <item msgid="9103697205127645916">"Poissa päältä"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 1123c40..440369d 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Écran de verrouillage"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Verrouillage de l\'écran du profil professionnel"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Fermer"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi désactivé"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi activé."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Le mode Avion est désactivé."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Le mode Avion est activé."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"aucune interruption"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarmes seulement"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne pas déranger."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Le mode Ne pas déranger est désactivé."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Le mode Ne pas déranger est activé."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth activé."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth désactivé."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth activé."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Signalement de position désactivé."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Signalement de position activé."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Plus longtemps"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Moins longtemps."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lampe de poche désactivée."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lampe de poche activée."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversion des couleurs désactivée."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversion des couleurs activée."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Point d\'accès mobile désactivé."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Point d\'accès mobile activé."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Diffusion d\'écran arrêtée."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Mode Économiseur de données désactivé."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Mode Économiseur de données activé."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Luminosité de l\'écran"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Données cellulaires interrompues"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Données désactivées"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Non connecté au Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Plus de paramètres"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Terminé"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Un problème est survenu lors de la récupération de vos cartes, veuillez réessayer plus tard"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Numériser le code QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Cliquez pour numériser un code QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pour connecter votre clavier à votre tablette, vous devez d\'abord activer la connectivité Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activer"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Réglages avancés des notifications"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activé : en fonction du visage"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Avec les réglages avancés des notifications, vous pouvez définir un degré d\'importance de 0 à 5 pour les notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher dans le haut de la liste des notifications \n- Autoriser les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 4"</b>" \n- Empêcher les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 3"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n\n"<b>"Niveau 2"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n\n"<b>"Niveau 1"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n- Masquer de l\'écran de verrouillage et de la barre d\'état status bar \n- Afficher dans le bas de la liste des notifications \n\n"<b>"Level 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Terminé"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Rapprochez-vous pour faire jouer le contenu sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Sélect. utilisateur"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Applications exécutées en arrière-plan"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index eb38d94..0b087ad 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Désactivé"</item>
     <item msgid="2075645297847971154">"Activé"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Non disponible"</item>
+    <item msgid="1909756493418256167">"Désactivée"</item>
+    <item msgid="4531508423703413340">"Activée"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Non disponible"</item>
     <item msgid="9103697205127645916">"Désactivée"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 74ad1bd..891e85c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Écran de verrouillage"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Écran de verrouillage du profil professionnel"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Fermer"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi désactivé."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi activé."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Le mode Avion est désactivé."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Le mode Avion est activé."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"aucune interruption"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alarmes uniquement"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne pas déranger."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Le mode Ne pas déranger a été désactivé."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Le mode Ne pas déranger a été activé."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth activé."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth désactivé."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth activé."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Mise à jour de la position désactivée."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Mise à jour de la position activée."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Plus longtemps"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Moins longtemps"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lampe de poche désactivée."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lampe de poche activée."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversion des couleurs désactivée."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversion des couleurs activée."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Point d\'accès mobile désactivé."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Point d\'accès mobile activé."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Diffusion d\'écran interrompue."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"L\'économiseur de données est désactivé."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"L\'économiseur de données est activé."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Luminosité de l\'affichage"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Les données mobiles sont suspendues"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Données désactivées"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi non connecté"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Plus de paramètres"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"OK"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scanner un code QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Cliquer pour scanner un code QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Appuyer pour scanner"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pour connecter un clavier à votre tablette, vous devez avoir activé le Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activer"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Commandes de gestion des notifications"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Active - En fonction du visage"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Grâce aux commandes de gestion des notifications, vous pouvez définir le niveau d\'importance (compris entre 0 et 5) des notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher en haut de la liste des notifications \n- Autoriser l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 4"</b>" \n- Empêcher l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 3"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n\n"<b>"Niveau 2"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n\n"<b>"Niveau 1"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n- Masquer les notifications dans l\'écran de verrouillage et la barre d\'état \n- Afficher au bas de la liste des notifications \n\n"<b>"Niveau 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"OK"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> depuis <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Rapprochez-vous pour lire sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>…"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir utilisateur"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Applis exécutées en arrière-plan"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 3aefb1d..fbae02a 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Désactivé"</item>
     <item msgid="2075645297847971154">"Activé"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Indisponible"</item>
+    <item msgid="1909756493418256167">"Désactivée"</item>
+    <item msgid="4531508423703413340">"Activée"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Indisponible"</item>
     <item msgid="9103697205127645916">"Désactivée"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 121d434..70a3c68 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueo."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla de bloqueo do perfil de traballo"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Pechar"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi desactivada."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi activada."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Desactivouse o modo avión."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Activouse o modo avión."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silencio total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"só alarmas"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Modo Non molestar."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Desactivouse o modo Non molestar."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Activouse o modo Non molestar."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth activado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth desactivado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth activado."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Desactiváronse os Informes de localización."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Activáronse os Informes de localización."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarma definida para as <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Máis tempo."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menos tempo."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Desactivouse a lanterna."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Activouse a lanterna."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Desactivouse a inversión da cor."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Activouse a inversión da cor."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Desactivouse a zona wifi móbil."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Activouse a zona wifi móbil."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Detívose a emisión en pantalla."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Desactivouse o aforro de datos."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Activouse o aforro de datos."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brillo de pantalla"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Pausáronse os datos móbiles"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Os datos están en pausa"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"A wifi non está conectada"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión da cor"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección da cor"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Máis opcións"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración de usuario"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Feito"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Produciuse un problema ao obter as tarxetas. Téntao de novo máis tarde"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración da pantalla de bloqueo"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fai clic para escanear un código QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar o teu teclado coa tableta, primeiro tes que activar o Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activar"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controis de notificacións mellorados"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activada: baseada na cara"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Cos controis de notificacións mellorados, podes asignarlles un nivel de importancia comprendido entre 0 e 5 ás notificacións dunha aplicación determinada. \n\n"<b>"Nivel 5"</b>" \n- Mostrar na parte superior da lista de notificacións. \n- Permitir interrupcións no modo de pantalla completa. \n- Mostrar sempre. \n\n"<b>"Nivel 4"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Mostrar sempre. \n\n"<b>"Nivel 3"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n\n"<b>"Nivel 2"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n- Non soar nin vibrar nunca. \n\n"<b>"Nivel 1"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n- Non soar nin vibrar nunca. \n- Ocultar na pantalla de bloqueo e na barra de estado. \n- Mostrar na parte inferior da lista de notificacións. \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas as notificacións da aplicación."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Feito"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfacer"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Achega o dispositivo para reproducir a música en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Estase reproducindo o contido en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Non se atopou"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicacións que se están executando en segundo plano"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Deter"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index 90dcf7d..531e7ff 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Non"</item>
     <item msgid="2075645297847971154">"Si"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Non dispoñible"</item>
+    <item msgid="1909756493418256167">"Desactivada"</item>
+    <item msgid="4531508423703413340">"Activada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Non dispoñible"</item>
     <item msgid="9103697205127645916">"Non"</item>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index c397848..5f262b5 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"લૉક સ્ક્રીન."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"કાર્ય લૉક સ્ક્રીન"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"બંધ કરો"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi ચાલુ કર્યું."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"એરપ્લેન મોડ બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"એરપ્લેન મોડ ચાલુ કર્યો."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"બિલકુલ અવાજ નહીં"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"માત્ર અલાર્મ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ખલેલ પાડશો નહીં."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"ખલેલ પાડશો નહીં બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"ખલેલ પાડશો નહીં ચાલુ કર્યું."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"બ્લૂટૂથ."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"બ્લૂટૂથ ચાલુ."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"બ્લૂટૂથ બંધ કરી."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"બ્લૂટૂથ ચાલુ કર્યું."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"સ્થાનની જાણ કરવી બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"સ્થાનની જાણ કરવી ચાલુ કર્યું."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> માટે એલાર્મ સેટ કર્યું."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"વધુ સમય."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"ઓછો સમય."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ફ્લેશલાઇટ બંધ કરી."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ફ્લેશલાઇટ ચાલુ કરી."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"રંગ ઉલટાવવાનું બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"રંગ ઉલટાવવાનું ચાલુ કર્યું."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"મોબાઇલ હૉટસ્પૉટ બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"મોબાઇલ હૉટસ્પૉટ ચાલુ કર્યું."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"સ્ક્રીન કાસ્ટિંગ બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ડેટા સેવર બંધ કર્યું."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ડેટા સેવર ચાલુ કર્યું."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"પ્રદર્શન તેજ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"મોબાઇલ ડેટા થોભાવ્યો છે"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ડેટા થોભાવ્યો છે"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"વાઇ-ફાઇ કનેક્ટ નથી"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"તેજ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"વિપરીત રંગમાં બદલવું"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"રંગ સુધારણા"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"વધુ સેટિંગ"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"વપરાશકર્તા સેટિંગ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"થઈ ગયું"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ઉપયોગ કરવા માટે અનલૉક કરો"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"તમારા કાર્ડની માહિતી મેળવવામાં સમસ્યા આવી હતી, કૃપા કરીને થોડા સમય પછી ફરી પ્રયાસ કરો"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"લૉક સ્ક્રીનના સેટિંગ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR સ્કૅન કરો"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR કોડ સ્કૅન કરવા માટે ક્લિક કરો"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"સ્કૅન કરવા માટે ટૅપ કરો"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"તમે <xliff:g id="WHEN">%1$s</xliff:g> એ તમારો આગલો એલાર્મ સાંભળશો નહીં"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"તમારા ટેબ્લેટ સાથે કીબોર્ડ કનેક્ટ કરવા માટે, તમારે પહેલાં બ્લૂટૂથ ચાલુ કરવાની જરૂર પડશે."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ચાલુ કરો"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"પાવર સૂચના નિયંત્રણો"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ચાલુ છે - ચહેરા આધારિત રોટેશન"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"પાવર સૂચના નિયંત્રણો સાથે, તમે ઍપની સૂચનાઓ માટે 0 થી 5 સુધીના મહત્વના સ્તરને સેટ કરી શકો છો. \n\n"<b>"સ્તર 5"</b>" \n- સૂચના સૂચિની ટોચ પર બતાવો \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 4"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 3"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n\n"<b>"સ્તર 2"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n\n"<b>"સ્તર 1"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n- લૉક સ્ક્રીન અને સ્ટેટસ બારથી છુપાવો \n- સૂચના સૂચિના તળિયા પર બતાવો \n\n"<b>"સ્તર 0"</b>" \n- ઍપની તમામ સૂચનાઓને બ્લૉક કરો"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"થઈ ગયું"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"લાગુ કરો"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> પર <xliff:g id="ARTIST_NAME">%2$s</xliff:g>નું <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> પર <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"છેલ્લો ફેરફાર રદ કરો"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવા માટે વધુ નજીક ખસેડો"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવામાં આવી રહ્યું છે"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"મળ્યું નથી"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"બૅકગ્રાઉન્ડમાં ચાલતી ઍપ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"રોકો"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index e50f3cf..10e7ac7 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ચાલુ છે"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ઉપલબ્ધ નથી"</item>
     <item msgid="9103697205127645916">"બંધ છે"</item>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 098b2a1..755c480 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"लॉक स्क्रीन."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"वर्क लॉक स्‍क्रीन"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"बंद करें"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"वाई-फ़ाई को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"वाई-फ़ाई को चालू किया गया."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"हवाई जहाज़ मोड को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"हवाई जहाज़ मोड को चालू किया गया."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"कोई आवाज़ सुनाई नहीं देगी"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"सिर्फ़ अलार्म की आवाज़ सुनाई देगी"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"परेशान न करें."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'परेशान न करें\' बंद किया गया."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\'परेशान न करें\' चालू किया गया."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ब्लूटूथ."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ब्लूटूथ चालू है."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ब्लूटूथ को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ब्लूटूथ को चालू किया गया."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"जगह की रिपोर्ट को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"जगह की रिपोर्ट को चालू किया गया."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> के लिए अलार्म सेट किया गया."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ज़्यादा समय."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"कम समय."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"फ़्लैशलाइट को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"फ़्लैशलाइट को चालू किया गया."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रंग व्‍युत्‍क्रमण को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रंग व्‍युत्‍क्रमण को चालू किया गया."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हॉटस्‍पॉट को बंद किया गया."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हॉटस्‍पॉट को चालू किया गया."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्‍क्रीन कास्‍ट करना रुक गया."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"डेटा बचाने की सेटिंग बंद कर दी गई है."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"डेटा बचाने की सेटिंग चालू कर दी गई है."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"स्क्रीन की स्क्रीन की रोशनी"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"मोबाइल डेटा रोक दिया गया है"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"डेटा रोक दिया गया है"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"वाई-फ़ाई कनेक्ट नहीं है"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"स्क्रीन की रोशनी"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"रंग बदलने की सुविधा"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग में सुधार करने की सुविधा"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"और सेटिंग"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"उपयोगकर्ता सेटिंग"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"हो गया"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"इस्तेमाल करने के लिए, डिवाइस अनलॉक करें"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"आपके कार्ड की जानकारी पाने में कोई समस्या हुई है. कृपया बाद में कोशिश करें"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन की सेटिंग"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"क्यूआर कोड स्कैन करें"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"क्यूआर कोड स्कैन करने के लिए क्लिक करें"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"हवाई जहाज़ मोड"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"आपको <xliff:g id="WHEN">%1$s</xliff:g> पर अपना अगला अलार्म नहीं सुनाई देगा"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"अपने कीबोर्ड को अपने टैबलेट से कनेक्ट करने के लिए, आपको पहले ब्लूटूथ चालू करना होगा."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"चालू करें"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"पावर सूचना नियंत्रण"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"चालू है - चेहरे की गतिविधि के हिसाब से कैमरे को घुमाने की सुविधा"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"पावर सूचना नियंत्रण के ज़रिये, आप किसी ऐप की सूचना को उसकी अहमियत के हिसाब से 0 से 5 के लेवल पर सेट कर सकते हैं.\n\n"<b>"लेवल 5"</b>" \n- सूचना सूची में सबसे ऊपर दिखाएं \n- पूरे स्क्रीन को ढंकने की अनुमति दें \n- लगातार देखते रहें \n\n"<b>" लेवल 4"</b>" \n- पूरे स्क्रीन को ढंकें \n- लगातार देखते रहें \n\n"<b>"लेवल 3"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n-कभी भी न देखें \n\n"<b>"लेवल 2"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n\n"<b>"लेवल 1"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी न देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n- लॉक स्क्रीन और स्टेटस बार से छिपाएं \n- सूचना सूची के नीचे दिखाएं \n\n"<b>"लेवल 0"</b>" \n- ऐप्लिकेशन की सभी सूचनाएं रोक दें"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"हो गया"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"लागू करें"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> पर, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> का <xliff:g id="SONG_NAME">%1$s</xliff:g> चलाएं"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> पर, <xliff:g id="SONG_NAME">%1$s</xliff:g> चलाएं"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"पहले जैसा करें"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर गाने चलाने के लिए उसके पास जाएं"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर चल रहा है"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल नहीं है"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"उपयोगकर्ता चुनें"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"बैकग्राउंड में चल रहे ऐप्लिकेशन"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"बंद करें"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index 7698912..e52ee17 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"चालू है"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"उपलब्ध नहीं है"</item>
     <item msgid="9103697205127645916">"बंद है"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 45fa359..a5be1e3 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključavanje zaslona."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan zaslon radnog profila"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvaranje"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi isključen."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi uključen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Način rada u zrakoplovu isključen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Način rada u zrakoplovu uključen."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"potpuna tišina"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"samo alarmi"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne uznemiravaj."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Isključena je značajka Ne uznemiravaj."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Uključena je značajka Ne uznemiravaj."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth uključen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth isključen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth uključen."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Izvješćivanje o lokaciji isključeno."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Izvješćivanje o lokaciji uključeno."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Vrijeme alarma: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Više vremena."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Manje vremena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Svjetiljka isključena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Svjetiljka uključena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inverzija boja isključena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inverzija boja uključena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilna žarišna točka isključena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilna žarišna točka uključena."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Emitiranje zaslona zaustavljeno."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Štednja podatkovnog prometa isključena."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Štednja podatkovnog prometa uključena."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Svjetlina zaslona"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobilni su podaci pauzirani"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Podaci su pauzirani"</string>
@@ -252,6 +234,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi mreža nije povezana"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svjetlina"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Više  postavki"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -469,8 +452,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da biste koristili"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Pojavio se problem prilikom dohvaćanja kartica, pokušajte ponovo kasnije"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključanog zaslona"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR kôd"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da biste skenirali QR kôd"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -490,6 +475,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Da biste povezali tipkovnicu s tabletom, morate uključiti Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Uključi"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Napredne kontrole obavijesti"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na temelju lica"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Napredne kontrole obavijesti omogućuju vam da postavite razinu važnosti za obavijesti aplikacije od 0 do 5. \n\n"<b>"Razina 5"</b>" \n– prikaži na vrhu popisa obavijesti \n– dopusti prekide prikaza na cijelom zaslonu \n– uvijek dopusti brzi pregled \n\n"<b>"Razina 4"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– uvijek dopusti brzi pregled \n\n"<b>"Razina 3"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled\n\n"<b>"Razina 2"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled \n– nikad ne emitiraj zvuk ni vibraciju \n\n"<b>"Razina 1"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled \n– nikad ne emitiraj zvuk ni vibraciju \n– ne prikazuj na zaključanom zaslonu i traci statusa \n– prikaži na dnu popisa obavijesti \n\n"<b>"Razina 0"</b>" \n– blokiraj sve obavijesti aplikacije"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
@@ -813,7 +799,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Približite se radi reprodukcije na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -901,4 +888,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odabir korisnika"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije koje se izvode u pozadini"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index ae9ffb3..eb9ae52 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Isključeno"</item>
     <item msgid="2075645297847971154">"Uključeno"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nedostupno"</item>
+    <item msgid="1909756493418256167">"Isključeno"</item>
+    <item msgid="4531508423703413340">"Uključeno"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nedostupno"</item>
     <item msgid="9103697205127645916">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 5539787..0275b72 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lezárási képernyő."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Munka lezárási képernyővel"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Bezárás"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi kikapcsolva."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi bekapcsolva."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Repülős üzemmód kikapcsolva."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Repülős üzemmód bekapcsolva."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"teljes némítás"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"csak ébresztések"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne zavarjanak."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"A Ne zavarjanak mód kikapcsolva."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"A Ne zavarjanak mód bekapcsolva."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth bekapcsolva."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth kikapcsolva."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth bekapcsolva."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"A tartózkodási hely jelentése kikapcsolva."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"A tartózkodási hely jelentése bekapcsolva."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Ébresztés időpontja: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Több idő."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Kevesebb idő."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Vaku kikapcsolva."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Vaku bekapcsolva."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"A színek invertálása kikapcsolva."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"A színek invertálása bekapcsolva."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"A mobil hotspot kikapcsolva."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"A mobil hotspot bekapcsolva."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A képernyő átküldése leállítva."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Adatforgalom-csökkentő kikapcsolva."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Adatforgalom-csökkentő bekapcsolva."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"A kijelző fényereje"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobiladatok szüneteltetve"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Az adatforgalom szünetel"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nem kapcsolódik Wi‑Fi-hálózathoz"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Fényerő"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színjavítás"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"További beállítások"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Felhasználói beállítások"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Oldja fel a használathoz"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Probléma merült fel a kártyák lekérésekor, próbálja újra később"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lezárási képernyő beállításai"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-kód beolvasása"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kattintson a QR-kód beolvasához"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nem fogja hallani az ébresztést ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Ha a billentyűzetet csatlakoztatni szeretné táblagépéhez, először engedélyeznie kell a Bluetooth-kapcsolatot."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Bekapcsolás"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Teljes körű értesítésvezérlők"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Be: Arcalapú"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Az értesítési beállítások révén 0-tól 5-ig állíthatja be a fontossági szintet az alkalmazás értesítéseinél. \n\n"<b>"5. szint"</b>" \n– Megjelenítés az értesítési lista tetején \n– Teljes képernyő megszakításának engedélyezése \n– Mindig felugrik \n\n"<b>"4. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Mindig felugrik \n\n"<b>"3. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n\n"<b>"2. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n– Soha nincs hangjelzés és rezgés \n\n"<b>"1. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n– Soha nincs hangjelzés vagy rezgés \n– Elrejtés a lezárási képernyőről és az állapotsávról \n– Megjelenítés az értesítési lista alján \n\n"<b>"0. szint"</b>" \n– Az alkalmazás összes értesítésének letiltása"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Kész"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Alkalmaz"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> <xliff:g id="SONG_NAME">%1$s</xliff:g> című számának lejátszása innen: <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> lejátszása innen: <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Visszavonás"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Menjen közelebb a következőn való lejátszáshoz: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Lejátszás folyamatban a(z) <xliff:g id="DEVICENAME">%1$s</xliff:g> eszközön"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nem található"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Felhasználóválasztás"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Több alkalmazás is fut a háttérben"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Leállítás"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 140f2db..ba92bfd 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Ki"</item>
     <item msgid="2075645297847971154">"Be"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nem áll rendelkezésre"</item>
+    <item msgid="1909756493418256167">"Ki"</item>
+    <item msgid="4531508423703413340">"Be"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nem áll rendelkezésre"</item>
     <item msgid="9103697205127645916">"Ki"</item>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 7323cef..c32b166 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Էկրանի կողպում:"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Աշխատանքային պրոֆիլի կողպէկրան"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Փակել"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi-ն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi-ը միացավ:"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Ավիառեժիմն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Ավիառեժիմը միացավ:"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"կատարյալ լռություն"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"միայն զարթուցիչը"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Չանհանգստացնել։"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"«Չանհանգստացնել» ռեժիմն անջատվեց։"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"«Չանհանգստացնել» ռեժիմը միացվեց։"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth:"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth-ը միացված է:"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth-ն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth-ը միացավ:"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Տեղադրության ծանուցումն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Տեղադրության ծանուցումը միացավ:"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Զարթուցիչը դրված է <xliff:g id="TIME">%s</xliff:g>-ին:"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Ավելացնել ժամանակը:"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Պակասեցնել ժամանակը:"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Լապտերն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Լապտերը միացավ:"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Գունաշրջումն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Գունաշրջումը միացավ:"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Շարժական կապի WiFi ցրիչն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Շարժական կապի WiFi ցրիչը միացավ:"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Էկրանի հեռարձակումն ընդհատվեց:"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Տվյալների խնայումն անջատվեց:"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Թրաֆիկի տնտեսումը միացվեց:"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ցուցադրել պայծառությունը"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Բջջային տվյալներն ընդհատված են"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Տվյալների օգտագործումը դադարեցված է"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi-ը միացված չէ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Պայծառություն"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Գունաշրջում"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Գունաշտկում"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Հավելյալ կարգավորումներ"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Օգտատիրոջ կարգավորումներ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Պատրաստ է"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ապակողպել՝ օգտագործելու համար"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Չհաջողվեց բեռնել քարտերը։ Նորից փորձեք։"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Կողպէկրանի կարգավորումներ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR կոդերի սկանավորում"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Սեղմեք՝ QR կոդը սկանավորելու համար"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Հպեք՝ սկանավորելու համար"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ժամը <xliff:g id="WHEN">%1$s</xliff:g>-ի զարթուցիչը չի զանգի"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Ստեղնաշարը ձեր պլանշետին միացնելու համար նախ անհրաժեշտ է միացնել Bluetooth-ը:"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Միացնել"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Ծանուցումների ընդլայնված կառավարում"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Միաց․ – Դիմաճանաչման հիման վրա"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Ծանուցումների ընդլայնված կառավարման օգնությամբ կարող եք յուրաքանչյուր հավելվածի ծանուցումների համար նշանակել կարևորության աստիճան՝ 0-5 սահմաններում: \n\n"<b>"5-րդ աստիճան"</b>" \n- Ցուցադրել ծանուցումների ցանկի վերևում \n- Թույլատրել լիաէկրան ընդհատումները \n- Միշտ ցուցադրել կարճ ծանուցումները \n\n"<b>"4-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Միշտ ցուցադրել կարճ ծանուցումները \n\n"<b>"3-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n\n"<b>"2-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n- Անջատել ձայնը և թրթռումը \n\n"<b>"1-ին աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n- Անջատել ձայնը և թրթռումը \n- Չցուցադրել կողպէկրանում և կարգավիճակի գոտում \n- Ցուցադրել ծանուցումների ցանկի ներքևում \n\n"<b>"0-րդ աստիճան"</b>\n"- Արգելափակել հավելվածի բոլոր ծանուցումները"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Փակել"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Կիրառել"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Նվագարկել <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="ARTIST_NAME">%2$s</xliff:g>-ի կատարմամբ <xliff:g id="APP_LABEL">%3$s</xliff:g> հավելվածից"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Նվագարկել <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="APP_LABEL">%2$s</xliff:g> հավելվածից"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Հետարկել"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Ավելի մոտ եկեք՝ <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքում նվագարկելու համար"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Նվագարկվում է <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքում"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Չի գտնվել"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Ընտրեք օգտատեր"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Ֆոնային ռեժիմում աշխատող հավելվածներ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Դադարեցնել"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index df69ce7..b52646f 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Միացված է"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Հասանելի չէ"</item>
     <item msgid="9103697205127645916">"Անջատված է"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f659465..24f8698 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Layar kunci."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Layar kunci kantor"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Tutup"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi diaktifkan."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Mode pesawat dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Mode pesawat diaktifkan."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"senyap total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"hanya alarm"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Jangan Ganggu."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Mode Jangan Ganggu dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Mode Jangan Ganggu diaktifkan."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth aktif."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth diaktifkan."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Pelaporan lokasi dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Pelaporan lokasi diaktifkan."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm disetel ke <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Lebih lama."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Lebih cepat."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Senter dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Senter diaktifkan."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversi warna dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversi warna diaktifkan."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Hotspot seluler dinonaktifkan."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Hotspot seluler diaktifkan."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmisi layar berhenti."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Penghemat Data nonaktif."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Penghemat Data diaktifkan."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Kecerahan tampilan"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Data seluler dijeda"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data dijeda"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi tidak terhubung"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversi warna"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koreksi warna"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Setelan lainnya"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setelan pengguna"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Terjadi masalah saat mendapatkan kartu Anda, coba lagi nanti"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setelan layar kunci"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Pindai QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik untuk memindai kode QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Untuk menghubungkan keyboard dengan tablet, terlebih dahulu aktifkan Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktifkan"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kontrol notifikasi daya"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktif - Berbasis deteksi wajah"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Dengan kontrol notifikasi daya, Anda dapt menyetel level kepentingan notifikasi aplikasi dari 0 sampai 5. \n\n"<b>"Level 5"</b>" \n- Muncul di atas daftar notifikasi \n- Izinkan interupsi layar penuh \n- Selalu intip pesan \n\n"<b>"Level 4"</b>" \n- Jangan interupsi layar penuh \n- Selalu intip pesan \n\n"<b>"Level 3"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n\n"<b>"Level 2"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n- Tanpa suara dan getaran \n\n"<b>"Level 1"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n- Tanpa suara atau getaran \n- Sembunyikan dari layar kunci dan bilah status \n- Muncul di bawah daftar notifikasi \n\n"<b>"Level 0"</b>" \n- Blokir semua notifikasi dari aplikasi"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Terapkan"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> dari <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> dari <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Urungkan"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Dekatkan untuk memutar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Diputar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikasi berjalan di latar belakang"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 811294f..0007dfc 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Nonaktif"</item>
     <item msgid="2075645297847971154">"Aktif"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Tidak tersedia"</item>
+    <item msgid="1909756493418256167">"Nonaktif"</item>
+    <item msgid="4531508423703413340">"Aktif"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Tidak tersedia"</item>
     <item msgid="9103697205127645916">"Nonaktif"</item>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 48068b1..a7a43e6 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lásskjár."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Vinnulásskjár"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Loka"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Slökkt á Wi-Fi."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Kveikt á Wi-Fi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Slökkt á flugstillingu."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Kveikt á flugstillingu."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"algjör þögn"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"aðeins vekjarar"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ónáðið ekki."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Slökkt á „Ónáðið ekki“."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Kveikt á „Ónáðið ekki“."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Kveikt á Bluetooth."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Slökkt á Bluetooth."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Kveikt á Bluetooth."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Slökkt á staðsetningartilkynningum."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Kveikt á staðsetningartilkynningum."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Vekjari stilltur á <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Meiri tími."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Minni tími."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Slökkt á vasaljósi."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Kveikt á vasaljósi."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Slökkt á umsnúningi lita."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Kveikt á umsnúningi lita."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Slökkt á farsímaaðgangsstað."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Kveikt á farsímaaðgangsstað."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Skjáútsendingu hætt."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Slökkt var á gagnasparnaði."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Kveikt var á gagnasparnaði."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Birtustig skjás"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Hlé gert á farsímagögnum"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Slökkt er á gagnanotkun"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi ekki tengt"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Birtustig"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Umsnúningur lita"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Litaleiðrétting"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Fleiri stillingar"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Notandastillingar"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Lokið"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Taktu úr lás til að nota"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Vandamál kom upp við að sækja kortin þín. Reyndu aftur síðar"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Stillingar fyrir læstan skjá"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanna QR-kóða"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Smelltu til að skanna QR-kóða"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ekki mun heyrast í vekjaranum <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Til að geta tengt lyklaborðið við spjaldtölvuna þarftu fyrst að kveikja á Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Kveikja"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Orkustillingar tilkynninga"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Kveikt – út frá andliti"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Með orkutilkynningastýringum geturðu stillt mikilvægi frá 0 upp í 5 fyrir tilkynningar forrita. \n\n"<b>"Stig 5"</b>" \n- Sýna efst á tilkynningalista \n- Leyfa truflun þegar birt er á öllum skjánum \n- Kíkja alltaf \n\n"<b>"Stig 4"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja alltaf \n\n"<b>"Stig 3"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n\n"<b>"Stig 2"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n- Slökkva á hljóði og titringi \n\n"<b>"Stig 1"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n- Slökkva á hljóði og titringi \n- Fela á lásskjá og stöðustiku \n- Sýna neðst á tilkynningalista \n\n"<b>"Stig 0"</b>" \n- Setja allar tilkynningar frá forriti á bannlista"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Lokið"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Nota"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> með <xliff:g id="ARTIST_NAME">%2$s</xliff:g> í <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> í <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Afturkalla"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Færðu nær til að spila í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Spilast í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Fannst ekki"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velja notanda"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Forrit keyra í bakgrunni"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stöðva"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index dd704a7..88472ef 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Slökkt"</item>
     <item msgid="2075645297847971154">"Kveikt"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Ekki í boði"</item>
+    <item msgid="1909756493418256167">"Slökkt"</item>
+    <item msgid="4531508423703413340">"Kveikt"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Ekki í boði"</item>
     <item msgid="9103697205127645916">"Slökkt"</item>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index cd53a50..2f3d50f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Schermata di blocco."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Schermata di blocco del profilo di lavoro"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Chiudi"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi disattivato."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi attivato."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Modalità aereo disattivata."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Modalità aereo attivata."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silenzio totale"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"solo sveglie"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Non disturbare."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"Non disturbare\" disattivato."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"Non disturbare\" attivato."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth attivo."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth disattivato."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth attivato."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Segnalazione della posizione disattivata."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Segnalazione della posizione attivata."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Sveglia impostata per le <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Più tempo."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Meno tempo."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Torcia disattivata."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Torcia attivata."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversione dei colori disattivata."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversione dei colori attivata."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Hotspot mobile disattivato."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Hotspot mobile attivato."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Trasmissione dello schermo interrotta."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Funzione Risparmio dati disattivata."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Funzione Risparmio dati attivata."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Luminosità dello schermo"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Dati mobili sospesi"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Dati sospesi"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nessuna connessione Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosità"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversione dei colori"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correzione del colore"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Altre impostazioni"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Impostazioni utente"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Fine"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Sblocca per usare"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Si è verificato un problema durante il recupero delle tue carte. Riprova più tardi."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Impostazioni schermata di blocco"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scansiona QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fai clic per scansionare un codice QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tocca per scansionare"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Non sentirai la tua prossima sveglia <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Per connettere la tastiera al tablet, devi prima attivare il Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Attiva"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controlli di gestione delle notifiche"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On - Rotazione basata sul viso"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"I controlli di gestione delle notifiche ti consentono di impostare un livello di importanza compreso tra 0 e 5 per le notifiche di un\'app. \n\n"<b>"Livello 5"</b>" \n- Mostra in cima all\'elenco di notifiche \n- Consenti l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 4"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 3"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n\n"<b>"Livello 2"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n\n"<b>"Livello 1"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n- Nascondi da schermata di blocco e barra di stato \n- Mostra in fondo all\'elenco di notifiche \n\n"<b>"Livello 0"</b>" \n- Blocca tutte le notifiche dell\'app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Fine"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Applica"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> di <xliff:g id="ARTIST_NAME">%2$s</xliff:g> da <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> da <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Annulla"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Avvicinati per riprodurre su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"In riproduzione su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Controllo non trovato"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"App in esecuzione in background"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Interrompi"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 1596998..071a970 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Off"</item>
     <item msgid="2075645297847971154">"On"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Non disponibile"</item>
+    <item msgid="1909756493418256167">"Off"</item>
+    <item msgid="4531508423703413340">"On"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Non disponibile"</item>
     <item msgid="9103697205127645916">"Off"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f36827c..757ff77 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"מסך נעילה."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"מסך נעילה של עבודה"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"סגירה"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"‏Wifi כבוי."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"‏Wifi מופעל."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"מצב טיסה נכבה."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"מצב טיסה הופעל."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"השתקה מוחלטת"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"רק התראות"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"נא לא להפריע."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"התכונה \'נא לא להפריע\' כבויה."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"התכונה \'נא לא להפריע\' פועלת."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"‏Bluetooth מופעל."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"‏Bluetooth נכבה."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"‏Bluetooth הופעל."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"התכונה \'דיווח מיקום\' הושבתה."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"התכונה \'דיווח מיקום\' הופעלה."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"ההתראה נקבעה ל-<xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"יותר זמן."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"פחות זמן."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"הפנס נכבה."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"הפנס הופעל."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"היפוך צבעים כבוי."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"היפוך צבעים מופעל."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"‏נקודת האינטרנט (hotspot) כבויה."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"‏נקודת האינטרנט (hotspot) מופעלת."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"העברת המסך הופסקה."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"‏חוסך הנתונים (Data Saver) כובה."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"‏חוסך הנתונים (Data Saver) הופעל."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"בהירות תצוגה"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"חבילת הגלישה מושהה"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"השימוש בנתונים מושהה"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏אין חיבור ל-Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"בהירות"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"היפוך צבעים"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"תיקון צבע"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"הגדרות נוספות"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"הגדרות המשתמש"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"בוצע"</string>
@@ -472,8 +455,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"יש לבטל את הנעילה כדי להשתמש"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"הייתה בעיה בקבלת הכרטיסים שלך. כדאי לנסות שוב מאוחר יותר"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"הגדרות מסך הנעילה"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"‏סריקת קוד QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"‏צריך ללחוץ כאן כדי לסרוק קוד QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -493,6 +478,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"‏כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעלה"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"פקדים של הודעות הפעלה"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"פועל – מבוסס על זיהוי פנים"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"בעזרת פקדים של התראות הפעלה, אפשר להגדיר רמת חשיבות מ-0 עד 5 להתראות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצגה בראש רשימת ההתראות \n- לאפשר הפרעה במסך מלא \n- תמיד לאפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מניעת הפרעה במסך מלא \n- תמיד לאפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מניעת הפרעה במסך מלא \n- אף פעם לא לאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מניעת הפרעה במסך מלא \n- אף פעם לא לאפשר הצצה \n- אף פעם לא לאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מניעת הפרעה במסך מלא \n- אף פעם לא לאפשר הצצה \n- אף פעם לא לאפשר קול ורטט \n- הסתרה ממסך הנעילה ומשורת הסטטוס \n- הצגה בתחתית רשימת ההתראות \n\n"<b>"רמה 0"</b>" \n- חסימת כל ההתראות מהאפליקציה"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"סיום"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"אישור"</string>
@@ -819,7 +805,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> של <xliff:g id="ARTIST_NAME">%2$s</xliff:g> מ-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> מ-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ביטול"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"צריך להתקרב כדי להפעיל מוזיקה במכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"ההפעלה הועברה למכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"לא פעיל, יש לבדוק את האפליקציה"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"לא נמצא"</string>
@@ -907,4 +894,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"בחירת משתמש"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"אפליקציות שפועלות ברקע"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"עצירה"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index 28548bc..49fb4b6 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"פועל"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"לא זמין"</item>
     <item msgid="9103697205127645916">"כבוי"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 6fbcbb3..f219de3 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ロック画面"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"仕事用プロファイルのロック画面"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"閉じる"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-FiをOFFにしました。"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-FiをONにしました。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"機内モードをOFFにしました。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"機内モードをONにしました。"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"サイレント"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"アラームのみ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"サイレント モード。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"サイレント モードを無効にしました。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"サイレント モードを有効にしました。"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"BluetoothがONです。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"BluetoothをOFFにしました。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"BluetoothをONにしました。"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"現在地送信機能をOFFにしました。"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"現在地送信機能をONにしました。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"アラームは<xliff:g id="TIME">%s</xliff:g>に設定されています。"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"長くします。"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"短くします。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ライトをOFFにしました。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ライトをONにしました。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"色反転をOFFにしました。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"色反転をONにしました。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"モバイルアクセスポイントをOFFにしました。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"モバイルアクセスポイントをONにしました。"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"画面のキャストが停止しました。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"データセーバーが OFF になりました。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"データセーバーが ON になりました。"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ディスプレイの明るさ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"モバイルデータが一時停止"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"データの一時停止"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi 未接続"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"画面の明るさ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色反転"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色補正"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"詳細設定"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ユーザー設定"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"完了"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ロックを解除して使用"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"カードの取得中に問題が発生しました。しばらくしてからもう一度お試しください"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ロック画面の設定"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR のスキャン"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"クリックすると、QR コードをスキャンします"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR コード"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"タップしてスキャンします"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"次回のアラーム(<xliff:g id="WHEN">%1$s</xliff:g>)は鳴りません"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"タブレットでキーボードに接続するには、最初にBluetoothをONにする必要があります。"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ONにする"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"電源通知管理"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ON - 顔ベース"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"電源通知管理では、アプリの通知の重要度をレベル 0~5 で設定できます。\n\n"<b>"レベル 5"</b>" \n- 通知リストの一番上に表示する \n- 全画面表示を許可する \n- 常にポップアップする \n\n"<b>"レベル 4"</b>" \n- 全画面表示しない \n- 常にポップアップする \n\n"<b>"レベル 3"</b>" \n- 全画面表示しない \n- ポップアップしない \n\n"<b>"レベル 2"</b>" \n- 全画面表示しない \n- ポップアップしない \n- 音やバイブレーションを使用しない \n\n"<b>"レベル 1"</b>" \n- 全画面表示しない \n- ポップアップしない \n- 音やバイブレーションを使用しない \n- ロック画面やステータスバーに表示しない \n- 通知リストの一番下に表示する \n\n"<b>"レベル 0"</b>" \n- アプリからのすべての通知をブロックする"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完了"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"適用"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g>(アーティスト名: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>)を <xliff:g id="APP_LABEL">%3$s</xliff:g> で再生"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> を <xliff:g id="APP_LABEL">%2$s</xliff:g> で再生"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"元に戻す"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生するにはもっと近づけてください"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生しています"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"見つかりませんでした"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ユーザーの選択"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"バックグラウンドで実行中のアプリ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 7fc9c08c..55cbe8b 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"OFF"</item>
     <item msgid="2075645297847971154">"ON"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"使用不可"</item>
+    <item msgid="1909756493418256167">"OFF"</item>
+    <item msgid="4531508423703413340">"ON"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"使用不可"</item>
     <item msgid="9103697205127645916">"OFF"</item>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 08adb0f..b932c45 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ეკრანის დაბლოკვა."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"სამსახურის ჩაკეტილი ეკრანი"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"დახურვა"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi გამორთულია."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi ჩართულია."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"თვითმფრინავის რეჟიმი გამოირთო."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"თვითმფრინავის რეჟიმი ჩაირთო."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"სრული სიჩუმე"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"მხოლოდ მაღვიძარები"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"არ შემაწუხოთ."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"„არ შემაწუხოთ“ რეჟიმი გამორთულია."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"„არ შემაწუხოთ“ რეჟიმი ჩართულია."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth ჩართულია."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth გამოირთო."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth ჩაირთო."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"მდებარეობის შეტყობინება გამოირთო."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"მდებარეობის შეტყობინება ჩაირთო."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"მაღვიძარა დაყენებულია: <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"მეტი დრო."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"ნაკლები დრო."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ფანარი გამოირთო."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ფანარი ჩაირთო."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"ფერის ინვერსია გამოირთო."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ფერის ინვერსია ჩაირთო."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"მობილური ქსელის წერტილი გამოირთო."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"მობილური ქსელის წერტილი ჩაირთო."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ეკრანის გადაცემა შეჩერებულია."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"მონაცემთა დამზოგველი გამორთულია."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"მონაცემთა დამზოგველი ჩართულია."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ეკრანის სიკაშკაშე"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"მობილური ინტერნეტი დაპაუზებულია"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"მონაცემები შეჩერებულია"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi არ არის დაკავშირებული"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"განათება"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ფერთა ინვერსია"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ფერთა კორექცია"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"დამატებითი პარამეტრები"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"მომხმარებლის პარამეტრები"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"დასრულდა"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"გამოსაყენებლად განბლოკვა"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"თქვენი ბარათების მიღებისას პრობლემა წარმოიშვა. ცადეთ ხელახლა მოგვიანებით"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ჩაკეტილი ეკრანის პარამეტრები"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-ის სკანირება"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"დააწკაპუნეთ QR კოდის სკანირებისთვის"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR კოდი"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"შეეხეთ დასასკანირებლად"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ვერ გაიგონებთ მომდევნო მაღვიძარას <xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"კლავიატურის ტაბლეტთან დასაკავშირებლად, ჯერ უნდა ჩართოთ Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ჩართვა"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"შეტყობინებების მართვის საშუალებები"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ჩართული — სახის მიხედვით"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"შეტყობინებების მართვის საშუალებების მეშვეობით, შეგიძლიათ განსაზღვროთ აპის შეტყობინებების მნიშვნელობის დონე 0-დან 5-მდე დიაპაზონში. \n\n"<b>"დონე 5"</b>" \n— შეტყობინებათა სიის თავში ჩვენება \n— სრულეკრანიანი რეჟიმის შეფერხების დაშვება \n— ეკრანზე ყოველთვის გამოჩენა \n\n"<b>"დონე 4"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე ყოველთვის გამოჩენა \n\n"<b>"დონე 3"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n\n"<b>"დონე 2"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n— ხმისა და ვიბრაციის აღკვეთა \n\n"<b>"დონე 1"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n— ხმისა და ვიბრაციის აღკვეთა \n— ჩაკეტილი ეკრანიდან და სტატუსის ზოლიდან დამალვა \n— შეტყობინებათა სიის ბოლოში ჩვენება \n\n"<b>"დონე 0"</b>" \n— აპის ყველა შეტყობინების დაბლოკვა"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"მზადაა"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"მისადაგება"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="APP_LABEL">%3$s</xliff:g>-დან"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g>-დან"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"მოქმედების გაუქმება"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"მიიტანეტ უფრო ახლოს, რომ დაუკრათ <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"მიმდინარეობს დაკვრა <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"არააქტიურია, გადაამოწმეთ აპი"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ვერ მოიძებნა"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"მომხმარებლის არჩევა"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ფონურად მომუშავე აპები"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"შეწყვეტა"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 40dfd39..34caeff 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ჩართვა"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"მიუწვდომელია"</item>
     <item msgid="9103697205127645916">"გამორთულია"</item>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index ffdde5b..1ac6c7e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Бекіту экраны."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Әрекетті құлыптау экраны"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Жабу"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi өшірілді."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi қосылды."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Ұшақ режимі өшірілді."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Ұшақ режимі қосылды."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"үнсіз"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"оятқыштар ғана"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Мазаламау."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Мазаламау режимі өшірілді."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Мазаламау режимі қосылды."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth қосулы."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth өшірілді."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth қосылды."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Геодерек жіберу функциясы өшірілді."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Геодерек жіберу функциясы қосылды."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Дабыл <xliff:g id="TIME">%s</xliff:g> уақытына реттелген."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Көбірек уақыт."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Азырақ уақыт."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Қол шам өшірілді."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Қол шам қосылды."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Түстердің инверсиясы өшірілді."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Түстердің инверсиясы қосылды."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобильді хотспот өшірілді."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобильді хотспот қосылды."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Экранды трансляциялау тоқтатылды."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Трафикті үнемдеу режимі өшірілді."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Трафикті үнемдеу режимі қосылды."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Дисплей жарықтығы"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобильдік деректер кідіртілді"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Деректер кідіртілді"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi желісіне жалғанбаған"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарықтығы"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түс инверсиясы"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түсті түзету"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Қосымша параметрлер"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пайдаланушы параметрлері"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Дайын"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Пайдалану үшін құлыпты ашу"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Карталарыңыз алынбады, кейінірек қайталап көріңіз."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экран құлпының параметрлері"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR кодын сканерлеу"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодын сканерлеу үшін басыңыз."</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Сканерлеу үшін түртіңіз."</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Келесі <xliff:g id="WHEN">%1$s</xliff:g> дабылыңызды есітпейсіз"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Пернетақтаны планшетке қосу үшін алдымен Bluetooth функциясын қосу керек."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Қосу"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Қуат хабарландыруының басқару элементтері"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Қосулы – бет негізінде"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Қуат хабарландыруының басқару элементтерімен қолданбаның хабарландырулары үшін 0-ден бастап 5-ке дейін маңыздылық деңгейін орнатуға болады. \n\n"<b>"5-деңгей"</b>" \n- Хабарландыру тізімінің ең басында көрсету \n- Толық экранға ашылуын рұқсат ету \n- Әрдайым қалқымалы хабарландыру түрінде көрсету \n\n"<b>"4-деңгей"</b>" \n- Толық экранға шығармау \n- Әрдайым қалқымалы хабарландыру түрінде көрсету \n\n"<b>"3-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n\n"<b>"2-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n- Ешқашан дыбыс және діріл шығармау \n\n"<b>"1-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n- Ешқашан дыбыс немесе діріл шығармау \n- Құлыпталған экраннан және күйін көрсету жолағынан жасыру \n- Хабарландыру тізімінің ең астында көрсету \n\n"<b>"0-деңгей"</b>" \n- Қолданбадағы барлық хабарландыруларға тыйым салу"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Дайын"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Қолдану"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> қолданбасында <xliff:g id="ARTIST_NAME">%2$s</xliff:g> орындайтын \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әнін ойнату"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> қолданбасында \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әнін ойнату"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Қайтару"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында музыка ойнату үшін оған жақындаңыз."</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында ойнатылуда."</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Табылмады"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Пайдаланушыны таңдау"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Фондық режимде жұмыс істеп тұрған қолданбалар"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Тоқтату"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index 3e9aefa..616ad53 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Қосулы"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Қолжетімсіз"</item>
     <item msgid="9103697205127645916">"Өшірулі"</item>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 02f7550..bc69da9 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ចាក់​សោ​អេក្រង់។"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"អេក្រង់​ចាក់​សោ​លក្ខណៈ​ការងារ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"បិទ"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"បាន​បិទ​វ៉ាយហ្វាយ។"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"បាន​បើក​វ៉ាយហ្វាយ។"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"បាន​បិទ​របៀប​ជិះ​យន្តហោះ។"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"បាន​បើក​របៀប​ជិះ​យន្តហោះ។"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"បិទសំឡេង​ទាំងស្រុង"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"សំឡេងរោទ៍​ប៉ុណ្ណោះ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"កុំ​រំខាន។"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"បានបិទមុខងារកុំរំខាន។"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"បានបើកមុខងារកុំរំខាន។"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ប៊្លូធូស"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"បើក​ប៊្លូធូស។"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"បាន​បិទ​ប៊្លូធូស។"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"បាន​បើក​ប៊្លូធូស។"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"បាន​បិទ​ការ​រាយការណ៍​ទីតាំង។"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"បាន​បើក​ការ​រាយការណ៍​ទីតាំង។"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"កំណត់​សំឡេង​រោទ៍​សម្រាប់ <xliff:g id="TIME">%s</xliff:g> ។"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ច្រើនជាង"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"តិច​ជាង"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"បាន​បិទ​ពិល។"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"បាន​បើក​ពិល។"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"បាន​បិទ​ការ​បញ្ច្រាស​ពណ៌។"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"បាន​បើក​ការ​បញ្ច្រាស​ពណ៌។"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"បាន​បិទ​ហតស្ប៉ត​ចល័ត។"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"បាន​បើក​ហតស្ប៉ត​ចល័ត។"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"បាន​បញ្ឈប់​ការ​ចាត់​ថ្នាក់​អេក្រង់។"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"បានបិទកម្មវិធីសន្សំសំចៃទិន្នន័យ"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"បានបើកកម្មវិធីសន្សំសំចៃទិន្នន័យ"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ពន្លឺ​ការ​បង្ហាញ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ទិន្នន័យទូរសព្ទចល័តបានផ្អាក"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ទិន្នន័យត្រូវបានផ្អាក"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"មិនមាន​ការតភ្ជាប់ Wi-Fi ទេ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ពន្លឺ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ការបញ្ច្រាស​ពណ៌"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ការ​កែតម្រូវ​ពណ៌"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"ការ​កំណត់​ច្រើន​ទៀត"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ការកំណត់អ្នកប្រើប្រាស់"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"រួចរាល់"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ដោះសោដើម្បីប្រើប្រាស់"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"មានបញ្ហា​ក្នុងការទាញយក​កាត​របស់អ្នក សូម​ព្យាយាមម្ដងទៀត​នៅពេលក្រោយ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ការកំណត់អេក្រង់ចាក់សោ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"ស្កេនកូដ QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"ចុចដើម្បីស្កេនកូដ QR"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"​កូដ QR"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"ចុច​ដើម្បីស្កេន"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ប្រវត្តិរូបការងារ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"អ្នកនឹងមិនលឺម៉ោងរោទ៍ <xliff:g id="WHEN">%1$s</xliff:g> បន្ទាប់របស់អ្នកទេ"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ដើម្បីភ្ជាប់ក្តារចុចរបស់អ្នកជាមួយនឹងថេប្លេតរបស់អ្នក អ្នកត្រូវតែបើកប៊្លូធូសជាមុនសិន។"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"បើក"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"អង្គគ្រប់គ្រងការជូនដំណឹងថាមពល"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"បើក - ផ្អែកលើមុខ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ជាមួយអង្គគ្រប់គ្រងការជូនដំណឹងថាមពល អ្នកអាចកំណត់កម្រិតសំខាន់ពី 0 ទៅ 5 សម្រាប់ការជូនដំណឹងរបស់កម្មវិធី។ \n\n"<b>"កម្រិត 5"</b>" \n- បង្ហាញនៅផ្នែកខាងលើបញ្ជីជូនដំណឹង \n- អនុញ្ញាតការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 4"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 3"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 2"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n- មិនបន្លឺសំឡេង ឬញ័រ \n\n"<b>"កម្រិត 1"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n- មិនបន្លឺសំឡេង ឬញ័រ \n- លាក់ពីអេក្រង់ចាក់សោ និងរបារស្ថានភាព \n- បង្ហាញនៅផ្នែកខាងក្រោមបញ្ជីជូនដំណឹង \n\n"<b>"កម្រិត 0"</b>" \n- រារាំងការជូនដំណឹងទាំងអស់ពីកម្មវិធី"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"រួចរាល់"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ប្រើ"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ច្រៀងដោយ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ពី <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ពី <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ត្រឡប់វិញ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"រំកិលឱ្យកាន់តែជិត ដើម្បីចាក់នៅលើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"កំពុង​ចាក់​​នៅ​លើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើល​កម្មវិធី"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"រកមិន​ឃើញទេ"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ជ្រើសរើសអ្នកប្រើប្រាស់"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"កម្មវិធីដែលកំពុងដំណើរការ​នៅផ្ទៃខាងក្រោយ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ឈប់"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index 32a6e22..b1a1a8f 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"បើក"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"មិនមានទេ"</item>
     <item msgid="9103697205127645916">"បិទ"</item>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index b29b0bb..c388d82 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್‌ ಪರದೆ."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ವೈಫೈ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ವೈಫೈ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ಅಲಾರಮ್‌ಗಳು ಮಾತ್ರ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ಅಡಚಣೆ ಮಾಡಬೇಡ."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"ಅಡಚಣೆ ಮಾಡಬೇಡ ಆಯ್ಕೆಯನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"ಅಡಚಣೆ ಮಾಡಬೇಡ ಆಯ್ಕೆಯನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ಬ್ಲೂಟೂತ್."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ಬ್ಲೂಟೂತ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ಸ್ಥಳ ವರದಿಮಾಡುವಿಕೆಯನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"ಸ್ಥಳ ವರದಿಮಾಡುವಿಕೆಯನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> ಗಂಟೆಗೆ ಅಲಾರಮ್ ಹೊಂದಿಸಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ಹೆಚ್ಚು ಸಮಯ."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"ಕಡಿಮೆ ಸಮಯ."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ಫ್ಲ್ಯಾಶ್‌ಲೈಟ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ಫ್ಲ್ಯಾಶ್‌ಲೈಟ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"ಬಣ್ಣ ತಿರುಗಿಸುವಿಕೆಯನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ಬಣ್ಣ ತಿರುಗಿಸುವಿಕೆಯನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ಮೊಬೈಲ್ ಹಾಟ್‌ಸ್ಪಾಟ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ಮೊಬೈಲ್ ಹಾಟ್‌ಸ್ಪಾಟ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ಸ್ಕ್ರೀನ್ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ಡೇಟಾ ಸೇವರ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ಹೊಳಪನ್ನು ಪ್ರದರ್ಶಿಸಿ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ಮೊಬೈಲ್ ಡೇಟಾವನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ಡೇಟಾ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ಕಲರ್ ಇನ್‍ವರ್ಶನ್"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್‌ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ನಿಮ್ಮ ಮುಂದಿನ <xliff:g id="WHEN">%1$s</xliff:g> ಅಲಾರಮ್ ಅನ್ನು ನೀವು ಆಲಿಸುವುದಿಲ್ಲ"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಟ್ಯಾಬ್ಲೆಟ್‌ಗೆ ಸಂಪರ್ಕಿಸಲು, ನೀವು ಮೊದಲು ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ಆನ್‌ ಮಾಡಿ"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ಪವರ್ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ಆನ್ ಆಗಿದೆ - ಮುಖ-ಆಧಾರಿತ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ಪವರ್ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳ ಮೂಲಕ, ನೀವು ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಅಧಿಸೂಚನೆಗಳನ್ನು 0 ರಿಂದ 5 ರವರೆಗಿನ ಹಂತಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಬಹುದು. \n\n"<b>"ಹಂತ 5"</b>" \n- ಮೇಲಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ಅನುಮತಿಸಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ \n\n"<b>"ಹಂತ 4"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ\n\n"<b>"ಹಂತ 3"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n\n"<b>"ಹಂತ 2"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n\n"<b>"ಹಂತ 1"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n- ಸ್ಥಿತಿ ಪಟ್ಟಿ ಮತ್ತು ಲಾಕ್ ಪರದೆಯಿಂದ ಮರೆಮಾಡಿ \n- ಕೆಳಗಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n\n"<b>"ಹಂತ 0"</b>" \n- ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ಅನ್ವಯಿಸಿ"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ಅವರ <xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%3$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%2$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ರದ್ದುಗೊಳಿಸಿ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಅದರ ಹತ್ತಿರಕ್ಕೆ ಸರಿಯಿರಿ"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತಿದೆ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ಕಂಡುಬಂದಿಲ್ಲ"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ಬಳಕೆದಾರ ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿರುವ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ನಿಲ್ಲಿಸಿ"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index c13e198..e5bf6ef 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ಆನ್ ಮಾಡಿ"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ಲಭ್ಯವಿಲ್ಲ"</item>
     <item msgid="9103697205127645916">"ಆಫ್ ಮಾಡಿ"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b5f2ad3..16b5447 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"화면을 잠급니다."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"업무용 잠금 화면"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"닫기"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi가 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi를 사용합니다."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"비행기 모드가 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"비행기 모드를 사용합니다."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"모두 음소거"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"알람만 허용"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"방해 금지 모드"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"방해 금지 모드가 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"방해 금지 모드가 사용 설정되었습니다."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"블루투스"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"블루투스: 사용"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"블루투스가 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"블루투스를 사용합니다."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"위치 정보 전송이 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"위치 정보 전송을 사용합니다."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"알람이 <xliff:g id="TIME">%s</xliff:g>(으)로 설정되었습니다."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"시간 늘리기"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"시간 줄이기"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"손전등이 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"손전등을 사용합니다."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"색상 반전이 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"색상 반전을 사용합니다."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"모바일 핫스팟이 사용 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"모바일 핫스팟을 사용합니다."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"화면 전송이 중지되었습니다."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"데이터 절약 모드를 사용 중지했습니다."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"데이터 절약 모드를 사용 설정했습니다."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"디스플레이 밝기"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"모바일 데이터가 일시중지됨"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"데이터 사용 중지됨"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi가 연결되지 않음"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"밝기"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"색상 반전"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"색상 보정"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"설정 더보기"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"사용자 설정"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"완료"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"잠금 해제하여 사용"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"카드를 가져오는 중에 문제가 발생했습니다. 나중에 다시 시도해 보세요."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"잠금 화면 설정"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR 스캔"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR 코드를 스캔하려면 클릭하세요."</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>에 다음 알람을 들을 수 없습니다."</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"키보드를 태블릿에 연결하려면 먼저 블루투스를 켜야 합니다."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"사용"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"전원 알림 컨트롤"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"켜짐 - 얼굴 기준"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"전원 알림 컨트롤을 사용하면 앱 알림 관련 중요도를 0부터 5까지로 설정할 수 있습니다. \n\n"<b>"레벨 5"</b>" \n- 알림 목록 상단에 표시 \n- 전체 화면일 경우 알림 표시 허용 \n- 항상 엿보기 표시 \n\n"<b>"레벨 4"</b>" \n- 전체 화면에 알림 표시 금지 \n- 항상 엿보기 표시 \n\n"<b>"레벨 3"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n\n"<b>"레벨 2"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n- 소리나 진동으로 알리지 않음 \n\n"<b>"레벨 1"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n- 소리나 진동으로 알리지 않음 \n- 잠금 화면 및 상태 표시줄에서 숨김 \n- 알림 목록 하단에 표시 \n\n"<b>"레벨 0"</b>" \n- 앱의 모든 알림 차단"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"완료"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"적용"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>에서 <xliff:g id="ARTIST_NAME">%2$s</xliff:g>의 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>에서 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"실행취소"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생하려면 기기를 더 가까이로 옮기세요."</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생 중"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"찾을 수 없음"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"사용자 선택"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"백그라운드에서 실행 중인 앱"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"중지"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index 28d45cf..595b12c 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"켜짐"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"이용 불가"</item>
     <item msgid="9103697205127645916">"꺼짐"</item>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 2d2f1cf..31c8522 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Кулпуланган экран."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Жумуштун кулпуланган экраны"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Жабуу"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi күйгүзүлдү."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Учак режими өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Учак режими күйгүзүлдү."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"тымтырс"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ойготкуч гана"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Тынчымды алба."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"Тынчымды алба\" режими өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"Тынчымды алба\" режими күйгүзүлдү."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth күйүк."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth күйгүзүлдү."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Жайгашкан жерди кабарлоо өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Жайгашкан жерди кабарлоо күйгүзүлдү."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Ойготкуч кийинкиге коюлган: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Көбүрөөк убакыт."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Азыраак убакыт."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Колчырак өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Колчырак күйгүзүлдү."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Түстү өзгөртүү аракети өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Түстү өзгөртүү аракети күйгүзүлдү."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобилдик байланыш түйүнү өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобилдик байланыш түйүнү күйгүзүлдү."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Тышкы экранга чыгаруу аракети токтотулду."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Трафикти үнөмдөө режими өчүрүлдү."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Трафикти үнөмдөө режими күйгүзүлдү."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Жарыктыгын көрсөтүү"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобилдик Интернет кызматы тындырылды"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Дайындар тындырылды"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi туташкан жок"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарыктыгы"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түстү инверсиялоо"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түсүн тууралоо"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Дагы жөндөөлөр"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Колдонуучунун жөндөөлөрү"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Бүттү"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Колдонуу үчүн кулпусун ачыңыз"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Кыйытмаларды алууда ката кетти. Бир аздан кийин кайталап көрүңүз."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Кулпуланган экран жөндөөлөрү"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR кодун скандоо"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодун скандоо үчүн чыкылдатыңыз"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Скандоо үчүн таптап коюңуз"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> боло турган кийинки эскертмени укпайсыз"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Баскычтобуңузду планшетиңизге туташтыруу үчүн, адегенде Bluetooth\'ту күйгүзүшүңүз керек."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Күйгүзүү"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Эскертмелерди башкаруу каражаттары"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Күйүк – Жүздүн негизинде"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрүнөт \n- Билдирмелер толук экранда көрүнөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрүнөт \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык билдирмелер бөгөттөлөт"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Колдонуу"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын (аткаруучу: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> колдонмосунан ойнотуу"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын <xliff:g id="APP_LABEL">%2$s</xliff:g> колдонмосунан ойнотуу"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Кайтаруу"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүндө ойнотуу үчүн жакыныраак жылдырыңыз"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> аркылуу ойнотулууда"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Табылган жок"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Колдонуучуну тандоо"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Фондо иштеп жаткан колдонмолор"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Токтотуу"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 139d784..3bcbf53 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Күйүк"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Жеткиликсиз"</item>
     <item msgid="9103697205127645916">"Өчүк"</item>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 6903250..f3884bc 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ລັອກ​ໜ້າ​ຈໍ."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ໜ້າຈໍລັອກວຽກ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ປິດ"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ປິດ Wi-Fi ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ເປີດ Wi-Fi ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ປິດ​ໂໝດ​ຢູ່​ໃນ​ຍົນ​ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ເປີດ​ໂໝດ​ຢູ່​ໃນ​ຍົນ​ແລ້ວ."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ງຽບທັງໝົດ"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ໂມງປຸກເທົ່ານັ້ນ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ຫ້າມລົບກວນ."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"ປິດໂໝດຫ້າມລົບກວນແລ້ວ."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"ຢ່າລົບກວນເປີດແລ້ວ."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth ເປີດ."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ປິດ Bluetooth ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ເປີດ Bluetooth ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ປິດ​ການ​ລາຍ​ງານ​ສະ​ຖານ​ທີ່​ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"ເປີດ​ການ​ລາຍ​ງານ​ສະ​ຖານ​ທີ່​ແລ້ວ."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"ຕັ້ງໂມງປຸກ <xliff:g id="TIME">%s</xliff:g> ແລ້ວ."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"​ເພີ່ມ​ເວ​ລາ."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"ຫຼຸດ​ເວ​ລາ."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ປິດ​ໄຟ​ສາຍ​ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"​ເປີດ​ໄຟ​ສາຍ​ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"​ປິດ​ການ​ສະ​ລັບ​ສີ."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ເປີດ​ການ​ສຳ​ລັບ​ສີ."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ປິດ​ຮັອດ​ສະ​ປອດ​ເຄື່ອນ​ທີ່​ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ເປີດ​ຮັອດ​ສະ​ປອດ​ເຄື່ອນ​ທີ່​ແລ້ວ."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ຢຸດ​ການ​ສົ່ງ​​ພາບ​ໜ້າ​ຈໍ​ແລ້ວ."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ປິດຕົວປະຢັດອິນເຕີເນັດແລ້ວ."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ເປີດຕົວປະຢັດອິນເຕີເນັດແລ້ວ."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"​ຄວາມ​ແຈ້ງ​​ຂອງ​ຈໍ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ຢຸດການໃຊ້ອິນເຕີເນັດມືຖືຊົ່ວຄາວແລ້ວ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ຂໍ້​ມູນ​ຢຸດ​ຊົ່ວ​ຄາວແລ້ວ"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ບໍ່ໄດ້ເຊື່ອມຕໍ່ Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ຄວາມແຈ້ງ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ການປີ້ນສີ"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ການແກ້ໄຂສີ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"​ການ​ຕັ້ງ​ຄ່າ​ເພີ່ມ​ເຕີມ"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ຕັ້ງຄ່າຜູ້ໃຊ້"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ແລ້ວໆ"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ປົດລັອກເພື່ອໃຊ້"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"ເກີດບັນຫາໃນການໂຫຼດບັດຂອງທ່ານ, ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ການຕັ້ງຄ່າໜ້າຈໍລັອກ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"ສະແກນ QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"ຄລິກເພື່ອສະແກນລະຫັດ QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"ແຕະເພື່ອສະແກນ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດເຮືອ​ບິນ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ທ່ານ​ຈະ​ບໍ່​ໄດ້​ຍິນ​ສຽງ​ໂມງ​ປ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ເພື່ອ​ເຊື່ອມ​ຕໍ່​ແປ້ນ​ພິມ​ຂອງ​ທ່ານ​ກັບ​ແທັບ​ເລັດ​ຂອງ​ທ່ານ, ກ່ອນ​ອື່ນ​ໝົດ​ທ່ານ​ຕ້ອງ​ເປີດ Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ເປີດ​"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ການຄວບຄຸມການແຈ້ງເຕືອນ"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ເປີດ - ອ້າງອີງໃບໜ້າ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ດ້ວຍການຄວບຄຸມການແຈ້ງເຕືອນ, ທ່ານສາມາດຕັ້ງລະດັບຄວາມສຳຄັນຈາກ 0 ຮອດ 5 ໃຫ້ກັບການແຈ້ງເຕືອນແອັບໃດໜຶ່ງໄດ້. \n\n"<b>"ລະດັບ 5"</b>" \n- ສະແດງຢູ່ເທິງສຸດຂອງລາຍການແຈ້ງເຕືອນ \n- ອະນຸຍາດໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 4"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 3"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n\n"<b>"ລະດັບ 2"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n\n"<b>"ລະດັບ 1"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n- ເຊື່ອງຈາກໜ້າຈໍລັອກ ແລະ ແຖບສະຖານະ \n- ສະແດງຢູ່ລຸ່ມສຸດຂອງລາຍການແຈ້ງເຕືອນ \n\n"<b>"ລະດັບ 0"</b>" \n- ປິດກັ້ນການແຈ້ງເຕືອນທັງໝົດຈາກແອັບ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ແລ້ວໆ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ນຳໃຊ້"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ຫຼິ້ນ <xliff:g id="SONG_NAME">%1$s</xliff:g> ໂດຍ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ຈາກ <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ຫຼິ້ນ <xliff:g id="SONG_NAME">%1$s</xliff:g> ຈາກ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ຍົກເລີກ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"ຍ້າຍໄປໃກ້ຂຶ້ນເພື່ອຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"ກຳລັງຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ບໍ່ພົບ"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ເລືອກຜູ້ໃຊ້"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ແອັບທີ່ກຳລັງເອີ້ນໃຊ້ໃນພື້ນຫຼັງ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ຢຸດ"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index 74c0f24..0cb8afd 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ເປີດ"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
     <item msgid="9103697205127645916">"ປິດ"</item>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index b8c8a4a..f2918d5 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Užrakinimo ekranas."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Darbo profilio užrakinimo ekranas"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Uždaryti"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"„Wi-Fi“ ryšys išjungtas."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"„Wi-Fi“ ryšys įjungtas."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Lėktuvo režimas išjungtas."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Lėktuvo režimas įjungtas."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"visiška tyla"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"tik įspėjimai"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Netrukdymo režimas."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Netrukdymo režimas išjungtas."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Netrukdymo režimas įjungtas."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"„Bluetooth“."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"„Bluetooth“ įjungtas."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"„Bluetooth“ išjungtas."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"„Bluetooth“ įjungtas."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Vietovių ataskaitų teikimas išjungtas."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Vietovių ataskaitų teikimas įjungtas."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Signalas nustatytas <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Daugiau laiko."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mažiau laiko."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Blykstė išjungta."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Blykstė įjungta."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Spalvų inversija išjungta."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Spalvų inversija įjungta."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiliojo ryšio viešosios interneto prieigos taškas išjungtas."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiliojo ryšio viešosios interneto prieigos taškas įjungtas."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekrano perdavimas sustabdytas."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Duomenų taupymo priemonė išjungta."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Duomenų taupymo priemonė įjungta."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ekrano šviesumas"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobiliojo ryšio duomenys pristabdyti"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Duomenys pristabdyti"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"„Wi-Fi“ neprijungtas"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Šviesumas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Spalvų inversija"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Spalvų taisymas"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Daugiau nustatymų"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Naudotojo nustatymai"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Atlikta"</string>
@@ -472,8 +455,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Atrakinti, kad būtų galima naudoti"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Gaunant korteles kilo problema, bandykite dar kartą vėliau"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Užrakinimo ekrano nustatymai"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Nuskaityti QR kodą"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Spustelėkite, kad nuskaitytumėte QR kodą"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Palieskite, kad nuskaitytumėte"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Negirdėsite kito signalo <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -493,6 +477,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Kad galėtumėte prijungti klaviatūrą prie planšetinio kompiuterio, pirmiausia turite įjungti „Bluetooth“."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Įjungti"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Galingi pranešimų valdikliai"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Įjungta – pagal veidą"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Naudodami pranešimų valdiklius galite nustatyti programos pranešimų svarbos lygį nuo 0 iki 5. \n\n"<b>"5 lygis"</b>" \n– Rodyti pranešimų sąrašo viršuje \n– Leisti pertraukti, kai veikia viso ekrano režimas \n– Visada rodyti pranešimus \n\n"<b>"4 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Visada rodyti pranešimus \n\n"<b>"3 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n\n"<b>"2 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n\n"<b>"1 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n– Slėpti užrakinimo ekrane ir būsenos juostoje \n– Rodyti pranešimų sąrašo apačioje \n\n"<b>"0 lygis"</b>" \n– Blokuoti visus programos pranešimus"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Atlikta"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Taikyti"</string>
@@ -819,7 +804,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Leisti <xliff:g id="ARTIST_NAME">%2$s</xliff:g> – „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%3$s</xliff:g>“"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Leisti „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%2$s</xliff:g>“"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Anuliuoti"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Prieikite arčiau, kad galėtumėte leisti įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Leidžiama įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nerasta"</string>
@@ -907,4 +893,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Naudotojo pasirinkimas"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Fone veikiančios programos"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Sustabdyti"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 08e0e6d..44a3fd5 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Išjungta"</item>
     <item msgid="2075645297847971154">"Įjungta"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nepasiekiama"</item>
+    <item msgid="1909756493418256167">"Išjungta"</item>
+    <item msgid="4531508423703413340">"Įjungta"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nepasiekiama"</item>
     <item msgid="9103697205127645916">"Išjungta"</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 48715802..86a5df8 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Bloķēšanas ekrāns."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Darba profila bloķēšanas ekrāns"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Aizvērt"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi ir izslēgts."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi ir ieslēgts."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Lidojuma režīms ir izslēgts."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Lidojuma režīms ir ieslēgts."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"pilnīgs klusums"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"tikai signāli"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Režīms “Netraucēt”."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Statuss Netraucēt tika izslēgts."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Režīms “Netraucēt” tika ieslēgts."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth savienojums ir ieslēgts."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth savienojums ir izslēgts."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth savienojums ir ieslēgts."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Ziņošana par atrašanās vietu ir izslēgta."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Ziņošana par atrašanās vietu ir ieslēgta."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Signāls ir iestatīts uz: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Vairāk laika."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mazāk laika."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Apgaismojums ir izslēgts."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Apgaismojums ir ieslēgts."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Krāsu inversija ir izslēgta."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Krāsu inversija ir ieslēgta."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilais tīklājs ir izslēgts."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilais tīklājs ir ieslēgts."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekrāna apraidīšana ir apturēta."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datu lietojuma samazinātājs ir izslēgts."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datu lietojuma samazinātājs ir ieslēgts."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ekrāna spilgtums"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobilo datu lietojums apturēts"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Datu lietojums ir apturēts"</string>
@@ -252,6 +234,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nav izveidots savienojums ar Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Spilgtums"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Krāsu inversija"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Krāsu korekcija"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Vairāk iestatījumu"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Lietotāja iestatījumi"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gatavs"</string>
@@ -469,8 +452,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lai izmantotu, atbloķējiet ekrānu"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ienesot jūsu kartes, radās problēma. Lūdzu, vēlāk mēģiniet vēlreiz."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Bloķēšanas ekrāna iestatījumi"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ātrās atbildes koda skeneris"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Noklikšķiniet, lai skenētu ātrās atbildes kodu."</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nākamais signāls (<xliff:g id="WHEN">%1$s</xliff:g>) netiks atskaņots."</string>
@@ -490,6 +475,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Lai pievienotu tastatūru planšetdatoram, vispirms ir jāieslēdz Bluetooth savienojums."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ieslēgt"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Barošanas paziņojumu vadīklas"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ieslēgta — ar sejas noteikšanu"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Izmantojot barošanas paziņojumu vadīklas, varat lietotnes paziņojumiem iestatīt svarīguma līmeni (no 0 līdz 5). \n\n"<b>"5. līmenis"</b>" \n- Tiek rādīts paziņojumu saraksta augšdaļā \n- Tiek atļauta pilnekrāna režīma pārtraukšana \n- Ieskats vienmēr atļauts \n\n"<b>"4. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats vienmēr atļauts \n\n"<b>"3. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n\n"<b>"2. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n- Nav atļautas skaņas un vibrosignāls \n\n"<b>"1. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n- Nav atļautas skaņas un vibrosignāls \n- Paziņojumi tiek paslēpti bloķēšanas ekrānā un statusa joslā \n- Paziņojumi tiek rādīti paziņojumu saraksta apakšdaļā \n\n"<b>"0. līmenis"</b>" \n- Visi lietotnes paziņojumi tiek bloķēti"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gatavs"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Lietot"</string>
@@ -813,7 +799,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Atskaņojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” (izpildītājs: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) no lietotnes <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Atskaņojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” no lietotnes <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Atsaukt"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Pārvietojiet savu ierīci tuvāk, lai atskaņotu mūziku ierīcē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Notiek atskaņošana ierīcē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Netika atrasta"</string>
@@ -901,4 +888,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Lietotāja atlase"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Lietotnes, kas darbojas fonā"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Apturēt"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index c5718f8..35264ae 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Izslēgts"</item>
     <item msgid="2075645297847971154">"Ieslēgts"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nav pieejama"</item>
+    <item msgid="1909756493418256167">"Izslēgta"</item>
+    <item msgid="4531508423703413340">"Ieslēgta"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nav pieejama"</item>
     <item msgid="9103697205127645916">"Izslēgta"</item>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index ae9f3c0..b21f183 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Заклучи екран."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Работен заклучен екран"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Затвори"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi е исклученo."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi е вклученo."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Авионскиот режим е исклучен."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Авионскиот режим е вклучен."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"целосна тишина"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"само аларми"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Не вознемирувај."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"„Не вознемирувај“ е исклучено."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"„Не вознемирувај“ е вклучено."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth е вклучен."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth е исклучен."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth е вклучен."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Известувањето за локација е исклучено."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Известувањето за локација е вклучено."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Аларм наместен за <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Повеќе време."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Помалку време."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Блицот е исклучен."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Блицот е вклучен."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Инверзијата на бои е исклучена."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Инверзијата на бои е вклучена."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобилната точка на пристап е исклучена."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобилната точка на пристап е вклучена."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Емитувањето на екранот запре."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Штедачот на интернет е исклучен."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Штедачот на интернет е вклучен."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Осветленост на екранот"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобилниот интернет е паузиран"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Податоците се паузирани"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi не е поврзано"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветленост"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија на боите"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција на боите"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Повеќе поставки"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Поставки на корисникот"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отклучете за да користите"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Имаше проблем при преземањето на картичките. Обидете се повторно подоцна"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Поставки за заклучен екран"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Скенирајте QR-код"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликнете за да скенирате QR-код"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-код"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Допрете за скенирање"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"За да ја поврзете тастатурата со таблетот, најпрво треба да вклучите Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Вклучи"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Контроли за известувањата за напојување"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вклучено - според лице"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Со контролите за известувањата за напојување, може да поставите ниво на важност од 0 до 5 за известувањата на која било апликација. \n\n"<b>"Ниво 5"</b>" \n- Прикажувај на врвот на списокот со известувања \n- Дозволи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 4"</b>" \n- Спречи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 3"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n\n"<b>"Ниво 2"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n\n"<b>"Ниво 1"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n- Сокриј од заклучен екран и статусна лента \n- Прикажувај на дното на списокот со известувања \n\n"<b>"Ниво 0"</b>" \n- Блокирај ги сите известувања од апликацијата"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Пуштете <xliff:g id="SONG_NAME">%1$s</xliff:g> од <xliff:g id="ARTIST_NAME">%2$s</xliff:g> на <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пуштете <xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Врати"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Приближете се за да пуштите на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Се репродуцира на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивна, провери апликација"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Не е најдено"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изберете корисник"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Апликации се извршуваат во заднина"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Крај"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index fa484ae..c2c6f5d 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Вклучена"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Недостапно"</item>
     <item msgid="9103697205127645916">"Исклучено"</item>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index ea54bd0..798ece0 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ലോക്ക് സ്‌ക്രീൻ."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ഔദ്യോഗിക ലോക്ക് സ്ക്രീൻ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"അവസാനിപ്പിക്കുക"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"വൈഫൈ ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"വൈഫൈ ഓണാക്കി."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ഫ്ലൈറ്റ് മോഡ് ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ഫ്ലൈറ്റ് മോഡ് ഓണാക്കി."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"പൂർണ്ണ നിശബ്‌ദത"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"അലാറങ്ങൾ മാത്രം"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ശല്യപ്പെടുത്തരുത്."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"ശല്യപ്പെടുത്തരുത് എന്നത് ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"ശല്യപ്പെടുത്തരുത് എന്നത് ഓണാക്കി."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ബ്ലൂടൂത്ത് ഓണാണ്."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ബ്ലൂടൂത്ത് ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ബ്ലൂടൂത്ത് ഓണാക്കി."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ലൊക്കേഷൻ റിപ്പോർട്ടുചെയ്യൽ ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"ലൊക്കേഷൻ റിപ്പോർട്ടുചെയ്യൽ ഓണാക്കി."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g>-ന് അലാറം സജ്ജീകരിച്ചു."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"കൂടുതൽ സമയം."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"സമയം കുറയ്‌ക്കുക."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ടോർച്ച് ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ടോർച്ച് ഓണാക്കി."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"വർണ്ണ വൈപരീത്യം ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"വർണ്ണ വൈപരീത്യം ഓണാക്കി."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"മൊബൈൽ ഹോട്ട്‌സ്‌പോട്ട് ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"മൊബൈൽ ഹോട്ട്‌സ്‌പോട്ട് ഓണാക്കി."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"സ്ക്രീൻ കാസ്‌റ്റുചെയ്യൽ നിർത്തി."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ഡാറ്റ സേവർ ഓഫാക്കി."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ഡാറ്റ സേവർ ഓണാക്കി."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ഡിസ്പ്ലേ തെളിച്ചം"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"മൊബൈൽ ഡാറ്റ തല്‍ക്കാലം നിര്‍ത്തിയിരിക്കുന്നു"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്‌തിട്ടില്ല"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"നിറം വിപരീതമാക്കൽ"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"നിറം ശരിയാക്കൽ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ഉപയോക്തൃ ക്രമീകരണം"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"നിങ്ങളുടെ കാർഡുകൾ ലഭ്യമാക്കുന്നതിൽ ഒരു പ്രശ്‌നമുണ്ടായി, പിന്നീട് വീണ്ടും ശ്രമിക്കുക"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ലോക്ക് സ്ക്രീൻ ക്രമീകരണം"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR സ്‌കാൻ ചെയ്യുക"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR കോഡ് സ്‌കാൻ ചെയ്യാൻ ക്ലിക്ക് ചെയ്യുക"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR കോഡ്"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"സ്‌കാൻ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-നുള്ള നിങ്ങളുടെ അടുത്ത അലാറം കേൾക്കില്ല"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"നിങ്ങളുടെ ടാബ്‌ലെറ്റുമായി കീബോർഡ് കണക്റ്റുചെയ്യുന്നതിന്, ആദ്യം Bluetooth ഓണാക്കേണ്ടതുണ്ട്."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ഓണാക്കുക"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"പവർ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ഓണാണ് - ഫേസ് ബേസ്‌ഡ്"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"പവർ അറിയിപ്പ് നിയന്ത്രണം ഉപയോഗിച്ച്, ഒരു ആപ്പിനായുള്ള അറിയിപ്പുകൾക്ക് 0 മുതൽ 5 വരെയുള്ള പ്രാധാന്യ ലെവലുകളിലൊന്ന് നിങ്ങൾക്ക് സജ്ജമാക്കാവുന്നതാണ്. \n\n"<b>"ലെവൽ 5"</b>" \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ മുകളിൽ കാണിക്കുക \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം അനുവദിക്കുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 4"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 3"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും സൃശ്യമാക്കരുത് \n\n"<b>"ലെവൽ 2"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n\n"<b>"ലെവൽ 1"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n- ലോക്ക് സ്ക്രീനിൽ നിന്നും സ്റ്റാറ്റസ് ബാറിൽ നിന്നും മറയ്ക്കുക \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ അടിയിൽ കാണിക്കുക \n\n"<b>"ലെവൽ 0"</b>" \n- ആപ്പിൽ നിന്നുള്ള എല്ലാ അറിയിപ്പുകളും ബ്ലോക്കുചെയ്യുക"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"പൂർത്തിയായി"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ബാധകമാക്കുക"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> എന്ന ആർട്ടിസ്റ്റിന്റെ <xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%3$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുക"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%2$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുക"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"പഴയപടിയാക്കുക"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യാൻ അടുത്തേക്ക് നീക്കുക"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"നിഷ്‌ക്രിയം, ആപ്പ് പരിശോധിക്കൂ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"കണ്ടെത്തിയില്ല"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ഉപയോക്താവിനെ തിരഞ്ഞെടുക്കൂ"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ആപ്പുകൾ പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുന്നു"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"നിർത്തുക"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 0cca763..c683c1b 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ഓണാണ്"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ലഭ്യമല്ല"</item>
     <item msgid="9103697205127645916">"ഓഫാണ്"</item>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index d4c924d..d283916 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Дэлгэц түгжих."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ажлын түгжигдсэн дэлгэц"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Хаах"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi унтраасан."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi асаасан."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Нислэгийн горимыг унтраасан."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Нислэгийн горимыг асаасан."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"бүх дууг хаах"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"зөвхөн сэрүүлэг"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Бүү саад бол."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Бүү саад бол горимыг унтраалаа."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Бүү саад бол горимыг асаалаа."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth идэвхтэй."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Блютүүтийг унтраасан."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Блютүүтийг асаасан."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Байршил мэдээлэлтийг унтраасан."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Байршил мэдээлэлтийг асаасан."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Сэрүүлгийг <xliff:g id="TIME">%s</xliff:g>-д тохируулсан."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Хугацаа нэмэх."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Хугацаа хасах."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Флаш гэрлийг унтраасан."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Флаш гэрлийг асаасан."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Өнгө хувиргалтыг унтраасан."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Өнгө хувиргалтыг асаасан."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобайл хотспотыг унтраасан."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобайл хотспотыг асаасан."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Дэлгэц дамжуулалт зогссон."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Дата хэмнэгчийг унтраасан."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Дата хэмнэгчийг асаасан."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Дэлгэцийн гэрэлтэлт"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобайл датаг түр зогсоосон"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Дата-г түр зогсоосон байна"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi-д холбогдоогүй байна"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Тодрол"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Өнгө урвуулах"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Өнгөний засвар"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Бусад тохиргоо"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Хэрэглэгчийн тохиргоо"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Дууссан"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ашиглахын тулд түгжээг тайлах"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Таны картыг авахад асуудал гарлаа. Дараа дахин оролдоно уу"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Түгжигдсэн дэлгэцийн тохиргоо"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-г скан хийх"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодыг скан хийхийн тулд товшино уу"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR код"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Скан хийхийн тулд товшино уу"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Компьютерийн гараа таблетад холбохын тулд эхлээд Bluetooth-г асаана уу."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Асаах"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Тэжээлийн мэдэгдлийн удирдлага"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Асаалттай - Царайнд суурилсан"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Тэжээлийн мэдэгдлийн удирдлагын тусламжтайгаар та апп-н мэдэгдэлд 0-5 хүртэлх ач холбогдлын түвшин тогтоох боломжтой. \n\n"<b>"5-р түвшин"</b>" \n- Мэдэгдлийн жагсаалтын хамгийн дээр харуулна \n- Бүтэн дэлгэцэд саад болно \n- Дэлгэцэд тогтмол гарч ирнэ \n\n"<b>"4-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд тогтмол гарч ирнэ \n\n"<b>"3-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд хэзээ ч гарч ирэхгүй \n\n"<b>"2-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд хэзээ ч гарч ирэхгүй \n- Дуу болон чичиргээ хэзээ ч гаргахгүй \n\n"<b>"1-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд хэзээ ч гарч ирэхгүй \n- Дуу болон чичиргээ хэзээ ч гаргахгүй \n- Түгжигдсэн дэлгэц болон статусын самбараас нууна \n- Мэдэгдлийн жагсаалтын доор харуулна \n\n"<b>"0-р түвшин"</b>" \n- Энэ апп-н бүх мэдэгдлийг блоклоно"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Болсон"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Ашиглах"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-н <xliff:g id="SONG_NAME">%1$s</xliff:g>-г <xliff:g id="APP_LABEL">%3$s</xliff:g> дээр тоглуулах"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g>-г <xliff:g id="APP_LABEL">%2$s</xliff:g> дээр тоглуулах"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Болих"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулахын тулд төхөөрөмжөө ойртуулна уу"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулж байна"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Олдсонгүй"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Хэрэглэгч сонгох"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Ард ажиллаж байгаа аппууд"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Зогсоох"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 41049d8..7e01fbd 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Асаалттай"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Боломжгүй"</item>
     <item msgid="9103697205127645916">"Унтраалттай"</item>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 5b41bed..933a0e4 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"लॉक स्क्रीन."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"कार्य लॉक स्क्रीन"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"बंद करा"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi बंद झाले."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi सुरू झाले."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"विमान मोड बंद केला."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"विमान मोड सुरू केला."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"संपूर्ण शांतता"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"फक्‍त अलार्म"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"व्यत्यय आणू नका."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"व्यत्यय आणू नका बंद केले आहे."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"व्यत्यय आणू नका सुरू केले आहे."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ब्लूटूथ."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ब्लूटूथ सुरू."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ब्लूटूथ बंद केले."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ब्लूटूथ सुरू केले."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"स्थान अहवाल देणे बंद केले."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"स्थान अहवाल देणे सुरू केले."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> साठी अलार्म सेट केला."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"अधिक वेळ."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"कमी वेळ."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"फ्लॅशलाइट बंद केला."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"फ्लॅशलाइट सुरू केला."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रंग उत्क्रमण बंद केले."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रंग उत्क्रमण सुरू केले."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हॉटस्पॉट बंद केला."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हॉटस्पॉट सुरू केला."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्क्रीन कास्ट करणे थांबले."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"डेटा सर्व्हर बंद केला."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"डेटा सर्व्हर सुरू केला."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"डिस्प्ले चमक"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"मोबाइल डेटा थांबवला आहे"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"डेटास विराम दिला आहे"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"वाय-फाय नाही"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"चमक"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्व्हर्जन"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग सुधारणा"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"अधिक सेटिंग्ज"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"वापरकर्ता सेटिंग्ज"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"पूर्ण झाले"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"वापरण्यासाठी अनलॉक करा"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"तुमची कार्ड मिळवताना समस्या आली, कृपया नंतर पुन्हा प्रयत्न करा"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन सेटिंग्ज"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR स्कॅन करा"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR कोड स्कॅन करण्यासाठी क्लिक करा"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR कोड"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"स्कॅन करण्यासाठी टॅप करा"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"तुमचा कीबोर्ड तुमच्या टॅबलेटसह कनेक्ट करण्यासाठी, तुम्ही प्रथम ब्लूटूथ सुरू करणे आवश्यक आहे."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"सुरू करा"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"पॉवर सूचना नियंत्रणे"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"सुरू - चेहऱ्यावर आधारित"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अ‍ॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अ‍ॅपमधील सर्व सूचना ब्लॉक करा"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"पूर्ण झाले"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"लागू करा"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> मध्ये <xliff:g id="ARTIST_NAME">%2$s</xliff:g> चे <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले करा"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> मध्ये <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले करा"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"पहिल्यासारखे करा"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले करण्यासाठी जवळ जा"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले केला जात आहे"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"आढळले नाही"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"वापरकर्ता निवडा"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ॲप्स बॅकग्राउंडमध्ये रन होत आहेत"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"थांबवा"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index 588c5ad..7fd88cc 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"सुरू आहे"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"उपलब्ध नाही"</item>
     <item msgid="9103697205127645916">"बंद आहे"</item>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 1dc3873..4cf476b 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kunci skrin."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Skrin kunci kerja"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Tutup"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi dimatikan."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi dihidupkan."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Mod pesawat dimatikan."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Mod pesawat dihidupkan."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"senyap sepenuhnya"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"penggera sahaja"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Jangan Ganggu."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Jangan Ganggu dimatikan."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Jangan Ganggu dihidupkan."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth dihidupkan."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth dimatikan."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth dihidupkan."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Pelaporan lokasi dimatikan."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Pelaporan lokasi dihidupkan."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Penggera ditetapkan pada <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Lagi masa."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Kurang masa."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lampu suluh dimatikan."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lampu suluh dihidupkan."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Penyongsangan warna dimatikan."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Penyongsangan warna dihidupkan."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Tempat liputan mudah alih bergerak dimatikan."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Tempat liputan mudah alih bergerak dihidupkan."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Penghantaran skrin dihentikan."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Penjimat Data dimatikan."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Penjimat Data dihidupkan."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Kecerahan paparan"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Data mudah alih dijeda"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data dijeda"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi tidak disambungkan"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Penyongsangan warna"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pembetulan warna"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Lagi tetapan"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Tetapan pengguna"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Imbas QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik untuk mengimbas kod QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Ketik untuk membuat imbasan"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar penggera yang seterusnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Untuk menyambungkan papan kekunci anda dengan tablet, anda perlu menghidupkan Bluetooth terlebih dahulu."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Hidupkan"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kawalan pemberitahuan berkuasa"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Hidup - Berasaskan wajah"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Dengan kawalan pemberitahuan berkuasa, anda boleh menetapkan tahap kepentingan dari 0 hingga 5 untuk pemberitahuan apl. \n\n"<b>"Tahap 5"</b>" \n- Tunjukkan pada bahagian atas senarai pemberitahuan \n- Benarkan gangguan skrin penuh \n- Sentiasa intai \n\n"<b>"Tahap 4"</b>" \n- Halang gangguan skrin penuh \n- Sentiasa intai \n\n"<b>"Tahap 3"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n\n"<b>"Tahap 2"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n- Jangan berbunyi dan bergetar \n\n"<b>"Tahap 1"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n- Jangan berbunyi atau bergetar \n- Sembunyikan daripada skrin kunci dan bar status \n- Tunjukkan di bahagian bawah senarai pemberitahuan \n\n"<b>"Tahap 0"</b>" \n- Sekat semua pemberitahuan daripada apl"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Guna"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> daripada <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> daripada <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Buat asal"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Alihkan lebih dekat untuk bermain pada<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Dimainkan pada <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apl berjalan di latar"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index 60ce1f0..eaafd19 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Mati"</item>
     <item msgid="2075645297847971154">"Hidup"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Tidak tersedia"</item>
+    <item msgid="1909756493418256167">"Mati"</item>
+    <item msgid="4531508423703413340">"Hidup"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Tidak tersedia"</item>
     <item msgid="9103697205127645916">"Mati"</item>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 5ceadae..8964ed5 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"မျက်နှာပြင် သော့ပိတ်ရန်"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"အလုပ်သုံး လော့ခ်မျက်နှာပြင်"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ပိတ်ရန်"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ကြိုးမဲ့ ပိတ်ထား။"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ကြိုးမဲ့ ဖွင့်ထား။"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"လေယာဉ် မုဒ်ကို ပိတ်ထားလိုက်ပြီ။"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"လေယာဉ် မုဒ်ကို ဖွင့်ထားလိုက်ပြီ။"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"လုံးဝ အသံပိတ်ထားရန်"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"နှိုးစက်များသာ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"မနှောင့်ယှက်ရ။"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'မနှောင့်ယှက်ရ\' ကိုပိတ်ထားသည်။"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\'မနှောင့်ယှက်ရ\' ကိုဖွင့်ထားသည်။"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ဘလူးတုသ်။"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ဘလူးတုသ် ဖွင့်ထား။"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ဘလူးတုသ် ပိတ်ထား။"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ဘလူးတုသ် ဖွင့်ထား။"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"တည်နေရာ သတင်းပို့မှု ပိတ်ထား။"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"တည်နေရာ သတင်းပို့မှု ဖွင့်ထား။"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"နိုးစက်ပေးထားသော အချိန် <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"အချိန် တိုး"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"အချိန် လျှော့"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ဖလက်ရှမီး ပိတ်ထားသည်။"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ဖလက်ရှမီး ဖွင့်ထားသည်။"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"အရောင် ပြောင်းပြန်လှန်မှု ပိတ်ထား။"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"အရောင် ပြောင်းပြန်လှန်မှု ဖွင့်ထား။"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"မိုဘိုင်း ဟော့စပေါ့ ပိတ်ထား။"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"မိုဘိုင်း ဟော့စပေါ့ ဖွင့်ထား။"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"မျက်နှာပြင် ကာစ်တင် လုပ်မှု ရပ်လိုက်ပြီ။"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ဒေတာချွေတာမှု ပိတ်ထားသည်။"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ဒေတာချွေတာမှု ဖွင့်ထားသည်။"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"တောက်ပမှုကို ပြရန်"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"မိုဘိုင်းဒေတာကို ခေတ္တရပ်ထားသည်"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ဒေတာ ခေတ္တရပ်တန့်သည်"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi ချိတ်ဆက်ထားခြင်းမရှိပါ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"အလင်းတောက်ပမှု"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"အရောင်ပြောင်းပြန်ပြုလုပ်ရန်"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"အရောင် အမှန်ပြင်ခြင်း"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"နောက်ထပ် ဆက်တင်များ"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"အသုံးပြုသူ ဆက်တင်များ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ပြီးပါပြီ"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"သုံးရန် လော့ခ်ဖွင့်ပါ"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"သင်၏ကတ်များ ရယူရာတွင် ပြဿနာရှိနေသည်၊ နောက်မှ ထပ်စမ်းကြည့်ပါ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"လော့ခ်မျက်နှာပြင် ဆက်တင်များ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR စကင်ဖတ်ခြင်း"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ကုဒ် စကင်ဖတ်ရန် ကလစ်နှိပ်ပါ"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> ၌သင့်နောက်ထပ် နှိုးစက်ကို ကြားမည်မဟုတ်ပါ"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ကီးဘုတ်ကို တပ်ဘလက်နှင့် ချိတ်ဆက်ရန်၊ ပမထဦးစွာ ဘလူးတုသ်ကို ဖွင့်ပါ။"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ဖွင့်ပါ"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ဖွင့် - မျက်နှာအခြေခံ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများကိုအသုံးပြုပြီး အက်ပ်တစ်ခု၏ အကြောင်းကြားချက် အရေးပါမှု ၀ မှ ၅ အထိသတ်မှတ်ပေးနိုင်သည်။ \n\n"<b>"အဆင့် ၅"</b>" \n- အကြောင်းကြားချက်စာရင်း၏ ထိပ်ဆုံးတွင် ပြသည် \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်းကို ခွင့်ပြုသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၄"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၃"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n\n"<b>"အဆင့် ၂"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n\n"<b>"အဆင့် ၁"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n- လော့ခ်ချထားသည့် မျက်နှာပြင်နှင့် အခြေအနေဘားတန်းတို့တွင် မပြပါ \n- အကြောင်းကြားချက်စာရင်း အောက်ဆုံးတွင်ပြသည် \n\n"<b>"အဆင့် ၀"</b>" \n- အက်ပ်မှ အကြောင်းကြားချက်များ အားလုံးကို ပိတ်ဆို့သည်"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ပြီးပြီ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"အသုံးပြုရန်"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ၏ <xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%3$s</xliff:g> တွင် ဖွင့်ပါ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%2$s</xliff:g> တွင် ဖွင့်ပါ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"နောက်ပြန်ရန်"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင်ဖွင့်ရန် အနီးသို့ရွှေ့ပါ"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင်ဖွင့်ထားသည်"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"မတွေ့ပါ"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"အသုံးပြုသူ ရွေးခြင်း"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"နောက်ခံတွင် ဖွင့်ထားသောအက်ပ်များ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ရပ်ရန်"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 7d2f20b..dfc8ccc 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ဖွင့်"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"မရနိုင်ပါ"</item>
     <item msgid="9103697205127645916">"ပိတ်"</item>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5aa4dd2..6e66156 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Låseskjerm."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Låseskjerm for arbeid"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Lukk"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi er slått av."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi er slått på."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Flymodus er slått av."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Flymodus er slått på."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"total stillhet"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"bare alarmer"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ikke forstyrr."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"«Ikke forstyrr» er slått av."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"«Ikke forstyrr» er slått på."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth er på."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth er av."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth er på."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Posisjonsrapportering er slått av."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Posisjonsrapportering er slått på."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarmen ble stilt for <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mer tid."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mindre tid."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lommelykten er slått av."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lommelykten er slått på."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inverterte farger er slått av."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inverterte farger er slått på."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil Wi-Fi-sone er slått av."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil Wi-Fi-sone er slått på."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Skjermcastingen er stoppet."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datasparing er slått av."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datasparing er slått på."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Lysstyrken på skjermen"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobildatabruk er satt på pause"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data er satt på pause"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi er ikke tilkoblet"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Fargekorrigering"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Flere innstillinger"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brukerinnstillinger"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås opp for å bruke"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Det oppsto et problem med henting av kortene. Prøv igjen senere"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Innstillinger for låseskjermen"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skann QR-kode"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klikk for å skanne en QR-kode"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Du hører ikke neste innstilte alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"For å koble tastaturet til nettbrettet ditt må du først slå på Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Slå på"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Effektive varselinnstillinger"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbasert"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Med effektive varselinnstillinger kan du angi viktighetsnivåer fra 0 til 5 for appvarsler. \n\n"<b>"Nivå 5"</b>" \n– Vis øverst på varsellisten \n– Tillat forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 4"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 3"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n\n"<b>"Nivå 2"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri fort \n– Tillat aldri lyder eller vibrering \n\n"<b>"Nivå 1"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n– Tillat aldri lyder eller vibrering \n– Skjul fra låseskjermen og statusfeltet \n– Vis nederst på varsellisten \n\n"<b>"Nivå 0"</b>" \n– Blokkér alle varsler fra appen"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Ferdig"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Bruk"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> av <xliff:g id="ARTIST_NAME">%2$s</xliff:g> fra <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> fra <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Angre"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Flytt nærmere for å spille av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Spilles av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ikke funnet"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velg bruker"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apper som kjører i bakgrunnen"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stopp"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index e512a84..38e10456 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Av"</item>
     <item msgid="2075645297847971154">"På"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Utilgjengelig"</item>
+    <item msgid="1909756493418256167">"Av"</item>
+    <item msgid="4531508423703413340">"På"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Utilgjengelig"</item>
     <item msgid="9103697205127645916">"Av"</item>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index afa8f1f..db64a7e 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"स्क्रीन बन्द गर्नुहोस्।"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"कार्य प्रोफाइलको लक स्क्रिन"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"बन्द गर्नुहोस्"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi बन्द गरियो।"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi खुला गरियो।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"हवाइजहाज मोड बन्द छ।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"हवाइजहाज मोड खोलियो।"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"पूर्ण मौनता"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"अलार्महरू मात्र"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"बाधा नपुऱ्याउनुहोस्।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"बाधा नपुऱ्याउनुहोस् नामक सुविधा निष्क्रिय पारियो।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"बाधा नपुऱ्याउनुहोस् नामक सुविधा सक्रिय पारियो।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ब्लुटुथ।"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ब्लुटुथ खुला छ।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ब्लुटुथ बन्द गरियो।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ब्लुटुथ चालू गरियो।"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"स्थान रिपोर्टिङ बन्द गरियो।"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"स्थान रिपोर्टिङ खुला गरियो।"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g>को लागि सङ्केत घन्टी सेट गरिएको"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"थप समय।"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"कम समय।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"टर्च बन्द गरियो।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"टर्च खुला गरियो।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रङ्ग उल्टाउने बन्द गरियो।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रङ्ग उल्टाउने खुला गरियो।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हटस्पट बन्द गरियो।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हटस्पट खुला गरियो।"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्क्रिन कास्टिङ रोकियो।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"डेटा सेभरलाई निष्क्रिय पारियो।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"डेटा सेभरलाई सक्रिय गरियो।"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"प्रदर्शन चमक"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"मोबाइल डेटा पज गरिएको छ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"डेटा रोकिएको छ"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi जडान गरिएको छैन"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"उज्यालपन"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्भर्सन"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रङ सच्याउने कार्य"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"थप सेटिङहरू"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"प्रयोगकर्तासम्बन्धी सेटिङ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"भयो"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"यो वालेट प्रयोग गर्न डिभाइस अनलक गर्नुहोस्"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"तपाईंका कार्डहरू प्राप्त गर्ने क्रममा समस्या भयो, कृपया पछि फेरि प्रयास गर्नुहोस्"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लक स्क्रिनसम्बन्धी सेटिङ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR स्क्यान गर्नुहोस्"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR कोड स्क्यान गर्न क्लिक गर्नुहोस्"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR कोड"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"स्क्यान गर्न ट्याप गर्नुहोस्"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"तपाईँले आफ्नो अर्को अलार्म <xliff:g id="WHEN">%1$s</xliff:g> सुन्नुहुने छैन"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"सक्रिय पार्नुहोस्"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"सशक्त सूचना नियन्त्रण"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"अन छ - अनुहारमा आधारित"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- एपका सबै सूचनाहरूलाई रोक्ने"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"सम्पन्न भयो"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"लागू गर्नुहोस्"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> को <xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%3$s</xliff:g> मा बजाउनुहोस्"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%2$s</xliff:g> मा बजाउनुहोस्"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"अन्डू गर्नुहोस्"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गर्न आफ्नो डिभाइस नजिकै लैजानुहोस्"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गरिँदै छ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"फेला परेन"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"प्रयोगकर्ता चयन गर्नु…"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"अहिले ब्याकग्राउन्डमा चलिरहेका एप"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"रोक्नुहोस्"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 355df408..abe94e7 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"अन छ"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"उपलब्ध छैन"</item>
     <item msgid="9103697205127645916">"अफ छ"</item>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index cb963e6..1f815b7 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -38,6 +38,15 @@
         <item name="android:textColorSecondary">?android:attr/textColorPrimaryInverse</item>
     </style>
 
+    <!-- Clipboard overlay's edit text activity. -->
+    <style name="EditTextActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowLightStatusBar">false</item>
+        <item name="android:windowLightNavigationBar">false</item>
+        <item name="android:navigationBarColor">?android:attr/colorBackgroundFloating</item>
+        <item name="android:textColorSecondary">?android:attr/textColorPrimaryInverse</item>
+    </style>
+
     <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight">
         <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
     </style>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 09b278f..b895d9e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Vergrendelscherm."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Vergrendelscherm voor werk"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Sluiten"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi staat uit."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi staat aan."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Vliegtuigmodus staat uit."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Vliegtuigmodus staat aan."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"totale stilte"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"alleen wekkers"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Niet storen."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Niet storen staat uit."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Niet storen staat aan."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth aan."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth staat uit."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth staat aan."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Locatiemelding staat uit."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Locatiemelding staat aan."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Wekker is ingesteld op <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Meer tijd."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Minder tijd."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Zaklamp staat uit."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Zaklamp staat aan."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Kleurinversie staat uit."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Kleurinversie staat aan."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele hotspot staat uit."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele hotspot staat aan."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casten van scherm gestopt."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databesparing staat uit."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databesparing staat aan."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Helderheid van het scherm"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobiele data zijn onderbroken"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Gegevens zijn onderbroken"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wifi niet verbonden"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleurinversie"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurcorrectie"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellingen"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikersinstellingen"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-code scannen"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik om een QR-code te scannen"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tik om te scannen"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Als je je toetsenbord wilt verbinden met je tablet, moet je eerst Bluetooth aanzetten."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aanzetten"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Beheeropties voor meldingen met betrekking tot stroomverbruik"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan: op basis van gezicht"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Met beheeropties voor meldingen met betrekking tot stroomverbruik kun je een belangrijkheidsniveau van 0 tot 5 instellen voor de meldingen van een app. \n\n"<b>"Niveau 5"</b>" \n- Bovenaan de lijst met meldingen tonen \n- Onderbreking op volledig scherm toestaan \n- Altijd korte weergave \n\n"<b>"Niveau 4"</b>" \n- Geen onderbreking op volledig scherm \n- Altijd korte weergave \n\n"<b>"Niveau 3"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n\n"<b>"Niveau 2"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n\n"<b>"Niveau 1"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n- Verbergen op vergrendelscherm en statusbalk \n- Onderaan de lijst met meldingen tonen \n\n"<b>"Niveau 0"</b>" \n- Alle meldingen van de app blokkeren"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> van <xliff:g id="ARTIST_NAME">%2$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ongedaan maken"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Ga dichter naar <xliff:g id="DEVICENAME">%1$s</xliff:g> toe om af te spelen"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Afspelen op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Niet gevonden"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Gebruiker selecteren"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps die op de achtergrond worden uitgevoerd"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stoppen"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index fa85b88..ac85f28 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Uit"</item>
     <item msgid="2075645297847971154">"Aan"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Niet beschikbaar"</item>
+    <item msgid="1909756493418256167">"Uit"</item>
+    <item msgid="4531508423703413340">"Aan"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Niet beschikbaar"</item>
     <item msgid="9103697205127645916">"Uit"</item>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 0d0cea5..e47513c 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ଲକ୍‌ ସ୍କ୍ରୀନ୍‌।"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ୱର୍କ ଲକ୍‍ ସ୍କ୍ରୀନ୍‍"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ବନ୍ଦ କରନ୍ତୁ"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ୱାଇ-ଫାଇ ବନ୍ଦ ଅଛି।"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ୱାଇ-ଫାଇ ଚାଲୁ ଅଛି।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌କୁ ଚାଲୁ କରାଯାଇଛି।"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବତା"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"କେବଳ ଆଲାର୍ମ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\"କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଚାଲୁ ଅଛି।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ବ୍ଲୁଟୁଥ।"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ବ୍ଲୁଟୂଥ୍‍‍ ଚାଲୁ ଅଛି।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ବ୍ଲୁ-ଟୁଥ୍‍କୁ ବନ୍ଦ କରିଦିଆଯାଇଛି।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"ବ୍ଲୁ-ଟୁଥ୍‍କୁ ଚାଲୁ କରାଯାଇଛି।"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ଲୋକେଶନ୍‌ର ରିପୋର୍ଟ ବନ୍ଦ କରାଗଲା।"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"ଲୋକେଶନ୍‌ର ରିପୋର୍ଟ ଅନ୍ କରାଗଲା।"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g>ରେ ଆଲାର୍ମ ସେଟ୍‍ କରାଯାଇଛି।"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ଅଧିକ ସମୟ।"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"କମ୍ ସମୟ।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ଟର୍ଚ୍ଚ ଲାଇଟ୍ ବନ୍ଦ ଅଛି।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ଟର୍ଚ୍ଚ ଲାଇଟ୍ ଚାଲୁ ଅଛି।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"ରଙ୍ଗ ବିପରୀତିକରଣକୁ ବନ୍ଦ କରିଦିଆଗଲା।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ରଙ୍ଗ ବିପରୀତିକରଣକୁ ଚାଲୁ କରିଦିଆଗଲା।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ମୋବାଇଲ୍ ହଟସ୍ପଟ୍‌ ବନ୍ଦ ଅଛି।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ମୋବାଇଲ୍ ହଟସ୍ପଟ୍‌ ଚାଲୁ ଅଛି।"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ସ୍କ୍ରୀନ୍‌ କାଷ୍ଟ କରିବା ରହିଯାଇଛି।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ଡାଟା ସେଭର୍‌ ଅଫ୍‍ କରାଗଲା।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ଡାଟା ସେଭର୍‌ ଅନ୍‍ କରାଗଲା।"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ଡିସ୍‌ପ୍ଲେ ଉଜ୍ଜ୍ୱଳତା"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ମୋବାଇଲ୍‍ ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ୱାଇ-ଫାଇ ସଂଯୋଜିତ ହୋଇନାହିଁ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ଉଜ୍ଜ୍ୱଳତା"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ରଙ୍ଗ ଇନଭାର୍ସନ"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"ଅଧିକ ସେଟିଂସ୍"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ଉପଯୋଗକର୍ତ୍ତା ସେଟିଂସ୍"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ହୋଇଗଲା"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"ଆପଣଙ୍କ କାର୍ଡଗୁଡ଼ିକ ପାଇବାରେ ଏକ ସମସ୍ୟା ହୋଇଥିଲା। ଦୟାକରି ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟିଂସ୍"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ସ୍କାନ କରନ୍ତୁ"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"ଏକ QR କୋଡ ସ୍କାନ କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"ସ୍କାନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>ବେଳେ ଆପଣ ନିଜର ପରବର୍ତ୍ତୀ ଆଲାର୍ମ ଶୁଣିପାରିବେ ନାହିଁ"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ କୀ’ବୋର୍ଡ ସଂଯୋଗ କରିବା ପାଇଁ ଆପଣଙ୍କୁ ପ୍ରଥମେ ବ୍ଲୁଟୂଥ୍‍‍ ଅନ୍‍ କରିବାକୁ ହେବ।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ପାୱାର୍‍ ବିଜ୍ଞପ୍ତି କଣ୍ଟ୍ରୋଲ୍‌"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ଚାଲୁ ଅଛି - ଫେସ-ଆଧାରିତ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ପାୱାର୍‍ ବିଜ୍ଞପ୍ତି କଣ୍ଟ୍ରୋଲ୍‌ରେ, ଆପଣ ଏକ ଆପ୍‍ ବିଜ୍ଞପ୍ତି ପାଇଁ 0 ରୁ 5 ଗୁରୁତ୍ୱ ସ୍ତର ସେଟ୍‍ କରିହେବେ। \n\n"<b>"ସ୍ତର 5"</b>" \n- ବିଜ୍ଞପ୍ତି ତାଲିକାର ଶୀର୍ଷରେ ଦେଖାନ୍ତୁ \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ \n- ସର୍ବଦା ପିକ୍‍ କରନ୍ତୁ \n\n"<b>"ସ୍ତର 4"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- ସର୍ବଦା ପିକ୍‍ କରନ୍ତୁ \n\n"<b>"ସ୍ତର 3"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- କଦାପି ପିକ୍‍ କରନ୍ତୁ ନାହିଁ \n\n"<b>"ସ୍ତର 2"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- କଦାପି ପିକ୍‍ କରନ୍ତୁ ନାହିଁ \n- କଦାପି ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେଟ୍‍ କରନ୍ତୁ ନାହିଁ \n\n"<b>"ସ୍ତର 1"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- କଦାପି ପିକ୍‍ କରନ୍ତୁ ନାହିଁ \n- କଦାପି ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେଟ୍‍ କରନ୍ତୁ ନାହିଁ \n- ଲକ୍‍ ସ୍କ୍ରୀନ୍‍ ଓ ଷ୍ଟାଟସ୍‍ ବାର୍‌ରୁ ଲୁଚାନ୍ତୁ \n- ବିଜ୍ଞପ୍ତି ତାଲିକାର ନିମ୍ନରେ ଦେଖାନ୍ତୁ \n\n"<b>"ସ୍ତର 0"</b>" \n- ଆପରୁ ସମସ୍ତ ବିଜ୍ଞପ୍ତି ବ୍ଲକ୍‌ କରନ୍ତୁ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ହୋଇଗଲା"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ଲାଗୁ କରନ୍ତୁ"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ରୁ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ଙ୍କ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ରୁ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ପୂର୍ବବତ୍ କରନ୍ତୁ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ଚଲାଇବା ପାଇଁ ନିକଟକୁ ଯାଆନ୍ତୁ"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ଚାଲୁଛି"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ମିଳିଲା ନାହିଁ"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ଉପଯୋଗକର୍ତ୍ତା ଚୟନ କର"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ପୃଷ୍ଠପଟରେ ଚାଲୁଥିବା ଆପଗୁଡ଼ିକ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index bb94e0d..48ebb63 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ଚାଲୁ ଅଛି"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ଉପଲବ୍ଧ ନାହିଁ"</item>
     <item msgid="9103697205127645916">"ବନ୍ଦ ଅଛି"</item>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 902f36c..3eae70a 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">" ਲਾਕ  ਸਕ੍ਰੀਨ।"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ਕਾਰਜ-ਸਥਾਨ  ਲਾਕ  ਸਕ੍ਰੀਨ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ਬੰਦ ਕਰੋ"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi ਬੰਦ ਕੀਤਾ।"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi ਚਾਲੂ ਕੀਤਾ।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ਏਅਰਪਲੇਨ ਮੋਡ ਬੰਦ ਹੈ।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ਏਅਰਪਲੇਨ ਮੋਡ ਚਾਲੂ ਹੋਇਆ"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ਪੂਰਾ ਸ਼ਾਂਤ"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ਸਿਰਫ਼ ਅਲਾਰਮ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ਬਲੂਟੁੱਥ।"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth ਚਾਲੂ।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth ਬੰਦ ਹੈ।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth ਚਾਲੂ ਕੀਤੀ।"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ਟਿਕਾਣਾ ਰਿਪੋਰਟਿੰਗ ਬੰਦ ਕੀਤੀ।"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"ਟਿਕਾਣਾ ਰਿਪੋਰਟਿੰਗ ਚਾਲੂ ਕੀਤੀ।"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"ਅਲਾਰਮ <xliff:g id="TIME">%s</xliff:g> ਲਈ ਸੈੱਟ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ਹੋਰ ਸਮਾਂ।"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"ਘੱਟ ਸਮਾਂ।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ਫਲੈਸ਼ਲਾਈਟ ਬੰਦ ਕੀਤਾ।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ਫਲੈਸ਼ਲਾਈਟ ਚਾਲੂ ਕੀਤੀ।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"ਰੰਗ ਦੀ ਉਲਟੀ ਤਰਤੀਬ ਬੰਦ ਕੀਤੀ।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ਰੰਗ ਦੀ ਉਲਟੀ ਤਰਤੀਬ ਚਾਲੂ ਕੀਤੀ।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ਮੋਬਾਈਲ ਹੌਟਸਪੌਟ ਬੰਦ ਕੀਤਾ।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ਮੋਬਾਈਲ ਹੌਟਸਪੌਟ ਚਾਲੂ ਕੀਤਾ।"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ਸਕ੍ਰੀਨ ਜੋੜਨਾ ਬੰਦ ਹੋਇਆ।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ਡਾਟਾ ਸੇਵਰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ਡਿਸਪਲੇ ਚਮਕ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ਮੋਬਾਈਲ ਡਾਟਾ ਰੋਕ ਦਿੱਤਾ ਗਿਆ ਹੈ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">" ਡਾਟਾ  ਰੁਕ ਗਿਆ ਹੈ"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ਵਾਈ-ਫਾਈ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ਚਮਕ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ਰੰਗ ਪਲਟਨਾ"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ਰੰਗ ਸੁਧਾਈ"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ਵਰਤੋਂਕਾਰ ਸੈਟਿੰਗਾਂ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ਹੋ ਗਿਆ"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"ਤੁਹਾਡੇ ਕਾਰਡ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਕੋਈ ਸਮੱਸਿਆ ਆਈ, ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ਲਾਕ ਸਕ੍ਰੀਨ ਸੈਟਿੰਗਾਂ"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ਸਕੈਨ ਕਰੋ"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ਕੋਡ ਨੂੰ ਸਕੈਨ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"ਸਕੈਨ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ਤੁਸੀਂ <xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ ਆਪਣਾ ਅਗਲਾ ਅਲਾਰਮ ਨਹੀਂ ਸੁਣੋਗੇ"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ਆਪਣੇ ਟੈਬਲੈੱਟ ਨਾਲ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਕਨੈਕਟ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ ਬਲੂਟੁੱਥ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ਚਾਲੂ ਕਰੋ"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ਪਾਵਰ ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ਚਾਲੂ ਹੈ - ਚਿਹਰਾ-ਆਧਾਰਿਤ"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ਪਾਵਰ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨਾਲ, ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਲਈ ਮਹੱਤਤਾ ਪੱਧਰ ਨੂੰ 0 ਤੋਂ 5 ਤੱਕ ਸੈੱਟ ਕਰ ਸਕਦੇ ਹੋ। \n\n"<b>"ਪੱਧਰ 5"</b>" \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਦਿਖਾਓ \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਦੀ ਆਗਿਆ ਦਿਓ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 4"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 3"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 2"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਵੀ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n\n"<b>"ਪੱਧਰ 1"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n- ਲਾਕ ਸਕ੍ਰੀਨ ਅਤੇ ਸਥਿਤੀ ਪੱਟੀ ਤੋਂ ਲੁਕਾਓ \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਹੇਠਾਂ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 0"</b>" \n- ਐਪ ਤੋਂ ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕਰੋ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ਹੋ ਗਿਆ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ਲਾਗੂ ਕਰੋ"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ਤੋਂ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ਦਾ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚਲਾਓ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ਤੋਂ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚਲਾਓ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ਅਣਕੀਤਾ ਕਰੋ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਉਣ ਲਈ ਨੇੜੇ ਲਿਜਾਓ"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ਨਹੀਂ ਮਿਲਿਆ"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਹੀਆਂ ਐਪਾਂ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ਬੰਦ ਕਰੋ"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 2403141c..85c5d89 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ਚਾਲੂ ਹੈ"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ਅਣਉਪਲਬਧ ਹੈ"</item>
     <item msgid="9103697205127645916">"ਬੰਦ ਹੈ"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 3d09c50..41ad75a 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ekran blokady."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ekran blokady wyświetlany podczas działania"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zamknij"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi wyłączone."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi włączone."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Tryb samolotowy został wyłączony."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Tryb samolotowy został włączony."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"całkowita cisza"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"tylko alarmy"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Nie przeszkadzać."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Tryb Nie przeszkadzać został wyłączony."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Tryb Nie przeszkadzać został włączony."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth włączony."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth jest wyłączony."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth jest włączony."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Raportowanie lokalizacji zostało wyłączone."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Raportowanie lokalizacji zostało włączone."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm ustawiony na <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Więcej czasu."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mniej czasu."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Latarka została wyłączona."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Latarka została włączona."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Odwrócenie kolorów zostało wyłączone."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Odwrócenie kolorów zostało włączone."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilny hotspot został wyłączony."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilny hotspot został włączony."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Zatrzymano przesyłanie ekranu."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Oszczędzanie danych jest wyłączone."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Oszczędzanie danych jest włączone."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Jasność wyświetlacza"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobilna transmisja danych jest wstrzymana"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Transmisja danych została wstrzymana"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Brak połączenia z Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jasność"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Odwrócenie kolorów"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcja kolorów"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Więcej ustawień"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ustawienia użytkownika"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotowe"</string>
@@ -472,8 +455,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odblokuj, aby użyć"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Podczas pobierania kart wystąpił problem. Spróbuj ponownie później."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ustawienia ekranu blokady"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanowanie kodu QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknij, aby zeskanować kod QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nie usłyszysz swojego następnego alarmu <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -493,6 +478,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Aby połączyć klawiaturę z tabletem, musisz najpierw włączyć Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Włącz"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Zaawansowane ustawienia powiadomień"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Włączono – na podstawie twarzy"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Dzięki zaawansowanym ustawieniom możesz określić poziom ważności powiadomień z aplikacji w skali od 0 do 5. \n\n"<b>"Poziom 5"</b>" \n– Pokazuj u góry listy powiadomień \n– Zezwalaj na powiadomienia na pełnym ekranie \n– Zawsze pokazuj podgląd \n\n"<b>"Poziom 4"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Zawsze pokazuj podgląd \n\n"<b>"Poziom 3"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n\n"<b>"Poziom 2"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n– NIgdy nie powiadamiaj dźwiękiem ani wibracjami \n\n"<b>"Poziom 1"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n– NIgdy nie powiadamiaj dźwiękiem ani wibracjami \n– Ukrywaj na ekranie blokady i pasku stanu \n– Pokazuj u dołu listy powiadomień \n\n"<b>"Poziom 0"</b>" \n– Blokuj wszystkie powiadomienia aplikacji"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotowe"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Zastosuj"</string>
@@ -819,7 +805,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) w aplikacji <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> w aplikacji <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Cofnij"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Przysuń się bliżej, aby odtwarzać na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Odtwarzam na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nie znaleziono"</string>
@@ -907,4 +894,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Wybierz użytkownika"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacje działające w tle"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zatrzymaj"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 30026e8..8b922e5 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Wyłączony"</item>
     <item msgid="2075645297847971154">"Włączony"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Brak dostępu"</item>
+    <item msgid="1909756493418256167">"Wyłączono"</item>
+    <item msgid="4531508423703413340">"Włączono"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Niedostępny"</item>
     <item msgid="9103697205127645916">"Wyłączony"</item>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e166fa4..d21ea54 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Tela de bloqueio."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Tela de bloqueio de trabalho"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Fechar"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"O Wi-Fi foi desativado."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"O Wi-Fi foi ativado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"O modo avião foi desativado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"O modo avião foi ativado."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silêncio total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"somente alarmes"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Não perturbe."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"Não perturbe\" desativado."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"Não perturbe\" ativado."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth ativado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"O Bluetooth foi desativado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"O Bluetooth foi ativado."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"O Relatório de localização foi desativado."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"O Relatório de localização foi ativado."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mais tempo."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menos tempo."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"A lanterna foi desativada."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"A lanterna foi ativada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"A inversão de cores foi desativada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"A inversão de cores foi ativada."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brilho da tela"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Os dados móveis estão pausados"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Os dados foram pausados"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi não conectado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Mais configurações"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Config. do usuário"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ler código QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Toque para fazer a leitura"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ativar"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controles de ativação/desativação de notificações"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Aproxime os dispositivos para ouvir música no <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Reproduzido em <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps em execução em segundo plano"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index f874dd4..932ddc0 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desativado"</item>
     <item msgid="2075645297847971154">"Ativado"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Indisponível"</item>
+    <item msgid="1909756493418256167">"Desativada"</item>
+    <item msgid="4531508423703413340">"Ativada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Indisponível"</item>
     <item msgid="9103697205127645916">"Desativada"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 808dd2b..556e92a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ecrã de bloqueio."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ecrã de bloqueio de trabalho"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Fechar"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi desligado."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi ligado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Modo de avião desligado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Modo de avião ligado."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silêncio total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"apenas alarmes"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Não incomodar."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Modo Não incomodar desativado."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Modo Não incomodar ativado."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth ligado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth desligado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth ligado."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Relatórios de localização desligados."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Relatórios de localização ligados."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mais tempo."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menos tempo."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lanterna desligada."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lanterna ligada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversão de cores desligada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversão de cores ligada."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Zona Wi-Fi móvel desligada."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona Wi-Fi móvel ligada."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmissão do ecrã interrompida."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Poupança de dados desativada."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Poupança de dados ativada."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brilho do visor"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Dados móveis em pausa"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Dados em pausa"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi não ligado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção da cor"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Mais definições"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Definições do utilizador"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para utilizar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao obter os seus cartões. Tente novamente mais tarde."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Definições do ecrã de bloqueio"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Leia o QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"Código QR"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Toque para ler"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Para ligar o teclado ao tablet, tem de ativar primeiro o Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ativar"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controlos de notificações do consumo de energia"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada – Com base no rosto"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Com os controlos de notificações do consumo de energia, pode definir um nível de importância de 0 a 5 para as notificações de aplicações. \n\n"<b>"Nível 5"</b>" \n- Mostrar no início da lista de notificações \n- Permitir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre \n\n"<b>"Nível 4"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre\n\n"<b>"Nível 3"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n\n"<b>"Nível 2"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n- Ocultar do ecrã de bloqueio e da barra de estado \n- Mostrar no fim da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações da app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Anular"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Aproxime-se para reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"A reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativa. Consulte a app."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado."</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecione utilizador"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps em execução em segundo plano"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index 1e426e1..e6ebea8 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desligado"</item>
     <item msgid="2075645297847971154">"Ligado"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Indisponível"</item>
+    <item msgid="1909756493418256167">"Desativada"</item>
+    <item msgid="4531508423703413340">"Ativada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Indisponível"</item>
     <item msgid="9103697205127645916">"Desligado"</item>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e166fa4..d21ea54 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Tela de bloqueio."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Tela de bloqueio de trabalho"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Fechar"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"O Wi-Fi foi desativado."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"O Wi-Fi foi ativado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"O modo avião foi desativado."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"O modo avião foi ativado."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"silêncio total"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"somente alarmes"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Não perturbe."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"Não perturbe\" desativado."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"Não perturbe\" ativado."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth ativado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"O Bluetooth foi desativado."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"O Bluetooth foi ativado."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"O Relatório de localização foi desativado."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"O Relatório de localização foi ativado."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mais tempo."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Menos tempo."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"A lanterna foi desativada."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"A lanterna foi ativada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"A inversão de cores foi desativada."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"A inversão de cores foi ativada."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Brilho da tela"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Os dados móveis estão pausados"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Os dados foram pausados"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi não conectado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Mais configurações"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Config. do usuário"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ler código QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Toque para fazer a leitura"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Ativar"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Controles de ativação/desativação de notificações"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Aproxime os dispositivos para ouvir música no <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Reproduzido em <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps em execução em segundo plano"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index f874dd4..932ddc0 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Desativado"</item>
     <item msgid="2075645297847971154">"Ativado"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Indisponível"</item>
+    <item msgid="1909756493418256167">"Desativada"</item>
+    <item msgid="4531508423703413340">"Ativada"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Indisponível"</item>
     <item msgid="9103697205127645916">"Desativada"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 0fdd176..ab3c186 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ecranul de blocare."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ecran de blocare pentru serviciu"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Închideți"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Conexiunea prin Wi-Fi este dezactivată."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Conexiunea prin Wi-Fi este activată."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Modul Avion este dezactivat."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Modul Avion este activat."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"niciun sunet"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"numai alarme"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Nu deranja."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Funcția Nu deranja a fost dezactivată."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Funcția Nu deranja a fost activată."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Conexiunea prin Bluetooth este activată."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Conexiunea prin Bluetooth este dezactivată."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Conexiunea prin Bluetooth este activată."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Raportarea locației este dezactivată."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Raportarea locației este activată."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarmă setată pentru <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mai mult timp."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mai puțin timp."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Lanterna este dezactivată."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Lanterna este activată."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inversarea culorilor este dezactivată."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inversarea culorilor este activată."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Hotspotul mobil este dezactivat."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Hotspotul mobil este activat."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmiterea ecranului a fost oprită."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economizorul de date a fost dezactivat."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economizorul de date a fost activat."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Luminozitatea ecranului"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Datele mobile sunt întrerupte"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Conexiunea de date este întreruptă"</string>
@@ -252,6 +234,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Rețeaua Wi-Fi nu este conectată"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Mai multe setări"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setări de utilizator"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
@@ -469,8 +452,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblocați pentru a folosi"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încercați din nou mai târziu"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scanați un cod QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Dați clic pentru a scana un cod QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Atingeți pentru a scana"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nu veți auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -490,6 +474,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activați Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activați"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Comenzi de gestionare a notificărilor"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activată – În funcție de chip"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gata"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicați"</string>
@@ -813,7 +798,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Anulați"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Apropiați-vă pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Se redă pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
@@ -901,4 +887,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicațiile rulează în fundal"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Opriți"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index c63e7fe..ba936966 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Dezactivat"</item>
     <item msgid="2075645297847971154">"Activat"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Indisponibilă"</item>
+    <item msgid="1909756493418256167">"Dezactivată"</item>
+    <item msgid="4531508423703413340">"Activată"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Indisponibilă"</item>
     <item msgid="9103697205127645916">"Dezactivată"</item>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 2b06d10..e92364c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Экран блокировки."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Заблокировано"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Закрыть"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Модуль Wi-Fi отключен."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Модуль Wi-Fi включен."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Режим полета отключен."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Режим полета включен."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"полная тишина"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"только будильник"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Не беспокоить."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Режим \"Не беспокоить\" выключен."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Режим \"Не беспокоить\" включен."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Модуль Bluetooth включен."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Модуль Bluetooth отключен."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Модуль Bluetooth включен."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Отправка геоданных отключена."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Отправка геоданных включена."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Будильник установлен на <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Увеличить время."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Уменьшить время."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Фонарик отключен."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Фонарик включен."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Инверсия цвета отключена."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Инверсия цвета включена."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Точка доступа отключена."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Точка доступа включена."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Трансляция прекращена."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Режим экономии трафика отключен."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Режим экономии трафика включен."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Яркость экрана"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Передача данных остановлена"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Передача данных приостановлена"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Нет подключения к сети Wi-Fi."</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркость"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверсия цветов"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Коррекция цвета"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Настройки"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пользовательские настройки"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -472,8 +455,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблокировать для использования"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Не удалось получить информацию о картах. Повторите попытку позже."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки заблокированного экрана"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканер QR-кодов"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Нажмите, чтобы отсканировать QR-код."</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-код"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Нажмите, чтобы отсканировать код"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Следующий будильник: <xliff:g id="WHEN">%1$s</xliff:g>. Звук отключен."</string>
@@ -493,6 +476,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Чтобы подключить клавиатуру к планшету, включите Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Включить"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Расширенное управление уведомлениями"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Включить (на основе распознавания лиц)"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"С помощью этой функции вы можете устанавливать уровень важности уведомлений от 0 до 5 для каждого приложения.\n\n"<b>"Уровень 5"</b>\n"‒ Помещать уведомления в начало списка.\n‒ Показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 4\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 3\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\nУровень 2\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\nУровень 1\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\n‒ Не показывать на экране блокировки и в строке состояния.\n‒ Помещать уведомления в конец списка.\nУровень 0\n"<b></b>\n"‒ Блокировать все уведомления приложения."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Применить"</string>
@@ -819,7 +803,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Воспроизвести медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (исполнитель: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) из приложения \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\""</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Воспроизвести медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" из приложения \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Отменить"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Чтобы включить музыку на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", подойдите к нему ближе."</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Воспроизводится на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\"."</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Не найдено."</string>
@@ -907,4 +892,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выберите профиль"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Приложения, работающие в фоновом режиме"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Остановить"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index ae31f32..32e6ac97 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Включено"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Функция недоступна"</item>
     <item msgid="9103697205127645916">"Откл."</item>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index bdd41a5..327025c 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"අගුළු තිරය."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"කාර්යාල අගුලු තිරය"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"වසන්න"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi අක්‍රියයි."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi ක්‍රියාත්මකයි."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"අහස්යානා අකාරය අක්‍රියයි."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"අහස්යානා ආකාරය සක්‍රීයයි."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"සම්පූර්ණ නිහඬතාව"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"එලාම පමණි"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"බාධා නොකරන්න."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'බාධා නොකරන්න\' අක්‍රියයි."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"බාධා නොකරන්න සක්‍රීයයි."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"බ්ලූටූත්."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"බ්ලූටූත් ක්‍රියාත්මකයි."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"බ්ලූටූත් අක්‍රියයි."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"බ්ලූටූත් ක්‍රියාත්මක කෙරිණි."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ස්ථානය වාර්තාකරණය අක්‍රියයි."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"ස්ථානය වාර්තාකරණය ක්‍රියාත්මක කෙරිණි."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> සඳහා සීනුව සකස් කර ඇත."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"වේලාව වැඩියෙන්."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"වේලාව අඩුවෙන්."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"සැණෙළි ආලෝකය අක්‍රිය කරන ලදි."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"සැණෙළි ආලෝකය සක්‍රිය කරන ලදි."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"වර්ණ අපවර්තනය අක්‍රිය කරන ලදි."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"වර්ණ අපවර්තනය සක්‍රිය කරන ලදි."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ජංගම හොට්ස්පොටය අක්‍රිය කරන ලදි."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ජංගම හොට්ස්පොටය සක්‍රිය කරන ලදි."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"තිරය විකාශය කිරීම නැවත් වන ලදි."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"දත්ත සුරැකුම ක්‍රියාවිරහිත කරන ලදී."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"දත්ත සුරැකුම ක්‍රියාත්මක කරන ලදී."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"සංදර්ශක දීප්තිය"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"ජංගම දත්ත විරාම කර ඇත"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"දත්ත විරාම කර ඇත"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi සම්බන්ධ නොවීය"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"දීප්තිමත් බව"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"වර්ණ අපවර්තනය"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"වර්ණ නිවැරදි කිරීම"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"තව සැකසීම්"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"පරිශීලක සැකසීම්"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"නිමයි"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"භාවිත කිරීමට අගුලු හරින්න"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"ඔබගේ කාඩ්පත ලබා ගැනීමේ ගැටලුවක් විය, කරුණාකර පසුව නැවත උත්සාහ කරන්න"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"අගුලු තිර සැකසීම්"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR කේතය ස්කෑන් කරන්න"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR කේතයක් ස්කෑන් කිරීමට ක්ලික් කරන්න"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්‍රකාරය"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ඔබට ඔබේ ඊළඟ එලාමය <xliff:g id="WHEN">%1$s</xliff:g> නොඇසෙනු ඇත"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"ඔබේ යතුරු පුවරුව ඔබේ ටැබ්ලට් පරිගණකයට සම්බන්ධ කිරීමට, ඔබ පළමුව බ්ලූටූත් ක්‍රියාත්මක කළ යුතුය."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ක්‍රියාත්මක කරන්න"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"බල දැනුම්දීම් පාලන"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ක්‍රියාත්මකයි - මුහුණ-පදනම්ව"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"බල දැනුම්දීම් පාලන සමගින්, ඔබට යෙදුමක දැනුම්දීම් සඳහා වැදගත්කම 0 සිට 5 දක්වා සැකසිය හැකිය. \n\n"<b>"5 මට්ටම"</b>" \n- දැනුම්දීම් ලැයිස්තුවේ ඉහළින්ම පෙන්වන්න \n- පූර්ණ තිර බාධාවට ඉඩ දෙන්න \n- සැම විට එබී බලන්න \n\n"<b>"4 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- සැම විට එබී බලන්න \n\n"<b>"3 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n\n"<b>"2 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n\n"<b>"1 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n- අගුලු තිරය සහ තත්ත්ව තීරුව වෙතින් සඟවන්න \n- දැනුම්දීම් ලැයිස්තුවේ පහළින්ම පෙන්වන්න \n\n"<b>"0 මට්ටම"</b>" \n- යෙදුම වෙතින් වන සියලු දැනුම් දීම් සඟවන්න."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"නිමයි"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"යොදන්න"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>ගේ <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%3$s</xliff:g> වෙතින් වාදනය කරන්න"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> වෙතින් වාදනය කරන්න"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"පසුගමනය කරන්න"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කිරීමට වඩාත් ළං වන්න"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කරමින්"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"අක්‍රියයි, යෙදුම පරීක්ෂා කරන්න"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"හමු නොවිණි"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"පරිශීලක තෝරන්න"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"පසුබිමින් ධාවනය වෙමින් පවතින යෙදුම්"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"නවත්වන්න"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index fa78ccc..8929a3c5 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ක්‍රියාත්මකයි"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"නොමැත"</item>
     <item msgid="9103697205127645916">"අක්‍රියයි"</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0838f65..bb8870f 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Uzamknutá obrazovka"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Uzamknutá obrazovka pracovného profilu"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zavrieť"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Pripojenie Wi‑Fi je vypnuté."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Pripojenie Wi‑Fi je zapnuté."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Režim v lietadle je vypnutý."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Režim v lietadle je zapnutý."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"úplné ticho"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"iba budíky"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Režim bez vyrušení."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Režim bez vyrušení je vypnutý"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Režim bez vyrušení je zapnutý."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Rozhranie Bluetooth je zapnuté."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Rozhranie Bluetooth je vypnuté."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Rozhranie Bluetooth je zapnuté."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Nahlasovanie polohy je vypnuté."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Nahlasovanie polohy je zapnuté."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Budík nastavený na <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Dlhší čas"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Kratší čas"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Baterka je vypnutá."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Baterka je zapnutá."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Prevrátenie farieb je vypnuté."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Prevrátenie farieb je zapnuté."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilný hotspot je vypnutý."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilný hotspot je zapnutý."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prenášanie bolo zastavené."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Šetrič dát bol vypnutý."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Šetrič dát bol zapnutý."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Jas displeja"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobilné dáta sú pozastavené"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Dáta sú pozastavené"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Sieť Wi‑Fi nie je pripojená"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzia farieb"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Úprava farieb"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Ďalšie nastavenia"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Používateľské nastavenia"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
@@ -472,8 +455,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skenovanie QR kódu"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknutím naskenujte QR kód"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kód"</string>
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Váš budík o <xliff:g id="WHEN">%1$s</xliff:g> sa nespustí"</string>
@@ -493,6 +477,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Ak chcete klávesnicu pripojiť k tabletu, najprv musíte zapnúť Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Zapnúť"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Ovládacie prvky zobrazovania upozornení"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuté – podľa tváre"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Pomocou ovládacích prvkov zobrazovania upozornení môžete nastaviť pre upozornenia aplikácie úroveň dôležitosti od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazovať v hornej časti zoznamu upozornení. \n– Povoliť prerušenia na celú obrazovku. \n– Vždy zobrazovať čiastočne. \n\n"<b>"Úroveň 4"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Vždy zobrazovať čiastočne. \n\n"<b>"Úroveň 3"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n\n"<b>"Úroveň 2"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n– Nikdy nespúšťať zvuk ani vibrácie. \n\n"<b>"Úroveň 1"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n– Nikdy nespúšťať zvuk ani vibrácie. \n– Skryť na uzamknutej obrazovke a v stavovom riadku. \n– Zobraziť v dolnej časti zoznamu upozornení. \n\n"<b>"Úroveň 0"</b>" \n– Blokovať všetky upozornenia z aplikácie."</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Použiť"</string>
@@ -819,7 +804,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Späť"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Ak chcete prehrávať v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblížte sa"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Prehráva sa v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nenájdené"</string>
@@ -907,4 +893,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vyberte používateľa"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikácie spustené na pozadí"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ukončiť"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 1920f04..a8c3545 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Vypnuté"</item>
     <item msgid="2075645297847971154">"Zapnuté"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nedostupné"</item>
+    <item msgid="1909756493418256167">"Vypnuté"</item>
+    <item msgid="4531508423703413340">"Zapnuté"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nie je k dispozícii"</item>
     <item msgid="9103697205127645916">"Vypnuté"</item>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index e497d51..9c2c32c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaklenjen zaslon"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaklenjen zaslon delovnega profila"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Zapri"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi je izklopljen."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi je vklopljen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Način za letalo je izklopljen."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Način za letalo je vklopljen."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"popolna tišina"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"samo alarmi"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ne moti."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Način »ne moti« je izklopljen."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Način »ne moti« je vklopljen."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth je vklopljen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth je izklopljen."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth je vklopljen."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Poročanje o lokaciji je izklopljeno."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Poročanje o lokaciji je vklopljeno."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm je nastavljen čez: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Daljši čas."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Krajši čas."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Svetilka je izklopljena."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Svetilka je vklopljena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Inverzija barv je izklopljena."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Inverzija barv je vklopljena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilna dostopna točka je izklopljena."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilna dostopna točka je vklopljena."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Predvajanje zaslona je ustavljeno."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Varčevanje s podatki je izklopljeno."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Varčevanje s podatki je vklopljeno."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Svetlost zaslona"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Prenos podatkov v mobil. omrežju je zaustavljen"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Prenos podatkov je zaustavljen"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Povezava Wi-Fi ni vzpostavljena"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svetlost"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija barv"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Popravljanje barv"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Več nastavitev"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uporabniške nastavitve"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Končano"</string>
@@ -472,8 +455,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odklenite za uporabo"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Pri pridobivanju kartic je prišlo do težave. Poskusite znova pozneje."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavitve zaklepanja zaslona"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Optično branje kode QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite, če želite optično prebrati kodo QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Dotaknite se za optično branje"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Naslednjega alarma ob <xliff:g id="WHEN">%1$s</xliff:g> ne boste slišali"</string>
@@ -493,6 +477,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Če želite povezati tipkovnico in tablični računalnik, vklopite Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Vklop"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kontrolniki za pomembnost obvestil"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vklopljeno – na podlagi obraza"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"S kontrolniki za pomebnost obvestila je mogoče za obvestila aplikacije nastaviti stopnjo pomembnosti od 0 do 5. \n\n"<b>"Stopnja 5"</b>" \n– Prikaz na vrhu seznama obvestil \n– Omogočanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 4"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 3"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n\n"<b>"Stopnja 2"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n\n"<b>"Stopnja 1"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n– Skrivanje na zaklenjenem zaslonu in v vrstici stanja \n– Prikaz na dnu seznama obvestil \n\n"<b>"Stopnja 0"</b>" \n– Blokiranje vseh obvestil aplikacije"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Končano"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Uporabi"</string>
@@ -819,7 +804,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> izvajalca <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Razveljavi"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Za predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> bolj približajte telefon."</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ni mogoče najti"</string>
@@ -907,4 +893,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izberite uporabnika"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije z izvajanjem v ozadju"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ustavi"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index 924ec58..c09d911 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Izklopljeno"</item>
     <item msgid="2075645297847971154">"Vklopljeno"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Ni na voljo"</item>
+    <item msgid="1909756493418256167">"Izklopljeno"</item>
+    <item msgid="4531508423703413340">"Vklopljeno"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Ni na voljo"</item>
     <item msgid="9103697205127645916">"Izklopljeno"</item>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index d6d5047..993ab0ce 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ekrani i kyçjes."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ekrani i kyçjes së punës"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Mbylle"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi është i çaktivizuar."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi është i aktivizuar."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Modaliteti i aeroplanit është i çaktivizuar."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Modaliteti i aeroplanit është i aktivizuar."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"heshtje e plotë"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"vetëm alarmet"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Mos shqetëso."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Funksioni \"Mos shqetëso\" është çaktivizuar."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\"Mos shqetëso\" është aktivizuar."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth-i."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"\"Bluetooth-i\" është i aktivizuar."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"\"Bluetooth-i\" është i çaktivizuar."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"\"Bluetooth-i\" është i aktivizuar."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Raportimi i vendndodhjes është i aktivizuar."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Raportimi i vendndodhjes është i aktivizuar."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarmi u caktua për në <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Më shumë kohë."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Më pak kohë."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Elektriku u çaktivizua."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Elektriku është i aktivizuar."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Anasjellja e ngjyrës u çaktivizua."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Anasjellja e ngjyrës u aktivizua."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Qasja në zona publike interneti është e çaktivizuar."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona e qasjes publike për internet është e aktivizuar."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmetimi i ekranit ndaloi."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Kursyesi i të dhënave është çaktivizuar."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Kursyesi i të dhënave është aktivizuar."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ndriçimi i ekranit"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Të dhënat celulare janë ndërprerë"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Të dhënat janë ndërprerë"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi nuk është lidhur"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ndriçimi"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Anasjellja e ngjyrës"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korrigjimi i ngjyrës"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Cilësime të tjera"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cilësimet e përdoruesit"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"U krye"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Shkyçe për ta përdorur"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Pati një problem me marrjen e kartave të tua. Provo përsëri më vonë"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cilësimet e ekranit të kyçjes"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skano kodin QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliko për të skanuar një kod QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Profili i punës"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modaliteti i aeroplanit"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nuk do ta dëgjosh alarmin e radhës në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Për të lidhur tastierën me tabletin, në fillim duhet të aktivizosh \"bluetooth-in\"."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivizo"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Kontrollet e njoftimit të energjisë"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Në bazë të fytyrës"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Me kontrollet e njoftimit të energjisë, mund të caktosh një nivel rëndësie nga 0 në 5 për njoftimet e një aplikacioni. \n\n"<b>"Niveli 5"</b>" \n- Shfaq në krye të listës së njoftimeve \n- Lejo ndërprerjen e ekranit të plotë \n- Gjithmonë shfaq shpejt \n\n"<b>"Niveli 4"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Gijthmonë shfaq shpejt \n\n"<b>"Niveli 3"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n\n"<b>"Niveli 2"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n- Asnjëherë mos lësho tingull dhe dridhje \n\n"<b>"Niveli 1"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n- Asnjëherë mos lësho tingull ose dridhje \n- Fshih nga ekrani i kyçjes dhe shiriti i statusit \n- Shfaq në fund të listës së njoftimeve \n\n"<b>"Niveli 0"</b>" \n- Blloko të gjitha njoftimet nga aplikacioni"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"U krye"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Zbato"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="ARTIST_NAME">%2$s</xliff:g> nga <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Zhbëj"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Afrohu për të luajtur në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Po luhet në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nuk u gjet"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zgjidh përdoruesin"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacionet që ekzekutohen në sfond"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ndalo"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index 34879ce..c7e3883 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Joaktive"</item>
     <item msgid="2075645297847971154">"Aktive"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Nuk ofrohet"</item>
+    <item msgid="1909756493418256167">"Joaktiv"</item>
+    <item msgid="4531508423703413340">"Aktiv"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Nuk ofrohet"</item>
     <item msgid="9103697205127645916">"Joaktiv"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index dae3070..f637f2c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Закључан екран."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Закључан екран за посао"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Затвори"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi је искључен."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi је укључен."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Режим рада у авиону је искључен."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Режим рада у авиону је укључен."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"потпуна тишина"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"само аларми"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Не узнемиравај."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Подешавање Не узнемиравај је искључено."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Подешавање Не узнемиравај је укључено."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth је укључен."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth је искључен."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth је укључен."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Извештавање о локацији је искључено."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Извештавање о локацији је укључено."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Аларм је подешен за <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Више времена."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Мање времена."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Батеријска лампа је искључена."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Батеријска лампа је укључена."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Инверзија боја је искључена."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Инверзија боја је укључена."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобилни хотспот је искључен."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобилни хотспот је укључен."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Пребацивање екрана је заустављено."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Уштеда података је искључена."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Уштеда података је укључена."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Осветљеност екрана"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобилни подаци су паузирани"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Подаци су паузирани"</string>
@@ -252,6 +234,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi није повезан"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветљеност"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија боја"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција боја"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Још подешавања"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Корисничка подешавања"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -469,8 +452,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Откључај ради коришћења"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Дошло је до проблема при преузимању картица. Пробајте поново касније"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Подешавања закључаног екрана"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Скенирај QR кôд"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликните да бисте скенирали QR кôд"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR кôд"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Додирните да бисте скенирали"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Нећете чути следећи аларм у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -490,6 +473,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Да бисте повезали тастатуру са таблетом, прво морате да укључите Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Укључи"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Напредне контроле за обавештења"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Укључено – на основу лица"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Помоћу напредних контрола за обавештења можете да подесите ниво важности од 0. до 5. за обавештења апликације. \n\n"<b>"5. ниво"</b>" \n– Приказују се у врху листе обавештења \n- Дозволи прекид режима целог екрана \n– Увек завируј \n\n"<b>"4. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Увек завируј \n\n"<b>"3. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n\n"<b>"2. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n– Никада не производи звук или вибрацију \n\n"<b>"1. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n– Никада не производи звук или вибрацију \n– Сакриј на закључаном екрану и статусној траци \n– Приказују се у дну листе обавештења \n\n"<b>"0. ниво"</b>" \n– Блокирај сва обавештења из апликације"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
@@ -813,7 +797,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Пустите <xliff:g id="SONG_NAME">%1$s</xliff:g> извођача <xliff:g id="ARTIST_NAME">%2$s</xliff:g> из апликације <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пустите <xliff:g id="SONG_NAME">%1$s</xliff:g> из апликације <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Опозови"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Приближите да бисте пуштали музику на: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Пушта се на: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Није пронађено"</string>
@@ -901,4 +886,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изаберите корисника"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Апликације покренуте у позадини"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Заустави"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index 855b84b..fda7465 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Укључено"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Недоступно"</item>
     <item msgid="9103697205127645916">"Искључено"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 668e7d3..04cdcbe 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Låsskärm."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Låsskärm för arbete"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Stäng"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"wifi har inaktiverats."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"wifi har aktiverats."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Flygplansläget har inaktiverats."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Flygplansläget har aktiverats."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"helt tyst"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"endast alarm"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Stör ej."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Stör ej har inaktiverats."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Stör ej har aktiverats."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth på."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth har inaktiverats."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth har aktiverats."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Platsrapporteringen har inaktiverats."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Platsrapporteringen har aktiverats."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarmet ringer <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Längre tid."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Kortare tid."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Ficklampan har inaktiverats."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Ficklampan har aktiverats."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Färginverteringen har inaktiverats."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Färginverteringen har aktiverats."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Den mobila surfzonen har inaktiverats."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Den mobila surfzonen har aktiverats."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Castningen av skärmen har stoppats."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databesparing har inaktiverats."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databesparing har aktiverats."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Skärmens ljusstyrka"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobildata har pausats"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Dataanvändningen har pausats"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Ej ansluten till wifi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Fler inställningar"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Användarinställningar"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås upp för att använda"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Det gick inte att hämta dina kort. Försök igen senare."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Inställningar för låsskärm"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanna QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klicka för att skanna en QR-kod"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-kod"</string>
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nästa alarm, kl. <xliff:g id="WHEN">%1$s</xliff:g>, kommer inte att höras"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Om du vill ansluta tangentbordet till surfplattan måste du först aktivera Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aktivera"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Prioritetsinställningar för aviseringar"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbaserad"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Med aviseringsinställningarna kan du ange prioritetsnivå från 0 till 5 för aviseringar från en app. \n\n"<b>"Nivå 5"</b>" \n– Visa högst upp i aviseringslistan\n– Tillåt avbrott i helskärmsläge \n– Snabbvisa alltid \n\n"<b>"Nivå 4"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa alltid \n\n"<b>"Nivå 3"</b>" \n- Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n\n"<b>"Nivå 2"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n– Aldrig med ljud eller vibration \n\n"<b>"Nivå 1"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n– Aldrig med ljud eller vibration \n– Visa inte på låsskärmen och i statusfältet \n– Visa längst ned i aviseringslistan \n\n"<b>"Nivå 0"</b>" \n– Blockera alla aviseringar från appen"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Klart"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tillämpa"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> med <xliff:g id="ARTIST_NAME">%2$s</xliff:g> från <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> från <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ångra"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Flytta närmare för att spela upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Spelas upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Hittades inte"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Välj användare"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Appar som körs i bakgrunden"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stoppa"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index b071b91..1102698 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Av"</item>
     <item msgid="2075645297847971154">"På"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Inte tillgängligt"</item>
+    <item msgid="1909756493418256167">"Av"</item>
+    <item msgid="4531508423703413340">"På"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Inte tillgängligt"</item>
     <item msgid="9103697205127645916">"Av"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fad086ad..26184849 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Skrini iliyofungwa."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Skrini iliyofungwa ya kazini"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Funga"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi imezimwa."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi imewashwa."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Hali ya ndegeni imezimwa."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Hali ya ndegeni imewashwa."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"kimya kabisa"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"kengele pekee"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Usinisumbue."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Kipengee cha Usinisumbue kimezimwa."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Kipengee cha Usinisumbue kimewashwa."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth imewashwa."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth imezimwa."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth imewashwa."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Programu ya Kuonyesha mahali ulipo imezimwa."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Programu ya Kuonyesha mahali ulipo imewashwa."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Kengele imewashwa na italia <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Muda zaidi."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Muda kidogo"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Tochi imezimwa."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Tochi imewashwa."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Ugeuzaji rangi umezimwa."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Ugeuzaji rangi umewashwa."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mtandaopepe unahamishika umezimwa."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mtandaopepe unaohamishika umewashwa."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Utumaji wa skrini umesitishwa."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Kiokoa Data kimezimwa."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Kiokoa Data kimewashwa."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ung\'aavu wa skrini"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Data ya mtandao wa simu imesitishwa"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Data imesitishwa"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi haijaunganishwa"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangirangi"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Mipangilio zaidi"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mipangilio ya mtumiaji"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Fungua ili utumie"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Hitilafu imetokea wakati wa kuleta kadi zako, tafadhali jaribu tena baadaye"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mipangilio ya kufunga skrini"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Changanua QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Bofya ili uchanganue msimbo wa QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Hutasikia kengele yako inayofuata ya saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Ili uunganishe Kibodi yako kwenye kompyuta yako kibao, lazima kwanza uwashe Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Washa"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Udhibiti wa arifa"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Imewashwa - Inayolenga nyuso"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Ukiwa na udhibiti wa arifa, unaweza kuweka kiwango cha umuhimu wa arifa za programu kuanzia 0 hadi 5. \n\n"<b>"Kiwango cha 5"</b>" \n- Onyesha katika sehemu ya juu ya orodha ya arifa \n- Ruhusu ukatizaji wa skrini nzima \n- Ruhusu arifa za kuchungulia kila wakati\n\n"<b>"Kiwango cha 4"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Ruhusu arifa za kuchungulia kila wakati \n\n"<b>"Kiwango cha 3"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia\n\n"<b>"Kiwango cha 2"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti au mtetemo \n\n"<b>"Kiwango cha 1"</b>" \n- Zuia ukatizaji wa skrini nzima \n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti na mtetemo \n- Usionyeshe skrini iliyofungwa na sehemu ya arifa \n- Onyesha katika sehemu ya chini ya orodha ya arifa \n\n"<b>"Kiwango cha 0"</b>" \n- Zuia arifa zote kutoka programu"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Nimemaliza"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tumia"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> ulioimbwa na <xliff:g id="ARTIST_NAME">%2$s</xliff:g> katika <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> katika <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Tendua"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Sogea karibu ili ucheze kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Inacheza kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Hakipatikani"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chagua mtumiaji"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Programu zinazotumika chinichini"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Simamisha"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index a92c7f9..d186d51 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Kimezimwa"</item>
     <item msgid="2075645297847971154">"Kimewashwa"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Haupatikani"</item>
+    <item msgid="1909756493418256167">"Umezimwa"</item>
+    <item msgid="4531508423703413340">"Umewashwa"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Hakipatikani"</item>
     <item msgid="9103697205127645916">"Kimezimwa"</item>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
index e0b1614..ae89ef4 100644
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -31,7 +31,7 @@
     <bool name="config_use_split_notification_shade">true</bool>
 
     <!-- The number of columns in the QuickSettings -->
-    <integer name="quick_settings_num_columns">2</integer>
+    <integer name="quick_settings_num_columns">3</integer>
 
     <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
     <bool name="config_skinnyNotifsInLandscape">false</bool>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 10912be..e45f072 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"லாக் ஸ்கிரீன்."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"பணி லாக் ஸ்கிரீன்"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"மூடு"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"வைஃபை முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"வைஃபை இயக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"விமானப் பயன்முறை முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"விமானப் பயன்முறை இயக்கப்பட்டது."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"முழு அமைதி"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"அலாரங்கள் மட்டும்"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"தொந்தரவு செய்ய வேண்டாம்."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"’தொந்தரவு செய்ய வேண்டாம்’ அம்சம் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"’தொந்தரவு செய்ய வேண்டாம்’ அம்சம் இயக்கப்பட்டது."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"புளூடூத்."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"புளூடூத் இயக்கத்தில்."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"புளூடூத் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"புளூடூத் இயக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"இருப்பிட அறிக்கையிடல் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"இருப்பிட அறிக்கையிடல் இயக்கப்பட்டது."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> மணிக்கு அலாரம் அமைக்கப்பட்டது."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"நேரத்தை அதிகரி."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"நேரத்தைக் குறை."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ஃபிளாஷ்லைட் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"டார்ச் லைட் எரிகிறது"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"கலர் இன்வெர்ஷன் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"கலர் இன்வெர்ஷன் இயக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"மொபைல் ஹாட்ஸ்பாட் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"மொபைல் ஹாட்ஸ்பாட் இயக்கப்பட்டது."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"திரையை அனுப்புதல் நிறுத்தப்பட்டது."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"டேட்டா சேமிப்பான் முடக்கப்பட்டது."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"டேட்டா சேமிப்பான் இயக்கப்பட்டது."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"திரை பிரகாசம்"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"மொபைல் டேட்டா இடைநிறுத்தப்பட்டுள்ளது"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"தரவு இடைநிறுத்தப்பட்டது"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"வைஃபை இணைக்கப்படவில்லை"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ஒளிர்வு"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"கலர் இன்வெர்ஷன்"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"கலர் கரெக்‌ஷன்"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"அமைப்பில் மாற்று"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"பயனர் அமைப்புகள்"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"முடிந்தது"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"பயன்படுத்துவதற்கு அன்லாக் செய்க"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"உங்கள் கார்டுகளின் விவரங்களைப் பெறுவதில் சிக்கல் ஏற்பட்டது, பிறகு முயலவும்"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"பூட்டுத் திரை அமைப்புகள்"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR குறியீட்டை ஸ்கேன் செய்யுங்கள்"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR குறியீட்டை ஸ்கேன் செய்யக் கிளிக் செய்யவும்"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR குறியீடு"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"ஸ்கேன் செய்யத் தட்டவும்"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"அடுத்த அலாரத்தை <xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு கேட்க மாட்டீர்கள்"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"உங்கள் டேப்லெட்டுடன் கீபோர்டை இணைக்க, முதலில் புளூடூத்தை இயக்க வேண்டும்."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"இயக்கு"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ஆற்றல்மிக்க அறிவிப்புக் கட்டுப்பாடுகள்"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ஆன் - முகம் அடிப்படையிலானது"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ஆற்றல்மிக்க அறிவிப்புக் கட்டுப்பாடுகள் மூலம், ஆப்ஸின் அறிவிப்புகளுக்கு முக்கியத்துவ நிலையை (0-5) அமைக்கலாம். \n\n"<b>"நிலை 5"</b>" \n- அறிவிப்புப் பட்டியலின் மேலே காட்டும் \n- முழுத் திரைக் குறுக்கீட்டை அனுமதிக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 4"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 3"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n\n"<b>"நிலை 2"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது, அதிர்வுறாது \n\n"<b>"நிலை 1"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது அல்லது அதிர்வுறாது \n- லாக் ஸ்கிரீன் மற்றும் நிலைப்பட்டியிலிருந்து மறைக்கும் \n- அறிவிப்புகள் பட்டியலின் கீழே காட்டும் \n\n"<b>"நிலை 0"</b>" \n- ஆப்ஸின் எல்லா அறிவிப்புகளையும் தடுக்கும்"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"முடிந்தது"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"பயன்படுத்து"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> இன் <xliff:g id="SONG_NAME">%1$s</xliff:g> பாடலை <xliff:g id="APP_LABEL">%3$s</xliff:g> ஆப்ஸில் பிளேசெய்"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> பாடலை <xliff:g id="APP_LABEL">%2$s</xliff:g> ஆப்ஸில் பிளேசெய்"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"செயல்தவிர்"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் பிளே செய்ய உங்கள் சாதனத்தை அருகில் எடுத்துச் செல்லவும்"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் பிளே ஆகிறது"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"இல்லை"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"பயனரைத் தேர்வுசெய்க"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"பின்னணியில் இயங்கும் ஆப்ஸ்"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"நிறுத்து"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index f3ba1a1..0883d22 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"இயக்கப்பட்டுள்ளது"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"கிடைக்கவில்லை"</item>
     <item msgid="9103697205127645916">"முடக்கப்பட்டுள்ளது"</item>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index abba928..4e41023 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"లాక్ స్క్రీన్."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"కార్యాలయ లాక్ స్క్రీన్"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"మూసివేస్తుంది"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"వైఫై ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"వైఫై ఆన్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ఎయిర్‌ప్లేన్ మోడ్ ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ఎయిర్‌ప్లేన్ మోడ్ ఆన్ చేయబడింది."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"మొత్తం నిశ్శబ్దం"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"అలారాలు మాత్రమే"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"అంతరాయం కలిగించవద్దు."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'అంతరాయం కలిగించవద్దు\' ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"బ్లూటూత్."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"బ్లూటూత్ ఆన్‌లో ఉంది."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"బ్లూటూత్ ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"బ్లూటూత్ ఆన్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"లొకేషన్ రిపోర్టింగ్ ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"లొకేషన్ రిపోర్టింగ్ ఆన్ చేయబడింది."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g>కి అలారం సెట్ చేయబడింది."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"ఎక్కువ సమయం."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"తక్కువ సమయం."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ఫ్లాష్‌లైట్ ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"ఫ్లాష్‌లైట్ ఆన్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"రంగు విలోమం ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"రంగు విలోమం ఆన్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"మొబైల్ హాట్‌స్పాట్ ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"మొబైల్ హాట్‌స్పాట్ ఆన్ చేయబడింది."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"స్క్రీన్ ప్రసారం ఆపివేయబడింది."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"డేటా సేవర్ ఆఫ్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"డేటా సేవర్ ఆన్ చేయబడింది."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ప్రదర్శన ప్రకాశం"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"మొబైల్ డేటా పాజ్ చేయబడింది"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"డేటా పాజ్ చేయబడింది"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi కనెక్ట్ కాలేదు"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ప్రకాశం"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"కలర్ మార్పిడి"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"కలర్ కరెక్షన్"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"మరిన్ని సెట్టింగ్‌లు"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"యూజర్ సెట్టింగ్‌లు"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"పూర్తయింది"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ఉపయోగించడానికి అన్‌లాక్ చేయండి"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"మీ కార్డ్‌లను పొందడంలో సమస్య ఉంది, దయచేసి తర్వాత మళ్లీ ట్రై చేయండి"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"లాక్ స్క్రీన్ సెట్టింగ్‌లు"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QRను స్కాన్ చేయండి"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR కోడ్‌ను స్కాన్ చేయడానికి క్లిక్ చేయండి"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR కోడ్"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"స్కాన్ చేయడానికి ట్యాప్ చేయండి"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్‌"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ఎయిర్‌ప్లేన్ మోడ్"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీకు వినిపించదు"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"మీ కీబోర్డ్‌ను మీ టాబ్లెట్‌తో కనెక్ట్ చేయడానికి, మీరు ముందుగా బ్లూటూత్ ఆన్ చేయాలి."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"ఆన్ చేయి"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"పవర్ నోటిఫికేషన్ నియంత్రణలు"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"\'ముఖం ఆధారం\'ను - ఆన్ చేయండి"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"పవర్ నోటిఫికేషన్ కంట్రోల్స్ సాయంతో, మీరు యాప్ నోటిఫికేషన్‌లకు ప్రాముఖ్యతా స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ లిస్ట్‌ పైభాగంలో చూపబడతాయి \n- ఫుల్-స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ క్విక్ వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- ఫుల్-స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ క్విక్ వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- ఫుల్-స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ క్విక్ వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- ఫుల్-స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ క్విక్ వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- ఫుల్-స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ క్విక్ వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్, స్టేటస్ బార్‌ల నుండి దాచబడతాయి \n- నోటిఫికేషన్ లిస్ట్‌ దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్‌లు బ్లాక్ చేయబడతాయి"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string>
@@ -651,7 +635,7 @@
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్‌లను తెరవండి."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"శీఘ్ర సెట్టింగ్‌లను తెరవండి."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"శీఘ్ర సెట్టింగ్‌లను మూసివేయండి."</string>
-    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేశారు"</string>
+    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> లాగా సైన్ ఇన్ చేశారు"</string>
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"యూజర్‌ను ఎంపిక చేయండి"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ఇంటర్నెట్ లేదు"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"వివరాలను తెరవండి."</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> నుండి <xliff:g id="ARTIST_NAME">%2$s</xliff:g> పాడిన <xliff:g id="SONG_NAME">%1$s</xliff:g>‌ను ప్లే చేయండి"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> నుండి <xliff:g id="SONG_NAME">%1$s</xliff:g>‌ను ప్లే చేయండి"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"చర్య రద్దు"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే చేయడానికి దగ్గరగా వెళ్లండి"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే అవుతోంది"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ఇన్‌యాక్టివ్, యాప్ చెక్ చేయండి"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"కనుగొనబడలేదు"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"యూజర్‌ను ఎంచుకోండి"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"యాప్‌లు బ్యాక్‌గ్రౌండ్‌లో రన్ అవుతున్నాయి"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ఆపివేయండి"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index bed919f..5c8ae3d 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"ఆన్‌లో ఉంది"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"అందుబాటులో లేదు"</item>
     <item msgid="9103697205127645916">"ఆఫ్‌లో ఉంది"</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index a586f76f..8897186 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ล็อกหน้าจอ"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"หน้าจอล็อกของโปรไฟล์งาน"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ปิด"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ปิด Wi-Fi แล้ว"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"เปิด Wi-Fi แล้ว"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ปิดโหมดบนเครื่องบินแล้ว"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"เปิดโหมดบนเครื่องบินแล้ว"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ปิดเสียงทั้งหมด"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"เฉพาะปลุกเท่านั้น"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ห้ามรบกวน"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"โหมดห้ามรบกวนปิดอยู่"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"เปิดโหมดห้ามรบกวนแล้ว"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"บลูทูธ"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"บลูทูธเปิดอยู่"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"ปิดบลูทูธแล้ว"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"เปิดบลูทูธแล้ว"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"ปิดการรายงานตำแหน่งแล้ว"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"เปิดการรายงานตำแหน่งแล้ว"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"เวลามากขึ้น"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"เวลาน้อยลง"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"ปิดไฟฉายแล้ว"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"เปิดไฟฉายแล้ว"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"ปิดการกลับสีแล้ว"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"เปิดการกลับสีแล้ว"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ปิดฮอตสปอตเคลื่อนที่แล้ว"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"เปิดฮอตสปอตเคลื่อนที่แล้ว"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"หยุดการส่งหน้าจอแล้ว"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ปิดโปรแกรมประหยัดอินเทอร์เน็ตแล้ว"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"เปิดโปรแกรมประหยัดอินเทอร์เน็ตแล้ว"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ความสว่างของหน้าจอ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"หยุดการใช้อินเทอร์เน็ตมือถือชั่วคราว"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"หยุดการใช้ข้อมูลชั่วคราวแล้ว"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ความสว่าง"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"การกลับสี"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"การแก้สี"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"การตั้งค่าเพิ่มเติม"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"การตั้งค่าของผู้ใช้"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"เสร็จสิ้น"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ปลดล็อกเพื่อใช้"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"เกิดปัญหาในการดึงข้อมูลบัตรของคุณ โปรดลองอีกครั้งในภายหลัง"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"การตั้งค่าหน้าจอล็อก"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"สแกนคิวอาร์"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"คลิกเพื่อสแกนคิวอาร์โค้ด"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"คิวอาร์โค้ด"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"แตะเพื่อสแกน"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"คุณจะไม่ได้ยินเสียงปลุกครั้งถัดไปในเวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"หากต้องการเชื่อมต่อแป้นพิมพ์กับแท็บเล็ต คุณต้องเปิดบลูทูธก่อน"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"เปิด"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"ส่วนควบคุมการแจ้งเตือนแบบเปิด/ปิด"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"เปิด - ตามใบหน้า"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"ส่วนควบคุมการแจ้งเตือนแบบเปิด/ปิดช่วยให้คุณตั้งค่าระดับความสำคัญสำหรับการแจ้งเตือนของแอปได้ตั้งแต่ระดับ 0-5 \n\n"<b>"ระดับ 5"</b>" \n- แสดงที่ด้านบนของรายการแจ้งเตือน \n- อนุญาตให้รบกวนแบบเต็มหน้าจอ \n- อนุญาตให้แสดงชั่วครู่ \n\n"<b>"ระดับ 4"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- แสดงชั่วครู่เสมอ \n\n"<b>"ระดับ 3"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- ไม่แสดงชั่วครู่เลย \n\n"<b>"ระดับ 2"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- ไม่แสดงชั่วครู่เลย \n- ไม่ส่งเสียงหรือสั่นเลย \n\n"<b>"ระดับ 1"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- ไม่แสดงชั่วครู่เลย \n- ไม่ส่งเสียงหรือสั่นเลย \n- ซ่อนจากหน้าจอล็อกและแถบสถานะ \n- แสดงที่ด้านล่างของรายการแจ้งเตือน \n\n"<b>"ระดับ 0"</b>" \n- บล็อกการแจ้งเตือนทั้งหมดจากแอป"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"เสร็จสิ้น"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ใช้"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> ของ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> จาก <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> จาก <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"เลิกทำ"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"ขยับเข้ามาใกล้ขึ้นเพื่อเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"กำลังเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ไม่มีการใช้งาน โปรดตรวจสอบแอป"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ไม่พบ"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"เลือกผู้ใช้"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"แอปที่ทำงานอยู่เบื้องหลัง"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"หยุด"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index d5591b2..e7eae73 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"เปิด"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ไม่พร้อมใช้งาน"</item>
     <item msgid="9103697205127645916">"ปิด"</item>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 42eed44..6ab6ef66 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lock screen."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Lock screen sa trabaho"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Isara"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Na-off ang wifi."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Na-on ang wifi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Na-off ang Airplane mode."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Na-on ang Airplane mode."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ganap na katahimikan"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"mga alarm lang"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Huwag Istorbohin."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Na-off ang Huwag Istorbohin."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Na-on ang Huwag Istorbohin."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Naka-on ang Bluetooth."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Na-off ang Bluetooth."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Na-on ang Bluetooth."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Na-off ang pag-uulat ng lokasyon."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Na-on ang pag-uulat ng lokasyon."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Nakatakda ang alarm nang <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Higit pang oras."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mas kaunting oras."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Na-off ang flashlight."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Na-on ang flashlight."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Na-off ang pag-invert ng kulay."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Na-on ang pag-invert ng kulay."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Na-off ang mobile hotspot."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Na-on ang mobile hotspot."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Itinigil ang pagka-cast sa screen."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Na-off ang Data Saver."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Na-on ang Data Saver."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Liwanag ng display"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Naka-pause ang mobile data"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Naka-pause ang data"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Hindi nakakonekta sa Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Pag-invert ng kulay"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pagtatama ng kulay"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Higit pang setting"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mga setting ng user"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Tapos na"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"I-unlock para magamit"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Nagkaproblema sa pagkuha ng iyong mga card, pakisubukan ulit sa ibang pagkakataon"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mga setting ng lock screen"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"I-scan ang QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Mag-click para mag-scan ng QR code"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Hindi mo maririnig ang iyong susunod na alarm ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Upang ikonekta ang iyong keyboard sa iyong tablet, kailangan mo munang i-on ang Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"I-on"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Mga kontrol sa notification ng power"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Naka-on - Batay sa mukha"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Sa pamamagitan ng mga kontrol sa notification ng power, magagawa mong itakda ang antas ng kahalagahan ng mga notification ng isang app mula 0 hanggang 5. \n\n"<b>"Antas 5"</b>" \n- Ipakita sa itaas ng listahan ng notification \n- Payagan ang pag-istorbo kapag full screen \n- Palaging sumilip \n\n"<b>"Antas 4"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Palaging sumilip \n\n"<b>"Antas 3"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Huwag kailanman sumilip \n\n"<b>"Antas 2"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Huwag kailanman sumilip \n- Huwag kailanman tumunog o mag-vibrate \n\n"<b>"Antas 1"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Huwag kailanman sumilip \n- Huwag kailanman tumunog o mag-vibrate \n- Itago sa lock screen at status bar \n- Ipakita sa ibaba ng listahan ng notification \n\n"<b>"Antas 0"</b>" \n- I-block ang lahat ng notification mula sa app"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Tapos na"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Ilapat"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> ni/ng <xliff:g id="ARTIST_NAME">%2$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"I-undo"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Lumapit pa para mag-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Nagpe-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Hindi nahanap"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pumili ng user"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Mga app na tumatakbo sa background"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ihinto"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index c601d2f..f33d8a2 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Naka-off"</item>
     <item msgid="2075645297847971154">"Naka-on"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Hindi available"</item>
+    <item msgid="1909756493418256167">"Naka-off"</item>
+    <item msgid="4531508423703413340">"Naka-on"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Hindi available"</item>
     <item msgid="9103697205127645916">"Naka-off"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f79420e..ee44b76 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kilit ekranı"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"İş profili kilit ekranı"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Kapat"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Kablosuz kapatıldı."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Kablosuz açıldı."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Uçak modu kapatıldı."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Uçak modu açıldı."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"tamamen sessiz"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"yalnızca alarmlar"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Rahatsız Etmeyin."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Rahatsız Etmeyin ayarı kapalı."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Rahatsız Etmeyin ayarı açık."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth açık."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth kapatıldı."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth açıldı."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Konum Bildirme kapatıldı."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Konum Bildirme açıldı."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarm saati: <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Daha uzun süre."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Daha kısa süre."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"El feneri kapatıldı."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"El feneri açıldı."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Renkleri ters çevirme işlevi kapatıldı."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Renkleri ters çevirme işlevi açıldı."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil hotspot kapatıldı."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil hotspot açıldı."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekran yayını durduruldu."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Veri Tasarrufu kapatıldı."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Veri Tasarrufu açıldı."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ekran parlaklığı"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobil veri duraklatıldı"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Veri kullanımı duraklatıldı"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Kablosuz ağ bağlı değil"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaklık"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rengi ters çevirme"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Renk düzeltme"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Diğer ayarlar"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kullanıcı ayarları"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Bitti"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Kullanmak için kilidi aç"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Kartlarınız alınırken bir sorun oluştu. Lütfen daha sonra tekrar deneyin"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilit ekranı ayarları"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodunu tarayın"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodu taramak için tıklayın"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> olarak ayarlanmış bir sonraki alarmınızı duymayacaksınız"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Klavyenizi tabletinize bağlamak için önce Bluetooth\'u açmanız gerekir."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Aç"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Güç bildirim kontrolleri"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Açık - Yüze göre"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Güç bildirim kontrolleriyle, bir uygulamanın bildirimleri için 0 ile 5 arasında bir önem düzeyi ayarlayabilirsiniz. \n\n"<b>"5. Düzey"</b>" \n- Bildirim listesinin en üstünde gösterilsin \n- Tam ekran kesintisine izin verilsin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"4. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"3. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n\n"<b>"2. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman belirmesin \n- Hiçbir zaman ses çıkarmasın ve titreştirmesin \n\n"<b>"1. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n- Hiçbir zaman ses çıkarmasın veya titreştirmesin \n- Kilit ekranından ve durum çubuğundan gizlensin \n- Bildirim listesinin en altında gösterilsin \n\n"<b>"0. Düzey"</b>" \n- Uygulamadan gelen tüm bildirimler engellensin"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Bitti"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Uygula"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> uygulamasından <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> uygulamasından <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Geri al"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında çalmak için yaklaşın"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında çalınıyor"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Bulunamadı"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kullanıcı seçin"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Arka planda çalışan uygulamalar"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Durdur"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 17ce6b3..17b4bb4 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Kapalı"</item>
     <item msgid="2075645297847971154">"Açık"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Bilinmiyor"</item>
+    <item msgid="1909756493418256167">"Kapalı"</item>
+    <item msgid="4531508423703413340">"Açık"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Kullanılamıyor"</item>
     <item msgid="9103697205127645916">"Kapalı"</item>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 17a3ce8..27aad2e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Заблокований екран."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Екран блокування завдання"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Закрити"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi вимкнено."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi увімкнено."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Режим польоту вимкнено."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Режим польоту ввімкнено."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"без сигналів"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"лише будильники"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Не турбувати."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Режим \"Не турбувати\" вимкнено."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Режим \"Не турбувати\" ввімкнено."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth увімк."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth вимкнено."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth увімкнено."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Надсилання геоданих вимкнено."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Надсилання геоданих увімкнено."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Сигнал установлено на <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Більше часу."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Менше часу."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Ліхтарик вимкнено."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Ліхтарик увімкнено."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Інверсію кольорів вимкнено."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Інверсію кольорів увімкнено."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Мобільну точку доступу вимкнено."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Мобільну точку доступу ввімкнено."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Трансляцію екрана зупинено."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Заощадження трафіку вимкнено."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Заощадження трафіку ввімкнено."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Яскравість дисплея"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобільне передавання даних призупинено"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Передавання даних призупинено"</string>
@@ -253,6 +235,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi не під’єднано"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яскравість"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія кольорів"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекція кольору"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Більше налаштувань"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налаштування користувача"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -472,8 +455,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Розблокувати, щоб використовувати"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Не вдалось отримати ваші картки. Повторіть спробу пізніше."</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Параметри блокування екрана"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканувати QR-код"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Натисніть, щоб відсканувати QR-код"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Наступний сигнал о <xliff:g id="WHEN">%1$s</xliff:g> не пролунає"</string>
@@ -493,6 +478,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Щоб під’єднати клавіатуру до планшета, спершу потрібно ввімкнути Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Увімкнути"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Елементи керування сповіщеннями"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Увімкнути (за обличчям)"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"За допомогою елементів керування сповіщеннями ви можете налаштувати пріоритет сповіщень додатка – від 0 до 5 рівня. \n\n"<b>"Рівень 5"</b>\n"- Показувати сповіщення вгорі списку \n- Виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 4"</b>\n"- Не виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 3"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n\n"<b>"Рівень 2"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n\n"<b>"Рівень 1"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n- Не показувати на заблокованому екрані та в рядку стану \n- Показувати сповіщення внизу списку \n\n"<b>"Рівень 0"</b>\n"- Блокувати всі сповіщення з додатка"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Застосувати"</string>
@@ -819,7 +805,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Увімкнути пісню \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\", яку виконує <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, у додатку <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Увімкнути пісню \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" у додатку <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Відмінити"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Щоб відтворити на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>, наблизьтеся до нього"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Відтворюється на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Не знайдено"</string>
@@ -907,4 +894,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Виберіть користувача"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Додатки, що працюють у фоновому режимі"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Зупинити"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 6f9ef21..c4ac1949 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"Увімкнено"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Недоступно"</item>
     <item msgid="9103697205127645916">"Вимкнено"</item>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 350aaba..015d520 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"مقفل اسکرین۔"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"دفتری مقفل اسکرین"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"بند کریں"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"‏Wifi کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"‏Wifi کو آن کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ہوائی جہاز وضع کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ہوائی جہاز وضع کو آن کر دیا گیا۔"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"مکمل خاموشی"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"صرف الارمز"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ڈسٹرب نہ کریں۔"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\'ڈسٹرب نہ کریں\' کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"\'ڈسٹرب نہ کریں\' کو آن کر دیا گیا۔"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"بلوٹوتھ۔"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"بلوٹوتھ آن ہے۔"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"بلوٹوتھ کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"بلوٹوتھ کو آن کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"مقام کی اطلاع دہندگی کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"مقام کی اطلاع دہندگی کو آن کر دیا گیا۔"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"الارم <xliff:g id="TIME">%s</xliff:g> کیلئے سیٹ ہے۔"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"مزید وقت۔"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"کم وقت۔"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"فلیش لائٹ کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"فلیش لائٹ کو آن کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"رنگ کی تبدیلی کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"رنگ کی تبدیلی کو آن کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"موبائل ہاٹ اسپاٹ کو آف کر دیا گیا۔"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"موبائل ہاٹ اسپاٹ کو آن کر دیا گیا۔"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"اسکرین کو کاسٹ کرنا بند کر دیا۔"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ڈیٹا سیور آف ہو گیا۔"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ڈیٹا سرور آن ہو گیا۔"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"ڈسپلے کی چمک"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"موبائل ڈیٹا موقوف کر دیا گیا ہے"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"ڈیٹا موقوف کر دیا گیا"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏Wi-Fi سے منسلک نہیں ہے"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"چمکیلا پن"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"رنگوں کی تقلیب"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"رنگ کی اصلاح"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"مزید ترتیبات"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"صارف کی ترتیبات"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ہو گیا"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"استعمال کرنے کے لیے غیر مقفل کریں"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"آپ کے کارڈز حاصل کرنے میں ایک مسئلہ درپیش تھا، براہ کرم بعد میں دوبارہ کوشش کریں"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"مقفل اسکرین کی ترتیبات"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"‏QR اسکین کریں"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"‏QR کوڈ اسکین کرنے کے لیے کلک کریں"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"‏QR کوڈ"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"اسکین کرنے کے لیے تھپتھپائیں"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"آپ کو <xliff:g id="WHEN">%1$s</xliff:g> بجے اپنا اگلا الارم سنائی نہیں دے گا"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"اپنے کی بورڈ کو اپنے ٹیبلٹ کے ساتھ منسلک کرنے کیلئے پہلے آپ کو اپنا بلو ٹوتھ آن کرنا ہو گا۔"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"آن کریں"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"پاور اطلاع کے کنٹرولز"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"آن - چہرے پر مبنی"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"پاور اطلاع کنٹرولز کے ساتھ آپ کسی ایپ کی اطلاعات کیلئے 0 سے 5 تک اہمیت کی سطح سیٹ کر سکتے ہیں۔ \n\n"<b>"سطح 5"</b>\n"- اطلاعات کی فہرست کے اوپر دکھائیں \n- پوری اسکرین کی مداخلت کی اجازت دیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 4"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 3"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n\n"<b>"سطح 2"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n- کبھی آواز اور ارتعاش پیدا نہ کرنا \n\n"<b>" سطح 1"</b>\n"- پوری اسکرین کی مداخلت کو روکنا \n- کبھی نہ جھانکنا \n- کبھی بھی آواز یا ارتعاش پیدا نہ کرنا\n- مقفل اسکرین اور اسٹیٹس بار سے چھپانا \n - اطلاع کی فہرست کی نیچے دکھانا \n\n"<b>"سطح 0"</b>\n"- ایپ سے تمام اطلاعات مسدود کریں"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ہو گیا"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"لاگو کریں"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> سے <xliff:g id="ARTIST_NAME">%2$s</xliff:g> کا <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> سے <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"کالعدم کریں"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چلانے کے لیے قریب کریں"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چل رہا ہے"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"نہیں ملا"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"صارف منتخب کریں"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"ایپس پس منظر میں چل رہی ہیں"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"روکیں"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index 3284fc5..1554031 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"آن"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"دستیاب نہیں ہے"</item>
     <item msgid="9103697205127645916">"آف ہے"</item>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 277f1c3..ce7fc32 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Qulflash ekrani."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ishchi ekran qulfi"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Yopish"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi o‘chirildi."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi yoqildi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Parvoz rejimi o‘chirildi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Parvoz rejimi yoqildi."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"jimjitlik"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"faqat signallar"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Bezovta qilinmasin."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Bezovta qilinmasin funksiyasi faolsizlantirildi."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Bezovta qilinmasin rejimi yoqildi."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth yoqilgan."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth o‘chirildi."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth yoqildi."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Joylashuv ma’lumotini yuborish o‘chirildi."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Joylashuv ma’lumotini yuborish yoqildi."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Signal <xliff:g id="TIME">%s</xliff:g> da chalinadi."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Ko‘proq vaqt."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Kamroq vaqt."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Fonar o‘chirildi."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Fonar yoqildi."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Ranglarni akslantirish o‘chirildi."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Ranglarni akslantirish yoqildi."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil ulanish nuqtasi o‘chirildi."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil ulanish nuqtasi yoqildi."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekranni translatsiya qilish to‘xtadi."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Trafik tejash rejimi o‘chirib qo‘yildi."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Trafik tejash rejimi yoqildi."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Ekran yorqinligi"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Mobil internet pauza qilindi"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Internetdan foydalanish to‘xtatib qo‘yildi"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi tarmoqqa ulanmagan"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Yorqinlik"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ranglarni akslantirish"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ranglarni tuzatish"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Boshqa sozlamalar"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Foydalanuvchi sozlamalari"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Tayyor"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Foydalanish uchun qulfdan chiqarish"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Bildirgilarni yuklashda xatolik yuz berdi, keyinroq qaytadan urining"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Qulflangan ekran sozlamalari"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodni skanerlash"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodni skanerlash uchun bosing"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Keyingi signal (<xliff:g id="WHEN">%1$s</xliff:g>) chalinmaydi"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Klaviaturani planshetingizga ulash uchun Bluetooth xizmatini yoqishingiz kerak."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Yoqish"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Bildirishnomalar uchun kengaytirilgan boshqaruv"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Yoqish - Yuz asosida"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Bildirishnomalar uchun kengaytirilgan boshqaruv yordamida ilova bildirishnomalarining muhimlik darajasini (0-5) sozlash mumkin. \n\n"<b>"5-daraja"</b>" \n- Bildirishnomani ro‘yxatning boshida ko‘rsatish \n- To‘liq ekranli bildirishnomalarni ko‘rsatish \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"4-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"3-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n\n"<b>"2-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n\n"<b>"1-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n- Ekran qulfi va holat qatorida ko‘rsatmaslik \n- Bildirishnomani ro‘yxatning oxirida ko‘rsatish \n\n"<b>"0-daraja"</b>" \n- Ilovadan keladigan barcha bildirishnomalarni bloklash"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Tayyor"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Joriy qilish"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ilovasida ijro etish: <xliff:g id="SONG_NAME">%1$s</xliff:g> – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ilovasida ijro etilmoqda: <xliff:g id="SONG_NAME">%1$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Qaytarish"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>da ijro etish uchun yaqinroq keling"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> qurilmasida ijro qilinmoqda"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nofaol. Ilovani tekshiring"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Topilmadi"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Foydalanuvchini tanlang"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Orqa fonda ishlayotgan ilovalar"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index 47bcf4db..52a8b0a 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Oʻchiq"</item>
     <item msgid="2075645297847971154">"Yoniq"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Ishlamaydi"</item>
+    <item msgid="1909756493418256167">"Yoqilmagan"</item>
+    <item msgid="4531508423703413340">"Yoniq"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Ishlamaydi"</item>
     <item msgid="9103697205127645916">"Oʻchiq"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 1c1557e..e4066d9e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Màn hình khóa."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Màn hình khóa công việc"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Đóng"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Đã tắt Wifi."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Đã bật Wifi."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Đã tắt chế độ trên máy bay."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Đã bật chế độ trên máy bay."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"hoàn toàn tắt tiếng"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"chỉ chuông báo"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Không làm phiền."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Đã tắt chế độ Không làm phiền."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Đã bật tính năng Không làm phiền."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth bật."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Đã tắt Bluetooth."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Đã bật Bluetooth."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Đã tắt Báo cáo vị trí."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Đã bật Báo cáo vị trí."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Báo thức được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Nhiều thời gian hơn."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Ít thời gian hơn."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Đã tắt đèn pin."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Đã bật đèn pin."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Đã tắt đảo màu."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Đã bật đảo màu."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Đã tắt điểm phát sóng di động."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Đã bật điểm phát sóng di động."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Đã ngừng truyền màn hình."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Đã tắt Trình tiết kiệm dữ liệu."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Đã bật Trình tiết kiệm dữ liệu."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Độ sáng màn hình"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Dữ liệu di động đã bị tạm dừng"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Đã tạm dừng dữ liệu"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Chưa kết nối với Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Độ sáng"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đảo màu"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Chỉnh màu"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Chế độ cài đặt khác"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cài đặt người dùng"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Xong"</string>
@@ -466,8 +449,10 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Mở khóa để sử dụng"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Đã xảy ra sự cố khi tải thẻ của bạn. Vui lòng thử lại sau"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cài đặt màn hình khóa"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Quét mã QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Nhấp để quét mã QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ máy bay"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy báo thức tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
@@ -487,6 +472,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Để kết nối bàn phím với máy tính bảng, trước tiên, bạn phải bật Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Bật"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Điều khiển thông báo nguồn"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Đang bật – Dựa trên khuôn mặt"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Với các kiểm soát thông báo nguồn, bạn có thể đặt cấp độ quan trọng từ 0 đến 5 cho các thông báo của ứng dụng. \n\n"<b>"Cấp 5"</b>" \n- Hiển thị ở đầu danh sách thông báo \n- Cho phép gián đoạn ở chế độ toàn màn hình \n- Luôn xem nhanh \n\n"<b>"Cấp 4"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Luôn xem nhanh \n\n"<b>"Cấp 3"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n\n"<b>"Cấp 2"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n- Không bao giờ có âm báo và rung \n\n"<b>"Cấp 1"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n- Không bao giờ có âm báo và rung \n- Ẩn khỏi màn hình khóa và thanh trạng thái \n- Hiển thị ở cuối danh sách thông báo \n\n"<b>"Cấp 0"</b>" \n- Chặn tất cả các thông báo từ ứng dụng"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Xong"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Áp dụng"</string>
@@ -807,7 +793,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> của <xliff:g id="ARTIST_NAME">%2$s</xliff:g> trên <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> trên <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Hủy"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Đưa thiết bị đến gần hơn để phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Đang phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Không tìm thấy"</string>
@@ -895,4 +882,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chọn người dùng"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Các ứng dụng chạy trong nền"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Dừng"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index ecc3223..94e8012 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Đang tắt"</item>
     <item msgid="2075645297847971154">"Đang bật"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Không có"</item>
+    <item msgid="1909756493418256167">"Đang tắt"</item>
+    <item msgid="4531508423703413340">"Đang bật"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Không hoạt động"</item>
     <item msgid="9103697205127645916">"Đang tắt"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index efd24bc..4666a88 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"锁定屏幕。"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"工作锁定屏幕"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"关闭"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WLAN已关闭。"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WLAN已开启。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"飞行模式已关闭。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"飞行模式已开启。"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"完全静音"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"仅限闹钟"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"勿扰。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"勿扰模式已关闭。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"勿扰模式已开启。"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"蓝牙。"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"蓝牙开启。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"蓝牙已关闭。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"蓝牙已开启。"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"位置报告功能已关闭。"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"位置报告功能已开启。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"闹钟已设置为:<xliff:g id="TIME">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"延长时间。"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"缩短时间。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"手电筒已关闭。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"手电筒已打开。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"颜色反转功能已关闭。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"颜色反转功能已开启。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"移动热点已关闭。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"移动热点已开启。"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"屏幕投射已停止。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"流量节省程序已关闭。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"流量节省程序已开启。"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"屏幕亮度"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"已暂停使用移动数据网络"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"数据网络已暂停使用"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未连接到 WLAN 网络"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"颜色反转"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"更多设置"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"用户设置"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解锁设备即可使用"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"获取您的卡片时出现问题,请稍后重试"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"锁定屏幕设置"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"扫描二维码"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"点击即可扫描二维码"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"二维码"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"点按即可扫描"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"您在<xliff:g id="WHEN">%1$s</xliff:g>将不会听到下次闹钟响铃"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"要将您的键盘连接到平板电脑,您必须先开启蓝牙。"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"开启"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"高级通知设置"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已开启 - 基于人脸"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"利用高级通知设置,您可以为应用通知设置从 0 级到 5 级的重要程度等级。\n\n"<b>"5 级"</b>" \n- 在通知列表顶部显示 \n- 允许全屏打扰 \n- 一律短暂显示通知 \n\n"<b>"4 级"</b>" \n- 禁止全屏打扰 \n- 一律短暂显示通知 \n\n"<b>"3 级"</b>" \n- 禁止全屏打扰 \n- 一律不短暂显示通知 \n\n"<b>"2 级"</b>" \n- 禁止全屏打扰 \n- 一律不短暂显示通知 \n- 一律不发出声音或振动 \n\n"<b>"1 级"</b>" \n- 禁止全屏打扰 \n- 一律不短暂显示通知 \n- 一律不发出声音或振动 \n- 不在锁定屏幕和状态栏中显示 \n- 在通知列表底部显示 \n\n"<b>"0 级"</b>" \n- 屏蔽应用的所有通知"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"应用"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"通过<xliff:g id="APP_LABEL">%3$s</xliff:g>播放<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"通过<xliff:g id="APP_LABEL">%2$s</xliff:g>播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"撤消"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"移近一点以在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"正在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"未找到"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"正在在后台运行的应用"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 43de98e..a266d92 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"开启"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"不可用"</item>
     <item msgid="9103697205127645916">"已关闭"</item>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index e7cb583..f64e78c 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"上鎖畫面。"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"工作螢幕鎖定"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"關閉"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi 已關閉。"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi 已開啟。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"飛行模式已關閉。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"飛行模式已開啟。"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"完全靜音"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"只限鬧鐘"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"請勿騷擾。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"閂咗「請勿騷擾」。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"已開咗「請勿騷擾」。"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"藍牙。"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"藍牙已開啟。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"藍牙已關閉。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"藍牙已開啟。"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"位置報告已關閉。"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"位置報告已開啟。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"鬧鐘已設定為:<xliff:g id="TIME">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"增加時間。"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"減少時間。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"閃光燈已關閉。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"閃光燈已開啟。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"色彩反轉模式已關閉。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"色彩反轉模式已開啟。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"流動熱點已關閉。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"流動熱點已開啟。"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"已停止投放螢幕。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"已關閉數據節省模式。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"已開啟數據節省模式。"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"顯示光暗度"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"已暫停使用流動數據"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"已暫停使用數據"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未連線至 Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"更多設定"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -466,8 +449,8 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"擷取資訊卡時發生問題,請稍後再試。"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"上鎖畫面設定"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"掃瞄 QR 碼"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"按一下即可掃瞄 QR 碼"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR 碼"</string>
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"輕按即可掃瞄"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"您不會<xliff:g id="WHEN">%1$s</xliff:g>聽到鬧鐘"</string>
@@ -487,6 +470,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"如要將鍵盤連接至平板電腦,請先開啟藍牙。"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"開啟"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"通知控制項"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 根據面孔偵測"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"通知控制項讓您設定應用程式通知的重要性 (0 至 5 級)。\n\n"<b>"第 5 級"</b>" \n- 在通知清單頂部顯示 \n- 允許全螢幕騷擾 \n- 一律顯示通知 \n\n"<b>"第 4 級"</b>" \n- 阻止全螢幕騷擾 \n- 一律顯示通知 \n\n"<b>"第 3 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n\n"<b>"第 2 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n- 永不發出聲響和震動 \n\n"<b>"第 1 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n- 永不發出聲響和震動 \n- 從上鎖畫面和狀態列中隱藏 \n- 在通知清單底部顯示 \n\n"<b>"第 0 級"</b>" \n- 封鎖所有應用程式通知"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
@@ -807,7 +791,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"在 <xliff:g id="APP_LABEL">%3$s</xliff:g> 播放 <xliff:g id="ARTIST_NAME">%2$s</xliff:g> 的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"在 <xliff:g id="APP_LABEL">%2$s</xliff:g> 播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"移近一點以在 <xliff:g id="DEVICENAME">%1$s</xliff:g> 上播放"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"正在 <xliff:g id="DEVICENAME">%1$s</xliff:g> 上播放"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"找不到"</string>
@@ -895,4 +880,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"正在背景中執行的應用程式"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 637fb4c..d5d092f 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"開啟"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"無法使用"</item>
     <item msgid="9103697205127645916">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 11e4ac6..87a6ae8 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"螢幕鎖定。"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Work 螢幕鎖定"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"關閉"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi 已關閉。"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi 已開啟。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"飛航模式已關閉。"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"飛航模式已開啟。"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"完全靜音"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"僅限鬧鐘"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"零打擾。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"「零打擾」設定已關閉。"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"「零打擾」設定已開啟。"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"藍牙。"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"藍牙已開啟。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"藍牙已關閉。"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"藍牙已開啟。"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"位置回報設定已關閉。"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"位置回報設定已開啟。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"鬧鐘已設定為:<xliff:g id="TIME">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"增加時間。"</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"減少時間。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"閃光燈已關閉。"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"閃光燈已開啟。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"色彩反轉模式已關閉。"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"色彩反轉模式已開啟。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"可攜式無線基地台已關閉。"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"可攜式無線基地台已開啟。"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"已停止投放螢幕。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"數據節省模式已關閉。"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"數據節省模式已開啟。"</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"螢幕亮度"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"行動數據已暫停使用"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"已暫停數據連線"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未連線至 Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"更多設定"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"擷取卡片時發生問題,請稍後再試"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"螢幕鎖定設定"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"掃描 QR 圖碼"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"按一下即可掃描 QR 圖碼"</string>
+    <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR 圖碼"</string>
+    <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+    <skip />
     <string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"你不會聽到下一個<xliff:g id="WHEN">%1$s</xliff:g> 的鬧鐘"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"如要將鍵盤連線到平板電腦,你必須先開啟藍牙。"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"開啟"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"電源通知控制項"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 依臉部方向旋轉"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"只要使用電源通知控制項,你就能為應用程式通知設定從 0 到 5 的重要性等級。\n\n"<b>"等級 5"</b>" \n- 顯示在通知清單頂端 \n- 允許全螢幕通知 \n- 一律允許短暫顯示通知 \n\n"<b>"等級 4"</b>" \n- 禁止全螢幕通知 \n- 一律允許短暫顯示通知 \n\n"<b>"等級 3"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n\n"<b>"等級 2"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n- 一律不發出音效或震動 \n\n"<b>"等級 1"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n- 一律不發出音效或震動 \n- 在鎖定畫面和狀態列中隱藏 \n- 顯示在通知清單底端 \n\n"<b>"等級 0"</b>" \n- 封鎖應用程式的所有通知"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"透過「<xliff:g id="APP_LABEL">%3$s</xliff:g>」播放<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"透過「<xliff:g id="APP_LABEL">%2$s</xliff:g>」播放〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"如要在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放,請靠近這部裝置"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"正在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"無效,請查看應用程式"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"找不到控制項"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"目前在背景執行的應用程式"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 266cf5d..ad24413 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <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="4531508423703413340">"已開啟"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"無法使用"</item>
     <item msgid="9103697205127645916">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8c7f030..602845b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -175,33 +175,15 @@
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Khiya isikrini."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ukukhiya isikrini somsebenzi"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"Vala"</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"I-Wifi ivaliwe."</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"I-Wifi ivuliwe."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"Imodi yendiza ivaliwe."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Imodi yendiza ivuliwe."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ukuthula okuphelele"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ama-alamu kuphela"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Ungaphazamisi"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"Ukungaphazamisi kuvaliwe."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"Ukungaphazamisi kuvuliwe."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"I-Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"I-Bluetooth ivuliwe."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"I-Bluetooth ivaliwe."</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"I-Bluetooth ivuliwe."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Ukubikwa kwendawo kuvaliwe."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Ukubikwa kwendawo kuvuliwe."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"I-alamu isethiwe ngo-<xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Isikhathi esiningi."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Isikhathi esincane."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"I-Flashlight ivaliwe."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"I-Flashlight ivuliwe."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"Ukufakwa kombhala kuvaliwe."</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"Ukufakwa kombhala kuvuliwe."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"I-hotspot ivaliwe."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"I-hotspot ivuliwe."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ukusakaza kwesikrini kumisiwe."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Iseva yedatha ivaliwe."</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Iseva yedatha ivuliwe."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"Bonisa ukukhanya"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Idatha yeselula imisiwe"</string>
     <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Idatha imisiwe"</string>
@@ -251,6 +233,7 @@
     <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"I-Wi-Fi ayixhunyiwe"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ukugqama"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ukuguqulwa kombala"</string>
+    <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ukulungiswa kombala"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Izilungiselelo eziningi"</string>
     <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Amasethingi womsebenzisi"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Kwenziwe"</string>
@@ -466,8 +449,9 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
-    <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skena i-QR"</string>
-    <string name="qr_code_scanner_description" msgid="7452098243938659945">"Chofoza ukuze uskene ikhodi ye-QR"</string>
+    <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+    <skip />
+    <string name="qr_code_scanner_description" msgid="7937603775306661863">"Thepha ukuze uskene"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ngeke uzwe i-alamu yakho elandelayo ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -487,6 +471,7 @@
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Ukuze uxhume ikhibhodi yakho nethebhulethi yakho, kufanele uqale ngokuvula i-Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Vula"</string>
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Izilawuli zesaziso zamandla"</string>
+    <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vuliwe - Kususelwe kubuso"</string>
     <string name="power_notification_controls_description" msgid="1334963837572708952">"Ngezilawuli zesaziso zamandla, ungasetha ileveli ebalulekile kusuka ku-0 kuya ku-5 kusuka kuzaziso zohlelo lokusebenza. \n\n"<b>"Ileveli 5"</b>" \n- Ibonisa phezulu kuhlu lwesaziso \n- Vumela ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 4"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 3"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n\n"<b>"Ileveli 2"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo nokudlidliza \n\n"<b>"Ileveli 1"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo noma ukudlidliza \n- Fihla kusuka kusikrini sokukhiya nebha yesimo \n- Bonisa phansi kohlu lwesaziso \n\n"<b>"Ileveli 0"</b>" \n- Vimbela zonke izaziso kusuka kuhlelo lokusebenza"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Kwenziwe"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Faka"</string>
@@ -807,7 +792,8 @@
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> ka-<xliff:g id="ARTIST_NAME">%2$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Hlehlisa"</string>
-    <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Sondeza eduze ukudlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+    <skip />
     <string name="media_transfer_playing" msgid="3760048096352107789">"Idlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ayitholakali"</string>
@@ -895,4 +881,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Khetha umsebenzisi"</string>
     <string name="fgs_manager_dialog_title" msgid="656091833603337197">"Ama-app ayaqhubeka ngemuva"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Misa"</string>
+    <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+    <skip />
+    <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index 9cac4ae..92290d6 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -86,6 +86,11 @@
     <item msgid="5715725170633593906">"Valiwe"</item>
     <item msgid="2075645297847971154">"Vuliwe"</item>
   </string-array>
+  <string-array name="tile_states_color_correction">
+    <item msgid="2840507878437297682">"Akutholakali"</item>
+    <item msgid="1909756493418256167">"Valiwe"</item>
+    <item msgid="4531508423703413340">"Vuliwe"</item>
+  </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"Akutholakali"</item>
     <item msgid="9103697205127645916">"Valiwe"</item>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index fc28f09..461a598 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -23,7 +23,6 @@
     <color name="system_bar_background_transparent">#00000000</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
-    <color name="qs_detail_transition">#66FFFFFF</color>
     <color name="status_bar_clock_color">#FFFFFFFF</color>
     <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9f017b2..fc2756e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -82,7 +82,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,fgsmanager
+        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,fgsmanager,color_correction
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -97,6 +97,7 @@
          The syntax is setting-name:spec. If the tile is a TileService, the spec should be specified
          as custom(package/class). Relative class name is supported. -->
     <string-array name="config_quickSettingsAutoAdd" translatable="false">
+        <item>accessibility_display_daltonizer_enabled:color_correction</item>
         <item>accessibility_display_inversion_enabled:inversion</item>
         <item>one_handed_mode_enabled:onehanded</item>
     </string-array>
@@ -293,6 +294,7 @@
     <string-array name="config_systemUIServiceComponents" translatable="false">
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
+        <item>com.android.keyguard.KeyguardBiometricLockoutLogger</item>
         <item>com.android.systemui.recents.Recents</item>
         <item>com.android.systemui.volume.VolumeUI</item>
         <item>com.android.systemui.statusbar.phone.StatusBar</item>
@@ -314,6 +316,7 @@
         <item>com.android.systemui.accessibility.SystemActions</item>
         <item>com.android.systemui.toast.ToastUI</item>
         <item>com.android.systemui.wmshell.WMShell</item>
+        <item>com.android.systemui.clipboardoverlay.ClipboardListener</item>
     </string-array>
 
     <!-- SystemUI Services: The classes of the additional stuff to start. Services here are
@@ -713,26 +716,12 @@
     <!-- Flag to enable privacy dot views, it shall be true for normal case -->
     <bool name="config_enablePrivacyDot">true</bool>
 
-    <!-- The positions widgets can be in defined as View.Gravity constants -->
-    <integer-array name="config_dreamComplicationPositions">
-    </integer-array>
-
-    <!-- Widget components to show as dream complications -->
-    <string-array name="config_dreamAppWidgetComplications" translatable="false">
-    </string-array>
-
-    <!-- Width percentage of dream complications -->
-    <item name="config_dreamComplicationWidthPercent" translatable="false" format="float"
-          type="dimen">0.33</item>
-
-    <!-- Height percentage of dream complications -->
-    <item name="config_dreamComplicationHeightPercent" translatable="false" format="float"
-          type="dimen">0.25</item>
-
     <!-- Flag to enable dream overlay service and its registration -->
     <bool name="config_dreamOverlayServiceEnabled">false</bool>
 
     <!-- Class for the communal source connector to be used -->
     <string name="config_communalSourceConnector" translatable="false"></string>
 
+    <!-- How often in milliseconds to jitter the dream overlay in order to avoid burn-in. -->
+    <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">500</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b12db5d..ceaacfc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -984,10 +984,11 @@
     <!-- Media tap-to-transfer chip for sender device -->
     <dimen name="media_ttt_chip_outer_padding">16dp</dimen>
     <dimen name="media_ttt_text_size">16sp</dimen>
-    <dimen name="media_ttt_icon_size">24dp</dimen>
-    <dimen name="media_ttt_loading_size">20dp</dimen>
+    <dimen name="media_ttt_app_icon_size">24dp</dimen>
+    <dimen name="media_ttt_status_icon_size">20dp</dimen>
     <dimen name="media_ttt_undo_button_vertical_padding">8dp</dimen>
     <dimen name="media_ttt_undo_button_vertical_negative_margin">-8dp</dimen>
+    <dimen name="media_ttt_last_item_start_margin">12dp</dimen>
 
     <!-- Media tap-to-transfer chip for receiver device -->
     <dimen name="media_ttt_chip_size_receiver">100dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 95d140f..34e5aef 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -596,6 +596,7 @@
     <!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_inversion_label">Color inversion</string>
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_color_correction_label">Color correction</string>
     <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_more_settings">More settings</string>
     <!-- QuickSettings: Control panel: Label for button that navigates to user settings. [CHAR LIMIT=NONE] -->
@@ -669,6 +670,10 @@
     <string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
     <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
     <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
+    <!-- QuickSettings: Secondary text for when the Dark theme will be enabled at bedtime. [CHAR LIMIT=40] -->
+    <string name="quick_settings_dark_mode_secondary_label_on_at_bedtime">On at bedtime</string>
+    <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until bedtime ends. [CHAR LIMIT=40] -->
+    <string name="quick_settings_dark_mode_secondary_label_until_bedtime_ends">Until bedtime ends</string>
 
     <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_label">NFC</string>
@@ -1164,10 +1169,10 @@
     <string name="wallet_lockscreen_settings_label">Lock screen settings</string>
 
     <!-- QR Code Scanner label, title [CHAR LIMIT=32] -->
-    <string name="qr_code_scanner_title">Scan QR</string>
+    <string name="qr_code_scanner_title">QR code</string>
 
     <!-- QR Code Scanner description [CHAR LIMIT=NONE] -->
-    <string name="qr_code_scanner_description">Click to scan a QR code</string>
+    <string name="qr_code_scanner_description">Tap to scan</string>
 
     <!-- Name of the work status bar icon. -->
     <string name="status_bar_work">Work profile</string>
@@ -2151,10 +2156,14 @@
     <!--- ****** Media tap-to-transfer ****** -->
     <!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
     <string name="media_transfer_undo">Undo</string>
-    <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play music on the different device. [CHAR LIMIT=75] -->
-    <string name="media_move_closer_to_transfer">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+    <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play media on the different device. [CHAR LIMIT=75] -->
+    <string name="media_move_closer_to_start_cast">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+    <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to transfer media from the different device and back onto the current device. [CHAR LIMIT=75] -->
+    <string name="media_move_closer_to_end_cast">Move closer to <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g> to play here</string>
     <!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
     <string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+    <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] -->
+    <string name="media_transfer_failed">Something went wrong</string>
 
     <!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
     <string name="controls_error_timeout">Inactive, check app</string>
@@ -2348,4 +2357,9 @@
     <string name="fgs_manager_dialog_title">Apps running in the background</string>
     <!-- Label of the button to stop the app from running in the background [CHAR LIMIT=12]-->
     <string name="fgs_manager_app_item_stop_button_label">Stop</string>
+
+    <!-- Label for button to copy edited text back to the clipboard [CHAR LIMIT=20] -->
+    <string name="clipboard_edit_text_copy">Copy</string>
+    <!-- Text informing user that content has been copied to the system clipboard [CHAR LIMIT=NONE] -->
+    <string name="clipboard_overlay_text_copied">Copied</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 18bfb52..ff5699b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -666,6 +666,14 @@
 
     <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight"/>
 
+    <!-- Clipboard overlay's edit text activity. -->
+    <style name="EditTextActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowLightNavigationBar">true</item>
+        <item name="android:navigationBarColor">?android:attr/colorBackgroundFloating</item>
+    </style>
+
     <!-- Privacy dialog -->
     <style name="PrivacyDialog" parent="Theme.SystemUI.QuickSettings.Dialog">
         <item name="android:windowIsTranslucent">true</item>
@@ -968,8 +976,9 @@
     </style>
 
     <style name="InternetDialog.NetworkSummary">
-        <item name="android:layout_marginEnd">34dp</item>
+        <item name="android:layout_marginEnd">7dp</item>
         <item name="android:ellipsize">end</item>
+        <item name="android:maxLines">2</item>
         <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary</item>
     </style>
 
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index 5fdb497..a610caa 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -142,6 +142,16 @@
         <item>On</item>
     </string-array>
 
+    <!-- State names for color correction tile: unavailable, off, on.
+         This subtitle is shown when the tile is in that particular state but does not set its own
+         subtitle, so some of these may never appear on screen. They should still be translated as
+         if they could appear. [CHAR LIMIT=32] -->
+    <string-array name="tile_states_color_correction">
+        <item>Unavailable</item>
+        <item>Off</item>
+        <item>On</item>
+    </string-array>
+
     <!-- State names for (color) inversion tile: unavailable, off, on.
          This subtitle is shown when the tile is in that particular state but does not set its own
          subtitle, so some of these may never appear on screen. They should still be translated as
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index d172006..3cf5bc1 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -49,9 +49,12 @@
         "PluginCoreLib",
         "androidx.dynamicanimation_dynamicanimation",
         "androidx.concurrent_concurrent-futures",
+        "dagger2",
+        "jsr330",
     ],
     java_version: "1.8",
     min_sdk_version: "current",
+    plugins: ["dagger2-compiler"],
 }
 
 java_library {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java
rename to packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
new file mode 100644
index 0000000..861a4ed
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.mediattt;
+
+parcelable DeviceInfo;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
new file mode 100644
index 0000000..d41aaf3
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.mediattt
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * Represents a device that can send or receive media. Includes any device information necessary for
+ * SysUI to display an informative chip to the user.
+ */
+class DeviceInfo(val name: String) : Parcelable {
+    constructor(parcel: Parcel) : this(parcel.readString())
+
+    override fun writeToParcel(dest: Parcel?, flags: Int) {
+        dest?.writeString(name)
+    }
+
+    override fun describeContents() = 0
+
+    override fun toString() = "name: $name"
+
+    companion object CREATOR : Parcelable.Creator<DeviceInfo> {
+        override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel)
+        override fun newArray(size: Int) = arrayOfNulls<DeviceInfo?>(size)
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
new file mode 100644
index 0000000..8db3e9d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.mediattt;
+
+import android.media.MediaRoute2Info;
+import com.android.systemui.shared.mediattt.DeviceInfo;
+
+/**
+ * A callback interface that can be invoked to trigger media transfer events on System UI.
+ *
+ * This interface is for the *sender* device, which is the device currently playing media. This
+ * sender device can transfer the media to a different device, called the receiver.
+ *
+ * System UI will implement this interface and other services will invoke it.
+ */
+interface IDeviceSenderCallback {
+    /**
+     * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
+     * the user can potentially *start* a cast to the receiver device if the user moves their device
+     * a bit closer.
+     *
+     * Important notes:
+     *   - When this callback triggers, the device is close enough to inform the user that
+     *     transferring is an option, but the device is *not* close enough to actually initiate a
+     *     transfer yet.
+     *   - This callback is for *starting* a cast. It should be used when this device is currently
+     *     playing media locally and the media should be transferred to be played on the receiver
+     *     device instead.
+     */
+    oneway void closeToReceiverToStartCast(
+        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+    /**
+     * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
+     * the user can potentially *end* a cast on the receiver device if the user moves this device a
+     * bit closer.
+     *
+     * Important notes:
+     *   - When this callback triggers, the device is close enough to inform the user that
+     *     transferring is an option, but the device is *not* close enough to actually initiate a
+     *     transfer yet.
+     *   - This callback is for *ending* a cast. It should be used when media is currently being
+     *     played on the receiver device and the media should be transferred to play locally
+     *     instead.
+     */
+    oneway void closeToReceiverToEndCast(
+        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+    /**
+     * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
+     * device has been started.
+     *
+     * Important notes:
+     *   - This callback is for *starting* a cast. It should be used when this device is currently
+     *     playing media locally and the media has started being transferred to the receiver device
+     *     instead.
+     */
+    oneway void transferToReceiverTriggered(
+        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+    /**
+     * Invoke to notify System UI that the attempted transfer has failed.
+     *
+     * This callback will be used for both the transfer that should've *started* playing the media
+     * on the receiver and the transfer that should've *ended* the playing on the receiver.
+     */
+    oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index 9808374..6345d11 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -28,6 +28,8 @@
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 
+import androidx.annotation.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
 
@@ -60,6 +62,7 @@
     private final Rect mRegisteredSamplingBounds = new Rect();
     private final SamplingCallback mCallback;
     private final Executor mBackgroundExecutor;
+    private final SysuiCompositionSamplingListener mCompositionSamplingListener;
     private boolean mSamplingEnabled = false;
     private boolean mSamplingListenerRegistered = false;
 
@@ -91,9 +94,17 @@
 
     public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
             Executor backgroundExecutor) {
+        this(sampledView, samplingCallback, sampledView.getContext().getMainExecutor(),
+                backgroundExecutor, new SysuiCompositionSamplingListener());
+    }
+
+    @VisibleForTesting
+    RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
+            Executor mainExecutor, Executor backgroundExecutor,
+            SysuiCompositionSamplingListener compositionSamplingListener) {
         mBackgroundExecutor = backgroundExecutor;
-        mSamplingListener = new CompositionSamplingListener(
-                sampledView.getContext().getMainExecutor()) {
+        mCompositionSamplingListener = compositionSamplingListener;
+        mSamplingListener = new CompositionSamplingListener(mainExecutor) {
             @Override
             public void onSampleCollected(float medianLuma) {
                 if (mSamplingEnabled) {
@@ -136,7 +147,7 @@
 
     public void stopAndDestroy() {
         stop();
-        mSamplingListener.destroy();
+        mBackgroundExecutor.execute(mSamplingListener::destroy);
         mIsDestroyed = true;
     }
 
@@ -189,13 +200,12 @@
                 // We only want to re-register if something actually changed
                 unregisterSamplingListener();
                 mSamplingListenerRegistered = true;
-                SurfaceControl wrappedStopLayer = stopLayerControl == null
-                        ? null : new SurfaceControl(stopLayerControl, "regionSampling");
+                SurfaceControl wrappedStopLayer = wrap(stopLayerControl);
                 mBackgroundExecutor.execute(() -> {
                     if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) {
                         return;
                     }
-                    CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
+                    mCompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
                             wrappedStopLayer, mSamplingRequestBounds);
                 });
                 mRegisteredSamplingBounds.set(mSamplingRequestBounds);
@@ -208,14 +218,21 @@
         }
     }
 
+    @VisibleForTesting
+    protected SurfaceControl wrap(SurfaceControl stopLayerControl) {
+        return stopLayerControl == null ? null : new SurfaceControl(stopLayerControl,
+                "regionSampling");
+    }
+
     private void unregisterSamplingListener() {
         if (mSamplingListenerRegistered) {
             mSamplingListenerRegistered = false;
             SurfaceControl wrappedStopLayer = mWrappedStopLayer;
             mRegisteredStopLayer = null;
+            mWrappedStopLayer = null;
             mRegisteredSamplingBounds.setEmpty();
             mBackgroundExecutor.execute(() -> {
-                CompositionSamplingListener.unregister(mSamplingListener);
+                mCompositionSamplingListener.unregister(mSamplingListener);
                 if (wrappedStopLayer != null && wrappedStopLayer.isValid()) {
                     wrappedStopLayer.release();
                 }
@@ -258,23 +275,27 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println("RegionSamplingHelper:");
-        pw.println("  sampleView isAttached: " + mSampledView.isAttachedToWindow());
-        pw.println("  sampleView isScValid: " + (mSampledView.isAttachedToWindow()
+        dump("", pw);
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "RegionSamplingHelper:");
+        pw.println(prefix + "\tsampleView isAttached: " + mSampledView.isAttachedToWindow());
+        pw.println(prefix + "\tsampleView isScValid: " + (mSampledView.isAttachedToWindow()
                 ? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
                 : "notAttached"));
-        pw.println("  mSamplingEnabled: " + mSamplingEnabled);
-        pw.println("  mSamplingListenerRegistered: " + mSamplingListenerRegistered);
-        pw.println("  mSamplingRequestBounds: " + mSamplingRequestBounds);
-        pw.println("  mRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
-        pw.println("  mLastMedianLuma: " + mLastMedianLuma);
-        pw.println("  mCurrentMedianLuma: " + mCurrentMedianLuma);
-        pw.println("  mWindowVisible: " + mWindowVisible);
-        pw.println("  mWindowHasBlurs: " + mWindowHasBlurs);
-        pw.println("  mWaitingOnDraw: " + mWaitingOnDraw);
-        pw.println("  mRegisteredStopLayer: " + mRegisteredStopLayer);
-        pw.println("  mWrappedStopLayer: " + mWrappedStopLayer);
-        pw.println("  mIsDestroyed: " + mIsDestroyed);
+        pw.println(prefix + "\tmSamplingEnabled: " + mSamplingEnabled);
+        pw.println(prefix + "\tmSamplingListenerRegistered: " + mSamplingListenerRegistered);
+        pw.println(prefix + "\tmSamplingRequestBounds: " + mSamplingRequestBounds);
+        pw.println(prefix + "\tmRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
+        pw.println(prefix + "\tmLastMedianLuma: " + mLastMedianLuma);
+        pw.println(prefix + "\tmCurrentMedianLuma: " + mCurrentMedianLuma);
+        pw.println(prefix + "\tmWindowVisible: " + mWindowVisible);
+        pw.println(prefix + "\tmWindowHasBlurs: " + mWindowHasBlurs);
+        pw.println(prefix + "\tmWaitingOnDraw: " + mWaitingOnDraw);
+        pw.println(prefix + "\tmRegisteredStopLayer: " + mRegisteredStopLayer);
+        pw.println(prefix + "\tmWrappedStopLayer: " + mWrappedStopLayer);
+        pw.println(prefix + "\tmIsDestroyed: " + mIsDestroyed);
     }
 
     public interface SamplingCallback {
@@ -299,4 +320,19 @@
             return true;
         }
     }
+
+    @VisibleForTesting
+    public static class SysuiCompositionSamplingListener {
+        public void register(CompositionSamplingListener listener,
+                int displayId, SurfaceControl stopLayer, Rect samplingArea) {
+            CompositionSamplingListener.register(listener, displayId, stopLayer, samplingArea);
+        }
+
+        /**
+         * Unregisters a sampling listener.
+         */
+        public void unregister(CompositionSamplingListener listener) {
+            CompositionSamplingListener.unregister(listener);
+        }
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 7539f99..851ea65 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -137,7 +137,8 @@
         filter.addAction(PLUGIN_CHANGED);
         filter.addAction(DISABLE_PLUGIN);
         filter.addDataScheme("package");
-        mContext.registerReceiver(this, filter, PluginActionManager.PLUGIN_PERMISSION, null);
+        mContext.registerReceiver(this, filter, PluginActionManager.PLUGIN_PERMISSION, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiver(this, filter);
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 605d376..bb7a0a71 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.rotation;
 
+import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
@@ -51,13 +52,14 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.view.RotationPolicy;
-import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.recents.utilities.ViewRippler;
+import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
+import java.io.PrintWriter;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -200,7 +202,7 @@
     }
 
     public void registerListeners() {
-        if (mListenersRegistered) {
+        if (mListenersRegistered || getContext().getPackageManager().hasSystemFeature(FEATURE_PC)) {
             return;
         }
 
@@ -414,6 +416,9 @@
     }
 
     public void onTaskbarStateChange(boolean visible, boolean stashed) {
+        if (getRotationButton() == null) {
+            return;
+        }
         getRotationButton().onTaskbarStateChanged(visible, stashed);
     }
 
@@ -446,6 +451,30 @@
         return mDarkIconColor;
     }
 
+    public void dumpLogs(String prefix, PrintWriter pw) {
+        pw.println(prefix + "RotationButtonController:");
+
+        pw.println(String.format(
+                "%s\tmIsRecentsAnimationRunning=%b", prefix, mIsRecentsAnimationRunning));
+        pw.println(String.format("%s\tmHomeRotationEnabled=%b", prefix, mHomeRotationEnabled));
+        pw.println(String.format(
+                "%s\tmLastRotationSuggestion=%d", prefix, mLastRotationSuggestion));
+        pw.println(String.format(
+                "%s\tmPendingRotationSuggestion=%b", prefix, mPendingRotationSuggestion));
+        pw.println(String.format(
+                "%s\tmHoveringRotationSuggestion=%b", prefix, mHoveringRotationSuggestion));
+        pw.println(String.format("%s\tmListenersRegistered=%b", prefix, mListenersRegistered));
+        pw.println(String.format(
+                "%s\tmIsNavigationBarShowing=%b", prefix, mIsNavigationBarShowing));
+        pw.println(String.format("%s\tmBehavior=%d", prefix, mBehavior));
+        pw.println(String.format(
+                "%s\tmSkipOverrideUserLockPrefsOnce=%b", prefix, mSkipOverrideUserLockPrefsOnce));
+        pw.println(String.format(
+                "%s\tmLightIconColor=0x%s", prefix, Integer.toHexString(mLightIconColor)));
+        pw.println(String.format(
+                "%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor)));
+    }
+
     public RotationButton getRotationButton() {
         return mRotationButton;
     }
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 d182399..54c798c 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
@@ -60,8 +60,6 @@
     // See IRecentTasks.aidl
     public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
 
-    public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
-            WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
     public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
             WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
     public static final String NAV_BAR_MODE_GESTURAL_OVERLAY =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index e84b552..618d2d2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -183,8 +183,8 @@
                     }
                     // Make wallpaper visible immediately since launcher apparently won't do this.
                     for (int i = wallpapersCompat.length - 1; i >= 0; --i) {
-                        t.show(wallpapersCompat[i].leash.getSurfaceControl());
-                        t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);
+                        t.show(wallpapersCompat[i].leash);
+                        t.setAlpha(wallpapersCompat[i].leash, 1.f);
                     }
                 } else {
                     if (launcherTask != null) {
@@ -205,8 +205,10 @@
                     @Override
                     @SuppressLint("NewApi")
                     public void run() {
-                        counterLauncher.cleanUp(info.getRootLeash());
-                        counterWallpaper.cleanUp(info.getRootLeash());
+                        final SurfaceControl.Transaction finishTransaction =
+                                new SurfaceControl.Transaction();
+                        counterLauncher.cleanUp(finishTransaction);
+                        counterWallpaper.cleanUp(finishTransaction);
                         // Release surface references now. This is apparently to free GPU memory
                         // while doing quick operations (eg. during CTS).
                         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -216,7 +218,7 @@
                             leashMap.valueAt(i).release();
                         }
                         try {
-                            finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+                            finishCallback.onTransitionFinished(null /* wct */, finishTransaction);
                         } catch (RemoteException e) {
                             Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
                                     + " finished callback", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index c1d9d0d..4d0c443 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -17,7 +17,6 @@
 package com.android.systemui.shared.system;
 
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
-import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -57,7 +56,7 @@
     public final int activityType;
 
     public final int taskId;
-    public final SurfaceControlCompat leash;
+    public final SurfaceControl leash;
     public final boolean isTranslucent;
     public final Rect clipRect;
     public final int prefixOrderIndex;
@@ -82,7 +81,7 @@
     public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
         taskId = app.taskId;
         mode = app.mode;
-        leash = new SurfaceControlCompat(app.leash);
+        leash = app.leash;
         isTranslucent = app.isTranslucent;
         clipRect = app.clipRect;
         position = app.position;
@@ -119,7 +118,7 @@
 
     public RemoteAnimationTarget unwrap() {
         return new RemoteAnimationTarget(
-                taskId, mode, leash.getSurfaceControl(), isTranslucent, clipRect, contentInsets,
+                taskId, mode, leash, isTranslucent, clipRect, contentInsets,
                 prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfiguration,
                 isNotInRecents, mStartLeash, startBounds, taskInfo, allowEnterPip, windowType
         );
@@ -140,22 +139,11 @@
         // changes should be ordered top-to-bottom in z
         final int mode = change.getMode();
 
-        // Don't move anything that isn't independent within its parents
-        if (!TransitionInfo.isIndependent(change, info)) {
-            if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
-                t.show(leash);
-                t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
-            }
-            return;
-        }
+        // Launcher animates leaf tasks directly, so always reparent all task leashes to root leash.
+        t.reparent(leash, info.getRootLeash());
+        t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
+                change.getStartAbsBounds().top - info.getRootOffset().y);
 
-        boolean hasParent = change.getParent() != null;
-
-        if (!hasParent) {
-            t.reparent(leash, info.getRootLeash());
-            t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
-                    change.getStartAbsBounds().top - info.getRootOffset().y);
-        }
         t.show(leash);
         // Put all the OPEN/SHOW on top
         if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
@@ -211,7 +199,7 @@
         mode = newModeToLegacyMode(change.getMode());
 
         // TODO: once we can properly sync transactions across process, then get rid of this leash.
-        leash = new SurfaceControlCompat(createLeash(info, change, order, t));
+        leash = createLeash(info, change, order, t);
 
         isTranslucent = (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0
                 || (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0;
@@ -266,14 +254,15 @@
             SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
         final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
         for (int i = 0; i < info.getChanges().size(); i++) {
-            boolean changeIsWallpaper =
-                    (info.getChanges().get(i).getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            final boolean changeIsWallpaper =
+                    (change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
             if (wallpapers != changeIsWallpaper) continue;
-            out.add(new RemoteAnimationTargetCompat(info.getChanges().get(i),
-                    info.getChanges().size() - i, info, t));
-            if (leashMap == null) continue;
-            leashMap.put(info.getChanges().get(i).getLeash(),
-                    out.get(out.size() - 1).leash.mSurfaceControl);
+
+            out.add(new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t));
+            if (leashMap != null) {
+                leashMap.put(change.getLeash(), out.get(out.size() - 1).leash);
+            }
         }
         return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
     }
@@ -282,7 +271,9 @@
      * @see SurfaceControl#release()
      */
     public void release() {
-        leash.mSurfaceControl.release();
+        if (leash != null) {
+            leash.release();
+        }
         if (mStartLeash != null) {
             mStartLeash.release();
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 2d5080e..f2f382d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -25,6 +25,8 @@
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
 
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -145,11 +147,16 @@
                                 && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
                             pipTask = taskInfo.token;
                         }
+                    } else if (change.getTaskInfo() != null
+                            && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_RECENTS) {
+                        // This task is for recents, keep it on top.
+                        t.setLayer(leashMap.get(change.getLeash()),
+                                info.getChanges().size() * 3 - i);
                     }
                 }
                 // Also make all the wallpapers opaque since we want the visible from the start
                 for (int i = wallpapers.length - 1; i >= 0; --i) {
-                    t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
+                    t.setAlpha(wallpapers[i].leash, 1);
                 }
                 t.apply();
                 mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
@@ -267,10 +274,10 @@
                 // We are receiving new opening tasks, so convert to onTasksAppeared.
                 final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
                         openingTasks.get(i), layer, info, t);
-                mLeashMap.put(mOpeningLeashes.get(i), target.leash.mSurfaceControl);
-                t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
-                t.setLayer(target.leash.mSurfaceControl, layer);
-                t.hide(target.leash.mSurfaceControl);
+                mLeashMap.put(mOpeningLeashes.get(i), target.leash);
+                t.reparent(target.leash, mInfo.getRootLeash());
+                t.setLayer(target.leash, layer);
+                t.hide(target.leash);
                 targets[i] = target;
             }
             t.apply();
@@ -310,53 +317,48 @@
                 return;
             }
             if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
-            try {
-                if (!toHome && mPausingTasks != null && mOpeningLeashes == null) {
-                    // The gesture went back to opening the app rather than continuing with
-                    // recents, so end the transition by moving the app back to the top (and also
-                    // re-showing it's task).
-                    final WindowContainerTransaction wct = new WindowContainerTransaction();
-                    final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-                    for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
-                        // reverse order so that index 0 ends up on top
-                        wct.reorder(mPausingTasks.get(i), true /* onTop */);
-                        t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
-                    }
-                    mFinishCB.onTransitionFinished(wct, t);
-                } else {
-                    if (mOpeningLeashes != null) {
-                        // TODO: the launcher animation should handle this
-                        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-                        for (int i = 0; i < mOpeningLeashes.size(); ++i) {
-                            t.show(mOpeningLeashes.get(i));
-                            t.setAlpha(mOpeningLeashes.get(i), 1.f);
-                        }
-                        t.apply();
-                    }
-                    if (mPipTask != null && mPipTransaction != null) {
-                        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-                        t.show(mInfo.getChange(mPipTask).getLeash());
-                        PictureInPictureSurfaceTransaction.apply(mPipTransaction,
-                                mInfo.getChange(mPipTask).getLeash(), t);
-                        mPipTask = null;
-                        mPipTransaction = null;
-                        mFinishCB.onTransitionFinished(null /* wct */, t);
-                    } else {
-                        mFinishCB.onTransitionFinished(null /* wct */, null /* sct */);
-                    }
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            final WindowContainerTransaction wct;
 
+            if (!toHome && mPausingTasks != null && mOpeningLeashes == null) {
+                // The gesture went back to opening the app rather than continuing with
+                // recents, so end the transition by moving the app back to the top (and also
+                // re-showing it's task).
+                wct = new WindowContainerTransaction();
+                for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+                    // reverse order so that index 0 ends up on top
+                    wct.reorder(mPausingTasks.get(i), true /* onTop */);
+                    t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
                 }
-            } catch (RemoteException e) {
-                Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
+            } else {
+                wct = null;
+                if (mOpeningLeashes != null) {
+                    // TODO: the launcher animation should handle this
+                    for (int i = 0; i < mOpeningLeashes.size(); ++i) {
+                        t.show(mOpeningLeashes.get(i));
+                        t.setAlpha(mOpeningLeashes.get(i), 1.f);
+                    }
+                }
+                if (mPipTask != null && mPipTransaction != null) {
+                    t.show(mInfo.getChange(mPipTask).getLeash());
+                    PictureInPictureSurfaceTransaction.apply(mPipTransaction,
+                            mInfo.getChange(mPipTask).getLeash(), t);
+                    mPipTask = null;
+                    mPipTransaction = null;
+                }
             }
             // Release surface references now. This is apparently to free GPU
             // memory while doing quick operations (eg. during CTS).
-            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
             for (int i = 0; i < mLeashMap.size(); ++i) {
                 if (mLeashMap.keyAt(i) == mLeashMap.valueAt(i)) continue;
                 t.remove(mLeashMap.valueAt(i));
             }
-            t.apply();
+            try {
+                mFinishCB.onTransitionFinished(wct, t);
+            } catch (RemoteException e) {
+                Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
+                t.apply();
+            }
             for (int i = 0; i < mInfo.getChanges().size(); ++i) {
                 mInfo.getChanges().get(i).getLeash().release();
             }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
deleted file mode 100644
index acc6913..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewRootImpl;
-
-/**
- * TODO: Remove this class
- */
-public class SurfaceControlCompat {
-    final SurfaceControl mSurfaceControl;
-
-    public SurfaceControlCompat(SurfaceControl surfaceControl) {
-        mSurfaceControl = surfaceControl;
-    }
-
-    public SurfaceControlCompat(View v) {
-        ViewRootImpl viewRootImpl = v.getViewRootImpl();
-        mSurfaceControl = viewRootImpl != null
-                ? viewRootImpl.getSurfaceControl()
-                : null;
-    }
-
-    public boolean isValid() {
-        return mSurfaceControl != null && mSurfaceControl.isValid();
-    }
-
-    public SurfaceControl getSurfaceControl() {
-        return mSurfaceControl;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index e281914..30c062b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -205,13 +205,6 @@
             /**
              * @param surface The surface to modify.
              */
-            public Builder(SurfaceControlCompat surface) {
-                this(surface.mSurfaceControl);
-            }
-
-            /**
-             * @param surface The surface to modify.
-             */
             public Builder(SurfaceControl surface) {
                 this.surface = surface;
             }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index c043fba..43a882a5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -35,70 +35,69 @@
         mTransaction.apply();
     }
 
-    public TransactionCompat show(SurfaceControlCompat surfaceControl) {
-        mTransaction.show(surfaceControl.mSurfaceControl);
+    public TransactionCompat show(SurfaceControl surfaceControl) {
+        mTransaction.show(surfaceControl);
         return this;
     }
 
-    public TransactionCompat hide(SurfaceControlCompat surfaceControl) {
-        mTransaction.hide(surfaceControl.mSurfaceControl);
+    public TransactionCompat hide(SurfaceControl surfaceControl) {
+        mTransaction.hide(surfaceControl);
         return this;
     }
 
-    public TransactionCompat setPosition(SurfaceControlCompat surfaceControl, float x, float y) {
-        mTransaction.setPosition(surfaceControl.mSurfaceControl, x, y);
+    public TransactionCompat setPosition(SurfaceControl surfaceControl, float x, float y) {
+        mTransaction.setPosition(surfaceControl, x, y);
         return this;
     }
 
-    public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
-        mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);
+    public TransactionCompat setSize(SurfaceControl surfaceControl, int w, int h) {
+        mTransaction.setBufferSize(surfaceControl, w, h);
         return this;
     }
 
-    public TransactionCompat setLayer(SurfaceControlCompat surfaceControl, int z) {
-        mTransaction.setLayer(surfaceControl.mSurfaceControl, z);
+    public TransactionCompat setLayer(SurfaceControl surfaceControl, int z) {
+        mTransaction.setLayer(surfaceControl, z);
         return this;
     }
 
-    public TransactionCompat setAlpha(SurfaceControlCompat surfaceControl, float alpha) {
-        mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);
+    public TransactionCompat setAlpha(SurfaceControl surfaceControl, float alpha) {
+        mTransaction.setAlpha(surfaceControl, alpha);
         return this;
     }
 
-    public TransactionCompat setOpaque(SurfaceControlCompat surfaceControl, boolean opaque) {
-        mTransaction.setOpaque(surfaceControl.mSurfaceControl, opaque);
+    public TransactionCompat setOpaque(SurfaceControl surfaceControl, boolean opaque) {
+        mTransaction.setOpaque(surfaceControl, opaque);
         return this;
     }
 
-    public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, float dsdx, float dtdx,
+    public TransactionCompat setMatrix(SurfaceControl surfaceControl, float dsdx, float dtdx,
             float dtdy, float dsdy) {
-        mTransaction.setMatrix(surfaceControl.mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
+        mTransaction.setMatrix(surfaceControl, dsdx, dtdx, dtdy, dsdy);
         return this;
     }
 
-    public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, Matrix matrix) {
-        mTransaction.setMatrix(surfaceControl.mSurfaceControl, matrix, mTmpValues);
+    public TransactionCompat setMatrix(SurfaceControl surfaceControl, Matrix matrix) {
+        mTransaction.setMatrix(surfaceControl, matrix, mTmpValues);
         return this;
     }
 
-    public TransactionCompat setWindowCrop(SurfaceControlCompat surfaceControl, Rect crop) {
-        mTransaction.setWindowCrop(surfaceControl.mSurfaceControl, crop);
+    public TransactionCompat setWindowCrop(SurfaceControl surfaceControl, Rect crop) {
+        mTransaction.setWindowCrop(surfaceControl, crop);
         return this;
     }
 
-    public TransactionCompat setCornerRadius(SurfaceControlCompat surfaceControl, float radius) {
-        mTransaction.setCornerRadius(surfaceControl.mSurfaceControl, radius);
+    public TransactionCompat setCornerRadius(SurfaceControl surfaceControl, float radius) {
+        mTransaction.setCornerRadius(surfaceControl, radius);
         return this;
     }
 
-    public TransactionCompat setBackgroundBlurRadius(SurfaceControlCompat surfaceControl,
-            int radius) {
-        mTransaction.setBackgroundBlurRadius(surfaceControl.mSurfaceControl, radius);
+    public TransactionCompat setBackgroundBlurRadius(SurfaceControl surfaceControl, int radius) {
+        mTransaction.setBackgroundBlurRadius(surfaceControl, radius);
         return this;
     }
 
-    public TransactionCompat setColor(SurfaceControlCompat surfaceControl, float[] color) {
-        mTransaction.setColor(surfaceControl.mSurfaceControl, color);
+    public TransactionCompat setColor(SurfaceControl surfaceControl, float[] color) {
+        mTransaction.setColor(surfaceControl, color);
         return this;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
new file mode 100644
index 0000000..ac62cf9
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.content.ContentResolver
+import android.content.Context
+import android.hardware.SensorManager
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
+import dagger.BindsInstance
+import dagger.Component
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Singleton
+
+/**
+ * Provides [UnfoldTransitionProgressProvider]. The [Optional] is empty when the transition
+ * animation is disabled.
+ *
+ * This component is meant to be used for places that don't use dagger. By providing those
+ * parameters to the factory, all dagger objects are correctly instantiated. See
+ * [createUnfoldTransitionProgressProvider] for an example.
+ */
+@Singleton
+@Component(modules = [UnfoldSharedModule::class])
+internal interface UnfoldSharedComponent {
+
+    @Component.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance context: Context,
+            @BindsInstance config: UnfoldTransitionConfig,
+            @BindsInstance screenStatusProvider: ScreenStatusProvider,
+            @BindsInstance deviceStateManager: DeviceStateManager,
+            @BindsInstance sensorManager: SensorManager,
+            @BindsInstance @Main handler: Handler,
+            @BindsInstance @Main executor: Executor,
+            @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
+            @BindsInstance contentResolver: ContentResolver = context.contentResolver
+        ): UnfoldSharedComponent
+    }
+
+    val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
new file mode 100644
index 0000000..23e4c97
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.hardware.SensorManager
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
+import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import javax.inject.Singleton
+
+@Module
+class UnfoldSharedModule {
+    @Provides
+    @Singleton
+    fun unfoldTransitionProgressProvider(
+        config: UnfoldTransitionConfig,
+        scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+        tracingListener: ATraceLoggerTransitionProgressListener,
+        foldStateProvider: FoldStateProvider
+    ): Optional<UnfoldTransitionProgressProvider> =
+        if (!config.isEnabled) {
+            Optional.empty()
+        } else {
+            val baseProgressProvider =
+                if (config.isHingeAngleEnabled) {
+                    PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
+                } else {
+                    FixedTimingTransitionProgressProvider(foldStateProvider)
+                }
+            Optional.of(
+                scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
+                    // Always present callback that logs animation beginning and end.
+                    addCallback(tracingListener)
+                })
+        }
+
+    @Provides
+    @Singleton
+    fun provideFoldStateProvider(
+        deviceFoldStateProvider: DeviceFoldStateProvider
+    ): FoldStateProvider = deviceFoldStateProvider
+
+    @Provides
+    fun hingeAngleProvider(
+        config: UnfoldTransitionConfig,
+        sensorManager: SensorManager
+    ): HingeAngleProvider =
+        if (config.isHingeAngleEnabled) {
+            HingeSensorAngleProvider(sensorManager)
+        } else {
+            EmptyHingeAngleProvider
+        }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 24e93ef..d5d6362 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -21,24 +21,18 @@
 import android.hardware.SensorManager
 import android.hardware.devicestate.DeviceStateManager
 import android.os.Handler
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
-import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
-import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
-import com.android.systemui.unfold.updates.DeviceFoldStateProvider
-import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
-import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
-import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
-import java.lang.IllegalStateException
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import java.util.concurrent.Executor
 
 /**
  * Factory for [UnfoldTransitionProgressProvider].
  *
- * This is needed as Launcher has to create the object manually.
- * Sysui create it using dagger (see [UnfoldTransitionModule]).
+ * This is needed as Launcher has to create the object manually. If dagger is available, this object
+ * is provided in [UnfoldSharedModule].
+ *
+ * This should **never** be called from sysui, as the object is already provided in that process.
  */
 fun createUnfoldTransitionProgressProvider(
     context: Context,
@@ -49,42 +43,21 @@
     mainHandler: Handler,
     mainExecutor: Executor,
     tracingTagPrefix: String
-): UnfoldTransitionProgressProvider {
+): UnfoldTransitionProgressProvider =
+    DaggerUnfoldSharedComponent.factory()
+        .create(
+            context,
+            config,
+            screenStatusProvider,
+            deviceStateManager,
+            sensorManager,
+            mainHandler,
+            mainExecutor,
+            tracingTagPrefix)
+        .unfoldTransitionProvider
+        .orElse(null)
+        ?: throw IllegalStateException(
+            "Trying to create " +
+                "UnfoldTransitionProgressProvider when the transition is disabled")
 
-    if (!config.isEnabled) {
-        throw IllegalStateException("Trying to create " +
-            "UnfoldTransitionProgressProvider when the transition is disabled")
-    }
-
-    val hingeAngleProvider =
-        if (config.isHingeAngleEnabled) {
-            HingeSensorAngleProvider(sensorManager)
-        } else {
-            EmptyHingeAngleProvider()
-        }
-
-    val foldStateProvider = DeviceFoldStateProvider(
-        context,
-        hingeAngleProvider,
-        screenStatusProvider,
-        deviceStateManager,
-        mainExecutor,
-        mainHandler
-    )
-
-    val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
-        PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
-    } else {
-        FixedTimingTransitionProgressProvider(foldStateProvider)
-    }
-    return ScaleAwareTransitionProgressProvider(
-        unfoldTransitionProgressProvider,
-        context.contentResolver
-    ).apply {
-        // Always present callback that logs animation beginning and end.
-        addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
-    }
-}
-
-fun createConfig(context: Context): UnfoldTransitionConfig =
-    ResourceUnfoldTransitionConfig(context)
+fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index e17f43e..409dc95 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -16,12 +16,14 @@
 package com.android.systemui.unfold
 
 import android.annotation.FloatRange
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 
 /**
  * Interface that allows to receive unfold transition progress updates.
+ *
  * It can be used to update view properties based on the current animation progress.
+ *
  * onTransitionProgress callback could be called on each frame.
  *
  * Use [createUnfoldTransitionProgressProvider] to create instances of this interface
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index 3f027e3..d1b0639 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -18,9 +18,8 @@
 import android.content.Context
 import android.os.SystemProperties
 
-internal class ResourceUnfoldTransitionConfig(
-    private val context: Context
-) : UnfoldTransitionConfig {
+internal class ResourceUnfoldTransitionConfig(private val context: Context) :
+    UnfoldTransitionConfig {
 
     override val isEnabled: Boolean
         get() = readIsEnabledResource() && isPropertyEnabled
@@ -29,19 +28,22 @@
         get() = readIsHingeAngleEnabled()
 
     private val isPropertyEnabled: Boolean
-        get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
-            UNFOLD_TRANSITION_PROPERTY_ENABLED) == UNFOLD_TRANSITION_PROPERTY_ENABLED
+        get() =
+            SystemProperties.getInt(
+                UNFOLD_TRANSITION_MODE_PROPERTY_NAME, UNFOLD_TRANSITION_PROPERTY_ENABLED) ==
+                UNFOLD_TRANSITION_PROPERTY_ENABLED
 
-    private fun readIsEnabledResource(): Boolean = context.resources
-        .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+    private fun readIsEnabledResource(): Boolean =
+        context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
 
-    private fun readIsHingeAngleEnabled(): Boolean = context.resources
-        .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
+    private fun readIsHingeAngleEnabled(): Boolean =
+        context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
 }
 
 /**
- * Temporary persistent property to control unfold transition mode
- * See [com.android.unfold.config.AnimationMode]
+ * Temporary persistent property to control unfold transition mode.
+ *
+ * See [com.android.unfold.config.AnimationMode].
  */
 private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled"
 private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
index 732882e..4c85b05 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -25,21 +25,17 @@
 import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 
-/**
- * Emits animation progress with fixed timing after unfolding
- */
+/** Emits animation progress with fixed timing after unfolding */
 internal class FixedTimingTransitionProgressProvider(
     private val foldStateProvider: FoldStateProvider
 ) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
 
     private val animatorListener = AnimatorListener()
     private val animator =
-        ObjectAnimator.ofFloat(this, AnimationProgressProperty, 0f, 1f)
-            .apply {
-                duration = TRANSITION_TIME_MILLIS
-                addListener(animatorListener)
-            }
-
+        ObjectAnimator.ofFloat(this, AnimationProgressProperty, 0f, 1f).apply {
+            duration = TRANSITION_TIME_MILLIS
+            addListener(animatorListener)
+        }
 
     private var transitionProgress: Float = 0.0f
         set(value) {
@@ -62,10 +58,8 @@
 
     override fun onFoldUpdate(@FoldUpdate update: Int) {
         when (update) {
-            FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE ->
-                animator.start()
-            FOLD_UPDATE_FINISH_CLOSED ->
-                animator.cancel()
+            FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> animator.start()
+            FOLD_UPDATE_FINISH_CLOSED -> animator.cancel()
         }
     }
 
@@ -77,16 +71,12 @@
         listeners.remove(listener)
     }
 
-    override fun onHingeAngleUpdate(angle: Float) {
-    }
+    override fun onHingeAngleUpdate(angle: Float) {}
 
     private object AnimationProgressProperty :
         FloatProperty<FixedTimingTransitionProgressProvider>("animation_progress") {
 
-        override fun setValue(
-            provider: FixedTimingTransitionProgressProvider,
-            value: Float
-        ) {
+        override fun setValue(provider: FixedTimingTransitionProgressProvider, value: Float) {
             provider.transitionProgress = value
         }
 
@@ -104,11 +94,9 @@
             listeners.forEach { it.onTransitionFinished() }
         }
 
-        override fun onAnimationRepeat(animator: Animator) {
-        }
+        override fun onAnimationRepeat(animator: Animator) {}
 
-        override fun onAnimationCancel(animator: Animator) {
-        }
+        override fun onAnimationCancel(animator: Animator) {}
     }
 
     private companion object {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index dc64f14..5266f09 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -23,10 +23,10 @@
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.updates.FOLD_UPDATE_ABORTED
 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
 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_UNFOLDED_SCREEN_AVAILABLE
 import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -35,13 +35,10 @@
 /** Maps fold updates to unfold transition progress using DynamicAnimation. */
 internal class PhysicsBasedUnfoldTransitionProgressProvider(
     private val foldStateProvider: FoldStateProvider
-) :
-    UnfoldTransitionProgressProvider,
-    FoldUpdatesListener,
-    DynamicAnimation.OnAnimationEndListener {
+) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
 
-    private val springAnimation = SpringAnimation(this, AnimationProgressProperty)
-        .apply {
+    private val springAnimation =
+        SpringAnimation(this, AnimationProgressProperty).apply {
             addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
         }
 
@@ -84,7 +81,7 @@
                     cancelTransition(endValue = 1f, animate = true)
                 }
             }
-            FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_ABORTED -> {
+            FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> {
                 // Do not cancel if we haven't started the transition yet.
                 // This could happen when we fully unfolded the device before the screen
                 // became available. In this case we start and immediately cancel the animation
@@ -121,9 +118,7 @@
             isTransitionRunning = false
             springAnimation.cancel()
 
-            listeners.forEach {
-                it.onTransitionFinished()
-            }
+            listeners.forEach { it.onTransitionFinished() }
 
             if (DEBUG) {
                 Log.d(TAG, "onTransitionFinished")
@@ -143,9 +138,7 @@
     }
 
     private fun onStartTransition() {
-        listeners.forEach {
-            it.onTransitionStarted()
-        }
+        listeners.forEach { it.onTransitionStarted() }
         isTransitionRunning = true
 
         if (DEBUG) {
@@ -157,11 +150,12 @@
         if (!isTransitionRunning) onStartTransition()
 
         springAnimation.apply {
-            spring = SpringForce().apply {
-                finalPosition = startValue
-                dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-                stiffness = SPRING_STIFFNESS
-            }
+            spring =
+                SpringForce().apply {
+                    finalPosition = startValue
+                    dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+                    stiffness = SPRING_STIFFNESS
+                }
             minimumVisibleChange = MINIMAL_VISIBLE_CHANGE
             setStartValue(startValue)
             setMinValue(0f)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 6d9631c..24ecf87 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -22,6 +22,7 @@
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
@@ -29,23 +30,24 @@
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import java.util.concurrent.Executor
+import javax.inject.Inject
 
-class DeviceFoldStateProvider(
+class DeviceFoldStateProvider
+@Inject
+constructor(
     context: Context,
     private val hingeAngleProvider: HingeAngleProvider,
     private val screenStatusProvider: ScreenStatusProvider,
     private val deviceStateManager: DeviceStateManager,
-    private val mainExecutor: Executor,
-    private val handler: Handler
+    @Main private val mainExecutor: Executor,
+    @Main private val handler: Handler
 ) : FoldStateProvider {
 
     private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
 
-    @FoldUpdate
-    private var lastFoldUpdate: Int? = null
+    @FoldUpdate private var lastFoldUpdate: Int? = null
 
-    @FloatRange(from = 0.0, to = 180.0)
-    private var lastHingeAngle: Float = 0f
+    @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
 
     private val hingeAngleListener = HingeAngleListener()
     private val screenListener = ScreenStatusListener()
@@ -56,10 +58,7 @@
     private var isUnfoldHandled = true
 
     override fun start() {
-        deviceStateManager.registerCallback(
-            mainExecutor,
-            foldStateListener
-        )
+        deviceStateManager.registerCallback(mainExecutor, foldStateListener)
         screenStatusProvider.addCallback(screenListener)
         hingeAngleProvider.addCallback(hingeAngleListener)
     }
@@ -83,11 +82,14 @@
         get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
 
     private val isTransitionInProgess: Boolean
-        get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
+        get() =
+            lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
                 lastFoldUpdate == FOLD_UPDATE_START_CLOSING
 
     private fun onHingeAngle(angle: Float) {
-        if (DEBUG) { Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") }
+        if (DEBUG) {
+            Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle")
+        }
 
         val isClosing = angle < lastHingeAngle
         val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
@@ -112,24 +114,28 @@
     }
 
     private inner class FoldStateListener(context: Context) :
-        DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
-            isFolded = folded
-            lastHingeAngle = FULLY_CLOSED_DEGREES
+        DeviceStateManager.FoldStateListener(
+            context,
+            { folded: Boolean ->
+                isFolded = folded
+                lastHingeAngle = FULLY_CLOSED_DEGREES
 
-            if (folded) {
-                hingeAngleProvider.stop()
-                notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
-                cancelTimeout()
-                isUnfoldHandled = false
-            } else {
-                notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
-                rescheduleAbortAnimationTimeout()
-                hingeAngleProvider.start()
-            }
-        })
+                if (folded) {
+                    hingeAngleProvider.stop()
+                    notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+                    cancelTimeout()
+                    isUnfoldHandled = false
+                } else {
+                    notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
+                    rescheduleAbortAnimationTimeout()
+                    hingeAngleProvider.start()
+                }
+            })
 
     private fun notifyFoldUpdate(@FoldUpdate update: Int) {
-        if (DEBUG) { Log.d(TAG, stateToString(update)) }
+        if (DEBUG) {
+            Log.d(TAG, stateToString(update))
+        }
         outputListeners.forEach { it.onFoldUpdate(update) }
         lastFoldUpdate = update
     }
@@ -138,15 +144,14 @@
         if (isTransitionInProgess) {
             cancelTimeout()
         }
-        handler.postDelayed(timeoutRunnable, ABORT_CLOSING_MILLIS)
+        handler.postDelayed(timeoutRunnable, HALF_OPENED_TIMEOUT_MILLIS)
     }
 
     private fun cancelTimeout() {
         handler.removeCallbacks(timeoutRunnable)
     }
 
-    private inner class ScreenStatusListener :
-        ScreenStatusProvider.ScreenListener {
+    private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
 
         override fun onScreenTurnedOn() {
             // Trigger this event only if we are unfolded and this is the first screen
@@ -163,16 +168,14 @@
     }
 
     private inner class HingeAngleListener : Consumer<Float> {
-
         override fun accept(angle: Float) {
             onHingeAngle(angle)
         }
     }
 
     private inner class TimeoutRunnable : Runnable {
-
         override fun run() {
-            notifyFoldUpdate(FOLD_UPDATE_ABORTED)
+            notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
         }
     }
 }
@@ -180,9 +183,7 @@
 private fun stateToString(@FoldUpdate update: Int): String {
     return when (update) {
         FOLD_UPDATE_START_OPENING -> "START_OPENING"
-        FOLD_UPDATE_HALF_OPEN -> "HALF_OPEN"
         FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
-        FOLD_UPDATE_ABORTED -> "ABORTED"
         FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
         FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN"
         FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN"
@@ -195,12 +196,10 @@
 private const val DEBUG = false
 
 /**
- * Time after which [FOLD_UPDATE_ABORTED] is emitted following a [FOLD_UPDATE_START_CLOSING] or
- * [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
+ * Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
+ * [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
  */
-@VisibleForTesting
-const val ABORT_CLOSING_MILLIS = 1000L
+@VisibleForTesting const val HALF_OPENED_TIMEOUT_MILLIS = 1000L
 
 /** Threshold after which we consider the device fully unfolded. */
-@VisibleForTesting
-const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
+@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index bffebcd..5495316 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -17,8 +17,8 @@
 
 import android.annotation.FloatRange
 import android.annotation.IntDef
-import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 
 /**
  * Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
@@ -35,25 +35,23 @@
         fun onFoldUpdate(@FoldUpdate update: Int)
     }
 
-    @IntDef(prefix = ["FOLD_UPDATE_"], value = [
-        FOLD_UPDATE_START_OPENING,
-        FOLD_UPDATE_HALF_OPEN,
-        FOLD_UPDATE_START_CLOSING,
-        FOLD_UPDATE_ABORTED,
-        FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
-        FOLD_UPDATE_FINISH_HALF_OPEN,
-        FOLD_UPDATE_FINISH_FULL_OPEN,
-        FOLD_UPDATE_FINISH_CLOSED
-    ])
+    @IntDef(
+        prefix = ["FOLD_UPDATE_"],
+        value =
+            [
+                FOLD_UPDATE_START_OPENING,
+                FOLD_UPDATE_START_CLOSING,
+                FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
+                FOLD_UPDATE_FINISH_HALF_OPEN,
+                FOLD_UPDATE_FINISH_FULL_OPEN,
+                FOLD_UPDATE_FINISH_CLOSED])
     @Retention(AnnotationRetention.SOURCE)
     annotation class FoldUpdate
 }
 
 const val FOLD_UPDATE_START_OPENING = 0
-const val FOLD_UPDATE_HALF_OPEN = 1
-const val FOLD_UPDATE_START_CLOSING = 2
-const val FOLD_UPDATE_ABORTED = 3
-const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 4
-const val FOLD_UPDATE_FINISH_HALF_OPEN = 5
-const val FOLD_UPDATE_FINISH_FULL_OPEN = 6
-const val FOLD_UPDATE_FINISH_CLOSED = 7
+const val FOLD_UPDATE_START_CLOSING = 1
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 2
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 3
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 4
+const val FOLD_UPDATE_FINISH_CLOSED = 5
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
index 9b58b1f..b351585 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -2,16 +2,12 @@
 
 import androidx.core.util.Consumer
 
-internal class EmptyHingeAngleProvider : HingeAngleProvider {
-    override fun start() {
-    }
+internal object EmptyHingeAngleProvider : HingeAngleProvider {
+    override fun start() {}
 
-    override fun stop() {
-    }
+    override fun stop() {}
 
-    override fun removeCallback(listener: Consumer<Float>) {
-    }
+    override fun removeCallback(listener: Consumer<Float>) {}
 
-    override fun addCallback(listener: Consumer<Float>) {
-    }
+    override fun addCallback(listener: Consumer<Float>) {}
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
index 6f52456..48a5b12 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -5,9 +5,10 @@
 
 /**
  * Emits device hinge angle values (angle between two integral parts of the device).
- * The hinge angle could be from 0 to 360 degrees inclusive.
- * For foldable devices usually 0 corresponds to fully closed (folded) state and
- * 180 degrees corresponds to fully open (flat) state
+ *
+ * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0
+ * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat)
+ * state.
  */
 interface HingeAngleProvider : CallbackController<Consumer<Float>> {
     fun start()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index a42ebef..f6fe1ed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -6,9 +6,8 @@
 import android.hardware.SensorManager
 import androidx.core.util.Consumer
 
-internal class HingeSensorAngleProvider(
-    private val sensorManager: SensorManager
-) : HingeAngleProvider {
+internal class HingeSensorAngleProvider(private val sensorManager: SensorManager) :
+    HingeAngleProvider {
 
     private val sensorListener = HingeAngleSensorListener()
     private val listeners: MutableList<Consumer<Float>> = arrayListOf()
@@ -32,8 +31,7 @@
 
     private inner class HingeAngleSensorListener : SensorEventListener {
 
-        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
-        }
+        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
 
         override fun onSensorChanged(event: SensorEvent) {
             listeners.forEach { it.accept(event.values[0]) }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
index 1eec803..668c694 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
@@ -15,8 +15,8 @@
  */
 package com.android.systemui.unfold.updates.screen
 
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
 import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
 
 interface ScreenStatusProvider : CallbackController<ScreenListener> {
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
new file mode 100644
index 0000000..1574c8d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.unfold.util
+
+import android.os.Trace
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+import javax.inject.Qualifier
+
+/**
+ * Listener that logs start and end of the fold-unfold transition.
+ *
+ * [tracePrefix] arg helps in differentiating those. Currently, this is expected to be logged twice
+ * for each fold/unfold: in (1) systemui and (2) launcher process.
+ */
+class ATraceLoggerTransitionProgressListener
+@Inject
+internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String) :
+    TransitionProgressListener {
+
+    private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
+
+    override fun onTransitionStarted() {
+        Trace.beginAsyncSection(traceName, /* cookie= */ 0)
+    }
+
+    override fun onTransitionFinished() {
+        Trace.endAsyncSection(traceName, /* cookie= */ 0)
+    }
+
+    override fun onTransitionProgress(progress: Float) {
+        Trace.setCounter(traceName, (progress * 100).toLong())
+    }
+}
+
+private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
+
+@Qualifier annotation class UnfoldTransitionATracePrefix
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
deleted file mode 100644
index f3eeb32..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.android.systemui.unfold.util
-
-import android.os.Trace
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-
-/**
- * Listener that logs start and end of the fold-unfold transition.
- *
- * [tracePrefix] arg helps in differentiating those. Currently, this is expected to be logged twice
- * for each fold/unfold: in (1) systemui and (2) launcher process.
- */
-class ATraceLoggerTransitionProgressListener(tracePrefix: String) : TransitionProgressListener {
-
-    private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
-
-    override fun onTransitionStarted() {
-        Trace.beginAsyncSection(traceName, /* cookie= */ 0)
-    }
-
-    override fun onTransitionFinished() {
-        Trace.endAsyncSection(traceName, /* cookie= */ 0)
-    }
-
-    override fun onTransitionProgress(progress: Float) {
-        Trace.setCounter(traceName, (progress * 100).toLong())
-    }
-}
-
-private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/JankMonitorTransitionProgressListener.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/JankMonitorTransitionProgressListener.kt
new file mode 100644
index 0000000..2b38f3d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/JankMonitorTransitionProgressListener.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold.util
+
+import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.jank.InteractionJankMonitor.CUJ_UNFOLD_ANIM
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.util.function.Supplier
+
+class JankMonitorTransitionProgressListener(private val attachedViewProvider: Supplier<View>) :
+    TransitionProgressListener {
+
+    private val interactionJankMonitor = InteractionJankMonitor.getInstance()
+
+    override fun onTransitionStarted() {
+        interactionJankMonitor.begin(attachedViewProvider.get(), CUJ_UNFOLD_ANIM)
+    }
+
+    override fun onTransitionFinished() {
+        interactionJankMonitor.end(CUJ_UNFOLD_ANIM)
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index 58d7dfb..53c528f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -10,9 +10,8 @@
 
 /**
  * [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
- * default rotation or 180 degrees opposite rotation (ROTATION_0 or ROTATION_180).
- * It could be helpful to run the animation only when the display's rotation is perpendicular
- * to the fold.
+ * default rotation or 180 degrees opposite rotation (ROTATION_0 or ROTATION_180). It could be
+ * helpful to run the animation only when the display's rotation is perpendicular to the fold.
  */
 class NaturalRotationUnfoldProgressProvider(
     private val context: Context,
@@ -21,7 +20,7 @@
 ) : UnfoldTransitionProgressProvider {
 
     private val scopedUnfoldTransitionProgressProvider =
-            ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+        ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
     private val rotationWatcher = RotationWatcher()
 
     private var isNaturalRotation: Boolean = false
@@ -37,8 +36,8 @@
     }
 
     private fun onRotationChanged(rotation: Int) {
-        val isNewRotationNatural = rotation == Surface.ROTATION_0 ||
-                rotation == Surface.ROTATION_180
+        val isNewRotationNatural =
+            rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
 
         if (isNaturalRotation != isNewRotationNatural) {
             isNaturalRotation = isNewRotationNatural
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index df9078a..dfe8792 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -6,27 +6,33 @@
 import android.provider.Settings
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
 /** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */
-class ScaleAwareTransitionProgressProvider(
-    unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+class ScaleAwareTransitionProgressProvider
+@AssistedInject
+constructor(
+    @Assisted progressProviderToWrap: UnfoldTransitionProgressProvider,
     private val contentResolver: ContentResolver
 ) : UnfoldTransitionProgressProvider {
 
     private val scopedUnfoldTransitionProgressProvider =
-            ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+        ScopedUnfoldTransitionProgressProvider(progressProviderToWrap)
 
-    private val animatorDurationScaleObserver = object : ContentObserver(null) {
-        override fun onChange(selfChange: Boolean) {
-            onAnimatorScaleChanged()
+    private val animatorDurationScaleObserver =
+        object : ContentObserver(null) {
+            override fun onChange(selfChange: Boolean) {
+                onAnimatorScaleChanged()
+            }
         }
-    }
 
     init {
         contentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
-                /* notifyForDescendants= */ false,
-                animatorDurationScaleObserver)
+            Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+            /* notifyForDescendants= */ false,
+            animatorDurationScaleObserver)
         onAnimatorScaleChanged()
     }
 
@@ -47,4 +53,11 @@
         contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
         scopedUnfoldTransitionProgressProvider.destroy()
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun wrap(
+            progressProvider: UnfoldTransitionProgressProvider
+        ): ScaleAwareTransitionProgressProvider
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index 22698a8..7b67917 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -20,16 +20,18 @@
 
 /**
  * Manages progress listeners that can have smaller lifespan than the unfold animation.
- * Allows to limit getting transition updates to only when
- * [ScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition] is called
- * with readyToHandleTransition = true
  *
- * If the transition has already started by the moment when the clients are ready to play
- * the transition then it will report transition started callback and current animation progress.
+ * Allows to limit getting transition updates to only when
+ * [ScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition] is called with
+ * readyToHandleTransition = true
+ *
+ * If the transition has already started by the moment when the clients are ready to play the
+ * transition then it will report transition started callback and current animation progress.
  */
-class ScopedUnfoldTransitionProgressProvider @JvmOverloads constructor(
-    source: UnfoldTransitionProgressProvider? = null
-) : UnfoldTransitionProgressProvider, TransitionProgressListener {
+class ScopedUnfoldTransitionProgressProvider
+@JvmOverloads
+constructor(source: UnfoldTransitionProgressProvider? = null) :
+    UnfoldTransitionProgressProvider, TransitionProgressListener {
 
     private var source: UnfoldTransitionProgressProvider? = null
 
@@ -43,8 +45,8 @@
         setSourceProvider(source)
     }
     /**
-     * Sets the source for the unfold transition progress updates,
-     * it replaces current provider if it is already set
+     * Sets the source for the unfold transition progress updates. Replaces current provider if it
+     * is already set
      * @param provider transition provider that emits transition progress updates
      */
     fun setSourceProvider(provider: UnfoldTransitionProgressProvider?) {
@@ -60,8 +62,10 @@
 
     /**
      * Allows to notify this provide whether the listeners can play the transition or not.
-     * Call this method with readyToHandleTransition = true when all listeners
-     * are ready to consume the transition progress events.
+     *
+     * Call this method with readyToHandleTransition = true when all listeners are ready to consume
+     * the transition progress events.
+     *
      * Call it with readyToHandleTransition = false when listeners can't process the events.
      */
     fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
@@ -89,6 +93,7 @@
 
     override fun destroy() {
         source?.removeCallback(this)
+        source?.destroy()
     }
 
     override fun onTransitionStarted() {
diff --git a/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java b/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
new file mode 100644
index 0000000..dc804ca
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+/** Constants that vary by compilation configuration. */
+public class Compile {
+    /** Whether SystemUI was compiled in debug mode, and supports debug features */
+    public static final boolean IS_DEBUG = true;
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/util/Compile.java b/packages/SystemUI/src-release/com/android/systemui/util/Compile.java
new file mode 100644
index 0000000..8a63763
--- /dev/null
+++ b/packages/SystemUI/src-release/com/android/systemui/util/Compile.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+/** Constants that vary by compilation configuration. */
+public class Compile {
+    /** Whether SystemUI was compiled in debug mode, and supports debug features */
+    public static final boolean IS_DEBUG = false;
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index cc6df45..d02b875 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -30,7 +30,6 @@
  */
 public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
     protected View mEcaView;
-    protected boolean mEnableHaptics;
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     // any passwords with length less than or equal to this length.
@@ -45,10 +44,6 @@
         super(context, attrs);
     }
 
-    void setEnableHaptics(boolean enableHaptics) {
-        mEnableHaptics = enableHaptics;
-    }
-
     protected abstract int getPasswordTextViewId();
     protected abstract void resetState();
 
@@ -80,11 +75,9 @@
 
     // Cause a VIRTUAL_KEY vibration
     public void doHapticKeyClick() {
-        if (mEnableHaptics) {
-            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
-                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
-        }
+        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+                | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
     }
 
     public void setKeyDownListener(KeyDownListener keyDownListener) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 1c4559e..f86d08d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -98,7 +98,6 @@
     protected void onViewAttached() {
         super.onViewAttached();
         mView.setKeyDownListener(mKeyDownListener);
-        mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
         mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
new file mode 100644
index 0000000..214b284
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.keyguard
+
+import android.content.Context
+import android.hardware.biometrics.BiometricSourceType
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
+import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Logs events when primary authentication requirements change. Primary authentication is considered
+ * authentication using pin/pattern/password input.
+ *
+ * See [PrimaryAuthRequiredEvent] for all the events and their descriptions.
+ */
+@SysUISingleton
+class KeyguardBiometricLockoutLogger @Inject constructor(
+    context: Context?,
+    private val uiEventLogger: UiEventLogger,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val dumpManager: DumpManager
+) : CoreStartable(context) {
+    private var fingerprintLockedOut = false
+    private var faceLockedOut = false
+    private var encryptedOrLockdown = false
+    private var unattendedUpdate = false
+    private var timeout = false
+
+    override fun start() {
+        dumpManager.registerDumpable(this)
+        mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
+                KeyguardUpdateMonitor.getCurrentUser())
+        keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
+    }
+
+    private val mKeyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback =
+            object : KeyguardUpdateMonitorCallback() {
+        override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType) {
+            if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+                val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut
+                if (lockedOut && !fingerprintLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+                } else if (!lockedOut && fingerprintLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent
+                                    .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+                }
+                fingerprintLockedOut = lockedOut
+            } else if (biometricSourceType == BiometricSourceType.FACE) {
+                val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
+                if (lockedOut && !faceLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+                } else if (!lockedOut && faceLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+                }
+                faceLockedOut = lockedOut
+            }
+        }
+
+        override fun onStrongAuthStateChanged(userId: Int) {
+            if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+                return
+            }
+            val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker
+                    .getStrongAuthForUser(userId)
+
+            val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId)
+            if (newEncryptedOrLockdown && !encryptedOrLockdown) {
+                uiEventLogger.log(
+                        PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+            }
+            encryptedOrLockdown = newEncryptedOrLockdown
+
+            val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
+            if (newUnattendedUpdate && !unattendedUpdate) {
+                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+            }
+            unattendedUpdate = newUnattendedUpdate
+
+            val newTimeout = isStrongAuthTimeout(strongAuthFlags)
+            if (newTimeout && !timeout) {
+                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
+            }
+            timeout = newTimeout
+        }
+    }
+
+    private fun isUnattendedUpdate(
+        @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
+    ) = containsFlag(flags, STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
+
+    private fun isStrongAuthTimeout(
+        @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
+    ) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) ||
+            containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT)
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+        pw.println("  mFingerprintLockedOut=$fingerprintLockedOut")
+        pw.println("  mFaceLockedOut=$faceLockedOut")
+        pw.println("  mIsEncryptedOrLockdown=$encryptedOrLockdown")
+        pw.println("  mIsUnattendedUpdate=$unattendedUpdate")
+        pw.println("  mIsTimeout=$timeout")
+    }
+
+    /**
+     * Events pertaining to whether primary authentication (pin/pattern/password input) is required
+     * for device entry.
+     */
+    @VisibleForTesting
+    enum class PrimaryAuthRequiredEvent(private val mId: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Fingerprint cannot be used to authenticate for device entry. This" +
+                "can persist until the next primary auth or may timeout.")
+        PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT(924),
+
+        @UiEvent(doc = "Fingerprint can be used to authenticate for device entry.")
+        PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET(925),
+
+        @UiEvent(doc = "Face cannot be used to authenticate for device entry.")
+        PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT(926),
+
+        @UiEvent(doc = "Face can be used to authenticate for device entry.")
+        PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET(927),
+
+        @UiEvent(doc = "Device is encrypted (ie: after reboot) or device is locked down by DPM " +
+                "or a manual user lockdown.")
+        PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN(928),
+
+        @UiEvent(doc = "Primary authentication is required because it hasn't been used for a " +
+                "time required by a device admin or because primary auth hasn't been used for a " +
+                "time after a non-strong biometric (weak or convenience) is used to unlock the " +
+                "device.")
+        PRIMARY_AUTH_REQUIRED_TIMEOUT(929),
+
+        @UiEvent(doc = "Strong authentication is required to prepare for unattended upgrade.")
+        PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE(931);
+
+        override fun getId(): Int {
+            return mId
+        }
+    }
+
+    companion object {
+        private fun containsFlag(strongAuthFlags: Int, flagCheck: Int): Boolean {
+            return strongAuthFlags and flagCheck != 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 0340904..b2658c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import android.util.Log;
+
 /**
  * Defines constants for the Keyguard.
  */
@@ -25,7 +27,7 @@
      * Turns on debugging information for the whole Keyguard. This is very verbose and should only
      * be used temporarily for debugging.
      */
-    public static final boolean DEBUG = false;
+    public static final boolean DEBUG = Log.isLoggable("Keyguard", Log.DEBUG);
     public static final boolean DEBUG_SIM_STATES = true;
     public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 8c7ede2..848b8ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -262,7 +262,6 @@
         private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
         private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
         private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
-        private final Context mContext;
         private KeyguardClockSwitchController mKeyguardClockSwitchController;
         private View mClock;
         private int mUsableWidth;
@@ -286,7 +285,6 @@
                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
             mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
             setCancelable(false);
-            mContext = context;
         }
 
         @Override
@@ -311,7 +309,7 @@
 
             updateBounds();
 
-            setContentView(LayoutInflater.from(mContext)
+            setContentView(LayoutInflater.from(getContext())
                     .inflate(R.layout.keyguard_presentation, null));
 
             // Logic to make the lock screen fullscreen
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 26c227d..8bcb7c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -91,7 +91,8 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_DISABLE_ESIM),
-                PERMISSION_SELF, null /* scheduler */);
+                PERMISSION_SELF, null /* scheduler */,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     public static boolean isEsimLocked(Context context, int subId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 03f04d3..36fe5ba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -64,3 +64,19 @@
     val secureCameraLaunched: Boolean,
     val switchingUser: Boolean
 ) : KeyguardListenModel()
+/**
+ * Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock].
+ */
+data class KeyguardActiveUnlockModel(
+    @CurrentTimeMillisLong override val timeMillis: Long,
+    override val userId: Int,
+    override val listening: Boolean,
+    // keep sorted
+    val authInterruptActive: Boolean,
+    val encryptedOrTimedOut: Boolean,
+    val fpLockout: Boolean,
+    val lockDown: Boolean,
+    val switchingUser: Boolean,
+    val triggerActiveUnlockForAssistant: Boolean,
+    val userCanDismissLockScreen: Boolean
+) : KeyguardListenModel()
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
index f13a59a..210f5e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
@@ -32,15 +32,17 @@
 ) {
     private val faceQueue = ArrayDeque<KeyguardFaceListenModel>()
     private val fingerprintQueue = ArrayDeque<KeyguardFingerprintListenModel>()
+    private val activeUnlockQueue = ArrayDeque<KeyguardActiveUnlockModel>()
 
     @get:VisibleForTesting val models: List<KeyguardListenModel>
-        get() = faceQueue + fingerprintQueue
+        get() = faceQueue + fingerprintQueue + activeUnlockQueue
 
     /** Push a [model] to the queue (will be logged until the queue exceeds [sizePerModality]). */
     fun add(model: KeyguardListenModel) {
         val queue = when (model) {
             is KeyguardFaceListenModel -> faceQueue.apply { add(model) }
             is KeyguardFingerprintListenModel -> fingerprintQueue.apply { add(model) }
+            is KeyguardActiveUnlockModel -> activeUnlockQueue.apply { add(model) }
         }
 
         if (queue.size > sizePerModality) {
@@ -63,5 +65,9 @@
         for (model in fingerprintQueue) {
             writer.println(stringify(model))
         }
+        writer.println("  Active unlock triggers (last ${activeUnlockQueue.size} calls):")
+        for (model in activeUnlockQueue) {
+            writer.println(stringify(model))
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 099dd5d..75579b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -30,6 +30,8 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.policy.SystemBarUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
@@ -57,6 +59,11 @@
     private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
     private boolean mBouncerVisible;
     private boolean mAltBouncerShowing;
+    /**
+     * Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't
+     * contain {@link R.id.keyguard_message_area_container}.
+     */
+    @Nullable
     private ViewGroup mContainer;
     private int mTopMargin;
 
@@ -75,6 +82,9 @@
     }
 
     void onConfigChanged() {
+        if (mContainer == null) {
+            return;
+        }
         final int newTopMargin = SystemBarUtils.getStatusBarHeight(getContext());
         if (mTopMargin == newTopMargin) {
             return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 94e07b7..238acd5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -224,8 +224,6 @@
         mLockPatternView.setSaveEnabled(false);
         mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
                 KeyguardUpdateMonitor.getCurrentUser()));
-        // vibrate mode will be the same for the life of this screen
-        mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
         mLockPatternView.setOnTouchListener((v, event) -> {
             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                 mFalsingCollector.avoidGesture();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b5ea498..3fab724 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -616,9 +616,13 @@
             final View view = getChildAt(i);
             if (view.getVisibility() != GONE) {
                 int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
+                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+                // When using EXACTLY spec, measure will use the layout width if > 0. Set before
+                // measuring the child
+                lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
                 measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
 
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
                 maxWidth = Math.max(maxWidth,
                         view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                 maxHeight = Math.max(maxHeight,
@@ -969,7 +973,7 @@
         public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
             return MeasureSpec.makeMeasureSpec(
                     MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
-                    MeasureSpec.getMode(parentWidthMeasureSpec));
+                    MeasureSpec.EXACTLY);
         }
 
         @Override
@@ -1026,7 +1030,7 @@
         public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
             return MeasureSpec.makeMeasureSpec(
                     MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
-                    MeasureSpec.getMode(parentWidthMeasureSpec));
+                    MeasureSpec.EXACTLY);
         }
 
         private void updateSecurityViewGravity() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 2fb22111..49a8022 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -51,6 +51,8 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -83,6 +85,7 @@
     private final FalsingManager mFalsingManager;
     private final UserSwitcherController mUserSwitcherController;
     private final GlobalSettings mGlobalSettings;
+    private final FeatureFlags mFeatureFlags;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
 
@@ -238,6 +241,7 @@
             FalsingCollector falsingCollector,
             FalsingManager falsingManager,
             UserSwitcherController userSwitcherController,
+            FeatureFlags featureFlags,
             GlobalSettings globalSettings) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
@@ -255,6 +259,7 @@
         mFalsingCollector = falsingCollector;
         mFalsingManager = falsingManager;
         mUserSwitcherController = userSwitcherController;
+        mFeatureFlags = featureFlags;
         mGlobalSettings = globalSettings;
     }
 
@@ -510,7 +515,7 @@
     }
 
     private boolean canDisplayUserSwitcher() {
-        return getResources().getBoolean(R.bool.bouncer_display_user_switcher);
+        return mFeatureFlags.isEnabled(Flags.BOUNCER_USER_SWITCHER);
     }
 
     private void configureMode() {
@@ -615,6 +620,7 @@
         private final FalsingCollector mFalsingCollector;
         private final FalsingManager mFalsingManager;
         private final GlobalSettings mGlobalSettings;
+        private final FeatureFlags mFeatureFlags;
         private final UserSwitcherController mUserSwitcherController;
 
         @Inject
@@ -632,6 +638,7 @@
                 FalsingCollector falsingCollector,
                 FalsingManager falsingManager,
                 UserSwitcherController userSwitcherController,
+                FeatureFlags featureFlags,
                 GlobalSettings globalSettings) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
@@ -645,6 +652,7 @@
             mConfigurationController = configurationController;
             mFalsingCollector = falsingCollector;
             mFalsingManager = falsingManager;
+            mFeatureFlags = featureFlags;
             mGlobalSettings = globalSettings;
             mUserSwitcherController = userSwitcherController;
         }
@@ -656,8 +664,7 @@
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
                     mConfigurationController, mFalsingCollector, mFalsingManager,
-                    mUserSwitcherController, mGlobalSettings);
+                    mUserSwitcherController, mFeatureFlags, mGlobalSettings);
         }
-
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 98721fd..f2d0427 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -143,8 +143,10 @@
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_FINGERPRINT = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_ACTIVE_UNLOCK = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_SPEW = false;
     private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
+    private int mNumActiveUnlockTriggers = 0;
 
     private static final String ACTION_FACE_UNLOCK_STARTED
             = "com.android.facelock.FACE_UNLOCK_STARTED";
@@ -183,7 +185,6 @@
     private static final int MSG_USER_STOPPED = 340;
     private static final int MSG_USER_REMOVED = 341;
     private static final int MSG_KEYGUARD_GOING_AWAY = 342;
-    private static final int MSG_LOCK_SCREEN_MODE = 343;
     private static final int MSG_TIME_FORMAT_UPDATE = 344;
     private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
 
@@ -221,7 +222,6 @@
     private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
     public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
 
-    private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
     /**
      * If no cancel signal has been received after this amount of time, set the biometric running
      * state to stopped to allow Keyguard to retry authentication.
@@ -231,7 +231,6 @@
     private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
             "com.android.settings", "com.android.settings.FallbackHome");
 
-
     /**
      * If true, the system is in the half-boot-to-decryption-screen state.
      * Prudently disable lockscreen.
@@ -438,7 +437,8 @@
     }
 
     @Override
-    public void onTrustChanged(boolean enabled, int userId, int flags) {
+    public void onTrustChanged(boolean enabled, int userId, int flags,
+            List<String> trustGrantedMessages) {
         Assert.isMainThread();
         boolean wasTrusted = mUserHasTrust.get(userId, false);
         mUserHasTrust.put(userId, enabled);
@@ -460,6 +460,19 @@
                 }
             }
         }
+
+        if (KeyguardUpdateMonitor.getCurrentUser() == userId && getUserHasTrust(userId)) {
+            CharSequence message = null;
+            if (trustGrantedMessages != null && trustGrantedMessages.size() > 0) {
+                message = trustGrantedMessages.get(0); // for now only shows the first in the list
+            }
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+                if (cb != null) {
+                    cb.showTrustGrantedMessage(message);
+                }
+            }
+        }
     }
 
     @Override
@@ -1250,7 +1263,11 @@
                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
     }
 
-    private boolean isEncryptedOrLockdown(int userId) {
+    /**
+     * Returns true if primary authentication is required for the given user due to lockdown
+     * or encryption after reboot.
+     */
+    public boolean isEncryptedOrLockdown(int userId) {
         final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
         final boolean isLockDown =
                 containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
@@ -1311,6 +1328,9 @@
     void setAssistantVisible(boolean assistantVisible) {
         mAssistantVisible = assistantVisible;
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+        if (mAssistantVisible) {
+            requestActiveUnlock();
+        }
     }
 
     static class DisplayClientState {
@@ -1650,6 +1670,7 @@
         Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
         Assert.isMainThread();
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+        requestActiveUnlock();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -2180,6 +2201,7 @@
         }
         mAuthInterruptActive = active;
         updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+        requestActiveUnlock();
     }
 
     /**
@@ -2228,6 +2250,75 @@
         }
     }
 
+    /**
+     * Attempts to trigger active unlock.
+     */
+    public void requestActiveUnlock() {
+        // If this message exists, FP has already authenticated, so wait until that is handled
+        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+            return;
+        }
+
+        if (shouldTriggerActiveUnlock()) {
+            mTrustManager.reportUserRequestedUnlock(KeyguardUpdateMonitor.getCurrentUser());
+        }
+    }
+
+    private boolean shouldTriggerActiveUnlock() {
+        // Triggers:
+        final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
+        final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
+                && mStatusBarState != StatusBarState.SHADE_LOCKED;
+
+        // Gates:
+        final int user = getCurrentUser();
+
+        // No need to trigger active unlock if we're already unlocked or don't have
+        // pin/pattern/password setup
+        final boolean userCanDismissLockScreen = getUserCanSkipBouncer(user)
+                || !mLockPatternUtils.isSecure(user);
+
+        // Don't trigger active unlock if fp is locked out
+        final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+
+        // Don't trigger active unlock if primary auth is required
+        final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
+        final boolean isLockDown =
+                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+        final boolean isEncryptedOrTimedOut =
+                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+
+        final boolean shouldTriggerActiveUnlock =
+                (mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard)
+                        && !mSwitchingUser
+                        && !userCanDismissLockScreen
+                        && !fpLockedout
+                        && !isLockDown
+                        && !isEncryptedOrTimedOut
+                        && !mKeyguardGoingAway
+                        && !mSecureCameraLaunched;
+
+        // Aggregate relevant fields for debug logging.
+        if (DEBUG_ACTIVE_UNLOCK || DEBUG_SPEW) {
+            maybeLogListenerModelData(
+                    new KeyguardActiveUnlockModel(
+                            System.currentTimeMillis(),
+                            user,
+                            shouldTriggerActiveUnlock,
+                            mAuthInterruptActive,
+                            isEncryptedOrTimedOut,
+                            fpLockedout,
+                            isLockDown,
+                            mSwitchingUser,
+                            triggerActiveUnlockForAssistant,
+                            userCanDismissLockScreen));
+        }
+
+        return shouldTriggerActiveUnlock;
+    }
+
     private boolean shouldListenForFingerprintAssistant() {
         BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser());
         return mAssistantVisible && mKeyguardOccluded
@@ -2242,6 +2333,11 @@
                 && !mUserHasTrust.get(getCurrentUser(), false);
     }
 
+    private boolean shouldTriggerActiveUnlockForAssistant() {
+        return mAssistantVisible && mKeyguardOccluded
+                && !mUserHasTrust.get(getCurrentUser(), false);
+    }
+
     @VisibleForTesting
     protected boolean shouldListenForFingerprint(boolean isUdfps) {
         final int user = getCurrentUser();
@@ -2406,6 +2502,13 @@
             Log.v(TAG, model.toString());
         }
 
+        if (DEBUG_ACTIVE_UNLOCK
+                && model instanceof KeyguardActiveUnlockModel
+                && model.getListening()) {
+            mListenModels.add(model);
+            return;
+        }
+
         // Add model data to the historical buffer.
         final boolean notYetRunning =
                 (DEBUG_FACE
@@ -2514,6 +2617,10 @@
         return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
     }
 
+    public boolean isFaceLockedOut() {
+        return mFaceLockedOutPermanent;
+    }
+
     /**
      * If biometrics hardware is available, not disabled, and user has enrolled templates.
      * This does NOT check if the device is encrypted or in lockdown.
@@ -3171,11 +3278,19 @@
     }
 
     public void clearBiometricRecognized() {
+        clearBiometricRecognized(UserHandle.USER_NULL);
+    }
+
+    public void clearBiometricRecognizedWhenKeyguardDone(int unlockedUser) {
+        clearBiometricRecognized(unlockedUser);
+    }
+
+    private void clearBiometricRecognized(int unlockedUser) {
         Assert.isMainThread();
         mUserFingerprintAuthenticated.clear();
         mUserFaceAuthenticated.clear();
-        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
-        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE);
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
 
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index a74fd15..47e1035 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -23,6 +23,8 @@
 import android.telephony.TelephonyManager;
 import android.view.WindowManagerPolicyConstants;
 
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 
@@ -216,6 +218,11 @@
     public void onTrustGrantedWithFlags(int flags, int userId) { }
 
     /**
+     * Called when setting the trust granted message.
+     */
+    public void showTrustGrantedMessage(@Nullable CharSequence message) { }
+
+    /**
      * Called when a biometric has been acquired.
      * <p>
      * It is guaranteed that either {@link #onBiometricAuthenticated} or
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index e79ea9a..a5a3f80 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -213,10 +213,8 @@
 
     // Cause a VIRTUAL_KEY vibration
     public void doHapticKeyClick() {
-        if (mLockPatternUtils.isTactileFeedbackEnabled()) {
-            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
-                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
-        }
+        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+                | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 33538ec..9c2971c 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -96,6 +96,8 @@
 import com.android.systemui.util.concurrency.ThreadFactory;
 import com.android.systemui.util.settings.SecureSettings;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -107,7 +109,7 @@
  * for antialiasing and emulation purposes.
  */
 @SysUISingleton
-public class ScreenDecorations extends CoreStartable implements Tunable {
+public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable{
     private static final boolean DEBUG = false;
     private static final String TAG = "ScreenDecorations";
 
@@ -342,7 +344,7 @@
             mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
             mDensity = metrics.density;
 
-            mExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
+            mMainExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
 
             // Watch color inversion and invert the overlay as needed.
             if (mColorInversionSetting == null) {
@@ -677,6 +679,20 @@
         });
     }
 
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("ScreenDecorations state:");
+        pw.println("  DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS);
+        pw.println("  mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
+        pw.println("  mIsPrivacyDotEnabled:" + mIsPrivacyDotEnabled);
+        pw.println("  mPendingRotationChange:" + mPendingRotationChange);
+        pw.println("  mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
+        pw.println("  mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
+                + ")");
+        pw.println("  mRoundedDefaultBottom(x,y)=(" + mRoundedDefaultBottom.x + ","
+                + mRoundedDefaultBottom.y + ")");
+    }
+
     private void updateOrientation() {
         Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
                 "must call on " + mHandler.getLooper().getThread()
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index d7da63b..1f2de4c 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -112,7 +112,8 @@
 
         public void register(Context context, ComponentName receiver, IntentFilter filter) {
             mReceivers.add(receiver);
-            context.registerReceiver(this, filter);
+            context.registerReceiver(this, filter,
+                    Context.RECEIVER_EXPORTED_UNAUDITED);
         }
 
         public void unregister(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 63962fa..daca918 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -32,6 +32,9 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Dumpable;
+import android.util.DumpableContainer;
 import android.util.Log;
 import android.util.TimingsTraceLog;
 import android.view.SurfaceControl;
@@ -53,13 +56,19 @@
  * Application class for SystemUI.
  */
 public class SystemUIApplication extends Application implements
-        SystemUIAppComponentFactory.ContextInitializer {
+        SystemUIAppComponentFactory.ContextInitializer, DumpableContainer {
 
     public static final String TAG = "SystemUIService";
     private static final boolean DEBUG = false;
 
     private ContextComponentHelper mComponentHelper;
     private BootCompleteCacheImpl mBootCompleteCache;
+    private DumpManager mDumpManager;
+
+    /**
+     * Map of dumpables added externally.
+     */
+    private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
 
     /**
      * Hold a reference on the stuff we start.
@@ -214,7 +223,7 @@
             }
         }
 
-        final DumpManager dumpManager = mSysUIComponent.createDumpManager();
+        mDumpManager = mSysUIComponent.createDumpManager();
 
         Log.v(TAG, "Starting SystemUI services for user " +
                 Process.myUserHandle().getIdentifier() + ".");
@@ -255,7 +264,7 @@
                 mServices[i].onBootCompleted();
             }
 
-            dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
+            mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
         }
         mSysUIComponent.getInitController().executePostInitTasks();
         log.traceEnd();
@@ -263,6 +272,29 @@
         mServicesStarted = true;
     }
 
+    // TODO(b/149254050): add unit tests? There doesn't seem to be a SystemUiApplicationTest...
+    @Override
+    public boolean addDumpable(Dumpable dumpable) {
+        String name = dumpable.getDumpableName();
+        if (mDumpables.containsKey(name)) {
+            // This is normal because SystemUIApplication is an application context that is shared
+            // among multiple components
+            if (DEBUG) {
+                Log.d(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+                        + " with that name (" + name + "): " + mDumpables.get(name));
+            }
+            return false;
+        }
+        if (DEBUG) Log.d(TAG, "addDumpable(): adding '" + name + "' = " + dumpable);
+        mDumpables.put(name, dumpable);
+
+        // TODO(b/149254050): replace com.android.systemui.dump.Dumpable by
+        // com.android.util.Dumpable and get rid of the intermediate lambda
+        mDumpManager.registerDumpable(dumpable.getDumpableName(),
+                (fd, pw, args) -> dumpable.dump(pw, args));
+        return true;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         if (mServicesStarted) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 2767904..b3be877 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -122,7 +122,8 @@
                     .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
                     .setRecentTasks(mWMComponent.getRecentTasks())
                     .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
-                    .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
+                    .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()))
+                    .setBackAnimation(mWMComponent.getBackAnimation());
         } else {
             // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
             // is separating this logic into newly creating SystemUITestsFactory.
@@ -142,7 +143,8 @@
                     .setTaskSurfaceHelper(Optional.ofNullable(null))
                     .setRecentTasks(Optional.ofNullable(null))
                     .setCompatUI(Optional.ofNullable(null))
-                    .setDragAndDrop(Optional.ofNullable(null));
+                    .setDragAndDrop(Optional.ofNullable(null))
+                    .setBackAnimation(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
         if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 052ec86..dbd215d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -22,8 +22,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.UiContext;
+import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -54,7 +56,8 @@
  * The button icon is movable by dragging and it would not overlap navigation bar window.
  * And the button UI would automatically be dismissed after displaying for a period of time.
  */
-class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener {
+class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener,
+        ComponentCallbacks {
 
     @VisibleForTesting
     static final long FADING_ANIMATION_DURATION_MS = 300;
@@ -75,6 +78,7 @@
     private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
     private final LayoutParams mParams;
     private final SwitchListener mSwitchListener;
+    private final Configuration mConfiguration;
     @VisibleForTesting
     final Rect mDraggableWindowBounds = new Rect();
     private boolean mIsVisible = false;
@@ -101,6 +105,7 @@
     MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
             SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SwitchListener switchListener) {
         mContext = context;
+        mConfiguration = new Configuration(context.getResources().getConfiguration());
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mSfVsyncFrameProvider = sfVsyncFrameProvider;
@@ -270,6 +275,7 @@
         mIsFadeOutAnimating = false;
         mImageView.setAlpha(0f);
         mWindowManager.removeView(mImageView);
+        mContext.unregisterComponentCallbacks(this);
         mIsVisible = false;
     }
 
@@ -291,6 +297,8 @@
             mImageView.setImageResource(getIconResId(mode));
         }
         if (!mIsVisible) {
+            onConfigurationChanged(mContext.getResources().getConfiguration());
+            mContext.registerComponentCallbacks(this);
             if (resetPosition) {
                 mDraggableWindowBounds.set(getDraggableWindowBounds());
                 mParams.x = mDraggableWindowBounds.right;
@@ -321,7 +329,21 @@
         }
     }
 
+    @Override
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
+        final int configDiff = newConfig.diff(mConfiguration);
+        mConfiguration.setTo(newConfig);
+        onConfigurationChanged(configDiff);
+    }
+
+    @Override
+    public void onLowMemory() {
+    }
+
     void onConfigurationChanged(int configDiff) {
+        if (configDiff == 0) {
+            return;
+        }
         if ((configDiff & (ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE))
                 != 0) {
             final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index a10efa9..885a177 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -23,7 +23,6 @@
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
@@ -65,7 +64,6 @@
     private final OverviewProxyService mOverviewProxyService;
 
     private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
-    private Configuration mLastConfiguration;
     private SysUiState mSysUiState;
 
     private static class ControllerSupplier extends
@@ -107,7 +105,6 @@
             SysUiState sysUiState, OverviewProxyService overviewProxyService) {
         super(context);
         mHandler = mainHandler;
-        mLastConfiguration = new Configuration(context.getResources().getConfiguration());
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mCommandQueue = commandQueue;
         mModeSwitchesController = modeSwitchesController;
@@ -118,18 +115,6 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        final int configDiff = newConfig.diff(mLastConfiguration);
-        mLastConfiguration.setTo(newConfig);
-        mMagnificationControllerSupplier.forEach(
-                magnificationController -> magnificationController.onConfigurationChanged(
-                        configDiff));
-        if (mModeSwitchesController != null) {
-            mModeSwitchesController.onConfigurationChanged(configDiff);
-        }
-    }
-
-    @Override
     public void start() {
         mCommandQueue.addCallback(this);
         mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
@@ -225,6 +210,13 @@
     }
 
     @Override
+    public void onDrag(int displayId) {
+        if (mWindowMagnificationConnectionImpl != null) {
+            mWindowMagnificationConnectionImpl.onDrag(displayId);
+        }
+    }
+
+    @Override
     public void requestWindowMagnificationConnection(boolean connect) {
         if (connect) {
             setWindowMagnificationConnection();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 2133da2..1d22633 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -142,4 +142,14 @@
             }
         }
     }
+
+    void onDrag(int displayId) {
+        if (mConnectionCallback != null) {
+            try {
+                mConnectionCallback.onDrag(displayId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to inform taking control by a user", e);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index b064ba9..0d20403 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -26,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
+import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -76,7 +77,8 @@
  * Class to handle adding and removing a window magnification.
  */
 class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback,
-        MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
+        MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener,
+        ComponentCallbacks {
 
     private static final String TAG = "WindowMagnificationController";
     @SuppressWarnings("isloggabletaglength")
@@ -143,6 +145,7 @@
     private View mTopDrag;
     private View mRightDrag;
     private View mBottomDrag;
+    private final Configuration mConfiguration;
 
     @NonNull
     private final WindowMagnifierCallback mWindowMagnifierCallback;
@@ -191,6 +194,7 @@
         mSfVsyncFrameProvider = sfVsyncFrameProvider;
         mWindowMagnifierCallback = callback;
         mSysUiState = sysUiState;
+        mConfiguration = new Configuration(context.getResources().getConfiguration());
 
         final Display display = mContext.getDisplay();
         mDisplayId = mContext.getDisplayId();
@@ -252,7 +256,12 @@
                                 mMagnificationFrame.height());
                         mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect,
                                 Surface.ROTATION_0).apply();
-                        mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds);
+
+                        // Notify source bounds change when the magnifier is not animating.
+                        if (!mAnimationController.isAnimating()) {
+                            mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId,
+                                    mSourceBounds);
+                        }
                     }
                 };
         mUpdateStateDescriptionRunnable = () -> {
@@ -334,6 +343,18 @@
         }
         mMirrorViewBounds.setEmpty();
         updateSystemUIStateIfNeeded();
+        mContext.unregisterComponentCallbacks(this);
+    }
+
+    @Override
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
+        final int configDiff = newConfig.diff(mConfiguration);
+        mConfiguration.setTo(newConfig);
+        onConfigurationChanged(configDiff);
+    }
+
+    @Override
+    public void onLowMemory() {
     }
 
     /**
@@ -346,6 +367,9 @@
             Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString(
                     configDiff));
         }
+        if (configDiff == 0) {
+            return;
+        }
         if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
             onRotate();
         }
@@ -385,7 +409,7 @@
 
         if (currentWindowBounds.equals(oldWindowBounds)) {
             if (DEBUG) {
-                Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed");
+                Log.d(TAG, "handleScreenSizeChanged -- window bounds is not changed");
             }
             return false;
         }
@@ -596,7 +620,6 @@
     private void modifyWindowMagnification(SurfaceControl.Transaction t) {
         mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
         updateMirrorViewLayout();
-
     }
 
     /**
@@ -800,7 +823,7 @@
      *                          are as same as current values, or the transition is interrupted
      *                          due to the new transition request.
      */
-    void enableWindowMagnification(float scale, float centerX, float centerY,
+    public void enableWindowMagnification(float scale, float centerX, float centerY,
             float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
             @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
         mAnimationController.enableWindowMagnification(scale, centerX, centerY,
@@ -847,6 +870,10 @@
             deleteWindowMagnification();
             return;
         }
+        if (!isWindowVisible()) {
+            onConfigurationChanged(mResources.getConfiguration());
+            mContext.registerComponentCallbacks(this);
+        }
 
         mMagnificationFrameOffsetX = Float.isNaN(magnificationFrameOffsetRatioX)
                 ? mMagnificationFrameOffsetX
@@ -960,6 +987,7 @@
     @Override
     public boolean onDrag(float offsetX, float offsetY) {
         moveWindowMagnifier(offsetX, offsetY);
+        mWindowMagnifierCallback.onDrag(mDisplayId);
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
index 628a5e8..bdded10 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility;
 
 import android.graphics.Rect;
+import android.view.ViewConfiguration;
 
 /**
  * A callback to inform {@link com.android.server.accessibility.AccessibilityManagerService} about
@@ -53,4 +54,13 @@
      * @param displayId The logical display id.
      */
     void onAccessibilityActionPerformed(int displayId);
+
+    /**
+     * Called when the user is performing dragging gesture. It is started after the offset
+     * between the down location and the move event location exceed
+     * {@link ViewConfiguration#getScaledTouchSlop()}.
+     *
+     * @param displayId The logical display id.
+     */
+    void onDrag(int displayId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 1ac9016..2b12f67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -518,7 +518,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
 
-        context.registerReceiver(mBroadcastReceiver, filter);
+        context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index ab8162f..11498db 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -107,6 +107,5 @@
         mLockPatternView.setOnPatternListener(new UnlockPatternListener());
         mLockPatternView.setInStealthMode(
                 !mLockPatternUtils.isVisiblePatternEnabled(mUserId));
-        mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index 7bb4708..4c00735 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -26,6 +26,7 @@
 import android.hardware.biometrics.BiometricOverlayConstants
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.SensorLocationInternal
 import android.hardware.display.DisplayManager
 import android.hardware.fingerprint.FingerprintManager
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
@@ -113,6 +114,7 @@
                 orientationListener.enable()
             }
         }
+    private var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT
 
     private val overlayViewParams = WindowManager.LayoutParams(
         WindowManager.LayoutParams.WRAP_CONTENT,
@@ -158,11 +160,19 @@
         val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
         val display = context.display!!
 
+        val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
+            if (location == null) {
+                Log.w(TAG, "No location specified for display: ${display.uniqueId}")
+            }
+            location ?: sensorProps.location
+        }
+        overlayOffsets = offsets
+
         val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
-        lottie.setAnimation(display.asSideFpsAnimation())
-        view.rotation = display.asSideFpsAnimationRotation()
+        view.rotation = display.asSideFpsAnimationRotation(offsets.isYAligned())
 
         updateOverlayParams(display, lottie.composition?.bounds ?: Rect())
+        lottie.setAnimation(display.asSideFpsAnimation(offsets.isYAligned()))
         lottie.addLottieOnCompositionLoadedListener {
             if (overlayView == view) {
                 updateOverlayParams(display, it.bounds)
@@ -179,24 +189,37 @@
         val size = windowManager.maximumWindowMetrics.bounds
         val displayWidth = if (isPortrait) size.width() else size.height()
         val displayHeight = if (isPortrait) size.height() else size.width()
-        val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
-            if (location == null) {
-                Log.w(TAG, "No location specified for display: ${display.uniqueId}")
-            }
-            location ?: sensorProps.location
-        }
 
-        // ignore sensorLocationX and sensorRadius since it's assumed to be on the side
-        // of the device and centered at sensorLocationY
-        val (x, y) = when (display.rotation) {
-            Surface.ROTATION_90 ->
-                Pair(offsets.sensorLocationY, 0)
-            Surface.ROTATION_270 ->
-                Pair(displayHeight - offsets.sensorLocationY - bounds.width(), displayWidth)
-            Surface.ROTATION_180 ->
-                Pair(0, displayHeight - offsets.sensorLocationY - bounds.height())
-            else ->
-                Pair(displayWidth, offsets.sensorLocationY)
+        // ignore sensorRadius since it's assumed that the sensor is on the side and centered at
+        // either sensorLocationX or sensorLocationY (both should not be set)
+        val (x, y) = if (overlayOffsets.isYAligned()) {
+            when (display.rotation) {
+                Surface.ROTATION_90 ->
+                    Pair(overlayOffsets.sensorLocationY, 0)
+                Surface.ROTATION_270 ->
+                    Pair(
+                        displayHeight - overlayOffsets.sensorLocationY - bounds.width(),
+                        displayWidth + bounds.height()
+                    )
+                Surface.ROTATION_180 ->
+                    Pair(0, displayHeight - overlayOffsets.sensorLocationY - bounds.height())
+                else ->
+                    Pair(displayWidth, overlayOffsets.sensorLocationY)
+            }
+        } else {
+            when (display.rotation) {
+                Surface.ROTATION_90 ->
+                    Pair(0, displayWidth - overlayOffsets.sensorLocationX - bounds.height())
+                Surface.ROTATION_270 ->
+                    Pair(displayWidth, overlayOffsets.sensorLocationX - bounds.height())
+                Surface.ROTATION_180 ->
+                    Pair(
+                        displayWidth - overlayOffsets.sensorLocationX - bounds.width(),
+                        displayHeight
+                    )
+                else ->
+                    Pair(overlayOffsets.sensorLocationX, 0)
+            }
         }
         overlayViewParams.x = x
         overlayViewParams.y = y
@@ -209,8 +232,10 @@
 
         // hide after a few seconds if the sensor is oriented down and there are
         // large overlapping system bars
-        if ((context.display?.rotation == Surface.ROTATION_270) &&
-            windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar()) {
+        val rotation = context.display?.rotation
+        if (windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar() &&
+            ((rotation == Surface.ROTATION_270 && overlayOffsets.isYAligned()) ||
+                    (rotation == Surface.ROTATION_180 && !overlayOffsets.isYAligned()))) {
             overlayHideAnimator = view.animate()
                 .alpha(0f)
                 .setStartDelay(3_000)
@@ -245,18 +270,21 @@
     getTasks(1).firstOrNull()?.topActivity?.className ?: ""
 
 @RawRes
-private fun Display.asSideFpsAnimation(): Int = when (rotation) {
-    Surface.ROTATION_0 -> R.raw.sfps_pulse
-    Surface.ROTATION_180 -> R.raw.sfps_pulse
-    else -> R.raw.sfps_pulse_landscape
+private fun Display.asSideFpsAnimation(yAligned: Boolean): Int = when (rotation) {
+    Surface.ROTATION_0 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
+    Surface.ROTATION_180 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
+    else -> if (yAligned) R.raw.sfps_pulse_landscape else R.raw.sfps_pulse
 }
 
-private fun Display.asSideFpsAnimationRotation(): Float = when (rotation) {
+private fun Display.asSideFpsAnimationRotation(yAligned: Boolean): Float = when (rotation) {
+    Surface.ROTATION_90 -> if (yAligned) 0f else 180f
     Surface.ROTATION_180 -> 180f
-    Surface.ROTATION_270 -> 180f
+    Surface.ROTATION_270 -> if (yAligned) 180f else 0f
     else -> 0f
 }
 
+private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0
+
 private fun Display.isPortrait(): Boolean =
     rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index fd7ae32..6581490 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -53,6 +53,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
@@ -130,6 +131,7 @@
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
+    @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
 
     // Tracks the velocity of a touch to help filter out the touches that move too fast.
     @Nullable private VelocityTracker mVelocityTracker;
@@ -198,7 +200,8 @@
                             mLockscreenShadeTransitionController, mConfigurationController,
                             mSystemClock, mKeyguardStateController,
                             mUnlockedScreenOffAnimationController, mSensorProps, mHbmProvider,
-                            reason, callback, UdfpsController.this::onTouch)));
+                            reason, callback, UdfpsController.this::onTouch,
+                            mActivityLaunchAnimator)));
         }
 
         @Override
@@ -487,7 +490,8 @@
             @NonNull SystemClock systemClock,
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull SystemUIDialogManager dialogManager,
-            @NonNull LatencyTracker latencyTracker) {
+            @NonNull LatencyTracker latencyTracker,
+            @NonNull ActivityLaunchAnimator activityLaunchAnimator) {
         mContext = context;
         mExecution = execution;
         mVibrator = vibrator;
@@ -516,6 +520,7 @@
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLatencyTracker = latencyTracker;
+        mActivityLaunchAnimator = activityLaunchAnimator;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -534,7 +539,8 @@
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        context.registerReceiver(mBroadcastReceiver, filter);
+        context.registerReceiver(mBroadcastReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         udfpsHapticsSimulator.setUdfpsController(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index e091250..590963b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -41,6 +41,7 @@
 import androidx.annotation.LayoutRes
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -81,7 +82,8 @@
     private var hbmProvider: UdfpsHbmProvider,
     @ShowReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
-    private val onTouch: (View, MotionEvent, Boolean) -> Boolean
+    private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+    private val activityLaunchAnimator: ActivityLaunchAnimator
 ) {
     /** The view, when [isShowing], or null. */
     var overlayView: UdfpsView? = null
@@ -200,7 +202,8 @@
                     keyguardStateController,
                     unlockedScreenOffAnimationController,
                     dialogManager,
-                    controller
+                    controller,
+                    activityLaunchAnimator
                 )
             }
             BiometricOverlayConstants.REASON_AUTH_BP -> {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index e701511..1317492 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -29,9 +29,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
 
-import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -43,18 +41,11 @@
 public class UdfpsEnrollDrawable extends UdfpsDrawable {
     private static final String TAG = "UdfpsAnimationEnroll";
 
-    private static final long HINT_COLOR_ANIM_DELAY_MS = 233L;
-    private static final long HINT_COLOR_ANIM_DURATION_MS = 517L;
-    private static final long HINT_WIDTH_ANIM_DURATION_MS = 233L;
     private static final long TARGET_ANIM_DURATION_LONG = 800L;
     private static final long TARGET_ANIM_DURATION_SHORT = 600L;
     // 1 + SCALE_MAX is the maximum that the moving target will animate to
     private static final float SCALE_MAX = 0.25f;
 
-    private static final float HINT_PADDING_DP = 10f;
-    private static final float HINT_MAX_WIDTH_DP = 6f;
-    private static final float HINT_ANGLE = 40f;
-
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     @NonNull private final Drawable mMovingTargetFpIcon;
@@ -72,30 +63,10 @@
     // Moving target size
     float mCurrentScale = 1.f;
 
-    @ColorInt private final int mHintColorFaded;
-    @ColorInt private final int mHintColorHighlight;
-    private final float mHintMaxWidthPx;
-    private final float mHintPaddingPx;
-
     @NonNull private final Animator.AnimatorListener mTargetAnimListener;
 
     private boolean mShouldShowTipHint = false;
-    @NonNull private final Paint mTipHintPaint;
-    @Nullable private AnimatorSet mTipHintAnimatorSet;
-    @Nullable private ValueAnimator mTipHintColorAnimator;
-    @Nullable private ValueAnimator mTipHintWidthAnimator;
-    @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintColorUpdateListener;
-    @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintWidthUpdateListener;
-    @NonNull private final Animator.AnimatorListener mTipHintPulseListener;
-
     private boolean mShouldShowEdgeHint = false;
-    @NonNull private final Paint mEdgeHintPaint;
-    @Nullable private AnimatorSet mEdgeHintAnimatorSet;
-    @Nullable private ValueAnimator mEdgeHintColorAnimator;
-    @Nullable private ValueAnimator mEdgeHintWidthAnimator;
-    @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintColorUpdateListener;
-    @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintWidthUpdateListener;
-    @NonNull private final Animator.AnimatorListener mEdgeHintPulseListener;
 
     UdfpsEnrollDrawable(@NonNull Context context) {
         super(context);
@@ -117,11 +88,6 @@
 
         getFingerprintDrawable().setTint(context.getColor(R.color.udfps_enroll_icon));
 
-        mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
-        mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
-        mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
-        mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
-
         mTargetAnimListener = new Animator.AnimatorListener() {
             @Override
             public void onAnimationStart(Animator animation) {}
@@ -137,80 +103,6 @@
             @Override
             public void onAnimationRepeat(Animator animation) {}
         };
-
-        mTipHintPaint = new Paint(0 /* flags */);
-        mTipHintPaint.setAntiAlias(true);
-        mTipHintPaint.setColor(mHintColorFaded);
-        mTipHintPaint.setStyle(Paint.Style.STROKE);
-        mTipHintPaint.setStrokeCap(Paint.Cap.ROUND);
-        mTipHintPaint.setStrokeWidth(0f);
-        mTipHintColorUpdateListener = animation -> {
-            mTipHintPaint.setColor((int) animation.getAnimatedValue());
-            invalidateSelf();
-        };
-        mTipHintWidthUpdateListener = animation -> {
-            mTipHintPaint.setStrokeWidth((float) animation.getAnimatedValue());
-            invalidateSelf();
-        };
-        mTipHintPulseListener = new Animator.AnimatorListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {}
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mHandler.postDelayed(() -> {
-                    mTipHintColorAnimator =
-                            ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorFaded);
-                    mTipHintColorAnimator.setInterpolator(new LinearInterpolator());
-                    mTipHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS);
-                    mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener);
-                    mTipHintColorAnimator.start();
-                }, HINT_COLOR_ANIM_DELAY_MS);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {}
-
-            @Override
-            public void onAnimationRepeat(Animator animation) {}
-        };
-
-        mEdgeHintPaint = new Paint(0 /* flags */);
-        mEdgeHintPaint.setAntiAlias(true);
-        mEdgeHintPaint.setColor(mHintColorFaded);
-        mEdgeHintPaint.setStyle(Paint.Style.STROKE);
-        mEdgeHintPaint.setStrokeCap(Paint.Cap.ROUND);
-        mEdgeHintPaint.setStrokeWidth(0f);
-        mEdgeHintColorUpdateListener = animation -> {
-            mEdgeHintPaint.setColor((int) animation.getAnimatedValue());
-            invalidateSelf();
-        };
-        mEdgeHintWidthUpdateListener = animation -> {
-            mEdgeHintPaint.setStrokeWidth((float) animation.getAnimatedValue());
-            invalidateSelf();
-        };
-        mEdgeHintPulseListener = new Animator.AnimatorListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {}
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mHandler.postDelayed(() -> {
-                    mEdgeHintColorAnimator =
-                            ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorFaded);
-                    mEdgeHintColorAnimator.setInterpolator(new LinearInterpolator());
-                    mEdgeHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS);
-                    mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener);
-                    mEdgeHintColorAnimator.start();
-                }, HINT_COLOR_ANIM_DELAY_MS);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {}
-
-            @Override
-            public void onAnimationRepeat(Animator animation) {}
-        };
     }
 
     void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
@@ -287,25 +179,12 @@
 
     private void updateTipHintVisibility() {
         final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isTipEnrollmentStage();
+        // With the new update, we will git rid of most of this code, and instead
+        // we will change the fingerprint icon.
         if (mShouldShowTipHint == shouldShow) {
             return;
         }
         mShouldShowTipHint = shouldShow;
-
-        if (mTipHintWidthAnimator != null && mTipHintWidthAnimator.isRunning()) {
-            mTipHintWidthAnimator.cancel();
-        }
-
-        final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f;
-        mTipHintWidthAnimator = ValueAnimator.ofFloat(mTipHintPaint.getStrokeWidth(), targetWidth);
-        mTipHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
-        mTipHintWidthAnimator.addUpdateListener(mTipHintWidthUpdateListener);
-
-        if (shouldShow) {
-            startTipHintPulseAnimation();
-        } else {
-            mTipHintWidthAnimator.start();
-        }
     }
 
     private void updateEdgeHintVisibility() {
@@ -314,71 +193,6 @@
             return;
         }
         mShouldShowEdgeHint = shouldShow;
-
-        if (mEdgeHintWidthAnimator != null && mEdgeHintWidthAnimator.isRunning()) {
-            mEdgeHintWidthAnimator.cancel();
-        }
-
-        final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f;
-        mEdgeHintWidthAnimator =
-                ValueAnimator.ofFloat(mEdgeHintPaint.getStrokeWidth(), targetWidth);
-        mEdgeHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
-        mEdgeHintWidthAnimator.addUpdateListener(mEdgeHintWidthUpdateListener);
-
-        if (shouldShow) {
-            startEdgeHintPulseAnimation();
-        } else {
-            mEdgeHintWidthAnimator.start();
-        }
-    }
-
-    private void startTipHintPulseAnimation() {
-        mHandler.removeCallbacksAndMessages(null);
-        if (mTipHintAnimatorSet != null && mTipHintAnimatorSet.isRunning()) {
-            mTipHintAnimatorSet.cancel();
-        }
-        if (mTipHintColorAnimator != null && mTipHintColorAnimator.isRunning()) {
-            mTipHintColorAnimator.cancel();
-        }
-
-        mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorHighlight);
-        mTipHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
-        mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener);
-        mTipHintColorAnimator.addListener(mTipHintPulseListener);
-
-        mTipHintAnimatorSet = new AnimatorSet();
-        mTipHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
-        mTipHintAnimatorSet.playTogether(mTipHintColorAnimator, mTipHintWidthAnimator);
-        mTipHintAnimatorSet.start();
-    }
-
-    private void startEdgeHintPulseAnimation() {
-        mHandler.removeCallbacksAndMessages(null);
-        if (mEdgeHintAnimatorSet != null && mEdgeHintAnimatorSet.isRunning()) {
-            mEdgeHintAnimatorSet.cancel();
-        }
-        if (mEdgeHintColorAnimator != null && mEdgeHintColorAnimator.isRunning()) {
-            mEdgeHintColorAnimator.cancel();
-        }
-
-        mEdgeHintColorAnimator =
-                ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorHighlight);
-        mEdgeHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
-        mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener);
-        mEdgeHintColorAnimator.addListener(mEdgeHintPulseListener);
-
-        mEdgeHintAnimatorSet = new AnimatorSet();
-        mEdgeHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
-        mEdgeHintAnimatorSet.playTogether(mEdgeHintColorAnimator, mEdgeHintWidthAnimator);
-        mEdgeHintAnimatorSet.start();
-    }
-
-    private boolean isTipHintVisible() {
-        return mTipHintPaint.getStrokeWidth() > 0f;
-    }
-
-    private boolean isEdgeHintVisible() {
-        return mEdgeHintPaint.getStrokeWidth() > 0f;
     }
 
     @Override
@@ -409,58 +223,6 @@
             mSensorOutlinePaint.setAlpha(getAlpha());
         }
 
-        // Draw the finger tip or edges hint.
-        if (isTipHintVisible() || isEdgeHintVisible()) {
-            canvas.save();
-
-            // Make arcs start from the top, rather than the right.
-            canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY());
-
-            final float halfSensorHeight = Math.abs(mSensorRect.bottom - mSensorRect.top) / 2f;
-            final float halfSensorWidth = Math.abs(mSensorRect.right - mSensorRect.left) / 2f;
-            final float hintXOffset = halfSensorWidth + mHintPaddingPx;
-            final float hintYOffset = halfSensorHeight + mHintPaddingPx;
-
-            if (isTipHintVisible()) {
-                canvas.drawArc(
-                        mSensorRect.centerX() - hintXOffset,
-                        mSensorRect.centerY() - hintYOffset,
-                        mSensorRect.centerX() + hintXOffset,
-                        mSensorRect.centerY() + hintYOffset,
-                        -HINT_ANGLE / 2f,
-                        HINT_ANGLE,
-                        false /* useCenter */,
-                        mTipHintPaint);
-            }
-
-            if (isEdgeHintVisible()) {
-                // Draw right edge hint.
-                canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY());
-                canvas.drawArc(
-                        mSensorRect.centerX() - hintXOffset,
-                        mSensorRect.centerY() - hintYOffset,
-                        mSensorRect.centerX() + hintXOffset,
-                        mSensorRect.centerY() + hintYOffset,
-                        -HINT_ANGLE / 2f,
-                        HINT_ANGLE,
-                        false /* useCenter */,
-                        mEdgeHintPaint);
-
-                // Draw left edge hint.
-                canvas.rotate(180f, mSensorRect.centerX(), mSensorRect.centerY());
-                canvas.drawArc(
-                        mSensorRect.centerX() - hintXOffset,
-                        mSensorRect.centerY() - hintYOffset,
-                        mSensorRect.centerX() + hintXOffset,
-                        mSensorRect.centerY() + hintYOffset,
-                        -HINT_ANGLE / 2f,
-                        HINT_ANGLE,
-                        false /* useCenter */,
-                        mEdgeHintPaint);
-            }
-
-            canvas.restore();
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index d90a746..3ece3cc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -25,6 +25,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -55,6 +56,7 @@
     @NonNull private final UdfpsController mUdfpsController;
     @NonNull private final UnlockedScreenOffAnimationController
             mUnlockedScreenOffAnimationController;
+    @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
 
     private boolean mShowingUdfpsBouncer;
     private boolean mUdfpsRequested;
@@ -66,6 +68,8 @@
     private long mLastUdfpsBouncerShowTime = -1;
     private float mStatusBarExpansion;
     private boolean mLaunchTransitionFadingAway;
+    private boolean mIsLaunchingActivity;
+    private float mActivityLaunchProgress;
 
     /**
      * hidden amount of pin/pattern/password bouncer
@@ -88,7 +92,8 @@
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull SystemUIDialogManager systemUIDialogManager,
-            @NonNull UdfpsController udfpsController) {
+            @NonNull UdfpsController udfpsController,
+            @NonNull ActivityLaunchAnimator activityLaunchAnimator) {
         super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
                 dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -99,6 +104,7 @@
         mKeyguardStateController = keyguardStateController;
         mUdfpsController = udfpsController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+        mActivityLaunchAnimator = activityLaunchAnimator;
     }
 
     @Override
@@ -136,6 +142,7 @@
         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
         mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
+        mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
     }
 
     @Override
@@ -153,6 +160,7 @@
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
         mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
+        mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener);
     }
 
     @Override
@@ -216,7 +224,8 @@
 
         if (mUdfpsRequested && !getNotificationShadeVisible()
                 && (!mIsBouncerVisible
-                || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)) {
+                || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)
+                && mKeyguardStateController.isShowing()) {
             return false;
         }
 
@@ -294,6 +303,12 @@
                     0f, 255f);
         if (!mShowingUdfpsBouncer) {
             alpha *= (1.0f - mTransitionToFullShadeProgress);
+
+            // Fade out the icon if we are animating an activity launch over the lockscreen and the
+            // activity didn't request the UDFPS.
+            if (mIsLaunchingActivity && !mUdfpsRequested) {
+                alpha *= (1.0f - mActivityLaunchProgress);
+            }
         }
         mView.setUnpausedAlpha(alpha);
     }
@@ -426,4 +441,26 @@
 
     private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
             (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
+
+    private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
+            new ActivityLaunchAnimator.Listener() {
+                @Override
+                public void onLaunchAnimationStart() {
+                    mIsLaunchingActivity = true;
+                    mActivityLaunchProgress = 0f;
+                    updateAlpha();
+                }
+
+                @Override
+                public void onLaunchAnimationEnd() {
+                    mIsLaunchingActivity = false;
+                    updateAlpha();
+                }
+
+                @Override
+                public void onLaunchAnimationProgress(float linearProgress) {
+                    mActivityLaunchProgress = linearProgress;
+                    updateAlpha();
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
new file mode 100644
index 0000000..41a4963
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
+
+import static java.util.Objects.requireNonNull;
+
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.provider.DeviceConfig;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.TimeoutHandler;
+
+import javax.inject.Inject;
+
+/**
+ * ClipboardListener brings up a clipboard overlay when something is copied to the clipboard.
+ */
+@SysUISingleton
+public class ClipboardListener extends CoreStartable
+        implements ClipboardManager.OnPrimaryClipChangedListener {
+
+    private ClipboardOverlayController mClipboardOverlayController;
+    private ClipboardManager mClipboardManager;
+
+    @Inject
+    public ClipboardListener(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void start() {
+        if (DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, false)) {
+            mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
+            mClipboardManager.addPrimaryClipChangedListener(this);
+        }
+    }
+
+    @Override
+    public void onPrimaryClipChanged() {
+        if (!mClipboardManager.hasPrimaryClip()) {
+            return;
+        }
+        if (mClipboardOverlayController == null) {
+            mClipboardOverlayController = new ClipboardOverlayController(mContext,
+                    new TimeoutHandler(mContext));
+        }
+        mClipboardOverlayController.setClipData(mClipboardManager.getPrimaryClip());
+        mClipboardOverlayController.setOnSessionCompleteListener(() -> {
+            // Session is complete, free memory until it's needed again.
+            mClipboardOverlayController = null;
+        });
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
new file mode 100644
index 0000000..ae0702c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.MainThread;
+import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
+import android.hardware.input.InputManager;
+import android.net.Uri;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Size;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.Gravity;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.InputMonitor;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.policy.PhoneWindow;
+import com.android.systemui.R;
+import com.android.systemui.screenshot.FloatingWindowUtil;
+import com.android.systemui.screenshot.ScreenshotActionChip;
+import com.android.systemui.screenshot.TimeoutHandler;
+
+import java.io.IOException;
+
+/**
+ * Controls state and UI for the overlay that appears when something is added to the clipboard
+ */
+public class ClipboardOverlayController {
+    private static final String TAG = "ClipboardOverlayCtrlr";
+    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
+
+    /** Constants for screenshot/copy deconflicting */
+    public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
+    public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
+    public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";
+
+    private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
+
+    private final Context mContext;
+    private final DisplayManager mDisplayManager;
+    private final WindowManager mWindowManager;
+    private final WindowManager.LayoutParams mWindowLayoutParams;
+    private final PhoneWindow mWindow;
+    private final TimeoutHandler mTimeoutHandler;
+
+    private final DraggableConstraintLayout mView;
+    private final ImageView mImagePreview;
+    private final TextView mTextPreview;
+    private final ScreenshotActionChip mEditChip;
+    private final ScreenshotActionChip mRemoteCopyChip;
+    private final View mActionContainerBackground;
+
+    private Runnable mOnSessionCompleteListener;
+
+    private InputEventReceiver mInputEventReceiver;
+
+    private BroadcastReceiver mCloseDialogsReceiver;
+    private BroadcastReceiver mScreenshotReceiver;
+
+    private boolean mBlockAttach = false;
+
+    public ClipboardOverlayController(Context context, TimeoutHandler timeoutHandler) {
+        mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
+        final Context displayContext = context.createDisplayContext(getDefaultDisplay());
+        mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
+
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+
+        mTimeoutHandler = timeoutHandler;
+        mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
+
+        // Setup the window that we are going to use
+        mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
+        mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mWindowLayoutParams.height = WRAP_CONTENT;
+        mWindowLayoutParams.gravity = Gravity.BOTTOM;
+        mWindowLayoutParams.setTitle("ClipboardOverlay");
+        mWindow = FloatingWindowUtil.getFloatingWindow(mContext);
+        mWindow.setWindowManager(mWindowManager, null, null);
+
+        mView = (DraggableConstraintLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay, null);
+        mActionContainerBackground = requireNonNull(
+                mView.findViewById(R.id.actions_container_background));
+        mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
+        mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
+        mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
+        mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
+
+        mView.setOnDismissCallback(this::hideImmediate);
+        mView.setOnInteractionCallback(() -> mTimeoutHandler.resetTimeout());
+
+        mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
+        mRemoteCopyChip.setIcon(
+                Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24), true);
+
+        // Only show remote copy if it's available.
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.resolveActivity(getRemoteCopyIntent(), 0) != null) {
+            mRemoteCopyChip.setOnClickListener((v) -> {
+                showNearby();
+            });
+            mRemoteCopyChip.setAlpha(1f);
+        } else {
+            mRemoteCopyChip.setVisibility(View.GONE);
+        }
+
+        attachWindow();
+        withWindowAttached(() -> {
+            mWindow.setContentView(mView);
+            updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
+            getEnterAnimation().start();
+        });
+
+        mTimeoutHandler.setOnTimeoutRunnable(() -> animateOut());
+
+        mCloseDialogsReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                    animateOut();
+                }
+            }
+        };
+        mContext.registerReceiver(mCloseDialogsReceiver,
+                new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
+
+        mScreenshotReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (SCREENSHOT_ACTION.equals(intent.getAction())) {
+                    animateOut();
+                }
+            }
+        };
+        mContext.registerReceiver(mScreenshotReceiver, new IntentFilter(SCREENSHOT_ACTION),
+                SELF_PERMISSION, null);
+        monitorOutsideTouches();
+
+        mContext.sendBroadcast(new Intent(COPY_OVERLAY_ACTION), SELF_PERMISSION);
+    }
+
+    void setClipData(ClipData clipData) {
+        reset();
+
+        if (clipData == null || clipData.getItemCount() == 0) {
+            showTextPreview(
+                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
+        } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
+            showEditableText(clipData.getItemAt(0).getText());
+        } else if (clipData.getItemAt(0).getUri() != null) {
+            // How to handle non-image URIs?
+            showEditableImage(clipData.getItemAt(0).getUri());
+        } else {
+            showTextPreview(
+                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
+        }
+
+        mTimeoutHandler.resetTimeout();
+    }
+
+    void setOnSessionCompleteListener(Runnable runnable) {
+        mOnSessionCompleteListener = runnable;
+    }
+
+    private void monitorOutsideTouches() {
+        InputManager inputManager = mContext.getSystemService(InputManager.class);
+        InputMonitor monitor = inputManager.monitorGestureInput("clipboard overlay", 0);
+        mInputEventReceiver = new InputEventReceiver(monitor.getInputChannel(),
+                Looper.getMainLooper()) {
+            @Override
+            public void onInputEvent(InputEvent event) {
+                if (event instanceof MotionEvent) {
+                    MotionEvent motionEvent = (MotionEvent) event;
+                    if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                        int[] pt = new int[2];
+                        mView.getLocationOnScreen(pt);
+                        Rect rect = new Rect(pt[0], pt[1], pt[0] + mView.getWidth(),
+                                pt[1] + mView.getHeight());
+                        if (!rect.contains((int) motionEvent.getRawX(),
+                                (int) motionEvent.getRawY())) {
+                            animateOut();
+                        }
+                    }
+                }
+                finishInputEvent(event, true /* handled */);
+            }
+        };
+    }
+
+    private void editImage(Uri uri) {
+        String editorPackage = mContext.getString(R.string.config_screenshotEditor);
+        Intent editIntent = new Intent(Intent.ACTION_EDIT);
+        if (!TextUtils.isEmpty(editorPackage)) {
+            editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+        }
+        editIntent.setDataAndType(uri, "image/*");
+        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(editIntent);
+        animateOut();
+    }
+
+    private void editText() {
+        Intent editIntent = new Intent(mContext, EditTextActivity.class);
+        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(editIntent);
+        animateOut();
+    }
+
+    private void showNearby() {
+        mContext.startActivity(getRemoteCopyIntent());
+        animateOut();
+    }
+
+    private void showTextPreview(CharSequence text) {
+        mTextPreview.setVisibility(View.VISIBLE);
+        mImagePreview.setVisibility(View.GONE);
+        mTextPreview.setText(text);
+        mEditChip.setVisibility(View.GONE);
+    }
+
+    private void showEditableText(CharSequence text) {
+        showTextPreview(text);
+        mEditChip.setVisibility(View.VISIBLE);
+        mEditChip.setAlpha(1f);
+        View.OnClickListener listener = v -> editText();
+        mEditChip.setOnClickListener(listener);
+        mTextPreview.setOnClickListener(listener);
+    }
+
+    private void showEditableImage(Uri uri) {
+        mTextPreview.setVisibility(View.GONE);
+        mImagePreview.setVisibility(View.VISIBLE);
+        mEditChip.setAlpha(1f);
+        ContentResolver resolver = mContext.getContentResolver();
+        try {
+            int size = mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale);
+            // The width of the view is capped, height maintains aspect ratio, so allow it to be
+            // taller if needed.
+            Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
+            mImagePreview.setImageBitmap(thumbnail);
+        } catch (IOException e) {
+            Log.e(TAG, "Thumbnail loading failed", e);
+        }
+        View.OnClickListener listener = v -> editImage(uri);
+        mEditChip.setOnClickListener(listener);
+        mImagePreview.setOnClickListener(listener);
+    }
+
+    private Intent getRemoteCopyIntent() {
+        Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
+        nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        return nearbyIntent;
+    }
+
+    private void animateOut() {
+        getExitAnimation().start();
+    }
+
+    private ValueAnimator getEnterAnimation() {
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+
+        mView.setAlpha(0);
+        final View previewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
+        final View actionBackground = requireNonNull(
+                mView.findViewById(R.id.actions_container_background));
+        mImagePreview.setVisibility(View.VISIBLE);
+        mActionContainerBackground.setVisibility(View.VISIBLE);
+
+        anim.addUpdateListener(animation -> {
+            mView.setAlpha(animation.getAnimatedFraction());
+            float scale = 0.6f + 0.4f * animation.getAnimatedFraction();
+            mView.setPivotY(mView.getHeight() - previewBorder.getHeight() / 2f);
+            mView.setPivotX(actionBackground.getWidth() / 2f);
+            mView.setScaleX(scale);
+            mView.setScaleY(scale);
+        });
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mView.setAlpha(1);
+                mTimeoutHandler.resetTimeout();
+            }
+        });
+        return anim;
+    }
+
+    private ValueAnimator getExitAnimation() {
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+
+        anim.addUpdateListener(animation -> {
+            mView.setAlpha(1 - animation.getAnimatedFraction());
+            final View actionBackground = requireNonNull(
+                    mView.findViewById(R.id.actions_container_background));
+            mView.setTranslationX(
+                    -animation.getAnimatedFraction() * actionBackground.getWidth() / 2);
+        });
+
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                hideImmediate();
+            }
+        });
+
+        return anim;
+    }
+
+    private void hideImmediate() {
+        // Note this may be called multiple times if multiple dismissal events happen at the same
+        // time.
+        mTimeoutHandler.cancelTimeout();
+        final View decorView = mWindow.peekDecorView();
+        if (decorView != null && decorView.isAttachedToWindow()) {
+            mWindowManager.removeViewImmediate(decorView);
+        }
+        if (mCloseDialogsReceiver != null) {
+            mContext.unregisterReceiver(mCloseDialogsReceiver);
+            mCloseDialogsReceiver = null;
+        }
+        if (mScreenshotReceiver != null) {
+            mContext.unregisterReceiver(mScreenshotReceiver);
+            mScreenshotReceiver = null;
+        }
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.dispose();
+            mInputEventReceiver = null;
+        }
+        if (mOnSessionCompleteListener != null) {
+            mOnSessionCompleteListener.run();
+        }
+    }
+
+    private void reset() {
+        mView.setTranslationX(0);
+        mView.setAlpha(1);
+        mTimeoutHandler.cancelTimeout();
+    }
+
+    @MainThread
+    private void attachWindow() {
+        View decorView = mWindow.getDecorView();
+        if (decorView.isAttachedToWindow() || mBlockAttach) {
+            return;
+        }
+        mBlockAttach = true;
+        mWindowManager.addView(decorView, mWindowLayoutParams);
+        decorView.requestApplyInsets();
+        mView.requestApplyInsets();
+        decorView.getViewTreeObserver().addOnWindowAttachListener(
+                new ViewTreeObserver.OnWindowAttachListener() {
+                    @Override
+                    public void onWindowAttached() {
+                        mBlockAttach = false;
+                    }
+
+                    @Override
+                    public void onWindowDetached() {
+                    }
+                }
+        );
+    }
+
+    private void withWindowAttached(Runnable action) {
+        View decorView = mWindow.getDecorView();
+        if (decorView.isAttachedToWindow()) {
+            action.run();
+        } else {
+            decorView.getViewTreeObserver().addOnWindowAttachListener(
+                    new ViewTreeObserver.OnWindowAttachListener() {
+                        @Override
+                        public void onWindowAttached() {
+                            mBlockAttach = false;
+                            decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
+                            action.run();
+                        }
+
+                        @Override
+                        public void onWindowDetached() {
+                        }
+                    });
+        }
+    }
+
+    private void updateInsets(WindowInsets insets) {
+        int orientation = mContext.getResources().getConfiguration().orientation;
+        FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) mView.getLayoutParams();
+        if (p == null) {
+            return;
+        }
+        DisplayCutout cutout = insets.getDisplayCutout();
+        Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
+        if (cutout == null) {
+            p.setMargins(0, 0, 0, navBarInsets.bottom);
+        } else {
+            Insets waterfall = cutout.getWaterfallInsets();
+            if (orientation == ORIENTATION_PORTRAIT) {
+                p.setMargins(
+                        waterfall.left,
+                        Math.max(cutout.getSafeInsetTop(), waterfall.top),
+                        waterfall.right,
+                        Math.max(cutout.getSafeInsetBottom(),
+                                Math.max(navBarInsets.bottom, waterfall.bottom)));
+            } else {
+                p.setMargins(
+                        Math.max(cutout.getSafeInsetLeft(), waterfall.left),
+                        waterfall.top,
+                        Math.max(cutout.getSafeInsetRight(), waterfall.right),
+                        Math.max(navBarInsets.bottom, waterfall.bottom));
+            }
+        }
+        mView.setLayoutParams(p);
+        mView.requestLayout();
+    }
+
+    private Display getDefaultDisplay() {
+        return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
new file mode 100644
index 0000000..6a4be6e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.screenshot.SwipeDismissHandler;
+
+/**
+ * ConstraintLayout that is draggable when touched in a specific region
+ */
+public class DraggableConstraintLayout extends ConstraintLayout {
+    private final SwipeDismissHandler mSwipeDismissHandler;
+    private final GestureDetector mSwipeDetector;
+    private Runnable mOnDismiss;
+    private Runnable mOnInteraction;
+
+    public DraggableConstraintLayout(Context context) {
+        this(context, null);
+    }
+
+    public DraggableConstraintLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DraggableConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mSwipeDismissHandler = new SwipeDismissHandler(mContext, this,
+                new SwipeDismissHandler.SwipeDismissCallbacks() {
+                    @Override
+                    public void onInteraction() {
+                        if (mOnInteraction != null) {
+                            mOnInteraction.run();
+                        }
+                    }
+
+                    @Override
+                    public void onDismiss() {
+                        if (mOnDismiss != null) {
+                            mOnDismiss.run();
+                        }
+                    }
+                });
+        setOnTouchListener(mSwipeDismissHandler);
+
+        mSwipeDetector = new GestureDetector(mContext,
+                new GestureDetector.SimpleOnGestureListener() {
+                    final Rect mActionsRect = new Rect();
+
+                    @Override
+                    public boolean onScroll(
+                            MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+                        View actionsContainer = findViewById(R.id.actions_container);
+                        actionsContainer.getBoundsOnScreen(mActionsRect);
+                        // return true if we aren't in the actions bar, or if we are but it isn't
+                        // scrollable in the direction of movement
+                        return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
+                                || !actionsContainer.canScrollHorizontally((int) distanceX);
+                    }
+                });
+        mSwipeDetector.setIsLongpressEnabled(false);
+    }
+
+    @Override // View
+    protected void onFinishInflate() {
+
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mSwipeDismissHandler.onTouch(this, ev);
+        }
+
+        return mSwipeDetector.onTouchEvent(ev);
+    }
+
+    public void setOnDismissCallback(Runnable callback) {
+        mOnDismiss = callback;
+    }
+
+    public void setOnInteractionCallback(Runnable callback) {
+        mOnInteraction = callback;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
new file mode 100644
index 0000000..be10c35
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import com.android.systemui.R;
+
+/**
+ * Lightweight activity for editing text clipboard contents
+ */
+public class EditTextActivity extends Activity {
+    private EditText mEditText;
+    private ClipboardManager mClipboardManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.clipboard_edit_text_activity);
+        findViewById(R.id.copy_button).setOnClickListener((v) -> saveToClipboard());
+        findViewById(R.id.share).setOnClickListener((v) -> share());
+        mEditText = findViewById(R.id.edit_text);
+        mClipboardManager = requireNonNull(getSystemService(ClipboardManager.class));
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        ClipData clip = mClipboardManager.getPrimaryClip();
+        if (clip == null) {
+            finish();
+            return;
+        }
+        // TODO: put clip attribution in R.id.attribution TextView
+        mEditText.setText(clip.getItemAt(0).getText());
+        mEditText.requestFocus();
+    }
+
+    private void saveToClipboard() {
+        ClipData clip = ClipData.newPlainText("text", mEditText.getText());
+        mClipboardManager.setPrimaryClip(clip);
+        hideImeAndFinish();
+    }
+
+    private void share() {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_SEND);
+        sendIntent.putExtra(Intent.EXTRA_TEXT, mEditText.getText());
+        sendIntent.setType("text/plain");
+
+        Intent shareIntent = Intent.createChooser(sendIntent, null);
+        startActivity(shareIntent);
+    }
+
+    private void hideImeAndFinish() {
+        InputMethodManager imm = getSystemService(InputMethodManager.class);
+        imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+        finish();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalManagerUpdater.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalManagerUpdater.java
deleted file mode 100644
index ebe804a..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalManagerUpdater.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.app.communal.CommunalManager;
-import android.content.Context;
-import android.util.Log;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.dagger.SysUISingleton;
-
-import java.lang.ref.WeakReference;
-
-import javax.inject.Inject;
-
-/**
- * The {@link CommunalManagerUpdater} is responsible for forwarding state from SystemUI to
- * the {@link CommunalManager} system service.
- */
-@SysUISingleton
-public class CommunalManagerUpdater extends CoreStartable {
-    private static final String TAG = "CommunalManagerUpdater";
-
-    private final CommunalManager mCommunalManager;
-    private final CommunalSourceMonitor mMonitor;
-
-    private final CommunalSourceMonitor.Callback mSourceCallback =
-            new CommunalSourceMonitor.Callback() {
-                @Override
-                public void onSourceAvailable(WeakReference<CommunalSource> source) {
-                    try {
-                        mCommunalManager.setCommunalViewShowing(
-                                source != null && source.get() != null);
-                    } catch (RuntimeException e) {
-                        Log.e(TAG, "Error updating communal manager service state", e);
-                    }
-                }
-            };
-
-    @Inject
-    public CommunalManagerUpdater(Context context, CommunalSourceMonitor monitor) {
-        super(context);
-        mCommunalManager = context.getSystemService(CommunalManager.class);
-        mMonitor = monitor;
-    }
-
-    @Override
-    public void start() {
-        if (mCommunalManager != null) {
-            mMonitor.addCallback(mSourceCallback);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 2598518..8367e11 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -244,7 +244,8 @@
             restoreFinishedReceiver,
             IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
             PERMISSION_SELF,
-            null
+            null,
+            Context.RECEIVER_NOT_EXPORTED
         )
         listingController.addCallback(listingCallback)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 977e46a..d2ded71 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -131,6 +131,12 @@
             wrapper = null
             bindService(false)
         }
+
+        override fun onNullBinding(name: ComponentName?) {
+            if (DEBUG) Log.d(TAG, "onNullBinding $name")
+            wrapper = null
+            context.unbindService(this)
+        }
     }
 
     private fun handlePendingServiceMethods() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index cffb2f7..bda8e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -28,12 +28,15 @@
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
 import com.android.systemui.people.PeopleProvider;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.unfold.FoldStateLogger;
+import com.android.systemui.unfold.FoldStateLoggingProvider;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.unfold.UnfoldLatencyTracker;
 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.wm.shell.ShellCommandHandler;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
@@ -119,6 +122,9 @@
         @BindsInstance
         Builder setDragAndDrop(Optional<DragAndDrop> d);
 
+        @BindsInstance
+        Builder setBackAnimation(Optional<BackAnimation> b);
+
         SysUIComponent build();
     }
 
@@ -139,6 +145,8 @@
         getMediaTttChipControllerReceiver();
         getMediaTttCommandLineHelper();
         getUnfoldLatencyTracker().init();
+        getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
+        getFoldStateLogger().ifPresent(FoldStateLogger::init);
     }
 
     /**
@@ -166,6 +174,18 @@
     UnfoldLatencyTracker getUnfoldLatencyTracker();
 
     /**
+     * Creates a FoldStateLoggingProvider.
+     */
+    @SysUISingleton
+    Optional<FoldStateLoggingProvider> getFoldStateLoggingProvider();
+
+    /**
+     * Creates a FoldStateLogger.
+     */
+    @SysUISingleton
+    Optional<FoldStateLogger> getFoldStateLogger();
+
+    /**
      * Main dependency providing module.
      */
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 9dddbb1..bbe9dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.dagger;
 
+import com.android.keyguard.KeyguardBiometricLockoutLogger;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.LatencyTester;
 import com.android.systemui.ScreenDecorations;
@@ -23,9 +24,8 @@
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.accessibility.WindowMagnification;
 import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.communal.CommunalManagerUpdater;
+import com.android.systemui.clipboardoverlay.ClipboardListener;
 import com.android.systemui.dreams.DreamOverlayRegistrant;
-import com.android.systemui.dreams.appwidgets.ComplicationPrimer;
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -72,6 +72,12 @@
     @ClassKey(GarbageMonitor.Service.class)
     public abstract CoreStartable bindGarbageMonitorService(GarbageMonitor.Service sysui);
 
+    /** Inject into ClipboardListener. */
+    @Binds
+    @IntoMap
+    @ClassKey(ClipboardListener.class)
+    public abstract CoreStartable bindClipboardListener(ClipboardListener sysui);
+
     /** Inject into GlobalActionsComponent. */
     @Binds
     @IntoMap
@@ -90,6 +96,13 @@
     @ClassKey(KeyguardViewMediator.class)
     public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);
 
+    /** Inject into KeyguardBiometricLockoutLogger. */
+    @Binds
+    @IntoMap
+    @ClassKey(KeyguardBiometricLockoutLogger.class)
+    public abstract CoreStartable bindKeyguardBiometricLockoutLogger(
+            KeyguardBiometricLockoutLogger sysui);
+
     /** Inject into LatencyTests. */
     @Binds
     @IntoMap
@@ -198,18 +211,4 @@
     @ClassKey(DreamOverlayRegistrant.class)
     public abstract CoreStartable bindDreamOverlayRegistrant(
             DreamOverlayRegistrant dreamOverlayRegistrant);
-
-    /** Inject into AppWidgetOverlayPrimer. */
-    @Binds
-    @IntoMap
-    @ClassKey(ComplicationPrimer.class)
-    public abstract CoreStartable bindAppWidgetOverlayPrimer(
-            ComplicationPrimer complicationPrimer);
-
-    /** Inject into CommunalManagerUpdater. */
-    @Binds
-    @IntoMap
-    @ClassKey(CommunalManagerUpdater.class)
-    public abstract CoreStartable bindCommunalManagerUpdater(
-            CommunalManagerUpdater communalManagerUpdater);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index c4a58db..9bc3f17 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
@@ -208,6 +209,7 @@
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
+            CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline, SysUiState sysUiState,
             NotifPipelineFlags notifPipelineFlags, DumpManager dumpManager,
             @Main Executor sysuiMainExecutor) {
@@ -216,7 +218,7 @@
                 configurationController, statusBarService, notificationManager,
                 visibilityProvider,
                 interruptionStateProvider, zenModeController, notifUserManager,
-                groupManager, entryManager, notifPipeline, sysUiState, notifPipelineFlags,
-                dumpManager, sysuiMainExecutor));
+                groupManager, entryManager, notifCollection, notifPipeline, sysUiState,
+                notifPipelineFlags, dumpManager, sysuiMainExecutor));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b815d4e..b926692 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -24,6 +24,7 @@
 import com.android.wm.shell.ShellInit;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.dagger.TvWMShellModule;
@@ -123,4 +124,7 @@
 
     @WMSingleton
     DragAndDrop getDragAndDrop();
+
+    @WMSingleton
+    Optional<BackAnimation> getBackAnimation();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 3631938..63d4d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -163,16 +163,12 @@
 
             // Delay screen state transitions even longer while animations are running.
             boolean shouldDelayTransitionEnteringDoze = newState == DOZE_AOD
-                    && mParameters.shouldControlScreenOff() && !turningOn;
+                    && mParameters.shouldDelayDisplayDozeTransition() && !turningOn;
 
             // Delay screen state transition longer if UDFPS is actively authenticating a fp
             boolean shouldDelayTransitionForUDFPS = newState == DOZE_AOD
                     && mUdfpsController != null && mUdfpsController.isFingerDown();
 
-            if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) {
-                mWakeLock.setAcquired(true);
-            }
-
             if (!messagePending) {
                 if (DEBUG) {
                     Log.d(TAG, "Display state changed to " + screenState + " delayed by "
@@ -180,6 +176,18 @@
                 }
 
                 if (shouldDelayTransitionEnteringDoze) {
+                    if (justInitialized) {
+                        // If we are delaying transitioning to doze and the display was not
+                        // turned on we set it to 'on' first to make sure that the animation
+                        // is visible before eventually moving it to doze state.
+                        // The display might be off at this point for example on foldable devices
+                        // when we switch displays and go to doze at the same time.
+                        applyScreenState(Display.STATE_ON);
+
+                        // Restore pending screen state as it gets cleared by 'applyScreenState'
+                        mPendingScreenState = screenState;
+                    }
+
                     mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY);
                 } else if (shouldDelayTransitionForUDFPS) {
                     mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState);
@@ -190,6 +198,10 @@
             } else if (DEBUG) {
                 Log.d(TAG, "Pending display state change to " + screenState);
             }
+
+            if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) {
+                mWakeLock.setAcquired(true);
+            }
         } else if (turningOff) {
             mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState));
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
index 099e379..e5d6319 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
@@ -16,8 +16,14 @@
 
 package com.android.systemui.dreams;
 
+import android.annotation.IntDef;
 import android.content.Context;
 
+import com.android.settingslib.dream.DreamBackend;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * {@link ComplicationProvider} is an interface for defining entities that can supply complications
  * to show over a dream. Presentation components such as the {@link DreamOverlayService} supply
@@ -25,6 +31,27 @@
  */
 public interface ComplicationProvider {
     /**
+     * The type of dream complications which can be provided by a {@link ComplicationProvider}.
+     */
+    @IntDef(prefix = {"COMPLICATION_TYPE_"}, flag = true, value = {
+            COMPLICATION_TYPE_NONE,
+            COMPLICATION_TYPE_TIME,
+            COMPLICATION_TYPE_DATE,
+            COMPLICATION_TYPE_WEATHER,
+            COMPLICATION_TYPE_AIR_QUALITY,
+            COMPLICATION_TYPE_CAST_INFO
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ComplicationType {}
+
+    int COMPLICATION_TYPE_NONE = 0;
+    int COMPLICATION_TYPE_TIME = 1;
+    int COMPLICATION_TYPE_DATE = 1 << 1;
+    int COMPLICATION_TYPE_WEATHER = 1 << 2;
+    int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
+    int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
+
+    /**
      * Called when the {@link ComplicationHost} requests the associated complication be produced.
      *
      * @param context The {@link Context} used to construct the view.
@@ -33,4 +60,26 @@
      */
     void onCreateComplication(Context context, ComplicationHost.CreationCallback creationCallback,
             ComplicationHost.InteractionCallback interactionCallback);
+
+    /**
+     * Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
+     * {@link ComplicationType}.
+     */
+    @ComplicationType
+    default int convertComplicationType(@DreamBackend.ComplicationType int type) {
+        switch (type) {
+            case DreamBackend.COMPLICATION_TYPE_TIME:
+                return COMPLICATION_TYPE_TIME;
+            case DreamBackend.COMPLICATION_TYPE_DATE:
+                return COMPLICATION_TYPE_DATE;
+            case DreamBackend.COMPLICATION_TYPE_WEATHER:
+                return COMPLICATION_TYPE_WEATHER;
+            case DreamBackend.COMPLICATION_TYPE_AIR_QUALITY:
+                return COMPLICATION_TYPE_AIR_QUALITY;
+            case DreamBackend.COMPLICATION_TYPE_CAST_INFO:
+                return COMPLICATION_TYPE_CAST_INFO;
+            default:
+                return COMPLICATION_TYPE_NONE;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 572bb44..5b46079 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.dreams;
 
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Handler;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -26,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
 import com.android.systemui.util.ViewController;
@@ -47,6 +51,15 @@
     // the space into which widgets are placed.
     private final ViewGroup mDreamOverlayContentView;
 
+    // The maximum translation offset to apply to the overlay container to avoid screen burn-in.
+    private final int mMaxBurnInOffset;
+
+    // The interval in milliseconds between burn-in protection updates.
+    private final long mBurnInProtectionUpdateInterval;
+
+    // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
+    private final Handler mHandler;
+
     // A hook into the internal inset calculation where we declare the overlays as the only
     // touchable regions.
     private final ViewTreeObserver.OnComputeInternalInsetsListener
@@ -81,13 +94,21 @@
     public DreamOverlayContainerViewController(
             DreamOverlayContainerView containerView,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
-            DreamOverlayStatusBarViewController statusBarViewController) {
+            DreamOverlayStatusBarViewController statusBarViewController,
+            @Main Handler handler,
+            @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
+            @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
+                    burnInProtectionUpdateInterval) {
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
         mDreamOverlayNotificationsDragAreaHeight =
                 mView.getResources().getDimensionPixelSize(
                         R.dimen.dream_overlay_notifications_drag_area_height);
+
+        mHandler = handler;
+        mMaxBurnInOffset = maxBurnInOffset;
+        mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
     }
 
     @Override
@@ -99,10 +120,12 @@
     protected void onViewAttached() {
         mView.getViewTreeObserver()
                 .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
     }
 
     @Override
     protected void onViewDetached() {
+        mHandler.removeCallbacks(this::updateBurnInOffsets);
         mView.getViewTreeObserver()
                 .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
     }
@@ -123,4 +146,15 @@
     int getDreamOverlayNotificationsDragAreaHeight() {
         return mDreamOverlayNotificationsDragAreaHeight;
     }
+
+    private void updateBurnInOffsets() {
+        // These translation values change slowly, and the set translation methods are idempotent,
+        // so no translation occurs when the values don't change.
+        mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
+                - mMaxBurnInOffset);
+        mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
+                - mMaxBurnInOffset);
+
+        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
deleted file mode 100644
index 687f7a2..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.appwidgets;
-
-import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * {@link AppWidgetProvider} is a singleton for accessing app widgets within SystemUI. This
- * consolidates resources such as the {@link AppWidgetHost} across potentially multiple
- * {@link ComplicationProvider} instances and other usages.
- */
-@SysUISingleton
-public class AppWidgetProvider {
-    private static final String TAG = "AppWidgetProvider";
-    public static final int APP_WIDGET_HOST_ID = 1025;
-
-    private final Context mContext;
-    private final AppWidgetManager mAppWidgetManager;
-    private final AppWidgetHost mAppWidgetHost;
-    private final Resources mResources;
-
-    @Inject
-    public AppWidgetProvider(Context context, @Main Resources resources) {
-        mContext = context;
-        mResources = resources;
-        mAppWidgetManager = android.appwidget.AppWidgetManager.getInstance(context);
-        mAppWidgetHost = new AppWidgetHost(context, APP_WIDGET_HOST_ID);
-        mAppWidgetHost.startListening();
-    }
-
-    /**
-     * Returns an {@link AppWidgetHostView} associated with a given {@link ComponentName}.
-     * @param component The {@link ComponentName} of the target {@link AppWidgetHostView}.
-     * @return The {@link AppWidgetHostView} or {@code null} on error.
-     */
-    public AppWidgetHostView getWidget(ComponentName component) {
-        final List<AppWidgetProviderInfo> appWidgetInfos =
-                mAppWidgetManager.getInstalledProviders();
-
-        for (AppWidgetProviderInfo widgetInfo : appWidgetInfos) {
-            if (widgetInfo.provider.equals(component)) {
-                final int widgetId = mAppWidgetHost.allocateAppWidgetId();
-
-                boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(widgetId,
-                        widgetInfo.provider);
-
-                if (!success) {
-                    Log.e(TAG, "could not bind to app widget:" + component);
-                    break;
-                }
-
-                final AppWidgetHostView appWidgetView =
-                        mAppWidgetHost.createView(mContext, widgetId, widgetInfo);
-
-                if (appWidgetView != null) {
-                    // Register a layout change listener to update the widget on any sizing changes.
-                    appWidgetView.addOnLayoutChangeListener(
-                            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                                final float density = mResources.getDisplayMetrics().density;
-                                final int height = Math.round((bottom - top) / density);
-                                final int width = Math.round((right - left) / density);
-                                appWidgetView.updateAppWidgetSize(null, width, height,
-                                        width, height);
-                            });
-                }
-
-                return appWidgetView;
-            }
-        }
-
-        return null;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java
deleted file mode 100644
index 7d30fafd..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.appwidgets;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.Gravity;
-
-import androidx.constraintlayout.widget.ConstraintSet;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
-
-import javax.inject.Inject;
-
-/**
- * {@link ComplicationPrimer} reads the configured AppWidget Complications from resources on start
- * and populates them into the {@link DreamOverlayStateController}.
- */
-public class ComplicationPrimer extends CoreStartable {
-    private final Resources mResources;
-    private final DreamOverlayStateController mDreamOverlayStateController;
-    private final AppWidgetComponent.Factory mComponentFactory;
-
-    @Inject
-    public ComplicationPrimer(Context context, @Main Resources resources,
-            DreamOverlayStateController overlayStateController,
-            AppWidgetComponent.Factory appWidgetOverlayFactory) {
-        super(context);
-        mResources = resources;
-        mDreamOverlayStateController = overlayStateController;
-        mComponentFactory = appWidgetOverlayFactory;
-    }
-
-    @Override
-    public void start() {
-    }
-
-    @Override
-    protected void onBootCompleted() {
-        super.onBootCompleted();
-        loadDefaultWidgets();
-    }
-
-    /**
-     * Generates the {@link ComplicationHostView.LayoutParams} for a given gravity. Default
-     * dimension constraints are also included in the params.
-     * @param gravity The gravity for the layout as defined by {@link Gravity}.
-     * @param resources The resourcs from which default dimensions will be extracted from.
-     * @return {@link ComplicationHostView.LayoutParams} representing the provided gravity and
-     *         default parameters.
-     */
-    private static ComplicationHostView.LayoutParams getLayoutParams(int gravity,
-            Resources resources) {
-        final ComplicationHostView.LayoutParams params = new ComplicationHostView.LayoutParams(
-                ComplicationHostView.LayoutParams.MATCH_CONSTRAINT,
-                ComplicationHostView.LayoutParams.MATCH_CONSTRAINT);
-
-        if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
-            params.bottomToBottom = ConstraintSet.PARENT_ID;
-        }
-
-        if ((gravity & Gravity.TOP) == Gravity.TOP) {
-            params.topToTop = ConstraintSet.PARENT_ID;
-        }
-
-        if ((gravity & Gravity.END) == Gravity.END) {
-            params.endToEnd = ConstraintSet.PARENT_ID;
-        }
-
-        if ((gravity & Gravity.START) == Gravity.START) {
-            params.startToStart = ConstraintSet.PARENT_ID;
-        }
-
-        // For now, apply the same sizing constraints on every widget.
-        params.matchConstraintPercentHeight =
-                resources.getFloat(R.dimen.config_dreamComplicationHeightPercent);
-        params.matchConstraintPercentWidth =
-                resources.getFloat(R.dimen.config_dreamComplicationWidthPercent);
-
-        return params;
-    }
-
-    /**
-     * Helper method for loading widgets based on configuration.
-     */
-    private void loadDefaultWidgets() {
-        final int[] positions = mResources.getIntArray(R.array.config_dreamComplicationPositions);
-        final String[] components =
-                mResources.getStringArray(R.array.config_dreamAppWidgetComplications);
-
-        for (int i = 0; i < Math.min(positions.length, components.length); i++) {
-            final AppWidgetComponent component = mComponentFactory.build(
-                    ComponentName.unflattenFromString(components[i]),
-                    getLayoutParams(positions[i], mResources));
-
-            mDreamOverlayStateController.addComplication(
-                    component.getAppWidgetComplicationProvider());
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java
deleted file mode 100644
index 9188ee5..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.appwidgets;
-
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
-import android.content.Context;
-import android.util.Log;
-import android.widget.RemoteViews;
-
-import com.android.systemui.dreams.ComplicationHost;
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.plugins.ActivityStarter;
-
-import javax.inject.Inject;
-
-/**
- * {@link ComplicationProvider} is an implementation of
- * {@link com.android.systemui.dreams.ComplicationProvider} for providing app widget-based
- * complications.
- */
-public class ComplicationProvider implements com.android.systemui.dreams.ComplicationProvider {
-    private static final String TAG = "AppWidgetCompProvider";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final ActivityStarter mActivityStarter;
-    private final AppWidgetProvider mAppWidgetProvider;
-    private final ComponentName mComponentName;
-    private final ComplicationHostView.LayoutParams mLayoutParams;
-
-    @Inject
-    public ComplicationProvider(ActivityStarter activityStarter,
-            ComponentName componentName, AppWidgetProvider widgetProvider,
-            ComplicationHostView.LayoutParams layoutParams) {
-        mActivityStarter = activityStarter;
-        mComponentName = componentName;
-        mAppWidgetProvider = widgetProvider;
-        mLayoutParams = layoutParams;
-    }
-
-    @Override
-    public void onCreateComplication(Context context,
-            ComplicationHost.CreationCallback creationCallback,
-            ComplicationHost.InteractionCallback interactionCallback) {
-        final AppWidgetHostView widget = mAppWidgetProvider.getWidget(mComponentName);
-
-        if (widget == null) {
-            Log.e(TAG, "could not create widget");
-            return;
-        }
-
-        widget.setInteractionHandler((view, pendingIntent, response) -> {
-            if (pendingIntent.isActivity()) {
-                if (DEBUG) {
-                    Log.d(TAG, "launching pending intent from app widget:" + mComponentName);
-                }
-                interactionCallback.onExit();
-                mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent,
-                        null /*intentSentUiThreadCallback*/, view);
-                return true;
-            } else {
-                return RemoteViews.startPendingIntent(view, pendingIntent,
-                        response.getLaunchOptions(view));
-            }
-        });
-
-        creationCallback.onCreated(widget, mLayoutParams);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java
deleted file mode 100644
index 7beed17..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.appwidgets.dagger;
-
-import android.content.ComponentName;
-
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.dreams.appwidgets.ComplicationProvider;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/** */
-@Subcomponent
-public interface AppWidgetComponent {
-    /** */
-    @Subcomponent.Factory
-    interface Factory {
-        AppWidgetComponent build(@BindsInstance ComponentName component,
-                @BindsInstance ComplicationHostView.LayoutParams layoutParams);
-    }
-
-    /** Builds a {@link ComplicationProvider}. */
-    ComplicationProvider getAppWidgetComplicationProvider();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 0d4688e..072f50d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -16,15 +16,12 @@
 
 package com.android.systemui.dreams.dagger;
 
-import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
-
 import dagger.Module;
 
 /**
  * Dagger Module providing Communal-related functionality.
  */
 @Module(subcomponents = {
-        AppWidgetComponent.class,
         DreamOverlayComponent.class})
 public interface DreamModule {
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 5b588a9..d291203 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams.dagger;
 
 import android.content.ContentResolver;
+import android.content.res.Resources;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
@@ -45,6 +46,9 @@
     public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
             "dream_overlay_battery_controller";
     public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
+    public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
+    public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
+            "burn_in_protection_update_interval";
 
     /** */
     @Provides
@@ -104,4 +108,20 @@
                 contentResolver,
                 batteryController);
     }
+
+    /** */
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    @Named(MAX_BURN_IN_OFFSET)
+    static int providesMaxBurnInOffset(@Main Resources resources) {
+        return resources.getDimensionPixelSize(R.dimen.default_burn_in_prevention_offset);
+    }
+
+    /** */
+    @Provides
+    @Named(BURN_IN_PROTECTION_UPDATE_INTERVAL)
+    static long providesBurnInProtectionUpdateInterval(@Main Resources resources) {
+        return resources.getInteger(
+                R.integer.config_dreamOverlayBurnInProtectionUpdateIntervalMillis);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 89623f4..f3b721c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -88,7 +88,8 @@
         filter.addAction(ACTION_GET_FLAGS);
         flagManager.setRestartAction(this::restartSystemUI);
         flagManager.setClearCacheAction(this::removeFromCache);
-        context.registerReceiver(mReceiver, filter, null, null);
+        context.registerReceiver(mReceiver, filter, null, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         dumpManager.registerDumpable(TAG, this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 726f865..4be819a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -57,8 +57,10 @@
 
     /***************************************/
     // 200 - keyguard/lockscreen
-    public static final BooleanFlag KEYGUARD_LAYOUT =
-            new BooleanFlag(200, true);
+
+    // ** Flag retired **
+    // public static final BooleanFlag KEYGUARD_LAYOUT =
+    //         new BooleanFlag(200, true);
 
     public static final BooleanFlag LOCKSCREEN_ANIMATIONS =
             new BooleanFlag(201, true);
@@ -69,6 +71,9 @@
     public static final ResourceBooleanFlag CHARGING_RIPPLE =
             new ResourceBooleanFlag(203, R.bool.flag_charging_ripple);
 
+    public static final ResourceBooleanFlag BOUNCER_USER_SWITCHER =
+            new ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher);
+
     /***************************************/
     // 300 - power menu
     public static final BooleanFlag POWER_MENU_LITE =
@@ -123,7 +128,8 @@
     /***************************************/
     // 900 - media
     public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, false);
-    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
+    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
+    public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
 
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index c46ffa0..3ae11ff 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcelable;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -53,7 +54,7 @@
     private final View mRootView;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
-                | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
+                    | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_ASSETS_PATHS);
     private final FragmentService mManager;
     private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();
 
@@ -224,10 +225,12 @@
     }
 
     public void reloadFragments() {
+        Trace.beginSection("FrargmentHostManager#reloadFragments");
         // Save the old state.
         Parcelable p = destroyFragmentHost();
         // Generate a new fragment host and restore its state.
         createFragmentHost(p);
+        Trace.endSection();
     }
 
     class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index d73d9cd..0ad2807 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -119,14 +119,13 @@
             return;
         }
         long minShowDuration = getMinVisibilityMillis(mIndicationMessages.get(mCurrIndicationType));
-        final boolean hasPreviousIndication = mIndicationMessages.get(type) != null
-                && !TextUtils.isEmpty(mIndicationMessages.get(type).getMessage());
-        final boolean hasNewIndication = newIndication != null;
+        final boolean hasNewIndication = newIndication != null
+                && !TextUtils.isEmpty(newIndication.getMessage());
         if (!hasNewIndication) {
             mIndicationMessages.remove(type);
             mIndicationQueue.removeIf(x -> x == type);
         } else {
-            if (!hasPreviousIndication) {
+            if (!mIndicationQueue.contains(type)) {
                 mIndicationQueue.add(type);
             }
 
@@ -230,6 +229,7 @@
     public void clearMessages() {
         mCurrIndicationType = INDICATION_TYPE_NONE;
         mIndicationQueue.clear();
+        mIndicationMessages.clear();
         mView.clearMessages();
     }
 
@@ -310,7 +310,7 @@
                     if (mIsDozing) {
                         showIndication(INDICATION_TYPE_NONE);
                     } else if (mIndicationQueue.size() > 0) {
-                        showIndication(mIndicationQueue.remove(0));
+                        showIndication(mIndicationQueue.get(0));
                     }
                 }
             };
@@ -327,7 +327,7 @@
         ShowNextIndication(long delay) {
             mShowIndicationRunnable = () -> {
                 int type = mIndicationQueue.size() == 0
-                        ? INDICATION_TYPE_NONE : mIndicationQueue.remove(0);
+                        ? INDICATION_TYPE_NONE : mIndicationQueue.get(0);
                 showIndication(type);
             };
             mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4658a74..8376681 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -100,6 +100,7 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -893,7 +894,8 @@
         delayedActionFilter.addAction(DELAYED_KEYGUARD_ACTION);
         delayedActionFilter.addAction(DELAYED_LOCK_PROFILE_ACTION);
         mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
-                SYSTEMUI_PERMISSION, null /* scheduler */);
+                SYSTEMUI_PERMISSION, null /* scheduler */,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
@@ -1687,6 +1689,21 @@
                 || mUpdateMonitor.isSimPinSecure();
     }
 
+    /**
+     * Whether any of the SIMs on the device are secured with a PIN. If so, the keyguard should not
+     * be dismissable until the PIN is entered, even if the device itself has no lock set.
+     */
+    public boolean isAnySimPinSecure() {
+        for (int i = 0; i < mLastSimStates.size(); i++) {
+            final int key = mLastSimStates.keyAt(i);
+            if (KeyguardUpdateMonitor.isSimPinSecure(mLastSimStates.get(key))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     public void setSwitchingUser(boolean switching) {
         mUpdateMonitor.setSwitchingUser(switching);
     }
@@ -1744,7 +1761,7 @@
         }
     };
 
-    public void keyguardDone() {
+    private void keyguardDone() {
         Trace.beginSection("KeyguardViewMediator#keyguardDone");
         if (DEBUG) Log.d(TAG, "keyguardDone()");
         userActivity();
@@ -1878,9 +1895,8 @@
             resetKeyguardDonePendingLocked();
         }
 
-
         if (mGoingToSleep) {
-            mUpdateMonitor.clearBiometricRecognized();
+            mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser);
             Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
             return;
         }
@@ -1901,7 +1917,7 @@
         }
 
         handleHide();
-        mUpdateMonitor.clearBiometricRecognized();
+        mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser);
         Trace.endSection();
     }
 
@@ -2345,16 +2361,20 @@
         // Block the panel from expanding, in case we were doing a swipe to dismiss gesture.
         mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
         final boolean wasShowing = mShowing;
-        onKeyguardExitFinished();
-
-        if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
-            mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
-        }
-
-        finishSurfaceBehindRemoteAnimation(cancelled);
-        mSurfaceBehindRemoteAnimationRequested = false;
-        mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
         InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+
+        // Post layout changes to the next frame, so we don't hang at the end of the animation.
+        DejankUtils.postAfterTraversal(() -> {
+            onKeyguardExitFinished();
+
+            if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
+                mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
+            }
+
+            finishSurfaceBehindRemoteAnimation(cancelled);
+            mSurfaceBehindRemoteAnimationRequested = false;
+            mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
+        });
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 2e1c9fa..474a81b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -138,6 +138,7 @@
         }
         setWakefulness(WAKEFULNESS_AWAKE);
         dispatch(Observer::onFinishedWakingUp);
+        dispatch(Observer::onPostFinishedWakingUp);
     }
 
     public void dispatchStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
@@ -236,6 +237,12 @@
     public interface Observer {
         default void onStartedWakingUp() {}
         default void onFinishedWakingUp() {}
+
+        /**
+         * Called after the finished waking up call, ensuring it's after all the other listeners,
+         * reacting to {@link #onFinishedWakingUp()}
+         */
+        default void onPostFinishedWakingUp() {}
         default void onStartedGoingToSleep() {}
         default void onFinishedGoingToSleep() {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 492fdc6..b15807c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -66,11 +66,12 @@
  * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to
  * sequential calls to [document] that aren't immediately followed by a matching call to [push].
  */
-class LogBuffer(
+class LogBuffer @JvmOverloads constructor(
     private val name: String,
     private val maxLogs: Int,
     private val poolSize: Int,
-    private val logcatEchoTracker: LogcatEchoTracker
+    private val logcatEchoTracker: LogcatEchoTracker,
+    private val systrace: Boolean = true
 ) {
     init {
         if (maxLogs < poolSize) {
@@ -175,6 +176,10 @@
             buffer.removeFirst()
         }
         buffer.add(message as LogMessageImpl)
+        if (systrace) {
+            val messageStr = message.printer(message)
+            Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "$name - $messageStr")
+        }
         if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
                 logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
             echo(message)
@@ -237,7 +242,6 @@
             LogLevel.ERROR -> Log.e(message.tag, strMessage)
             LogLevel.WTF -> Log.wtf(message.tag, strMessage)
         }
-        Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", strMessage)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index 5296ee6..cbfca25 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -36,8 +36,13 @@
     }
 
     @JvmOverloads
-    fun create(name: String, maxPoolSize: Int, flexSize: Int = 10): LogBuffer {
-        val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker)
+    fun create(
+        name: String,
+        maxPoolSize: Int,
+        flexSize: Int = 10,
+        systrace: Boolean = true
+    ): LogBuffer {
+        val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker, systrace)
         dumpManager.registerBuffer(name, buffer)
         return buffer
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java
new file mode 100644
index 0000000..08d969b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for lockscreen to shade transition events. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface LSShadeTransitionLog {
+}
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 d3bd0a5..b323586 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -49,7 +49,8 @@
     @SysUISingleton
     @NotificationLog
     public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
-        return factory.create("NotifLog", 1000);
+        return factory.create("NotifLog", 1000 /* maxPoolSize */,
+                10 /* flexSize */, false /* systrace */);
     }
 
     /** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -60,12 +61,21 @@
         return factory.create("NotifHeadsUpLog", 1000);
     }
 
+    /** Provides a logging buffer for all logs for lockscreen to shade transition events. */
+    @Provides
+    @SysUISingleton
+    @LSShadeTransitionLog
+    public static LogBuffer provideLSShadeTransitionControllerBuffer(LogBufferFactory factory) {
+        return factory.create("LSShadeTransitionLog", 50);
+    }
+
     /** Provides a logging buffer for all logs related to managing notification sections. */
     @Provides
     @SysUISingleton
     @NotificationSectionLog
     public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) {
-        return factory.create("NotifSectionLog", 1000);
+        return factory.create("NotifSectionLog", 1000 /* maxPoolSize */,
+                10 /* flexSize */, false /* systrace */);
     }
 
     /** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -81,7 +91,8 @@
     @SysUISingleton
     @QSLog
     public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) {
-        return factory.create("QSLog", 500);
+        return factory.create("QSLog", 500 /* maxPoolSize */,
+                10 /* flexSize */, false /* systrace */);
     }
 
     /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
@@ -89,7 +100,8 @@
     @SysUISingleton
     @BroadcastDispatcherLog
     public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) {
-        return factory.create("BroadcastDispatcherLog", 500);
+        return factory.create("BroadcastDispatcherLog", 500 /* maxPoolSize */,
+                10 /* flexSize */, false /* systrace */);
     }
 
     /** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
@@ -127,7 +139,8 @@
     @SysUISingleton
     @QSFragmentDisableLog
     public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
-        return factory.create("QSFragmentDisableFlagsLog", 10);
+        return factory.create("QSFragmentDisableFlagsLog", 10 /* maxPoolSize */,
+                10 /* flexSize */, false /* systrace */);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 5ff624d..44727f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.Utils
@@ -96,14 +96,14 @@
     /**
      * single pane media container placed at the top of the notifications list
      */
-    var singlePaneContainer: MediaHeaderView? = null
+    var singlePaneContainer: MediaContainerView? = null
         private set
     private var splitShadeContainer: ViewGroup? = null
 
     /**
      * Attaches media container in single pane mode, situated at the top of the notifications list
      */
-    fun attachSinglePaneContainer(mediaView: MediaHeaderView?) {
+    fun attachSinglePaneContainer(mediaView: MediaContainerView?) {
         val needsListener = singlePaneContainer == null
         singlePaneContainer = mediaView
         if (needsListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ce3b443..c404f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -418,7 +418,7 @@
                 .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
         if (existingPlayer == null) {
             var newPlayer = mediaControlPanelFactory.get()
-            if (mediaFlags.areMediaSessionActionsEnabled()) {
+            if (mediaFlags.useMediaSessionLayout()) {
                 newPlayer.attachPlayer(
                         PlayerSessionViewHolder.create(LayoutInflater.from(context), mediaContent),
                         MediaViewController.TYPE.PLAYER_SESSION)
@@ -865,6 +865,10 @@
             println("playerKeys: ${MediaPlayerData.playerKeys()}")
             println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
             println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
+            println("current size: $currentCarouselWidth x $currentCarouselHeight")
+            println("location: $desiredLocation")
+            println("state: ${desiredHostState?.expansion}, " +
+                "only active ${desiredHostState?.showsOnlyActiveMedia}")
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 3c23722..69a7ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -61,6 +61,7 @@
 import com.android.systemui.util.time.SystemClock;
 
 import java.net.URISyntaxException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -480,6 +481,24 @@
         List<MediaAction> actionIcons = data.getActions();
         List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
 
+        // If the session actions flag is enabled, but we're still using the regular layout, use
+        // the session actions anyways
+        if (mMediaFlags.areMediaSessionActionsEnabled() && data.getSemanticActions() != null) {
+            MediaButton semanticActions = data.getSemanticActions();
+
+            actionIcons = new ArrayList<MediaAction>();
+            actionIcons.add(semanticActions.getStartCustom());
+            actionIcons.add(semanticActions.getPrevOrCustom());
+            actionIcons.add(semanticActions.getPlayOrPause());
+            actionIcons.add(semanticActions.getNextOrCustom());
+            actionIcons.add(semanticActions.getEndCustom());
+
+            actionsWhenCollapsed = new ArrayList<Integer>();
+            actionsWhenCollapsed.add(1);
+            actionsWhenCollapsed.add(2);
+            actionsWhenCollapsed.add(3);
+        }
+
         int i = 0;
         for (; i < actionIcons.size() && i < ACTION_IDS.length; i++) {
             int actionId = ACTION_IDS[i];
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
index b4a4b42..b9795f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
@@ -29,4 +29,12 @@
     fun areMediaSessionActionsEnabled(): Boolean {
         return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
     }
+
+    /**
+     * Check whether media controls should use the new session-based layout
+     */
+    fun useMediaSessionLayout(): Boolean {
+        return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) &&
+            featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 66c51d2..e2716e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -159,7 +159,9 @@
         mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
 
         final Window w = mDialog.getWindow();
-        w.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        // QS is not closed when pressing CastTile. Match the type of the dialog shown from the
+        // tile.
+        w.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
 
         mDialog.show();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 558f0e6..dd60b30 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,12 +16,11 @@
 
 package com.android.systemui.media.dagger;
 
+import android.app.Service;
 import android.content.Context;
 import android.view.WindowManager;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
@@ -30,16 +29,18 @@
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.Optional;
-import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 /** Dagger module for the media package. */
 @Module
@@ -84,14 +85,11 @@
     static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
             MediaTttFlags mediaTttFlags,
             Context context,
-            WindowManager windowManager,
-            @Main Executor mainExecutor,
-            @Background Executor backgroundExecutor) {
+            WindowManager windowManager) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaTttChipControllerSender(
-                context, windowManager, mainExecutor, backgroundExecutor));
+        return Optional.of(new MediaTttChipControllerSender(context, windowManager));
     }
 
     /** */
@@ -115,8 +113,7 @@
             CommandRegistry commandRegistry,
             Context context,
             MediaTttChipControllerSender mediaTttChipControllerSender,
-            MediaTttChipControllerReceiver mediaTttChipControllerReceiver,
-            @Main DelayableExecutor mainExecutor) {
+            MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
@@ -125,7 +122,12 @@
                         commandRegistry,
                         context,
                         mediaTttChipControllerSender,
-                        mediaTttChipControllerReceiver,
-                        mainExecutor));
+                        mediaTttChipControllerReceiver));
     }
+
+    /** Inject into MediaTttSenderService. */
+    @Binds
+    @IntoMap
+    @ClassKey(MediaTttSenderService.class)
+    Service bindMediaTttSenderService(MediaTttSenderService service);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 5a86723..e8a847f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,25 +16,32 @@
 
 package com.android.systemui.media.taptotransfer
 
+import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
 import android.graphics.Color
 import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.os.IBinder
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer
-import com.android.systemui.media.taptotransfer.sender.TransferInitiated
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
+import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
+import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
+import com.android.systemui.media.taptotransfer.sender.TransferFailed
+import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
 import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.PrintWriter
-import java.util.concurrent.FutureTask
 import javax.inject.Inject
 
 /**
@@ -44,11 +51,13 @@
 @SysUISingleton
 class MediaTttCommandLineHelper @Inject constructor(
     commandRegistry: CommandRegistry,
-    context: Context,
+    private val context: Context,
     private val mediaTttChipControllerSender: MediaTttChipControllerSender,
     private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
-    @Main private val mainExecutor: DelayableExecutor,
 ) {
+    private var senderCallback: IDeviceSenderCallback? = null
+    private val senderServiceConnection = SenderServiceConnection()
+
     private val appIconDrawable =
         Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
             it.setTint(Color.YELLOW)
@@ -68,27 +77,29 @@
     inner class AddChipCommandSender : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
             val otherDeviceName = args[0]
-            when (args[1]) {
-                MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
-                    mediaTttChipControllerSender.displayChip(
-                        MoveCloserToTransfer(
-                            appIconDrawable, APP_ICON_CONTENT_DESCRIPTION, otherDeviceName
-                        )
-                    )
-                }
-                TRANSFER_INITIATED_COMMAND_NAME -> {
-                    val futureTask = FutureTask { fakeUndoRunnable }
-                    mediaTttChipControllerSender.displayChip(
-                        TransferInitiated(
-                            appIconDrawable,
-                            APP_ICON_CONTENT_DESCRIPTION,
-                            otherDeviceName,
-                            futureTask
-                        )
-                    )
-                    mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
+            val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
+                .addFeature("feature")
+                .build()
+            val otherDeviceInfo = DeviceInfo(otherDeviceName)
 
+            when (args[1]) {
+                MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
+                    runOnService { senderCallback ->
+                        senderCallback.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
+                    }
                 }
+                MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> {
+                    runOnService { senderCallback ->
+                        senderCallback.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
+                    }
+                }
+                TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> {
+                    runOnService { senderCallback ->
+                        senderCallback.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
+                    }
+                }
+                // TODO(b/203800643): Migrate this command to invoke the service instead of the
+                //   controller.
                 TRANSFER_SUCCEEDED_COMMAND_NAME -> {
                     mediaTttChipControllerSender.displayChip(
                         TransferSucceeded(
@@ -99,11 +110,18 @@
                         )
                     )
                 }
+                TRANSFER_FAILED_COMMAND_NAME -> {
+                    runOnService { senderCallback ->
+                        senderCallback.transferFailed(mediaInfo, otherDeviceInfo)
+                    }
+                }
                 else -> {
                     pw.println("Chip type must be one of " +
-                            "$MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME, " +
-                            "$TRANSFER_INITIATED_COMMAND_NAME, " +
-                            TRANSFER_SUCCEEDED_COMMAND_NAME
+                            "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
+                            "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
+                            "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
+                            "$TRANSFER_SUCCEEDED_COMMAND_NAME, " +
+                            TRANSFER_FAILED_COMMAND_NAME
                     )
                 }
             }
@@ -114,19 +132,40 @@
                     "$ADD_CHIP_COMMAND_SENDER_TAG <deviceName> <chipStatus>"
             )
         }
+
+        private fun runOnService(command: SenderCallbackCommand) {
+            val currentServiceCallback = senderCallback
+            if (currentServiceCallback != null) {
+                command.run(currentServiceCallback)
+            } else {
+                bindService(command)
+            }
+        }
+
+        private fun bindService(command: SenderCallbackCommand) {
+            senderServiceConnection.pendingCommand = command
+            val binding = context.bindService(
+                Intent(context, MediaTttSenderService::class.java),
+                senderServiceConnection,
+                Context.BIND_AUTO_CREATE
+            )
+            Log.i(TAG, "Starting service binding? $binding")
+        }
     }
 
     /** A command to REMOVE the media ttt chip on the SENDER device. */
     inner class RemoveChipCommandSender : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
             mediaTttChipControllerSender.removeChip()
+            if (senderCallback != null) {
+                context.unbindService(senderServiceConnection)
+            }
         }
         override fun help(pw: PrintWriter) {
             pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_SENDER_TAG")
         }
     }
 
-
     /** A command to DISPLAY the media ttt chip on the RECEIVER device. */
     inner class AddChipCommandReceiver : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
@@ -149,6 +188,29 @@
         }
     }
 
+    /** A service connection for [IDeviceSenderCallback]. */
+    private inner class SenderServiceConnection : ServiceConnection {
+        // A command that should be run when the service gets connected.
+        var pendingCommand: SenderCallbackCommand? = null
+
+        override fun onServiceConnected(className: ComponentName, service: IBinder) {
+            val newCallback = IDeviceSenderCallback.Stub.asInterface(service)
+            senderCallback = newCallback
+            pendingCommand?.run(newCallback)
+            pendingCommand = null
+        }
+
+        override fun onServiceDisconnected(className: ComponentName) {
+            senderCallback = null
+        }
+    }
+
+    /** An interface defining a command that should be run on the sender callback. */
+    private fun interface SenderCallbackCommand {
+        /** Runs the command on the provided [senderCallback]. */
+        fun run(senderCallback: IDeviceSenderCallback)
+    }
+
     private val fakeUndoRunnable = Runnable {
         Log.i(TAG, "Undo runnable triggered")
     }
@@ -163,12 +225,15 @@
 @VisibleForTesting
 const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
 @VisibleForTesting
-val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!!
+val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!!
 @VisibleForTesting
-val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
+val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME = TransferToReceiverTriggered::class.simpleName!!
 @VisibleForTesting
 val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
 
-private const val FUTURE_WAIT_TIME = 2000L
 private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
 private const val TAG = "MediaTapToTransferCli"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index b1f6faa..1fd3af4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -20,7 +20,6 @@
 import androidx.annotation.StringRes
 import com.android.systemui.R
 import com.android.systemui.media.taptotransfer.common.MediaTttChipState
-import java.util.concurrent.Future
 
 /**
  * A class that stores all the information necessary to display the media tap-to-transfer chip on
@@ -40,33 +39,45 @@
 ) : MediaTttChipState(appIconDrawable, appIconContentDescription)
 
 /**
- * A state representing that the two devices are close but not close enough to initiate a transfer.
- * The chip will instruct the user to move closer in order to initiate the transfer.
+ * A state representing that the two devices are close but not close enough to *start* a cast to
+ * the receiver device. The chip will instruct the user to move closer in order to initiate the
+ * transfer to the receiver.
  */
-class MoveCloserToTransfer(
+class MoveCloserToStartCast(
     appIconDrawable: Drawable,
     appIconContentDescription: String,
     otherDeviceName: String,
 ) : ChipStateSender(
     appIconDrawable,
     appIconContentDescription,
-    R.string.media_move_closer_to_transfer,
+    R.string.media_move_closer_to_start_cast,
     otherDeviceName
 )
 
 /**
- * A state representing that a transfer has been initiated (but not completed).
- *
- * @property future a future that will be resolved when the transfer has either succeeded or failed.
- *   If the transfer succeeded, the future can optionally return an undo runnable (see
- *   [TransferSucceeded.undoRunnable]). [MediaTttChipControllerSender] is responsible for transitioning
- *   the chip to the [TransferSucceeded] state if the future resolves successfully.
+ * A state representing that the two devices are close but not close enough to *end* a cast that's
+ * currently occurring the receiver device. The chip will instruct the user to move closer in order
+ * to initiate the transfer from the receiver and back onto this device (the original sender).
  */
-class TransferInitiated(
+class MoveCloserToEndCast(
     appIconDrawable: Drawable,
     appIconContentDescription: String,
     otherDeviceName: String,
-    val future: Future<Runnable?>
+) : ChipStateSender(
+    appIconDrawable,
+    appIconContentDescription,
+    R.string.media_move_closer_to_end_cast,
+    otherDeviceName
+)
+
+/**
+ * A state representing that a transfer to the receiver device has been initiated (but not
+ * completed).
+ */
+class TransferToReceiverTriggered(
+    appIconDrawable: Drawable,
+    appIconContentDescription: String,
+    otherDeviceName: String
 ) : ChipStateSender(
     appIconDrawable,
     appIconContentDescription,
@@ -90,3 +101,17 @@
     R.string.media_transfer_playing,
     otherDeviceName
 )
+
+/** A state representing that a transfer has failed. */
+class TransferFailed(
+    appIconDrawable: Drawable,
+    appIconContentDescription: String,
+    // TODO(b/211493953): The failed chip doesn't need [otherDeviceName] so we may want to remove
+    //   [otherDeviceName] from the superclass [ChipStateSender].
+    otherDeviceName: String,
+) : ChipStateSender(
+    appIconDrawable,
+    appIconContentDescription,
+    R.string.media_transfer_failed,
+    otherDeviceName
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 77d3d70..84672ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -23,11 +23,7 @@
 import android.widget.TextView
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
-import java.util.concurrent.Executor
-import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
 /**
@@ -38,8 +34,6 @@
 class MediaTttChipControllerSender @Inject constructor(
     context: Context,
     windowManager: WindowManager,
-    @Main private val mainExecutor: Executor,
-    @Background private val backgroundExecutor: Executor,
 ) : MediaTttChipControllerCommon<ChipStateSender>(
     context, windowManager, R.layout.media_ttt_chip
 ) {
@@ -55,7 +49,7 @@
         }
 
         // Loading
-        val showLoading = chipState is TransferInitiated
+        val showLoading = chipState is TransferToReceiverTriggered
         currentChipView.requireViewById<View>(R.id.loading).visibility =
             if (showLoading) { View.VISIBLE } else { View.GONE }
 
@@ -73,41 +67,9 @@
         }
         undoView.setOnClickListener(undoClickListener)
 
-        // Future handling
-        if (chipState is TransferInitiated) {
-            addFutureCallback(chipState)
-        }
-    }
-
-    /**
-     * Adds the appropriate callbacks to [chipState.future] so that we update the chip correctly
-     * when the future resolves.
-     */
-    private fun addFutureCallback(chipState: TransferInitiated) {
-        // Listen to the future on a background thread so we don't occupy the main thread while we
-        // wait for it to complete.
-        backgroundExecutor.execute {
-            try {
-                val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS)
-                // Make UI changes on the main thread
-                mainExecutor.execute {
-                    displayChip(
-                        TransferSucceeded(
-                            chipState.appIconDrawable,
-                            chipState.appIconContentDescription,
-                            chipState.otherDeviceName,
-                            undoRunnable
-                        )
-                    )
-                }
-            } catch (ex: Exception) {
-                // TODO(b/203800327): Maybe show a failure chip here if UX decides we need one.
-                mainExecutor.execute {
-                    removeChip()
-                }
-            }
-        }
+        // Failure
+        val showFailure = chipState is TransferFailed
+        currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
+            if (showFailure) { View.VISIBLE } else { View.GONE }
     }
 }
-
-private const val TRANSFER_TIMEOUT_SECONDS = 10L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
new file mode 100644
index 0000000..0fe324e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.sender
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.os.IBinder
+import com.android.systemui.R
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import javax.inject.Inject
+
+/**
+ * Service that allows external handlers to trigger the media chip on the sender device.
+ */
+class MediaTttSenderService @Inject constructor(
+    context: Context,
+    val controller: MediaTttChipControllerSender
+) : Service() {
+
+    // TODO(b/203800643): Add logging when callbacks trigger.
+    private val binder: IBinder = object : IDeviceSenderCallback.Stub() {
+        override fun closeToReceiverToStartCast(
+            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+        ) {
+            [email protected](mediaInfo, otherDeviceInfo)
+        }
+
+        override fun closeToReceiverToEndCast(
+            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+        ) {
+            [email protected](mediaInfo, otherDeviceInfo)
+        }
+
+        override fun transferFailed(
+            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+        ) {
+            [email protected](mediaInfo, otherDeviceInfo)
+        }
+
+        override fun transferToReceiverTriggered(
+            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+        ) {
+            [email protected](mediaInfo, otherDeviceInfo)
+        }
+    }
+
+    // TODO(b/203800643): Use the app icon from the media info instead of a fake one.
+    private val fakeAppIconDrawable =
+        Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
+            it.setTint(Color.YELLOW)
+        }
+
+    override fun onBind(intent: Intent?): IBinder = binder
+
+    private fun closeToReceiverToStartCast(
+        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+    ) {
+        val chipState = MoveCloserToStartCast(
+            appIconDrawable = fakeAppIconDrawable,
+            appIconContentDescription = mediaInfo.name.toString(),
+            otherDeviceName = otherDeviceInfo.name
+        )
+        controller.displayChip(chipState)
+    }
+
+    private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
+        val chipState = MoveCloserToEndCast(
+            appIconDrawable = fakeAppIconDrawable,
+            appIconContentDescription = mediaInfo.name.toString(),
+            otherDeviceName = otherDeviceInfo.name
+        )
+        controller.displayChip(chipState)
+    }
+
+    private fun transferFailed(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
+        val chipState = TransferFailed(
+            appIconDrawable = fakeAppIconDrawable,
+            appIconContentDescription = mediaInfo.name.toString(),
+            otherDeviceName = otherDeviceInfo.name
+        )
+        controller.displayChip(chipState)
+    }
+
+    private fun transferToReceiverTriggered(
+        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+    ) {
+        val chipState = TransferToReceiverTriggered(
+            appIconDrawable = fakeAppIconDrawable,
+            appIconContentDescription = mediaInfo.name.toString(),
+            otherDeviceName = otherDeviceInfo.name
+        )
+        controller.displayChip(chipState)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9c4227e2..1bef32a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -141,6 +141,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -190,6 +191,7 @@
     private final Optional<Pip> mPipOptional;
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     private final Optional<Recents> mRecentsOptional;
+    private final Optional<BackAnimation> mBackAnimation;
     private final SystemActions mSystemActions;
     private final Handler mHandler;
     private final NavigationBarOverlayController mNavbarOverlayController;
@@ -227,6 +229,7 @@
     private @Behavior int mBehavior;
 
     private boolean mTransientShown;
+    private boolean mTransientShownFromGestureOnSystemBar;
     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
     private LightBarController mLightBarController;
     private final LightBarController mMainLightBarController;
@@ -503,7 +506,8 @@
             AutoHideController mainAutoHideController,
             AutoHideController.Factory autoHideControllerFactory,
             Optional<TelecomManager> telecomManagerOptional,
-            InputMethodManager inputMethodManager) {
+            InputMethodManager inputMethodManager,
+            Optional<BackAnimation> backAnimation) {
         mContext = context;
         mWindowManager = windowManager;
         mAccessibilityManager = accessibilityManager;
@@ -523,6 +527,7 @@
         mPipOptional = pipOptional;
         mSplitScreenOptional = splitScreenOptional;
         mRecentsOptional = recentsOptional;
+        mBackAnimation = backAnimation;
         mSystemActions = systemActions;
         mHandler = mainHandler;
         mNavbarOverlayController = navbarOverlayController;
@@ -628,6 +633,7 @@
 
         mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
         mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
+        mBackAnimation.ifPresent(mNavigationBarView::registerBackAnimation);
 
         prepareNavigationBarView();
         checkNavBarModes();
@@ -871,6 +877,9 @@
                 + windowStateToString(mNavigationBarWindowState));
         pw.println("  mNavigationBarMode="
                 + BarTransitions.modeToString(mNavigationBarMode));
+        pw.println("  mTransientShown=" + mTransientShown);
+        pw.println("  mTransientShownFromGestureOnSystemBar="
+                + mTransientShownFromGestureOnSystemBar);
         dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
         mNavigationBarView.dump(pw);
     }
@@ -989,7 +998,8 @@
     }
 
     @Override
-    public void showTransient(int displayId, @InternalInsetsType int[] types) {
+    public void showTransient(int displayId, @InternalInsetsType int[] types,
+            boolean isGestureOnSystemBar) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -998,6 +1008,7 @@
         }
         if (!mTransientShown) {
             mTransientShown = true;
+            mTransientShownFromGestureOnSystemBar = isGestureOnSystemBar;
             handleTransientChanged();
         }
     }
@@ -1016,12 +1027,14 @@
     private void clearTransient() {
         if (mTransientShown) {
             mTransientShown = false;
+            mTransientShownFromGestureOnSystemBar = false;
             handleTransientChanged();
         }
     }
 
     private void handleTransientChanged() {
-        mNavigationBarView.onTransientStateChanged(mTransientShown);
+        mNavigationBarView.onTransientStateChanged(mTransientShown,
+                mTransientShownFromGestureOnSystemBar);
         final int barMode = barMode(mTransientShown, mAppearance);
         if (updateBarMode(barMode) && mLightBarController != null) {
             mLightBarController.onNavigationBarModeChanged(barMode);
@@ -1672,6 +1685,7 @@
         private final AutoHideController.Factory mAutoHideControllerFactory;
         private final Optional<TelecomManager> mTelecomManagerOptional;
         private final InputMethodManager mInputMethodManager;
+        private final Optional<BackAnimation> mBackAnimation;
 
         @Inject
         public Factory(
@@ -1704,7 +1718,8 @@
                 AutoHideController mainAutoHideController,
                 AutoHideController.Factory autoHideControllerFactory,
                 Optional<TelecomManager> telecomManagerOptional,
-                InputMethodManager inputMethodManager) {
+                InputMethodManager inputMethodManager,
+                Optional<BackAnimation> backAnimation) {
             mAssistManagerLazy = assistManagerLazy;
             mAccessibilityManager = accessibilityManager;
             mDeviceProvisionedController = deviceProvisionedController;
@@ -1735,6 +1750,7 @@
             mAutoHideControllerFactory = autoHideControllerFactory;
             mTelecomManagerOptional = telecomManagerOptional;
             mInputMethodManager = inputMethodManager;
+            mBackAnimation = backAnimation;
         }
 
         /** Construct a {@link NavigationBar} */
@@ -1751,7 +1767,7 @@
                     mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
                     mUserTracker, mMainLightBarController, mLightBarControllerFactory,
                     mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
-                    mInputMethodManager);
+                    mInputMethodManager, mBackAnimation);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index a984974..98b49b1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
 import java.io.FileDescriptor;
@@ -109,7 +110,8 @@
             DumpManager dumpManager,
             AutoHideController autoHideController,
             LightBarController lightBarController,
-            Optional<Pip> pipOptional) {
+            Optional<Pip> pipOptional,
+            Optional<BackAnimation> backAnimation) {
         mContext = context;
         mHandler = mainHandler;
         mNavigationBarFactory = navigationBarFactory;
@@ -121,7 +123,8 @@
         mTaskbarDelegate = taskbarDelegate;
         mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
-                dumpManager, autoHideController, lightBarController, pipOptional);
+                dumpManager, autoHideController, lightBarController, pipOptional,
+                backAnimation.orElse(null));
         mIsTablet = isTablet(mContext);
         dumpManager.registerDumpable(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 7adb7ac..2dd89f3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.navigationbar;
 
+import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
@@ -90,6 +91,7 @@
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -185,6 +187,17 @@
     @Nullable
     private Rect mOrientedHandleSamplingRegion;
 
+    /**
+     * {@code true} if the IME can render the back button and the IME switcher button.
+     *
+     * <p>The value must be used when and only when
+     * {@link com.android.systemui.shared.system.QuickStepContract#isGesturalMode(int)} returns
+     * {@code true}</p>
+     *
+     * <p>Cache the value here for better performance.</p>
+     */
+    private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons();
+
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
         private boolean mHomeAppearing;
@@ -447,12 +460,17 @@
         mRegionSamplingHelper.setWindowHasBlurs(hasBlurs);
     }
 
-    void onTransientStateChanged(boolean isTransient) {
+    void onTransientStateChanged(boolean isTransient, boolean isGestureOnSystemBar) {
         mEdgeBackGestureHandler.onNavBarTransientStateChanged(isTransient);
 
         // The visibility of the navigation bar buttons is dependent on the transient state of
         // the navigation bar.
         if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            // Always allow the overlay if in non-gestural nav mode, otherwise, only allow showing
+            // the overlay if the user is swiping directly over a system bar
+            boolean allowNavBarOverlay = !QuickStepContract.isGesturalMode(mNavBarMode)
+                    || isGestureOnSystemBar;
+            isTransient = isTransient && allowNavBarOverlay;
             mNavBarOverlayController.setButtonState(isTransient, /* force */ false);
         }
     }
@@ -755,9 +773,14 @@
 
         updateRecentsIcon();
 
+        boolean isImeRenderingNavButtons = isGesturalMode(mNavBarMode)
+                && mImeCanRenderGesturalNavButtons;
+
         // Update IME button visibility, a11y and rotate button always overrides the appearance
-        mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher,
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
+        boolean disableImeSwitcher =
+                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0
+                || isImeRenderingNavButtons;
+        mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
 
         mBarTransitions.reapplyDarkIntensity();
 
@@ -772,7 +795,8 @@
                 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
 
         boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
-                || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0));
+                || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0))
+                || isImeRenderingNavButtons;
 
         // When screen pinning, don't hide back and home when connected service or back and
         // recents buttons when disconnected from launcher service in screen pinning mode,
@@ -1394,6 +1418,10 @@
         pip.removePipExclusionBoundsChangeListener(mPipListener);
     }
 
+    void registerBackAnimation(BackAnimation backAnimation) {
+        mEdgeBackGestureHandler.setBackAnimation(backAnimation);
+    }
+
     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
         pw.print("      " + caption + ": ");
         if (button == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index feda99f..441e79a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -19,7 +19,7 @@
 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.containsType;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
 import java.io.FileDescriptor;
@@ -86,6 +87,7 @@
     private static final String TAG = TaskbarDelegate.class.getSimpleName();
 
     private final EdgeBackGestureHandler mEdgeBackGestureHandler;
+    private final NavigationBarOverlayController mNavBarOverlayController;
     private boolean mInitialized;
     private CommandQueue mCommandQueue;
     private OverviewProxyService mOverviewProxyService;
@@ -140,13 +142,26 @@
 
         @Override
         public void hide() {
+            clearTransient();
         }
     };
 
+    private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
+        if (visible) {
+            mAutoHideController.touchAutoHide();
+        }
+    };
+    private BackAnimation mBackAnimation;
+
     @Inject
     public TaskbarDelegate(Context context) {
         mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
                 .create(context);
+        mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback,
+                    mEdgeBackGestureHandler::updateNavigationBarOverlayExcludeRegion);
+        }
         mContext = context;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds;
@@ -159,7 +174,8 @@
             SysUiState sysUiState, DumpManager dumpManager,
             AutoHideController autoHideController,
             LightBarController lightBarController,
-            Optional<Pip> pipOptional) {
+            Optional<Pip> pipOptional,
+            BackAnimation backAnimation) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mCommandQueue = commandQueue;
         mOverviewProxyService = overviewProxyService;
@@ -171,6 +187,7 @@
         mLightBarController = lightBarController;
         mLightBarTransitionsController = createLightBarTransitionsController();
         mPipOptional = pipOptional;
+        mBackAnimation = backAnimation;
     }
 
     // Separated into a method to keep setDependencies() clean/readable.
@@ -206,6 +223,9 @@
                 mNavigationModeController.addListener(this));
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
         mNavBarHelper.init();
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.registerListeners();
+        }
         mEdgeBackGestureHandler.onNavBarAttached();
         // Initialize component callback
         Display display = mDisplayManager.getDisplay(displayId);
@@ -217,6 +237,7 @@
         mAutoHideController.setNavigationBar(mAutoHideUiElement);
         mLightBarController.setNavigationBar(mLightBarTransitionsController);
         mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
+        mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
         mInitialized = true;
     }
 
@@ -229,6 +250,9 @@
         mNavigationModeController.removeListener(this);
         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
         mNavBarHelper.destroy();
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.unregisterListeners();
+        }
         mEdgeBackGestureHandler.onNavBarDetached();
         mScreenPinningNotify = null;
         if (mWindowContext != null) {
@@ -350,14 +374,17 @@
     }
 
     @Override
-    public void showTransient(int displayId, int[] types) {
+    public void showTransient(int displayId, int[] types, boolean isGestureOnSystemBar) {
         if (displayId != mDisplayId) {
             return;
         }
-        if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+        if (!containsType(types, ITYPE_EXTRA_NAVIGATION_BAR)) {
             return;
         }
-        mTaskbarTransientShowing = true;
+        if (!mTaskbarTransientShowing) {
+            mTaskbarTransientShowing = true;
+            onTransientStateChanged();
+        }
     }
 
     @Override
@@ -365,15 +392,14 @@
         if (displayId != mDisplayId) {
             return;
         }
-        if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+        if (!containsType(types, ITYPE_EXTRA_NAVIGATION_BAR)) {
             return;
         }
-        mTaskbarTransientShowing = false;
+        clearTransient();
     }
 
     @Override
     public void onTaskbarAutohideSuspend(boolean suspend) {
-        mTaskbarTransientShowing = suspend;
         if (suspend) {
             mAutoHideController.suspendAutoHide();
         } else {
@@ -381,6 +407,30 @@
         }
     }
 
+    private void clearTransient() {
+        if (mTaskbarTransientShowing) {
+            mTaskbarTransientShowing = false;
+            onTransientStateChanged();
+        }
+    }
+
+    private void onTransientStateChanged() {
+        mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTaskbarTransientShowing);
+
+        // The visibility of the navigation bar buttons is dependent on the transient state of
+        // the navigation bar.
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.setButtonState(mTaskbarTransientShowing, /* force */ false);
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationStateChanged(boolean running) {
+        if (running) {
+            mNavBarOverlayController.setButtonState(/* visible */false, /* force */true);
+        }
+    }
+
     @Override
     public void onNavigationModeChanged(int mode) {
         mNavigationMode = mode;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index ab48a28..4f4bd1e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -36,6 +36,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.provider.DeviceConfig;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -79,6 +80,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.wm.shell.back.BackAnimation;
 
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
@@ -231,6 +233,7 @@
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
 
     private NavigationEdgeBackPlugin mEdgeBackPlugin;
+    private BackAnimation mBackAnimation;
     private int mLeftInset;
     private int mRightInset;
     private int mSysUiFlags;
@@ -494,7 +497,7 @@
                     Choreographer.getInstance(), this::onInputEvent);
 
             // Add a nav bar panel window
-            setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+            setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation));
             mPluginManager.addPluginListener(
                     this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
         }
@@ -509,7 +512,7 @@
 
     @Override
     public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
-        setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+        setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation));
     }
 
     private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
@@ -576,7 +579,9 @@
             mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
                     SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
             if (mBackGestureTfClassifierProvider.isActive()) {
+                Trace.beginSection("EdgeBackGestureHandler#loadVocab");
                 mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
+                Trace.endSection();
                 mUseMLModel = true;
                 return;
             }
@@ -930,6 +935,10 @@
         proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
     }
 
+    public void setBackAnimation(BackAnimation backAnimation) {
+        mBackAnimation = backAnimation;
+    }
+
     /**
      * Injectable instance to create a new EdgeBackGestureHandler.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 8d1dfc8..c18209d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -57,6 +57,7 @@
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.wm.shell.back.BackAnimation;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -277,11 +278,14 @@
                 }
             };
     private BackCallback mBackCallback;
+    private final BackAnimation mBackAnimation;
 
-    public NavigationBarEdgePanel(Context context) {
+    public NavigationBarEdgePanel(Context context,
+            BackAnimation backAnimation) {
         super(context);
 
         mWindowManager = context.getSystemService(WindowManager.class);
+        mBackAnimation = backAnimation;
         mVibratorHelper = Dependency.get(VibratorHelper.class);
 
         mDensity = context.getResources().getDisplayMetrics().density;
@@ -459,6 +463,9 @@
 
     @Override
     public void onMotionEvent(MotionEvent event) {
+        if (mBackAnimation != null) {
+            mBackAnimation.onBackMotion(event);
+        }
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -866,6 +873,9 @@
             // Whenever the trigger back state changes the existing translation animation should be
             // cancelled
             mTranslationAnimation.cancel();
+            if (mBackAnimation != null) {
+                mBackAnimation.setTriggerBack(triggerBack);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
index c8e2ca7..e26c768 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
@@ -93,6 +93,7 @@
     private final DeviceConfigProxy mDeviceConfigProxy;
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final UserTracker mUserTracker;
+    private final boolean mConfigEnableLockScreenButton;
 
     private HashMap<Integer, ContentObserver> mQRCodeScannerPreferenceObserver = new HashMap<>();
     private DeviceConfig.OnPropertiesChangedListener mOnDefaultQRCodeScannerChangedListener = null;
@@ -118,6 +119,9 @@
         mSecureSettings = secureSettings;
         mDeviceConfigProxy = proxy;
         mUserTracker = userTracker;
+
+        mConfigEnableLockScreenButton = mContext.getResources().getBoolean(
+            android.R.bool.config_enableQrCodeScannerOnLockScreen);
     }
 
     /**
@@ -156,7 +160,7 @@
      * Returns true if lock screen entry point for QR Code Scanner is to be enabled.
      */
     public boolean isEnabledForLockScreenButton() {
-        return mQRCodeScannerEnabled && mIntent != null;
+        return mQRCodeScannerEnabled && mIntent != null && mConfigEnableLockScreenButton;
     }
 
     /**
@@ -235,6 +239,11 @@
     }
 
     private void updateQRCodeScannerPreferenceDetails(boolean updateSettings) {
+        if (!mConfigEnableLockScreenButton) {
+            // Settings only apply to lock screen entry point.
+            return;
+        }
+
         boolean prevQRCodeScannerEnabled = mQRCodeScannerEnabled;
         mQRCodeScannerEnabled = mSecureSettings.getIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 mUserTracker.getUserId()) != 0;
@@ -251,8 +260,15 @@
     private void updateQRCodeScannerActivityDetails() {
         String qrCodeScannerActivity = mDeviceConfigProxy.getString(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER,
-                mContext.getResources().getString(R.string.def_qr_code_component));
+                SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER, "");
+
+        // "" means either the flags is not available or is set to "", and in both the cases we
+        // want to use R.string.def_qr_code_component
+        if (Objects.equals(qrCodeScannerActivity, "")) {
+            qrCodeScannerActivity =
+                    mContext.getResources().getString(R.string.def_qr_code_component);
+        }
+
         String prevQrCodeScannerActivity = mQRCodeScannerActivity;
         ComponentName componentName = null;
         Intent intent = new Intent();
@@ -281,8 +297,12 @@
         // Our intent should always be explicit and should have a component set
         if (intent.getComponent() == null) return false;
 
-        int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+        int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                | PackageManager.MATCH_DISABLED_COMPONENTS
+                | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
         return !mContext.getPackageManager().queryIntentActivities(intent,
                 flags).isEmpty();
     }
@@ -296,6 +316,11 @@
     }
 
     private void unregisterQRCodePreferenceObserver() {
+        if (!mConfigEnableLockScreenButton) {
+            // Settings only apply to lock screen entry point.
+            return;
+        }
+
         mQRCodeScannerPreferenceObserver.forEach((key, value) -> {
             mSecureSettings.unregisterContentObserver(value);
         });
@@ -357,6 +382,11 @@
     }
 
     private void registerQRCodePreferenceObserver() {
+        if (!mConfigEnableLockScreenButton) {
+            // Settings only apply to lock screen entry point.
+            return;
+        }
+
         int userId = mUserTracker.getUserId();
         if (mQRCodeScannerPreferenceObserver.getOrDefault(userId, null) != null) return;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
index 1195184..18d28bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
@@ -36,6 +36,7 @@
     private final int mItemSize;
     private final Handler mHandler;
 
+    @Nullable
     private ListAdapter mAdapter;
     private int mCount;
     private boolean mEnableAutoSizing;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index e10e4d8..7ac9205 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -56,7 +56,6 @@
  */
 class FooterActionsController @Inject constructor(
     view: FooterActionsView,
-    private val qsPanelController: QSPanelController,
     private val activityStarter: ActivityStarter,
     private val userManager: UserManager,
     private val userTracker: UserTracker,
@@ -82,7 +81,6 @@
 
     private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
     private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
-    private val editButton: View = view.findViewById(android.R.id.edit)
     private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
 
     private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
@@ -176,13 +174,6 @@
             powerMenuLite.visibility = View.GONE
         }
         settingsButton.setOnClickListener(onClickListener)
-        editButton.setOnClickListener(View.OnClickListener { view: View? ->
-            if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return@OnClickListener
-            }
-            activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
-        })
-
         updateView()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index dd4dc87..7694be5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -36,7 +36,6 @@
 import javax.inject.Named
 
 class FooterActionsControllerBuilder @Inject constructor(
-    private val qsPanelController: QSPanelController,
     private val activityStarter: ActivityStarter,
     private val userManager: UserManager,
     private val userTracker: UserTracker,
@@ -66,7 +65,7 @@
     }
 
     fun build(): FooterActionsController {
-        return FooterActionsController(view, qsPanelController, activityStarter, userManager,
+        return FooterActionsController(view, activityStarter, userManager,
                 userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
                 deviceProvisionedController, falsingManager, metricsLogger, tunerService,
                 globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index f81f7bf..e6fa2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -43,7 +43,6 @@
     private lateinit var multiUserSwitch: MultiUserSwitch
     private lateinit var multiUserAvatar: ImageView
     private lateinit var tunerIcon: View
-    private lateinit var editTilesButton: View
 
     private var settingsCogAnimator: TouchAnimator? = null
 
@@ -52,7 +51,6 @@
 
     override fun onFinishInflate() {
         super.onFinishInflate()
-        editTilesButton = requireViewById(android.R.id.edit)
         settingsButton = findViewById(R.id.settings_button)
         settingsContainer = findViewById(R.id.settings_button_container)
         multiUserSwitch = findViewById(R.id.multi_user_switch)
@@ -130,7 +128,6 @@
 
     private fun updateClickabilities() {
         multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
-        editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
         settingsButton.isClickable = settingsButton.visibility == VISIBLE
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 6d1f8f7..d20141b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -19,6 +19,7 @@
 import android.view.animation.OvershootInterpolator;
 import android.widget.Scroller;
 
+import androidx.annotation.Nullable;
 import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 
@@ -51,14 +52,17 @@
     private final ArrayList<TileRecord> mTiles = new ArrayList<>();
     private final ArrayList<TileLayout> mPages = new ArrayList<>();
 
+    @Nullable
     private PageIndicator mPageIndicator;
     private float mPageIndicatorPosition;
 
+    @Nullable
     private PageListener mPageListener;
 
     private boolean mListening;
     private Scroller mScroller;
 
+    @Nullable
     private AnimatorSet mBounceAnimatorSet;
     private float mLastExpansion;
     private boolean mDistributeTiles = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 6c7d6e0..1dba536 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -26,6 +26,9 @@
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnLayoutChangeListener;
 
+import androidx.annotation.Nullable;
+
+import com.android.systemui.animation.Interpolators;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSTile;
@@ -39,7 +42,6 @@
 import com.android.systemui.qs.tileimpl.HeightOverrideable;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.wm.shell.animation.Interpolators;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -88,6 +90,7 @@
     private final View mQSFooterActions;
     private final View mQQSFooterActions;
 
+    @Nullable
     private PagedTileLayout mPagedLayout;
 
     private boolean mOnFirstPage = true;
@@ -95,6 +98,7 @@
     private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
     // Animator for elements in the first page, including secondary labels and qqs brightness
     // slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
+    @Nullable
     private TouchAnimator mFirstPageAnimator;
     // TranslationX animator for QQS/QS tiles
     private TouchAnimator mTranslationXAnimator;
@@ -109,13 +113,16 @@
     // This animates fading of SecurityFooter and media divider
     private TouchAnimator mAllPagesDelayedAnimator;
     // Animator for brightness slider(s)
+    @Nullable
     private TouchAnimator mBrightnessAnimator;
     // Animator for Footer actions in QQS
     private TouchAnimator mQQSFooterActionsAnimator;
     // Height animator for QQS tiles (height changing from QQS size to QS size)
+    @Nullable
     private HeightExpansionAnimator mQQSTileHeightAnimator;
     // Height animator for QS tile in first page but not in QQS, to present the illusion that they
     // are expanding alongside the QQS tiles
+    @Nullable
     private HeightExpansionAnimator mOtherFirstPageTilesHeightAnimator;
     // Pair of animators for each non first page. The creation is delayed until the user first
     // scrolls to that page, in order to get the proper measures and layout.
@@ -215,7 +222,7 @@
     }
 
     @Override
-    public void onViewAttachedToWindow(View v) {
+    public void onViewAttachedToWindow(@Nullable View v) {
         mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
                 MOVE_FULL_ROWS);
     }
@@ -294,6 +301,8 @@
         TouchAnimator.Builder qqsTranslationYBuilder = new Builder();
         TouchAnimator.Builder translationXBuilder = new Builder();
         TouchAnimator.Builder nonFirstPageAlphaBuilder = new Builder();
+        TouchAnimator.Builder quadraticInterpolatorBuilder = new Builder()
+                .setInterpolator(Interpolators.ACCELERATE);
 
         Collection<QSTile> tiles = mHost.getTiles();
         int count = 0;
@@ -406,7 +415,13 @@
                             qqsTranslationYBuilder
                     );
 
-                    firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
+                    // Secondary labels on tiles not in QQS have two alpha animation applied:
+                    // * on the tile themselves
+                    // * on TileLayout
+                    // Therefore, we use a quadratic interpolator animator to animate the alpha
+                    // for tiles in QQS to match.
+                    quadraticInterpolatorBuilder
+                            .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
                     nonFirstPageAlphaBuilder
                             .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 0);
 
@@ -454,6 +469,7 @@
             mFirstPageAnimator = firstPageBuilder
                     // Fade in the tiles/labels as we reach the final position.
                     .addFloat(tileLayout, "alpha", 0, 1)
+                    .addFloat(quadraticInterpolatorBuilder.build(), "position", 0, 1)
                     .setListener(this)
                     .build();
 
@@ -528,7 +544,11 @@
             builder.addFloat(tileView.getSecondaryIcon(), "translationY", -centerDiff, 0);
             // The labels have different apparent size in QQS vs QS (no secondary label), so the
             // translation needs to account for that.
-            int labelDiff = centerDiff - tileView.getSecondaryLabel().getMeasuredHeight() / 2;
+            int secondaryLabelOffset = 0;
+            if (tileView.getSecondaryLabel().getVisibility() == View.VISIBLE) {
+                secondaryLabelOffset = tileView.getSecondaryLabel().getMeasuredHeight() / 2;
+            }
+            int labelDiff = centerDiff - secondaryLabelOffset;
             builder.addFloat(tileView.getLabelContainer(), "translationY", -labelDiff, 0);
             builder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 0.3f, 1);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index d43404b..04e2252 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -64,15 +64,18 @@
     protected TextView mDetailDoneButton;
     @VisibleForTesting
     QSDetailClipper mClipper;
+    @Nullable
     private DetailAdapter mDetailAdapter;
     private QSPanelController mQsPanelController;
 
     protected View mQsDetailHeader;
     protected TextView mQsDetailHeaderTitle;
     private ViewStub mQsDetailHeaderSwitchStub;
+    @Nullable
     private Switch mQsDetailHeaderSwitch;
     protected ImageView mQsDetailHeaderProgress;
 
+    @Nullable
     protected QSTileHost mHost;
 
     private boolean mScanState;
@@ -87,6 +90,7 @@
     private boolean mSwitchState;
     private QSFooter mFooter;
 
+    @Nullable
     private QSContainerController mQsContainerController;
 
     public QSDetail(Context context, @Nullable AttributeSet attrs) {
@@ -183,12 +187,14 @@
     }
 
     public interface Callback {
-        void onShowingDetail(DetailAdapter detail, int x, int y);
+        /** Handle an event of showing detail. */
+        void onShowingDetail(@Nullable DetailAdapter detail, int x, int y);
         void onToggleStateChanged(boolean state);
         void onScanStateChanged(boolean state);
     }
 
-    public void handleShowingDetail(final DetailAdapter adapter, int x, int y,
+    /** Handle an event of showing detail. */
+    public void handleShowingDetail(final @Nullable DetailAdapter adapter, int x, int y,
             boolean toggleQs) {
         final boolean showingDetail = adapter != null;
         final boolean wasShowingDetail = mDetailAdapter != null;
@@ -378,7 +384,8 @@
         }
 
         @Override
-        public void onShowingDetail(final DetailAdapter detail, final int x, final int y) {
+        public void onShowingDetail(
+                final @Nullable DetailAdapter detail, final int x, final int y) {
             post(new Runnable() {
                 @Override
                 public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index 63cedd0..43136d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -23,12 +23,15 @@
 import android.view.View;
 import android.view.ViewAnimationUtils;
 
+import androidx.annotation.Nullable;
+
 /** Helper for quick settings detail panel clip animations. **/
 public class QSDetailClipper {
 
     private final View mDetail;
     private final TransitionDrawable mBackground;
 
+    @Nullable
     private Animator mAnimator;
 
     public QSDetailClipper(View detail) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
index b50af00..afd4f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.qs.DetailAdapter;
 
@@ -26,13 +28,14 @@
  */
 @SysUISingleton
 public class QSDetailDisplayer {
+    @Nullable
     private QSPanelController mQsPanelController;
 
     @Inject
     public QSDetailDisplayer() {
     }
 
-    public void setQsPanelController(QSPanelController qsPanelController) {
+    public void setQsPanelController(@Nullable QSPanelController qsPanelController) {
         mQsPanelController = qsPanelController;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index e93c349..eb3247b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -33,6 +33,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile;
@@ -50,6 +52,7 @@
     private final Adapter mAdapter = new Adapter();
 
     private String mTag;
+    @Nullable
     private Callback mCallback;
     private boolean mItemsVisible = true;
     private AutoSizingList mItemList;
@@ -130,7 +133,8 @@
         mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
     }
 
-    public void setItems(Item[] items) {
+    /** Set items. */
+    public void setItems(@Nullable Item[] items) {
         mHandler.removeMessages(H.SET_ITEMS);
         mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
     }
@@ -266,9 +270,12 @@
         }
 
         public int iconResId;
+        @Nullable
         public QSTile.Icon icon;
+        @Nullable
         public Drawable overlay;
         public CharSequence line1;
+        @Nullable
         public CharSequence line2;
         public Object tag;
         public boolean canDisconnect;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 4d23958..4622660 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -47,7 +47,9 @@
     private PageIndicator mPageIndicator;
     private TextView mBuildText;
     private View mActionsContainer;
+    private View mEditButton;
 
+    @Nullable
     protected TouchAnimator mFooterAnimator;
 
     private boolean mQsDisabled;
@@ -56,6 +58,7 @@
 
     private boolean mShouldShowBuildText;
 
+    @Nullable
     private OnClickListener mExpandClickListener;
 
     private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
@@ -77,6 +80,7 @@
         mPageIndicator = findViewById(R.id.footer_page_indicator);
         mActionsContainer = requireViewById(R.id.qs_footer_actions);
         mBuildText = findViewById(R.id.build);
+        mEditButton = findViewById(android.R.id.edit);
 
         updateResources();
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -128,6 +132,7 @@
                 .addFloat(mActionsContainer, "alpha", 0, 1)
                 .addFloat(mPageIndicator, "alpha", 0, 1)
                 .addFloat(mBuildText, "alpha", 0, 1)
+                .addFloat(mEditButton, "alpha", 0, 1)
                 .setStartDelay(0.9f);
         return builder.build();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index e7c06e3..5327b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -26,6 +26,8 @@
 import android.widget.Toast;
 
 import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.ViewController;
@@ -45,10 +47,15 @@
     private final FooterActionsController mFooterActionsController;
     private final TextView mBuildText;
     private final PageIndicator mPageIndicator;
+    private final View mEditButton;
+    private final FalsingManager mFalsingManager;
+    private final ActivityStarter mActivityStarter;
 
     @Inject
     QSFooterViewController(QSFooterView view,
             UserTracker userTracker,
+            FalsingManager falsingManager,
+            ActivityStarter activityStarter,
             QSPanelController qsPanelController,
             QuickQSPanelController quickQSPanelController,
             @Named(QS_FOOTER) FooterActionsController footerActionsController) {
@@ -57,9 +64,12 @@
         mQsPanelController = qsPanelController;
         mQuickQSPanelController = quickQSPanelController;
         mFooterActionsController = footerActionsController;
+        mFalsingManager = falsingManager;
+        mActivityStarter = activityStarter;
 
         mBuildText = mView.findViewById(R.id.build);
         mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
+        mEditButton = mView.findViewById(android.R.id.edit);
     }
 
     @Override
@@ -91,6 +101,14 @@
             }
             return false;
         });
+
+        mEditButton.setOnClickListener(view -> {
+            if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                return;
+            }
+            mActivityStarter
+                    .postQSRunnableDismissingKeyguard(() -> mQsPanelController.showEdit(view));
+        });
         mQsPanelController.setFooterPageIndicator(mPageIndicator);
         mView.updateEverything();
     }
@@ -103,6 +121,7 @@
     @Override
     public void setVisibility(int visibility) {
         mView.setVisibility(visibility);
+        mEditButton.setClickable(visibility == View.VISIBLE);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bb8a1e8..259b786 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -25,6 +25,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Trace;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
@@ -117,6 +118,7 @@
     private QSPanelController mQSPanelController;
     private QuickQSPanelController mQuickQSPanelController;
     private QSCustomizerController mQSCustomizerController;
+    @Nullable
     private ScrollListener mScrollListener;
     /**
      * When true, QS will translate from outside the screen. It will be clipped with parallax
@@ -171,9 +173,14 @@
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
-                R.style.Theme_SystemUI_QuickSettings));
-        return inflater.inflate(R.layout.qs_panel, container, false);
+        try {
+            Trace.beginSection("QSFragment#onCreateView");
+            inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+                    R.style.Theme_SystemUI_QuickSettings));
+            return inflater.inflate(R.layout.qs_panel, container, false);
+        } finally {
+            Trace.endSection();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ff9790c..d4d6da8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -401,6 +401,9 @@
                 pw.print("    "); pw.println(record.tileView.toString());
             }
         }
+        if (mMediaHost != null) {
+            pw.println("  media bounds: " + mMediaHost.getCurrentBounds());
+        }
     }
 
     public QSPanel.QSTileLayout getTileLayout() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 7f19d0e..878f753 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -48,6 +48,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -85,8 +86,10 @@
     protected H mHandler;
 
     private boolean mIsVisible;
+    @Nullable
     private CharSequence mFooterTextContent = null;
     private int mFooterIconId;
+    @Nullable
     private Drawable mPrimaryFooterIconDrawable;
 
     @Inject
@@ -240,6 +243,7 @@
         mMainHandler.post(mUpdateDisplayState);
     }
 
+    @Nullable
     protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
             boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
             String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
@@ -497,6 +501,7 @@
         return mContext.getString(R.string.ok);
     }
 
+    @Nullable
     private String getNegativeButton() {
         if (mSecurityController.isParentalControlsEnabled()) {
             return mContext.getString(R.string.monitoring_button_view_controls);
@@ -504,6 +509,7 @@
         return null;
     }
 
+    @Nullable
     protected CharSequence getManagementMessage(boolean isDeviceManaged,
             CharSequence organizationName) {
         if (!isDeviceManaged) {
@@ -521,6 +527,7 @@
         return mContext.getString(R.string.monitoring_description_management);
     }
 
+    @Nullable
     protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts,
             boolean hasCACertsInWorkProfile) {
         if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
@@ -534,6 +541,7 @@
         return mContext.getString(R.string.monitoring_description_ca_certificate);
     }
 
+    @Nullable
     protected CharSequence getNetworkLoggingMessage(boolean isDeviceManaged,
             boolean isNetworkLoggingEnabled) {
         if (!isNetworkLoggingEnabled) return null;
@@ -545,6 +553,7 @@
         }
     }
 
+    @Nullable
     protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
             String vpnName, String vpnNameWorkProfile) {
         if (vpnName == null && vpnNameWorkProfile == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 1030c21..cca4913 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -29,6 +29,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.UiEventLogger;
@@ -98,6 +100,7 @@
     private final CustomTileStatePersister mCustomTileStatePersister;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
+    @Nullable
     private AutoTileManager mAutoTiles;
     private final StatusBarIconController mIconController;
     private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
@@ -472,6 +475,8 @@
         saveTilesToSettings(newTiles);
     }
 
+    /** Create a {@link QSTile} of a {@code tileSpec} type. */
+    @Nullable
     public QSTile createTile(String tileSpec) {
         for (int i = 0; i < mQsFactories.size(); i++) {
             QSTile t = mQsFactories.get(i).createTile(tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 8b94983..d3f8db38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -33,6 +33,7 @@
 import android.widget.Space;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.policy.SystemBarUtils;
 import com.android.settingslib.Utils;
@@ -44,7 +45,6 @@
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.VariableDateView;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
 
 import java.util.List;
 
@@ -57,8 +57,11 @@
     private boolean mExpanded;
     private boolean mQsDisabled;
 
+    @Nullable
     private TouchAnimator mAlphaAnimator;
+    @Nullable
     private TouchAnimator mTranslationAnimator;
+    @Nullable
     private TouchAnimator mIconsAlphaAnimator;
     private TouchAnimator mIconsAlphaAnimatorFixed;
 
@@ -85,7 +88,9 @@
     private StatusIconContainer mIconContainer;
     private View mPrivacyChip;
 
+    @Nullable
     private TintedIconManager mTintedIconManager;
+    @Nullable
     private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
     private StatusBarContentInsetsProvider mInsetsProvider;
 
@@ -242,10 +247,9 @@
         boolean shouldUseSplitShade =
                 resources.getBoolean(R.bool.config_use_split_notification_shade);
 
-        mStatusIconsView.setVisibility(
-                shouldUseSplitShade || mUseCombinedQSHeader ? View.GONE : View.VISIBLE);
-        mDatePrivacyView.setVisibility(
-                shouldUseSplitShade || mUseCombinedQSHeader ? View.GONE : View.VISIBLE);
+        boolean gone = shouldUseSplitShade || mUseCombinedQSHeader || mQsDisabled;
+        mStatusIconsView.setVisibility(gone ? View.GONE : View.VISIBLE);
+        mDatePrivacyView.setVisibility(gone ? View.GONE : View.VISIBLE);
 
         mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
index bb2340c..130bcab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
@@ -7,13 +7,15 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import androidx.annotation.Nullable;
+
 public class QuickTileLayout extends LinearLayout {
 
     public QuickTileLayout(Context context) {
         this(context, null);
     }
 
-    public QuickTileLayout(Context context, AttributeSet attrs) {
+    public QuickTileLayout(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         setGravity(Gravity.CENTER);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
index a9b2376..9011853 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
@@ -60,7 +60,9 @@
     private final RectF mSlashRect = new RectF(0, 0, 0, 0);
     private float mRotation;
     private boolean mSlashed;
+    @Nullable
     private Mode mTintMode;
+    @Nullable
     private ColorStateList mTintList;
     private boolean mAnimationEnabled = true;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index bff318a..da82d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -9,6 +9,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -49,7 +51,7 @@
         this(context, null);
     }
 
-    public TileLayout(Context context, AttributeSet attrs) {
+    public TileLayout(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         setFocusableInTouchMode(true);
         mLessRows = ((Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0)
@@ -67,7 +69,7 @@
     }
 
     @Override
-    public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+    public void setListening(boolean listening, @Nullable UiEventLogger uiEventLogger) {
         if (mListening == listening) return;
         mListening = listening;
         for (TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index ca8f681..bc62416 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -20,6 +20,8 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.Nullable;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -37,12 +39,19 @@
     private final float mStartDelay;
     private final float mEndDelay;
     private final float mSpan;
+    @Nullable
     private final Interpolator mInterpolator;
+    @Nullable
     private final Listener mListener;
     private float mLastT = -1;
 
-    private TouchAnimator(Object[] targets, KeyframeSet[] keyframeSets,
-            float startDelay, float endDelay, Interpolator interpolator, Listener listener) {
+    private TouchAnimator(
+            Object[] targets,
+            KeyframeSet[] keyframeSets,
+            float startDelay,
+            float endDelay,
+            @Nullable Interpolator interpolator,
+            @Nullable Listener listener) {
         mTargets = targets;
         mKeyframeSets = keyframeSets;
         mStartDelay = startDelay;
@@ -126,7 +135,9 @@
 
         private float mStartDelay;
         private float mEndDelay;
+        @Nullable
         private Interpolator mInterpolator;
+        @Nullable
         private Listener mListener;
 
         public Builder addFloat(Object target, String property, float... values) {
@@ -183,7 +194,8 @@
             return this;
         }
 
-        public Builder setInterpolator(Interpolator intepolator) {
+        /** Sets interpolator. */
+        public Builder setInterpolator(@Nullable Interpolator intepolator) {
             mInterpolator = intepolator;
             return this;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 32ac733..2959c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -25,6 +25,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.Utils;
@@ -40,6 +41,7 @@
     private ImageView mMobileSignal;
     private ImageView mMobileRoaming;
     private View mSpacer;
+    @Nullable
     private CellSignalState mLastSignalState;
     private boolean mProviderModelInitialized = false;
     private boolean mIsSingleCarrier;
@@ -125,7 +127,7 @@
         return true;
     }
 
-    private boolean hasValidTypeContentDescription(String typeContentDescription) {
+    private boolean hasValidTypeContentDescription(@Nullable String typeContentDescription) {
         return TextUtils.equals(typeContentDescription,
                 mContext.getString(R.string.data_connection_no_internet))
                 || TextUtils.equals(typeContentDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 9ebdb1c..b59c0cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -28,6 +28,7 @@
 import android.widget.LinearLayout;
 import android.widget.Toolbar;
 
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -107,7 +108,7 @@
         mQsContainerController = controller;
     }
 
-    public void setQs(QS qs) {
+    public void setQs(@Nullable QS qs) {
         mQs = qs;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 618a429..97390112 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -28,6 +28,7 @@
 import android.widget.Toolbar;
 import android.widget.Toolbar.OnMenuItemClickListener;
 
+import androidx.annotation.Nullable;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -198,7 +199,7 @@
     }
 
     /** */
-    public void setQs(QSFragment qsFragment) {
+    public void setQs(@Nullable QSFragment qsFragment) {
         mView.setQs(qsFragment);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 9acd3eb..d3bad16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -414,9 +414,9 @@
                 public void onLayoutChange(View v, int left, int top, int right, int bottom,
                         int oldLeft, int oldTop, int oldRight, int oldBottom) {
                     holder.mTileView.removeOnLayoutChangeListener(this);
-                    holder.mTileView.requestFocus();
+                    holder.mTileView.requestAccessibilityFocus();
                     if (mAccessibilityAction == ACTION_NONE) {
-                        holder.mTileView.clearFocus();
+                        holder.mTileView.clearAccessibilityFocus();
                     }
                 }
             });
@@ -449,12 +449,13 @@
         // Update the tile divider position
         mTileDividerIndex++;
         mFocusIndex = mEditIndex - 1;
+        final int focus = mFocusIndex;
         mNeedsFocus = true;
         if (mRecyclerView != null) {
             mRecyclerView.post(() -> {
                 final RecyclerView recyclerView = mRecyclerView;
                 if (recyclerView != null) {
-                    recyclerView.smoothScrollToPosition(mFocusIndex);
+                    recyclerView.smoothScrollToPosition(focus);
                 }
             });
         }
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 eae2565..b29687f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -31,6 +31,8 @@
 import android.util.ArraySet;
 import android.widget.Button;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -79,7 +81,7 @@
         mUserTracker = userTracker;
     }
 
-    public void setListener(TileStateListener listener) {
+    public void setListener(@Nullable TileStateListener listener) {
         mListener = listener;
     }
 
@@ -269,6 +271,7 @@
         });
     }
 
+    @Nullable
     private State getState(Collection<QSTile> tiles, String spec) {
         for (QSTile tile : tiles) {
             if (spec.equals(tile.getTileSpec())) {
@@ -278,7 +281,8 @@
         return null;
     }
 
-    private void addTile(String spec, CharSequence appLabel, State state, boolean isSystem) {
+    private void addTile(
+            String spec, @Nullable CharSequence appLabel, State state, boolean isSystem) {
         if (mSpecs.contains(spec)) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 10efec3..4f15351 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -89,7 +89,9 @@
     private final TileServiceManager mServiceManager;
     private final int mUser;
     private final CustomTileStatePersister mCustomTileStatePersister;
+    @Nullable
     private android.graphics.drawable.Icon mDefaultIcon;
+    @Nullable
     private CharSequence mDefaultLabel;
 
     private final Context mUserContext;
@@ -197,8 +199,8 @@
     /**
      * Compare two icons, only works for resources.
      */
-    private boolean iconEquals(android.graphics.drawable.Icon icon1,
-            android.graphics.drawable.Icon icon2) {
+    private boolean iconEquals(@Nullable android.graphics.drawable.Icon icon1,
+            @Nullable android.graphics.drawable.Icon icon2) {
         if (icon1 == icon2) {
             return true;
         }
@@ -372,6 +374,7 @@
                 Uri.fromParts("package", mComponent.getPackageName(), null));
     }
 
+    @Nullable
     private Intent resolveIntent(Intent i) {
         ResolveInfo result = mContext.getPackageManager().resolveActivityAsUser(i, 0, mUser);
         return result != null ? new Intent(TileService.ACTION_QS_TILE_PREFERENCES)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index e6612fe..17ae7ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -36,6 +36,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -85,6 +86,7 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
 
     private Set<Integer> mQueuedMessages = new ArraySet<>();
+    @Nullable
     private QSTileServiceWrapper mWrapper;
     private boolean mListening;
     private IBinder mClickBinder;
@@ -95,6 +97,7 @@
     private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
     private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
     private boolean mUnbindImmediate;
+    @Nullable
     private TileChangeListener mChangeListener;
     // Return value from bindServiceAsUser, determines whether safe to call unbind.
     private boolean mIsBound;
@@ -466,6 +469,7 @@
         }
     }
 
+    @Nullable
     @Override
     public IBinder asBinder() {
         return mWrapper != null ? mWrapper.asBinder() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 11c4949..bd2f64b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -59,6 +59,7 @@
                                     R.dimen.qs_tile_service_request_tile_width),
                             context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
                     )
+                    isSelected = true
         }
         val spacing = 0
         setView(ll, spacing, spacing, spacing, spacing / 2)
@@ -68,12 +69,17 @@
         val tile = QSTileViewImpl(context, QSIconViewImpl(context), true)
         val state = QSTile.BooleanState().apply {
             label = tileData.label
+            handlesLongClick = false
             icon = tileData.icon?.loadDrawable(context)?.let {
                 QSTileImpl.DrawableIcon(it)
             } ?: ResourceIcon.get(R.drawable.android)
         }
         tile.onStateChanged(state)
-        tile.isSelected = true
+        tile.post {
+            tile.stateDescription = ""
+            tile.isClickable = false
+            tile.isSelected = true
+        }
         return tile
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index bfa2aaa4..0a3c17c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -35,6 +35,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Dependency;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -297,6 +299,7 @@
         }
     }
 
+    @Nullable
     @Override
     public Tile getTile(IBinder token) {
         CustomTile customTile = getTileForToken(token);
@@ -330,12 +333,14 @@
         return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing();
     }
 
+    @Nullable
     private CustomTile getTileForToken(IBinder token) {
         synchronized (mServices) {
             return mTokenMap.get(token);
         }
     }
 
+    @Nullable
     private CustomTile getTileForComponent(ComponentName component) {
         synchronized (mServices) {
             return mTiles.get(component);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index c2a9e3a..5e68f61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -18,6 +18,8 @@
 import android.os.Build;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSIconView;
@@ -32,6 +34,7 @@
 import com.android.systemui.qs.tiles.CameraToggleTile;
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
+import com.android.systemui.qs.tiles.ColorCorrectionTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
 import com.android.systemui.qs.tiles.DataSaverTile;
 import com.android.systemui.qs.tiles.DeviceControlsTile;
@@ -71,6 +74,7 @@
     private final Provider<BluetoothTile> mBluetoothTileProvider;
     private final Provider<CellularTile> mCellularTileProvider;
     private final Provider<DndTile> mDndTileProvider;
+    private final Provider<ColorCorrectionTile> mColorCorrectionTileProvider;
     private final Provider<ColorInversionTile> mColorInversionTileProvider;
     private final Provider<AirplaneModeTile> mAirplaneModeTileProvider;
     private final Provider<WorkModeTile> mWorkModeTileProvider;
@@ -133,7 +137,8 @@
             Provider<QuickAccessWalletTile> quickAccessWalletTileProvider,
             Provider<QRCodeScannerTile> qrCodeScannerTileProvider,
             Provider<OneHandedModeTile> oneHandedModeTileProvider,
-            Provider<FgsManagerTile> fgsManagerTileProvider) {
+            Provider<FgsManagerTile> fgsManagerTileProvider,
+            Provider<ColorCorrectionTile> colorCorrectionTileProvider) {
         mQsHostLazy = qsHostLazy;
         mCustomTileBuilderProvider = customTileBuilderProvider;
 
@@ -167,8 +172,11 @@
         mQRCodeScannerTileProvider = qrCodeScannerTileProvider;
         mOneHandedModeTileProvider = oneHandedModeTileProvider;
         mFgsManagerTileProvider = fgsManagerTileProvider;
+        mColorCorrectionTileProvider = colorCorrectionTileProvider;
     }
 
+    /** Creates a tile with a type based on {@code tileSpec} */
+    @Nullable
     public final QSTile createTile(String tileSpec) {
         QSTileImpl tile = createTileInternal(tileSpec);
         if (tile != null) {
@@ -178,6 +186,7 @@
         return tile;
     }
 
+    @Nullable
     protected QSTileImpl createTileInternal(String tileSpec) {
         // Stock tiles.
         switch (tileSpec) {
@@ -239,6 +248,8 @@
                 return mOneHandedModeTileProvider.get();
             case "fgsmanager":
                 return mFgsManagerTileProvider.get();
+            case "color_correction":
+                return mColorCorrectionTileProvider.get();
         }
 
         // Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index b5f07d1..6d9d5b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -116,6 +116,7 @@
     private boolean mAnnounceNextStateChange;
 
     private String mTileSpec;
+    @Nullable
     private EnforcedAdmin mEnforcedAdmin;
     private boolean mShowingDetail;
     private int mIsFullQs;
@@ -260,6 +261,8 @@
         return new QSIconViewImpl(context);
     }
 
+    /** Returns corresponding DetailAdapter. */
+    @Nullable
     public DetailAdapter getDetailAdapter() {
         return null; // optional
     }
@@ -342,7 +345,7 @@
         refreshState(null);
     }
 
-    protected final void refreshState(Object arg) {
+    protected final void refreshState(@Nullable Object arg) {
         mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
     }
 
@@ -432,9 +435,10 @@
      *
      * @return the intent to launch
      */
+    @Nullable
     public abstract Intent getLongClickIntent();
 
-    protected void handleRefreshState(Object arg) {
+    protected void handleRefreshState(@Nullable Object arg) {
         handleUpdateState(mTmpState, arg);
         boolean changed = mTmpState.copyTo(mState);
         if (mReadyState == READY_STATE_READYING) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 6bb79867..7efb983 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -653,7 +653,8 @@
         "qr_code_scanner" to R.array.tile_states_qr_code_scanner,
         "alarm" to R.array.tile_states_alarm,
         "onehanded" to R.array.tile_states_onehanded,
-        "fgsmanager" to R.array.tile_states_fgsmanager
+        "fgsmanager" to R.array.tile_states_fgsmanager,
+        "color_correction" to R.array.tile_states_color_correction
     )
 
     fun getSubtitleId(spec: String?): Int {
@@ -665,4 +666,4 @@
     return PropertyValuesHolder.ofInt(name, *values).apply {
         setEvaluator(ArgbEvaluator.getInstance())
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
index 72c68ce..f1e82b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -27,6 +27,7 @@
 
 public class SlashImageView extends ImageView {
 
+    @Nullable
     @VisibleForTesting
     protected SlashDrawable mSlash;
     private boolean mAnimationEnabled = true;
@@ -35,6 +36,7 @@
         super(context);
     }
 
+    @Nullable
     protected SlashDrawable getSlash() {
         return mSlash;
     }
@@ -52,7 +54,7 @@
     }
 
     @Override
-    public void setImageDrawable(Drawable drawable) {
+    public void setImageDrawable(@Nullable Drawable drawable) {
         if (drawable == null) {
             mSlash = null;
             super.setImageDrawable(null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 5552105..754f8e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -320,6 +320,7 @@
         // We probably won't ever have space in the UI for more than 20 devices, so don't
         // get info for them.
         private static final int MAX_DEVICES = 20;
+        @Nullable
         private QSDetailItems mItems;
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e5601f2..698a253 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -257,7 +257,9 @@
 
     private static final class CallbackInfo {
         boolean airplaneModeEnabled;
+        @Nullable
         CharSequence dataSubscriptionName;
+        @Nullable
         CharSequence dataContentDescription;
         boolean activityIn;
         boolean activityOut;
@@ -320,6 +322,7 @@
             return mContext.getString(R.string.quick_settings_cellular_detail_title);
         }
 
+        @Nullable
         @Override
         public Boolean getToggleState() {
             return mDataController.isMobileDataSupported()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
new file mode 100644
index 0000000..6dfcf5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.service.quicksettings.Tile;
+import android.view.View;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.R.drawable;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Color correction **/
+public class ColorCorrectionTile extends QSTileImpl<BooleanState> {
+
+    private final Icon mIcon = ResourceIcon.get(drawable.ic_qs_color_correction);
+    private final SettingObserver mSetting;
+
+    @Inject
+    public ColorCorrectionTile(
+            QSHost host,
+            @Background Looper backgroundLooper,
+            @Main Handler mainHandler,
+            FalsingManager falsingManager,
+            MetricsLogger metricsLogger,
+            StatusBarStateController statusBarStateController,
+            ActivityStarter activityStarter,
+            QSLogger qsLogger,
+            UserTracker userTracker,
+            SecureSettings secureSettings
+    ) {
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
+
+        mSetting = new SettingObserver(secureSettings, mHandler,
+                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, userTracker.getUserId()) {
+            @Override
+            protected void handleValueChanged(int value, boolean observedChange) {
+                // mHandler is the background handler so calling this is OK
+                handleRefreshState(value);
+            }
+        };
+    }
+
+    @Override
+    protected void handleDestroy() {
+        super.handleDestroy();
+        mSetting.setListening(false);
+    }
+
+    @Override
+    public BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
+        mSetting.setListening(listening);
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+        mSetting.setUserId(newUserId);
+        handleRefreshState(mSetting.getValue());
+    }
+
+    @Override
+    public Intent getLongClickIntent() {
+        return new Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS);
+    }
+
+    @Override
+    protected void handleClick(@Nullable View view) {
+        mSetting.setValue(mState.value ? 0 : 1);
+    }
+
+    @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_color_correction_label);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
+        final boolean enabled = value != 0;
+        state.value = enabled;
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+        state.label = mContext.getString(R.string.quick_settings_color_correction_label);
+        state.icon = mIcon;
+        state.expandedAccessibilityClassName = Switch.class.getName();
+        state.contentDescription = state.label;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        // MetricsProto/MetricsEvent is deprecated, so just simply return 0 here.
+        return 0;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 49c548da..a06dc8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -360,6 +360,7 @@
 
     private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
 
+        @Nullable
         private ZenModePanel mZenPanel;
         private boolean mAuto;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index cd81b4a..9df942d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -145,13 +145,15 @@
                         && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM);
     }
 
-    private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
+    @Nullable
+    private CharSequence getSecondaryLabel(boolean isTransient, @Nullable String statusLabel) {
         return isTransient
                 ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
                 : statusLabel;
     }
 
-    private static String removeDoubleQuotes(String string) {
+    @Nullable
+    private static String removeDoubleQuotes(@Nullable String string) {
         if (string == null) return null;
         final int length = string.length();
         if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
@@ -163,6 +165,7 @@
     private static final class EthernetCallbackInfo {
         boolean mConnected;
         int mEthernetSignalIconId;
+        @Nullable
         String mEthernetContentDescription;
 
         @Override
@@ -180,11 +183,14 @@
         boolean mEnabled;
         boolean mConnected;
         int mWifiSignalIconId;
+        @Nullable
         String mSsid;
         boolean mActivityIn;
         boolean mActivityOut;
+        @Nullable
         String mWifiSignalContentDescription;
         boolean mIsTransient;
+        @Nullable
         public String mStatusLabel;
         boolean mNoDefaultNetwork;
         boolean mNoValidatedNetwork;
@@ -211,7 +217,9 @@
 
     private static final class CellularCallbackInfo {
         boolean mAirplaneModeEnabled;
+        @Nullable
         CharSequence mDataSubscriptionName;
+        @Nullable
         CharSequence mDataContentDescription;
         int mMobileSignalIconId;
         int mQsTypeIcon;
@@ -540,7 +548,8 @@
         }
     }
 
-    private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+    private CharSequence appendMobileDataType(
+            @Nullable CharSequence current, @Nullable CharSequence dataType) {
         if (TextUtils.isEmpty(dataType)) {
             return Html.fromHtml((current == null ? "" : current.toString()), 0);
         }
@@ -551,6 +560,7 @@
         return Html.fromHtml(concat, 0);
     }
 
+    @Nullable
     private CharSequence getMobileDataContentName(CellularCallbackInfo cb) {
         if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) {
             String roaming = mContext.getString(R.string.data_connection_roaming);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 0886b46..a61f0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -54,6 +54,7 @@
     private static final String NFC = "nfc";
     private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
 
+    @Nullable
     private NfcAdapter mAdapter;
     private BroadcastDispatcher mBroadcastDispatcher;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 9996ecd..b658025 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -131,6 +131,7 @@
         return mQRCodeScannerController.isCameraAvailable();
     }
 
+    @Nullable
     @Override
     public Intent getLongClickIntent() {
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index d9919bd..247f02b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -72,8 +72,10 @@
     private final SecureSettings mSecureSettings;
     private final QuickAccessWalletController mController;
 
+    @Nullable
     private WalletCard mSelectedCard;
     private boolean mIsWalletUpdating = true;
+    @Nullable
     @VisibleForTesting Drawable mCardViewDrawable;
 
     @Inject
@@ -200,6 +202,7 @@
                 && mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT) != null;
     }
 
+    @Nullable
     @Override
     public Intent getLongClickIntent() {
         return null;
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 8ff75cb..45e43ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -136,6 +136,7 @@
         return 0;
     }
 
+    @Nullable
     @Override
     public Intent getLongClickIntent() {
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index b0a1b18..0be0619 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -134,6 +134,7 @@
         return new Intent(Settings.ACTION_PRIVACY_SETTINGS);
     }
 
+    @Nullable
     @Override
     public DetailAdapter getDetailAdapter() {
         return super.getDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 596d8f0..e2964ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -129,17 +129,27 @@
                     ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
                     : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
         } else if (uiMode == UiModeManager.MODE_NIGHT_CUSTOM) {
-            final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat(mContext);
-            final LocalTime time;
-            if (nightMode) {
-                time = mUiModeManager.getCustomNightModeEnd();
+            int nightModeCustomType = mUiModeManager.getNightModeCustomType();
+            if (nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) {
+                final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat(
+                        mContext);
+                final LocalTime time;
+                if (nightMode) {
+                    time = mUiModeManager.getCustomNightModeEnd();
+                } else {
+                    time = mUiModeManager.getCustomNightModeStart();
+                }
+                state.secondaryLabel = mContext.getResources().getString(nightMode
+                                ? R.string.quick_settings_dark_mode_secondary_label_until
+                                : R.string.quick_settings_dark_mode_secondary_label_on_at,
+                        use24HourFormat ? time.toString() : formatter.format(time));
+            } else if (nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+                state.secondaryLabel = mContext.getResources().getString(nightMode
+                        ? R.string.quick_settings_dark_mode_secondary_label_until_bedtime_ends
+                        : R.string.quick_settings_dark_mode_secondary_label_on_at_bedtime);
             } else {
-                time = mUiModeManager.getCustomNightModeStart();
+                state.secondaryLabel = null;
             }
-            state.secondaryLabel = mContext.getResources().getString(nightMode
-                    ? R.string.quick_settings_dark_mode_secondary_label_until
-                    : R.string.quick_settings_dark_mode_secondary_label_on_at,
-                    use24HourFormat ? time.toString() : formatter.format(time));
         } else {
             state.secondaryLabel = null;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 6ca550c..076ef35 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -28,6 +28,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.util.ArrayUtils;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
@@ -50,15 +52,15 @@
         this(context, null);
     }
 
-    public UserDetailItemView(Context context, AttributeSet attrs) {
+    public UserDetailItemView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public UserDetailItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
+    public UserDetailItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index f793a50..ce6aaae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -77,6 +77,7 @@
 
         private final Context mContext;
         protected UserSwitcherController mController;
+        @Nullable
         private View mCurrentUserView;
         private final UiEventLogger mUiEventLogger;
         private final FalsingManager mFalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index e110a64..db1b6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -48,6 +48,7 @@
 
     private final UserSwitcherController mUserSwitcherController;
     private final UserInfoController mUserInfoController;
+    @Nullable
     private Pair<String, Drawable> mLastUpdate;
 
     @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 1608248..c82ff34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -258,6 +258,7 @@
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
     }
 
+    @Nullable
     private static String removeDoubleQuotes(String string) {
         if (string == null) return null;
         final int length = string.length();
@@ -271,11 +272,14 @@
         boolean enabled;
         boolean connected;
         int wifiSignalIconId;
+        @Nullable
         String ssid;
         boolean activityIn;
         boolean activityOut;
+        @Nullable
         String wifiSignalContentDescription;
         boolean isTransient;
+        @Nullable
         public String statusLabel;
 
         @Override
@@ -321,7 +325,9 @@
     protected class WifiDetailAdapter implements DetailAdapter,
             AccessPointController.AccessPointCallback, QSDetailItems.Callback {
 
+        @Nullable
         private QSDetailItems mItems;
+        @Nullable
         private WifiEntry[] mAccessPoints;
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 544246e..4fe155c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -52,6 +52,7 @@
     private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
 
     private final InternetDialogController mInternetDialogController;
+    @Nullable
     private List<WifiEntry> mWifiEntries;
     @VisibleForTesting
     protected int mWifiEntriesCount;
@@ -189,6 +190,7 @@
             mWifiSummaryText.setText(summary);
         }
 
+        @Nullable
         Drawable getWifiDrawable(int level, boolean hasNoInternet) {
             // If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
             // will be returned.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index e7982bf..8e01942 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -42,7 +42,6 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Button;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ProgressBar;
@@ -98,6 +97,7 @@
     private InternetDialogFactory mInternetDialogFactory;
     private SubscriptionManager mSubscriptionManager;
     private TelephonyManager mTelephonyManager;
+    @Nullable
     private AlertDialog mAlertDialog;
     private UiEventLogger mUiEventLogger;
     private Context mContext;
@@ -130,12 +130,14 @@
     private Button mDoneButton;
     private Button mAirplaneModeButton;
     private Drawable mBackgroundOn;
+    @Nullable
     private Drawable mBackgroundOff = null;
     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private boolean mCanConfigMobileData;
 
     // Wi-Fi entries
     private int mWifiNetworkHeight;
+    @Nullable
     @VisibleForTesting
     protected WifiEntry mConnectedWifiEntry;
     @VisibleForTesting
@@ -536,6 +538,7 @@
         return mInternetDialogController.getDialogTitleText();
     }
 
+    @Nullable
     CharSequence getSubtitleText() {
         return mInternetDialogController.getSubtitleText(
                 mIsProgressBarVisible && !mIsSearchingHidden);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 3189d2f9..f89b7a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -303,6 +303,7 @@
         return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 
+    @Nullable
     protected Intent getWifiDetailsSettingsIntent(String key) {
         if (TextUtils.isEmpty(key)) {
             if (DEBUG) {
@@ -320,6 +321,7 @@
         return mContext.getText(R.string.quick_settings_internet_label);
     }
 
+    @Nullable
     CharSequence getSubtitleText(boolean isProgressBarVisible) {
         if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
             // When Wi-Fi is disabled.
@@ -391,6 +393,7 @@
         return null;
     }
 
+    @Nullable
     Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
         if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
             return null;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/FloatingWindowUtil.java b/packages/SystemUI/src/com/android/systemui/screenshot/FloatingWindowUtil.java
new file mode 100644
index 0000000..3dec387
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/FloatingWindowUtil.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.util.DisplayMetrics;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.policy.PhoneWindow;
+
+/**
+ * Utility methods for setting up a floating window
+ */
+public class FloatingWindowUtil {
+
+    /**
+     * Convert input dp to pixels given DisplayMetrics
+     */
+    public static float dpToPx(DisplayMetrics metrics, float dp) {
+        return dp * metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
+    }
+
+    /**
+     * Sets up window params for a floating window
+     */
+    public static WindowManager.LayoutParams getFloatingWindowParams() {
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        params.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        params.setFitInsetsTypes(0);
+        // This is needed to let touches pass through outside the touchable areas
+        params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+        return params;
+    }
+
+    /**
+     * Constructs a transparent floating window
+     */
+    public static PhoneWindow getFloatingWindow(Context context) {
+        PhoneWindow window = new PhoneWindow(context);
+        window.requestFeature(Window.FEATURE_NO_TITLE);
+        window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+        window.setBackgroundDrawableResource(android.R.color.transparent);
+        return window;
+    }
+
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
index 4a1aa16..6c01f0e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -70,19 +70,28 @@
         super.setPressed(mIsPending || pressed);
     }
 
-    void setIcon(Icon icon, boolean tint) {
+    /**
+     * Set chip icon and whether to tint with theme color
+     */
+    public void setIcon(Icon icon, boolean tint) {
         mIconView.setImageIcon(icon);
         if (!tint) {
             mIconView.setImageTintList(null);
         }
     }
 
-    void setText(CharSequence text) {
+    /**
+     * Set chip text
+     */
+    public void setText(CharSequence text) {
         mTextView.setText(text);
         updatePadding(text.length() > 0);
     }
 
-    void setPendingIntent(PendingIntent intent, Runnable finisher) {
+    /**
+     * Set PendingIntent to be sent and Runnable to be run, when chip is clicked
+     */
+    public void setPendingIntent(PendingIntent intent, Runnable finisher) {
         setOnClickListener(v -> {
             try {
                 intent.send();
@@ -93,7 +102,10 @@
         });
     }
 
-    void setIsPending(boolean isPending) {
+    /**
+     * Set pressed state of chip (to be used when chip is clicked before underlying intent is ready)
+     */
+    public void setIsPending(boolean isPending) {
         mIsPending = isPending;
         setPressed(mIsPending);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index ce571e5..83d8d19 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -18,7 +18,6 @@
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
@@ -41,23 +40,21 @@
 import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
 import android.app.ICompatCameraControlCallback;
 import android.app.Notification;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.media.MediaActionSound;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
@@ -76,7 +73,6 @@
 import android.view.View;
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
-import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -90,6 +86,7 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.R;
+import com.android.systemui.clipboardoverlay.ClipboardOverlayController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
@@ -235,13 +232,11 @@
     static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
     static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
 
-
-    private static final int MESSAGE_CORNER_TIMEOUT = 2;
-    private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
-
     // From WizardManagerHelper.java
     private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
 
+    private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
+
     private final WindowContext mContext;
     private final ScreenshotNotificationsController mNotificationsController;
     private final ScreenshotSmartActions mScreenshotSmartActions;
@@ -260,6 +255,7 @@
     private final ScrollCaptureController mScrollCaptureController;
     private final LongScreenshotData mLongScreenshotHolder;
     private final boolean mIsLowRamDevice;
+    private final TimeoutHandler mScreenshotHandler;
 
     private ScreenshotView mScreenshotView;
     private Bitmap mScreenBitmap;
@@ -270,24 +266,8 @@
     private Animator mScreenshotAnimation;
     private RequestCallback mCurrentRequestCallback;
     private String mPackageName = "";
+    private BroadcastReceiver mCopyBroadcastReceiver;
 
-    private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_CORNER_TIMEOUT:
-                    if (DEBUG_UI) {
-                        Log.d(TAG, "Corner timeout hit");
-                    }
-                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT, 0,
-                            mPackageName);
-                    ScreenshotController.this.dismissScreenshot(false);
-                    break;
-                default:
-                    break;
-            }
-        }
-    };
 
     /** Tracks config changes that require re-creating UI */
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
@@ -309,7 +289,8 @@
             @Main Executor mainExecutor,
             ScrollCaptureController scrollCaptureController,
             LongScreenshotData longScreenshotHolder,
-            ActivityManager activityManager) {
+            ActivityManager activityManager,
+            TimeoutHandler timeoutHandler) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mScrollCaptureClient = scrollCaptureClient;
@@ -321,6 +302,17 @@
         mIsLowRamDevice = activityManager.isLowRamDevice();
         mBgExecutor = Executors.newSingleThreadExecutor();
 
+        mScreenshotHandler = timeoutHandler;
+        mScreenshotHandler.setDefaultTimeoutMillis(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS);
+        mScreenshotHandler.setOnTimeoutRunnable(() -> {
+            if (DEBUG_UI) {
+                Log.d(TAG, "Corner timeout hit");
+            }
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT, 0,
+                    mPackageName);
+            ScreenshotController.this.dismissScreenshot(false);
+        });
+
         mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
         mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
@@ -329,27 +321,11 @@
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
 
         // Setup the window that we are going to use
-        mWindowLayoutParams = new WindowManager.LayoutParams(
-                MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT,
-                WindowManager.LayoutParams.FLAG_FULLSCREEN
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                PixelFormat.TRANSLUCENT);
+        mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
-        mWindowLayoutParams.layoutInDisplayCutoutMode =
-                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mWindowLayoutParams.setFitInsetsTypes(0);
-        // This is needed to let touches pass through outside the touchable areas
-        mWindowLayoutParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
-        mWindow = new PhoneWindow(mContext);
+        mWindow = FloatingWindowUtil.getFloatingWindow(mContext);
         mWindow.setWindowManager(mWindowManager, null, null);
-        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-        mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
-        mWindow.setBackgroundDrawableResource(android.R.color.transparent);
 
         mConfigChanges.applyNewConfig(context.getResources());
         reloadAssets();
@@ -357,6 +333,18 @@
         // Setup the Camera shutter sound
         mCameraSound = new MediaActionSound();
         mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+
+        mCopyBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (ClipboardOverlayController.COPY_OVERLAY_ACTION.equals(intent.getAction())) {
+                    dismissScreenshot(false);
+                }
+            }
+        };
+        mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter(
+                        ClipboardOverlayController.COPY_OVERLAY_ACTION),
+                ClipboardOverlayController.SELF_PERMISSION, null);
     }
 
     void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
@@ -424,7 +412,7 @@
             }
             return;
         }
-        cancelTimeout();
+        mScreenshotHandler.cancelTimeout();
         if (immediate) {
             finishDismiss();
         } else {
@@ -440,6 +428,7 @@
      * Release the constructed window context.
      */
     void releaseContext() {
+        mContext.unregisterReceiver(mCopyBroadcastReceiver);
         mContext.release();
         mCameraSound.release();
         mBgExecutor.shutdownNow();
@@ -462,7 +451,7 @@
                 if (DEBUG_INPUT) {
                     Log.d(TAG, "onUserInteraction");
                 }
-                resetTimeout();
+                mScreenshotHandler.resetTimeout();
             }
 
             @Override
@@ -517,6 +506,9 @@
         }
 
         saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true);
+
+        mContext.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
+                ClipboardOverlayController.SELF_PERMISSION);
     }
 
     private Bitmap captureScreenshot(Rect crop) {
@@ -651,7 +643,7 @@
         // ignore system bar insets for the purpose of window layout
         mWindow.getDecorView().setOnApplyWindowInsetsListener(
                 (v, insets) -> WindowInsets.CONSUMED);
-        cancelTimeout(); // restarted after animation
+        mScreenshotHandler.cancelTimeout(); // restarted after animation
     }
 
     private void requestScrollCapture() {
@@ -878,7 +870,7 @@
         }
         mScreenshotView.reset();
         removeWindow();
-        cancelTimeout();
+        mScreenshotHandler.cancelTimeout();
     }
 
     /**
@@ -905,30 +897,6 @@
         mSaveInBgTask.execute();
     }
 
-    private void cancelTimeout() {
-        if (DEBUG_DISMISS) {
-            Log.d(TAG, "cancel timeout");
-        }
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
-    }
-
-    private void resetTimeout() {
-        cancelTimeout();
-
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
-                SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
-                AccessibilityManager.FLAG_CONTENT_CONTROLS);
-
-        mScreenshotHandler.sendMessageDelayed(
-                mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
-                timeoutMs);
-        if (DEBUG_DISMISS) {
-            Log.d(TAG, "dismiss timeout: " + timeoutMs + " ms");
-        }
-
-    }
 
     /**
      * Sets up the action shade and its entrance animation, once we get the screenshot URI.
@@ -939,7 +907,7 @@
             Log.d(TAG, "Showing UI actions");
         }
 
-        resetTimeout();
+        mScreenshotHandler.resetTimeout();
 
         if (imageData.uri != null) {
             mScreenshotHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 7bcaf5f..e5649a1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -222,7 +222,6 @@
                     }
                 });
         mSwipeDetector.setIsLongpressEnabled(false);
-        mSwipeDismissHandler = new SwipeDismissHandler();
         addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -244,7 +243,7 @@
      * Called to display the scroll action chip when support is detected.
      *
      * @param packageName the owning package of the window to be captured
-     * @param onClick the action to take when the chip is clicked.
+     * @param onClick     the action to take when the chip is clicked.
      */
     public void showScrollChip(String packageName, Runnable onClick) {
         if (DEBUG_SCROLL) {
@@ -273,10 +272,12 @@
 
         final Rect tmpRect = new Rect();
         mScreenshotPreview.getBoundsOnScreen(tmpRect);
-        tmpRect.inset((int) dpToPx(-SWIPE_PADDING_DP), (int) dpToPx(-SWIPE_PADDING_DP));
+        tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
+                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
         touchRegion.op(tmpRect, Region.Op.UNION);
         mActionsContainerBackground.getBoundsOnScreen(tmpRect);
-        tmpRect.inset((int) dpToPx(-SWIPE_PADDING_DP), (int) dpToPx(-SWIPE_PADDING_DP));
+        tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
+                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
         touchRegion.op(tmpRect, Region.Op.UNION);
         mDismissButton.getBoundsOnScreen(tmpRect);
         touchRegion.op(tmpRect, Region.Op.UNION);
@@ -365,7 +366,7 @@
         mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip));
         mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip));
 
-        int swipePaddingPx = (int) dpToPx(SWIPE_PADDING_DP);
+        int swipePaddingPx = (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, SWIPE_PADDING_DP);
         TouchDelegate previewDelegate = new TouchDelegate(
                 new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx),
                 mScreenshotPreview);
@@ -390,6 +391,24 @@
         // Get focus so that the key events go to the layout.
         setFocusableInTouchMode(true);
         requestFocus();
+
+        mSwipeDismissHandler = new SwipeDismissHandler(mContext, mScreenshotStatic,
+                new SwipeDismissHandler.SwipeDismissCallbacks() {
+                    @Override
+                    public void onInteraction() {
+                        mCallbacks.onUserInteraction();
+                    }
+
+                    @Override
+                    public void onDismiss() {
+                        if (DEBUG_DISMISS) {
+                            Log.d(ScreenshotView.TAG, "dismiss triggered via swipe gesture");
+                        }
+                        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0,
+                                mPackageName);
+                        mCallbacks.onDismiss();
+                    }
+                });
     }
 
     View getScreenshotPreview() {
@@ -859,8 +878,8 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
-                        mCallbacks.onDismiss();
-                    }
+                mCallbacks.onDismiss();
+            }
         });
         animSet.start();
     }
@@ -934,38 +953,7 @@
     }
 
     void animateDismissal() {
-        animateDismissal(createScreenshotTranslateDismissAnimation());
-    }
-
-    private void animateDismissal(Animator dismissAnimation) {
-        mDismissAnimation = dismissAnimation;
-        mDismissAnimation.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled = false;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                super.onAnimationCancel(animation);
-                if (DEBUG_ANIM) {
-                    Log.d(TAG, "Cancelled dismiss animation");
-                }
-                mCancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (!mCancelled) {
-                    if (DEBUG_ANIM) {
-                        Log.d(TAG, "after dismiss animation, calling onDismissRunnable.run()");
-                    }
-                    mCallbacks.onDismiss();
-                }
-            }
-        });
-        if (DEBUG_ANIM) {
-            Log.d(TAG, "Starting dismiss animation");
-        }
-        mDismissAnimation.start();
+        mSwipeDismissHandler.dismiss();
     }
 
     void reset() {
@@ -979,6 +967,7 @@
             }
             mDismissAnimation.cancel();
         }
+        mSwipeDismissHandler.cancel();
         if (DEBUG_WINDOW) {
             Log.d(TAG, "removing OnComputeInternalInsetsListener");
         }
@@ -1042,8 +1031,8 @@
         xAnim.setInterpolator(mAccelerateInterpolator);
         xAnim.setDuration(SCREENSHOT_DISMISS_X_DURATION_MS);
         float deltaX = mDirectionLTR
-                    ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth())
-                    : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX());
+                ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth())
+                : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX());
         xAnim.addUpdateListener(animation -> {
             float currXDelta = MathUtils.lerp(0, deltaX, animation.getAnimatedFraction());
             mScreenshotStatic.setTranslationX(currXDelta);
@@ -1100,130 +1089,4 @@
             return insetDrawable;
         }
     }
-
-    private float dpToPx(float dp) {
-        return dp * mDisplayMetrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
-    }
-
-    class SwipeDismissHandler implements OnTouchListener {
-        // distance needed to register a dismissal
-        private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20;
-
-        private final GestureDetector mGestureDetector;
-
-        private float mStartX;
-        // Keeps track of the most recent direction (between the last two move events).
-        // -1 for left; +1 for right.
-        private int mDirectionX;
-        private float mPreviousX;
-
-        SwipeDismissHandler() {
-            GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
-            mGestureDetector = new GestureDetector(mContext, gestureListener);
-        }
-
-        @Override
-        public boolean onTouch(View view, MotionEvent event) {
-            boolean gestureResult = mGestureDetector.onTouchEvent(event);
-            mCallbacks.onUserInteraction();
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                mStartX = event.getRawX();
-                mPreviousX = mStartX;
-                return true;
-            } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
-                if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
-                    return true;
-                }
-                if (isPastDismissThreshold()) {
-                    if (DEBUG_DISMISS) {
-                        Log.d(TAG, "dismiss triggered via swipe gesture");
-                    }
-                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0, mPackageName);
-                    animateDismissal(createSwipeDismissAnimation());
-                } else {
-                    // if we've moved, but not past the threshold, start the return animation
-                    if (DEBUG_DISMISS) {
-                        Log.d(TAG, "swipe gesture abandoned");
-                    }
-                    createSwipeReturnAnimation().start();
-                }
-                return true;
-            }
-            return gestureResult;
-        }
-
-        class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
-            @Override
-            public boolean onScroll(
-                    MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
-                mScreenshotStatic.setTranslationX(ev2.getRawX() - mStartX);
-                mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1;
-                mPreviousX = ev2.getRawX();
-                return true;
-            }
-
-            @Override
-            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
-                    float velocityY) {
-                if (mScreenshotStatic.getTranslationX() * velocityX > 0
-                        && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
-                    animateDismissal(createSwipeDismissAnimation(velocityX / (float) 1000));
-                    return true;
-                }
-                return false;
-            }
-        }
-
-        private boolean isPastDismissThreshold() {
-            float translationX = mScreenshotStatic.getTranslationX();
-            // Determines whether the absolute translation from the start is in the same direction
-            // as the current movement. For example, if the user moves most of the way to the right,
-            // but then starts dragging back left, we do not dismiss even though the absolute
-            // distance is greater than the threshold.
-            if (translationX * mDirectionX > 0) {
-                return Math.abs(translationX) >= dpToPx(DISMISS_DISTANCE_THRESHOLD_DP);
-            }
-            return false;
-        }
-
-        private ValueAnimator createSwipeDismissAnimation() {
-            return createSwipeDismissAnimation(1);
-        }
-
-        private ValueAnimator createSwipeDismissAnimation(float velocity) {
-            // velocity is measured in pixels per millisecond
-            velocity = Math.min(3, Math.max(1, velocity));
-            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-            float startX = mScreenshotStatic.getTranslationX();
-            // make sure the UI gets all the way off the screen in the direction of movement
-            // (the actions container background is guaranteed to be both the leftmost and
-            // rightmost UI element in LTR and RTL)
-            float finalX = startX < 0
-                    ? -1 * mActionsContainerBackground.getRight()
-                    : mDisplayMetrics.widthPixels;
-            float distance = Math.abs(finalX - startX);
-
-            anim.addUpdateListener(animation -> {
-                float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
-                mScreenshotStatic.setTranslationX(translation);
-                setAlpha(1 - animation.getAnimatedFraction());
-            });
-            anim.setDuration((long) (distance / Math.abs(velocity)));
-            return anim;
-        }
-
-        private ValueAnimator createSwipeReturnAnimation() {
-            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-            float startX = mScreenshotStatic.getTranslationX();
-            float finalX = 0;
-
-            anim.addUpdateListener(animation -> {
-                float translation = MathUtils.lerp(
-                        startX, finalX, animation.getAnimatedFraction());
-                mScreenshotStatic.setTranslationX(translation);
-            });
-
-            return anim;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
new file mode 100644
index 0000000..4e96003
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Allows a view to be swipe-dismissed, or returned to its location if distance threshold is not met
+ */
+public class SwipeDismissHandler implements View.OnTouchListener {
+    private static final String TAG = "SwipeDismissHandler";
+
+    // distance needed to register a dismissal
+    private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20;
+
+    /**
+     * Stores the callbacks when the view is interacted with or dismissed.
+     */
+    public interface SwipeDismissCallbacks {
+        /**
+         * Run when the view is interacted with (touched)
+         */
+        void onInteraction();
+
+        /**
+         * Run when the view is dismissed (the distance threshold is met), post-dismissal animation
+         */
+        void onDismiss();
+    }
+
+    private final View mView;
+    private final SwipeDismissCallbacks mCallbacks;
+    private final GestureDetector mGestureDetector;
+    private DisplayMetrics mDisplayMetrics;
+    private ValueAnimator mDismissAnimation;
+
+
+    private float mStartX;
+    // Keeps track of the most recent direction (between the last two move events).
+    // -1 for left; +1 for right.
+    private int mDirectionX;
+    private float mPreviousX;
+
+    public SwipeDismissHandler(Context context, View view, SwipeDismissCallbacks callbacks) {
+        mView = view;
+        mCallbacks = callbacks;
+        GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
+        mGestureDetector = new GestureDetector(context, gestureListener);
+        mDisplayMetrics = new DisplayMetrics();
+        context.getDisplay().getRealMetrics(mDisplayMetrics);
+    }
+
+    @Override
+    public boolean onTouch(View view, MotionEvent event) {
+        boolean gestureResult = mGestureDetector.onTouchEvent(event);
+        mCallbacks.onInteraction();
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mStartX = event.getRawX();
+            mPreviousX = mStartX;
+            return true;
+        } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+                return true;
+            }
+            if (isPastDismissThreshold()) {
+                dismiss();
+            } else {
+                // if we've moved, but not past the threshold, start the return animation
+                if (DEBUG_DISMISS) {
+                    Log.d(TAG, "swipe gesture abandoned");
+                }
+                createSwipeReturnAnimation().start();
+            }
+            return true;
+        }
+        return gestureResult;
+    }
+
+    class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
+        @Override
+        public boolean onScroll(
+                MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+            mView.setTranslationX(ev2.getRawX() - mStartX);
+            mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1;
+            mPreviousX = ev2.getRawX();
+            return true;
+        }
+
+        @Override
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                float velocityY) {
+            if (mView.getTranslationX() * velocityX > 0
+                    && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
+                dismiss(velocityX / (float) 1000);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private boolean isPastDismissThreshold() {
+        float translationX = mView.getTranslationX();
+        // Determines whether the absolute translation from the start is in the same direction
+        // as the current movement. For example, if the user moves most of the way to the right,
+        // but then starts dragging back left, we do not dismiss even though the absolute
+        // distance is greater than the threshold.
+        if (translationX * mDirectionX > 0) {
+            return Math.abs(translationX) >= FloatingWindowUtil.dpToPx(mDisplayMetrics,
+                    DISMISS_DISTANCE_THRESHOLD_DP);
+        }
+        return false;
+    }
+
+    /**
+     * Cancel the currently-running dismissal animation, if any.
+     */
+    public void cancel() {
+        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+            mDismissAnimation.cancel();
+        }
+    }
+
+    /**
+     * Start dismissal animation (will run onDismiss callback when animation complete)
+     */
+    public void dismiss() {
+        dismiss(1);
+    }
+
+    private void dismiss(float velocity) {
+        mDismissAnimation = createSwipeDismissAnimation(velocity);
+        mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+            private boolean mCancelled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                mCancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (!mCancelled) {
+                    mCallbacks.onDismiss();
+                }
+            }
+        });
+        mDismissAnimation.start();
+    }
+
+    private ValueAnimator createSwipeDismissAnimation(float velocity) {
+        // velocity is measured in pixels per millisecond
+        velocity = Math.min(3, Math.max(1, velocity));
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        float startX = mView.getTranslationX();
+        // make sure the UI gets all the way off the screen in the direction of movement
+        // (the actions container background is guaranteed to be both the leftmost and
+        // rightmost UI element in LTR and RTL)
+        float finalX = startX <= 0 ? -1 * mView.getRight() : mDisplayMetrics.widthPixels;
+        float distance = Math.abs(finalX - startX);
+
+        anim.addUpdateListener(animation -> {
+            float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
+            mView.setTranslationX(translation);
+            mView.setAlpha(1 - animation.getAnimatedFraction());
+        });
+        anim.setDuration((long) (distance / Math.abs(velocity)));
+        return anim;
+    }
+
+    private ValueAnimator createSwipeReturnAnimation() {
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        float startX = mView.getTranslationX();
+        float finalX = 0;
+
+        anim.addUpdateListener(animation -> {
+            float translation = MathUtils.lerp(
+                    startX, finalX, animation.getAnimatedFraction());
+            mView.setTranslationX(translation);
+        });
+
+        return anim;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f380911..98e6bd1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -83,7 +83,7 @@
 
     /** Informs about coarse grained state of the Controller. */
     interface RequestCallback {
-        /** Respond to the current request indicating the screenshot request failed.*/
+        /** Respond to the current request indicating the screenshot request failed. */
         void reportError();
 
         /** The controller has completed handling this request UI has been removed */
@@ -113,7 +113,8 @@
 
     @Override
     public IBinder onBind(@NonNull Intent intent) {
-        registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
+        registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS),
+                Context.RECEIVER_EXPORTED);
         final Messenger m = new Messenger(mHandler);
         if (DEBUG_SERVICE) {
             Log.d(TAG, "onBind: returning connection: " + m);
@@ -229,7 +230,7 @@
                 return false;
         }
         return true;
-    };
+    }
 
     private static void sendComplete(Messenger target) {
         try {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java
new file mode 100644
index 0000000..9156601
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
+
+import javax.inject.Inject;
+
+/**
+ * Starts a configurable runnable on timeout. Can be cancelled. Used for automatically dismissing
+ * floating overlays.
+ */
+public class TimeoutHandler extends Handler {
+    private static final String TAG = "TimeoutHandler";
+
+    private static final int MESSAGE_CORNER_TIMEOUT = 2;
+    private static final int DEFAULT_TIMEOUT_MILLIS = 6000;
+
+    private final Context mContext;
+
+    private Runnable mOnTimeout;
+    private int mDefaultTimeout = DEFAULT_TIMEOUT_MILLIS;
+
+    @Inject
+    public TimeoutHandler(Context context) {
+        super(Looper.getMainLooper());
+        mContext = context;
+        mOnTimeout = () -> {
+        };
+    }
+
+    public void setOnTimeoutRunnable(Runnable onTimeout) {
+        mOnTimeout = onTimeout;
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MESSAGE_CORNER_TIMEOUT:
+                mOnTimeout.run();
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Set the default timeout (if not overridden by accessibility)
+     */
+    public void setDefaultTimeoutMillis(int timeout) {
+        mDefaultTimeout = timeout;
+    }
+
+    /**
+     * Cancel the current timeout, if any. To reset the delayed runnable use resetTimeout instead.
+     */
+    public void cancelTimeout() {
+        if (DEBUG_DISMISS) {
+            Log.d(TAG, "cancel timeout");
+        }
+        removeMessages(MESSAGE_CORNER_TIMEOUT);
+    }
+
+    /**
+     * Reset the timeout.
+     */
+    public void resetTimeout() {
+        cancelTimeout();
+
+        AccessibilityManager accessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
+                mDefaultTimeout,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
+
+        sendMessageDelayed(obtainMessage(MESSAGE_CORNER_TIMEOUT), timeoutMs);
+        if (DEBUG_DISMISS) {
+            Log.d(TAG, "dismiss timeout: " + timeoutMs + " ms");
+        }
+    }
+}
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 c9c1a9b..a22fda7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -93,6 +93,12 @@
     }
 
     @Override
+    protected void onPause() {
+        super.onPause();
+        overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+    }
+
+    @Override
     protected void onStop() {
         super.onStop();
         MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 43b3fb1..2a21f42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -238,7 +238,7 @@
      * @param key the key to check if removable
      * @return true if the alert entry can be removed
      */
-    protected boolean canRemoveImmediately(String key) {
+    public boolean canRemoveImmediately(String key) {
         AlertEntry alertEntry = mAlertEntries.get(key);
         return alertEntry == null || alertEntry.wasShownLongEnough()
                 || alertEntry.mEntry.isRowDismissed();
@@ -257,6 +257,30 @@
         return !canRemoveImmediately(entry.getKey());
     }
 
+    /**
+     * @param key
+     * @return true if the entry is pinned
+     */
+    public boolean isSticky(String key) {
+        AlertEntry alerting = mAlertEntries.get(key);
+        if (alerting != null) {
+            return alerting.isSticky();
+        }
+        return false;
+    }
+
+    /**
+     * @param key
+     * @return When a HUN entry should be removed in milliseconds from now
+     */
+    public long getEarliestRemovalTime(String key) {
+        AlertEntry alerting = mAlertEntries.get(key);
+        if (alerting != null) {
+            return Math.max(0, alerting.mEarliestRemovaltime - mClock.currentTimeMillis());
+        }
+        return 0;
+    }
+
     @Override
     public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3cecbb7..597e424 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -348,11 +348,19 @@
                 String packageName) { }
 
         /**
-         * @see IStatusBar#showTransient(int, int[]).
+         * @see IStatusBar#showTransient(int, int[], boolean).
          */
         default void showTransient(int displayId, @InternalInsetsType int[] types) { }
 
         /**
+         * @see IStatusBar#showTransient(int, int[], boolean).
+         */
+        default void showTransient(int displayId, @InternalInsetsType int[] types,
+                boolean isGestureOnSystemBar) {
+            showTransient(displayId, types);
+        }
+
+        /**
          * @see IStatusBar#abortTransient(int, int[]).
          */
         default void abortTransient(int displayId, @InternalInsetsType int[] types) { }
@@ -1038,9 +1046,10 @@
     }
 
     @Override
-    public void showTransient(int displayId, int[] types) {
+    public void showTransient(int displayId, int[] types, boolean isGestureOnSystemBar) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, 0, types).sendToTarget();
+            mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, isGestureOnSystemBar ? 1 : 0,
+                    types).sendToTarget();
         }
     }
 
@@ -1444,8 +1453,9 @@
                 case MSG_SHOW_TRANSIENT: {
                     final int displayId = msg.arg1;
                     final int[] types = (int[]) msg.obj;
+                    final boolean isGestureOnSystemBar = msg.arg2 != 0;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).showTransient(displayId, types);
+                        mCallbacks.get(i).showTransient(displayId, types, isGestureOnSystemBar);
                     }
                     break;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
index 4272bb1..66591b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
@@ -74,9 +74,12 @@
      * Returns a string representing the, old, new, and new-after-modification disable flag states,
      * as well as the differences between each of the states.
      *
-     * Example:
-     *   Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification:
-     *   EnaihBcRso.qInGR (.n)
+     * Example if [old], [new], and [newAfterLocalModification] are all different:
+     *   Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (changed: hB.iGR) | New after local
+     *   modification: EnaihBcRso.qInGR (changed: .n)
+     *
+     * Example if [old] and [new] are the same:
+     *   EnaihBcRso.qiNGR (unchanged)
      *
      * A capital character signifies the flag is set and a lowercase character signifies that the
      * flag isn't set. The flag states will be logged in the same order as the passed-in lists.
@@ -96,54 +99,51 @@
         new: DisableState,
         newAfterLocalModification: DisableState? = null
     ): String {
-        val builder = StringBuilder("Received new disable state. ")
+        val builder = StringBuilder("Received new disable state: ")
 
-        old?.let {
+        // This if/else has slightly repetitive code but is easier to read.
+        if (old != null && old != new) {
             builder.append("Old: ")
             builder.append(getFlagsString(old))
             builder.append(" | ")
-        }
-
-        builder.append("New: ")
-        if (old != null && old != new) {
-            builder.append(getFlagsStringWithDiff(old, new))
-        } else {
+            builder.append("New: ")
             builder.append(getFlagsString(new))
+            builder.append(" ")
+            builder.append(getDiffString(old, new))
+        } else if (old != null && old == new) {
+            // If old and new are the same, we only need to print one of them.
+            builder.append(getFlagsString(new))
+            builder.append(" ")
+            builder.append(getDiffString(old, new))
+        } else { // old == null
+            builder.append(getFlagsString(new))
+            // Don't get a diff string because we have no [old] to compare with.
         }
 
         if (newAfterLocalModification != null && new != newAfterLocalModification) {
             builder.append(" | New after local modification: ")
-            builder.append(getFlagsStringWithDiff(new, newAfterLocalModification))
+            builder.append(getFlagsString(newAfterLocalModification))
+            builder.append(" ")
+            builder.append(getDiffString(new, newAfterLocalModification))
         }
 
         return builder.toString()
     }
 
     /**
-     * Returns a string representing [new] state, as well as the difference from [old] to [new]
-     * (if there is one).
-     */
-    private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String {
-        val builder = StringBuilder()
-        builder.append(getFlagsString(new))
-        builder.append(" ")
-        builder.append(getDiffString(old, new))
-        return builder.toString()
-    }
-
-    /**
-     * Returns a string representing the difference between [old] and [new], or an empty string if
-     * there is no difference.
+     * Returns a string representing the difference between [old] and [new].
      *
-     * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be
-     * "(BC.e)".
+     * - If [old] was "abc.DE" and [new] was "aBC.De", the difference returned would be
+     *   "(changed: BC.e)".
+     * - If [old] and [new] are the same, the difference returned would be "(unchanged)".
      */
     private fun getDiffString(old: DisableState, new: DisableState): String {
         if (old == new) {
-            return ""
+            return "(unchanged)"
         }
 
         val builder = StringBuilder("(")
+        builder.append("changed: ")
         disable1FlagsList.forEach {
             val newSymbol = it.getFlagStatus(new.disable1)
             if (it.getFlagStatus(old.disable1) != newSymbol) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8ce73d7..963a0d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -34,8 +34,6 @@
 import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
 import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -72,7 +70,6 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -133,7 +130,7 @@
     private final DockManager mDockManager;
     private final DevicePolicyManager mDevicePolicyManager;
     private final UserManager mUserManager;
-    private final @Main DelayableExecutor mExecutor;
+    protected final @Main DelayableExecutor mExecutor;
     private final LockPatternUtils mLockPatternUtils;
     private final IActivityManager mIActivityManager;
     private final FalsingManager mFalsingManager;
@@ -149,6 +146,7 @@
     private CharSequence mBiometricMessage;
     protected ColorStateList mInitialTextColorState;
     private boolean mVisible;
+    private boolean mOrganizationOwnedDevice;
 
     private boolean mPowerPluggedIn;
     private boolean mPowerPluggedInWired;
@@ -256,13 +254,13 @@
             mExecutor,
             mStatusBarStateController);
         updateIndication(false /* animate */);
-        updateDisclosure();
+        updateOrganizedOwnedDevice();
         if (mBroadcastReceiver == null) {
             // Update the disclosure proactively to avoid IPC on the critical path.
             mBroadcastReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    updateDisclosure();
+                    updateOrganizedOwnedDevice();
                 }
             };
             IntentFilter intentFilter = new IntentFilter();
@@ -305,12 +303,11 @@
     }
 
     /**
-     * Doesn't include disclosure (also a persistent indication) which gets triggered separately.
-     *
      * This method also doesn't update transient messages like biometrics since those messages
      * are also updated separately.
      */
     private void updatePersistentIndications(boolean animate, int userId) {
+        updateDisclosure();
         updateOwnerInfo();
         updateBattery(animate);
         updateUserLocked(userId);
@@ -320,9 +317,14 @@
         updateResting();
     }
 
-    private void updateDisclosure() {
+    private void updateOrganizedOwnedDevice() {
         // avoid calling this method since it has an IPC
-        if (whitelistIpcs(this::isOrganizationOwnedDevice)) {
+        mOrganizationOwnedDevice = whitelistIpcs(this::isOrganizationOwnedDevice);
+        updatePersistentIndications(false, KeyguardUpdateMonitor.getCurrentUser());
+    }
+
+    private void updateDisclosure() {
+        if (mOrganizationOwnedDevice) {
             final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
             final CharSequence disclosure = getDisclosureText(organizationName);
             mRotateTextViewController.updateIndication(
@@ -335,8 +337,6 @@
         } else {
             mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE);
         }
-
-        updateResting();
     }
 
     private CharSequence getDisclosureText(@Nullable CharSequence organizationName) {
@@ -753,60 +753,6 @@
         updatePersistentIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
     }
 
-    // animates textView - textView moves up and bounces down
-    private void animateText(KeyguardIndicationTextView textView, String indication) {
-        int yTranslation = mContext.getResources().getInteger(
-                R.integer.wired_charging_keyguard_text_animation_distance);
-        int animateUpDuration = mContext.getResources().getInteger(
-                R.integer.wired_charging_keyguard_text_animation_duration_up);
-        int animateDownDuration = mContext.getResources().getInteger(
-                R.integer.wired_charging_keyguard_text_animation_duration_down);
-        textView.animate().cancel();
-        ViewClippingUtil.setClippingDeactivated(textView, true, mClippingParams);
-        textView.animate()
-                .translationYBy(yTranslation)
-                .setInterpolator(Interpolators.LINEAR)
-                .setDuration(animateUpDuration)
-                .setListener(new AnimatorListenerAdapter() {
-                    private boolean mCancelled;
-
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        textView.switchIndication(indication, null);
-                    }
-
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
-                        mCancelled = true;
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        if (mCancelled) {
-                            ViewClippingUtil.setClippingDeactivated(textView, false,
-                                    mClippingParams);
-                            return;
-                        }
-                        textView.animate()
-                                .setDuration(animateDownDuration)
-                                .setInterpolator(Interpolators.BOUNCE)
-                                .translationY(BOUNCE_ANIMATION_FINAL_Y)
-                                .setListener(new AnimatorListenerAdapter() {
-                                    @Override
-                                    public void onAnimationEnd(Animator animation) {
-                                        textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
-                                        ViewClippingUtil.setClippingDeactivated(textView, false,
-                                                mClippingParams);
-                                        // Unset the listener, otherwise this may persist for
-                                        // another view property animation
-                                        textView.animate().setListener(null);
-                                    }
-                                });
-                    }
-                });
-    }
-
     protected String computePowerIndication() {
         int chargingId;
         if (mBatteryOverheated) {
@@ -1182,9 +1128,12 @@
 
         @Override
         public void onKeyguardShowingChanged() {
+            // All transient messages are gone the next time keyguard is shown
             if (!mKeyguardStateController.isShowing()) {
                 mTopIndicationView.clearMessages();
                 mRotateTextViewController.clearMessages();
+            } else {
+                updatePersistentIndications(false, KeyguardUpdateMonitor.getCurrentUser());
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index c8115e2..9a932ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -355,7 +355,8 @@
     }
 
     override fun onDraw(canvas: Canvas?) {
-        if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0) {
+        if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0
+            || revealAmount == 0f) {
             if (revealAmount < 1f) {
                 canvas?.drawColor(revealGradientEndColor)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 491a175..c136d9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -7,13 +7,13 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.os.SystemClock
-import android.util.DisplayMetrics
+import android.util.IndentingPrintWriter
 import android.util.MathUtils
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewConfiguration
 import androidx.annotation.VisibleForTesting
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.Dumpable
 import com.android.systemui.ExpandHelper
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.R
@@ -22,23 +22,27 @@
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.media.MediaHierarchyManager
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.phone.NotificationPanelViewController
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.phone.StatusBar
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.Utils
+import java.io.FileDescriptor
+import java.io.PrintWriter
 import javax.inject.Inject
 
 private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375L
@@ -51,19 +55,20 @@
 @SysUISingleton
 class LockscreenShadeTransitionController @Inject constructor(
     private val statusBarStateController: SysuiStatusBarStateController,
-    private val lockscreenGestureLogger: LockscreenGestureLogger,
+    private val logger: LSShadeTransitionLogger,
     private val keyguardBypassController: KeyguardBypassController,
     private val lockScreenUserManager: NotificationLockscreenUserManager,
     private val falsingCollector: FalsingCollector,
     private val ambientState: AmbientState,
-    private val displayMetrics: DisplayMetrics,
     private val mediaHierarchyManager: MediaHierarchyManager,
     private val scrimController: ScrimController,
     private val depthController: NotificationShadeDepthController,
     private val context: Context,
+    wakefulnessLifecycle: WakefulnessLifecycle,
     configurationController: ConfigurationController,
-    falsingManager: FalsingManager
-) {
+    falsingManager: FalsingManager,
+    dumpManager: DumpManager,
+) : Dumpable {
     private var pulseHeight: Float = 0f
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
@@ -116,6 +121,12 @@
     private var nextHideKeyguardNeedsNoAnimation = false
 
     /**
+     * Are we currently waking up to the shade locked
+     */
+    var isWakingToShadeLocked: Boolean = false
+        private set
+
+    /**
      * The distance until we're showing the notifications when pulsing
      */
     val distanceUntilShowingPulsingNotifications
@@ -139,6 +150,30 @@
                 touchHelper.updateResources(context)
             }
         })
+        dumpManager.registerDumpable(this)
+        statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
+            override fun onExpandedChanged(isExpanded: Boolean) {
+                // safeguard: When the panel is fully collapsed, let's make sure to reset.
+                // See b/198098523
+                if (!isExpanded) {
+                    if (dragDownAmount != 0f && dragDownAnimator?.isRunning != true) {
+                        logger.logDragDownAmountResetWhenFullyCollapsed()
+                        dragDownAmount = 0f
+                    }
+                    if (pulseHeight != 0f && pulseHeightAnimator?.isRunning != true) {
+                        logger.logPulseHeightNotResetWhenFullyCollapsed()
+                        setPulseHeight(0f, animate = false)
+                    }
+                }
+            }
+        })
+        wakefulnessLifecycle.addObserver(object : WakefulnessLifecycle.Observer {
+            override fun onPostFinishedWakingUp() {
+                // when finishing waking up, the UnlockedScreenOffAnimation has another attempt
+                // to reset keyguard. Let's do it in post
+                isWakingToShadeLocked = false
+            }
+        })
     }
 
     private fun updateResources() {
@@ -182,19 +217,19 @@
      */
     internal fun onDraggedDown(startingChild: View?, dragLengthY: Int) {
         if (canDragDown()) {
+            val cancelRunnable = Runnable {
+                logger.logGoingToLockedShadeAborted()
+                setDragDownAmountAnimated(0f)
+            }
             if (nsslController.isInLockedDownShade()) {
+                logger.logDraggedDownLockDownShade(startingChild)
                 statusBarStateController.setLeaveOpenOnKeyguardHide(true)
                 statusbar.dismissKeyguardThenExecute(OnDismissAction {
                     nextHideKeyguardNeedsNoAnimation = true
                     false
-                },
-                        null /* cancelRunnable */, false /* afterKeyguardGone */)
+                }, cancelRunnable, false /* afterKeyguardGone */)
             } else {
-                lockscreenGestureLogger.write(
-                        MetricsEvent.ACTION_LS_SHADE,
-                        (dragLengthY / displayMetrics.density).toInt(),
-                        0 /* velocityDp */)
-                lockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+                logger.logDraggedDown(startingChild, dragLengthY)
                 if (!ambientState.isDozing() || startingChild != null) {
                     // go to locked shade while animating the drag down amount from its current
                     // value
@@ -216,11 +251,11 @@
                         dragDownAmount = 0f
                         forceApplyAmount = false
                     }
-                    val cancelRunnable = Runnable { setDragDownAmountAnimated(0f) }
                     goToLockedShadeInternal(startingChild, animationHandler, cancelRunnable)
                 }
             }
         } else {
+            logger.logUnSuccessfulDragDown(startingChild)
             setDragDownAmountAnimated(0f)
         }
     }
@@ -229,6 +264,7 @@
      * Called by the touch helper when the drag down was aborted and should be reset.
      */
     internal fun onDragDownReset() {
+        logger.logDragDownAborted()
         nsslController.setDimmed(true /* dimmed */, true /* animated */)
         nsslController.resetScrollPosition()
         nsslController.resetCheckSnoozeLeavebehind()
@@ -246,10 +282,16 @@
     /**
      * Called by the touch helper when the drag down was started
      */
-    internal fun onDragDownStarted() {
+    internal fun onDragDownStarted(startingChild: ExpandableView?) {
+        logger.logDragDownStarted(startingChild)
         nsslController.cancelLongPress()
         nsslController.checkSnoozeLeavebehind()
-        dragDownAnimator?.cancel()
+        dragDownAnimator?.apply {
+            if (isRunning) {
+                logger.logAnimationCancelled(isPulse = false)
+                cancel()
+            }
+        }
     }
 
     /**
@@ -294,12 +336,12 @@
         set(value) {
             if (field != value || forceApplyAmount) {
                 field = value
-                if (!nsslController.isInLockedDownShade() || forceApplyAmount) {
+                if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
                     nsslController.setTransitionToFullShadeAmount(field)
                     notificationPanelController.setTransitionToFullShadeAmount(field,
                             false /* animate */, 0 /* delay */)
-                    dragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
-                    qS.setTransitionToFullShadeAmount(field, dragProgress)
+                    qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+                    qS.setTransitionToFullShadeAmount(field, qSDragProgress)
                     // TODO: appear media also in split shade
                     val mediaAmount = if (useSplitShade) 0f else field
                     mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
@@ -308,7 +350,10 @@
             }
         }
 
-    var dragProgress = 0f
+    /**
+     * The drag progress of the quick settings drag down amount
+     */
+    var qSDragProgress = 0f
         private set
 
     private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
@@ -325,6 +370,7 @@
         delay: Long = 0,
         endlistener: (() -> Unit)? = null
     ) {
+        logger.logDragDownAnimation(target)
         val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
         dragDownAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN
         dragDownAnimator.duration = SPRING_BACK_ANIMATION_LENGTH_MS
@@ -380,7 +426,9 @@
      */
     @JvmOverloads
     fun goToLockedShade(expandedView: View?, needsQSAnimation: Boolean = true) {
-        if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+        val isKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
+        logger.logTryGoToLockedShade(isKeyguard)
+        if (isKeyguard) {
             val animationHandler: ((Long) -> Unit)?
             if (needsQSAnimation) {
                 // Let's use the default animation
@@ -416,6 +464,7 @@
     ) {
         if (statusbar.isShadeDisabled) {
             cancelAction?.run()
+            logger.logShadeDisabledOnGoToLockedShade()
             return
         }
         var userId: Int = lockScreenUserManager.getCurrentUserId()
@@ -454,9 +503,15 @@
                 }
                 cancelAction?.run()
             }
+            logger.logShowBouncerOnGoToLockedShade()
             statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
             draggedDownEntry = entry
         } else {
+            logger.logGoingToLockedShade(animationHandler != null)
+            if (statusBarStateController.isDozing) {
+                // Make sure we don't go back to keyguard immediately again after waking up
+                isWakingToShadeLocked = true
+            }
             statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
             // This call needs to be after updating the shade state since otherwise
             // the scrimstate resets too early
@@ -476,6 +531,7 @@
      * @param previousState which state were we in when we hid the keyguard?
      */
     fun onHideKeyguard(delay: Long, previousState: Int) {
+        logger.logOnHideKeyguard()
         if (animationHandlerOnKeyguardDismiss != null) {
             animationHandlerOnKeyguardDismiss!!.invoke(delay)
             animationHandlerOnKeyguardDismiss = null
@@ -498,6 +554,7 @@
      * not triggered by gestures, e.g. when clicking on the shelf or expand button.
      */
     private fun performDefaultGoToFullShadeAnimation(delay: Long) {
+        logger.logDefaultGoToFullShadeAnimation(delay)
         notificationPanelController.animateToFullShade(delay)
         animateAppear(delay)
     }
@@ -534,6 +591,7 @@
      * @param cancelled was the interaction cancelled and this is a reset?
      */
     fun finishPulseAnimation(cancelled: Boolean) {
+        logger.logPulseExpansionFinished(cancelled)
         if (cancelled) {
             setPulseHeight(0f, animate = true)
         } else {
@@ -546,7 +604,29 @@
      * Notify this class that a pulse expansion is starting
      */
     fun onPulseExpansionStarted() {
-        pulseHeightAnimator?.cancel()
+        logger.logPulseExpansionStarted()
+        pulseHeightAnimator?.apply {
+            if (isRunning) {
+                logger.logAnimationCancelled(isPulse = true)
+                cancel()
+            }
+        }
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        IndentingPrintWriter(pw, "  ").let {
+            it.println("LSShadeTransitionController:")
+            it.increaseIndent()
+            it.println("pulseHeight: $pulseHeight")
+            it.println("useSplitShade: $useSplitShade")
+            it.println("dragDownAmount: $dragDownAmount")
+            it.println("qSDragProgress: $qSDragProgress")
+            it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
+            it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
+            it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
+            it.println("hasPendingHandlerOnKeyguardDismiss: " +
+                "${animationHandlerOnKeyguardDismiss != null}")
+        }
     }
 }
 
@@ -626,7 +706,7 @@
                     captureStartingChild(initialTouchX, initialTouchY)
                     initialTouchY = y
                     initialTouchX = x
-                    dragDownCallback.onDragDownStarted()
+                    dragDownCallback.onDragDownStarted(startingChild)
                     dragDownAmountOnStart = dragDownCallback.dragDownAmount
                     return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 9dc823b..d785059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -293,7 +293,8 @@
 
         IntentFilter internalFilter = new IntentFilter();
         internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
-        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         mCurrentUserId = ActivityManager.getCurrentUser(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 761a203..3fe108f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -24,15 +24,18 @@
 import android.os.PowerManager
 import android.os.PowerManager.WAKE_REASON_GESTURE
 import android.os.SystemClock
+import android.util.IndentingPrintWriter
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import android.view.ViewConfiguration
+import com.android.systemui.Dumpable
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
@@ -43,6 +46,8 @@
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import java.io.FileDescriptor
+import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.max
 
@@ -61,8 +66,9 @@
     private val statusBarStateController: StatusBarStateController,
     private val falsingManager: FalsingManager,
     private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
-    private val falsingCollector: FalsingCollector
-) : Gefingerpoken {
+    private val falsingCollector: FalsingCollector,
+    dumpManager: DumpManager
+) : Gefingerpoken, Dumpable {
     companion object {
         private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
     }
@@ -101,8 +107,6 @@
     private var mDraggedFarEnough: Boolean = false
     private var mStartingChild: ExpandableView? = null
     private var mPulsing: Boolean = false
-    var isWakingToShadeLocked: Boolean = false
-        private set
 
     private var velocityTracker: VelocityTracker? = null
 
@@ -120,6 +124,7 @@
             }
         })
         mPowerManager = context.getSystemService(PowerManager::class.java)
+        dumpManager.registerDumpable(this)
     }
 
     private fun initResources(context: Context) {
@@ -228,7 +233,6 @@
             mStartingChild = null
         }
         if (statusBarStateController.isDozing) {
-            isWakingToShadeLocked = true
             wakeUpCoordinator.willWakeUp = true
             mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
                     "com.android.systemui:PULSEDRAG")
@@ -326,7 +330,15 @@
         mPulsing = pulsing
     }
 
-    fun onStartedWakingUp() {
-        isWakingToShadeLocked = false
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        IndentingPrintWriter(pw, "  ").let {
+            it.println("PulseExpansionHandler:")
+            it.increaseIndent()
+            it.println("isExpanding: $isExpanding")
+            it.println("leavingLockscreen: $leavingLockscreen")
+            it.println("mPulsing: $mPulsing")
+            it.println("qsExpanded: $qsExpanded")
+            it.println("bouncerShowing: $bouncerShowing")
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 2dbe59e..bd948ece 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -28,6 +28,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.text.format.DateFormat;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -193,6 +194,7 @@
             mState = state;
             mUpcomingState = state;
             mUiEventLogger.log(StatusBarStateEvent.fromState(mState));
+            Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "StatusBarState " + tag);
             for (RankedListener rl : new ArrayList<>(mListeners)) {
                 rl.mListener.onStateChanged(mState);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index ea90bdd..6c3a909 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -17,14 +17,10 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.database.ContentObserver;
-import android.media.AudioAttributes;
 import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.UserHandle;
+import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings;
 
 import com.android.systemui.dagger.SysUISingleton;
 
@@ -37,19 +33,8 @@
 
     private final Vibrator mVibrator;
     private final Context mContext;
-    private boolean mHapticFeedbackEnabled;
-    private static final AudioAttributes STATUS_BAR_VIBRATION_ATTRIBUTES =
-            new AudioAttributes.Builder()
-                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-                    .build();
-
-    final private ContentObserver mVibrationObserver = new ContentObserver(Handler.getMain()) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateHapticFeedBackEnabled();
-        }
-    };
+    private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
 
     /**
      */
@@ -57,23 +42,11 @@
     public VibratorHelper(Context context) {
         mContext = context;
         mVibrator = context.getSystemService(Vibrator.class);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED), true,
-                mVibrationObserver);
-        mVibrationObserver.onChange(false /* selfChange */);
     }
 
     public void vibrate(final int effectId) {
-        if (mHapticFeedbackEnabled) {
-            AsyncTask.execute(() ->
-                    mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */),
-                            STATUS_BAR_VIBRATION_ATTRIBUTES));
-        }
-    }
-
-    private void updateHapticFeedBackEnabled() {
-        mHapticFeedbackEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) != 0;
+        AsyncTask.execute(() ->
+                mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */),
+                        TOUCH_VIBRATION_ATTRIBUTES));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
index 73d3e2a..8dc01f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.charging
 
-import android.graphics.Color
 import android.graphics.PointF
 import android.graphics.RuntimeShader
 import android.util.MathUtils
@@ -40,7 +39,7 @@
                 uniform float in_time;
                 uniform float in_radius;
                 uniform float in_blur;
-                uniform vec4 in_color;
+                layout(color) uniform vec4 in_color;
                 uniform float in_phase1;
                 uniform float in_phase2;
                 uniform float in_distortion_strength;"""
@@ -98,7 +97,7 @@
     var origin: PointF = PointF()
         set(value) {
             field = value
-            setUniform("in_origin", floatArrayOf(value.x, value.y))
+            setFloatUniform("in_origin", value.x, value.y)
         }
 
     /**
@@ -107,9 +106,9 @@
     var progress: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_radius",
+            setFloatUniform("in_radius",
                     (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius)
-            setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
+            setFloatUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
         }
 
     /**
@@ -118,7 +117,7 @@
     var distortionStrength: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_distortion_strength", value)
+            setFloatUniform("in_distortion_strength", value)
         }
 
     /**
@@ -127,9 +126,9 @@
     var time: Float = 0.0f
         set(value) {
             field = value * 0.001f
-            setUniform("in_time", field)
-            setUniform("in_phase1", field * 3f + 0.367f)
-            setUniform("in_phase2", field * 7.2f * 1.531f)
+            setFloatUniform("in_time", field)
+            setFloatUniform("in_phase1", field * 3f + 0.367f)
+            setFloatUniform("in_phase2", field * 7.2f * 1.531f)
         }
 
     /**
@@ -138,8 +137,6 @@
     var color: Int = 0xffffff.toInt()
         set(value) {
             field = value
-            val color = Color.valueOf(value)
-            setUniform("in_color", floatArrayOf(color.red(),
-                    color.green(), color.blue(), color.alpha()))
+            setColorUniform("in_color", value)
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
index 5175977..22fbf91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar.charging
 
-import android.graphics.Color
 import android.graphics.PointF
 import android.graphics.RuntimeShader
 import android.util.MathUtils
@@ -43,7 +42,7 @@
                 uniform float in_fadeRing;
                 uniform float in_blur;
                 uniform float in_pixelDensity;
-                uniform vec4 in_color;
+                layout(color) uniform vec4 in_color;
                 uniform float in_sparkle_strength;"""
         private const val SHADER_LIB = """float triangleNoise(vec2 n) {
                     n  = fract(n * vec2(5.3987, 5.4421));
@@ -122,7 +121,7 @@
     var radius: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_maxRadius", value)
+            setFloatUniform("in_maxRadius", value)
         }
 
     /**
@@ -131,7 +130,7 @@
     var origin: PointF = PointF()
         set(value) {
             field = value
-            setUniform("in_origin", floatArrayOf(value.x, value.y))
+            setFloatUniform("in_origin", value.x, value.y)
         }
 
     /**
@@ -140,10 +139,10 @@
     var progress: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_progress", value)
-            setUniform("in_radius",
+            setFloatUniform("in_progress", value)
+            setFloatUniform("in_radius",
                     (1 - (1 - value) * (1 - value) * (1 - value))* radius)
-            setUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
+            setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
 
             val fadeIn = subProgress(0f, 0.1f, value)
             val fadeOutNoise = subProgress(0.4f, 1f, value)
@@ -153,9 +152,9 @@
                 fadeCircle = subProgress(0f, 0.2f, value)
                 fadeOutRipple = subProgress(0.3f, 1f, value)
             }
-            setUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise))
-            setUniform("in_fadeCircle", 1 - fadeCircle)
-            setUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
+            setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise))
+            setFloatUniform("in_fadeCircle", 1 - fadeCircle)
+            setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
         }
 
     /**
@@ -164,7 +163,7 @@
     var time: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_time", value)
+            setFloatUniform("in_time", value)
         }
 
     /**
@@ -173,9 +172,7 @@
     var color: Int = 0xffffff.toInt()
         set(value) {
             field = value
-            val color = Color.valueOf(value)
-            setUniform("in_color", floatArrayOf(color.red(),
-                    color.green(), color.blue(), color.alpha()))
+            setColorUniform("in_color", value)
         }
 
     /**
@@ -186,7 +183,7 @@
     var sparkleStrength: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_sparkle_strength", value)
+            setFloatUniform("in_sparkle_strength", value)
         }
 
     /**
@@ -195,14 +192,14 @@
     var distortionStrength: Float = 0.0f
         set(value) {
             field = value
-            setUniform("in_distort_radial", 75 * progress * value)
-            setUniform("in_distort_xy", 75 * value)
+            setFloatUniform("in_distort_radial", 75 * progress * value)
+            setFloatUniform("in_distort_xy", 75 * value)
         }
 
     var pixelDensity: Float = 1.0f
         set(value) {
             field = value
-            setUniform("in_pixelDensity", value)
+            setFloatUniform("in_pixelDensity", value)
         }
 
     var shouldFadeOutRipple: Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 5272a16..300c3a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -334,6 +334,8 @@
                 setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup());
             }
         });
+        // Get initial user setup state
+        setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup());
 
         WifiManager.ScanResultsCallback scanResultsCallback =
                 new WifiManager.ScanResultsCallback() {
@@ -994,6 +996,11 @@
     }
 
     @VisibleForTesting
+    boolean isUserSetup() {
+        return mUserSetup;
+    }
+
+    @VisibleForTesting
     boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
         if (allSubscriptions.size() != mMobileSignalControllers.size()) {
             return false;
@@ -1137,6 +1144,7 @@
     /** */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NetworkController state:");
+        pw.println("  mUserSetup=" + mUserSetup);
 
         pw.println("  - telephony ------");
         pw.print("  hasVoiceCallingFeature()=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index b0d41f1..5aeab84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -23,7 +23,6 @@
 import android.os.Handler
 import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.NotificationListenerService.RankingMap
-import com.android.internal.statusbar.NotificationVisibility
 import com.android.internal.widget.ConversationLayout
 import com.android.internal.widget.MessagingImageMessage
 import com.android.internal.widget.MessagingLayout
@@ -31,7 +30,10 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
@@ -70,7 +72,8 @@
  */
 @SysUISingleton
 class AnimatedImageNotificationManager @Inject constructor(
-    private val notificationEntryManager: NotificationEntryManager,
+    private val notifCollection: CommonNotifCollection,
+    private val bindEventManager: BindEventManager,
     private val headsUpManager: HeadsUpManager,
     private val statusBarStateController: StatusBarStateController
 ) {
@@ -81,33 +84,23 @@
     fun bind() {
         headsUpManager.addListener(object : OnHeadsUpChangedListener {
             override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
-                entry.row?.let { row ->
-                    updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded)
-                }
+                updateAnimatedImageDrawables(entry)
             }
         })
         statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
             override fun onExpandedChanged(isExpanded: Boolean) {
                 isStatusBarExpanded = isExpanded
-                notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry ->
-                    entry.row?.let { row ->
-                        updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp)
-                    }
-                }
+                notifCollection.allNotifs.forEach(::updateAnimatedImageDrawables)
             }
         })
-        notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
-            override fun onEntryInflated(entry: NotificationEntry) {
-                entry.row?.let { row ->
-                    updateAnimatedImageDrawables(
-                            row,
-                            animating = isStatusBarExpanded || row.isHeadsUp)
-                }
-            }
-            override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
-        })
+        bindEventManager.addListener(::updateAnimatedImageDrawables)
     }
 
+    private fun updateAnimatedImageDrawables(entry: NotificationEntry) =
+        entry.row?.let { row ->
+            updateAnimatedImageDrawables(row, animating = row.isHeadsUp || isStatusBarExpanded)
+        }
+
     private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) =
             (row.layouts?.asSequence() ?: emptySequence())
                     .flatMap { layout -> layout.allViews.asSequence() }
@@ -132,12 +125,15 @@
 /**
  * Tracks state related to conversation notifications, and updates the UI of existing notifications
  * when necessary.
+ * TODO(b/214083332) Refactor this class to use the right coordinators and controllers
  */
 @SysUISingleton
 class ConversationNotificationManager @Inject constructor(
-    private val notificationEntryManager: NotificationEntryManager,
+    private val bindEventManager: BindEventManager,
     private val notificationGroupManager: NotificationGroupManagerLegacy,
     private val context: Context,
+    private val notifCollection: CommonNotifCollection,
+    private val featureFlags: NotifPipelineFlags,
     @Main private val mainHandler: Handler
 ) {
     // Need this state to be thread safe, since it's accessed from the ui thread
@@ -146,76 +142,73 @@
 
     private var notifPanelCollapsed = true
 
+    private fun updateNotificationRanking(rankingMap: RankingMap) {
+        fun getLayouts(view: NotificationContentView) =
+                sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
+        val ranking = Ranking()
+        val activeConversationEntries = states.keys.asSequence()
+                .mapNotNull { notifCollection.getEntry(it) }
+        for (entry in activeConversationEntries) {
+            if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
+                val important = ranking.channel.isImportantConversation
+                var changed = false
+                entry.row?.layouts?.asSequence()
+                        ?.flatMap(::getLayouts)
+                        ?.mapNotNull { it as? ConversationLayout }
+                        ?.filterNot { it.isImportantConversation == important }
+                        ?.forEach { layout ->
+                            changed = true
+                            if (important && entry.isMarkedForUserTriggeredMovement) {
+                                // delay this so that it doesn't animate in until after
+                                // the notif has been moved in the shade
+                                mainHandler.postDelayed(
+                                        {
+                                            layout.setIsImportantConversation(
+                                                    important,
+                                                    true)
+                                        },
+                                        IMPORTANCE_ANIMATION_DELAY.toLong())
+                            } else {
+                                layout.setIsImportantConversation(important, false)
+                            }
+                        }
+                if (changed && !featureFlags.isNewPipelineEnabled()) {
+                    notificationGroupManager.updateIsolation(entry)
+                }
+            }
+        }
+    }
+    fun onEntryViewBound(entry: NotificationEntry) {
+        if (!entry.ranking.isConversation) {
+            return
+        }
+        fun updateCount(isExpanded: Boolean) {
+            if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
+                resetCount(entry.key)
+                entry.row?.let(::resetBadgeUi)
+            }
+        }
+        entry.row?.setOnExpansionChangedListener { isExpanded ->
+            if (entry.row?.isShown == true && isExpanded) {
+                entry.row.performOnIntrinsicHeightReached {
+                    updateCount(isExpanded)
+                }
+            } else {
+                updateCount(isExpanded)
+            }
+        }
+        updateCount(entry.row?.isExpanded == true)
+    }
+
     init {
-        notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
-            override fun onNotificationRankingUpdated(rankingMap: RankingMap) {
-                fun getLayouts(view: NotificationContentView) =
-                        sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
-                val ranking = Ranking()
-                val activeConversationEntries = states.keys.asSequence()
-                        .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) }
-                for (entry in activeConversationEntries) {
-                    if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
-                        val important = ranking.channel.isImportantConversation
-                        var changed = false
-                        entry.row?.layouts?.asSequence()
-                                ?.flatMap(::getLayouts)
-                                ?.mapNotNull { it as? ConversationLayout }
-                                ?.filterNot { it.isImportantConversation == important }
-                                ?.forEach { layout ->
-                                    changed = true
-                                    if (important && entry.isMarkedForUserTriggeredMovement) {
-                                        // delay this so that it doesn't animate in until after
-                                        // the notif has been moved in the shade
-                                        mainHandler.postDelayed(
-                                                {
-                                                    layout.setIsImportantConversation(
-                                                            important,
-                                                            true)
-                                                },
-                                                IMPORTANCE_ANIMATION_DELAY.toLong())
-                                    } else {
-                                        layout.setIsImportantConversation(important, false)
-                                    }
-                                }
-                        if (changed) {
-                            notificationGroupManager.updateIsolation(entry)
-                        }
-                    }
-                }
-            }
+        notifCollection.addCollectionListener(object : NotifCollectionListener {
+            override fun onRankingUpdate(ranking: RankingMap) =
+                updateNotificationRanking(ranking)
 
-            override fun onEntryInflated(entry: NotificationEntry) {
-                if (!entry.ranking.isConversation) {
-                    return
-                }
-                fun updateCount(isExpanded: Boolean) {
-                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
-                        resetCount(entry.key)
-                        entry.row?.let(::resetBadgeUi)
-                    }
-                }
-                entry.row?.setOnExpansionChangedListener { isExpanded ->
-                    if (entry.row?.isShown == true && isExpanded) {
-                        entry.row.performOnIntrinsicHeightReached {
-                            updateCount(isExpanded)
-                        }
-                    } else {
-                        updateCount(isExpanded)
-                    }
-                }
-                updateCount(entry.row?.isExpanded == true)
-            }
-
-            override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
-
-            override fun onEntryRemoved(
-                entry: NotificationEntry,
-                visibility: NotificationVisibility?,
-                removedByUser: Boolean,
-                reason: Int
-            ) = removeTrackedEntry(entry)
+            override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
+                removeTrackedEntry(entry)
         })
+        bindEventManager.addListener(::onEntryViewBound)
     }
 
     private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
@@ -243,11 +236,10 @@
         val expanded = states
                 .asSequence()
                 .mapNotNull { (key, _) ->
-                    notificationEntryManager.getActiveNotificationUnfiltered(key)
-                            ?.let { entry ->
-                                if (entry.row?.isExpanded == true) key to entry
-                                else null
-                            }
+                    notifCollection.getEntry(key)?.let { entry ->
+                        if (entry.row?.isExpanded == true) key to entry
+                        else null
+                    }
                 }
                 .toMap()
         states.replaceAll { key, state ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 97ae83e..643deb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -16,6 +16,8 @@
 package com.android.systemui.statusbar.notification;
 
 import android.annotation.Nullable;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
@@ -106,4 +108,20 @@
      */
     default void onNotificationRankingUpdated(RankingMap rankingMap) {
     }
+
+    /**
+     * Called when a notification channel is modified, in response to
+     * {@link NotificationListenerService#onNotificationChannelModified}.
+     *
+     * @param pkgName the package the notification channel belongs to.
+     * @param user the user the notification channel belongs to.
+     * @param channel the channel being modified.
+     * @param modificationType the type of modification that occurred to the channel.
+     */
+    default void onNotificationChannelModified(
+            String pkgName,
+            UserHandle user,
+            NotificationChannel channel,
+            int modificationType) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 4717b3a..c331608 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -21,8 +21,10 @@
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -59,6 +61,7 @@
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.Compile;
 import com.android.systemui.util.leak.LeakDetector;
 
 import java.io.FileDescriptor;
@@ -401,6 +404,15 @@
         @Override
         public void onNotificationsInitialized() {
         }
+
+        @Override
+        public void onNotificationChannelModified(
+                String pkgName,
+                UserHandle user,
+                NotificationChannel channel,
+                int modificationType) {
+            notifyChannelModified(pkgName, user, channel, modificationType);
+        }
     };
 
     /**
@@ -626,7 +638,6 @@
             entry = new NotificationEntry(
                     notification,
                     ranking,
-                    mFgsFeatureController.isForegroundServiceDismissalEnabled(),
                     SystemClock.uptimeMillis());
             mAllNotifications.add(entry);
             mLeakDetector.trackInstance(entry);
@@ -779,6 +790,19 @@
         }
     }
 
+    void notifyChannelModified(
+            String pkgName,
+            UserHandle user,
+            NotificationChannel channel,
+            int modificationType) {
+        for (NotifCollectionListener listener : mNotifCollectionListeners) {
+            listener.onNotificationChannelModified(pkgName, user, channel, modificationType);
+        }
+        for (NotificationEntryListener listener : mNotificationEntryListeners) {
+            listener.onNotificationChannelModified(pkgName, user, channel, modificationType);
+        }
+    }
+
     private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
         if (rankingMap == null) {
             return;
@@ -1012,7 +1036,7 @@
     }
 
     private static final String TAG = "NotificationEntryMgr";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * Used when a notification is removed and it doesn't have a reason that maps to one of the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index cd8897e..bd9383d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -21,6 +21,7 @@
 
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
 import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
@@ -37,6 +38,7 @@
 /**
  * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
  */
+@SysUISingleton
 class NotificationSectionsFeatureManager @Inject constructor(
     val proxy: DeviceConfigProxy,
     val context: Context
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index cbc113b..c3cc97b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -24,6 +24,7 @@
 
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * A util class for various reusable functions
@@ -73,4 +74,20 @@
         return (int) (dimensionPixelSize * factor);
     }
 
+    /** Get the notification key, reformatted for logging, for the (optional) entry */
+    public static String logKey(NotificationEntry entry) {
+        if (entry == null) {
+            return "null";
+        }
+        return logKey(entry.getKey());
+    }
+
+    /** Removes newlines from the notification key to prettify apps that have these in the tag */
+    public static String logKey(String key) {
+        if (key == null) {
+            return "null";
+        }
+        return key.replace("\n", "");
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
new file mode 100644
index 0000000..03b978e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A class which keeps track of whether section headers should be shown in the notification shade.
+ *
+ * (In an ideal world, this would directly monitor the state of the keyguard and invalidate the
+ * pipeline to show/hide headers, but the KeyguardController already invalidates the pipeline when
+ * the keyguard's state changes. Instead of having both classes monitor for state changes and ending
+ * up with duplicate runs of the pipeline, we let the KeyguardController update the header
+ * visibility when it invalidates, and we just store that state here.)
+ */
+@SysUISingleton
+class SectionHeaderVisibilityProvider @Inject constructor() {
+    var sectionHeadersVisible = true
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b6b9c3f..2a2cc81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -46,6 +46,7 @@
 import android.annotation.MainThread;
 import android.annotation.UserIdInt;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -60,6 +61,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
@@ -71,6 +73,7 @@
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
 import com.android.systemui.statusbar.notification.collection.notifcollection.BindEntryEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.ChannelChangedEvent;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CleanUpEntryEvent;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
@@ -240,6 +243,10 @@
         Assert.isMainThread();
         checkForReentrantCall();
 
+        // TODO (b/206842750): This method is called from (silent) clear all and non-clear all
+        // contexts and should be checking the NO_CLEAR flag, rather than depending on NSSL
+        // to pass in a properly filtered list of notifications
+
         final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
         for (int i = 0; i < entriesToDismiss.size(); i++) {
             NotificationEntry entry = entriesToDismiss.get(i).first;
@@ -419,6 +426,16 @@
         dispatchEventsAndRebuildList();
     }
 
+    private void onNotificationChannelModified(
+            String pkgName,
+            UserHandle user,
+            NotificationChannel channel,
+            int modificationType) {
+        Assert.isMainThread();
+        mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
+        dispatchEventsAndRebuildList();
+    }
+
     private void onNotificationsInitialized() {
         mInitializedTimestamp = mClock.uptimeMillis();
     }
@@ -616,7 +633,7 @@
         entry.mLifetimeExtenders.clear();
         mAmDispatchingToOtherCode = true;
         for (NotifLifetimeExtender extender : mLifetimeExtenders) {
-            if (extender.shouldExtendLifetime(entry, entry.mCancellationReason)) {
+            if (extender.maybeExtendLifetime(entry, entry.mCancellationReason)) {
                 mLogger.logLifetimeExtended(entry.getKey(), extender);
                 entry.mLifetimeExtenders.add(extender);
             }
@@ -742,13 +759,15 @@
      *
      * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
      */
-    private static boolean shouldAutoDismissChildren(
+    @VisibleForTesting
+    static boolean shouldAutoDismissChildren(
             NotificationEntry entry,
             String dismissedGroupKey) {
         return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
                 && !entry.getSbn().getNotification().isGroupSummary()
-                && !hasFlag(entry, Notification.FLAG_FOREGROUND_SERVICE)
+                && !hasFlag(entry, Notification.FLAG_ONGOING_EVENT)
                 && !hasFlag(entry, Notification.FLAG_BUBBLE)
+                && !hasFlag(entry, Notification.FLAG_NO_CLEAR)
                 && entry.getDismissState() != DISMISSED;
     }
 
@@ -828,6 +847,19 @@
         }
 
         @Override
+        public void onNotificationChannelModified(
+                String pkgName,
+                UserHandle user,
+                NotificationChannel channel,
+                int modificationType) {
+            NotifCollection.this.onNotificationChannelModified(
+                    pkgName,
+                    user,
+                    channel,
+                    modificationType);
+        }
+
+        @Override
         public void onNotificationsInitialized() {
             NotifCollection.this.onNotificationsInitialized();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d56938a..f22acb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -174,7 +174,6 @@
 
     private boolean mAutoHeadsUp;
     private boolean mPulseSupressed;
-    private boolean mAllowFgsDismissal;
     private int mBucket = BUCKET_ALERTING;
     @Nullable private Long mPendingAnimationDuration;
     private boolean mIsMarkedForUserTriggeredMovement;
@@ -192,14 +191,6 @@
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
             @NonNull Ranking ranking,
-            long creationTime) {
-        this(sbn, ranking, false, creationTime);
-    }
-
-    public NotificationEntry(
-            @NonNull StatusBarNotification sbn,
-            @NonNull Ranking ranking,
-            boolean allowFgsDismissal,
             long creationTime
     ) {
         super(requireNonNull(requireNonNull(sbn).getKey()), creationTime);
@@ -209,8 +200,6 @@
         mKey = sbn.getKey();
         setSbn(sbn);
         setRanking(ranking);
-
-        mAllowFgsDismissal = allowFgsDismissal;
     }
 
     @Override
@@ -743,13 +732,11 @@
     /**
      * @return Can the underlying notification be cleared? This can be different from whether the
      *         notification can be dismissed in case notifications are sensitive on the lockscreen.
-     * @see #canViewBeDismissed()
      */
-    // TOOD: This logic doesn't belong on NotificationEntry. It should be moved to the
-    // ForegroundsServiceDismissalFeatureController or some other controller that can be added
-    // as a dependency to any class that needs to answer this question.
+    // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
+    // that can be added as a dependency to any class that needs to answer this question.
     public boolean isClearable() {
-        if (!isDismissable()) {
+        if (!mSbn.isClearable()) {
             return false;
         }
 
@@ -757,7 +744,7 @@
         if (children != null && children.size() > 0) {
             for (int i = 0; i < children.size(); i++) {
                 NotificationEntry child =  children.get(i);
-                if (!child.isDismissable()) {
+                if (!child.getSbn().isClearable()) {
                     return false;
                 }
             }
@@ -766,28 +753,25 @@
     }
 
     /**
-     * Notifications might have any combination of flags:
-     * - FLAG_ONGOING_EVENT
-     * - FLAG_NO_CLEAR
-     * - FLAG_FOREGROUND_SERVICE
-     *
-     * We want to allow dismissal of notifications that represent foreground services, which may
-     * have all 3 flags set. If we only find NO_CLEAR though, we don't want to allow dismissal
+     * @return Can the underlying notification be individually dismissed?
+     * @see #canViewBeDismissed()
      */
-    private boolean isDismissable() {
-        boolean ongoing = ((mSbn.getNotification().flags & Notification.FLAG_ONGOING_EVENT) != 0);
-        boolean noclear = ((mSbn.getNotification().flags & Notification.FLAG_NO_CLEAR) != 0);
-        boolean fgs = ((mSbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0);
-
-        if (mAllowFgsDismissal) {
-            if (noclear && !ongoing && !fgs) {
-                return false;
-            }
-            return true;
-        } else {
-            return mSbn.isClearable();
+    // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
+    // that can be added as a dependency to any class that needs to answer this question.
+    public boolean isDismissable() {
+        if  (mSbn.isOngoing()) {
+            return false;
         }
-
+        List<NotificationEntry> children = getAttachedNotifChildren();
+        if (children != null && children.size() > 0) {
+            for (int i = 0; i < children.size(); i++) {
+                NotificationEntry child =  children.get(i);
+                if (child.getSbn().isOngoing()) {
+                    return false;
+                }
+            }
+        }
+        return true;
     }
 
     public boolean canViewBeDismissed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 09ae7eb..87e531c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -260,7 +260,8 @@
         }
         events.sort(mEventComparator);
 
-        mLogger.logEmitBatch(batch.mGroupKey);
+        long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+        mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
 
         mHandler.onNotificationBatchPosted(events);
     }
@@ -337,6 +338,6 @@
         void onNotificationBatchPosted(List<CoalescedEvent> events);
     }
 
-    private static final int MIN_GROUP_LINGER_DURATION = 50;
+    private static final int MIN_GROUP_LINGER_DURATION = 200;
     private static final int MAX_GROUP_LINGER_DURATION = 500;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index d4d5b64..211e374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -32,11 +32,13 @@
         })
     }
 
-    fun logEmitBatch(groupKey: String) {
+    fun logEmitBatch(groupKey: String, batchSize: Int, batchAgeMs: Long) {
         buffer.log(TAG, LogLevel.DEBUG, {
             str1 = groupKey
+            int1 = batchSize
+            long1 = batchAgeMs
         }, {
-            "Emitting event batch for group $str1"
+            "Emitting batch for group $str1 size=$int1 age=${long1}ms"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 3a39c39..f04b24e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -101,7 +101,7 @@
     };
 
     /**
-     * Puts foreground service notifications into its own section.
+     * Puts colorized foreground service and call notifications into its own section.
      */
     private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService",
             NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE) {
@@ -109,12 +109,22 @@
         public boolean isInSection(ListEntry entry) {
             NotificationEntry notificationEntry = entry.getRepresentativeEntry();
             if (notificationEntry != null) {
-                Notification notification = notificationEntry.getSbn().getNotification();
-                return notification.isForegroundService()
-                        && notification.isColorized()
-                        && entry.getRepresentativeEntry().getImportance() > IMPORTANCE_MIN;
+                return isColorizedForegroundService(notificationEntry) || isCall(notificationEntry);
             }
             return false;
         }
+
+        private boolean isColorizedForegroundService(NotificationEntry entry) {
+            Notification notification = entry.getSbn().getNotification();
+            return notification.isForegroundService()
+                    && notification.isColorized()
+                    && entry.getImportance() > IMPORTANCE_MIN;
+        }
+
+        private boolean isCall(NotificationEntry entry) {
+            Notification notification = entry.getSbn().getNotification();
+            return entry.getImportance() > IMPORTANCE_MIN
+                    && notification.isStyle(Notification.CallStyle.class);
+        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index e59f4a6..ba88ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.dagger.PeopleHeader
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
 import javax.inject.Inject
@@ -48,18 +50,36 @@
 
     val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) {
         override fun isInSection(entry: ListEntry): Boolean =
-                isConversation(entry.representativeEntry!!)
+                isConversation(entry)
         override fun getHeaderNodeController() =
                 // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
                 if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
     }
 
+    val comparator = object : NotifComparator("People") {
+        override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+            assert(entry1.section === entry2.section)
+            if (entry1.section?.sectioner !== sectioner) {
+                return 0
+            }
+            val type1 = getPeopleType(entry1)
+            val type2 = getPeopleType(entry2)
+            return type2.compareTo(type1)
+        }
+    }
+
     override fun attach(pipeline: NotifPipeline) {
         pipeline.addPromoter(notificationPromoter)
     }
 
-    private fun isConversation(entry: NotificationEntry): Boolean =
-        peopleNotificationIdentifier.getPeopleNotificationType(entry) != TYPE_NON_PERSON
+    private fun isConversation(entry: ListEntry): Boolean =
+        getPeopleType(entry) != TYPE_NON_PERSON
+
+    @PeopleNotificationType
+    private fun getPeopleType(entry: ListEntry): Int =
+        entry.representativeEntry?.let {
+            peopleNotificationIdentifier.getPeopleNotificationType(it)
+        } ?: TYPE_NON_PERSON
 
     companion object {
         private const val TAG = "ConversationCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
index dbecf1c..ac00581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
@@ -84,7 +84,7 @@
             onEndLifetimeExtensionCallback = callback
         }
 
-        override fun shouldExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+        override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
             val isShowingGuts = isCurrentlyShowingGuts(entry)
             if (isShowingGuts) {
                 notifsExtendingLifetime.add(entry.key)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
deleted file mode 100644
index f8b4274..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.coordinator;
-
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.interruption.HeadsUpController.alertAgain;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.render.NodeController;
-import com.android.systemui.statusbar.notification.dagger.IncomingHeader;
-import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
- * the HUN state reported by the {@link HeadsUpManager}. In this class we only consider one
- * notification, in particular the {@link HeadsUpManager#getTopEntry()}, to be HeadsUpping at a
- * time even though other notifications may be queued to heads up next.
- *
- * The current HUN, but not HUNs that are queued to heads up, will be:
- * - Lifetime extended until it's no longer heads upping.
- * - Promoted out of its group if it's a child of a group.
- * - In the HeadsUpCoordinatorSection. Ordering is configured in {@link NotifCoordinators}.
- * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
- *
- * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
- */
-@CoordinatorScope
-public class HeadsUpCoordinator implements Coordinator {
-    private static final String TAG = "HeadsUpCoordinator";
-
-    private final HeadsUpManager mHeadsUpManager;
-    private final HeadsUpViewBinder mHeadsUpViewBinder;
-    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
-    private final NotificationRemoteInputManager mRemoteInputManager;
-    private final NodeController mIncomingHeaderController;
-
-    // tracks the current HeadUpNotification reported by HeadsUpManager
-    private @Nullable NotificationEntry mCurrentHun;
-
-    private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
-    private NotificationEntry mNotifExtendingLifetime; // notif we've extended the lifetime for
-
-    @Inject
-    public HeadsUpCoordinator(
-            HeadsUpManager headsUpManager,
-            HeadsUpViewBinder headsUpViewBinder,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
-            NotificationRemoteInputManager remoteInputManager,
-            @IncomingHeader NodeController incomingHeaderController) {
-        mHeadsUpManager = headsUpManager;
-        mHeadsUpViewBinder = headsUpViewBinder;
-        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
-        mRemoteInputManager = remoteInputManager;
-        mIncomingHeaderController = incomingHeaderController;
-    }
-
-    @Override
-    public void attach(NotifPipeline pipeline) {
-        mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
-        pipeline.addCollectionListener(mNotifCollectionListener);
-        pipeline.addPromoter(mNotifPromoter);
-        pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
-    }
-
-    public NotifSectioner getSectioner() {
-        return mNotifSectioner;
-    }
-
-    private void onHeadsUpViewBound(NotificationEntry entry) {
-        mHeadsUpManager.showNotification(entry);
-    }
-
-    private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
-        /**
-         * Notification was just added and if it should heads up, bind the view and then show it.
-         */
-        @Override
-        public void onEntryAdded(NotificationEntry entry) {
-            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
-                mHeadsUpViewBinder.bindHeadsUpView(
-                        entry,
-                        HeadsUpCoordinator.this::onHeadsUpViewBound);
-            }
-        }
-
-        /**
-         * Notification could've updated to be heads up or not heads up. Even if it did update to
-         * heads up, if the notification specified that it only wants to alert once, don't heads
-         * up again.
-         */
-        @Override
-        public void onEntryUpdated(NotificationEntry entry) {
-            boolean hunAgain = alertAgain(entry, entry.getSbn().getNotification());
-            // includes check for whether this notification should be filtered:
-            boolean shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
-            final boolean wasHeadsUp = mHeadsUpManager.isAlerting(entry.getKey());
-            if (wasHeadsUp) {
-                if (shouldHeadsUp) {
-                    mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
-                } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
-                    // We don't want this to be interrupting anymore, let's remove it
-                    mHeadsUpManager.removeNotification(
-                            entry.getKey(), false /* removeImmediately */);
-                }
-            } else if (shouldHeadsUp && hunAgain) {
-                // This notification was updated to be heads up, show it!
-                mHeadsUpViewBinder.bindHeadsUpView(
-                        entry,
-                        HeadsUpCoordinator.this::onHeadsUpViewBound);
-            }
-        }
-
-        /**
-         * Stop alerting HUNs that are removed from the notification collection
-         */
-        @Override
-        public void onEntryRemoved(NotificationEntry entry, int reason) {
-            final String entryKey = entry.getKey();
-            if (mHeadsUpManager.isAlerting(entryKey)) {
-                boolean removeImmediatelyForRemoteInput =
-                        mRemoteInputManager.isSpinning(entryKey)
-                                && !FORCE_REMOTE_INPUT_HISTORY;
-                mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
-            }
-        }
-
-        @Override
-        public void onEntryCleanUp(NotificationEntry entry) {
-            mHeadsUpViewBinder.abortBindCallback(entry);
-        }
-    };
-
-    private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
-        @Override
-        public @NonNull String getName() {
-            return TAG;
-        }
-
-        @Override
-        public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
-            mEndLifetimeExtension = callback;
-        }
-
-        @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry, int reason) {
-            boolean isShowingHun = isCurrentlyShowingHun(entry);
-            if (isShowingHun) {
-                mNotifExtendingLifetime = entry;
-            }
-            return isShowingHun;
-        }
-
-        @Override
-        public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
-            if (Objects.equals(mNotifExtendingLifetime, entry)) {
-                mNotifExtendingLifetime = null;
-            }
-        }
-    };
-
-    private final NotifPromoter mNotifPromoter = new NotifPromoter(TAG) {
-        @Override
-        public boolean shouldPromoteToTopLevel(NotificationEntry entry) {
-            return isCurrentlyShowingHun(entry);
-        }
-    };
-
-    private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp",
-            NotificationPriorityBucketKt.BUCKET_HEADS_UP) {
-        @Override
-        public boolean isInSection(ListEntry entry) {
-            return isCurrentlyShowingHun(entry);
-        }
-
-        @Nullable
-        @Override
-        public NodeController getHeaderNodeController() {
-            // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
-            if (RankingCoordinator.SHOW_ALL_SECTIONS) {
-                return mIncomingHeaderController;
-            }
-            return null;
-        }
-    };
-
-    private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
-            new OnHeadsUpChangedListener() {
-        @Override
-        public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-            NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
-            if (!Objects.equals(mCurrentHun, newHUN)) {
-                mCurrentHun = newHUN;
-                endNotifLifetimeExtension();
-            }
-            if (!isHeadsUp) {
-                mHeadsUpViewBinder.unbindHeadsUpView(entry);
-            }
-        }
-    };
-
-    private boolean isCurrentlyShowingHun(ListEntry entry) {
-        return mCurrentHun == entry.getRepresentativeEntry();
-    }
-
-    private void endNotifLifetimeExtension() {
-        if (mNotifExtendingLifetime != null) {
-            mEndLifetimeExtension.onEndLifetimeExtension(
-                    mLifetimeExtender,
-                    mNotifExtendingLifetime);
-            mNotifExtendingLifetime = null;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
new file mode 100644
index 0000000..b84b382
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.ArraySet
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader
+import com.android.systemui.statusbar.notification.interruption.HeadsUpController
+import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
+import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+/**
+ * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
+ * the HUN state reported by the [HeadsUpManager]. In this class we only consider one
+ * notification, in particular the [HeadsUpManager.getTopEntry], to be HeadsUpping at a
+ * time even though other notifications may be queued to heads up next.
+ *
+ * The current HUN, but not HUNs that are queued to heads up, will be:
+ * - Lifetime extended until it's no longer heads upping.
+ * - Promoted out of its group if it's a child of a group.
+ * - In the HeadsUpCoordinatorSection. Ordering is configured in [NotifCoordinators].
+ * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
+ *
+ * Note: The inflation callback in [PreparationCoordinator] handles showing HUNs.
+ */
+@CoordinatorScope
+class HeadsUpCoordinator @Inject constructor(
+    private val mHeadsUpManager: HeadsUpManager,
+    private val mHeadsUpViewBinder: HeadsUpViewBinder,
+    private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
+    private val mRemoteInputManager: NotificationRemoteInputManager,
+    @IncomingHeader private val mIncomingHeaderController: NodeController,
+    @Main private val mExecutor: DelayableExecutor
+) : Coordinator {
+    private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
+
+    // notifs we've extended the lifetime for
+    private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
+
+    override fun attach(pipeline: NotifPipeline) {
+        mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
+        pipeline.addCollectionListener(mNotifCollectionListener)
+        pipeline.addPromoter(mNotifPromoter)
+        pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
+    }
+
+    private fun onHeadsUpViewBound(entry: NotificationEntry) {
+        mHeadsUpManager.showNotification(entry)
+    }
+
+    private val mNotifCollectionListener = object : NotifCollectionListener {
+        /**
+         * Notification was just added and if it should heads up, bind the view and then show it.
+         */
+        override fun onEntryAdded(entry: NotificationEntry) {
+            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+                mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
+            }
+        }
+
+        /**
+         * Notification could've updated to be heads up or not heads up. Even if it did update to
+         * heads up, if the notification specified that it only wants to alert once, don't heads
+         * up again.
+         */
+        override fun onEntryUpdated(entry: NotificationEntry) {
+            val hunAgain = HeadsUpController.alertAgain(entry, entry.sbn.notification)
+            // includes check for whether this notification should be filtered:
+            val shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+            val wasHeadsUp = mHeadsUpManager.isAlerting(entry.key)
+            if (wasHeadsUp) {
+                if (shouldHeadsUp) {
+                    mHeadsUpManager.updateNotification(entry.key, hunAgain)
+                } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) {
+                    // We don't want this to be interrupting anymore, let's remove it
+                    mHeadsUpManager.removeNotification(
+                        entry.key, false /* removeImmediately */
+                    )
+                }
+            } else if (shouldHeadsUp && hunAgain) {
+                // This notification was updated to be heads up, show it!
+                mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
+            }
+        }
+
+        /**
+         * Stop alerting HUNs that are removed from the notification collection
+         */
+        override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+            val entryKey = entry.key
+            if (mHeadsUpManager.isAlerting(entryKey)) {
+                val removeImmediatelyForRemoteInput = (mRemoteInputManager.isSpinning(entryKey) &&
+                        !NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
+                mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
+            }
+        }
+
+        override fun onEntryCleanUp(entry: NotificationEntry) {
+            mHeadsUpViewBinder.abortBindCallback(entry)
+        }
+    }
+
+    private val mLifetimeExtender = object : NotifLifetimeExtender {
+        override fun getName() = TAG
+
+        override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
+            mEndLifetimeExtension = callback
+        }
+
+        override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+            if (mHeadsUpManager.canRemoveImmediately(entry.key)) {
+                return false
+            }
+            if (isSticky(entry)) {
+                val removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
+                mExecutor.executeDelayed({
+                    val canStillRemove = mHeadsUpManager.canRemoveImmediately(entry.key)
+                    if (mNotifsExtendingLifetime.contains(entry) && canStillRemove) {
+                        mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ true)
+                    }
+                }, removeAfterMillis)
+            } else {
+                mExecutor.execute {
+                    mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ false)
+                }
+            }
+            mNotifsExtendingLifetime.add(entry)
+            return true
+        }
+
+        override fun cancelLifetimeExtension(entry: NotificationEntry) {
+            mNotifsExtendingLifetime.remove(entry)
+        }
+    }
+
+    private val mNotifPromoter = object : NotifPromoter(TAG) {
+        override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
+            isCurrentlyShowingHun(entry)
+    }
+
+    val sectioner = object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
+        override fun isInSection(entry: ListEntry): Boolean = isCurrentlyShowingHun(entry)
+
+        override fun getHeaderNodeController(): NodeController? =
+            // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
+            if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null
+    }
+
+    private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener {
+        override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+            if (!isHeadsUp) {
+                mHeadsUpViewBinder.unbindHeadsUpView(entry)
+                endNotifLifetimeExtensionIfExtended(entry)
+            }
+        }
+    }
+
+    private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
+
+    private fun isCurrentlyShowingHun(entry: ListEntry) = mHeadsUpManager.isAlerting(entry.key)
+
+    private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
+        if (mNotifsExtendingLifetime.remove(entry)) {
+            mEndLifetimeExtension?.onEndLifetimeExtension(mLifetimeExtender, entry)
+        }
+    }
+
+    companion object {
+        private const val TAG = "HeadsUpCoordinator"
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 33005b3..733be9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -36,6 +36,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -48,7 +50,8 @@
 import javax.inject.Inject;
 
 /**
- * Filters low priority and privacy-sensitive notifications from the lockscreen.
+ * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
+ * headers on the lockscreen.
  */
 @CoordinatorScope
 public class KeyguardCoordinator implements Coordinator {
@@ -62,6 +65,7 @@
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final HighPriorityProvider mHighPriorityProvider;
+    private final SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
 
     private boolean mHideSilentNotificationsOnLockscreen;
 
@@ -74,7 +78,8 @@
             BroadcastDispatcher broadcastDispatcher,
             StatusBarStateController statusBarStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            HighPriorityProvider highPriorityProvider) {
+            HighPriorityProvider highPriorityProvider,
+            SectionHeaderVisibilityProvider sectionHeaderVisibilityProvider) {
         mContext = context;
         mMainHandler = mainThreadHandler;
         mKeyguardStateController = keyguardStateController;
@@ -83,6 +88,7 @@
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mHighPriorityProvider = highPriorityProvider;
+        mSectionHeaderVisibilityProvider = sectionHeaderVisibilityProvider;
     }
 
     @Override
@@ -214,6 +220,8 @@
     }
 
     private void invalidateListFromFilter(String reason) {
+        mSectionHeaderVisibilityProvider.setSectionHeadersVisible(
+                mStatusBarStateController.getState() != StatusBarState.KEYGUARD);
         mNotifFilter.invalidateList();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 757fb5a..850cb4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import java.io.FileDescriptor
 import java.io.PrintWriter
@@ -63,6 +64,7 @@
 
     private val mCoordinators: MutableList<Coordinator> = ArrayList()
     private val mOrderedSections: MutableList<NotifSectioner> = ArrayList()
+    private val mOrderedComparators: MutableList<NotifComparator> = ArrayList()
 
     /**
      * Creates all the coordinators.
@@ -117,6 +119,9 @@
         mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
         mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
         mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
+
+        // Manually add ordered comparators
+        mOrderedComparators.add(conversationCoordinator.comparator)
     }
 
     /**
@@ -128,6 +133,7 @@
             c.attach(pipeline)
         }
         pipeline.setSections(mOrderedSections)
+        pipeline.setComparators(mOrderedComparators)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ec4e039..35fe0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -37,6 +37,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
@@ -98,6 +99,7 @@
 
     /** How long we can delay a group while waiting for all children to inflate */
     private final long mMaxGroupInflationDelay;
+    private final BindEventManagerImpl mBindEventManager;
 
     @Inject
     public PreparationCoordinator(
@@ -106,7 +108,8 @@
             NotifInflationErrorManager errorManager,
             NotifViewBarn viewBarn,
             NotifUiAdjustmentProvider adjustmentProvider,
-            IStatusBarService service) {
+            IStatusBarService service,
+            BindEventManagerImpl bindEventManager) {
         this(
                 logger,
                 notifInflater,
@@ -114,6 +117,7 @@
                 viewBarn,
                 adjustmentProvider,
                 service,
+                bindEventManager,
                 CHILD_BIND_CUTOFF,
                 MAX_GROUP_INFLATION_DELAY);
     }
@@ -126,6 +130,7 @@
             NotifViewBarn viewBarn,
             NotifUiAdjustmentProvider adjustmentProvider,
             IStatusBarService service,
+            BindEventManagerImpl bindEventManager,
             int childBindCutoff,
             long maxGroupInflationDelay) {
         mLogger = logger;
@@ -136,6 +141,7 @@
         mStatusBarService = service;
         mChildBindCutoff = childBindCutoff;
         mMaxGroupInflationDelay = maxGroupInflationDelay;
+        mBindEventManager = bindEventManager;
     }
 
     @Override
@@ -363,6 +369,7 @@
         mInflatingNotifs.remove(entry);
         mViewBarn.registerViewForEntry(entry, controller);
         mInflationStates.put(entry, STATE_INFLATED);
+        mBindEventManager.notifyViewBound(entry);
         mNotifInflatingFilter.invalidateList();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
new file mode 100644
index 0000000..51bdd00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.ListenerSet
+
+/**
+ * Helper class that allows distributing bind events regardless of the pipeline.
+ *
+ * NOTE: This class isn't ideal; this exposes the concept of view inflation as something that can be
+ * globally registered for. This is built as it is to provide compatibility with patterns developed
+ * for the legacy pipeline. Ideally we'd have functionality that needs to know this information be
+ * handled by events that go through the ViewController itself.
+ */
+open class BindEventManager {
+    protected val listeners = ListenerSet<Listener>()
+
+    /** Register a listener */
+    fun addListener(listener: Listener) =
+        listeners.addIfAbsent(listener)
+
+    /** Deregister a listener */
+    fun removeListener(listener: Listener) =
+        listeners.remove(listener)
+
+    /** Listener interface for view bind events */
+    fun interface Listener {
+        fun onViewBound(entry: NotificationEntry)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
new file mode 100644
index 0000000..9d5b859
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager.Listener
+import javax.inject.Inject
+
+/**
+ * Helper class that allows distributing bind events regardless of the pipeline.
+ */
+@SysUISingleton
+class BindEventManagerImpl @Inject constructor() : BindEventManager() {
+    /** Emit the [Listener.onViewBound] event to all registered listeners. */
+    fun notifyViewBound(entry: NotificationEntry) =
+        listeners.forEach { listener -> listener.onViewBound(entry) }
+
+    /** Initialize this for the legacy pipeline. */
+    fun attachToLegacyPipeline(notificationEntryManager: NotificationEntryManager) {
+        notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
+            override fun onEntryInflated(entry: NotificationEntry) = notifyViewBound(entry)
+            override fun onEntryReinflated(entry: NotificationEntry) = notifyViewBound(entry)
+        })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index f50038c..3bd91b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -111,7 +111,7 @@
         String group = entry.getSbn().getGroup();
         if (mNotifCollection.isOnlyChildInGroup(entry)) {
             NotificationEntry summary = mNotifCollection.getGroupSummary(group);
-            if (summary != null && summary.isClearable()) return summary;
+            if (summary != null && summary.isDismissable()) return summary;
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 5993f1d..cd2affe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar.notification.collection.legacy;
 
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
+
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -34,9 +38,9 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Compile;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.io.FileDescriptor;
@@ -49,6 +53,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.TreeSet;
+import java.util.function.Function;
 
 import javax.inject.Inject;
 
@@ -69,8 +74,8 @@
         Dumpable {
 
     private static final String TAG = "NotifGroupManager";
-    private static final boolean DEBUG = StatusBar.DEBUG;
-    private static final boolean SPEW = StatusBar.SPEW;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     /**
      * The maximum amount of time (in ms) between the posting of notifications that can be
      * considered part of the same update batch.
@@ -79,10 +84,9 @@
     private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
     private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
             new ArraySet<>();
-    private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
     private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
     private final Optional<Bubbles> mBubblesOptional;
-    private final EventBuffer mEventBuffer = new EventBuffer();
+    private final GroupEventDispatcher mEventDispatcher = new GroupEventDispatcher(mGroupMap::get);
     private int mBarState = -1;
     private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
     private HeadsUpManager mHeadsUpManager;
@@ -105,7 +109,7 @@
      * Add a listener for changes to groups.
      */
     public void registerGroupChangeListener(OnGroupChangeListener listener) {
-        mGroupChangeListeners.add(listener);
+        mEventDispatcher.registerGroupChangeListener(listener);
     }
 
     @Override
@@ -156,13 +160,15 @@
      */
     public void onEntryRemoved(NotificationEntry removed) {
         if (SPEW) {
-            Log.d(TAG, "onEntryRemoved: entry=" + removed);
+            Log.d(TAG, "onEntryRemoved: entry=" + logKey(removed));
         }
+        mEventDispatcher.openBufferScope();
         onEntryRemovedInternal(removed, removed.getSbn());
         StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
         if (oldSbn != null) {
             updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
         }
+        mEventDispatcher.closeBufferScope();
     }
 
     /**
@@ -190,7 +196,8 @@
             return;
         }
         if (SPEW) {
-            Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
+            Log.d(TAG, "onEntryRemovedInternal: entry=" + logKey(removed)
+                    + " group=" + logGroupKey(group));
         }
         if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
             group.children.remove(removed.getKey());
@@ -201,9 +208,7 @@
         if (group.children.isEmpty()) {
             if (group.summary == null) {
                 mGroupMap.remove(groupKey);
-                for (OnGroupChangeListener listener : mGroupChangeListeners) {
-                    listener.onGroupRemoved(group, groupKey);
-                }
+                mEventDispatcher.notifyGroupRemoved(group);
             }
         }
     }
@@ -213,10 +218,12 @@
      */
     public void onEntryAdded(final NotificationEntry added) {
         if (SPEW) {
-            Log.d(TAG, "onEntryAdded: entry=" + added);
+            Log.d(TAG, "onEntryAdded: entry=" + logKey(added));
         }
+        mEventDispatcher.openBufferScope();
         updateIsolation(added);
         onEntryAddedInternal(added);
+        mEventDispatcher.closeBufferScope();
     }
 
     private void onEntryAddedInternal(final NotificationEntry added) {
@@ -230,19 +237,17 @@
         if (group == null) {
             group = new NotificationGroup(groupKey);
             mGroupMap.put(groupKey, group);
-
-            for (OnGroupChangeListener listener : mGroupChangeListeners) {
-                listener.onGroupCreated(group, groupKey);
-            }
+            mEventDispatcher.notifyGroupCreated(group);
         }
         if (SPEW) {
-            Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
+            Log.d(TAG, "onEntryAddedInternal: entry=" + logKey(added)
+                    + " group=" + logGroupKey(group));
         }
         if (isGroupChild) {
             NotificationEntry existing = group.children.get(added.getKey());
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
-                Log.wtf(TAG, "Inconsistent entries found with the same key " + added.getKey()
+                Log.wtf(TAG, "Inconsistent entries found with the same key " + logKey(added)
                         + "existing removed: " + existing.isRowRemoved()
                         + (existingThrowable != null
                                 ? Log.getStackTraceString(existingThrowable) + "\n" : "")
@@ -262,9 +267,7 @@
                 for (NotificationEntry child : childrenCopy) {
                     onEntryBecomingChild(child);
                 }
-                for (OnGroupChangeListener listener : mGroupChangeListeners) {
-                    listener.onGroupCreatedFromChildren(group);
-                }
+                mEventDispatcher.notifyGroupsChanged();
             }
         }
     }
@@ -323,29 +326,26 @@
         boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
         boolean suppressionChanged = prevSuppressed != group.suppressed;
         if (alertOverrideChanged || suppressionChanged) {
-            if (DEBUG && alertOverrideChanged) {
-                Log.d(TAG, "updateSuppression: alertOverride was=" + prevAlertOverride
-                        + " now=" + group.alertOverride + " group:\n" + group);
-            }
-            if (DEBUG && suppressionChanged) {
-                Log.d(TAG,
-                        "updateSuppression: suppressed changed to " + group.suppressed
-                                + " group:\n" + group);
-            }
-            if (!mIsUpdatingUnchangedGroup) {
+            if (DEBUG) {
+                Log.d(TAG, "updateSuppression:"
+                        + " willNotifyListeners=" + !mIsUpdatingUnchangedGroup
+                        + " changes for group:\n" + group);
                 if (alertOverrideChanged) {
-                    mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
+                    Log.d(TAG, "updateSuppression: alertOverride was=" + logKey(prevAlertOverride)
+                            + " now=" + logKey(group.alertOverride));
                 }
                 if (suppressionChanged) {
-                    for (OnGroupChangeListener listener : mGroupChangeListeners) {
-                        listener.onGroupSuppressionChanged(group, group.suppressed);
-                    }
+                    Log.d(TAG, "updateSuppression: suppressed changed to " + group.suppressed);
                 }
-                mEventBuffer.notifyGroupsChanged();
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, group + " did not notify listeners of above change(s)");
-                }
+            }
+            if (alertOverrideChanged) {
+                mEventDispatcher.notifyAlertOverrideChanged(group, prevAlertOverride);
+            }
+            if (suppressionChanged) {
+                mEventDispatcher.notifySuppressedChanged(group);
+            }
+            if (!mIsUpdatingUnchangedGroup) {
+                mEventDispatcher.notifyGroupsChanged();
             }
         }
     }
@@ -369,13 +369,15 @@
         // but which should be alerting (because priority conversations are isolated), find it.
         if (group == null || group.summary == null) {
             if (SPEW) {
-                Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
+                Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary"
+                        + " group=" + logGroupKey(group));
             }
             return null;
         }
         if (isIsolated(group.summary.getKey())) {
             if (SPEW) {
-                Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
+                Log.d(TAG, "getPriorityConversationAlertOverride: isolated group"
+                        + " group=" + logGroupKey(group));
             }
             return null;
         }
@@ -384,9 +386,10 @@
         // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
         // * Only necessary when at least one notification in the group is on a priority channel
         if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
-                != Notification.GROUP_ALERT_SUMMARY) {
+                == Notification.GROUP_ALERT_CHILDREN) {
             if (SPEW) {
-                Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+                Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN"
+                        + " group=" + logGroupKey(group));
             }
             return null;
         }
@@ -396,7 +399,8 @@
         HashMap<String, NotificationEntry> children = getImportantConversations(group);
         if (children == null || children.isEmpty()) {
             if (SPEW) {
-                Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
+                Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations"
+                        + " group=" + logGroupKey(group));
             }
             return null;
         }
@@ -408,8 +412,8 @@
             if (child.getSbn().getNotification().getGroupAlertBehavior()
                     != Notification.GROUP_ALERT_SUMMARY) {
                 if (SPEW) {
-                    Log.d(TAG, "getPriorityConversationAlertOverride: "
-                            + "child != GROUP_ALERT_SUMMARY");
+                    Log.d(TAG, "getPriorityConversationAlertOverride: child != GROUP_ALERT_SUMMARY"
+                            + " group=" + logGroupKey(group));
                 }
                 return null;
             }
@@ -450,13 +454,16 @@
         }
         if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
             if (SPEW) {
-                Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
+                Log.d(TAG, "getPriorityConversationAlertOverride:"
+                        + " result=" + logKey(newestChild)
+                        + " group=" + logGroupKey(group));
             }
             return newestChild;
         }
         if (SPEW) {
-            Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
-                    + newestChild);
+            Log.d(TAG, "getPriorityConversationAlertOverride:"
+                    + " result=null newestChild=" + logKey(newestChild)
+                    + " group=" + logGroupKey(group));
         }
         return null;
     }
@@ -500,7 +507,7 @@
      */
     public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
         if (SPEW) {
-            Log.d(TAG, "onEntryUpdated: entry=" + entry);
+            Log.d(TAG, "onEntryUpdated: entry=" + logKey(entry));
         }
         onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
                 oldNotification.getNotification().isGroupSummary());
@@ -519,6 +526,7 @@
         boolean groupKeysChanged = !oldGroupKey.equals(newGroupKey);
         boolean wasGroupChild = isGroupChild(entry.getKey(), oldIsGroup, oldIsGroupSummary);
         boolean isGroupChild = isGroupChild(entry.getSbn());
+        mEventDispatcher.openBufferScope();
         mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
         if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) {
             onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary);
@@ -529,11 +537,14 @@
             mIsolatedEntries.put(entry.getKey(), entry.getSbn());
             if (groupKeysChanged) {
                 updateSuppression(mGroupMap.get(oldGroupKey));
-                updateSuppression(mGroupMap.get(newGroupKey));
             }
+            // Always update the suppression of the group from which you're isolated, in case
+            // this entry was or now is the alertOverride for that group.
+            updateSuppression(mGroupMap.get(newGroupKey));
         } else if (!wasGroupChild && isGroupChild) {
             onEntryBecomingChild(entry);
         }
+        mEventDispatcher.closeBufferScope();
     }
 
     /**
@@ -796,7 +807,7 @@
      */
     private void isolateNotification(NotificationEntry entry) {
         if (SPEW) {
-            Log.d(TAG, "isolateNotification: entry=" + entry);
+            Log.d(TAG, "isolateNotification: entry=" + logKey(entry));
         }
         // We will be isolated now, so lets update the groups
         onEntryRemovedInternal(entry, entry.getSbn());
@@ -809,9 +820,7 @@
         // When the notification gets added afterwards it is already isolated and therefore
         // it doesn't lead to an update.
         updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey()));
-        for (OnGroupChangeListener listener : mGroupChangeListeners) {
-            listener.onGroupsChanged();
-        }
+        mEventDispatcher.notifyGroupsChanged();
     }
 
     /**
@@ -825,7 +834,7 @@
         // listener may be unable to correctly determine the true state of the group.  By delaying
         // the alertOverride change until after the add phase, we can ensure that listeners only
         // have to handle a consistent state.
-        mEventBuffer.startBuffering();
+        mEventDispatcher.openBufferScope();
         boolean isIsolated = isIsolated(entry.getSbn().getKey());
         if (shouldIsolate(entry)) {
             if (!isIsolated) {
@@ -834,7 +843,7 @@
         } else if (isIsolated) {
             stopIsolatingNotification(entry);
         }
-        mEventBuffer.flushAndStopBuffering();
+        mEventDispatcher.closeBufferScope();
     }
 
     /**
@@ -844,15 +853,13 @@
      */
     private void stopIsolatingNotification(NotificationEntry entry) {
         if (SPEW) {
-            Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
+            Log.d(TAG, "stopIsolatingNotification: entry=" + logKey(entry));
         }
         // not isolated anymore, we need to update the groups
         onEntryRemovedInternal(entry, entry.getSbn());
         mIsolatedEntries.remove(entry.getKey());
         onEntryAddedInternal(entry);
-        for (OnGroupChangeListener listener : mGroupChangeListeners) {
-            listener.onGroupsChanged();
-        }
+        mEventDispatcher.notifyGroupsChanged();
     }
 
     private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
@@ -872,11 +879,11 @@
         pw.println("GroupManagerLegacy state:");
         pw.println("  number of groups: " +  mGroupMap.size());
         for (Map.Entry<String, NotificationGroup>  entry : mGroupMap.entrySet()) {
-            pw.println("\n    key: " + entry.getKey()); pw.println(entry.getValue());
+            pw.println("\n    key: " + logKey(entry.getKey())); pw.println(entry.getValue());
         }
         pw.println("\n    isolated entries: " +  mIsolatedEntries.size());
         for (Map.Entry<String, StatusBarNotification> entry : mIsolatedEntries.entrySet()) {
-            pw.print("      "); pw.print(entry.getKey());
+            pw.print("      "); pw.print(logKey(entry.getKey()));
             pw.print(", "); pw.println(entry.getValue());
         }
     }
@@ -886,6 +893,14 @@
         setStatusBarState(newState);
     }
 
+    /** Get the group key, reformatted for logging, for the (optional) group */
+    public static String logGroupKey(NotificationGroup group) {
+        if (group == null) {
+            return "null";
+        }
+        return logKey(group.groupKey);
+    }
+
     /**
      * A record of a notification being posted, containing the time of the post and the key of the
      * notification entry.  These are stored in a TreeSet by the NotificationGroup and used to
@@ -975,18 +990,35 @@
      * When buffering, instead of notifying the listeners it will set internal state that will allow
      * it to notify listeners of those events later
      */
-    private class EventBuffer {
+    static class GroupEventDispatcher {
+        private final Function<String, NotificationGroup> mGroupMapGetter;
+        private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
         private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
-        private boolean mIsBuffering = false;
+        private final HashMap<String, Boolean> mOldSuppressedByGroup = new HashMap<>();
+        private int mBufferScopeDepth = 0;
         private boolean mDidGroupsChange = false;
 
+        GroupEventDispatcher(Function<String, NotificationGroup> groupMapGetter) {
+            mGroupMapGetter = requireNonNull(groupMapGetter);
+        }
+
+        void registerGroupChangeListener(OnGroupChangeListener listener) {
+            mGroupChangeListeners.add(listener);
+        }
+
+        private boolean isBuffering() {
+            return mBufferScopeDepth > 0;
+        }
+
         void notifyAlertOverrideChanged(NotificationGroup group,
                 NotificationEntry oldAlertOverride) {
-            if (mIsBuffering) {
+            if (isBuffering()) {
                 // The value in this map is the override before the event.  If there is an entry
                 // already in the map, then we are effectively coalescing two events, which means
                 // we need to preserve the original initial value.
-                mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
+                if (!mOldAlertOverrideByGroup.containsKey(group.groupKey)) {
+                    mOldAlertOverrideByGroup.put(group.groupKey, oldAlertOverride);
+                }
             } else {
                 for (OnGroupChangeListener listener : mGroupChangeListeners) {
                     listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
@@ -995,8 +1027,21 @@
             }
         }
 
+        void notifySuppressedChanged(NotificationGroup group) {
+            if (isBuffering()) {
+                // The value in this map is the 'suppressed' before the event.  If there is a value
+                // already in the map, then we are effectively coalescing two events, which means
+                // we need to preserve the original initial value.
+                mOldSuppressedByGroup.putIfAbsent(group.groupKey, !group.suppressed);
+            } else {
+                for (OnGroupChangeListener listener : mGroupChangeListeners) {
+                    listener.onGroupSuppressionChanged(group, group.suppressed);
+                }
+            }
+        }
+
         void notifyGroupsChanged() {
-            if (mIsBuffering) {
+            if (isBuffering()) {
                 mDidGroupsChange = true;
             } else {
                 for (OnGroupChangeListener listener : mGroupChangeListeners) {
@@ -1005,26 +1050,94 @@
             }
         }
 
-        void startBuffering() {
-            mIsBuffering = true;
+        void notifyGroupCreated(NotificationGroup group) {
+            // NOTE: given how this event is used, it doesn't need to be buffered right now
+            final String groupKey = group.groupKey;
+            for (OnGroupChangeListener listener : mGroupChangeListeners) {
+                listener.onGroupCreated(group, groupKey);
+            }
         }
 
-        void flushAndStopBuffering() {
-            // stop buffering so that we can call our own helpers
-            mIsBuffering = false;
+        void notifyGroupRemoved(NotificationGroup group) {
+            // NOTE: given how this event is used, it doesn't need to be buffered right now
+            final String groupKey = group.groupKey;
+            for (OnGroupChangeListener listener : mGroupChangeListeners) {
+                listener.onGroupRemoved(group, groupKey);
+            }
+        }
+
+        void openBufferScope() {
+            mBufferScopeDepth++;
+            if (SPEW) {
+                Log.d(TAG, "openBufferScope: scopeDepth=" + mBufferScopeDepth);
+            }
+        }
+
+        void closeBufferScope() {
+            mBufferScopeDepth--;
+            if (SPEW) {
+                Log.d(TAG, "closeBufferScope: scopeDepth=" + mBufferScopeDepth);
+            }
+            // Flush buffered events if the last buffer scope has closed
+            if (!isBuffering()) {
+                flushBuffer();
+            }
+        }
+
+        private void flushBuffer() {
+            if (SPEW) {
+                Log.d(TAG, "flushBuffer: "
+                        + " suppressed.size=" + mOldSuppressedByGroup.size()
+                        + " alertOverride.size=" + mOldAlertOverrideByGroup.size()
+                        + " mDidGroupsChange=" + mDidGroupsChange);
+            }
+            // alert all group suppressed changes for groups that were not removed
+            for (Map.Entry<String, Boolean> entry : mOldSuppressedByGroup.entrySet()) {
+                NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
+                if (group == null) {
+                    // The group can be null if this suppressed changed before the group was
+                    // permanently removed, meaning that there's no guarantee that listeners will
+                    // that field clear.
+                    if (SPEW) {
+                        Log.d(TAG, "flushBuffer: suppressed:"
+                                + " cannot report for removed group: " + logKey(entry.getKey()));
+                    }
+                    continue;
+                }
+                boolean oldSuppressed = entry.getValue();
+                if (group.suppressed == oldSuppressed) {
+                    // If the final suppressed equals the initial, it means we coalesced two
+                    // events which undid the change, so we can drop it entirely.
+                    if (SPEW) {
+                        Log.d(TAG, "flushBuffer: suppressed:"
+                                + " did not change for group: " + logKey(entry.getKey()));
+                    }
+                    continue;
+                }
+                notifySuppressedChanged(group);
+            }
+            mOldSuppressedByGroup.clear();
             // alert all group alert override changes for groups that were not removed
             for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
-                NotificationGroup group = mGroupMap.get(entry.getKey());
+                NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
                 if (group == null) {
                     // The group can be null if this alertOverride changed before the group was
                     // permanently removed, meaning that there's no guarantee that listeners will
                     // that field clear.
+                    if (SPEW) {
+                        Log.d(TAG, "flushBuffer: alertOverride:"
+                                + " cannot report for removed group: " + entry.getKey());
+                    }
                     continue;
                 }
                 NotificationEntry oldAlertOverride = entry.getValue();
                 if (group.alertOverride == oldAlertOverride) {
                     // If the final alertOverride equals the initial, it means we coalesced two
                     // events which undid the change, so we can drop it entirely.
+                    if (SPEW) {
+                        Log.d(TAG, "flushBuffer: alertOverride:"
+                                + " did not change for group: " + logKey(entry.getKey()));
+                    }
                     continue;
                 }
                 notifyAlertOverrideChanged(group, oldAlertOverride);
@@ -1086,14 +1199,6 @@
                 @Nullable NotificationEntry newAlertOverride) {}
 
         /**
-         * A group of children just received a summary notification and should therefore become
-         * children of it.
-         *
-         * @param group the group created
-         */
-        default void onGroupCreatedFromChildren(NotificationGroup group) {}
-
-        /**
          * The groups have changed. This can happen if the isolation of a child has changes or if a
          * group became suppressed / unsuppressed
          */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
index 3b114bb..8daf8be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
@@ -111,7 +111,7 @@
     public NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) {
         if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
             NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
-            return groupSummary.isClearable() ? groupSummary : null;
+            return groupSummary.isDismissable() ? groupSummary : null;
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
index 0d150ed..f7bbd28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 
@@ -39,5 +41,5 @@
      * @return a negative integer, zero, or a positive integer as the first argument is less than
      *      equal to, or greater than the second (same as standard Comparator<> interface).
      */
-    public abstract int compare(ListEntry o1, ListEntry o2);
+    public abstract int compare(@NonNull ListEntry o1, @NonNull ListEntry o2);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 68a346f..9d56a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.notification.collection.notifcollection;
 
 import android.annotation.NonNull;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 
@@ -115,4 +117,20 @@
      */
     default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
     }
+
+    /**
+     * Called when a notification channel is modified, in response to
+     * {@link NotificationListenerService#onNotificationChannelModified}.
+     *
+     * @param pkgName the package the notification channel belongs to.
+     * @param user the user the notification channel belongs to.
+     * @param channel the channel being modified.
+     * @param modificationType the type of modification that occurred to the channel.
+     */
+    default void onNotificationChannelModified(
+            String pkgName,
+            UserHandle user,
+            NotificationChannel channel,
+            int modificationType) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
index 179e953..e20f0e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.notifcollection
 
+import android.app.NotificationChannel
+import android.os.UserHandle
 import android.service.notification.NotificationListenerService.RankingMap
 import android.service.notification.StatusBarNotification
 import com.android.systemui.statusbar.notification.collection.NotifCollection
@@ -102,3 +104,14 @@
         listener.onRankingApplied()
     }
 }
+
+data class ChannelChangedEvent(
+    val pkgName: String,
+    val user: UserHandle,
+    val channel: NotificationChannel,
+    val modificationType: Int
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onNotificationChannelModified(pkgName, user, channel, modificationType)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
index 2fe3bd6..70ad8a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
@@ -45,7 +45,7 @@
      * called on all lifetime extenders even if earlier ones return true (in other words, multiple
      * lifetime extenders can be extending a notification at the same time).
      */
-    boolean shouldExtendLifetime(@NonNull NotificationEntry entry, @CancellationReason int reason);
+    boolean maybeExtendLifetime(@NonNull NotificationEntry entry, @CancellationReason int reason);
 
     /**
      * Called by the NotifCollection to inform a lifetime extender that its extension of a notif
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt
index 145c1e5..51dab72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt
@@ -73,7 +73,7 @@
 
     final override fun getName(): String = name
 
-    final override fun shouldExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+    final override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
         val shouldExtend = queryShouldExtendLifetime(entry)
         if (debug) {
             Log.d(tag, "$name.shouldExtendLifetime(key=${entry.key}, reason=$reason)" +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
index d16d76a..ab777de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
@@ -77,7 +77,7 @@
         if (needsInitialization) {
             val filter = IntentFilter().apply { addAction(ACTION_SET_NOTIF_DEBUG_MODE) }
             val permission = NOTIF_DEBUG_MODE_PERMISSION
-            context.registerReceiver(mReceiver, filter, permission, null)
+            context.registerReceiver(mReceiver, filter, permission, null, Context.RECEIVER_EXPORTED)
             Log.d(TAG, "Registered: $mReceiver")
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
new file mode 100644
index 0000000..f949af0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
+import javax.inject.Inject
+
+@SysUISingleton
+class MediaContainerController @Inject constructor(
+    private val layoutInflater: LayoutInflater
+) : NodeController {
+
+    override val nodeLabel = "MediaContainer"
+    var mediaContainerView: MediaContainerView? = null
+        private set
+
+    fun reinflateView(parent: ViewGroup) {
+        var oldPos = -1
+        mediaContainerView?.let { _view ->
+            _view.removeFromTransientContainer()
+            if (_view.parent === parent) {
+                oldPos = parent.indexOfChild(_view)
+                parent.removeView(_view)
+            }
+        }
+        val inflated = layoutInflater.inflate(
+                R.layout.keyguard_media_container,
+                parent,
+                false /* attachToRoot */)
+                as MediaContainerView
+        if (oldPos != -1) {
+            parent.addView(inflated, oldPos)
+        }
+        mediaContainerView = inflated
+    }
+
+    override val view: View
+        get() = mediaContainerView!!
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
index 289dacb..26ba12c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
@@ -41,17 +41,29 @@
 
     fun getChildCount(): Int = 0
 
+    /** Called to add a child to this view */
     fun addChildAt(child: NodeController, index: Int) {
         throw RuntimeException("Not supported")
     }
 
+    /** Called to move one of this view's current children to a new position */
     fun moveChildTo(child: NodeController, index: Int) {
         throw RuntimeException("Not supported")
     }
 
+    /** Called to remove one of this view's current children */
     fun removeChild(child: NodeController, isTransfer: Boolean) {
         throw RuntimeException("Not supported")
     }
+
+    /** Called when this view has been added */
+    fun onViewAdded() {}
+
+    /** Called when this view has been moved */
+    fun onViewMoved() {}
+
+    /** Called when this view has been removed */
+    fun onViewRemoved() {}
 }
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index f59e4ab..607500e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.render
 
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,6 +34,9 @@
  * need to present in the shade, notably the section headers.
  */
 class NodeSpecBuilder(
+    private val mediaContainerController: MediaContainerController,
+    private val sectionsFeatureManager: NotificationSectionsFeatureManager,
+    private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
     private val viewBarn: NotifViewBarn
 ) {
     fun buildNodeSpec(
@@ -39,8 +44,16 @@
         notifList: List<ListEntry>
     ): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") {
         val root = NodeSpecImpl(null, rootController)
+
+        // The media container should be added as the first child of the root node
+        // TODO: Perhaps the node spec building process should be more of a pipeline of its own?
+        if (sectionsFeatureManager.isMediaControlsEnabled()) {
+            root.children.add(NodeSpecImpl(root, mediaContainerController))
+        }
+
         var currentSection: NotifSection? = null
         val prevSections = mutableSetOf<NotifSection?>()
+        val showHeaders = sectionHeaderVisibilityProvider.sectionHeadersVisible
 
         for (entry in notifList) {
             val section = entry.section!!
@@ -51,7 +64,7 @@
 
             // If this notif begins a new section, first add the section's header view
             if (section != currentSection) {
-                if (section.headerController != currentSection?.headerController) {
+                if (section.headerController != currentSection?.headerController && showHeaders) {
                     section.headerController?.let { headerController ->
                         root.children.add(NodeSpecImpl(root, headerController))
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
index a1800ed..4de8e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
@@ -40,6 +40,7 @@
 
     override fun addChildAt(child: NodeController, index: Int) {
         listContainer.addContainerViewAt(child.view, index)
+        listContainer.onNotificationViewUpdateFinished()
     }
 
     override fun moveChildTo(child: NodeController, index: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 8c15647..2c9508e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -60,7 +60,7 @@
     override fun reinflateView(parent: ViewGroup) {
         var oldPos = -1
         _view?.let { _view ->
-            _view.transientContainer?.removeView(_view)
+            _view.removeFromTransientContainer()
             if (_view.parent === parent) {
                 oldPos = parent.indexOfChild(_view)
                 parent.removeView(_view)
@@ -94,6 +94,10 @@
         _view?.setOnClearAllClickListener(listener)
     }
 
+    override fun onViewAdded() {
+        headerView?.isContentVisible = true
+    }
+
     override val view: View
         get() = _view!!
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 6d4ae4b..28cd285 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -215,13 +215,16 @@
 
     fun addChildAt(child: ShadeNode, index: Int) {
         controller.addChildAt(child.controller, index)
+        child.controller.onViewAdded()
     }
 
     fun moveChildTo(child: ShadeNode, index: Int) {
         controller.moveChildTo(child.controller, index)
+        child.controller.onViewMoved()
     }
 
     fun removeChild(child: ShadeNode, isTransfer: Boolean) {
         controller.removeChild(child.controller, isTransfer)
+        child.controller.onViewRemoved()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 1a8d720..4847072 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -18,28 +18,36 @@
 
 import android.content.Context
 import android.view.View
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.util.traceSection
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
 /**
  * Responsible for building and applying the "shade node spec": the list (tree) of things that
  * currently populate the notification shade.
  */
-class ShadeViewManager constructor(
+class ShadeViewManager @AssistedInject constructor(
     context: Context,
-    listContainer: NotificationListContainer,
-    private val stackController: NotifStackController,
+    @Assisted listContainer: NotificationListContainer,
+    @Assisted private val stackController: NotifStackController,
+    mediaContainerController: MediaContainerController,
+    featureManager: NotificationSectionsFeatureManager,
+    sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
     logger: ShadeViewDifferLogger,
     private val viewBarn: NotifViewBarn
 ) {
     // We pass a shim view here because the listContainer may not actually have a view associated
     // with it and the differ never actually cares about the root node's view.
     private val rootController = RootNodeController(listContainer, View(context))
-    private val specBuilder = NodeSpecBuilder(viewBarn)
+    private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager,
+            sectionHeaderVisibilityProvider, viewBarn)
     private val viewDiffer = ShadeViewDiffer(rootController, logger)
 
     /** Method for attaching this manager to the pipeline. */
@@ -65,16 +73,10 @@
     }
 }
 
-class ShadeViewManagerFactory @Inject constructor(
-    private val context: Context,
-    private val logger: ShadeViewDifferLogger,
-    private val viewBarn: NotifViewBarn
-) {
-    fun create(listContainer: NotificationListContainer, stackController: NotifStackController) =
-        ShadeViewManager(
-            context,
-            listContainer,
-            stackController,
-            logger,
-            viewBarn)
+@AssistedFactory
+interface ShadeViewManagerFactory {
+    fun create(
+        listContainer: NotificationListContainer,
+        stackController: NotifStackController
+    ): ShadeViewManager
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index f1cba34..05c40b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -50,6 +50,8 @@
 import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
 import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
@@ -358,5 +360,9 @@
 
     /** */
     @Binds
+    BindEventManager bindBindEventManagerImpl(BindEventManagerImpl bindEventManagerImpl);
+
+    /** */
+    @Binds
     NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 38f3c39..48f2daf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
 import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
 import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
@@ -76,6 +77,7 @@
     private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
     private val deviceProvisionedController: DeviceProvisionedController,
     private val notificationRowBinder: NotificationRowBinderImpl,
+    private val bindEventManagerImpl: BindEventManagerImpl,
     private val remoteInputUriController: RemoteInputUriController,
     private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>,
     private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
@@ -131,6 +133,7 @@
             targetSdkResolver.initialize(entryManager)
             remoteInputUriController.attach(entryManager)
             groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get())
+            bindEventManagerImpl.attachToLegacyPipeline(entryManager)
             headsUpManager.addListener(groupManagerLegacy.get())
             headsUpManager.addListener(groupAlertTransferHelper)
             headsUpController.attach(entryManager, headsUpManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 433d5e1..2b4bc91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Compile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,8 +53,8 @@
 @SysUISingleton
 public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
     private static final String TAG = "InterruptionStateProvider";
-    private static final boolean DEBUG = true; //false;
-    private static final boolean DEBUG_HEADS_UP = true;
+    private static final boolean DEBUG = Compile.IS_DEBUG;
+    private static final boolean DEBUG_HEADS_UP = Compile.IS_DEBUG;
     private static final boolean ENABLE_HEADS_UP = true;
     private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9e8200b..dc39413 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.util.Compile;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -65,7 +66,7 @@
  */
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     /** The minimum delay in ms between reports of notification visibility. */
     private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
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 f898470..dbd22db 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
@@ -115,6 +115,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.Compile;
 import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.wmshell.BubblesManager;
 
@@ -136,11 +137,11 @@
         implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
         NotificationFadeAware.FadeOptimizedNotification {
 
-    private static final boolean DEBUG = false;
+    private static final String TAG = "ExpandableNotifRow";
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
     private static final int MENU_VIEW_INDEX = 0;
-    private static final String TAG = "ExpandableNotifRow";
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
     private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
 
@@ -1462,7 +1463,7 @@
     public void performDismiss(boolean fromAccessibility) {
         Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
         dismiss(fromAccessibility);
-        if (mEntry.isClearable()) {
+        if (mEntry.isDismissable()) {
             if (mOnUserInteractionCallback != null) {
                 mOnUserInteractionCallback.onDismiss(mEntry, REASON_CANCEL,
                         mOnUserInteractionCallback.getGroupSummaryToDismiss(mEntry));
@@ -2673,9 +2674,18 @@
     /**
      * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
      *         otherwise some state might not be updated. To request about the general clearability
-     *         see {@link NotificationEntry#isClearable()}.
+     *         see {@link NotificationEntry#isDismissable()}.
      */
     public boolean canViewBeDismissed() {
+        return mEntry.isDismissable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+    }
+
+    /**
+     * @return Whether this view is allowed to be cleared with clear all. Only valid for visible
+     * notifications as otherwise some state might not be updated. To request about the general
+     * clearability see {@link NotificationEntry#isClearable()}.
+     */
+    public boolean canViewBeCleared() {
         return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index b28fb58..46efef6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -268,6 +268,18 @@
     }
 
     @Override
+    public void onViewAdded() {
+    }
+
+    @Override
+    public void onViewMoved() {
+    }
+
+    @Override
+    public void onViewRemoved() {
+    }
+
+    @Override
     public int getChildCount() {
         final List<ExpandableNotificationRow> mChildren = mView.getAttachedChildren();
         return mChildren != null ? mChildren.size() : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ab36da5..6eff799 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -520,31 +520,56 @@
     }
 
     /**
+     * Called when removing a view from its transient container, such as at the end of an animation.
+     * Generally, when operating on ExpandableView instances, this should be used rather than
+     * {@link ExpandableView#removeTransientView(View)} to ensure that the
+     * {@link #getTransientContainer() transient container} is correctly reset.
+     */
+    public void removeFromTransientContainer() {
+        final ViewGroup transientContainer = getTransientContainer();
+        if (transientContainer == null) {
+            return;
+        }
+        final ViewParent parent = getParent();
+        if (parent != transientContainer) {
+            Log.w(TAG, "Expandable view " + this
+                    + " has transient container " + transientContainer
+                    + " but different parent " + parent);
+            setTransientContainer(null);
+            return;
+        }
+        transientContainer.removeTransientView(this);
+        setTransientContainer(null);
+    }
+
+    /**
      * Called before adding this view to a group, which would always throw an exception if this view
-     * has a parent, so clean up the transient container and throw an exception if the parent isn't
-     * a transient container.  Provide as much detail in the event of a crash as possible.
+     * has a different parent, so clean up the transient container and throw an exception if the
+     * parent isn't a transient container.  Provide as much detail as possible in the crash.
      */
     public void removeFromTransientContainerForAdditionTo(ViewGroup newParent) {
         final ViewParent parent = getParent();
-        if (parent == null) {
-            // If this view has no parent, the add will succeed, so do nothing.
+        final ViewGroup transientContainer = getTransientContainer();
+        if (parent == null || parent == newParent) {
+            // If this view's current parent is null or the same as the new parent, the add will
+            // succeed as long as it's a true child, so just make sure the view isn't transient.
+            removeFromTransientContainer();
             return;
         }
-        ViewGroup transientContainer = getTransientContainer();
         if (transientContainer == null) {
-            throw new IllegalStateException(
-                    "Can't add view " + this + " to container " + newParent + "; current parent "
-                            + parent + " is not a transient container");
+            throw new IllegalStateException("Can't add view " + this + " to container " + newParent
+                    + "; current parent " + parent + " is not a transient container");
         }
         if (transientContainer != parent) {
-            throw new IllegalStateException(
-                    "Expandable view " + this + " has transient container " + transientContainer
-                            + " which is not the same as its parent " + parent);
+            // Crash with details before addView() crashes without any; the view is being added
+            // to a different parent, and the transient container isn't the parent, so we can't
+            // even (safely) clean that up.
+            throw new IllegalStateException("Expandable view " + this
+                    + " has transient container " + transientContainer
+                    + " but different parent " + parent);
         }
-        if (parent != newParent) {
-            Log.w(TAG, "Moving view " + this + " from transient container "
-                    + transientContainer + " to parent " + newParent);
-        }
+        Log.w(TAG, "Removing view " + this + " from transient container "
+                + transientContainer + " in preparation for moving to parent " + newParent);
         transientContainer.removeTransientView(this);
         setTransientContainer(null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index c0bafb7..4893490 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -48,11 +48,12 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.Compile;
 
 public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
 
     private static final String TAG = "FeedbackInfo";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     private NotificationGuts mGutsContainer;
     private NotificationListenerService.Ranking mRanking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index e8e6e31..0b6d759 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -32,7 +32,7 @@
 import java.io.PrintWriter;
 
 public class FooterView extends StackScrollerDecorView {
-    private FooterViewButton mDismissButton;
+    private FooterViewButton mClearAllButton;
     private FooterViewButton mManageButton;
     private boolean mShowHistory;
 
@@ -57,16 +57,16 @@
             pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
             pw.println("manageButton showHistory: " + mShowHistory);
             pw.println("manageButton visibility: "
-                    + DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
+                    + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
             pw.println("dismissButton visibility: "
-                    + DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
+                    + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
         });
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mDismissButton = (FooterViewButton) findSecondaryView();
+        mClearAllButton = (FooterViewButton) findSecondaryView();
         mManageButton = findViewById(R.id.manage_text);
     }
 
@@ -74,8 +74,8 @@
         mManageButton.setOnClickListener(listener);
     }
 
-    public void setDismissButtonClickListener(OnClickListener listener) {
-        mDismissButton.setOnClickListener(listener);
+    public void setClearAllButtonClickListener(OnClickListener listener) {
+        mClearAllButton.setOnClickListener(listener);
     }
 
     public boolean isOnEmptySpace(float touchX, float touchY) {
@@ -106,8 +106,8 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateColors();
-        mDismissButton.setText(R.string.clear_all_notifications_text);
-        mDismissButton.setContentDescription(
+        mClearAllButton.setText(R.string.clear_all_notifications_text);
+        mClearAllButton.setContentDescription(
                 mContext.getString(R.string.accessibility_clear_all));
         showHistory(mShowHistory);
     }
@@ -118,8 +118,8 @@
     public void updateColors() {
         Resources.Theme theme = mContext.getTheme();
         int textColor = getResources().getColor(R.color.notif_pill_text, theme);
-        mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
-        mDismissButton.setTextColor(textColor);
+        mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        mClearAllButton.setTextColor(textColor);
         mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
         mManageButton.setTextColor(textColor);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index ab78d19..6abfee9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+import com.android.systemui.util.Compile;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -43,8 +44,9 @@
  */
 public class NotificationBlockingHelperManager {
     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
-    private static final boolean DEBUG = false;
     private static final String TAG = "BlockingHelper";
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG_ALWAYS_SHOW = false;
 
     private final Context mContext;
     private final NotificationGutsManager mNotificationGutsManager;
@@ -98,7 +100,7 @@
         // - The dismissed row is a valid group (>1 or 0 children from the same channel)
         // or the only child in the group
         final NotificationEntry entry = row.getEntry();
-        if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
+        if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG_ALWAYS_SHOW)
                 && mIsShadeExpanded
                 && !row.getIsNonblockable()
                 && ((!row.isChildInGroup() || mGroupMembershipManager.isOnlyChildInGroup(entry))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 4dec1f1..9cb5dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -61,6 +61,7 @@
 import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.Compile;
 import com.android.systemui.wmshell.BubblesManager;
 
 import java.io.FileDescriptor;
@@ -77,7 +78,7 @@
 public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
 
     private static final String TAG = "NotificationContentView";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
     public static final int VISIBLE_TYPE_CONTRACTED = 0;
     public static final int VISIBLE_TYPE_EXPANDED = 1;
     public static final int VISIBLE_TYPE_HEADSUP = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 9d599cb..d0fb416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -57,9 +57,6 @@
 public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
         ExpandableNotificationRow.LayoutListener {
 
-    private static final boolean DEBUG = false;
-    private static final String TAG = "swipe";
-
     // Notification must be swiped at least this fraction of a single menu item to show menu
     private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
     private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 9c755e9..3cdaa9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -105,6 +105,9 @@
                 runAfter.run();
             };
             setViewVisible(mContent, visible, animate, endRunnable);
+        } else if (runAfter != null) {
+            // Execute the runAfter runnable immediately if there's no animation to perform.
+            runAfter.run();
         }
 
         if (!mContentAnimating) {
@@ -228,7 +231,7 @@
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener) {
         // TODO: Use duration
-        setContentVisible(false);
+        setContentVisible(false, true /* animate */, onFinishedRunnable);
         return 0;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index e658468..7dc2e19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -57,7 +57,7 @@
     private int mTopPadding;
     private boolean mShadeExpanded;
     private float mMaxHeadsUpTranslation;
-    private boolean mDismissAllInProgress;
+    private boolean mClearAllInProgress;
     private int mLayoutMinHeight;
     private int mLayoutMaxHeight;
     private NotificationShelf mShelf;
@@ -384,12 +384,12 @@
         return mMaxHeadsUpTranslation;
     }
 
-    public void setDismissAllInProgress(boolean dismissAllInProgress) {
-        mDismissAllInProgress = dismissAllInProgress;
+    public void setClearAllInProgress(boolean clearAllInProgress) {
+        mClearAllInProgress = clearAllInProgress;
     }
 
-    public boolean isDismissAllInProgress() {
-        return mDismissAllInProgress;
+    public boolean isClearAllInProgress() {
+        return mClearAllInProgress;
     }
 
     public void setLayoutMinHeight(int layoutMinHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
new file mode 100644
index 0000000..c9a0f6c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+
+/**
+ * Root view to insert Lock screen media controls into the notification stack.
+ */
+public class MediaContainerView extends ExpandableView {
+
+    public MediaContainerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public long performRemoveAnimation(long duration, long delay, float translationDirection,
+            boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
+        return 0;
+    }
+
+    @Override
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
+        // No animation, it doesn't need it, this would be local
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
deleted file mode 100644
index 0247a99..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-/**
- * Root view to insert Lock screen media controls into the notification stack.
- */
-public class MediaHeaderView extends ExpandableView {
-
-    public MediaHeaderView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public long performRemoveAnimation(long duration, long delay, float translationDirection,
-            boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
-            AnimatorListenerAdapter animationListener) {
-        return 0;
-    }
-
-    @Override
-    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
-        // No animation, it doesn't need it, this would be local
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 464fd06..b589d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -45,7 +45,7 @@
     private ExpandableNotificationRow mTrackedHeadsUp;
     private float mAppearFraction;
     private boolean mRoundForPulsingViews;
-    private boolean mIsDismissAllInProgress;
+    private boolean mIsClearAllInProgress;
 
     private ExpandableView mSwipedView = null;
     private ExpandableView mViewBeforeSwipedView = null;
@@ -156,8 +156,8 @@
         }
     }
 
-    void setDismissAllInProgress(boolean isClearingAll) {
-        mIsDismissAllInProgress = isClearingAll;
+    void setClearAllInProgress(boolean isClearingAll) {
+        mIsClearAllInProgress = isClearingAll;
     }
 
     private float getRoundnessFraction(ExpandableView view, boolean top) {
@@ -170,8 +170,8 @@
             return 1f;
         }
         if (view instanceof ExpandableNotificationRow
-                && ((ExpandableNotificationRow) view).canViewBeDismissed()
-                && mIsDismissAllInProgress) {
+                && ((ExpandableNotificationRow) view).canViewBeCleared()
+                && mIsClearAllInProgress) {
             return 1.0f;
         }
         if ((view.isPinned()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 88198f3..b02dc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -16,17 +16,15 @@
 package com.android.systemui.statusbar.notification.stack
 
 import android.annotation.ColorInt
-import android.annotation.LayoutRes
 import android.util.Log
-import android.view.LayoutInflater
 import android.view.View
 import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
 import com.android.systemui.media.KeyguardMediaController
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager
 import com.android.systemui.statusbar.notification.dagger.AlertingHeader
@@ -60,6 +58,7 @@
     private val sectionsFeatureManager: NotificationSectionsFeatureManager,
     private val logger: NotificationSectionsLogger,
     private val notifPipelineFlags: NotifPipelineFlags,
+    private val mediaContainerController: MediaContainerController,
     @IncomingHeader private val incomingHeaderController: SectionHeaderController,
     @PeopleHeader private val peopleHeaderController: SectionHeaderController,
     @AlertingHeader private val alertingHeaderController: SectionHeaderController,
@@ -68,7 +67,7 @@
 
     private val configurationListener = object : ConfigurationController.ConfigurationListener {
         override fun onLocaleListChanged() {
-            reinflateViews(LayoutInflater.from(parent.context))
+            reinflateViews()
         }
     }
 
@@ -91,39 +90,19 @@
     val peopleHeaderView: SectionHeaderView?
         get() = peopleHeaderController.headerView
 
-    @get:VisibleForTesting
-    var mediaControlsView: MediaHeaderView? = null
-        private set
+    @VisibleForTesting
+    val mediaControlsView: MediaContainerView?
+        get() = mediaContainerController.mediaContainerView
 
     /** Must be called before use.  */
-    fun initialize(parent: NotificationStackScrollLayout, layoutInflater: LayoutInflater) {
+    fun initialize(parent: NotificationStackScrollLayout) {
         check(!initialized) { "NotificationSectionsManager already initialized" }
         initialized = true
         this.parent = parent
-        reinflateViews(layoutInflater)
+        reinflateViews()
         configurationController.addCallback(configurationListener)
     }
 
-    private fun <T : ExpandableView> reinflateView(
-        view: T?,
-        layoutInflater: LayoutInflater,
-        @LayoutRes layoutResId: Int
-    ): T {
-        var oldPos = -1
-        view?.let {
-            view.transientContainer?.removeView(view)
-            if (view.parent === parent) {
-                oldPos = parent.indexOfChild(view)
-                parent.removeView(view)
-            }
-        }
-        val inflated = layoutInflater.inflate(layoutResId, parent, false) as T
-        if (oldPos != -1) {
-            parent.addView(inflated, oldPos)
-        }
-        return inflated
-    }
-
     fun createSectionsForBuckets(): Array<NotificationSection> =
             sectionsFeatureManager.getNotificationBuckets()
                     .map { NotificationSection(parent, it) }
@@ -132,13 +111,12 @@
     /**
      * Reinflates the entire notification header, including all decoration views.
      */
-    fun reinflateViews(layoutInflater: LayoutInflater) {
+    fun reinflateViews() {
         silentHeaderController.reinflateView(parent)
         alertingHeaderController.reinflateView(parent)
         peopleHeaderController.reinflateView(parent)
         incomingHeaderController.reinflateView(parent)
-        mediaControlsView =
-                reinflateView(mediaControlsView, layoutInflater, R.layout.keyguard_media_header)
+        mediaContainerController.reinflateView(parent)
         keyguardMediaController.attachSinglePaneContainer(mediaControlsView)
     }
 
@@ -215,8 +193,7 @@
                             // TODO: We should really cancel the active animations here. This will
                             //  happen automatically when the view's intro animation starts, but
                             //  it's a fragile link.
-                            header.transientContainer?.removeTransientView(header)
-                            header.transientContainer = null
+                            header.removeFromTransientContainer()
                             parent.addView(header, target)
                         } else {
                             parent.changeViewPosition(header, target)
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 3e9ce25..90f5179 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
@@ -132,6 +132,7 @@
 
     public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
     private static final String TAG = "StackScroller";
+    private static final boolean SPEW = Log.isLoggable(TAG, Log.VERBOSE);
 
     // Delay in milli-seconds before shade closes for clear all.
     private final int DELAY_BEFORE_SHADE_CLOSE = 200;
@@ -255,8 +256,8 @@
     private boolean mIsCurrentUserSetup;
     protected FooterView mFooterView;
     protected EmptyShadeView mEmptyShadeView;
-    private boolean mDismissAllInProgress;
-    private FooterDismissListener mFooterDismissListener;
+    private boolean mClearAllInProgress;
+    private FooterClearAllListener mFooterClearAllListener;
     private boolean mFlingAfterUpEvent;
 
     /**
@@ -439,8 +440,8 @@
     private int mQsScrollBoundaryPosition;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private final Rect mTmpRect = new Rect();
-    private DismissListener mDismissListener;
-    private DismissAllAnimationListener mDismissAllAnimationListener;
+    private ClearAllListener mClearAllListener;
+    private ClearAllAnimationListener mClearAllAnimationListener;
     private ShadeController mShadeController;
     private Consumer<Boolean> mOnStackYChanged;
 
@@ -574,7 +575,7 @@
         mScreenOffAnimationController =
                 Dependency.get(ScreenOffAnimationController.class);
         updateSplitNotificationShade();
-        mSectionsManager.initialize(this, LayoutInflater.from(context));
+        mSectionsManager.initialize(this);
         mSections = mSectionsManager.createSectionsForBuckets();
 
         mAmbientState = Dependency.get(AmbientState.class);
@@ -665,7 +666,7 @@
         inflateFooterView();
         inflateEmptyShadeView();
         updateFooter();
-        mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
+        mSectionsManager.reinflateViews();
     }
 
     public void setIsRemoteInputActive(boolean isActive) {
@@ -1207,7 +1208,7 @@
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void clampScrollPosition() {
         int scrollRange = getScrollRange();
-        if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) {
+        if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
             boolean animateStackY = false;
             if (scrollRange < getScrollAmountToScrollBoundary()
                     && mAnimateStackYForContentHeightChange) {
@@ -1729,7 +1730,7 @@
              return;
         }
         mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
-                true /* isDismissAll */);
+                true /* isClearAll */);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2228,7 +2229,7 @@
                 height += viewHeight;
 
                 numShownItems++;
-                if (viewHeight > 0 || !(expandableView instanceof MediaHeaderView)) {
+                if (viewHeight > 0 || !(expandableView instanceof MediaContainerView)) {
                     // Only count the media as a notification if it has a positive height.
                     numShownNotifs++;
                 }
@@ -3143,6 +3144,13 @@
             AnimationEvent event = new AnimationEvent(row, type);
             event.headsUpFromBottom = onBottom;
             mAnimationEvents.add(event);
+            if (SPEW) {
+                Log.v(TAG, "Generating HUN animation event: "
+                        + " isHeadsUp=" + isHeadsUp
+                        + " type=" + type
+                        + " onBottom=" + onBottom
+                        + " row=" + row.getEntry().getKey());
+            }
         }
         mHeadsUpChangeAnimations.clear();
         mAddedHeadsUpChildren.clear();
@@ -3202,7 +3210,7 @@
                     ignoreChildren = false;
                 }
                 childWasSwipedOut |= isFullySwipedOut(row);
-            } else if (child instanceof MediaHeaderView) {
+            } else if (child instanceof MediaContainerView) {
                 childWasSwipedOut = true;
             }
             if (!childWasSwipedOut) {
@@ -3213,10 +3221,7 @@
                     // Clean up any potential transient views if the child has already been swiped
                     // out, as we won't be animating it further (due to its height already being
                     // clipped to 0.
-                    ViewGroup transientContainer = child.getTransientContainer();
-                    if (transientContainer != null) {
-                        transientContainer.removeTransientView(child);
-                    }
+                    child.removeFromTransientContainer();
                 }
             }
             int animationType = childWasSwipedOut
@@ -3933,7 +3938,11 @@
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
         while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
-            viewGroup.removeTransientView(viewGroup.getTransientView(0));
+            final View transientView = viewGroup.getTransientView(0);
+            viewGroup.removeTransientView(transientView);
+            if (transientView instanceof ExpandableView) {
+                ((ExpandableView) transientView).setTransientContainer(null);
+            }
         }
     }
 
@@ -4070,8 +4079,8 @@
         clearTransient();
         clearHeadsUpDisappearRunning();
 
-        if (mAmbientState.isDismissAllInProgress()) {
-            setDismissAllInProgress(false);
+        if (mAmbientState.isClearAllInProgress()) {
+            setClearAllInProgress(false);
             if (mShadeNeedsToClose) {
                 mShadeNeedsToClose = false;
                 postDelayed(
@@ -4102,7 +4111,7 @@
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearTransient() {
         for (ExpandableView view : mClearTransientViewsWhenFinished) {
-            StackStateAnimator.removeTransientView(view);
+            view.removeFromTransientContainer();
         }
         mClearTransientViewsWhenFinished.clear();
     }
@@ -4445,19 +4454,19 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setDismissAllInProgress(boolean dismissAllInProgress) {
-        mDismissAllInProgress = dismissAllInProgress;
-        mAmbientState.setDismissAllInProgress(dismissAllInProgress);
-        mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress);
-        handleDismissAllClipping();
+    public void setClearAllInProgress(boolean clearAllInProgress) {
+        mClearAllInProgress = clearAllInProgress;
+        mAmbientState.setClearAllInProgress(clearAllInProgress);
+        mController.getNoticationRoundessManager().setClearAllInProgress(clearAllInProgress);
+        handleClearAllClipping();
     }
 
-    boolean getDismissAllInProgress() {
-        return mDismissAllInProgress;
+    boolean getClearAllInProgress() {
+        return mClearAllInProgress;
     }
 
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    private void handleDismissAllClipping() {
+    private void handleClearAllClipping() {
         final int count = getChildCount();
         boolean previousChildWillBeDismissed = false;
         for (int i = 0; i < count; i++) {
@@ -4465,12 +4474,12 @@
             if (child.getVisibility() == GONE) {
                 continue;
             }
-            if (mDismissAllInProgress && previousChildWillBeDismissed) {
+            if (mClearAllInProgress && previousChildWillBeDismissed) {
                 child.setMinClipTopAmount(child.getClipTopAmount());
             } else {
                 child.setMinClipTopAmount(0);
             }
-            previousChildWillBeDismissed = canChildBeDismissed(child);
+            previousChildWillBeDismissed = canChildBeCleared(child);
         }
     }
 
@@ -4676,7 +4685,22 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
-        if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
+        final boolean add = mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed);
+        if (SPEW) {
+            Log.v(TAG, "generateHeadsUpAnimation:"
+                    + " willAdd=" + add
+                    + " isHeadsUp=" + isHeadsUp
+                    + " row=" + row.getEntry().getKey());
+        }
+        if (add) {
+            // If we're hiding a HUN we just started showing THIS FRAME, then remove that event,
+            // and do not add the disappear event either.
+            if (!isHeadsUp && mHeadsUpChangeAnimations.remove(new Pair<>(row, true))) {
+                if (SPEW) {
+                    Log.v(TAG, "generateHeadsUpAnimation: previous hun appear animation cancelled");
+                }
+                return;
+            }
             mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
             mNeedsAnimation = true;
             if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
@@ -5021,7 +5045,7 @@
         }
         if (view instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            if (isVisible(row) && includeChildInDismissAll(row, selection)) {
+            if (isVisible(row) && includeChildInClearAll(row, selection)) {
                 return true;
             }
         }
@@ -5051,7 +5075,7 @@
 
                 if (isChildrenVisible(parent)) {
                     for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
-                        if (isVisible(child) && includeChildInDismissAll(child, selection)) {
+                        if (isVisible(child) && includeChildInClearAll(child, selection)) {
                             viewsToHide.add(child);
                         }
                     }
@@ -5072,13 +5096,13 @@
                 continue;
             }
             ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
-            if (includeChildInDismissAll(parent, selection)) {
+            if (includeChildInClearAll(parent, selection)) {
                 viewsToRemove.add(parent);
             }
             List<ExpandableNotificationRow> children = parent.getAttachedChildren();
             if (isVisible(parent) && children != null) {
                 for (ExpandableNotificationRow child : children) {
-                    if (includeChildInDismissAll(parent, selection)) {
+                    if (includeChildInClearAll(parent, selection)) {
                         viewsToRemove.add(child);
                     }
                 }
@@ -5089,7 +5113,7 @@
 
     /**
      * Collects a list of visible rows, and animates them away in a staggered fashion as if they
-     * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd.
+     * were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
      */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     @VisibleForTesting
@@ -5098,18 +5122,18 @@
         final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
         final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend =
                 getRowsToDismissInBackend(selection);
-        if (mDismissListener != null) {
-            mDismissListener.onDismiss(selection);
+        if (mClearAllListener != null) {
+            mClearAllListener.onClearAll(selection);
         }
         final Runnable dismissInBackend = () -> {
-            onDismissAllAnimationsEnd(rowsToDismissInBackend, selection);
+            onClearAllAnimationsEnd(rowsToDismissInBackend, selection);
         };
         if (viewsToAnimateAway.isEmpty()) {
             dismissInBackend.run();
             return;
         }
         // Disable normal animations
-        setDismissAllInProgress(true);
+        setClearAllInProgress(true);
         mShadeNeedsToClose = closeShade;
 
         // Decrease the delay for every row we animate to give the sense of
@@ -5130,10 +5154,10 @@
         }
     }
 
-    private boolean includeChildInDismissAll(
+    private boolean includeChildInClearAll(
             ExpandableNotificationRow row,
             @SelectedRows int selection) {
-        return canChildBeDismissed(row) && matchesSelection(row, selection);
+        return canChildBeCleared(row) && matchesSelection(row, selection);
     }
 
     /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
@@ -5149,9 +5173,9 @@
     protected void inflateFooterView() {
         FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
                 R.layout.status_bar_notification_footer, this, false);
-        footerView.setDismissButtonClickListener(v -> {
-            if (mFooterDismissListener != null) {
-                mFooterDismissListener.onDismiss();
+        footerView.setClearAllButtonClickListener(v -> {
+            if (mFooterClearAllListener != null) {
+                mFooterClearAllListener.onClearAll();
             }
             clearNotifications(ROWS_ALL, true /* closeShade */);
             footerView.setSecondaryVisible(false /* visible */, true /* animate */);
@@ -5388,20 +5412,20 @@
         return mCheckForLeavebehind;
     }
 
-    void setDismissListener (DismissListener listener) {
-        mDismissListener = listener;
+    void setClearAllListener(ClearAllListener listener) {
+        mClearAllListener = listener;
     }
 
-    void setDismissAllAnimationListener(DismissAllAnimationListener dismissAllAnimationListener) {
-        mDismissAllAnimationListener = dismissAllAnimationListener;
+    void setClearAllAnimationListener(ClearAllAnimationListener clearAllAnimationListener) {
+        mClearAllAnimationListener = clearAllAnimationListener;
     }
 
     public void setHighPriorityBeforeSpeedBump(boolean highPriorityBeforeSpeedBump) {
         mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
     }
 
-    void setFooterDismissListener(FooterDismissListener listener) {
-        mFooterDismissListener = listener;
+    void setFooterClearAllListener(FooterClearAllListener listener) {
+        mFooterClearAllListener = listener;
     }
 
     void setShadeController(ShadeController shadeController) {
@@ -5975,9 +5999,6 @@
     static boolean canChildBeDismissed(View v) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            if (row.isBlockingHelperShowingAndTranslationFinished()) {
-                return true;
-            }
             if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
                 return false;
             }
@@ -5989,6 +6010,20 @@
         return false;
     }
 
+    static boolean canChildBeCleared(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
+                return false;
+            }
+            return row.canViewBeCleared();
+        }
+        if (v instanceof PeopleHubView) {
+            return ((PeopleHubView) v).getCanSwipe();
+        }
+        return false;
+    }
+
     // --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
 
     void onEntryUpdated(NotificationEntry entry) {
@@ -6002,11 +6037,11 @@
     /**
      * Called after the animations for a "clear all notifications" action has ended.
      */
-    private void onDismissAllAnimationsEnd(
+    private void onClearAllAnimationsEnd(
             List<ExpandableNotificationRow> viewsToRemove,
             @SelectedRows int selectedRows) {
-        if (mDismissAllAnimationListener != null) {
-            mDismissAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
+        if (mClearAllAnimationListener != null) {
+            mClearAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
         }
     }
 
@@ -6148,15 +6183,15 @@
     /** Only rows where entry.isHighPriority() is false. */
     public static final int ROWS_GENTLE = 2;
 
-    interface DismissListener {
-        void onDismiss(@SelectedRows int selectedRows);
+    interface ClearAllListener {
+        void onClearAll(@SelectedRows int selectedRows);
     }
 
-    interface FooterDismissListener {
-        void onDismiss();
+    interface FooterClearAllListener {
+        void onClearAll();
     }
 
-    interface DismissAllAnimationListener {
+    interface ClearAllAnimationListener {
         void onAnimationEnd(
                 List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
     }
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 71dcc5d..5833ec2 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
@@ -28,7 +28,7 @@
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeCleared;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
 
 import android.content.res.Configuration;
@@ -93,7 +93,6 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
@@ -127,6 +126,7 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.Compile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -144,7 +144,7 @@
 @StatusBarComponent.StatusBarScope
 public class NotificationStackScrollLayoutController {
     private static final String TAG = "StackScrollerController";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     private final boolean mAllowLongPress;
     private final NotificationGutsManager mNotificationGutsManager;
@@ -455,10 +455,7 @@
                     if (!row.isDismissed()) {
                         handleChildViewDismissed(view);
                     }
-                    ViewGroup transientContainer = row.getTransientContainer();
-                    if (transientContainer != null) {
-                        transientContainer.removeTransientView(view);
-                    }
+                    row.removeFromTransientContainer();
                 }
 
                 /**
@@ -469,7 +466,7 @@
                  */
 
                 public void handleChildViewDismissed(View view) {
-                    if (mView.getDismissAllInProgress()) {
+                    if (mView.getClearAllInProgress()) {
                         return;
                     }
                     mView.onSwipeEnd();
@@ -513,7 +510,7 @@
                                 && (parent.areGutsExposed()
                                 || mSwipeHelper.getExposedMenuView() == parent
                                 || (parent.getAttachedChildren().size() == 1
-                                && parent.getEntry().isClearable()))) {
+                                && parent.getEntry().isDismissable()))) {
                             // In this case the group is expanded and showing the menu for the
                             // group, further interaction should apply to the group, not any
                             // child notifications so we use the parent of the child. We also do the
@@ -691,11 +688,6 @@
                 (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
         legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
             @Override
-            public void onGroupCreatedFromChildren(NotificationGroup group) {
-                mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
-            }
-
-            @Override
             public void onGroupsChanged() {
                 mStatusBar.requestNotificationUpdate("onGroupsChanged");
             }
@@ -723,10 +715,10 @@
         mView.setController(this);
         mView.setTouchHandler(new TouchHandler());
         mView.setStatusBar(mStatusBar);
-        mView.setDismissAllAnimationListener(this::onAnimationEnd);
-        mView.setDismissListener((selection) -> mUiEventLogger.log(
+        mView.setClearAllAnimationListener(this::onAnimationEnd);
+        mView.setClearAllListener((selection) -> mUiEventLogger.log(
                 NotificationPanelEvent.fromSelection(selection)));
-        mView.setFooterDismissListener(() ->
+        mView.setFooterClearAllListener(() ->
                 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
         mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
         mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
@@ -1455,7 +1447,7 @@
             }
         } else {
             for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
-                if (canChildBeDismissed(rowToRemove)) {
+                if (canChildBeCleared(rowToRemove)) {
                     mNotificationEntryManager.performRemoveNotification(
                             rowToRemove.getEntry().getSbn(),
                             getDismissedByUserStats(rowToRemove.getEntry()),
@@ -1506,7 +1498,7 @@
      *         from the keyguard host to the quick settings one.
      */
     public int getFullShadeTransitionInset() {
-        MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+        MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer();
         if (view == null || view.getHeight() == 0
                 || mStatusBarStateController.getState() != KEYGUARD) {
             return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2c70a5f..8f0579c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -458,7 +458,7 @@
                 final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
                 ((FooterView.FooterViewState) viewState).hideContent =
                         isShelfShowing || noSpaceForFooter
-                                || (ambientState.isDismissAllInProgress()
+                                || (ambientState.isClearAllInProgress()
                                 && !hasOngoingNotifs(algorithmState));
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 2dc9276..1d0d374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -322,9 +322,8 @@
     private void onAnimationFinished() {
         mHostLayout.onChildAnimationFinished();
 
-        for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
-            transientViewsToRemove.getTransientContainer()
-                    .removeTransientView(transientViewsToRemove);
+        for (ExpandableView transientViewToRemove : mTransientViewsToRemove) {
+            transientViewToRemove.removeFromTransientContainer();
         }
         mTransientViewsToRemove.clear();
     }
@@ -353,7 +352,7 @@
             } else if (event.animationType ==
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
                 if (changingView.getVisibility() != View.VISIBLE) {
-                    removeTransientView(changingView);
+                    changingView.removeFromTransientContainer();
                     continue;
                 }
 
@@ -390,12 +389,11 @@
                 }
                 changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
                         0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
-                        0, () -> removeTransientView(changingView), null);
+                        0, changingView::removeFromTransientContainer, null);
             } else if (event.animationType ==
                 NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
-                if (mHostLayout.isFullySwipedOut(changingView)
-                        && changingView.getTransientContainer() != null) {
-                    changingView.getTransientContainer().removeTransientView(changingView);
+                if (mHostLayout.isFullySwipedOut(changingView)) {
+                    changingView.removeFromTransientContainer();
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
@@ -425,7 +423,7 @@
                     mHostLayout.addTransientView(changingView, 0);
                     changingView.setTransientContainer(mHostLayout);
                     mTmpState.initFrom(changingView);
-                    endRunnable = () -> removeTransientView(changingView);
+                    endRunnable = changingView::removeFromTransientContainer;
                 }
                 float targetLocation = 0;
                 boolean needsAnimation = true;
@@ -468,12 +466,6 @@
         }
     }
 
-    public static void removeTransientView(ExpandableView viewToRemove) {
-        if (viewToRemove.getTransientContainer() != null) {
-            viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
-        }
-    }
-
     public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
             final boolean isRubberbanded) {
         final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index bfa4a24..dee1b33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -11,7 +11,7 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar.phone;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 1b42b58..d610b37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -305,6 +305,14 @@
     }
 
     /**
+     * When this method returns true then moving display state to power save mode will be
+     * delayed for a few seconds. This might be useful to play animations without reducing FPS.
+     */
+    public boolean shouldDelayDisplayDozeTransition() {
+        return mScreenOffAnimationController.shouldDelayDisplayDozeTransition();
+    }
+
+    /**
      * Whether we're capable of controlling the screen off animation if we want to. This isn't
      * possible if AOD isn't even enabled or if the flag is disabled.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
new file mode 100644
index 0000000..56b0d598
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback
+import com.android.internal.R
+
+/**
+ * Listens for fold state changes and reports the new folded state together with other properties
+ * associated with that state.
+ */
+internal class FoldStateListener(
+    context: Context,
+    private val listener: OnFoldStateChangeListener
+) : DeviceStateCallback {
+
+    internal interface OnFoldStateChangeListener {
+        /**
+         * Reports that the device either became folded or unfolded.
+         *
+         * @param folded whether the device is folded.
+         * @param willGoToSleep whether the device will go to sleep and keep the screen off.
+         */
+        fun onFoldStateChanged(folded: Boolean, willGoToSleep: Boolean)
+    }
+
+    private val foldedDeviceStates: IntArray =
+        context.resources.getIntArray(R.array.config_foldedDeviceStates)
+    private val goToSleepDeviceStates: IntArray =
+        context.resources.getIntArray(R.array.config_deviceStatesOnWhichToSleep)
+
+    private var wasFolded: Boolean? = null
+
+    override fun onStateChanged(state: Int) {
+        val isFolded = foldedDeviceStates.contains(state)
+        if (wasFolded == isFolded) {
+            return
+        }
+        wasFolded = isFolded
+        val willGoToSleep = goToSleepDeviceStates.contains(state)
+        listener.onFoldStateChanged(isFolded, willGoToSleep)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 01acce3..2824ab8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -412,7 +412,7 @@
     }
 
     @Override
-    protected boolean canRemoveImmediately(@NonNull String key) {
+    public boolean canRemoveImmediately(@NonNull String key) {
         if (mSwipedOutKeys.contains(key)) {
             // We always instantly dismiss views being manually swiped out.
             mSwipedOutKeys.remove(key);
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 ec2d036..571c10b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -54,6 +54,7 @@
             isListening = false
             updateListeningState()
             keyguardUpdateMonitor.requestFaceAuth(true)
+            keyguardUpdateMonitor.requestActiveUnlock()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
new file mode 100644
index 0000000..868efa0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.util.DisplayMetrics
+import android.view.View
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.LSShadeTransitionLog
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import javax.inject.Inject
+
+private const val TAG = "LockscreenShadeTransitionController"
+
+class LSShadeTransitionLogger @Inject constructor(
+    @LSShadeTransitionLog private val buffer: LogBuffer,
+    private val lockscreenGestureLogger: LockscreenGestureLogger,
+    private val displayMetrics: DisplayMetrics
+) {
+    fun logUnSuccessfulDragDown(startingChild: View?) {
+        val entry = (startingChild as? ExpandableNotificationRow)?.entry
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = entry?.key ?: "no entry"
+        }, {
+            "Tried to drag down but can't drag down on $str1"
+        })
+    }
+
+    fun logDragDownAborted() {
+        buffer.log(TAG, LogLevel.INFO, {}, {
+            "The drag down was reset"
+        })
+    }
+
+    fun logDragDownStarted(startingChild: ExpandableView?) {
+        val entry = (startingChild as? ExpandableNotificationRow)?.entry
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = entry?.key ?: "no entry"
+        }, {
+            "The drag down has started on $str1"
+        })
+    }
+
+    fun logDraggedDownLockDownShade(startingChild: View?) {
+        val entry = (startingChild as? ExpandableNotificationRow)?.entry
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = entry?.key ?: "no entry"
+        }, {
+            "Dragged down in locked down shade on $str1"
+        })
+    }
+
+    fun logDraggedDown(startingChild: View?, dragLengthY: Int) {
+        val entry = (startingChild as? ExpandableNotificationRow)?.entry
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = entry?.key ?: "no entry"
+        }, {
+            "Drag down succeeded on $str1"
+        })
+        // Do logging to event log not just our own buffer
+        lockscreenGestureLogger.write(
+            MetricsEvent.ACTION_LS_SHADE,
+            (dragLengthY / displayMetrics.density).toInt(),
+            0 /* velocityDp */)
+        lockscreenGestureLogger.log(
+            LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+    }
+
+    fun logDefaultGoToFullShadeAnimation(delay: Long) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            long1 = delay
+        }, {
+            "Default animation started to full shade with delay $long1"
+        })
+    }
+
+    fun logTryGoToLockedShade(keyguard: Boolean) {
+        buffer.log(TAG, LogLevel.INFO, {
+            bool1 = keyguard
+        }, {
+            "Trying to go to locked shade " + if (bool1) "from keyguard" else "not from keyguard"
+        })
+    }
+
+    fun logShadeDisabledOnGoToLockedShade() {
+        buffer.log(TAG, LogLevel.WARNING, {}, {
+            "The shade was disabled when trying to go to the locked shade"
+        })
+    }
+
+    fun logShowBouncerOnGoToLockedShade() {
+        buffer.log(TAG, LogLevel.INFO, {}, {
+            "Showing bouncer when trying to go to the locked shade"
+        })
+    }
+
+    fun logGoingToLockedShade(customAnimationHandler: Boolean) {
+        buffer.log(TAG, LogLevel.INFO, {
+            bool1 = customAnimationHandler
+        }, {
+            "Going to locked shade " + if (customAnimationHandler) "with" else "without" +
+                " a custom handler"
+        })
+    }
+
+    fun logOnHideKeyguard() {
+        buffer.log(TAG, LogLevel.INFO, {}, {
+            "Notified that the keyguard is being hidden"
+        })
+    }
+
+    fun logPulseExpansionStarted() {
+        buffer.log(TAG, LogLevel.INFO, {}, {
+            "Pulse Expansion has started"
+        })
+    }
+
+    fun logPulseExpansionFinished(cancelled: Boolean) {
+        if (cancelled) {
+            buffer.log(TAG, LogLevel.INFO, {}, {
+                "Pulse Expansion is requested to cancel"
+            })
+        } else {
+            buffer.log(TAG, LogLevel.INFO, {}, {
+                "Pulse Expansion is requested to finish"
+            })
+        }
+    }
+
+    fun logDragDownAnimation(target: Float) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            double1 = target.toDouble()
+        }, {
+            "Drag down amount animating to " + double1
+        })
+    }
+
+    fun logAnimationCancelled(isPulse: Boolean) {
+        if (isPulse) {
+            buffer.log(TAG, LogLevel.DEBUG, {}, {
+                "Pulse animation cancelled"
+            })
+        } else {
+            buffer.log(TAG, LogLevel.DEBUG, {}, {
+                "drag down animation cancelled"
+            })
+        }
+    }
+
+    fun logDragDownAmountResetWhenFullyCollapsed() {
+        buffer.log(TAG, LogLevel.WARNING, {}, {
+            "Drag down amount stuck and reset after shade was fully collapsed"
+        })
+    }
+
+    fun logPulseHeightNotResetWhenFullyCollapsed() {
+        buffer.log(TAG, LogLevel.WARNING, {}, {
+            "Pulse height stuck and reset after shade was fully collapsed"
+        })
+    }
+
+    fun logGoingToLockedShadeAborted() {
+        buffer.log(TAG, LogLevel.INFO, {}, {
+            "Going to the Locked Shade has been aborted"
+        })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 9787a944..8bababf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
+import static com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.logGroupKey;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -39,6 +42,7 @@
 import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Compile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -54,8 +58,8 @@
 
     private static final long ALERT_TRANSFER_TIMEOUT = 300;
     private static final String TAG = "NotifGroupAlertTransfer";
-    private static final boolean DEBUG = StatusBar.DEBUG;
-    private static final boolean SPEW = StatusBar.SPEW;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * The list of entries containing group alert metadata for each group. Keyed by group key.
@@ -147,7 +151,9 @@
         @Override
         public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
             if (DEBUG) {
-                Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
+                Log.d(TAG, "!! onGroupSuppressionChanged:"
+                        + " group=" + logGroupKey(group)
+                        + " group.summary=" + logKey(group.summary)
                         + " suppressed=" + suppressed);
             }
             NotificationEntry oldAlertOverride = group.alertOverride;
@@ -159,9 +165,11 @@
                 @Nullable NotificationEntry oldAlertOverride,
                 @Nullable NotificationEntry newAlertOverride) {
             if (DEBUG) {
-                Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
-                        + " oldAlertOverride=" + oldAlertOverride
-                        + " newAlertOverride=" + newAlertOverride);
+                Log.d(TAG, "!! onGroupAlertOverrideChanged:"
+                        + " group=" + logGroupKey(group)
+                        + " group.summary=" + logKey(group.summary)
+                        + " oldAlertOverride=" + logKey(oldAlertOverride)
+                        + " newAlertOverride=" + logKey(newAlertOverride));
             }
             onGroupChanged(group, oldAlertOverride);
         }
@@ -207,7 +215,9 @@
     @Override
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         if (DEBUG) {
-            Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
+            Log.d(TAG, "!! onHeadsUpStateChanged:"
+                    + " entry=" + logKey(entry)
+                    + " isHeadsUp=" + isHeadsUp);
         }
         if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
             // a group summary is alerting; trigger the forward transfer checks
@@ -239,6 +249,9 @@
         } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
             handleSuppressedSummaryAlerted(summary, oldAlertOverride);
         }
+        if (DEBUG) {
+            Log.d(TAG, "checkForForwardAlertTransfer: done");
+        }
     }
 
     private final NotificationEntryListener mNotificationEntryListener =
@@ -248,7 +261,7 @@
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
             if (DEBUG) {
-                Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
+                Log.d(TAG, "!! onPendingEntryAdded: entry=" + logKey(entry));
             }
             String groupKey = mGroupManager.getGroupKey(entry.getSbn());
             GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
@@ -344,7 +357,7 @@
     private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
             NotificationEntry oldAlertOverride) {
         if (DEBUG) {
-            Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
+            Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + logKey(summary));
         }
         GroupAlertEntry groupAlertEntry =
                 mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
@@ -406,7 +419,7 @@
      */
     private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
         if (DEBUG) {
-            Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
+            Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + logKey(summary));
         }
         GroupAlertEntry groupAlertEntry =
                 mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
@@ -480,7 +493,9 @@
                 groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
             }
             if (DEBUG) {
-                Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
+                Log.d(TAG, "transferAlertState:"
+                        + " fromEntry=" + logKey(fromEntry)
+                        + " toEntry=" + logKey(toEntry));
             }
             transferAlertState(fromEntry, toEntry);
         }
@@ -582,7 +597,8 @@
         final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
         if ((params.getContentViews() & contentFlag) == 0) {
             if (DEBUG) {
-                Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
+                Log.d(TAG, "alertNotificationWhenPossible:"
+                        + " async requestRebind entry=" + logKey(entry));
             }
             mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
             params.requireContentViews(contentFlag);
@@ -592,6 +608,10 @@
                     if (alertInfo.isStillValid()) {
                         alertNotificationWhenPossible(entry);
                     } else {
+                        if (DEBUG) {
+                            Log.d(TAG, "alertNotificationWhenPossible:"
+                                    + " markContentViewsFreeable entry=" + logKey(entry));
+                        }
                         // The transfer is no longer valid. Free the content.
                         mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
                                 contentFlag);
@@ -603,12 +623,14 @@
         }
         if (mHeadsUpManager.isAlerting(entry.getKey())) {
             if (DEBUG) {
-                Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
+                Log.d(TAG, "alertNotificationWhenPossible:"
+                        + " continue alerting entry=" + logKey(entry));
             }
             mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
         } else {
             if (DEBUG) {
-                Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
+                Log.d(TAG, "alertNotificationWhenPossible:"
+                        + " start alerting entry=" + logKey(entry));
             }
             mHeadsUpManager.showNotification(entry);
         }
@@ -656,7 +678,7 @@
                 // Notification is aborted due to the transfer being explicitly cancelled
                 return false;
             }
-            if (mEntry.getSbn().getGroupKey() != mOriginalNotification.getGroupKey()) {
+            if (!mEntry.getSbn().getGroupKey().equals(mOriginalNotification.getGroupKey())) {
                 // Groups have changed
                 return false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 5d6e807..aff73e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -349,6 +349,9 @@
     }
 
     private void updateShelfIcons() {
+        if (mShelfIcons == null) {
+            return;
+        }
         updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons,
                 true /* showAmbient */,
                 true /* showLowPriority */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 03b1627..016b953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -46,6 +46,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.Fragment;
 import android.app.StatusBarManager;
@@ -69,6 +70,7 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserManager;
 import android.os.VibrationEffect;
 import android.provider.Settings;
@@ -183,7 +185,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
+import com.android.systemui.statusbar.notification.stack.MediaContainerView;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -210,8 +212,10 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -1581,7 +1585,7 @@
                 if (row.isRemoved()) {
                     continue;
                 }
-            } else if (child instanceof MediaHeaderView) {
+            } else if (child instanceof MediaContainerView) {
                 if (child.getVisibility() == GONE) {
                     continue;
                 }
@@ -2433,12 +2437,12 @@
         // mLockscreenShadeTransitionController.getDragProgress change.
         // When in lockscreen, getDragProgress indicates the true expanded fraction of QS
         float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0
-                ? mLockscreenShadeTransitionController.getDragProgress()
+                ? mLockscreenShadeTransitionController.getQSDragProgress()
                 : getExpandedFraction();
         mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
         mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
         mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
-
+        mKeyguardStatusBarViewController.updateViewState();
 
         if (mCommunalViewController != null) {
             mCommunalViewController.updateQsExpansion(qsExpansionFraction);
@@ -4590,11 +4594,20 @@
 
         @Override
         public void onSmallestScreenWidthChanged() {
+            Trace.beginSection("onSmallestScreenWidthChanged");
             if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
 
             // Can affect multi-user switcher visibility as it depends on screen size by default:
             // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
-            reInflateViews();
+            boolean prevKeyguardUserSwitcherEnabled = mKeyguardUserSwitcherEnabled;
+            boolean prevKeyguardQsUserSwitchEnabled = mKeyguardQsUserSwitchEnabled;
+            updateUserSwitcherFlags();
+            if (prevKeyguardUserSwitcherEnabled != mKeyguardUserSwitcherEnabled
+                    || prevKeyguardQsUserSwitchEnabled != mKeyguardQsUserSwitchEnabled) {
+                reInflateViews();
+            }
+
+            Trace.endSection();
         }
 
         @Override
@@ -4733,8 +4746,6 @@
     public interface NotificationPanelViewStateProvider {
         /** Returns the expanded height of the panel view. */
         float getPanelViewExpandedHeight();
-        /** Returns the fraction of QS that's expanded. */
-        float getQsExpansionFraction();
         /**
          * Returns true if heads up should be visible.
          *
@@ -4755,18 +4766,15 @@
                 }
 
                 @Override
-                public float getQsExpansionFraction() {
-                    return computeQsExpansionFraction();
-                }
-
-                @Override
                 public boolean shouldHeadsUpBeVisible() {
                     return mHeadsUpAppearanceController.shouldBeVisible();
                 }
 
                 @Override
                 public float getLockscreenShadeDragProgress() {
-                    return mLockscreenShadeTransitionController.getDragProgress();
+                    return mTransitioningToFullShadeProgress > 0
+                            ? mLockscreenShadeTransitionController.getQSDragProgress()
+                            : computeQsExpansionFraction();
                 }
             };
 
@@ -4915,33 +4923,53 @@
 
     private class DebugDrawable extends Drawable {
 
+        private final Set<Integer> mDebugTextUsedYPositions = new HashSet<>();
+        private final Paint mDebugPaint = new Paint();
+
         @Override
-        public void draw(Canvas canvas) {
-            Paint p = new Paint();
-            p.setColor(Color.RED);
-            p.setStrokeWidth(2);
-            p.setStyle(Paint.Style.STROKE);
-            canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
-            p.setTextSize(24);
-            if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p);
-            p.setColor(Color.BLUE);
-            canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
-            p.setColor(Color.GREEN);
-            canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
-                    calculatePanelHeightQsExpanded(), p);
-            p.setColor(Color.YELLOW);
-            canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
-                    calculatePanelHeightShade(), p);
-            p.setColor(Color.MAGENTA);
-            canvas.drawLine(
-                    0, calculateNotificationsTopPadding(), mView.getWidth(),
-                    calculateNotificationsTopPadding(), p);
-            p.setColor(Color.CYAN);
+        public void draw(@NonNull Canvas canvas) {
+            mDebugTextUsedYPositions.clear();
+
+            mDebugPaint.setColor(Color.RED);
+            mDebugPaint.setStrokeWidth(2);
+            mDebugPaint.setStyle(Paint.Style.STROKE);
+            mDebugPaint.setTextSize(24);
+            if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, mDebugPaint);
+
+            drawDebugInfo(canvas, getMaxPanelHeight(), Color.RED, "getMaxPanelHeight()");
+            drawDebugInfo(canvas, (int) getExpandedHeight(), Color.BLUE, "getExpandedHeight()");
+            drawDebugInfo(canvas, calculatePanelHeightQsExpanded(), Color.GREEN,
+                    "calculatePanelHeightQsExpanded()");
+            drawDebugInfo(canvas, calculatePanelHeightShade(), Color.YELLOW,
+                    "calculatePanelHeightShade()");
+            drawDebugInfo(canvas, (int) calculateNotificationsTopPadding(), Color.MAGENTA,
+                    "calculateNotificationsTopPadding()");
+            drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY,
+                    "mClockPositionResult.clockY");
+
+            mDebugPaint.setColor(Color.CYAN);
             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
-                    mNotificationStackScrollLayoutController.getTopPadding(), p);
-            p.setColor(Color.GRAY);
-            canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
-                    mClockPositionResult.clockY, p);
+                    mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint);
+        }
+
+        private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
+            mDebugPaint.setColor(color);
+            canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(),
+                    /* stopY= */ y, mDebugPaint);
+            canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+        }
+
+        private int computeDebugYTextPosition(int lineY) {
+            if (lineY - mDebugPaint.getTextSize() < 0) {
+                // Avoiding drawing out of bounds
+                lineY += mDebugPaint.getTextSize();
+            }
+            int textY = lineY;
+            while (mDebugTextUsedYPositions.contains(textY)) {
+                textY = (int) (textY + mDebugPaint.getTextSize());
+            }
+            mDebugTextUsedYPositions.add(textY);
+            return textY;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index 72f1695..fb0e306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -50,7 +50,7 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.view.FloatingActionMode;
-import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 import com.android.systemui.R;
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index e806ca0..091831f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -180,6 +180,14 @@
         animations.all { it.shouldAnimateDozingChange() }
 
     /**
+     * Returns true when moving display state to power save mode should be
+     * delayed for a few seconds. This might be useful to play animations in full quality,
+     * without reducing FPS.
+     */
+    fun shouldDelayDisplayDozeTransition(): Boolean =
+        animations.any { it.shouldDelayDisplayDozeTransition() }
+
+    /**
      * Return true to animate large <-> small clock transition
      */
     fun shouldAnimateClockChange(): Boolean =
@@ -207,6 +215,7 @@
     fun shouldHideScrimOnWakeUp(): Boolean = false
     fun overrideNotificationsDozeAmount(): Boolean = false
     fun shouldShowAodIconsWhenShade(): Boolean = false
+    fun shouldDelayDisplayDozeTransition(): Boolean = false
     fun shouldAnimateAodIcons(): Boolean = true
     fun shouldAnimateDozingChange(): Boolean = true
     fun shouldAnimateClockChange(): Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index a23e726e..5d83cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -182,6 +182,9 @@
     private GradientColors mColors;
     private boolean mNeedsDrawableColorUpdate;
 
+    private float mAdditionalScrimBehindAlphaKeyguard = 0f;
+    // Combined scrim behind keyguard alpha of default scrim + additional scrim
+    // (if wallpaper dimming is applied).
     private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
     private final float mDefaultScrimAlpha;
 
@@ -437,7 +440,35 @@
         return mState;
     }
 
-    protected void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
+    /**
+     * Sets the additional scrim behind alpha keyguard that would be blended with the default scrim
+     * by applying alpha composition on both values.
+     *
+     * @param additionalScrimAlpha alpha value of additional scrim behind alpha keyguard.
+     */
+    protected void setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha) {
+        mAdditionalScrimBehindAlphaKeyguard = additionalScrimAlpha;
+    }
+
+    /**
+     * Applies alpha composition to the default scrim behind alpha keyguard and the additional
+     * scrim alpha, and sets this value to the scrim behind alpha keyguard.
+     * This is used to apply additional keyguard dimming on top of the default scrim alpha value.
+     */
+    protected void applyCompositeAlphaOnScrimBehindKeyguard() {
+        int compositeAlpha = ColorUtils.compositeAlpha(
+                (int) (255 * mAdditionalScrimBehindAlphaKeyguard),
+                (int) (255 * KEYGUARD_SCRIM_ALPHA));
+        float keyguardScrimAlpha = (float) compositeAlpha / 255;
+        setScrimBehindValues(keyguardScrimAlpha);
+    }
+
+    /**
+     * Sets the scrim behind alpha keyguard values. This is how much the keyguard will be dimmed.
+     *
+     * @param scrimBehindAlphaKeyguard alpha value of the scrim behind
+     */
+    private void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
         mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
         ScrimState[] states = ScrimState.values();
         for (int i = 0; i < states.length; i++) {
@@ -676,7 +707,10 @@
                     mNotificationsAlpha = behindFraction * mDefaultScrimAlpha;
                 } else {
                     mBehindAlpha = behindFraction * mDefaultScrimAlpha;
-                    mNotificationsAlpha = mBehindAlpha;
+                    // Delay fade-in of notification scrim a bit further, to coincide with the
+                    // view fade in. Otherwise the empty panel can be quite jarring.
+                    mNotificationsAlpha = MathUtils.constrainedMap(0f, 1f, 0.3f, 0.75f,
+                            mPanelExpansionFraction);
                 }
                 mInFrontAlpha = 0;
             }
@@ -732,7 +766,7 @@
             }
             if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
                 // We're unoccluding the keyguard and don't want to have a bright flash.
-                mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
+                mNotificationsAlpha = mScrimBehindAlphaKeyguard;
                 mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 8afa637..d2e1650 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -153,7 +153,7 @@
         // to make sure correct color is returned before "prepare" is called
         @Override
         public int getBehindTint() {
-            return Color.BLACK;
+            return DEBUG_MODE ? DEBUG_BEHIND_TINT : Color.BLACK;
         }
     },
 
@@ -264,6 +264,12 @@
         }
     };
 
+    private static final boolean DEBUG_MODE = false;
+
+    private static final int DEBUG_NOTIFICATIONS_TINT = Color.RED;
+    private static final int DEBUG_FRONT_TINT = Color.GREEN;
+    private static final int DEBUG_BEHIND_TINT = Color.BLUE;
+
     boolean mBlankScreen = false;
     long mAnimationDuration = ScrimController.ANIMATION_DURATION;
     int mFrontTint = Color.TRANSPARENT;
@@ -323,15 +329,15 @@
     }
 
     public int getFrontTint() {
-        return mFrontTint;
+        return DEBUG_MODE ? DEBUG_FRONT_TINT : mFrontTint;
     }
 
     public int getBehindTint() {
-        return mBehindTint;
+        return DEBUG_MODE ? DEBUG_BEHIND_TINT : mBehindTint;
     }
 
     public int getNotifTint() {
-        return mNotifTint;
+        return DEBUG_MODE ? DEBUG_NOTIFICATIONS_TINT : mNotifTint;
     }
 
     public long getAnimationDuration() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 2d41e5e..24bb7f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -55,6 +55,9 @@
      */
     boolean closeShadeIfOpen();
 
+    /** Returns whether the shade is currently open or opening. */
+    boolean isShadeOpen();
+
     /**
      * Add a runnable for NotificationPanelView to post when the panel is expanded.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index b4fed2b..72237b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -123,7 +123,8 @@
                 + " canPanelBeCollapsed(): "
                 + getNotificationPanelViewController().canPanelBeCollapsed());
         if (getNotificationShadeWindowView() != null
-                && getNotificationPanelViewController().canPanelBeCollapsed()) {
+                && getNotificationPanelViewController().canPanelBeCollapsed()
+                && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
             // release focus immediately to kick off focus change transition
             mNotificationShadeWindowController.setNotificationShadeFocusable(false);
 
@@ -148,6 +149,13 @@
     }
 
     @Override
+    public boolean isShadeOpen() {
+        NotificationPanelViewController controller =
+                getNotificationPanelViewController();
+        return controller.isExpanding() || controller.isFullyExpanded();
+    }
+
+    @Override
     public void postOnShadeExpanded(Runnable executable) {
         getNotificationPanelViewController().addOnGlobalLayoutListener(
                 new ViewTreeObserver.OnGlobalLayoutListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3671bb9..9a9e3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -70,6 +70,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.hardware.devicestate.DeviceStateManager;
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Bundle;
@@ -267,6 +268,7 @@
 
     // Should match the values in PhoneWindowManager
     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+    public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
     static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
 
     private static final String BANNER_ACTION_CANCEL =
@@ -617,6 +619,8 @@
     protected boolean mDozing;
     private boolean mIsFullscreen;
 
+    boolean mCloseQsBeforeScreenOff;
+
     private final NotificationMediaManager mMediaManager;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final NotificationRemoteInputManager mRemoteInputManager;
@@ -779,7 +783,8 @@
             Optional<StartingSurface> startingSurfaceOptional,
             ActivityLaunchAnimator activityLaunchAnimator,
             NotifPipelineFlags notifPipelineFlags,
-            InteractionJankMonitor jankMonitor) {
+            InteractionJankMonitor jankMonitor,
+            DeviceStateManager deviceStateManager) {
         super(context);
         mNotificationsController = notificationsController;
         mFragmentService = fragmentService;
@@ -902,6 +907,9 @@
                 data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
         mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
                 id -> onLaunchTransitionTimeout());
+
+        deviceStateManager.registerCallback(mMainExecutor,
+                new FoldStateListener(mContext, this::onFoldedStateChanged));
     }
 
     @Override
@@ -998,7 +1006,7 @@
         internalFilter.addAction(BANNER_ACTION_CANCEL);
         internalFilter.addAction(BANNER_ACTION_SETUP);
         mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
-                null);
+                null, Context.RECEIVER_EXPORTED_UNAUDITED);
 
         if (mWallpaperSupported) {
             IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
@@ -1100,6 +1108,36 @@
                                 requestTopUi, componentTag))));
     }
 
+    private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
+        // Folded state changes are followed by a screen off event.
+        // By default turning off the screen also closes the shade.
+        // We want to make sure that the shade status is kept after
+        // folding/unfolding.
+        boolean isShadeOpen = mShadeController.isShadeOpen();
+        boolean leaveOpen = isShadeOpen && !willGoToSleep;
+        if (DEBUG) {
+            Log.d(TAG, String.format(
+                    "#onFoldedStateChanged(): "
+                            + "isFolded=%s, "
+                            + "willGoToSleep=%s, "
+                            + "isShadeOpen=%s, "
+                            + "leaveOpen=%s",
+                    isFolded, willGoToSleep, isShadeOpen, leaveOpen));
+        }
+        if (leaveOpen) {
+            mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+            if (mIsKeyguard) {
+                // When device state changes on keyguard we don't want to keep the state of
+                // the shade and instead we open clean state of keyguard with shade closed.
+                // Normally some parts of QS state (like expanded/collapsed) are persisted and
+                // that causes incorrect UI rendering, especially when changing state with QS
+                // expanded. To prevent that we can close QS which resets QS and some parts of
+                // the shade to its default state. Read more in b/201537421
+                mCloseQsBeforeScreenOff = true;
+            }
+        }
+    }
+
     // ================================================================================
     // Constructing the view
     // ================================================================================
@@ -1306,7 +1344,8 @@
             demoFilter.addAction(ACTION_FAKE_ARTWORK);
         }
         mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
-                android.Manifest.permission.DUMP, null);
+                android.Manifest.permission.DUMP, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // listen for USER_SETUP_COMPLETE setting (per-user)
         mDeviceProvisionedController.addCallback(mUserSetupObserver);
@@ -1333,10 +1372,14 @@
         // Things that mean we're not dismissing the keyguard, and should ignore this expansion:
         // - Keyguard isn't even visible.
         // - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt).
+        // - The SIM is locked, you can't swipe to unlock. If the SIM is locked but there is no
+        //   device lock set, canDismissLockScreen returns true even though you should not be able
+        //   to dismiss the lock screen until entering the SIM PIN.
         // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
         //   keyguard.
         if (!isKeyguardShowing()
                 || !mKeyguardStateController.canDismissLockScreen()
+                || mKeyguardViewMediator.isAnySimPinSecure()
                 || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) {
             return;
         }
@@ -1386,7 +1429,8 @@
 
     private void setUpPresenter() {
         // Set up the initial notification state.
-        mActivityLaunchAnimator.setCallback(mKeyguardHandler);
+        mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
+        mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
         mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
                 mNotificationShadeWindowViewController,
                 mStackScrollerController.getNotificationListContainer(),
@@ -2603,8 +2647,17 @@
                 if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");
-                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
-                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+                    if (reason != null) {
+                        if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+                            flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+                        }
+                        // Do not collapse notifications when starting dreaming if the notifications
+                        // shade is used for the screen off animation. It might require expanded
+                        // state for the scrims to be visible
+                        if (reason.equals(SYSTEM_DIALOG_REASON_DREAM)
+                                && mScreenOffAnimationController.shouldExpandNotifications()) {
+                            flags |= CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL;
+                        }
                     }
                     mShadeController.animateCollapsePanels(flags);
                 }
@@ -2881,10 +2934,10 @@
     }
 
     boolean updateIsKeyguard() {
-        return updateIsKeyguard(false /* force */);
+        return updateIsKeyguard(false /* forceStateChange */);
     }
 
-    boolean updateIsKeyguard(boolean force) {
+    boolean updateIsKeyguard(boolean forceStateChange) {
         boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
                 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 
@@ -2917,7 +2970,7 @@
             //    as the animation could prepare 'fake AOD' interface (without actually
             //    transitioning to keyguard state) and this might reset the view states
             if (!mScreenOffAnimationController.isKeyguardHideDelayed()) {
-                return hideKeyguardImpl(force);
+                return hideKeyguardImpl(forceStateChange);
             }
         }
         return false;
@@ -2932,7 +2985,7 @@
         mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
             mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
-        } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
+        } else if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
         updatePanelExpansionForKeyguard();
@@ -3045,12 +3098,12 @@
     /**
      * @return true if we would like to stay in the shade, false if it should go away entirely
      */
-    public boolean hideKeyguardImpl(boolean force) {
+    public boolean hideKeyguardImpl(boolean forceStateChange) {
         mIsKeyguard = false;
         Trace.beginSection("StatusBar#hideKeyguard");
         boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
         int previousState = mStatusBarStateController.getState();
-        if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) {
+        if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) {
             //TODO: StatusBarStateController should probably know about hiding the keyguard and
             // notify listeners.
 
@@ -3136,16 +3189,29 @@
      * Switches theme from light to dark and vice-versa.
      */
     protected void updateTheme() {
+        // Set additional scrim only if the lock and system wallpaper are different to prevent
+        // applying the dimming effect twice.
+        mUiBgExecutor.execute(() -> {
+            float dimAmount = 0f;
+            if (mWallpaperManager.lockScreenWallpaperExists()) {
+                dimAmount = mWallpaperManager.getWallpaperDimAmount();
+            }
+            final float scrimDimAmount = dimAmount;
+            mMainExecutor.execute(() -> {
+                mScrimController.setAdditionalScrimBehindAlphaKeyguard(scrimDimAmount);
+                mScrimController.applyCompositeAlphaOnScrimBehindKeyguard();
+            });
+        });
+
         // Lock wallpaper defines the color of the majority of the views, hence we'll use it
         // to set our default theme.
         final boolean lockDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
         final int themeResId = lockDarkText ? R.style.Theme_SystemUI_LightWallpaper
                 : R.style.Theme_SystemUI;
-        if (mContext.getThemeResId() == themeResId) {
-            return;
+        if (mContext.getThemeResId() != themeResId) {
+            mContext.setTheme(themeResId);
+            mConfigurationController.notifyThemeChanged();
         }
-        mContext.setTheme(themeResId);
-        mConfigurationController.notifyThemeChanged();
     }
 
     private void updateDozingState() {
@@ -3538,7 +3604,6 @@
             // once we fully woke up.
             updateRevealEffect(true /* wakingUp */);
             updateNotificationPanelTouchState();
-            mPulseExpansionHandler.onStartedWakingUp();
 
             // If we are waking up during the screen off animation, we should undo making the
             // expanded visible (we did that so the LightRevealScrim would be visible).
@@ -3600,6 +3665,10 @@
         public void onScreenTurnedOff() {
             mFalsingCollector.onScreenOff();
             mScrimController.onScreenTurnedOff();
+            if (mCloseQsBeforeScreenOff) {
+                mNotificationPanelViewController.closeQs();
+                mCloseQsBeforeScreenOff = false;
+            }
             updateIsKeyguard();
         }
     };
@@ -4367,7 +4436,7 @@
                 }
             };
 
-    private final ActivityLaunchAnimator.Callback mKeyguardHandler =
+    private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
             new ActivityLaunchAnimator.Callback() {
                 @Override
                 public boolean isOnKeyguard() {
@@ -4386,11 +4455,6 @@
                 }
 
                 @Override
-                public void setBlursDisabledForAppLaunch(boolean disabled) {
-                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
-                }
-
-                @Override
                 public int getBackgroundColor(TaskInfo task) {
                     if (!mStartingSurfaceOptional.isPresent()) {
                         Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
@@ -4401,6 +4465,19 @@
                 }
             };
 
+    private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
+            new ActivityLaunchAnimator.Listener() {
+                @Override
+                public void onLaunchAnimationStart() {
+                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
+                }
+
+                @Override
+                public void onLaunchAnimationEnd() {
+                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
+                }
+            };
+
     private final DemoMode mDemoModeCallback = new DemoMode() {
         @Override
         public void onDemoModeFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index 51f2e67..cf9a5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -490,7 +490,8 @@
     }
 
     @Override
-    public void showTransient(int displayId, @InternalInsetsType int[] types) {
+    public void showTransient(int displayId, @InternalInsetsType int[] types,
+            boolean isGestureOnSystemBar) {
         if (displayId != mDisplayId) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0ba7134..dab37e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -99,6 +99,7 @@
 
             override fun onAnimationEnd(animation: Animator?) {
                 lightRevealAnimationPlaying = false
+                interactionJankMonitor.end(CUJ_SCREEN_OFF)
             }
 
             override fun onAnimationStart(animation: Animator?) {
@@ -218,7 +219,7 @@
             // even if we're going from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have
             // changed parts of the UI (such as showing AOD in the shade) without actually changing
             // the StatusBarState. This ensures that the UI definitely reflects the desired state.
-            statusBar.updateIsKeyguard(true /* force */)
+            statusBar.updateIsKeyguard(true /* forceStateChange */)
         }
     }
 
@@ -289,6 +290,9 @@
         return true
     }
 
+    override fun shouldDelayDisplayDozeTransition(): Boolean =
+        dozeParameters.get().shouldControlUnlockedScreenOff()
+
     fun addCallback(callback: Callback) {
         callbacks.add(callback)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 50176fe..977fe9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -20,6 +20,7 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.util.DisplayMetrics;
@@ -231,7 +232,8 @@
             Optional<StartingSurface> startingSurfaceOptional,
             ActivityLaunchAnimator activityLaunchAnimator,
             NotifPipelineFlags notifPipelineFlags,
-            InteractionJankMonitor jankMonitor) {
+            InteractionJankMonitor jankMonitor,
+            DeviceStateManager deviceStateManager) {
         return new StatusBar(
                 context,
                 notificationsController,
@@ -327,7 +329,8 @@
                 startingSurfaceOptional,
                 activityLaunchAnimator,
                 notifPipelineFlags,
-                jankMonitor
+                jankMonitor,
+                deviceStateManager
         );
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 41cacf5..33f2140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -16,110 +16,53 @@
 
 package com.android.systemui.statusbar.policy;
 
-
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
-import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
-
-import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
 
 import android.annotation.Nullable;
 import android.hardware.devicestate.DeviceStateManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseIntArray;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
 
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 /**
- * Handles reading and writing of rotation lock settings per device state, as well as setting
- * the rotation lock when device state changes.
- **/
+ * Handles reading and writing of rotation lock settings per device state, as well as setting the
+ * rotation lock when device state changes.
+ */
 @SysUISingleton
-public final class DeviceStateRotationLockSettingController implements Listenable,
-        RotationLockController.RotationLockControllerCallback {
+public final class DeviceStateRotationLockSettingController
+        implements Listenable, RotationLockController.RotationLockControllerCallback {
 
     private static final String TAG = "DSRotateLockSettingCon";
 
-    private static final String SEPARATOR_REGEX = ":";
-
-    private final SecureSettings mSecureSettings;
     private final RotationPolicyWrapper mRotationPolicyWrapper;
     private final DeviceStateManager mDeviceStateManager;
     private final Executor mMainExecutor;
-    private final String[] mDeviceStateRotationLockDefaults;
+    private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
 
-    private SparseIntArray mDeviceStateRotationLockSettings;
-    // TODO(b/183001527): Add API to query current device state and initialize this.
+    // On registration for DeviceStateCallback, we will receive a callback with the current state
+    // and this will be initialized.
     private int mDeviceState = -1;
-    @Nullable
-    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
-
+    @Nullable private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+    private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+            mDeviceStateRotationLockSettingsListener;
 
     @Inject
     public DeviceStateRotationLockSettingController(
-            SecureSettings secureSettings,
             RotationPolicyWrapper rotationPolicyWrapper,
             DeviceStateManager deviceStateManager,
             @Main Executor executor,
-            @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
-    ) {
-        mSecureSettings = secureSettings;
+            DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager) {
         mRotationPolicyWrapper = rotationPolicyWrapper;
         mDeviceStateManager = deviceStateManager;
         mMainExecutor = executor;
-        mDeviceStateRotationLockDefaults = deviceStateRotationLockDefaults;
-    }
-
-    /**
-     * Loads the settings from storage.
-     */
-    public void initialize() {
-        String serializedSetting =
-                mSecureSettings.getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                        UserHandle.USER_CURRENT);
-        if (TextUtils.isEmpty(serializedSetting)) {
-            // No settings saved, we should load the defaults and persist them.
-            fallbackOnDefaults();
-            return;
-        }
-        String[] values = serializedSetting.split(SEPARATOR_REGEX);
-        if (values.length % 2 != 0) {
-            // Each entry should be a key/value pair, so this is corrupt.
-            Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
-            fallbackOnDefaults();
-            return;
-        }
-        mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
-        int key;
-        int value;
-
-        for (int i = 0; i < values.length - 1; ) {
-            try {
-                key = Integer.parseInt(values[i++]);
-                value = Integer.parseInt(values[i++]);
-                mDeviceStateRotationLockSettings.put(key, value);
-            } catch (NumberFormatException e) {
-                Log.wtf(TAG, "Error deserializing one of the saved settings", e);
-                fallbackOnDefaults();
-                return;
-            }
-        }
-    }
-
-    private void fallbackOnDefaults() {
-        loadDefaults();
-        persistSettings();
+        mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
     }
 
     @Override
@@ -129,10 +72,17 @@
             // is no user action.
             mDeviceStateCallback = this::updateDeviceState;
             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
+            mDeviceStateRotationLockSettingsListener = () -> readPersistedSetting(mDeviceState);
+            mDeviceStateRotationLockSettingsManager.registerListener(
+                    mDeviceStateRotationLockSettingsListener);
         } else {
             if (mDeviceStateCallback != null) {
                 mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
             }
+            if (mDeviceStateRotationLockSettingsListener != null) {
+                mDeviceStateRotationLockSettingsManager.unregisterListener(
+                        mDeviceStateRotationLockSettingsListener);
+            }
         }
     }
 
@@ -143,7 +93,8 @@
             return;
         }
 
-        if (rotationLocked == isRotationLockedForCurrentState()) {
+        if (rotationLocked
+                == mDeviceStateRotationLockSettingsManager.isRotationLocked(mDeviceState)) {
             Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
             return;
         }
@@ -152,19 +103,15 @@
     }
 
     private void saveNewRotationLockSetting(boolean isRotationLocked) {
-        Log.v(TAG, "saveNewRotationLockSetting [state=" + mDeviceState + "] [isRotationLocked="
-                + isRotationLocked + "]");
+        Log.v(
+                TAG,
+                "saveNewRotationLockSetting [state="
+                        + mDeviceState
+                        + "] [isRotationLocked="
+                        + isRotationLocked
+                        + "]");
 
-        mDeviceStateRotationLockSettings.put(mDeviceState,
-                isRotationLocked
-                        ? DEVICE_STATE_ROTATION_LOCK_LOCKED
-                        : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
-        persistSettings();
-    }
-
-    private boolean isRotationLockedForCurrentState() {
-        return mDeviceStateRotationLockSettings.get(mDeviceState,
-                DEVICE_STATE_ROTATION_LOCK_IGNORED) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+        mDeviceStateRotationLockSettingsManager.updateSetting(mDeviceState, isRotationLocked);
     }
 
     private void updateDeviceState(int state) {
@@ -173,67 +120,29 @@
             return;
         }
 
+        readPersistedSetting(state);
+    }
+
+    private void readPersistedSetting(int state) {
         int rotationLockSetting =
-                mDeviceStateRotationLockSettings.get(state, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+                mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
         if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            // This should not happen. Device states that have an ignored setting, should also
+            // specify a fallback device state which is not ignored.
             // We won't handle this device state. The same rotation lock setting as before should
             // apply and any changes to the rotation lock setting will be written for the previous
             // valid device state.
-            Log.v(TAG, "Ignoring new device state: " + state);
+            Log.w(TAG, "Missing fallback. Ignoring new device state: " + state);
             return;
         }
 
         // Accept the new state
         mDeviceState = state;
 
-        // Update the rotation lock setting if needed for this new device state
+        // Update the rotation policy, if needed, for this new device state
         boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
         if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
             mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
         }
     }
-
-    private void persistSettings() {
-        if (mDeviceStateRotationLockSettings.size() == 0) {
-            mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                    /* value= */"", UserHandle.USER_CURRENT);
-            return;
-        }
-
-        StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append(mDeviceStateRotationLockSettings.keyAt(0))
-                .append(SEPARATOR_REGEX)
-                .append(mDeviceStateRotationLockSettings.valueAt(0));
-
-        for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
-            stringBuilder
-                    .append(SEPARATOR_REGEX)
-                    .append(mDeviceStateRotationLockSettings.keyAt(i))
-                    .append(SEPARATOR_REGEX)
-                    .append(mDeviceStateRotationLockSettings.valueAt(i));
-        }
-        mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                stringBuilder.toString(), UserHandle.USER_CURRENT);
-    }
-
-    private void loadDefaults() {
-        if (mDeviceStateRotationLockDefaults.length == 0) {
-            Log.w(TAG, "Empty default settings");
-            mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */0);
-            return;
-        }
-        mDeviceStateRotationLockSettings =
-                new SparseIntArray(mDeviceStateRotationLockDefaults.length);
-        for (String serializedDefault : mDeviceStateRotationLockDefaults) {
-            String[] entry = serializedDefault.split(SEPARATOR_REGEX);
-            try {
-                int key = Integer.parseInt(entry[0]);
-                int value = Integer.parseInt(entry[1]);
-                mDeviceStateRotationLockSettings.put(key, value);
-            } catch (NumberFormatException e) {
-                Log.wtf(TAG, "Error deserializing default settings", e);
-            }
-        }
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
new file mode 100644
index 0000000..bec5fc8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Manages device-state based rotation lock settings. Handles reading, writing, and listening for
+ * changes.
+ */
+public final class DeviceStateRotationLockSettingsManager {
+
+    private static final String TAG = "DSRotLockSettingsMngr";
+    private static final String SEPARATOR_REGEX = ":";
+
+    private static DeviceStateRotationLockSettingsManager sSingleton;
+
+    private final ContentResolver mContentResolver;
+    private final String[] mDeviceStateRotationLockDefaults;
+    private final Handler mMainHandler = Handler.getMain();
+    private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+    private SparseIntArray mDeviceStateRotationLockSettings;
+    private SparseIntArray mDeviceStateRotationLockFallbackSettings;
+
+    private DeviceStateRotationLockSettingsManager(Context context) {
+        mContentResolver = context.getContentResolver();
+        mDeviceStateRotationLockDefaults =
+                context.getResources()
+                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+        loadDefaults();
+        initializeInMemoryMap();
+        listenForSettingsChange(context);
+    }
+
+    /** Returns a singleton instance of this class */
+    public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
+        if (sSingleton == null) {
+            sSingleton =
+                    new DeviceStateRotationLockSettingsManager(context.getApplicationContext());
+        }
+        return sSingleton;
+    }
+
+    /** Returns true if device-state based rotation lock settings are enabled. */
+    public static boolean isDeviceStateRotationLockEnabled(Context context) {
+        return context.getResources()
+                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+                        .length
+                > 0;
+    }
+
+    private void listenForSettingsChange(Context context) {
+        context.getContentResolver()
+                .registerContentObserver(
+                        Settings.Secure.getUriFor(Settings.Secure.DEVICE_STATE_ROTATION_LOCK),
+                        /* notifyForDescendents= */ false, //NOTYPO
+                        new ContentObserver(mMainHandler) {
+                            @Override
+                            public void onChange(boolean selfChange) {
+                                onPersistedSettingsChanged();
+                            }
+                        },
+                        UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+     * change. Can be called multiple times with different listeners.
+     */
+    public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+        mListeners.add(runnable);
+    }
+
+    /**
+     * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+     * was never registered.
+     */
+    public void unregisterListener(
+            DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
+        if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+            Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
+        }
+    }
+
+    /** Updates the rotation lock setting for a specified device state. */
+    public void updateSetting(int deviceState, boolean rotationLocked) {
+        if (mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState) >= 0) {
+            // The setting for this device state is IGNORED, and has a fallback device state.
+            // The setting for that fallback device state should be the changed in this case.
+            deviceState = mDeviceStateRotationLockFallbackSettings.get(deviceState);
+        }
+        mDeviceStateRotationLockSettings.put(
+                deviceState,
+                rotationLocked
+                        ? DEVICE_STATE_ROTATION_LOCK_LOCKED
+                        : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        persistSettings();
+    }
+
+    /**
+     * Returns the {@link Settings.Secure.DeviceStateRotationLockSetting} for the given device
+     * state.
+     *
+     * <p>If the setting for this device state is {@link DEVICE_STATE_ROTATION_LOCK_IGNORED}, it
+     * will return the setting for the fallback device state.
+     *
+     * <p>If no fallback is specified for this device state, it will return {@link
+     * DEVICE_STATE_ROTATION_LOCK_IGNORED}.
+     */
+    @Settings.Secure.DeviceStateRotationLockSetting
+    public int getRotationLockSetting(int deviceState) {
+        int rotationLockSetting = mDeviceStateRotationLockSettings.get(
+                deviceState, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            rotationLockSetting = getFallbackRotationLockSetting(deviceState);
+        }
+        return rotationLockSetting;
+    }
+
+    private int getFallbackRotationLockSetting(int deviceState) {
+        int indexOfFallbackState = mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState);
+        if (indexOfFallbackState < 0) {
+            Log.w(TAG, "Setting is ignored, but no fallback was specified.");
+            return DEVICE_STATE_ROTATION_LOCK_IGNORED;
+        }
+        int fallbackState = mDeviceStateRotationLockFallbackSettings.valueAt(indexOfFallbackState);
+        return mDeviceStateRotationLockSettings.get(fallbackState,
+                /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
+    }
+
+
+    /** Returns true if the rotation is locked for the current device state */
+    public boolean isRotationLocked(int deviceState) {
+        return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+    }
+
+    /**
+     * Returns true if there is no device state for which the current setting is {@link
+     * DEVICE_STATE_ROTATION_LOCK_UNLOCKED}.
+     */
+    public boolean isRotationLockedForAllStates() {
+        for (int i = 0; i < mDeviceStateRotationLockSettings.size(); i++) {
+            if (mDeviceStateRotationLockSettings.valueAt(i)
+                    == DEVICE_STATE_ROTATION_LOCK_UNLOCKED) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void initializeInMemoryMap() {
+        String serializedSetting =
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT);
+        if (TextUtils.isEmpty(serializedSetting)) {
+            // No settings saved, we should load the defaults and persist them.
+            fallbackOnDefaults();
+            return;
+        }
+        String[] values = serializedSetting.split(SEPARATOR_REGEX);
+        if (values.length % 2 != 0) {
+            // Each entry should be a key/value pair, so this is corrupt.
+            Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
+            fallbackOnDefaults();
+            return;
+        }
+        mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
+        int key;
+        int value;
+
+        for (int i = 0; i < values.length - 1; ) {
+            try {
+                key = Integer.parseInt(values[i++]);
+                value = Integer.parseInt(values[i++]);
+                mDeviceStateRotationLockSettings.put(key, value);
+            } catch (NumberFormatException e) {
+                Log.wtf(TAG, "Error deserializing one of the saved settings", e);
+                fallbackOnDefaults();
+                return;
+            }
+        }
+    }
+
+    private void fallbackOnDefaults() {
+        loadDefaults();
+        persistSettings();
+    }
+
+    private void persistSettings() {
+        if (mDeviceStateRotationLockSettings.size() == 0) {
+            Settings.Secure.putStringForUser(
+                    mContentResolver,
+                    Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                    /* value= */ "",
+                    UserHandle.USER_CURRENT);
+            return;
+        }
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder
+                .append(mDeviceStateRotationLockSettings.keyAt(0))
+                .append(SEPARATOR_REGEX)
+                .append(mDeviceStateRotationLockSettings.valueAt(0));
+
+        for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
+            stringBuilder
+                    .append(SEPARATOR_REGEX)
+                    .append(mDeviceStateRotationLockSettings.keyAt(i))
+                    .append(SEPARATOR_REGEX)
+                    .append(mDeviceStateRotationLockSettings.valueAt(i));
+        }
+        Settings.Secure.putStringForUser(
+                mContentResolver,
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                stringBuilder.toString(),
+                UserHandle.USER_CURRENT);
+    }
+
+    private void loadDefaults() {
+        mDeviceStateRotationLockSettings = new SparseIntArray(
+                mDeviceStateRotationLockDefaults.length);
+        mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
+        for (String entry : mDeviceStateRotationLockDefaults) {
+            String[] values = entry.split(SEPARATOR_REGEX);
+            try {
+                int deviceState = Integer.parseInt(values[0]);
+                int rotationLockSetting = Integer.parseInt(values[1]);
+                if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+                    if (values.length == 3) {
+                        int fallbackDeviceState = Integer.parseInt(values[2]);
+                        mDeviceStateRotationLockFallbackSettings.put(deviceState,
+                                fallbackDeviceState);
+                    } else {
+                        Log.w(TAG,
+                                "Rotation lock setting is IGNORED, but values have unexpected "
+                                        + "size of "
+                                        + values.length);
+                    }
+                }
+                mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
+            } catch (NumberFormatException e) {
+                Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Called when the persisted settings have changed, requiring a reinitialization of the
+     * in-memory map.
+     */
+    @VisibleForTesting
+    public void onPersistedSettingsChanged() {
+        initializeInMemoryMap();
+        notifyListeners();
+    }
+
+    private void notifyListeners() {
+        for (DeviceStateRotationLockSettingsListener r : mListeners) {
+            r.onSettingsChanged();
+        }
+    }
+
+    /** Listener for changes in device-state based rotation lock settings */
+    public interface DeviceStateRotationLockSettingsListener {
+        /** Called whenever the settings have changed. */
+        void onSettingsChanged();
+    }
+}
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 2dfcc98..05a586b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -128,7 +128,7 @@
                         update(true /* updateAlways */);
                     }
                 }
-            }, filter, null, null);
+            }, filter, null, null, Context.RECEIVER_EXPORTED_UNAUDITED);
         }
     }
 
@@ -171,6 +171,8 @@
         if (mShowing == showing && mOccluded == occluded) return;
         mShowing = showing;
         mOccluded = occluded;
+        Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events",
+                "Keyguard showing: " + showing + " occluded: " + occluded);
         notifyKeyguardChanged();
 
         // Update the dismiss amount to the full 0f/1f if we explicitly show or hide the keyguard.
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 3831857..59969c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -22,10 +22,14 @@
 
 import static com.android.settingslib.Utils.updateLocationEnabled;
 
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.PermissionChecker;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.location.LocationManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -68,31 +72,37 @@
     private final BootCompleteCache mBootCompleteCache;
     private final UserTracker mUserTracker;
     private final H mHandler;
-
+    private final Handler mBackgroundHandler;
+    private final PackageManager mPackageManager;
 
     private boolean mAreActiveLocationRequests;
     private boolean mShouldDisplayAllAccesses;
+    private boolean mShowSystemAccesses;
 
     @Inject
     public LocationControllerImpl(Context context, AppOpsController appOpsController,
             DeviceConfigProxy deviceConfigProxy,
             @Main Looper mainLooper, @Background Handler backgroundHandler,
             BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache,
-            UserTracker userTracker) {
+            UserTracker userTracker, PackageManager packageManager) {
         mContext = context;
         mAppOpsController = appOpsController;
         mDeviceConfigProxy = deviceConfigProxy;
         mBootCompleteCache = bootCompleteCache;
         mHandler = new H(mainLooper);
         mUserTracker = userTracker;
-        mShouldDisplayAllAccesses = getDeviceConfigSetting();
+        mBackgroundHandler = backgroundHandler;
+        mPackageManager = packageManager;
+        mShouldDisplayAllAccesses = getAllAccessesSetting();
+        mShowSystemAccesses = getShowSystemSetting();
 
         // Register to listen for changes in DeviceConfig settings.
         mDeviceConfigProxy.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_PRIVACY,
                 backgroundHandler::post,
                 properties -> {
-                    mShouldDisplayAllAccesses = getDeviceConfigSetting();
+                    mShouldDisplayAllAccesses = getAllAccessesSetting();
+                    mShowSystemAccesses = getShowSystemSetting();
                     updateActiveLocationRequests();
                 });
 
@@ -176,11 +186,15 @@
                 UserHandle.of(userId));
     }
 
-    private boolean getDeviceConfigSetting() {
+    private boolean getAllAccessesSetting() {
         return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false);
     }
 
+    private boolean getShowSystemSetting() {
+        return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM, false);
+    }
     /**
      * Returns true if there currently exist active high power location requests.
      */
@@ -202,35 +216,74 @@
      * Returns true if there currently exist active location requests.
      */
     @VisibleForTesting
-    protected boolean areActiveLocationRequests() {
+    protected void areActiveLocationRequests() {
         if (!mShouldDisplayAllAccesses) {
-            return false;
+            return;
         }
-        List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps();
+        boolean hadActiveLocationRequests = mAreActiveLocationRequests;
+        boolean shouldDisplay = false;
 
+        List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps();
+        final List<UserInfo> profiles = mUserTracker.getUserProfiles();
         final int numItems = appOpsItems.size();
         for (int i = 0; i < numItems; i++) {
             if (appOpsItems.get(i).getCode() == OP_FINE_LOCATION
                     || appOpsItems.get(i).getCode() == OP_COARSE_LOCATION) {
-                return true;
+                if (mShowSystemAccesses) {
+                    shouldDisplay = true;
+                } else {
+                    shouldDisplay |= !isSystemApp(profiles, appOpsItems.get(i));
+                }
             }
         }
 
-        return false;
+        mAreActiveLocationRequests = areActiveHighPowerLocationRequests() || shouldDisplay;
+        if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+            mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
+        }
+    }
+
+    private boolean isSystemApp(List<UserInfo> profiles, AppOpItem item) {
+        final String permission = AppOpsManager.opToPermission(item.getCode());
+        UserHandle user = UserHandle.getUserHandleForUid(item.getUid());
+
+        // Don't show apps belonging to background users except managed users.
+        boolean foundUser = false;
+        final int numProfiles = profiles.size();
+        for (int i = 0; i < numProfiles; i++) {
+            if (profiles.get(i).getUserHandle().equals(user)) {
+                foundUser = true;
+            }
+        }
+        if (!foundUser) {
+            return true;
+        }
+
+        final int permissionFlags = mPackageManager.getPermissionFlags(
+                permission, item.getPackageName(), user);
+        if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+                PermissionChecker.PID_UNKNOWN, item.getUid(), item.getPackageName())
+                == PermissionChecker.PERMISSION_GRANTED) {
+            return (permissionFlags
+                    & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
+                    == 0;
+        } else {
+            return (permissionFlags
+                    & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0;
+        }
     }
 
     // Reads the active location requests from either OP_MONITOR_HIGH_POWER_LOCATION,
     // OP_FINE_LOCATION, or OP_COARSE_LOCATION and updates the status view if necessary.
     private void updateActiveLocationRequests() {
-        boolean hadActiveLocationRequests = mAreActiveLocationRequests;
         if (mShouldDisplayAllAccesses) {
-            mAreActiveLocationRequests =
-                    areActiveHighPowerLocationRequests() || areActiveLocationRequests();
+            mBackgroundHandler.post(this::areActiveLocationRequests);
         } else {
+            boolean hadActiveLocationRequests = mAreActiveLocationRequests;
             mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
-        }
-        if (mAreActiveLocationRequests != hadActiveLocationRequests) {
-            mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
+            if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+                mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 46fa20d..48949f92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -51,6 +51,7 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
@@ -665,8 +666,7 @@
             }
             // Hide soft-keyboard when the input view became invisible
             // (i.e. The notification shade collapsed by pressing the home key)
-            if (visibility != VISIBLE && !mEditText.isVisibleToUser()
-                    && !mController.isRemoteInputActive()) {
+            if (visibility != VISIBLE && !mController.isRemoteInputActive()) {
                 mEditText.hideIme();
             }
         }
@@ -779,8 +779,9 @@
         }
 
         private void hideIme() {
-            if (mInputMethodManager != null) {
-                mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+            final WindowInsetsController insetsController = getWindowInsetsController();
+            if (insetsController != null) {
+                insetsController.hide(WindowInsets.Type.ime());
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 3143a47..1eeb0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -64,7 +64,6 @@
         mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController;
         mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0;
         if (mIsPerDeviceStateRotationLockEnabled) {
-            deviceStateRotationLockSettingController.initialize();
             mCallbacks.add(mDeviceStateRotationLockSettingController);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
index f42e388..29285f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -74,7 +74,7 @@
         profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
         profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
-                null, null);
+                null, null, Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 79ee746..9f20bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -229,7 +229,8 @@
 
         filter = new IntentFilter();
         mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
-                PERMISSION_SELF, null /* scheduler */);
+                PERMISSION_SELF, null /* scheduler */,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         mSettingsObserver = new ContentObserver(mHandler) {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index b6a96a7..60938fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy.dagger;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.os.UserManager;
 
@@ -35,6 +36,7 @@
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.DevicePostureControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
@@ -163,6 +165,14 @@
         return controller;
     }
 
+    /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */
+    @SysUISingleton
+    @Provides
+    static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager(
+            Context context) {
+        return DeviceStateRotationLockSettingsManager.getInstance(context);
+    }
+
     /**
      * Default values for per-device state rotation lock settings.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index bd84520..31c7006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -47,6 +47,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener;
 
 import java.util.Optional;
 
@@ -81,7 +83,8 @@
             WindowManager windowManager,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
-            @Main Resources resources) {
+            @Main Resources resources,
+            Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
         mContext = context;
         mWindowManager = windowManager;
         mIWindowManager = iWindowManager;
@@ -94,6 +97,10 @@
         if (mBarHeight < 0) {
             mBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         }
+        unfoldTransitionProgressProvider.ifPresent(
+                unfoldProgressProvider -> unfoldProgressProvider.addCallback(
+                        new JankMonitorTransitionProgressListener(
+                                /* attachedViewProvider=*/ () -> mStatusBarWindowView)));
     }
 
     public int getStatusBarHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index e2d0bb9..31407b1 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -66,6 +66,8 @@
             "android.theme.customization.accent_color";
     static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
             "android.theme.customization.system_palette";
+    static final String OVERLAY_CATEGORY_THEME_STYLE =
+            "android.theme.customization.theme_style";
 
     static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source";
 
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c86d77b..fb80551 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -66,6 +66,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.monet.ColorScheme;
+import com.android.systemui.monet.Style;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -121,8 +122,8 @@
     private boolean mNeedsOverlayCreation;
     // Dominant color extracted from wallpaper, NOT the color used on the overlay
     protected int mMainWallpaperColor = Color.TRANSPARENT;
-    // Accent color extracted from wallpaper, NOT the color used on the overlay
-    protected int mWallpaperAccentColor = Color.TRANSPARENT;
+    // Theme variant: Vibrant, Tonal, Expressive, etc
+    private Style mThemeStyle = Style.TONAL_SPOT;
     // Accent colors overlay
     private FabricatedOverlay mSecondaryOverlay;
     // Neutral system colors overlay
@@ -453,26 +454,20 @@
     private void reevaluateSystemTheme(boolean forceReload) {
         final WallpaperColors currentColors = mCurrentColors.get(mUserTracker.getUserId());
         final int mainColor;
-        final int accentCandidate;
         if (currentColors == null) {
             mainColor = Color.TRANSPARENT;
-            accentCandidate = Color.TRANSPARENT;
         } else {
             mainColor = getNeutralColor(currentColors);
-            accentCandidate = getAccentColor(currentColors);
         }
 
-        if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate
-                && !forceReload) {
+        if (mMainWallpaperColor == mainColor && !forceReload) {
             return;
         }
-
         mMainWallpaperColor = mainColor;
-        mWallpaperAccentColor = accentCandidate;
 
         if (mIsMonetEnabled) {
-            mSecondaryOverlay = getOverlay(mWallpaperAccentColor, ACCENT);
-            mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL);
+            mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
+            mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
             mNeedsOverlayCreation = true;
             if (DEBUG) {
                 Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
@@ -497,11 +492,11 @@
     /**
      * Given a color candidate, return an overlay definition.
      */
-    protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
+    protected @Nullable FabricatedOverlay getOverlay(int color, int type, Style style) {
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
                 & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
-        mColorScheme = new ColorScheme(color, nightMode);
+        mColorScheme = new ColorScheme(color, nightMode, style);
         List<Integer> colorShades = type == ACCENT
                 ? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
         String name = type == ACCENT ? "accent" : "neutral";
@@ -537,6 +532,7 @@
                 currentUser);
         if (DEBUG) Log.d(TAG, "updateThemeOverlays. Setting: " + overlayPackageJson);
         final Map<String, OverlayIdentifier> categoryToPackage = new ArrayMap<>();
+        Style newStyle = mThemeStyle;
         if (!TextUtils.isEmpty(overlayPackageJson)) {
             try {
                 JSONObject object = new JSONObject(overlayPackageJson);
@@ -547,11 +543,25 @@
                         categoryToPackage.put(category, identifier);
                     }
                 }
+
+                try {
+                    newStyle = Style.valueOf(
+                            object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
+                } catch (IllegalArgumentException e) {
+                    newStyle = Style.TONAL_SPOT;
+                }
             } catch (JSONException e) {
                 Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
             }
         }
 
+        if (mIsMonetEnabled && newStyle != mThemeStyle) {
+            mThemeStyle = newStyle;
+            mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
+            mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
+            mNeedsOverlayCreation = true;
+        }
+
         // Let's generate system overlay if the style picker decided to override it.
         OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
         if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) {
@@ -561,9 +571,11 @@
                     colorString = "#" + colorString;
                 }
                 int color = Color.parseColor(colorString);
-                mNeutralOverlay = getOverlay(color, NEUTRAL);
+                mNeutralOverlay = getOverlay(color, NEUTRAL, mThemeStyle);
+                mSecondaryOverlay = getOverlay(color, ACCENT, mThemeStyle);
                 mNeedsOverlayCreation = true;
                 categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+                categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
             } catch (Exception e) {
                 // Color.parseColor doesn't catch any exceptions from the calls it makes
                 Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e);
@@ -574,30 +586,6 @@
                 // setting. We need to sanitize the input, otherwise the overlay transaction will
                 // fail.
                 categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
-            } catch (NumberFormatException e) {
-                // This is a package name. All good, let's continue
-            }
-        }
-
-        // Same for accent color.
-        OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR);
-        if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) {
-            try {
-                String colorString =  accentPalette.getPackageName().toLowerCase();
-                if (!colorString.startsWith("#")) {
-                    colorString = "#" + colorString;
-                }
-                int color = Color.parseColor(colorString);
-                mSecondaryOverlay = getOverlay(color, ACCENT);
-                mNeedsOverlayCreation = true;
-                categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
-            } catch (Exception e) {
-                // Color.parseColor doesn't catch any exceptions from the calls it makes
-                Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName(), e);
-            }
-        } else if (!mIsMonetEnabled && accentPalette != null) {
-            try {
-                Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
                 categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
             } catch (NumberFormatException e) {
                 // This is a package name. All good, let's continue
@@ -642,7 +630,6 @@
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("mSystemColors=" + mCurrentColors);
         pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
-        pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor));
         pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
         pw.println("mNeutralOverlay=" + mNeutralOverlay);
         pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
@@ -650,5 +637,6 @@
         pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
         pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
         pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
+        pw.println("mThemeStyle=" + mThemeStyle);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 52c416ba..aaf35af 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -30,17 +30,17 @@
 import javax.inject.Inject
 
 /**
- * Controls folding to AOD animation: when AOD is enabled and foldable device is folded
- * we play a special AOD animation on the outer screen
+ * Controls folding to AOD animation: when AOD is enabled and foldable device is folded we play a
+ * special AOD animation on the outer screen
  */
 @SysUIUnfoldScope
-class FoldAodAnimationController @Inject constructor(
+class FoldAodAnimationController
+@Inject
+constructor(
     private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
     private val wakefulnessLifecycle: WakefulnessLifecycle,
     private val globalSettings: GlobalSettings
-) : CallbackController<FoldAodAnimationStatus>,
-    ScreenOffAnimation,
-    WakefulnessLifecycle.Observer {
+) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
 
     private var alwaysOnEnabled: Boolean = false
     private var isScrimOpaque: Boolean = false
@@ -58,17 +58,13 @@
         wakefulnessLifecycle.addObserver(this)
     }
 
-    /**
-     * Returns true if we should run fold to AOD animation
-     */
-    override fun shouldPlayAnimation(): Boolean =
-        shouldPlayAnimation
+    /** Returns true if we should run fold to AOD animation */
+    override fun shouldPlayAnimation(): Boolean = shouldPlayAnimation
 
     override fun startAnimation(): Boolean =
         if (alwaysOnEnabled &&
             wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
-            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
-        ) {
+            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0") {
             shouldPlayAnimation = true
 
             isAnimationPlaying = true
@@ -107,9 +103,7 @@
         }
     }
 
-    /**
-     * Called when keyguard scrim opaque changed
-     */
+    /** Called when keyguard scrim opaque changed */
     override fun onScrimOpaqueChanged(isOpaque: Boolean) {
         isScrimOpaque = isOpaque
 
@@ -130,27 +124,21 @@
         }
     }
 
-    override fun isAnimationPlaying(): Boolean =
-        isAnimationPlaying
+    override fun isAnimationPlaying(): Boolean = isAnimationPlaying
 
-    override fun isKeyguardHideDelayed(): Boolean =
-        isAnimationPlaying()
+    override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
 
-    override fun shouldShowAodIconsWhenShade(): Boolean =
-        shouldPlayAnimation()
+    override fun shouldShowAodIconsWhenShade(): Boolean = shouldPlayAnimation()
 
-    override fun shouldAnimateAodIcons(): Boolean =
-        !shouldPlayAnimation()
+    override fun shouldAnimateAodIcons(): Boolean = !shouldPlayAnimation()
 
-    override fun shouldAnimateDozingChange(): Boolean =
-        !shouldPlayAnimation()
+    override fun shouldAnimateDozingChange(): Boolean = !shouldPlayAnimation()
 
-    override fun shouldAnimateClockChange(): Boolean =
-        !isAnimationPlaying()
+    override fun shouldAnimateClockChange(): Boolean = !isAnimationPlaying()
 
-    /**
-     * Called when AOD status is changed
-     */
+    override fun shouldDelayDisplayDozeTransition(): Boolean = shouldPlayAnimation()
+
+    /** Called when AOD status is changed */
     override fun onAlwaysOnChanged(alwaysOn: Boolean) {
         alwaysOnEnabled = alwaysOn
     }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt
new file mode 100644
index 0000000..1451c03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import com.android.internal.util.FrameworkStatsLog
+
+/** Logs fold state changes. */
+class FoldStateLogger(private val foldStateLoggingProvider: FoldStateLoggingProvider) :
+    FoldStateLoggingProvider.FoldStateLoggingListener {
+
+    fun init() {
+        foldStateLoggingProvider.addCallback(this)
+    }
+
+    override fun onFoldUpdate(foldStateUpdate: FoldStateChange) {
+        FrameworkStatsLog.write(
+            FrameworkStatsLog.FOLD_STATE_DURATION_REPORTED,
+            foldStateUpdate.previous,
+            foldStateUpdate.current,
+            foldStateUpdate.dtMillis)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt
new file mode 100644
index 0000000..1f5959e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.annotation.IntDef
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
+
+/** Reports device fold states for logging purposes. */
+// TODO(b/198305865): Log state changes.
+interface FoldStateLoggingProvider : CallbackController<FoldStateLoggingListener> {
+
+    fun init()
+    fun uninit()
+
+    interface FoldStateLoggingListener {
+        fun onFoldUpdate(foldStateUpdate: FoldStateChange)
+    }
+
+    @IntDef(prefix = ["LOGGED_FOLD_STATE_"], value = [FULLY_OPENED, FULLY_CLOSED, HALF_OPENED])
+    @Retention(AnnotationRetention.SOURCE)
+    annotation class LoggedFoldedStates
+}
+
+data class FoldStateChange(
+    @LoggedFoldedStates val previous: Int,
+    @LoggedFoldedStates val current: Int,
+    val dtMillis: Long
+)
+
+const val FULLY_OPENED = 1
+const val FULLY_CLOSED = 2
+const val HALF_OPENED = 3
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt
new file mode 100644
index 0000000..2683971
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.util.Log
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
+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.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.util.time.SystemClock
+
+/**
+ * Reports device fold states for logging purposes.
+ *
+ * Wraps the state provided by [FoldStateProvider] to output only [FULLY_OPENED], [FULLY_CLOSED] and
+ * [HALF_OPENED] for logging purposes.
+ *
+ * Note that [HALF_OPENED] state is only emitted after the device angle is stable for some timeout.
+ * Check [FoldStateProvider] impl for it.
+ *
+ * This doesn't log the following transitions:
+ * - [HALF_OPENED] -> [FULLY_OPENED]: not interesting, as there is no transition going on
+ * - [HALF_OPENED] -> [HALF_OPENED]: not meaningful.
+ */
+class FoldStateLoggingProviderImpl(
+    private val foldStateProvider: FoldStateProvider,
+    private val clock: SystemClock
+) : FoldStateLoggingProvider, FoldStateProvider.FoldUpdatesListener {
+
+    private val outputListeners: MutableList<FoldStateLoggingListener> = mutableListOf()
+
+    @LoggedFoldedStates private var lastState: Int? = null
+    private var actionStartMillis: Long? = null
+
+    override fun init() {
+        foldStateProvider.addCallback(this)
+        foldStateProvider.start()
+    }
+
+    override fun uninit() {
+        foldStateProvider.removeCallback(this)
+        foldStateProvider.stop()
+    }
+
+    override fun onHingeAngleUpdate(angle: Float) {}
+
+    override fun onFoldUpdate(@FoldUpdate update: Int) {
+        val now = clock.elapsedRealtime()
+        when (update) {
+            FOLD_UPDATE_START_OPENING -> {
+                lastState = FULLY_CLOSED
+                actionStartMillis = now
+            }
+            FOLD_UPDATE_START_CLOSING -> actionStartMillis = now
+            FOLD_UPDATE_FINISH_HALF_OPEN -> dispatchState(HALF_OPENED)
+            FOLD_UPDATE_FINISH_FULL_OPEN -> dispatchState(FULLY_OPENED)
+            FOLD_UPDATE_FINISH_CLOSED -> dispatchState(FULLY_CLOSED)
+        }
+    }
+
+    private fun dispatchState(@LoggedFoldedStates current: Int) {
+        val now = clock.elapsedRealtime()
+        val previous = lastState
+        val lastActionStart = actionStartMillis
+
+        if (previous != null && previous != current && lastActionStart != null) {
+            val time = now - lastActionStart
+            val foldStateChange = FoldStateChange(previous, current, time)
+            outputListeners.forEach { it.onFoldUpdate(foldStateChange) }
+            if (DEBUG) {
+                Log.d(TAG, "From $previous to $current in $time")
+            }
+        }
+
+        actionStartMillis = null
+        lastState = current
+    }
+
+    override fun addCallback(listener: FoldStateLoggingListener) {
+        outputListeners.add(listener)
+    }
+
+    override fun removeCallback(listener: FoldStateLoggingListener) {
+        outputListeners.remove(listener)
+    }
+}
+
+private const val DEBUG = false
+private const val TAG = "FoldStateLoggingProviderImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index ccde316..07f9c54 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -17,10 +17,11 @@
 package com.android.systemui.unfold
 
 import com.android.keyguard.KeyguardUnfoldTransition
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
-import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.kotlin.getOrNull
 import dagger.BindsInstance
 import dagger.Module
 import dagger.Provides
@@ -36,15 +37,17 @@
 
 /**
  * Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
- * [@SysUIUnfoldScope]. Since [SysUIUnfoldComponent] depends upon:
+ * [@SysUIUnfoldScope].
+ *
+ * Since [SysUIUnfoldComponent] depends upon:
  * * [Optional<UnfoldTransitionProgressProvider>]
  * * [Optional<ScopedUnfoldTransitionProgressProvider>]
  * * [Optional<NaturalRotationProgressProvider>]
+ *
  * no objects will get constructed if these parameters are empty.
  */
 @Module(subcomponents = [SysUIUnfoldComponent::class])
 class SysUIUnfoldModule {
-    constructor() {}
 
     @Provides
     @SysUISingleton
@@ -53,12 +56,16 @@
         rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
         @Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
         factory: SysUIUnfoldComponent.Factory
-    ) =
-        provider.flatMap { p1 ->
-            rotationProvider.flatMap { p2 ->
-                scopedProvider.map { p3 -> factory.create(p1, p2, p3) }
-            }
+    ): Optional<SysUIUnfoldComponent> {
+        val p1 = provider.getOrNull()
+        val p2 = rotationProvider.getOrNull()
+        val p3 = scopedProvider.getOrNull()
+        return if (p1 == null || p2 == null || p3 == null) {
+            Optional.empty()
+        } else {
+            Optional.of(factory.create(p1, p2, p3))
         }
+    }
 }
 
 @SysUIUnfoldScope
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
index 7f63d6c..79b42b8 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
@@ -29,12 +29,16 @@
  * Logs performance metrics regarding time to turn the inner screen on.
  *
  * This class assumes that [onFoldEvent] is always called before [onScreenTurnedOn].
+ *
  * This should be used from only one process.
+ *
  * For now, the focus is on the time the inner display is visible, but in the future, it is easily
  * possible to monitor the time to go from the inner screen to the outer.
  */
 @SysUISingleton
-class UnfoldLatencyTracker @Inject constructor(
+class UnfoldLatencyTracker
+@Inject
+constructor(
     private val latencyTracker: LatencyTracker,
     private val deviceStateManager: DeviceStateManager,
     @UiBackground private val uiBgExecutor: Executor,
@@ -45,8 +49,11 @@
     private var folded: Boolean? = null
     private val foldStateListener = FoldStateListener(context)
     private val isFoldable: Boolean
-        get() = context.resources.getIntArray(
-            com.android.internal.R.array.config_foldedDeviceStates).isNotEmpty()
+        get() =
+            context
+                .resources
+                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
+                .isNotEmpty()
 
     /** Registers for relevant events only if the device is foldable. */
     fun init() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 0b89ef2..4b09a58 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -20,8 +20,8 @@
 import android.graphics.PixelFormat
 import android.hardware.devicestate.DeviceStateManager
 import android.hardware.devicestate.DeviceStateManager.FoldStateListener
-import android.hardware.input.InputManager
 import android.hardware.display.DisplayManager
+import android.hardware.input.InputManager
 import android.os.Handler
 import android.os.Trace
 import android.view.Choreographer
@@ -46,7 +46,9 @@
 import javax.inject.Inject
 
 @SysUIUnfoldScope
-class UnfoldLightRevealOverlayAnimation @Inject constructor(
+class UnfoldLightRevealOverlayAnimation
+@Inject
+constructor(
     private val context: Context,
     private val deviceStateManager: DeviceStateManager,
     private val displayManager: DisplayManager,
@@ -75,12 +77,13 @@
         deviceStateManager.registerCallback(executor, FoldListener())
         unfoldTransitionProgressProvider.addCallback(transitionListener)
 
-        val containerBuilder = SurfaceControl.Builder(SurfaceSession())
-            .setContainerLayer()
-            .setName("unfold-overlay-container")
+        val containerBuilder =
+            SurfaceControl.Builder(SurfaceSession())
+                .setContainerLayer()
+                .setName("unfold-overlay-container")
 
-        displayAreaHelper.get().attachToRootDisplayArea(Display.DEFAULT_DISPLAY,
-            containerBuilder) { builder ->
+        displayAreaHelper.get().attachToRootDisplayArea(
+                Display.DEFAULT_DISPLAY, containerBuilder) { builder ->
             executor.execute {
                 overlayContainer = builder.build()
 
@@ -89,13 +92,13 @@
                     .show(overlayContainer)
                     .apply()
 
-                wwm = WindowlessWindowManager(context.resources.configuration,
-                    overlayContainer, null)
+                wwm =
+                    WindowlessWindowManager(context.resources.configuration, overlayContainer, null)
             }
         }
 
-        displayManager.registerDisplayListener(displayListener, handler,
-            DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
+        displayManager.registerDisplayListener(
+            displayListener, handler, DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
 
         // Get unfolded display size immediately as 'current display info' might be
         // not up-to-date during unfolding
@@ -136,8 +139,8 @@
         ensureOverlayRemoved()
 
         val newRoot = SurfaceControlViewHost(context, context.display!!, wwm, false)
-        val newView = LightRevealScrim(context, null)
-            .apply {
+        val newView =
+            LightRevealScrim(context, null).apply {
                 revealEffect = createLightRevealEffect()
                 isScrimOpaqueChangedListener = Consumer {}
                 revealAmount = 0f
@@ -147,8 +150,7 @@
         newRoot.setView(newView, params)
 
         onOverlayReady?.let { callback ->
-            Trace.beginAsyncSection(
-                "UnfoldLightRevealOverlayAnimation#relayout", 0)
+            Trace.beginAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
 
             newRoot.relayout(params) { transaction ->
                 val vsyncId = Choreographer.getSfInstance().vsyncId
@@ -161,15 +163,11 @@
                     // (turn on the brightness) only when the content is actually visible as it
                     // might be presented only in the next frame.
                     // See b/197538198
-                    transaction.setFrameTimelineVsync(vsyncId)
-                        .apply(/* sync */true)
+                    transaction.setFrameTimelineVsync(vsyncId).apply(/* sync */ true)
 
-                    transaction
-                        .setFrameTimelineVsync(vsyncId + 1)
-                        .apply(/* sync */ true)
+                    transaction.setFrameTimelineVsync(vsyncId + 1).apply(/* sync */ true)
 
-                    Trace.endAsyncSection(
-                        "UnfoldLightRevealOverlayAnimation#relayout", 0)
+                    Trace.endAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
                     callback.run()
                 }
             }
@@ -185,10 +183,10 @@
         val rotation = context.display!!.rotation
         val isNatural = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
 
-        params.height = if (isNatural)
-            unfoldedDisplayInfo.naturalHeight else unfoldedDisplayInfo.naturalWidth
-        params.width = if (isNatural)
-            unfoldedDisplayInfo.naturalWidth else unfoldedDisplayInfo.naturalHeight
+        params.height =
+            if (isNatural) unfoldedDisplayInfo.naturalHeight else unfoldedDisplayInfo.naturalWidth
+        params.width =
+            if (isNatural) unfoldedDisplayInfo.naturalWidth else unfoldedDisplayInfo.naturalHeight
 
         params.format = PixelFormat.TRANSLUCENT
         params.type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
@@ -206,8 +204,8 @@
     }
 
     private fun createLightRevealEffect(): LightRevealEffect {
-        val isVerticalFold = currentRotation == Surface.ROTATION_0 ||
-            currentRotation == Surface.ROTATION_180
+        val isVerticalFold =
+            currentRotation == Surface.ROTATION_0 || currentRotation == Surface.ROTATION_180
         return LinearLightRevealEffect(isVertical = isVerticalFold)
     }
 
@@ -218,7 +216,8 @@
     }
 
     private fun getUnfoldedDisplayInfo(): DisplayInfo =
-        displayManager.displays
+        displayManager
+            .displays
             .asSequence()
             .map { DisplayInfo().apply { it.getDisplayInfo(this) } }
             .filter { it.type == Display.TYPE_INTERNAL }
@@ -255,18 +254,19 @@
             }
         }
 
-        override fun onDisplayAdded(displayId: Int) {
-        }
+        override fun onDisplayAdded(displayId: Int) {}
 
-        override fun onDisplayRemoved(displayId: Int) {
-        }
+        override fun onDisplayRemoved(displayId: Int) {}
     }
 
-    private inner class FoldListener : FoldStateListener(context, Consumer { isFolded ->
-        if (isFolded) {
-            ensureOverlayRemoved()
-            isUnfoldHandled = false
-        }
-        this.isFolded = isFolded
-    })
+    private inner class FoldListener :
+        FoldStateListener(
+            context,
+            Consumer { isFolded ->
+                if (isFolded) {
+                    ensureOverlayRemoved()
+                    isUnfoldHandled = false
+                }
+                this.isFolded = isFolded
+            })
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
index bd04ad8..2325acf 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
@@ -21,29 +21,23 @@
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener
 import java.util.concurrent.Executor
 
-class UnfoldProgressProvider(
-    private val unfoldProgressProvider: UnfoldTransitionProgressProvider
-) : ShellUnfoldProgressProvider {
+class UnfoldProgressProvider(private val unfoldProgressProvider: UnfoldTransitionProgressProvider) :
+    ShellUnfoldProgressProvider {
 
     override fun addListener(executor: Executor, listener: UnfoldListener) {
-        unfoldProgressProvider.addCallback(object : TransitionProgressListener {
-            override fun onTransitionStarted() {
-                executor.execute {
-                    listener.onStateChangeStarted()
+        unfoldProgressProvider.addCallback(
+            object : TransitionProgressListener {
+                override fun onTransitionStarted() {
+                    executor.execute { listener.onStateChangeStarted() }
                 }
-            }
 
-            override fun onTransitionProgress(progress: Float) {
-                executor.execute {
-                    listener.onStateChangeProgress(progress)
+                override fun onTransitionProgress(progress: Float) {
+                    executor.execute { listener.onStateChangeProgress(progress) }
                 }
-            }
 
-            override fun onTransitionFinished() {
-                executor.execute {
-                    listener.onStateChangeFinished()
+                override fun onTransitionFinished() {
+                    executor.execute { listener.onStateChangeFinished() }
                 }
-            }
-        })
+            })
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 75dfd48..d2d2361 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -17,57 +17,51 @@
 package com.android.systemui.unfold
 
 import android.content.Context
-import android.hardware.SensorManager
-import android.hardware.devicestate.DeviceStateManager
-import android.os.Handler
 import android.view.IWindowManager
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.LifecycleScreenStatusProvider
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
+import com.android.systemui.util.time.SystemClockImpl
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
-import java.util.concurrent.Executor
 import javax.inject.Named
 import javax.inject.Singleton
 
-@Module
+@Module(includes = [UnfoldSharedModule::class])
 class UnfoldTransitionModule {
 
+    @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
+
     @Provides
     @Singleton
-    fun provideUnfoldTransitionProgressProvider(
-        context: Context,
+    fun providesFoldStateLoggingProvider(
         config: UnfoldTransitionConfig,
-        screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
-        deviceStateManager: DeviceStateManager,
-        sensorManager: SensorManager,
-        @Main executor: Executor,
-        @Main handler: Handler
-    ) =
-        if (config.isEnabled) {
-            Optional.of(
-                createUnfoldTransitionProgressProvider(
-                    context,
-                    config,
-                    screenStatusProvider.get(),
-                    deviceStateManager,
-                    sensorManager,
-                    handler,
-                    executor,
-                    tracingTagPrefix = "systemui"
-                )
-            )
+        foldStateProvider: Lazy<FoldStateProvider>
+    ): Optional<FoldStateLoggingProvider> =
+        if (config.isHingeAngleEnabled) {
+            Optional.of(FoldStateLoggingProviderImpl(foldStateProvider.get(), SystemClockImpl()))
         } else {
             Optional.empty()
         }
 
     @Provides
     @Singleton
+    fun providesFoldStateLogger(
+        optionalFoldStateLoggingProvider: Optional<FoldStateLoggingProvider>
+    ): Optional<FoldStateLogger> =
+        optionalFoldStateLoggingProvider.map { FoldStateLoggingProvider ->
+            FoldStateLogger(FoldStateLoggingProvider)
+        }
+
+    @Provides
+    @Singleton
     fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
         createConfig(context)
 
@@ -77,13 +71,9 @@
         context: Context,
         windowManager: IWindowManager,
         unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
-    ) =
-        unfoldTransitionProgressProvider.map {
-            provider -> NaturalRotationUnfoldProgressProvider(
-                context,
-                windowManager,
-                provider
-            )
+    ): Optional<NaturalRotationUnfoldProgressProvider> =
+        unfoldTransitionProgressProvider.map { provider ->
+            NaturalRotationUnfoldProgressProvider(context, windowManager, provider)
         }
 
     @Provides
@@ -91,10 +81,8 @@
     @Singleton
     fun provideStatusBarScopedTransitionProvider(
         source: Optional<NaturalRotationUnfoldProgressProvider>
-    ) =
-        source.map {
-            provider -> ScopedUnfoldTransitionProgressProvider(provider)
-        }
+    ): Optional<ScopedUnfoldTransitionProgressProvider> =
+        source.map { provider -> ScopedUnfoldTransitionProgressProvider(provider) }
 
     @Provides
     @Singleton
@@ -102,11 +90,14 @@
         config: UnfoldTransitionConfig,
         provider: Optional<UnfoldTransitionProgressProvider>
     ): ShellUnfoldProgressProvider =
-        if (config.isEnabled && provider.isPresent()) {
+        if (config.isEnabled && provider.isPresent) {
             UnfoldProgressProvider(provider.get())
         } else {
             ShellUnfoldProgressProvider.NO_PROVIDER
         }
+
+    @Provides
+    fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
 }
 
 const val UNFOLD_STATUS_BAR = "unfold_status_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
index a184315..d723760 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
@@ -21,7 +21,9 @@
 import javax.inject.Inject
 
 @SysUIUnfoldScope
-class UnfoldTransitionWallpaperController @Inject constructor(
+class UnfoldTransitionWallpaperController
+@Inject
+constructor(
     private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
     private val wallpaperController: WallpaperController
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 90c7f1f..cf361ec 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -167,9 +167,11 @@
         mStorageManager.registerListener(mListener);
 
         mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // Kick current state into place
         final List<DiskInfo> disks = mStorageManager.getDisks();
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
index aec14be..d10e890 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
@@ -70,6 +70,10 @@
         mEnableUsb.setText(getString(R.string.usb_disable_contaminant_detection));
         mGotIt.setText(getString(R.string.got_it));
         mLearnMore.setText(getString(R.string.learn_more));
+        if (getResources().getBoolean(
+                com.android.internal.R.bool.config_settingsHelpLinksEnabled)) {
+            mLearnMore.setVisibility(View.VISIBLE);
+        }
 
         mEnableUsb.setOnClickListener(this);
         mGotIt.setOnClickListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
index a40cf4f..5b188b2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
@@ -63,7 +63,8 @@
         setOnDismissListener(this);
 
         final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        context.registerReceiver(mReceiver, filter);
+        context.registerReceiver(mReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     abstract protected void cleanUp();
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 2e183b3..ba9b638 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -223,8 +223,7 @@
         }
         mUiEventLogger.log(WalletUiEvent.QAW_CLICK_CARD);
 
-        mActivityStarter.startActivity(
-                ((QAWalletCardViewInfo) cardInfo).mWalletCard.getPendingIntent().getIntent(), true);
+        mActivityStarter.startPendingIntentDismissingKeyguard(cardInfo.getPendingIntent());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e8f6de7..69ebfe8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -74,6 +74,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -88,6 +89,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Optional;
@@ -111,8 +113,10 @@
     private final INotificationManager mNotificationManager;
     private final NotificationVisibilityProvider mVisibilityProvider;
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final NotificationLockscreenUserManager mNotifUserManager;
     private final NotificationGroupManagerLegacy mNotificationGroupManager;
     private final NotificationEntryManager mNotificationEntryManager;
+    private final CommonNotifCollection mCommonNotifCollection;
     private final NotifPipeline mNotifPipeline;
     private final Executor mSysuiMainExecutor;
 
@@ -139,6 +143,7 @@
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManagerLegacy groupManager,
             NotificationEntryManager entryManager,
+            CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
             NotifPipelineFlags notifPipelineFlags,
@@ -150,8 +155,8 @@
                     configurationController, statusBarService, notificationManager,
                     visibilityProvider,
                     interruptionStateProvider, zenModeController, notifUserManager,
-                    groupManager, entryManager, notifPipeline, sysUiState, notifPipelineFlags,
-                    dumpManager, sysuiMainExecutor);
+                    groupManager, entryManager, notifCollection, notifPipeline, sysUiState,
+                    notifPipelineFlags, dumpManager, sysuiMainExecutor);
         } else {
             return null;
         }
@@ -172,6 +177,7 @@
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManagerLegacy groupManager,
             NotificationEntryManager entryManager,
+            CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
             NotifPipelineFlags notifPipelineFlags,
@@ -184,8 +190,10 @@
         mNotificationManager = notificationManager;
         mVisibilityProvider = visibilityProvider;
         mNotificationInterruptStateProvider = interruptionStateProvider;
+        mNotifUserManager = notifUserManager;
         mNotificationGroupManager = groupManager;
         mNotificationEntryManager = entryManager;
+        mCommonNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
         mSysuiMainExecutor = sysuiMainExecutor;
 
@@ -264,8 +272,7 @@
             @Override
             public void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback) {
                 sysuiMainExecutor.execute(() -> {
-                    NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     callback.accept(entry == null ? null : notifToBubbleEntry(entry));
                 });
             }
@@ -275,11 +282,11 @@
                     Consumer<List<BubbleEntry>> callback) {
                 sysuiMainExecutor.execute(() -> {
                     List<BubbleEntry> result = new ArrayList<>();
-                    List<NotificationEntry> activeEntries =
-                            mNotificationEntryManager.getActiveNotificationsForCurrentUser();
-                    for (int i = 0; i < activeEntries.size(); i++) {
-                        NotificationEntry entry = activeEntries.get(i);
-                        if (savedBubbleKeys.contains(entry.getKey())
+                    final Collection<NotificationEntry> activeEntries =
+                            mCommonNotifCollection.getAllNotifs();
+                    for (NotificationEntry entry : activeEntries) {
+                        if (mNotifUserManager.isCurrentProfile(entry.getSbn().getUserId())
+                                && savedBubbleKeys.contains(entry.getKey())
                                 && mNotificationInterruptStateProvider.shouldBubbleUp(entry)
                                 && entry.isBubble()) {
                             result.add(notifToBubbleEntry(entry));
@@ -292,8 +299,7 @@
             @Override
             public void setNotificationInterruption(String key) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null
                             && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
                         entry.setInterruption();
@@ -311,8 +317,7 @@
             @Override
             public void notifyRemoveNotification(String key, int reason) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null) {
                         for (NotifCallback cb : mCallbacks) {
                             cb.removeNotification(entry, getDismissedByUserStats(entry, true),
@@ -334,8 +339,7 @@
             @Override
             public void notifyMaybeCancelSummary(String key) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null) {
                         for (NotifCallback cb : mCallbacks) {
                             cb.maybeCancelSummary(entry);
@@ -347,8 +351,7 @@
             @Override
             public void removeNotificationEntry(String key) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null) {
                         mNotificationGroupManager.onEntryRemoved(entry);
                     }
@@ -358,8 +361,7 @@
             @Override
             public void updateNotificationBubbleButton(String key) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null && entry.getRow() != null) {
                         entry.getRow().updateBubbleButton();
                     }
@@ -369,8 +371,7 @@
             @Override
             public void updateNotificationSuppression(String key) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null) {
                         mNotificationGroupManager.updateSuppression(entry);
                     }
@@ -402,8 +403,7 @@
             @Override
             public void onUnbubbleConversation(String key) {
                 sysuiMainExecutor.execute(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
+                    final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
                     if (entry != null) {
                         onUserChangedBubble(entry, false /* shouldBubble */);
                     }
@@ -427,9 +427,11 @@
                     }
 
                     @Override
-                    public void onEntryRemoved(NotificationEntry entry,
+                    public void onEntryRemoved(
+                            NotificationEntry entry,
                             @Nullable NotificationVisibility visibility,
-                            boolean removedByUser, int reason) {
+                            boolean removedByUser,
+                            int reason) {
                         BubblesManager.this.onEntryRemoved(entry);
                     }
 
@@ -437,6 +439,18 @@
                     public void onNotificationRankingUpdated(RankingMap rankingMap) {
                         BubblesManager.this.onRankingUpdate(rankingMap);
                     }
+
+                    @Override
+                    public void onNotificationChannelModified(
+                            String pkgName,
+                            UserHandle user,
+                            NotificationChannel channel,
+                            int modificationType) {
+                        BubblesManager.this.onNotificationChannelModified(pkgName,
+                                user,
+                                channel,
+                                modificationType);
+                    }
                 });
 
         // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
@@ -556,6 +570,19 @@
             public void onRankingUpdate(RankingMap rankingMap) {
                 BubblesManager.this.onRankingUpdate(rankingMap);
             }
+
+            @Override
+            public void onNotificationChannelModified(
+                    String pkgName,
+                    UserHandle user,
+                    NotificationChannel channel,
+                    int modificationType) {
+                BubblesManager.this.onNotificationChannelModified(
+                        pkgName,
+                        user,
+                        channel,
+                        modificationType);
+            }
         });
     }
 
@@ -580,7 +607,7 @@
         HashMap<String, Pair<BubbleEntry, Boolean>> pendingOrActiveNotif = new HashMap<>();
         for (int i = 0; i < orderedKeys.length; i++) {
             String key = orderedKeys[i];
-            NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+            final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
             BubbleEntry bubbleEntry = entry != null
                     ? notifToBubbleEntry(entry)
                     : null;
@@ -592,6 +619,14 @@
         mBubbles.onRankingUpdated(rankingMap, pendingOrActiveNotif);
     }
 
+    void onNotificationChannelModified(
+            String pkg,
+            UserHandle user,
+            NotificationChannel channel,
+            int modificationType) {
+        mBubbles.onNotificationChannelModified(pkg, user, channel, modificationType);
+    }
+
     /**
      * Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
      * Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
@@ -761,7 +796,7 @@
     }
 
     static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
-        return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
+        return new BubbleEntry(e.getSbn(), e.getRanking(), e.isDismissable(),
                 e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
                 e.shouldSuppressPeek());
     }
diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index cbd6e86..0369d5b 100644
--- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -110,6 +110,17 @@
     private Collection<String> getClassNamesFromClassPath() {
         ClassPathScanner scanner = new ClassPathScanner(mContext.getPackageCodePath());
 
+        ChainedClassNameFilter filter = makeClassNameFilter();
+
+        try {
+            return scanner.getClassPathEntries(filter);
+        } catch (IOException e) {
+            Log.e(getTag(), "Failed to scan classes", e);
+        }
+        return Collections.emptyList();
+    }
+
+    protected ChainedClassNameFilter makeClassNameFilter() {
         ChainedClassNameFilter filter = new ChainedClassNameFilter();
 
         filter.add(new ExternalClassNameFilter());
@@ -122,13 +133,7 @@
         // the main SystemUI process. Therefore, exclude this package
         // from the base class whitelist.
         filter.add(s -> !s.startsWith("com.android.systemui.screenshot"));
-
-        try {
-            return scanner.getClassPathEntries(filter);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to scan classes", e);
-        }
-        return Collections.emptyList();
+        return filter;
     }
 
     private String getClsStr() {
@@ -212,8 +217,12 @@
      * as loggable to limit log spam during normal use.
      */
     private void logDebug(String msg) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, msg);
+        if (Log.isLoggable(getTag(), Log.DEBUG)) {
+            Log.d(getTag(), msg);
         }
     }
+
+    protected String getTag() {
+        return TAG;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
new file mode 100644
index 0000000..6bc6505
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.keyguard
+
+import android.hardware.biometrics.BiometricSourceType
+import org.mockito.Mockito.verify
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
+    @Mock
+    lateinit var uiEventLogger: UiEventLogger
+    @Mock
+    lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock
+    lateinit var dumpManager: DumpManager
+    @Mock
+    lateinit var strongAuthTracker: KeyguardUpdateMonitor.StrongAuthTracker
+
+    @Captor
+    lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+    lateinit var updateMonitorCallback: KeyguardUpdateMonitorCallback
+
+    lateinit var keyguardBiometricLockoutLogger: KeyguardBiometricLockoutLogger
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker)
+        keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
+                mContext,
+                uiEventLogger,
+                keyguardUpdateMonitor,
+                dumpManager)
+    }
+
+    @Test
+    fun test_logsOnStart() {
+        // GIVEN is encrypted / lockdown before start
+        whenever(keyguardUpdateMonitor.isEncryptedOrLockdown(anyInt()))
+                .thenReturn(true)
+
+        // WHEN start
+        keyguardBiometricLockoutLogger.start()
+
+        // THEN encrypted / lockdown state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+    }
+
+    @Test
+    fun test_logTimeoutChange() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c timeout
+        whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
+
+        // WHEN primary auth requirement changes
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+    }
+
+    @Test
+    fun test_logUnattendedUpdate() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c unattended update
+        whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
+
+        // WHEN primary auth requirement changes
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+    }
+
+    @Test
+    fun test_logMultipleChanges() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c timeout
+        whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+                        or STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
+
+        // WHEN primary auth requirement changes
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN primary auth required state is logged with all the reasons
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+
+        // WHEN onStrongAuthStateChanged is called again
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN no more events are sent since there haven't been any changes
+        verifyNoMoreInteractions(uiEventLogger)
+    }
+
+    @Test
+    fun test_logFaceLockout() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c face lock
+        whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(true)
+
+        // WHEN lockout state changes
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+
+        // WHEN face lockout is reset
+        whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(false)
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+    }
+
+    @Test
+    fun test_logFingerprintLockout() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c fingerprint lock
+        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
+
+        // WHEN lockout state changes
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+
+        // WHEN fingerprint lockout is reset
+        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+    }
+
+    fun captureUpdateMonitorCallback() {
+        verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallbackCaptor.capture())
+        updateMonitorCallback = updateMonitorCallbackCaptor.value
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index c873804..599e547 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -116,6 +117,8 @@
     @Mock
     private GlobalSettings mGlobalSettings;
     @Mock
+    private FeatureFlags mFeatureFlags;
+    @Mock
     private UserSwitcherController mUserSwitcherController;
     private Configuration mConfiguration;
 
@@ -151,7 +154,7 @@
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
                 mConfigurationController, mFalsingCollector, mFalsingManager,
-                mUserSwitcherController, mGlobalSettings).create(mSecurityCallback);
+                mUserSwitcherController, mFeatureFlags, mGlobalSettings).create(mSecurityCallback);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 70792cf..08d881f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -107,6 +107,7 @@
 import org.mockito.MockitoSession;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -175,6 +176,8 @@
     private LatencyTracker mLatencyTracker;
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+    @Mock
+    private KeyguardUpdateMonitorCallback mTestCallback;
     // Direct executor
     private Executor mBackgroundExecutor = Runnable::run;
     private Executor mMainExecutor = Runnable::run;
@@ -252,11 +255,13 @@
 
         verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
         mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
+        mKeyguardUpdateMonitor.registerCallback(mTestCallback);
     }
 
     @After
     public void tearDown() {
         mMockitoSession.finishMocking();
+        mKeyguardUpdateMonitor.removeCallback(mTestCallback);
         mKeyguardUpdateMonitor.destroy();
     }
 
@@ -596,7 +601,8 @@
         mTestableLooper.processAllMessages();
         when(mKeyguardBypassController.canBypass()).thenReturn(true);
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
-                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+                new ArrayList<>());
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
         verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
     }
@@ -606,7 +612,7 @@
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
-                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
         verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
                 anyBoolean());
@@ -751,7 +757,8 @@
     @Test
     public void testGetUserCanSkipBouncer_whenTrust() {
         int user = KeyguardUpdateMonitor.getCurrentUser();
-        mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
+        mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */,
+                new ArrayList<>());
         assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
     }
 
@@ -982,7 +989,7 @@
 
         // WHEN trust is enabled (ie: via smartlock)
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
-                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
 
         // THEN we shouldn't listen for udfps
         assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1066,6 +1073,17 @@
                 anyBoolean());
     }
 
+    @Test
+    public void testShowTrustGrantedMessage_onTrustGranted() {
+        // WHEN trust is enabled (ie: via some trust agent) with a trustGranted string
+        mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+                KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+                Arrays.asList("Unlocked by wearable"));
+
+        // THEN the showTrustGrantedMessage should be called with the first message
+        verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
+    }
+
     private void setKeyguardBouncerVisibility(boolean isVisible) {
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
index 7d55623..7c121e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -79,7 +80,7 @@
         intent.putExtra(SliceBroadcastRelay.EXTRA_URI, testUri);
 
         mRelayHandler.handleIntent(intent);
-        verify(mSpyContext).registerReceiver(any(), eq(value));
+        verify(mSpyContext).registerReceiver(any(), eq(value), anyInt());
     }
 
     @Test
@@ -99,7 +100,7 @@
 
         mRelayHandler.handleIntent(intent);
         ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
+        verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
 
         intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
         intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
@@ -138,7 +139,7 @@
 
         mRelayHandler.handleIntent(intent);
         ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
+        verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
         relay.getValue().onReceive(mSpyContext, new Intent(TEST_ACTION));
 
         verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index 40549d69..8c20b24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -29,8 +29,10 @@
 import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.Rule;
+import org.mockito.Mockito;
 
 public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
 
@@ -78,6 +80,11 @@
         SystemUIFactory.cleanup();
     }
 
+    @AfterClass
+    public static void mockitoTeardown() {
+        Mockito.framework().clearInlineMocks();
+    }
+
     @Override
     protected SysuiTestableContext getContext() {
         return new SysuiTestableContext(InstrumentationRegistry.getContext(), mLeakCheck);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index 3d679de..0674ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -83,6 +83,16 @@
     }
 
     @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+        if (receiver != null) {
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
+        }
+        return super.registerReceiver(receiver, filter, flags);
+    }
+
+    @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
             String broadcastPermission, Handler scheduler) {
         if (receiver != null) {
@@ -94,6 +104,17 @@
     }
 
     @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler, int flags) {
+        if (receiver != null) {
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
+        }
+        return super.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags);
+    }
+
+    @Override
     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
             IntentFilter filter, String broadcastPermission, Handler scheduler) {
         if (receiver != null) {
@@ -105,6 +126,18 @@
     }
 
     @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+        if (receiver != null) {
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
+        }
+        return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler,
+                flags);
+    }
+
+    @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
         if (receiver != null) {
             synchronized (mRegisteredReceivers) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 6ddfbb2..bc89da7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -111,6 +111,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mContext = Mockito.spy(getContext());
         final WindowManager wm = mContext.getSystemService(WindowManager.class);
         mSwitchListener = new SwitchListenerStub();
         mWindowManager = spy(new TestableWindowManager(wm));
@@ -139,16 +140,18 @@
     public void tearDown() {
         mFadeOutAnimation = null;
         mMotionEventHelper.recycleEvents();
+        mMagnificationModeSwitch.removeButton();
     }
 
     @Test
-    public void removeButton_buttonIsShowing_removeView() {
+    public void removeButton_buttonIsShowing_removeViewAndUnregisterComponentCallbacks() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
         mMagnificationModeSwitch.removeButton();
 
         verify(mWindowManager).removeView(mSpyImageView);
         verify(mViewPropertyAnimator).cancel();
+        verify(mContext).unregisterComponentCallbacks(mMagnificationModeSwitch);
     }
 
     @Test
@@ -464,6 +467,13 @@
     }
 
     @Test
+    public void showButton_registerComponentCallbacks() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+        verify(mContext).registerComponentCallbacks(mMagnificationModeSwitch);
+    }
+
+    @Test
     public void onLocaleChanged_buttonIsShowing_updateA11yWindowTitle() {
         final String newA11yWindowTitle = "new a11y window title";
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index 216f63f..a56218b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -91,7 +91,6 @@
         verify(mModeSwitch).onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
     }
 
-
     @Test
     public void testOnSwitchClick_showWindowModeButton_invokeListener() {
         mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 8fdcadd..1dd5e22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.accessibility;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.Choreographer.FrameCallback;
 import static android.view.WindowInsets.Type.systemGestures;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -32,8 +34,8 @@
 import static org.junit.Assert.fail;
 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.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
@@ -41,9 +43,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.animation.ValueAnimator;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.PointF;
@@ -51,6 +55,7 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableResources;
 import android.text.TextUtils;
 import android.view.Display;
@@ -60,6 +65,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
@@ -69,12 +75,14 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.util.leak.ReferenceTestUtils;
+import com.android.systemui.utils.os.FakeHandler;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -82,15 +90,12 @@
 import java.util.List;
 
 @LargeTest
[email protected]
 @RunWith(AndroidTestingRunner.class)
 public class WindowMagnificationControllerTest extends SysuiTestCase {
 
     private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
     @Mock
-    private Handler mHandler;
-    @Mock
-    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
-    @Mock
     private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
     @Mock
     private MirrorWindowControl mMirrorWindowControl;
@@ -98,16 +103,21 @@
     private WindowMagnifierCallback mWindowMagnifierCallback;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+    private Handler mHandler;
     private TestableWindowManager mWindowManager;
     private SysUiState mSysUiState = new SysUiState();
     private Resources mResources;
+    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
     private WindowMagnificationController mWindowMagnificationController;
     private Instrumentation mInstrumentation;
+    private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = Mockito.spy(getContext());
+        mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         final WindowManager wm = mContext.getSystemService(WindowManager.class);
         mWindowManager = spy(new TestableWindowManager(wm));
@@ -119,16 +129,11 @@
             return null;
         }).when(mSfVsyncFrameProvider).postFrameCallback(
                 any(FrameCallback.class));
-        doAnswer(invocation -> {
-            final Runnable runnable = invocation.getArgument(0);
-            runnable.run();
-            return null;
-        }).when(mHandler).post(
-                any(Runnable.class));
-
         mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
 
         mResources = getContext().getOrCreateTestableResources().getResources();
+        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+                mContext, mValueAnimator);
         mWindowMagnificationController = new WindowMagnificationController(mContext,
                 mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
                 mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
@@ -141,6 +146,7 @@
     public void tearDown() {
         mInstrumentation.runOnMainSync(
                 () -> mWindowMagnificationController.deleteWindowMagnification());
+        mValueAnimator.cancel();
     }
 
     @Test
@@ -157,6 +163,52 @@
     }
 
     @Test
+    public void enableWindowMagnification_notifySourceBoundsChanged() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
+                    /* magnificationFrameOffsetRatioY= */ 0, null);
+        });
+
+        // Waits for the surface created
+        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
+                (eq(mContext.getDisplayId())), any());
+    }
+
+    @Test
+    public void enableWindowMagnification_withAnimation_schedulesFrame() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
+                    10, /* magnificationFrameOffsetRatioX= */ 0,
+                    /* magnificationFrameOffsetRatioY= */ 0,
+                    Mockito.mock(IRemoteMagnificationAnimationCallback.class));
+        });
+
+        verify(mSfVsyncFrameProvider,
+                timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeast(2)).postFrameCallback(any());
+    }
+
+    @Test
+    public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN, 0, 0, null);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.moveWindowMagnifier(10, 10);
+        });
+
+        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
+                (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+        assertEquals(mWindowMagnificationController.getCenterX(),
+                sourceBoundsCaptor.getValue().exactCenterX(), 0);
+        assertEquals(mWindowMagnificationController.getCenterY(),
+                sourceBoundsCaptor.getValue().exactCenterY(), 0);
+    }
+
+    @Test
     public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
@@ -174,13 +226,9 @@
         final int screenSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.magnification_max_frame_size) * 10;
         mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-        //We need to initialize new one because the window size is determined when initialization.
-        final WindowMagnificationController controller = new WindowMagnificationController(mContext,
-                mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
-                mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
 
         mInstrumentation.runOnMainSync(() -> {
-            controller.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+            mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
                     Float.NaN);
         });
 
@@ -193,17 +241,17 @@
     }
 
     @Test
-    public void deleteWindowMagnification_destroyControl() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
+    public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN,
+                        Float.NaN));
 
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.deleteWindowMagnification();
-        });
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification());
 
         verify(mMirrorWindowControl).destroyControl();
+        verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController);
     }
 
     @Test
@@ -239,12 +287,6 @@
 
     @Test
     public void setScale_enabled_expectedValueAndUpdateStateDescription() {
-        doAnswer(invocation -> {
-            final Runnable runnable = invocation.getArgument(0);
-            runnable.run();
-            return null;
-        }).when(mHandler).postDelayed(any(Runnable.class), anyLong());
-
         mInstrumentation.runOnMainSync(
                 () -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f,
                         Float.NaN, Float.NaN));
@@ -273,11 +315,7 @@
 
     @Test
     public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
-        final Display display = Mockito.spy(mContext.getDisplay());
-        final int currentRotation = display.getRotation();
-        final int newRotation = (currentRotation + 1) % 4;
-        when(display.getRotation()).thenReturn(newRotation);
-        when(mContext.getDisplay()).thenReturn(display);
+        final int newRotation = simulateRotateTheDevice();
         final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
         final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
         final float displayWidth = windowBounds.width();
@@ -486,6 +524,30 @@
     }
 
     @Test
+    public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
+        final Configuration config = mContext.getResources().getConfiguration();
+        config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
+                : ORIENTATION_LANDSCAPE;
+        final int newRotation = simulateRotateTheDevice();
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+    }
+
+    @Test
+    public void enableWindowMagnification_registerComponentCallback() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN,
+                        Float.NaN));
+
+        verify(mContext).registerComponentCallbacks(mWindowMagnificationController);
+    }
+
+    @Test
     public void onLocaleChanged_enabled_updateA11yWindowTitle() {
         final String newA11yWindowTitle = "new a11y window title";
         mInstrumentation.runOnMainSync(() -> {
@@ -561,4 +623,14 @@
                 .build();
         mWindowManager.setWindowInsets(testInsets);
     }
+
+    @Surface.Rotation
+    private int simulateRotateTheDevice() {
+        final Display display = Mockito.spy(mContext.getDisplay());
+        final int currentRotation = display.getRotation();
+        final int newRotation = (currentRotation + 1) % 4;
+        when(display.getRotation()).thenReturn(newRotation);
+        when(mContext.getDisplay()).thenReturn(display);
+        return newRotation;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index c898150..d3f30c50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -30,7 +30,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
@@ -149,12 +148,13 @@
     }
 
     @Test
-    public void onConfigurationChanged_updateModeSwitches() {
-        final Configuration config = new Configuration();
-        config.densityDpi = Configuration.DENSITY_DPI_ANY;
-        mWindowMagnification.onConfigurationChanged(config);
+    public void onDrag_enabled_notifyCallback() throws RemoteException {
+        mCommandQueue.requestWindowMagnificationConnection(true);
+        waitForIdleSync();
 
-        verify(mModeSwitchesController).onConfigurationChanged(anyInt());
+        mWindowMagnification.onDrag(TEST_DISPLAY);
+
+        verify(mConnectionCallback).onDrag(TEST_DISPLAY);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 1fe3d44..589eeb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -21,13 +21,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertNotNull
 import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import junit.framework.AssertionFailedError
-import kotlin.concurrent.thread
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -40,6 +39,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Spy
 import org.mockito.junit.MockitoJUnit
+import kotlin.concurrent.thread
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -48,6 +48,7 @@
     private val launchContainer = LinearLayout(mContext)
     private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
     @Mock lateinit var callback: ActivityLaunchAnimator.Callback
+    @Mock lateinit var listener: ActivityLaunchAnimator.Listener
     @Spy private val controller = TestLaunchAnimatorController(launchContainer)
     @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
     @Mock lateinit var failHandler: Log.TerribleFailureHandler
@@ -59,6 +60,12 @@
     fun setup() {
         activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
         activityLaunchAnimator.callback = callback
+        activityLaunchAnimator.addListener(listener)
+    }
+
+    @After
+    fun tearDown() {
+        activityLaunchAnimator.removeListener(listener)
     }
 
     private fun startIntentWithAnimation(
@@ -177,7 +184,7 @@
         val runner = activityLaunchAnimator.createRunner(controller)
         runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
         waitForIdleSync()
-        verify(callback).setBlursDisabledForAppLaunch(eq(true))
+        verify(listener).onLaunchAnimationStart()
         verify(controller).onLaunchAnimationStart(anyBoolean())
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 2d51092..254fc59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -25,6 +25,7 @@
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
+import android.hardware.biometrics.SensorLocationInternal
 import android.hardware.biometrics.SensorProperties
 import android.hardware.display.DisplayManager
 import android.hardware.display.DisplayManagerGlobal
@@ -52,6 +53,7 @@
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -75,6 +77,12 @@
 private const val DISPLAY_ID = 2
 private const val SENSOR_ID = 1
 
+private const val DISPLAY_SIZE_X = 800
+private const val DISPLAY_SIZE_Y = 900
+
+private val X_LOCATION = SensorLocationInternal("", 540, 0, 20)
+private val Y_LOCATION = SensorLocationInternal("", 0, 1500, 22)
+
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
@@ -101,6 +109,8 @@
     lateinit var handler: Handler
     @Captor
     lateinit var overlayCaptor: ArgumentCaptor<View>
+    @Captor
+    lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
 
     private val executor = FakeExecutor(FakeSystemClock())
     private lateinit var overlayController: ISidefpsController
@@ -125,6 +135,17 @@
                 this
             }
         }
+        `when`(windowManager.maximumWindowMetrics).thenReturn(
+            WindowMetrics(Rect(0, 0, DISPLAY_SIZE_X, DISPLAY_SIZE_Y), WindowInsets.CONSUMED)
+        )
+    }
+
+    private fun testWithDisplay(
+        initInfo: DisplayInfo.() -> Unit = {},
+        locations: List<SensorLocationInternal> = listOf(X_LOCATION),
+        windowInsets: WindowInsets = insetsForSmallNavbar(),
+        block: () -> Unit
+    ) {
         `when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
             listOf(
                 FingerprintSensorPropertiesInternal(
@@ -133,22 +154,21 @@
                     5 /* maxEnrollmentsPerUser */,
                     listOf() /* componentInfo */,
                     FingerprintSensorProperties.TYPE_POWER_BUTTON,
-                    true /* resetLockoutRequiresHardwareAuthToken */
+                    true /* resetLockoutRequiresHardwareAuthToken */,
+                    locations
                 )
             )
         )
-        `when`(windowManager.maximumWindowMetrics).thenReturn(
-            WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
-        )
-    }
 
-    private fun testWithDisplay(initInfo: DisplayInfo.() -> Unit = {}, block: () -> Unit) {
         val displayInfo = DisplayInfo()
         displayInfo.initInfo()
         val dmGlobal = mock(DisplayManagerGlobal::class.java)
         val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
         `when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
         `when`(windowManager.defaultDisplay).thenReturn(display)
+        `when`(windowManager.currentWindowMetrics).thenReturn(
+            WindowMetrics(Rect(0, 0, DISPLAY_SIZE_X, DISPLAY_SIZE_Y), windowInsets)
+        )
 
         sideFpsController = SidefpsController(
             context.createDisplayContext(display), layoutInflater, fingerprintManager,
@@ -245,28 +265,71 @@
     }
 
     @Test
+    fun showsWithTaskbarOnY() = testWithDisplay(
+        { rotation = Surface.ROTATION_0 },
+        locations = listOf(Y_LOCATION)
+    ) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
     fun showsWithTaskbar90() = testWithDisplay({ rotation = Surface.ROTATION_90 }) {
         hidesWithTaskbar(visible = true)
     }
 
     @Test
+    fun showsWithTaskbar90OnY() = testWithDisplay(
+        { rotation = Surface.ROTATION_90 },
+        locations = listOf(Y_LOCATION)
+    ) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
     fun showsWithTaskbar180() = testWithDisplay({ rotation = Surface.ROTATION_180 }) {
         hidesWithTaskbar(visible = true)
     }
 
     @Test
-    fun showsWithTaskbarCollapsedDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
-        `when`(windowManager.currentWindowMetrics).thenReturn(
-            WindowMetrics(Rect(0, 0, 800, 800), insetsForSmallNavbar())
-        )
+    fun showsWithTaskbar270OnY() = testWithDisplay(
+        { rotation = Surface.ROTATION_270 },
+        locations = listOf(Y_LOCATION)
+    ) {
         hidesWithTaskbar(visible = true)
     }
 
     @Test
-    fun hidesWithTaskbarDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
-        `when`(windowManager.currentWindowMetrics).thenReturn(
-            WindowMetrics(Rect(0, 0, 800, 800), insetsForLargeNavbar())
-        )
+    fun showsWithTaskbarCollapsedDown() = testWithDisplay(
+        { rotation = Surface.ROTATION_270 },
+        windowInsets = insetsForSmallNavbar()
+    ) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
+    fun showsWithTaskbarCollapsedDownOnY() = testWithDisplay(
+        { rotation = Surface.ROTATION_180 },
+        locations = listOf(Y_LOCATION),
+        windowInsets = insetsForSmallNavbar()
+    ) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
+    fun hidesWithTaskbarDown() = testWithDisplay(
+        { rotation = Surface.ROTATION_180 },
+        locations = listOf(X_LOCATION),
+        windowInsets = insetsForLargeNavbar()
+    ) {
+        hidesWithTaskbar(visible = false)
+    }
+
+    @Test
+    fun hidesWithTaskbarDownOnY() = testWithDisplay(
+        { rotation = Surface.ROTATION_270 },
+        locations = listOf(Y_LOCATION),
+        windowInsets = insetsForLargeNavbar()
+    ) {
         hidesWithTaskbar(visible = false)
     }
 
@@ -281,6 +344,28 @@
         verify(windowManager, never()).removeView(any())
         verify(sidefpsView).visibility = if (visible) View.VISIBLE else View.GONE
     }
+
+    @Test
+    fun setsXAlign() = testWithDisplay {
+        overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+        executor.runAllReady()
+
+        verify(windowManager).addView(any(), overlayViewParamsCaptor.capture())
+
+        assertThat(overlayViewParamsCaptor.value.x).isEqualTo(X_LOCATION.sensorLocationX)
+        assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
+    }
+
+    @Test
+    fun setYAlign() = testWithDisplay(locations = listOf(Y_LOCATION)) {
+        overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+        executor.runAllReady()
+
+        verify(windowManager).addView(any(), overlayViewParamsCaptor.capture())
+
+        assertThat(overlayViewParamsCaptor.value.x).isEqualTo(DISPLAY_SIZE_X)
+        assertThat(overlayViewParamsCaptor.value.y).isEqualTo(Y_LOCATION.sensorLocationY)
+    }
 }
 
 private fun insetsForSmallNavbar() = insetsWithBottom(60)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 84eb935..066a866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -37,6 +37,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -89,6 +90,7 @@
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var udfpsView: UdfpsView
     @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
+    @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
 
     private val sensorProps = SensorLocationInternal("", 10, 100, 20)
         .asFingerprintSensorProperties()
@@ -118,7 +120,7 @@
             keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
             configurationController, systemClock, keyguardStateController,
             unlockedScreenOffAnimationController, sensorProps, hbmProvider, reason,
-            controllerCallback, onTouch)
+            controllerCallback, onTouch, activityLaunchAnimator)
         block()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 2afcbda..159bdba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -58,6 +58,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
@@ -180,6 +181,8 @@
     private TypedArray mBrightnessBacklight;
     @Mock
     private SystemUIDialogManager mSystemUIDialogManager;
+    @Mock
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
 
     // Capture listeners so that they can be used to send events
     @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -252,7 +255,8 @@
                 mSystemClock,
                 mUnlockedScreenOffAnimationController,
                 mSystemUIDialogManager,
-                mLatencyTracker);
+                mLatencyTracker,
+                mActivityLaunchAnimator);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0ae3c39..e9a4e15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -36,6 +36,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -96,6 +97,8 @@
     private SystemUIDialogManager mDialogManager;
     @Mock
     private UdfpsController mUdfpsController;
+    @Mock
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
     private FakeSystemClock mSystemClock = new FakeSystemClock();
 
     private UdfpsKeyguardViewController mController;
@@ -134,7 +137,8 @@
                 mKeyguardStateController,
                 mUnlockedScreenOffAnimationController,
                 mDialogManager,
-                mUdfpsController);
+                mUdfpsController,
+                mActivityLaunchAnimator);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
deleted file mode 100644
index 4a29ada..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.app.communal.CommunalManager;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.condition.Monitor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalManagerUpdaterTest extends SysuiTestCase {
-    private CommunalSourceMonitor mMonitor;
-    @Mock
-    private CommunalManager mCommunalManager;
-    @Mock
-    private Monitor mCommunalConditionsMonitor;
-
-    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mContext.addMockSystemService(CommunalManager.class, mCommunalManager);
-
-        doAnswer(invocation -> {
-            final Monitor.Callback callback = invocation.getArgument(0);
-            callback.onConditionsChanged(true);
-            return null;
-        }).when(mCommunalConditionsMonitor).addCallback(any());
-
-        mMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
-        final CommunalManagerUpdater updater = new CommunalManagerUpdater(mContext, mMonitor);
-        updater.start();
-        clearInvocations(mCommunalManager);
-    }
-
-    @Test
-    public void testUpdateSystemService_false() {
-        mMonitor.setSource(null);
-        mExecutor.runAllReady();
-        verify(mCommunalManager).setCommunalViewShowing(false);
-    }
-
-    @Test
-    public void testUpdateSystemService_true() {
-        final CommunalSource source = mock(CommunalSource.class);
-        mMonitor.setSource(source);
-        mExecutor.runAllReady();
-        verify(mCommunalManager).setCommunalViewShowing(true);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index 2d3757c2..12096bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.controls.controller
 
 import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
 import android.os.UserHandle
 import android.service.controls.IControlsActionCallback
 import android.service.controls.IControlsProvider
@@ -43,6 +46,8 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -57,8 +62,6 @@
     private lateinit var subscriberService: IControlsSubscriber.Stub
     @Mock
     private lateinit var service: IControlsProvider.Stub
-    @Mock
-    private lateinit var loadCallback: ControlsBindingController.LoadCallback
 
     @Captor
     private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper>
@@ -75,7 +78,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        mContext.addMockService(componentName, service)
+        context.addMockService(componentName, service)
         executor = FakeExecutor(FakeSystemClock())
         `when`(service.asBinder()).thenCallRealMethod()
         `when`(service.queryLocalInterface(ArgumentMatchers.anyString())).thenReturn(service)
@@ -98,7 +101,36 @@
     fun testBindService() {
         manager.bindService()
         executor.runAllReady()
-        assertTrue(mContext.isBound(componentName))
+        assertTrue(context.isBound(componentName))
+    }
+
+    @Test
+    fun testNullBinding() {
+        val mockContext = mock(Context::class.java)
+        lateinit var serviceConnection: ServiceConnection
+        `when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer {
+            val component = (it.arguments[0] as Intent).component
+            if (component == componentName) {
+                serviceConnection = it.arguments[1] as ServiceConnection
+                serviceConnection.onNullBinding(component)
+                true
+            } else {
+                false
+            }
+        }
+
+        val nullManager = ControlsProviderLifecycleManager(
+                mockContext,
+                executor,
+                actionCallbackService,
+                UserHandle.of(0),
+                componentName
+        )
+
+        nullManager.bindService()
+        executor.runAllReady()
+
+        verify(mockContext).unbindService(serviceConnection)
     }
 
     @Test
@@ -109,7 +141,7 @@
         manager.unbindService()
         executor.runAllReady()
 
-        assertFalse(mContext.isBound(componentName))
+        assertFalse(context.isBound(componentName))
     }
 
     @Test
@@ -119,7 +151,7 @@
 
         verify(service).load(subscriberService)
 
-        assertTrue(mContext.isBound(componentName))
+        assertTrue(context.isBound(componentName))
     }
 
     @Test
@@ -129,7 +161,7 @@
 
         manager.unbindService()
         executor.runAllReady()
-        assertFalse(mContext.isBound(componentName))
+        assertFalse(context.isBound(componentName))
     }
 
     @Test
@@ -162,7 +194,7 @@
         manager.maybeBindAndSubscribe(list, subscriberService)
         executor.runAllReady()
 
-        assertTrue(mContext.isBound(componentName))
+        assertTrue(context.isBound(componentName))
         verify(service).subscribe(list, subscriberService)
     }
 
@@ -173,7 +205,7 @@
         manager.maybeBindAndSendAction(controlId, action)
         executor.runAllReady()
 
-        assertTrue(mContext.isBound(componentName))
+        assertTrue(context.isBound(componentName))
         verify(service).action(eq(controlId), capture(wrapperCaptor),
                 eq(actionCallbackService))
         assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
index a328d9e..efb3db7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
@@ -32,7 +32,6 @@
 import androidx.test.rule.ActivityTestRule
 import androidx.test.runner.intercepting.SingleActivityFactory
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.util.mockito.capture
@@ -70,8 +69,6 @@
     @Mock
     private lateinit var listingController: ControlsListingController
     @Mock
-    private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock
     private lateinit var iIntentSender: IIntentSender
     @Captor
     private lateinit var captor: ArgumentCaptor<ControlInfo>
@@ -85,7 +82,7 @@
                     override fun create(intent: Intent?): TestControlsRequestDialog {
                         return TestControlsRequestDialog(
                                 controller,
-                                broadcastDispatcher,
+                                fakeBroadcastDispatcher,
                                 listingController
                         )
                     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 3e19cc4..cdffaec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -192,7 +192,7 @@
     public void test_holdsWakeLockWhenGoingToLowPowerDelayed() {
         // Transition to low power mode will be delayed to let
         // animations play at 60 fps.
-        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+        when(mDozeParameters.shouldDelayDisplayDozeTransition()).thenReturn(true);
         mHandlerFake.setMode(QUEUEING);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -209,7 +209,7 @@
     public void test_releasesWakeLock_abortingLowPowerDelayed() {
         // Transition to low power mode will be delayed to let
         // animations play at 60 fps.
-        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+        when(mDozeParameters.shouldDelayDisplayDozeTransition()).thenReturn(true);
         mHandlerFake.setMode(QUEUEING);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
index 7365568..ada7ddb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,122 +16,51 @@
 
 package com.android.systemui.dreams;
 
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.isNull;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertEquals;
 
-import android.app.PendingIntent;
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
+import android.content.Context;
 import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.dreams.appwidgets.AppWidgetProvider;
-import com.android.systemui.dreams.appwidgets.ComplicationProvider;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.settingslib.dream.DreamBackend;
 
 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.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-public class ComplicationProviderTest extends SysuiTestCase {
-    @Mock
-    ActivityStarter mActivityStarter;
-
-    @Mock
-    ComponentName mComponentName;
-
-    @Mock
-    AppWidgetProvider mAppWidgetProvider;
-
-    @Mock
-    AppWidgetHostView mAppWidgetHostView;
-
-    @Mock
-    ComplicationHost.CreationCallback mCreationCallback;
-
-    @Mock
-    ComplicationHost.InteractionCallback mInteractionCallback;
-
-    @Mock
-    PendingIntent mPendingIntent;
-
-    @Mock
-    RemoteViews.RemoteResponse mRemoteResponse;
-
-    ComplicationProvider mComplicationProvider;
-
-    RemoteViews.InteractionHandler mInteractionHandler;
-
-    @Rule
-    public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
-
-    @Rule
-    public SysuiTestableContext mContext = new SysuiTestableContext(
-            InstrumentationRegistry.getContext(), mLeakCheck);
-
-    ComplicationHostView.LayoutParams mLayoutParams = new ComplicationHostView.LayoutParams(
-            ComplicationHostView.LayoutParams.MATCH_PARENT,
-            ComplicationHostView.LayoutParams.MATCH_PARENT);
+public class ComplicationProviderTest {
+    private TestComplicationProvider mComplicationProvider;
 
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mPendingIntent.isActivity()).thenReturn(true);
-        when(mAppWidgetProvider.getWidget(mComponentName)).thenReturn(mAppWidgetHostView);
-
-        mComplicationProvider = new ComplicationProvider(
-                mActivityStarter,
-                mComponentName,
-                mAppWidgetProvider,
-                mLayoutParams
-        );
-
-        final ArgumentCaptor<RemoteViews.InteractionHandler> creationCallbackCapture =
-                ArgumentCaptor.forClass(RemoteViews.InteractionHandler.class);
-
-        mComplicationProvider.onCreateComplication(mContext, mCreationCallback,
-                mInteractionCallback);
-        verify(mAppWidgetHostView, times(1))
-                .setInteractionHandler(creationCallbackCapture.capture());
-        mInteractionHandler = creationCallbackCapture.getValue();
+        mComplicationProvider = new TestComplicationProvider();
     }
 
     @Test
-    public void testWidgetBringup() {
-        // Make sure widget was requested.
-        verify(mAppWidgetProvider, times(1)).getWidget(eq(mComponentName));
-
-        // Make sure widget was returned to callback.
-        verify(mCreationCallback, times(1)).onCreated(eq(mAppWidgetHostView),
-                eq(mLayoutParams));
+    public void testConvertComplicationType() {
+        assertEquals(ComplicationProvider.COMPLICATION_TYPE_TIME,
+                mComplicationProvider.convertComplicationType(DreamBackend.COMPLICATION_TYPE_TIME));
+        assertEquals(ComplicationProvider.COMPLICATION_TYPE_DATE,
+                mComplicationProvider.convertComplicationType(DreamBackend.COMPLICATION_TYPE_DATE));
+        assertEquals(ComplicationProvider.COMPLICATION_TYPE_WEATHER,
+                mComplicationProvider.convertComplicationType(
+                        DreamBackend.COMPLICATION_TYPE_WEATHER));
+        assertEquals(ComplicationProvider.COMPLICATION_TYPE_AIR_QUALITY,
+                mComplicationProvider.convertComplicationType(
+                        DreamBackend.COMPLICATION_TYPE_AIR_QUALITY));
+        assertEquals(ComplicationProvider.COMPLICATION_TYPE_CAST_INFO,
+                mComplicationProvider.convertComplicationType(
+                        DreamBackend.COMPLICATION_TYPE_CAST_INFO));
     }
 
-    @Test
-    public void testWidgetInteraction() {
-        // Trigger interaction.
-        mInteractionHandler.onInteraction(mAppWidgetHostView, mPendingIntent,
-                mRemoteResponse);
-
-        // Ensure activity is started.
-        verify(mActivityStarter, times(1))
-                .startPendingIntentDismissingKeyguard(eq(mPendingIntent), isNull(),
-                        eq(mAppWidgetHostView));
-        // Verify exit is requested.
-        verify(mInteractionCallback, times(1)).onExit();
+    private static class TestComplicationProvider implements ComplicationProvider {
+        @Override
+        public void onCreateComplication(Context context,
+                ComplicationHost.CreationCallback creationCallback,
+                ComplicationHost.InteractionCallback interactionCallback) {
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index cf53ccf..7c5f57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -19,10 +19,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
+import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,6 +48,8 @@
 @RunWith(AndroidTestingRunner.class)
 public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
     private static final int DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT = 100;
+    private static final int MAX_BURN_IN_OFFSET = 20;
+    private static final long BURN_IN_PROTECTION_UPDATE_INTERVAL = 10;
 
     @Mock
     Resources mResources;
@@ -61,6 +66,9 @@
     @Mock
     ViewGroup mDreamOverlayContentView;
 
+    @Mock
+    Handler mHandler;
+
     DreamOverlayContainerViewController mController;
 
     @Before
@@ -74,8 +82,12 @@
         when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
 
         mController = new DreamOverlayContainerViewController(
-                mDreamOverlayContainerView, mDreamOverlayContentView,
-                mDreamOverlayStatusBarViewController);
+                mDreamOverlayContainerView,
+                mDreamOverlayContentView,
+                mDreamOverlayStatusBarViewController,
+                mHandler,
+                MAX_BURN_IN_OFFSET,
+                BURN_IN_PROTECTION_UPDATE_INTERVAL);
     }
 
     @Test
@@ -129,4 +141,37 @@
         computeInsetsListenerCapture.getValue().onComputeInternalInsets(info);
         assertNotNull(info.touchableRegion);
     }
+
+    @Test
+    public void testBurnInProtectionStartsWhenContentViewAttached() {
+        mController.onViewAttached();
+        verify(mHandler).postDelayed(any(Runnable.class), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
+    }
+
+    @Test
+    public void testBurnInProtectionStopsWhenContentViewDetached() {
+        mController.onViewDetached();
+        verify(mHandler).removeCallbacks(any(Runnable.class));
+    }
+
+    @Test
+    public void testBurnInProtectionUpdatesPeriodically() {
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mController.onViewAttached();
+        verify(mHandler).postDelayed(
+                runnableCaptor.capture(), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
+        runnableCaptor.getValue().run();
+        verify(mDreamOverlayContainerView).setTranslationX(anyFloat());
+        verify(mDreamOverlayContainerView).setTranslationY(anyFloat());
+    }
+
+    @Test
+    public void testBurnInProtectionReschedulesUpdate() {
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mController.onViewAttached();
+        verify(mHandler).postDelayed(
+                runnableCaptor.capture(), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
+        runnableCaptor.getValue().run();
+        verify(mHandler).postDelayed(runnableCaptor.getValue(), BURN_IN_PROTECTION_UPDATE_INTERVAL);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 8cd8e4d..c0b7271 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.dreams;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Intent;
 import android.os.IBinder;
+import android.service.dreams.DreamService;
 import android.service.dreams.IDreamOverlay;
 import android.service.dreams.IDreamOverlayCallback;
 import android.testing.AndroidTestingRunner;
@@ -195,4 +198,22 @@
         mService.onDestroy();
         verify(mDreamOverlayStateController).removeCallback(callbackCapture.getValue());
     }
+
+    @Test
+    public void testShouldShowComplicationsTrueByDefault() {
+        assertThat(mService.shouldShowComplications()).isTrue();
+
+        mService.onBind(new Intent());
+
+        assertThat(mService.shouldShowComplications()).isTrue();
+    }
+
+    @Test
+    public void testShouldShowComplicationsSetByIntentExtra() {
+        final Intent intent = new Intent();
+        intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, false);
+        mService.onBind(intent);
+
+        assertThat(mService.shouldShowComplications()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java
deleted file mode 100644
index adf110b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.appwidgets;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-import android.view.Gravity;
-
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-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.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ComplicationPrimerTest extends SysuiTestCase {
-    @Rule
-    public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
-
-    @Rule
-    public SysuiTestableContext mContext = new SysuiTestableContext(
-            InstrumentationRegistry.getContext(), mLeakCheck);
-
-    @Mock
-    Resources mResources;
-
-    @Mock
-    AppWidgetComponent mAppWidgetComplicationComponent1;
-    @Mock
-    AppWidgetComponent mAppWidgetComplicationComponent2;
-
-    @Mock
-    ComplicationProvider mComplicationProvider1;
-
-    @Mock
-    ComplicationProvider mComplicationProvider2;
-
-    final ComponentName mAppComplicationComponent1 =
-            ComponentName.unflattenFromString("com.foo.bar/.Baz");
-    final ComponentName mAppComplicationComponent2 =
-            ComponentName.unflattenFromString("com.foo.bar/.Baz2");
-
-    final int mAppComplicationGravity1 = Gravity.BOTTOM | Gravity.START;
-    final int mAppComplicationGravity2 = Gravity.BOTTOM | Gravity.END;
-
-    final String[] mComponents = new String[]{mAppComplicationComponent1.flattenToString(),
-            mAppComplicationComponent2.flattenToString() };
-    final int[] mPositions = new int[]{mAppComplicationGravity1, mAppComplicationGravity2};
-
-    @Mock
-    DreamOverlayStateController mDreamOverlayStateController;
-
-    @Mock
-    AppWidgetComponent.Factory mAppWidgetComplicationProviderFactory;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mAppWidgetComplicationProviderFactory.build(eq(mAppComplicationComponent1), any()))
-                .thenReturn(mAppWidgetComplicationComponent1);
-        when(mAppWidgetComplicationComponent1.getAppWidgetComplicationProvider())
-                .thenReturn(mComplicationProvider1);
-        when(mAppWidgetComplicationProviderFactory.build(eq(mAppComplicationComponent2), any()))
-                .thenReturn(mAppWidgetComplicationComponent2);
-        when(mAppWidgetComplicationComponent2.getAppWidgetComplicationProvider())
-                .thenReturn(mComplicationProvider2);
-        when(mResources.getIntArray(R.array.config_dreamComplicationPositions))
-                .thenReturn(mPositions);
-        when(mResources.getStringArray(R.array.config_dreamAppWidgetComplications))
-                .thenReturn(mComponents);
-    }
-
-    @Test
-    public void testLoading() {
-        final ComplicationPrimer primer = new ComplicationPrimer(mContext,
-                mResources,
-                mDreamOverlayStateController,
-                mAppWidgetComplicationProviderFactory);
-
-        // Inform primer to begin.
-        primer.onBootCompleted();
-
-        // Verify the first component is added to the state controller with the proper position.
-        {
-            final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
-                    ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
-            verify(mAppWidgetComplicationProviderFactory, times(1))
-                    .build(eq(mAppComplicationComponent1),
-                    layoutParamsArgumentCaptor.capture());
-
-            assertEquals(layoutParamsArgumentCaptor.getValue().startToStart,
-                    ConstraintLayout.LayoutParams.PARENT_ID);
-            assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
-                    ConstraintLayout.LayoutParams.PARENT_ID);
-
-            verify(mDreamOverlayStateController, times(1))
-                    .addComplication(eq(mComplicationProvider1));
-        }
-
-        // Verify the second component is added to the state controller with the proper position.
-        {
-            final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
-                    ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
-            verify(mAppWidgetComplicationProviderFactory, times(1))
-                    .build(eq(mAppComplicationComponent2),
-                    layoutParamsArgumentCaptor.capture());
-
-            assertEquals(layoutParamsArgumentCaptor.getValue().endToEnd,
-                    ConstraintLayout.LayoutParams.PARENT_ID);
-            assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
-                    ConstraintLayout.LayoutParams.PARENT_ID);
-            verify(mDreamOverlayStateController, times(1))
-                    .addComplication(eq(mComplicationProvider1));
-        }
-    }
-
-    @Test
-    public void testNoComponents() {
-        when(mResources.getStringArray(R.array.config_dreamAppWidgetComplications))
-                .thenReturn(new String[]{});
-
-        final ComplicationPrimer primer = new ComplicationPrimer(mContext,
-                mResources,
-                mDreamOverlayStateController,
-                mAppWidgetComplicationProviderFactory);
-
-        // Inform primer to begin.
-        primer.onBootCompleted();
-
-
-        // Make sure there is no request to add a widget if no components are specified by the
-        // product.
-        verify(mAppWidgetComplicationProviderFactory, never()).build(any(), any());
-        verify(mDreamOverlayStateController, never()).addComplication(any());
-    }
-
-    @Test
-    public void testNoPositions() {
-        when(mResources.getIntArray(R.array.config_dreamComplicationPositions))
-                .thenReturn(new int[]{});
-
-        final ComplicationPrimer primer = new ComplicationPrimer(mContext,
-                mResources,
-                mDreamOverlayStateController,
-                mAppWidgetComplicationProviderFactory);
-
-        primer.onBootCompleted();
-
-        // Make sure there is no request to add a widget if no positions are specified by the
-        // product.
-        verify(mAppWidgetComplicationProviderFactory, never()).build(any(), any());
-        verify(mDreamOverlayStateController, never()).addComplication(any());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index cb16bec..87bc732 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -76,7 +76,8 @@
         )
         verify(mFlagManager).restartAction = any()
         mBroadcastReceiver = withArgCaptor {
-            verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable())
+            verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(),
+                any())
         }
         mClearCacheAction = withArgCaptor {
             verify(mFlagManager).clearCacheAction = capture()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index e453ff2d..fd282cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -80,6 +80,7 @@
         assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness());
 
         verify(mWakefulnessObserver).onFinishedWakingUp();
+        verify(mWakefulnessObserver).onPostFinishedWakingUp();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 8cc2776..43d9a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.animation.UniqueObjectHostView
@@ -57,7 +57,7 @@
     @JvmField @Rule
     val mockito = MockitoJUnit.rule()
 
-    private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null)
+    private val mediaContainerView: MediaContainerView = MediaContainerView(context, null)
     private val hostView = UniqueObjectHostView(context)
     private lateinit var keyguardMediaController: KeyguardMediaController
 
@@ -78,7 +78,7 @@
             context,
             configurationController
         )
-        keyguardMediaController.attachSinglePaneContainer(mediaHeaderView)
+        keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
         keyguardMediaController.useSplitShade = false
     }
 
@@ -88,7 +88,7 @@
 
         keyguardMediaController.refreshMediaPosition()
 
-        assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
+        assertThat(mediaContainerView.visibility).isEqualTo(GONE)
     }
 
     @Test
@@ -102,7 +102,7 @@
     private fun testStateVisibility(state: Int, visibility: Int) {
         whenever(statusBarStateController.state).thenReturn(state)
         keyguardMediaController.refreshMediaPosition()
-        assertThat(mediaHeaderView.visibility).isEqualTo(visibility)
+        assertThat(mediaContainerView.visibility).isEqualTo(visibility)
     }
 
     @Test
@@ -112,7 +112,7 @@
 
         keyguardMediaController.refreshMediaPosition()
 
-        assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
+        assertThat(mediaContainerView.visibility).isEqualTo(GONE)
     }
 
     @Test
@@ -130,7 +130,7 @@
         keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
 
         assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
-        assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE)
+        assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
     }
 
     @Test
@@ -149,6 +149,6 @@
         keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
 
         assertTrue("HostView wasn't attached to the single pane container",
-            mediaHeaderView.childCount == 1)
+            mediaContainerView.childCount == 1)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 7763e75..140a395 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -202,6 +202,7 @@
                 resumeAction = null)
 
         whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
     }
 
     /** Mock view holder for the notification player */
@@ -301,8 +302,47 @@
     }
 
     @Test
-    fun bindSemanticActions() {
+    fun bindSemanticActionsOldLayout() {
         whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
+
+        val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+        val semanticActions = MediaButton(
+            playOrPause = MediaAction(icon, Runnable {}, "play"),
+            nextOrCustom = MediaAction(icon, Runnable {}, "next"),
+            startCustom = MediaAction(icon, null, "custom 1"),
+            endCustom = MediaAction(icon, null, "custom 2")
+        )
+        val state = mediaData.copy(semanticActions = semanticActions)
+
+        player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+        player.bindPlayer(state, PACKAGE)
+
+        verify(expandedSet).setVisibility(R.id.action0, ConstraintSet.VISIBLE)
+        assertThat(action0.contentDescription).isEqualTo("custom 1")
+        assertThat(action0.isEnabled()).isFalse()
+
+        verify(expandedSet).setVisibility(R.id.action1, ConstraintSet.INVISIBLE)
+        assertThat(action1.isEnabled()).isFalse()
+
+        verify(expandedSet).setVisibility(R.id.action2, ConstraintSet.VISIBLE)
+        assertThat(action2.isEnabled()).isTrue()
+        assertThat(action2.contentDescription).isEqualTo("play")
+
+        verify(expandedSet).setVisibility(R.id.action3, ConstraintSet.VISIBLE)
+        assertThat(action3.isEnabled()).isTrue()
+        assertThat(action3.contentDescription).isEqualTo("next")
+
+        verify(expandedSet).setVisibility(R.id.action4, ConstraintSet.VISIBLE)
+        assertThat(action4.contentDescription).isEqualTo("custom 2")
+        assertThat(action4.isEnabled()).isFalse()
+    }
+
+    @Test
+    fun bindSemanticActionsNewLayout() {
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+
         val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
                 playOrPause = MediaAction(icon, Runnable {}, "play"),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index dec5a10..cf5d477 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -16,23 +16,26 @@
 
 package com.android.systemui.media.taptotransfer
 
+import android.content.ComponentName
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer
-import com.android.systemui.media.taptotransfer.sender.TransferInitiated
-import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.media.taptotransfer.sender.*
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
+import org.mockito.Mockito.anyString
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -51,17 +54,25 @@
     private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender
     @Mock
     private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
+    @Mock
+    private lateinit var mediaSenderService: IDeviceSenderCallback.Stub
+    private lateinit var mediaSenderServiceComponentName: ComponentName
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+
+        mediaSenderServiceComponentName = ComponentName(context, MediaTttSenderService::class.java)
+        context.addMockService(mediaSenderServiceComponentName, mediaSenderService)
+        whenever(mediaSenderService.queryLocalInterface(anyString())).thenReturn(mediaSenderService)
+        whenever(mediaSenderService.asBinder()).thenReturn(mediaSenderService)
+
         mediaTttCommandLineHelper =
             MediaTttCommandLineHelper(
                 commandRegistry,
                 context,
                 mediaTttChipControllerSender,
                 mediaTttChipControllerReceiver,
-                FakeExecutor(FakeSystemClock())
             )
     }
 
@@ -102,17 +113,36 @@
     }
 
     @Test
-    fun sender_moveCloserToTransfer_chipDisplayWithCorrectState() {
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+    fun sender_moveCloserToStartCast_serviceCallbackCalled() {
+        commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand())
 
-        verify(mediaTttChipControllerSender).displayChip(any(MoveCloserToTransfer::class.java))
+        assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+
+        val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+        verify(mediaSenderService).closeToReceiverToStartCast(any(), capture(deviceInfoCaptor))
+        assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
     }
 
     @Test
-    fun sender_transferInitiated_chipDisplayWithCorrectState() {
-        commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+    fun sender_moveCloserToEndCast_serviceCallbackCalled() {
+        commandRegistry.onShellCommand(pw, getMoveCloserToEndCastCommand())
 
-        verify(mediaTttChipControllerSender).displayChip(any(TransferInitiated::class.java))
+        assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+
+        val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+        verify(mediaSenderService).closeToReceiverToEndCast(any(), capture(deviceInfoCaptor))
+        assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+    }
+
+    @Test
+    fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
+        commandRegistry.onShellCommand(pw, getTransferToReceiverTriggeredCommand())
+
+        assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+
+        val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+        verify(mediaSenderService).transferToReceiverTriggered(any(), capture(deviceInfoCaptor))
+        assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
     }
 
     @Test
@@ -123,6 +153,14 @@
     }
 
     @Test
+    fun sender_transferFailed_serviceCallbackCalled() {
+        commandRegistry.onShellCommand(pw, getTransferFailedCommand())
+
+        assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+        verify(mediaSenderService).transferFailed(any(), any())
+    }
+
+    @Test
     fun sender_removeCommand_chipRemoved() {
         commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_SENDER_TAG))
 
@@ -143,18 +181,25 @@
         verify(mediaTttChipControllerReceiver).removeChip()
     }
 
-    private fun getMoveCloserToTransferCommand(): Array<String> =
+    private fun getMoveCloserToStartCastCommand(): Array<String> =
         arrayOf(
             ADD_CHIP_COMMAND_SENDER_TAG,
             DEVICE_NAME,
-            MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME
+            MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
         )
 
-    private fun getTransferInitiatedCommand(): Array<String> =
+    private fun getMoveCloserToEndCastCommand(): Array<String> =
         arrayOf(
             ADD_CHIP_COMMAND_SENDER_TAG,
             DEVICE_NAME,
-            TRANSFER_INITIATED_COMMAND_NAME
+            MOVE_CLOSER_TO_END_CAST_COMMAND_NAME
+        )
+
+    private fun getTransferToReceiverTriggeredCommand(): Array<String> =
+        arrayOf(
+            ADD_CHIP_COMMAND_SENDER_TAG,
+            DEVICE_NAME,
+            TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME
         )
 
     private fun getTransferSucceededCommand(): Array<String> =
@@ -164,6 +209,13 @@
             TRANSFER_SUCCEEDED_COMMAND_NAME
         )
 
+    private fun getTransferFailedCommand(): Array<String> =
+        arrayOf(
+            ADD_CHIP_COMMAND_SENDER_TAG,
+            DEVICE_NAME,
+            TRANSFER_FAILED_COMMAND_NAME
+        )
+
     class EmptyCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index caef5b9..e9ddf3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -26,26 +26,18 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.SettableFuture
 import org.junit.Before
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Future
 
 @SmallTest
 class MediaTttChipControllerSenderTest : SysuiTestCase() {
     private lateinit var appIconDrawable: Drawable
-    private lateinit var fakeMainClock: FakeSystemClock
-    private lateinit var fakeMainExecutor: FakeExecutor
-    private lateinit var fakeBackgroundClock: FakeSystemClock
-    private lateinit var fakeBackgroundExecutor: FakeExecutor
 
     private lateinit var controllerSender: MediaTttChipControllerSender
 
@@ -56,18 +48,12 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
-        fakeMainClock = FakeSystemClock()
-        fakeMainExecutor = FakeExecutor(fakeMainClock)
-        fakeBackgroundClock = FakeSystemClock()
-        fakeBackgroundExecutor = FakeExecutor(fakeBackgroundClock)
-        controllerSender = MediaTttChipControllerSender(
-            context, windowManager, fakeMainExecutor, fakeBackgroundExecutor
-        )
+        controllerSender = MediaTttChipControllerSender(context, windowManager)
     }
 
     @Test
-    fun moveCloserToTransfer_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
-        controllerSender.displayChip(moveCloserToTransfer())
+    fun moveCloserToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
+        controllerSender.displayChip(moveCloserToStartCast())
 
         val chipView = getChipView()
         assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
@@ -75,85 +61,37 @@
         assertThat(chipView.getChipText()).contains(DEVICE_NAME)
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+        assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
     }
 
     @Test
-    fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo() {
-        val future: SettableFuture<Runnable?> = SettableFuture.create()
-        controllerSender.displayChip(transferInitiated(future))
+    fun moveCloserToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
+        controllerSender.displayChip(moveCloserToEndCast())
 
-        // Don't resolve the future in any way and don't run our executors
+        val chipView = getChipView()
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+        assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+        assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+        assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+        assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
+    }
 
-        // Assert we're still in the loading state
+    @Test
+    fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
+        controllerSender.displayChip(transferToReceiverTriggered())
+
         val chipView = getChipView()
         assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
         assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
         assertThat(chipView.getChipText()).contains(DEVICE_NAME)
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+        assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
     }
 
     @Test
-    fun transferInitiated_futureResolvedSuccessfully_switchesToTransferSucceeded() {
-        val future: SettableFuture<Runnable?> = SettableFuture.create()
-        val undoRunnable = Runnable { }
-
-        controllerSender.displayChip(transferInitiated(future))
-
-        future.set(undoRunnable)
-        fakeBackgroundExecutor.advanceClockToLast()
-        fakeBackgroundExecutor.runAllReady()
-        fakeMainExecutor.advanceClockToLast()
-        val numRun = fakeMainExecutor.runAllReady()
-
-        // Assert we ran the future callback
-        assertThat(numRun).isEqualTo(1)
-        // Assert that we've moved to the successful state
-        val chipView = getChipView()
-        assertThat(chipView.getChipText()).contains(DEVICE_NAME)
-        assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
-        assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
-    }
-
-    @Test
-    fun transferInitiated_futureCancelled_chipRemoved() {
-        val future: SettableFuture<Runnable?> = SettableFuture.create()
-
-        controllerSender.displayChip(transferInitiated(future))
-
-        future.cancel(true)
-        fakeBackgroundExecutor.advanceClockToLast()
-        fakeBackgroundExecutor.runAllReady()
-        fakeMainExecutor.advanceClockToLast()
-        val numRun = fakeMainExecutor.runAllReady()
-
-        // Assert we ran the future callback
-        assertThat(numRun).isEqualTo(1)
-        // Assert that we've hidden the chip
-        verify(windowManager).removeView(any())
-    }
-
-    @Test
-    fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() {
-        val future: SettableFuture<Runnable?> = SettableFuture.create()
-        controllerSender.displayChip(transferInitiated(future))
-
-        // We won't set anything on the future, but we will still run the executors so that we're
-        // waiting on the future resolving. If we have a bug in our code, then this test will time
-        // out because we're waiting on the future indefinitely.
-        fakeBackgroundExecutor.advanceClockToLast()
-        fakeBackgroundExecutor.runAllReady()
-        fakeMainExecutor.advanceClockToLast()
-        val numRun = fakeMainExecutor.runAllReady()
-
-        // Assert we eventually decide to not wait for the future anymore
-        assertThat(numRun).isEqualTo(1)
-        // Assert we've hidden the chip
-        verify(windowManager).removeView(any())
-    }
-
-    @Test
-    fun transferSucceeded_appIcon_chipTextContainsDeviceName_noLoadingIcon() {
+    fun transferSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
         controllerSender.displayChip(transferSucceeded())
 
         val chipView = getChipView()
@@ -161,6 +99,7 @@
         assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
         assertThat(chipView.getChipText()).contains(DEVICE_NAME)
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+        assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
     }
 
     @Test
@@ -192,37 +131,58 @@
     }
 
     @Test
-    fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
-        controllerSender.displayChip(moveCloserToTransfer())
-        controllerSender.displayChip(transferInitiated())
+    fun transferFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
+        controllerSender.displayChip(transferFailed())
+
+        val chipView = getChipView()
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+        assertThat(chipView.getChipText()).doesNotContain(DEVICE_NAME)
+        assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+        assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+        assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
+    }
+
+    @Test
+    fun changeFromCloserToStartToTransferTriggered_loadingIconAppears() {
+        controllerSender.displayChip(moveCloserToStartCast())
+        controllerSender.displayChip(transferToReceiverTriggered())
 
         assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
     }
 
     @Test
-    fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
-        controllerSender.displayChip(transferInitiated())
+    fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() {
+        controllerSender.displayChip(transferToReceiverTriggered())
         controllerSender.displayChip(transferSucceeded())
 
         assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
     }
 
     @Test
-    fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
-        controllerSender.displayChip(transferInitiated())
+    fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() {
+        controllerSender.displayChip(transferToReceiverTriggered())
         controllerSender.displayChip(transferSucceeded { })
 
         assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE)
     }
 
     @Test
-    fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
+    fun changeFromTransferSucceededToMoveCloserToStart_undoButtonDisappears() {
         controllerSender.displayChip(transferSucceeded())
-        controllerSender.displayChip(moveCloserToTransfer())
+        controllerSender.displayChip(moveCloserToStartCast())
 
         assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
     }
 
+    @Test
+    fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
+        controllerSender.displayChip(transferToReceiverTriggered())
+        controllerSender.displayChip(transferFailed())
+
+        assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
+    }
+
     private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
 
     private fun LinearLayout.getChipText(): String =
@@ -233,6 +193,8 @@
 
     private fun LinearLayout.getUndoButton(): View = this.requireViewById(R.id.undo)
 
+    private fun LinearLayout.getFailureIcon(): View = this.requireViewById(R.id.failure_icon)
+
     private fun getChipView(): LinearLayout {
         val viewCaptor = ArgumentCaptor.forClass(View::class.java)
         verify(windowManager).addView(viewCaptor.capture(), any())
@@ -240,22 +202,26 @@
     }
 
     /** Helper method providing default parameters to not clutter up the tests. */
-    private fun moveCloserToTransfer() =
-        MoveCloserToTransfer(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+    private fun moveCloserToStartCast() =
+        MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
-    private fun transferInitiated(
-        future: Future<Runnable?> = TEST_FUTURE
-    ) = TransferInitiated(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, future)
+    private fun moveCloserToEndCast() =
+        MoveCloserToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+
+    /** Helper method providing default parameters to not clutter up the tests. */
+    private fun transferToReceiverTriggered() =
+        TransferToReceiverTriggered(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferSucceeded(
         undoRunnable: Runnable? = null
     ) = TransferSucceeded(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoRunnable)
+
+    /** Helper method providing default parameters to not clutter up the tests. */
+    private fun transferFailed() =
+        TransferFailed(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
 }
 
 private const val DEVICE_NAME = "My Tablet"
 private const val APP_ICON_CONTENT_DESC = "Content description"
-// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
-// state.
-private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
new file mode 100644
index 0000000..ca90945
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -0,0 +1,80 @@
+package com.android.systemui.media.taptotransfer.sender
+
+import android.media.MediaRoute2Info
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MediaTttSenderServiceTest : SysuiTestCase() {
+
+    private lateinit var service: MediaTttSenderService
+    private lateinit var callback: IDeviceSenderCallback
+
+    @Mock
+    private lateinit var controller: MediaTttChipControllerSender
+
+    private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
+        .addFeature("feature")
+        .build()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        service = MediaTttSenderService(context, controller)
+        callback = IDeviceSenderCallback.Stub.asInterface(service.onBind(null))
+    }
+
+    @Test
+    fun closeToReceiverToStartCast_controllerTriggeredWithCorrectState() {
+        val name = "Fake name"
+        callback.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name))
+
+        val chipStateCaptor = argumentCaptor<MoveCloserToStartCast>()
+        verify(controller).displayChip(capture(chipStateCaptor))
+
+        val chipState = chipStateCaptor.value!!
+        assertThat(chipState.otherDeviceName).isEqualTo(name)
+    }
+
+    @Test
+    fun closeToReceiverToEndCast_controllerTriggeredWithCorrectState() {
+        val name = "Fake name"
+        callback.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name))
+
+        val chipStateCaptor = argumentCaptor<MoveCloserToEndCast>()
+        verify(controller).displayChip(capture(chipStateCaptor))
+
+        val chipState = chipStateCaptor.value!!
+        assertThat(chipState.otherDeviceName).isEqualTo(name)
+    }
+
+    @Test
+    fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() {
+        val name = "Fake name"
+        callback.transferToReceiverTriggered(mediaInfo, DeviceInfo(name))
+
+        val chipStateCaptor = argumentCaptor<TransferToReceiverTriggered>()
+        verify(controller).displayChip(capture(chipStateCaptor))
+
+        val chipState = chipStateCaptor.value!!
+        assertThat(chipState.otherDeviceName).isEqualTo(name)
+    }
+
+    @Test
+    fun transferFailed_controllerTriggeredWithTransferFailedState() {
+        callback.transferFailed(mediaInfo, DeviceInfo("Fake name"))
+
+        verify(controller).displayChip(any<TransferFailed>())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 8cd7d94..5a06048 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -100,4 +100,34 @@
         Cam cam = Cam.fromInt(tertiaryMid);
         Assert.assertEquals(cam.getHue(), 50.0, 10.0);
     }
+
+    @Test
+    public void testSpritz() {
+        int colorInt = 0xffB3588A; // H350 C50 T50
+        ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+                Style.SPRITZ /* style */);
+        int primaryMid = colorScheme.getAccent1().get(colorScheme.getAccent1().size() / 2);
+        Cam cam = Cam.fromInt(primaryMid);
+        Assert.assertEquals(cam.getChroma(), 4.0, 1.0);
+    }
+
+    @Test
+    public void testVibrant() {
+        int colorInt = 0xffB3588A; // H350 C50 T50
+        ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+                Style.VIBRANT /* style */);
+        int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+        Cam cam = Cam.fromInt(neutralMid);
+        Assert.assertEquals(cam.getChroma(), 8.0, 1.0);
+    }
+
+    @Test
+    public void testExpressive() {
+        int colorInt = 0xffB3588A; // H350 C50 T50
+        ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+                Style.EXPRESSIVE /* style */);
+        int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+        Cam cam = Cam.fromInt(neutralMid);
+        Assert.assertEquals(cam.getChroma(), 16.0, 1.0);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 3e8e874..73d2b0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
 import org.junit.After;
@@ -92,7 +93,8 @@
                         mock(DumpManager.class),
                         mock(AutoHideController.class),
                         mock(LightBarController.class),
-                        Optional.of(mock(Pip.class))));
+                        Optional.of(mock(Pip.class)),
+                        Optional.of(mock(BackAnimation.class))));
         initializeNavigationBars();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 5003013..9ca898b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -95,6 +95,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -105,7 +106,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 
 import java.util.Optional;
 
@@ -383,7 +383,8 @@
                 mAutoHideController,
                 mAutoHideControllerFactory,
                 Optional.of(mTelecomManager),
-                mInputMethodManager);
+                mInputMethodManager,
+                Optional.of(mock(BackAnimation.class)));
         return spy(factory.create(context));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index 03a0da7..4a6bbbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -24,6 +24,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -73,7 +74,7 @@
     private DeviceConfigProxyFake mProxyFake;
 
     private void setUpLocal(String deviceConfigActivity, String defaultActivity,
-            boolean validateActivity, boolean enableSetting) {
+            boolean validateActivity, boolean enableSetting, boolean enableOnLockScreen) {
         MockitoAnnotations.initMocks(this);
         int enableSettingInt = enableSetting ? 1 : 0;
 
@@ -91,6 +92,8 @@
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)).thenReturn(true);
         mContext.getOrCreateTestableResources().addOverride(R.string.def_qr_code_component,
                 defaultActivity);
+        mContext.getOrCreateTestableResources().addOverride(
+                android.R.bool.config_enableQrCodeScannerOnLockScreen, enableOnLockScreen);
 
         mProxyFake = new DeviceConfigProxyFake();
         mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
@@ -126,7 +129,8 @@
     @Test
     public void qrCodeScannerInit_withoutDefaultValue() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "", /* validateActivity */ true, /* enableSetting */true);
+                "", /* validateActivity */ true, /* enableSetting */ true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails(null);
         assertThat(mController.isEnabledForLockScreenButton()).isFalse();
         assertThat(mController.isEnabledForQuickSettings()).isFalse();
@@ -135,7 +139,8 @@
     @Test
     public void qrCodeScannerInit_withIncorrectDefaultValue() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "abc/.def", /* validateActivity */ false, /* enableSetting */ true);
+                "abc/.def", /* validateActivity */ false, /* enableSetting */ true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails(null);
         assertThat(mController.isEnabledForLockScreenButton()).isFalse();
     }
@@ -143,7 +148,8 @@
     @Test
     public void qrCodeScannerInit_withCorrectDefaultValue() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+                "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -152,7 +158,8 @@
     @Test
     public void qrCodeScannerInit_withCorrectDeviceConfig() {
         setUpLocal(/* deviceConfigActivity */ "abc/.def", /* defaultActivity */
-                "", /* validateActivity */ true, /* enableSetting */true);
+                "", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -161,7 +168,8 @@
     @Test
     public void qrCodeScannerInit_withCorrectDeviceConfig_withCorrectDefaultValue() {
         setUpLocal(/* deviceConfigActivity */ "abc/.def", /* defaultActivity */
-                "xyz/.qrs", /* validateActivity */true, /* enableSetting */ true);
+                "xyz/.qrs", /* validateActivity */true, /* enableSetting */ true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -170,7 +178,8 @@
     @Test
     public void qrCodeScannerInit_withCorrectDeviceConfig_fullActivity() {
         setUpLocal(/* deviceConfigActivity */ "abc/abc.def", /* defaultActivity */
-                "", /* validateActivity */  true, /* enableSetting */ true);
+                "", /* validateActivity */  true, /* enableSetting */ true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/abc.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -179,7 +188,8 @@
     @Test
     public void qrCodeScannerInit_withIncorrectDeviceConfig() {
         setUpLocal(/* deviceConfigActivity */ "def/.efg", /* defaultActivity */
-                "", /* validateActivity */ false, /* enableSetting */ true);
+                "", /* validateActivity */ false, /* enableSetting */ true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails(null);
         assertThat(mController.isEnabledForLockScreenButton()).isFalse();
         assertThat(mController.isEnabledForQuickSettings()).isFalse();
@@ -188,7 +198,8 @@
     @Test
     public void verifyDeviceConfigChange_withDefaultActivity() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+                "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -214,7 +225,8 @@
     @Test
     public void verifyDeviceConfigChange_withoutDefaultActivity() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "", /* validateActivity */ true, /* enableSetting */ true);
+                "", /* validateActivity */ true, /* enableSetting */ true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails(null);
         assertThat(mController.isEnabledForLockScreenButton()).isFalse();
         assertThat(mController.isEnabledForQuickSettings()).isFalse();
@@ -239,7 +251,8 @@
     @Test
     public void verifyDeviceConfigChangeToSameValue() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "", /* validateActivity */ true, /* enableSetting */true);
+                "", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
 
         mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER,
@@ -261,7 +274,8 @@
     @Test
     public void verifyPreferenceChange() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+                "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
         mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
                 UserHandle.USER_CURRENT);
         mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
@@ -278,7 +292,8 @@
     @Test
     public void verifyPreferenceChangeToSameValue() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+                "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -301,7 +316,8 @@
     @Test
     public void verifyUnregisterRegisterChangeObservers() {
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
-                "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+                "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ true);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -312,7 +328,7 @@
         assertThat(mController.isEnabledForLockScreenButton()).isFalse();
         assertThat(mController.isEnabledForQuickSettings()).isFalse();
 
-        // Unregister once again and make sure, it affect affect the next register event
+        // Unregister once again and make sure it affects the next register event
         mController.unregisterQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE,
                 QR_CODE_SCANNER_PREFERENCE_CHANGE);
         mController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE,
@@ -321,4 +337,15 @@
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isEnabledForQuickSettings()).isTrue();
     }
+
+    @Test
+    public void verifyDisableLockscreenButton() {
+        setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
+                "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+                /* enableOnLockScreen */ false);
+        assertThat(mController.getIntent()).isNotNull();
+        assertThat(mController.isEnabledForLockScreenButton()).isFalse();
+        assertThat(mController.isEnabledForQuickSettings()).isTrue();
+        assertThat(getSettingsQRCodeDefaultComponent()).isNull();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 26f04fc..354bb51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -54,8 +54,6 @@
     @Mock
     private lateinit var userInfoController: UserInfoController
     @Mock
-    private lateinit var qsPanelController: QSPanelController
-    @Mock
     private lateinit var multiUserSwitchController: MultiUserSwitchController
     @Mock
     private lateinit var globalActionsDialog: GlobalActionsDialogLite
@@ -81,7 +79,7 @@
         view = LayoutInflater.from(context)
                 .inflate(R.layout.footer_actions, null) as FooterActionsView
 
-        controller = FooterActionsController(view, qsPanelController, activityStarter,
+        controller = FooterActionsController(view, activityStarter,
                 userManager, userTracker, userInfoController, multiUserSwitchController,
                 deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
                 globalActionsDialog, uiEventLogger, showPMLiteButton = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 8b19c50..f43e68f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -18,7 +18,11 @@
 
 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.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.ClipData;
@@ -31,6 +35,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -60,13 +66,20 @@
     private TextView mBuildText;
     @Mock
     private FooterActionsController mFooterActionsController;
+    @Mock
+    private FalsingManager mFalsingManager;
+    @Mock
+    private ActivityStarter mActivityStarter;
 
     private QSFooterViewController mController;
+    private View mEditButton;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mEditButton = new View(mContext);
+
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
 
         mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
@@ -77,9 +90,11 @@
 
         when(mView.isAttachedToWindow()).thenReturn(true);
         when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
+        when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton);
 
-        mController = new QSFooterViewController(mView, mUserTracker, mQSPanelController,
-                mQuickQSPanelController, mFooterActionsController);
+        mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager,
+                mActivityStarter, mQSPanelController, mQuickQSPanelController,
+                mFooterActionsController);
 
         mController.init();
     }
@@ -99,4 +114,27 @@
         verify(mClipboardManager).setPrimaryClip(captor.capture());
         assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
     }
+
+    @Test
+    public void testEditButton_falseTap() {
+        when(mFalsingManager.isFalseTap(anyInt())).thenReturn(true);
+
+        mEditButton.performClick();
+
+        verify(mQSPanelController, never()).showEdit(any());
+        verifyZeroInteractions(mActivityStarter);
+    }
+
+    @Test
+    public void testEditButton_realTap() {
+        when(mFalsingManager.isFalseTap(anyInt())).thenReturn(false);
+
+        mEditButton.performClick();
+
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+
+        verify(mActivityStarter).postQSRunnableDismissingKeyguard(captor.capture());
+        captor.getValue().run();
+        verify(mQSPanelController).showEdit(mEditButton);
+    }
 }
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 2e1fb07..8ccf559 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -218,7 +218,8 @@
         String expected = "TestableQSPanelControllerBase:\n"
                 + "  Tile records:\n"
                 + "    " + mockTileString + "\n"
-                + "    " + mockTileViewString + "\n";
+                + "    " + mockTileViewString + "\n"
+                + "  media bounds: null\n";
         assertEquals(expected, w.getBuffer().toString());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
index f56a185..2ad9c94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
@@ -130,4 +130,37 @@
         val tile = content.getChildAt(1) as QSTileView
         assertThat((tile.icon.iconView as ImageView).drawable).isNotNull()
     }
+
+    @Test
+    fun setTileData_hasNoStateDescription() {
+        val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+        val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+        dialog.setTileData(tileData)
+        dialog.show()
+
+        TestableLooper.get(this).processAllMessages()
+
+        val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+        val tile = content.getChildAt(1) as QSTileView
+
+        assertThat(tile.stateDescription).isEqualTo("")
+    }
+
+    @Test
+    fun setTileData_tileNotClickable() {
+        val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+        val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+        dialog.setTileData(tileData)
+        dialog.show()
+
+        TestableLooper.get(this).processAllMessages()
+
+        val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+        val tile = content.getChildAt(1) as QSTileView
+
+        assertThat(tile.isClickable).isFalse()
+        assertThat(tile.isLongClickable).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index af33daf..968b12a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.qs.tiles.CameraToggleTile
 import com.android.systemui.qs.tiles.CastTile
 import com.android.systemui.qs.tiles.CellularTile
+import com.android.systemui.qs.tiles.ColorCorrectionTile
 import com.android.systemui.qs.tiles.ColorInversionTile
 import com.android.systemui.qs.tiles.DataSaverTile
 import com.android.systemui.qs.tiles.DeviceControlsTile
@@ -91,7 +92,8 @@
         "wallet" to QuickAccessWalletTile::class.java,
         "qr_code_scanner" to QRCodeScannerTile::class.java,
         "onehanded" to OneHandedModeTile::class.java,
-        "fgsmanager" to FgsManagerTile::class.java
+        "fgsmanager" to FgsManagerTile::class.java,
+        "color_correction" to ColorCorrectionTile::class.java
 )
 
 @RunWith(AndroidTestingRunner::class)
@@ -132,6 +134,7 @@
     @Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile
     @Mock private lateinit var oneHandedModeTile: OneHandedModeTile
     @Mock private lateinit var fgsManagerTile: FgsManagerTile
+    @Mock private lateinit var colorCorrectionTile: ColorCorrectionTile
 
     private lateinit var factory: QSFactoryImpl
 
@@ -175,7 +178,8 @@
                 { quickAccessWalletTile },
                 { qrCodeScannerTile },
                 { oneHandedModeTile },
-                { fgsManagerTile }
+                { fgsManagerTile },
+                { colorCorrectionTile }
         )
         // When adding/removing tiles, fix also [specMap]
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
new file mode 100644
index 0000000..debe41c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+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.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
[email protected](setAsMainLooper = true)
+@SmallTest
+public class ColorCorrectionTileTest extends SysuiTestCase {
+
+    @Mock
+    private QSTileHost mHost;
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    @Mock
+    private QSLogger mQSLogger;
+    @Mock
+    private UiEventLogger mUiEventLogger;
+    @Mock
+    private UserTracker mUserTracker;
+
+    private TestableLooper mTestableLooper;
+    private SecureSettings mSecureSettings;
+    private ColorCorrectionTile mTile;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mSecureSettings = new FakeSettings();
+        mTestableLooper = TestableLooper.get(this);
+
+        when(mHost.getContext()).thenReturn(mContext);
+        when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
+
+        mTile = new ColorCorrectionTile(
+                mHost,
+                mTestableLooper.getLooper(),
+                new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
+                mMetricsLogger,
+                mStatusBarStateController,
+                mActivityStarter,
+                mQSLogger,
+                mUserTracker,
+                mSecureSettings
+        );
+
+        mTile.initialize();
+        mTestableLooper.processAllMessages();
+    }
+
+    @Test
+    public void longClick_expectedAction() {
+        final ArgumentCaptor<Intent> IntentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+        mTile.longClick(/* view= */ null);
+        mTestableLooper.processAllMessages();
+
+        verify(mActivityStarter).postStartActivityDismissingKeyguard(IntentCaptor.capture(),
+                anyInt(), any());
+        assertThat(IntentCaptor.getValue().getAction()).isEqualTo(
+                Settings.ACTION_COLOR_CORRECTION_SETTINGS);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
new file mode 100644
index 0000000..8bc438b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.navigationbar
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.concurrent.futures.DirectExecutor
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.*
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class RegionSamplingHelperTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var sampledView: View
+    @Mock
+    lateinit var samplingCallback: RegionSamplingHelper.SamplingCallback
+    @Mock
+    lateinit var compositionListener: RegionSamplingHelper.SysuiCompositionSamplingListener
+    @Mock
+    lateinit var viewRootImpl: ViewRootImpl
+    @Mock
+    lateinit var surfaceControl: SurfaceControl
+    @Mock
+    lateinit var wrappedSurfaceControl: SurfaceControl
+    @JvmField @Rule
+    var rule = MockitoJUnit.rule()
+    lateinit var regionSamplingHelper: RegionSamplingHelper
+
+    @Before
+    fun setup() {
+        whenever(sampledView.isAttachedToWindow).thenReturn(true)
+        whenever(sampledView.viewRootImpl).thenReturn(viewRootImpl)
+        whenever(viewRootImpl.surfaceControl).thenReturn(surfaceControl)
+        whenever(surfaceControl.isValid).thenReturn(true)
+        whenever(wrappedSurfaceControl.isValid).thenReturn(true)
+        whenever(samplingCallback.isSamplingEnabled).thenReturn(true)
+        regionSamplingHelper = object : RegionSamplingHelper(sampledView, samplingCallback,
+                DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener) {
+            override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+                return wrappedSurfaceControl
+            }
+        }
+        regionSamplingHelper.setWindowVisible(true)
+    }
+
+    @Test
+    fun testStart_register() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+    }
+
+    @Test
+    fun testStart_unregister() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        regionSamplingHelper.setWindowVisible(false)
+        verify(compositionListener).unregister(any())
+    }
+
+    @Test
+    fun testStart_hasBlur_neverRegisters() {
+        regionSamplingHelper.setWindowHasBlurs(true)
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        verify(compositionListener, never())
+                .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+    }
+
+    @Test
+    fun testStart_stopAndDestroy() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        regionSamplingHelper.stopAndDestroy()
+        verify(compositionListener).unregister(any())
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 5de4c11..6c29ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -151,17 +151,17 @@
     @Test
     public void testShowTransient() {
         int[] types = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
-        mCommandQueue.showTransient(DEFAULT_DISPLAY, types);
+        mCommandQueue.showTransient(DEFAULT_DISPLAY, types, true /* isGestureOnSystemBar */);
         waitForIdleSync();
-        verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types));
+        verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types), eq(true));
     }
 
     @Test
     public void testShowTransientForSecondaryDisplay() {
         int[] types = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
-        mCommandQueue.showTransient(SECONDARY_DISPLAY, types);
+        mCommandQueue.showTransient(SECONDARY_DISPLAY, types, true /* isGestureOnSystemBar */);
         waitForIdleSync();
-        verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types));
+        verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types), eq(true));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
index 38ad6b8..c0d1155 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
@@ -37,7 +37,7 @@
     private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags)
 
     @Test
-    fun getDisableFlagsString_oldAndNewSame_statesLoggedButDiffsNotLogged() {
+    fun getDisableFlagsString_oldAndNewSame_newAndUnchangedLoggedOldNotLogged() {
         val state = DisableFlagsLogger.DisableState(
                 0b111, // ABC
                 0b01 // mN
@@ -45,10 +45,9 @@
 
         val result = disableFlagsLogger.getDisableFlagsString(state, state)
 
-        assertThat(result).contains("Old: ABC.mN")
-        assertThat(result).contains("New: ABC.mN")
-        assertThat(result).doesNotContain("(")
-        assertThat(result).doesNotContain(")")
+        assertThat(result).doesNotContain("Old")
+        assertThat(result).contains("ABC.mN")
+        assertThat(result).contains("(unchanged)")
     }
 
     @Test
@@ -66,7 +65,7 @@
 
         assertThat(result).contains("Old: ABC.mN")
         assertThat(result).contains("New: abC.Mn")
-        assertThat(result).contains("(ab.Mn)")
+        assertThat(result).contains("(changed: ab.Mn)")
     }
 
     @Test
@@ -82,7 +81,7 @@
                 )
         )
 
-        assertThat(result).contains("(.n)")
+        assertThat(result).contains("(changed: .n)")
     }
 
     @Test
@@ -96,8 +95,7 @@
         )
 
         assertThat(result).doesNotContain("Old")
-        assertThat(result).contains("New: abC.mN")
-        // We have no state to diff on, so we shouldn't see any diff in parentheses
+        assertThat(result).contains("abC.mN")
         assertThat(result).doesNotContain("(")
         assertThat(result).doesNotContain(")")
     }
@@ -141,7 +139,7 @@
                 )
         )
 
-        assertThat(result).contains("local modification: Abc.Mn (A.M)")
+        assertThat(result).contains("local modification: Abc.Mn (changed: A.M)")
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 7f35732..cf1a36a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -24,6 +24,7 @@
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
@@ -86,7 +87,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
-import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -132,8 +132,6 @@
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
     @Mock
-    private LockIcon mLockIcon;
-    @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -738,6 +736,41 @@
         verifyHideIndication(INDICATION_TYPE_OWNER_INFO);
     }
 
+    @Test
+    public void testOnKeyguardShowingChanged_notShowing_resetsMessages() {
+        createController();
+
+        // GIVEN keyguard isn't showing
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+        // WHEN keyguard showing changed called
+        mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+        // THEN messages are reset
+        verify(mRotateTextViewController).clearMessages();
+        assertThat(mTextView.getText()).isEqualTo("");
+    }
+
+    @Test
+    public void testOnKeyguardShowingChanged_showing_updatesPersistentMessages() {
+        createController();
+
+        // GIVEN keyguard is showing
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        // WHEN keyguard showing changed called
+        mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+        // THEN persistent messages are updated (in this case, most messages are hidden since
+        // no info is provided) - verify that this happens
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_OWNER_INFO);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_BATTERY);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_TRUST);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_ALIGNMENT);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_LOGOUT);
+    }
+
     private void sendUpdateDisclosureBroadcast() {
         mBroadcastReceiver.onReceive(mContext, new Intent());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
new file mode 100644
index 0000000..6971c63
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.statusbar
+
+import android.testing.AndroidTestingRunner
+import android.util.DisplayMetrics
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LSShadeTransitionLoggerTest : SysuiTestCase() {
+    lateinit var logger: LSShadeTransitionLogger
+    @Mock
+    lateinit var gestureLogger: LockscreenGestureLogger
+    @Mock
+    lateinit var displayMetrics: DisplayMetrics
+    @JvmField @Rule
+    val mockito = MockitoJUnit.rule()
+
+    @Before
+    fun setup() {
+        logger = LSShadeTransitionLogger(
+                LogBuffer("Test", 10, 10, mock()),
+                gestureLogger,
+                displayMetrics)
+    }
+
+    @Test
+    fun testLogDragDownStarted() {
+        val view: ExpandableView = mock()
+        // log a non-null, non row, ensure no crash
+        logger.logDragDownStarted(view)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 89435ae..42647f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -4,11 +4,12 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
 import com.android.systemui.ExpandHelper
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.media.MediaHierarchyManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
@@ -18,7 +19,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.phone.NotificationPanelViewController
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.phone.StatusBar
@@ -56,12 +57,13 @@
     lateinit var transitionController: LockscreenShadeTransitionController
     lateinit var row: ExpandableNotificationRow
     @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
-    @Mock lateinit var lockscreenGestureLogger: LockscreenGestureLogger
+    @Mock lateinit var logger: LSShadeTransitionLogger
+    @Mock lateinit var dumpManager: DumpManager
     @Mock lateinit var keyguardBypassController: KeyguardBypassController
     @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
     @Mock lateinit var falsingCollector: FalsingCollector
     @Mock lateinit var ambientState: AmbientState
-    @Mock lateinit var displayMetrics: DisplayMetrics
+    @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
     @Mock lateinit var scrimController: ScrimController
     @Mock lateinit var configurationController: ConfigurationController
@@ -86,18 +88,19 @@
                 .addOverride(R.bool.config_use_split_notification_shade, false)
         transitionController = LockscreenShadeTransitionController(
             statusBarStateController = statusbarStateController,
-            lockscreenGestureLogger = lockscreenGestureLogger,
+            logger = logger,
             keyguardBypassController = keyguardBypassController,
             lockScreenUserManager = lockScreenUserManager,
             falsingCollector = falsingCollector,
             ambientState = ambientState,
-            displayMetrics = displayMetrics,
             mediaHierarchyManager = mediaHierarchyManager,
             scrimController = scrimController,
             depthController = depthController,
+            wakefulnessLifecycle = wakefulnessLifecycle,
             context = context,
             configurationController = configurationController,
-            falsingManager = falsingManager
+            falsingManager = falsingManager,
+            dumpManager = dumpManager
         )
         whenever(nsslController.view).thenReturn(stackscroller)
         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
@@ -144,6 +147,23 @@
     }
 
     @Test
+    fun testWakingToShadeLockedWhenDozing() {
+        whenever(statusbarStateController.isDozing).thenReturn(true)
+        transitionController.goToLockedShade(null)
+        verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+        assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
+    }
+
+    @Test
+    fun testNotWakingToShadeLockedWhenNotDozing() {
+        whenever(statusbarStateController.isDozing).thenReturn(false)
+        transitionController.goToLockedShade(null)
+        verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+        assertFalse("Waking to shade locked when not dozing",
+                transitionController.isWakingToShadeLocked)
+    }
+
+    @Test
     fun testGoToLockedShadeOnlyOnKeyguard() {
         whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         transitionController.goToLockedShade(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index b31dd3c..eef9dd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -23,7 +23,7 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isA;
@@ -209,8 +209,6 @@
         when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
         doAnswer(invocation -> {
             mUserCallback = (DeviceProvisionedListener) invocation.getArguments()[0];
-            mUserCallback.onUserSetupChanged();
-            mUserCallback.onDeviceProvisionedChanged();
             TestableLooper.get(this).processAllMessages();
             return null;
         }).when(mMockProvisionController).addCallback(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 73eddd1..6262a9b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -35,6 +35,7 @@
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.settingslib.graph.SignalDrawable;
@@ -60,6 +61,72 @@
 public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
 
     @Test
+    public void testDeviceProvisioned_userNotSetUp() {
+        // GIVEN - user is not setup
+        when(mMockProvisionController.isCurrentUserSetup()).thenReturn(false);
+
+        // WHEN - a NetworkController is created
+        mNetworkController = new NetworkControllerImpl(mContext,
+                mMockCm,
+                mMockTm,
+                mTelephonyListenerManager,
+                mMockWm,
+                mMockNsm,
+                mMockSm,
+                mConfig,
+                TestableLooper.get(this).getLooper(),
+                mFakeExecutor,
+                mCallbackHandler,
+                mock(AccessPointControllerImpl.class),
+                mock(DataUsageController.class),
+                mMockSubDefaults,
+                mMockProvisionController,
+                mMockBd,
+                mDemoModeController,
+                mCarrierConfigTracker,
+                mFeatureFlags,
+                mock(DumpManager.class)
+        );
+        TestableLooper.get(this).processAllMessages();
+
+        // THEN - NetworkController claims the user is not setup
+        assertFalse("User has not been set up", mNetworkController.isUserSetup());
+    }
+
+    @Test
+    public void testDeviceProvisioned_userSetUp() {
+        // GIVEN - user is not setup
+        when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
+
+        // WHEN - a NetworkController is created
+        mNetworkController = new NetworkControllerImpl(mContext,
+                mMockCm,
+                mMockTm,
+                mTelephonyListenerManager,
+                mMockWm,
+                mMockNsm,
+                mMockSm,
+                mConfig,
+                TestableLooper.get(this).getLooper(),
+                mFakeExecutor,
+                mCallbackHandler,
+                mock(AccessPointControllerImpl.class),
+                mock(DataUsageController.class),
+                mMockSubDefaults,
+                mMockProvisionController,
+                mMockBd,
+                mDemoModeController,
+                mCarrierConfigTracker,
+                mFeatureFlags,
+                mock(DumpManager.class)
+        );
+        TestableLooper.get(this).processAllMessages();
+
+        // THEN - NetworkController claims the user is not setup
+        assertTrue("User has been set up", mNetworkController.isUserSetup());
+    }
+
+    @Test
     public void testNoIconWithoutMobile() {
         // Turn off mobile network support.
         when(mMockTm.isDataCapable()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index dc83c0d..f2b7bf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification;
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
 import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
@@ -428,6 +429,18 @@
     }
 
     @Test
+    public void testNotifyChannelModified_notifiesListeners() {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        String pkg = "PKG";
+        mEntryManager.notifyChannelModified(pkg, UserHandle.CURRENT, channel,
+                NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+        verify(mNotifCollectionListener).onNotificationChannelModified(eq(pkg),
+                eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+        verify(mEntryListener).onNotificationChannelModified(eq(pkg),
+                eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+    }
+
+    @Test
     public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
         // GIVEN an entry manager with a notification
         mEntryManager.addActiveNotificationForTest(mEntry);
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 41163bf..7068009 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
@@ -16,7 +16,10 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
@@ -51,6 +54,8 @@
 
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -334,6 +339,37 @@
     }
 
     @Test
+    public void testEventDispatchedWhenChannelChanged() {
+        // GIVEN a collection with one notif that has a channel
+        NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        NotifEvent notif = mNoMan.postNotif(neb);
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+        clearInvocations(mCollectionListener);
+
+
+        // WHEN a notif channel is modified
+        channel.setAllowBubbles(true);
+        mNoMan.issueChannelModification(
+                TEST_PACKAGE,
+                entry.getSbn().getUser(),
+                channel,
+                NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN the listener is notified
+        mListenerInOrder.verify(mCollectionListener).onNotificationChannelModified(
+                TEST_PACKAGE,
+                entry.getSbn().getUser(),
+                channel,
+                NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+    }
+
+    @Test
     public void testRankingsAreUpdatedForOtherNotifs() {
         // GIVEN a collection with one notif
         NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
@@ -457,7 +493,7 @@
         mCollection.dismissNotification(entry1, defaultStats(entry1));
 
         // THEN lifetime extenders are never queried
-        verify(mExtender1, never()).shouldExtendLifetime(eq(entry1), anyInt());
+        verify(mExtender1, never()).maybeExtendLifetime(eq(entry1), anyInt());
     }
 
     @Test
@@ -797,7 +833,7 @@
     }
 
     @Test
-    public void testDismissingSummaryDoesNotDismissForegroundServiceChildren() {
+    public void testDismissingSummaryDoesDismissForegroundServiceChildren() {
         // GIVEN a collection with three grouped notifs in it
         CollectionEvent notif0 = postNotif(
                 buildNotif(TEST_PACKAGE, 0)
@@ -814,7 +850,31 @@
         // WHEN the summary is dismissed
         mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
 
-        // THEN the foreground service child is not dismissed
+        // THEN the foreground service child is dismissed
+        assertEquals(DISMISSED, notif0.entry.getDismissState());
+        assertEquals(PARENT_DISMISSED, notif1.entry.getDismissState());
+        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
+    }
+
+    @Test
+    public void testDismissingSummaryDoesNotDismissOngoingChildren() {
+        // GIVEN a collection with three grouped notifs in it
+        CollectionEvent notif0 = postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        CollectionEvent notif1 = postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1)
+                        .setFlag(mContext, FLAG_ONGOING_EVENT, true));
+        CollectionEvent notif2 = postNotif(
+                buildNotif(TEST_PACKAGE, 2)
+                        .setGroup(mContext, GROUP_1));
+
+        // WHEN the summary is dismissed
+        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
+
+        // THEN the ongoing child is not dismissed
         assertEquals(DISMISSED, notif0.entry.getDismissState());
         assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
         assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
@@ -886,9 +946,9 @@
         mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
 
         // THEN each extender is asked whether to extend, even if earlier ones return true
-        verify(mExtender1).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
-        verify(mExtender2).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
-        verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender1).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender2).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender3).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry is not removed
         assertTrue(mCollection.getAllNotifs().contains(entry2));
@@ -922,9 +982,9 @@
         mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
 
         // THEN each extender is re-queried
-        verify(mExtender1).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
-        verify(mExtender2).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
-        verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender1).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender2).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender3).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry is not removed
         assertTrue(mCollection.getAllNotifs().contains(entry2));
@@ -960,9 +1020,9 @@
         assertTrue(mCollection.getAllNotifs().contains(entry2));
 
         // THEN we don't re-query the extenders
-        verify(mExtender1, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
-        verify(mExtender2, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
-        verify(mExtender3, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender1, never()).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender2, never()).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender3, never()).maybeExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry properly records all extenders that returned true
         assertEquals(singletonList(mExtender1), entry2.mLifetimeExtenders);
@@ -1427,6 +1487,49 @@
         verify(mCollectionListener, never()).onEntryUpdated(any(), anyBoolean());
     }
 
+    @Test
+    public void testCannotDismissOngoingNotificationChildren() {
+        // GIVEN an ongoing notification
+        final NotificationEntry container = new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE)
+                .setId(47)
+                .setGroup(mContext, "group")
+                .setFlag(mContext, FLAG_ONGOING_EVENT, true)
+                .build();
+
+        // THEN its children are not dismissible
+        assertFalse(mCollection.shouldAutoDismissChildren(
+                container, container.getSbn().getGroupKey()));
+    }
+
+    @Test
+    public void testCannotDismissNoClearNotifications() {
+        // GIVEN an no-clear notification
+        final NotificationEntry container = new NotificationEntryBuilder()
+                .setFlag(mContext, FLAG_NO_CLEAR, true)
+                .build();
+
+        // THEN its children are not dismissible
+        assertFalse(mCollection.shouldAutoDismissChildren(
+                container, container.getSbn().getGroupKey()));
+    }
+
+    @Test
+    public void testCanDismissFgsNotificationChildren() {
+        // GIVEN an FGS but not ongoing notification
+        final NotificationEntry container = new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE)
+                .setId(47)
+                .setGroup(mContext, "group")
+                .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        container.setDismissState(NOT_DISMISSED);
+
+        // THEN its children are dismissible
+        assertTrue(mCollection.shouldAutoDismissChildren(
+                container, container.getSbn().getGroupKey()));
+    }
+
     private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
         return new NotificationEntryBuilder()
                 .setPkg(pkg)
@@ -1528,7 +1631,7 @@
         }
 
         @Override
-        public boolean shouldExtendLifetime(
+        public boolean maybeExtendLifetime(
                 @NonNull NotificationEntry entry,
                 @CancellationReason int reason) {
             return shouldExtendLifetime;
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 b832577..25dd23a 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
@@ -1968,7 +1968,7 @@
         }
 
         @Override
-        public int compare(ListEntry o1, ListEntry o2) {
+        public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
             boolean contains1 = mPreferredPackages.contains(
                     o1.getRepresentativeEntry().getSbn().getPackageName());
             boolean contains2 = mPreferredPackages.contains(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index f2e7081..bc32759 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -28,6 +28,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Intent;
 import android.graphics.Color;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
@@ -151,4 +155,35 @@
         // THEN the entry is NOT in the fgs section
         assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
     }
+
+    @Test
+    public void testIncludeCallInSection_importanceDefault() {
+        // GIVEN the notification represents a call with > min importance
+        mEntryBuilder
+                .setImportance(IMPORTANCE_DEFAULT)
+                .modifyNotification(mContext)
+                .setStyle(makeCallStyle());
+
+        // THEN the entry is in the fgs section
+        assertTrue(mFgsSection.isInSection(mEntryBuilder.build()));
+    }
+
+    @Test
+    public void testDiscludeCallInSection_importanceMin() {
+        // GIVEN the notification represents a call with min importance
+        mEntryBuilder
+                .setImportance(IMPORTANCE_MIN)
+                .modifyNotification(mContext)
+                .setStyle(makeCallStyle());
+
+        // THEN the entry is NOT in the fgs section
+        assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+    }
+
+    private Notification.CallStyle makeCallStyle() {
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent("action"), PendingIntent.FLAG_IMMUTABLE);
+        final Person person = new Person.Builder().setName("person").build();
+        return Notification.CallStyle.forIncomingCall(person, pendingIntent, pendingIntent);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index a46b440..8deac94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -24,18 +24,24 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
@@ -47,12 +53,15 @@
     // captured listeners and pluggables:
     private lateinit var promoter: NotifPromoter
     private lateinit var peopleSectioner: NotifSectioner
+    private lateinit var peopleComparator: NotifComparator
 
     @Mock private lateinit var pipeline: NotifPipeline
     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
     @Mock private lateinit var channel: NotificationChannel
     @Mock private lateinit var headerController: NodeController
     private lateinit var entry: NotificationEntry
+    private lateinit var entryA: NotificationEntry
+    private lateinit var entryB: NotificationEntry
 
     private lateinit var coordinator: ConversationCoordinator
 
@@ -70,8 +79,15 @@
         }
 
         peopleSectioner = coordinator.sectioner
+        peopleComparator = coordinator.comparator
 
         entry = NotificationEntryBuilder().setChannel(channel).build()
+
+        val section = NotifSection(peopleSectioner, 0)
+        entryA = NotificationEntryBuilder().setChannel(channel)
+            .setSection(section).setTag("A").build()
+        entryB = NotificationEntryBuilder().setChannel(channel)
+            .setSection(section).setTag("B").build()
     }
 
     @Test
@@ -90,4 +106,36 @@
         assertTrue(peopleSectioner.isInSection(entry))
         assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build()))
     }
+
+    @Test
+    fun testComparatorIgnoresFromOtherSection() {
+        val e1 = NotificationEntryBuilder().setId(1).setChannel(channel).build()
+        val e2 = NotificationEntryBuilder().setId(2).setChannel(channel).build()
+
+        // wrong section -- never classify
+        assertThat(peopleComparator.compare(e1, e2)).isEqualTo(0)
+        verify(peopleNotificationIdentifier, never()).getPeopleNotificationType(any())
+    }
+
+    @Test
+    fun testComparatorPutsImportantPeopleFirst() {
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
+            .thenReturn(TYPE_IMPORTANT_PERSON)
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
+            .thenReturn(TYPE_PERSON)
+
+        // only put people notifications in this section
+        assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1)
+    }
+
+    @Test
+    fun testComparatorEquatesPeopleWithSameType() {
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
+            .thenReturn(TYPE_PERSON)
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
+            .thenReturn(TYPE_PERSON)
+
+        // only put people notifications in this section
+        assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0)
+    }
 }
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 0f6bd77..4143647 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
@@ -73,43 +73,43 @@
 
     @Test
     fun testSimpleLifetimeExtension() {
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
         notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
         notifGutsViewListener.onGutsClose(entry1)
         verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
     }
 
     @Test
     fun testDoubleOpenLifetimeExtension() {
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
         notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
         notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
         notifGutsViewListener.onGutsClose(entry1)
         verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
     }
 
     @Test
     fun testTwoEntryLifetimeExtension() {
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
         notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
         notifGutsViewListener.onGutsOpen(entry2, mock(NotificationGuts::class.java))
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue()
         notifGutsViewListener.onGutsClose(entry1)
         verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isTrue()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue()
         notifGutsViewListener.onGutsClose(entry2)
         verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry2)
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
deleted file mode 100644
index a3569e4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.coordinator;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.render.NodeController;
-import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-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)
[email protected]
-public class HeadsUpCoordinatorTest extends SysuiTestCase {
-
-    private HeadsUpCoordinator mCoordinator;
-
-    // captured listeners and pluggables:
-    private NotifCollectionListener mCollectionListener;
-    private NotifPromoter mNotifPromoter;
-    private NotifLifetimeExtender mNotifLifetimeExtender;
-    private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
-    private NotifSectioner mNotifSectioner;
-
-    @Mock private NotifPipeline mNotifPipeline;
-    @Mock private HeadsUpManager mHeadsUpManager;
-    @Mock private HeadsUpViewBinder mHeadsUpViewBinder;
-    @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
-    @Mock private NotificationRemoteInputManager mRemoteInputManager;
-    @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
-    @Mock private NodeController mHeaderController;
-
-    private NotificationEntry mEntry;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mCoordinator = new HeadsUpCoordinator(
-                mHeadsUpManager,
-                mHeadsUpViewBinder,
-                mNotificationInterruptStateProvider,
-                mRemoteInputManager,
-                mHeaderController);
-
-        mCoordinator.attach(mNotifPipeline);
-
-        // capture arguments:
-        ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
-                ArgumentCaptor.forClass(NotifCollectionListener.class);
-        ArgumentCaptor<NotifPromoter> notifPromoterCaptor =
-                ArgumentCaptor.forClass(NotifPromoter.class);
-        ArgumentCaptor<NotifLifetimeExtender> notifLifetimeExtenderCaptor =
-                ArgumentCaptor.forClass(NotifLifetimeExtender.class);
-        ArgumentCaptor<OnHeadsUpChangedListener> headsUpChangedListenerCaptor =
-                ArgumentCaptor.forClass(OnHeadsUpChangedListener.class);
-
-        verify(mNotifPipeline).addCollectionListener(notifCollectionCaptor.capture());
-        verify(mNotifPipeline).addPromoter(notifPromoterCaptor.capture());
-        verify(mNotifPipeline).addNotificationLifetimeExtender(
-                notifLifetimeExtenderCaptor.capture());
-        verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
-
-        mCollectionListener = notifCollectionCaptor.getValue();
-        mNotifPromoter = notifPromoterCaptor.getValue();
-        mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
-        mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
-
-        mNotifSectioner = mCoordinator.getSectioner();
-        mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
-        mEntry = new NotificationEntryBuilder().build();
-    }
-
-    @Test
-    public void testPromotesCurrentHUN() {
-        // GIVEN the current HUN is set to mEntry
-        setCurrentHUN(mEntry);
-
-        // THEN only promote the current HUN, mEntry
-        assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
-        assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder().build()));
-    }
-
-    @Test
-    public void testIncludeInSectionCurrentHUN() {
-        // GIVEN the current HUN is set to mEntry
-        setCurrentHUN(mEntry);
-
-        // THEN only section the current HUN, mEntry
-        assertTrue(mNotifSectioner.isInSection(mEntry));
-        assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build()));
-    }
-
-    @Test
-    public void testLifetimeExtendsCurrentHUN() {
-        // GIVEN there is a HUN, mEntry
-        setCurrentHUN(mEntry);
-
-        // THEN only the current HUN, mEntry, should be lifetimeExtended
-        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0));
-        assertFalse(mNotifLifetimeExtender.shouldExtendLifetime(
-                new NotificationEntryBuilder().build(), /* cancellationReason */ 0));
-    }
-
-    @Test
-    public void testLifetimeExtensionEndsOnNewHUN() {
-        // GIVEN there was a HUN that was lifetime extended
-        setCurrentHUN(mEntry);
-        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
-                mEntry, /* cancellation reason */ 0));
-
-        // WHEN there's a new HUN
-        NotificationEntry newHUN = new NotificationEntryBuilder().build();
-        setCurrentHUN(newHUN);
-
-        // THEN the old entry's lifetime extension should be cancelled
-        verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
-    }
-
-    @Test
-    public void testLifetimeExtensionEndsOnNoHUNs() {
-        // GIVEN there was a HUN that was lifetime extended
-        setCurrentHUN(mEntry);
-        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
-                mEntry, /* cancellation reason */ 0));
-
-        // WHEN there's no longer a HUN
-        setCurrentHUN(null);
-
-        // THEN the old entry's lifetime extension should be cancelled
-        verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
-    }
-
-    @Test
-    public void testShowHUNOnInflationFinished() {
-        // WHEN a notification should HUN and its inflation is finished
-        when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
-
-        ArgumentCaptor<BindCallback> bindCallbackCaptor =
-                ArgumentCaptor.forClass(BindCallback.class);
-        mCollectionListener.onEntryAdded(mEntry);
-        verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), bindCallbackCaptor.capture());
-
-        bindCallbackCaptor.getValue().onBindFinished(mEntry);
-
-        // THEN we tell the HeadsUpManager to show the notification
-        verify(mHeadsUpManager).showNotification(mEntry);
-    }
-
-    @Test
-    public void testNoHUNOnInflationFinished() {
-        // WHEN a notification shouldn't HUN and its inflation is finished
-        when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
-        ArgumentCaptor<BindCallback> bindCallbackCaptor =
-                ArgumentCaptor.forClass(BindCallback.class);
-        mCollectionListener.onEntryAdded(mEntry);
-
-        // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
-        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(
-                eq(mEntry), bindCallbackCaptor.capture());
-        verify(mHeadsUpManager, never()).showNotification(mEntry);
-    }
-
-    @Test
-    public void testOnEntryRemovedRemovesHeadsUpNotification() {
-        // GIVEN the current HUN is mEntry
-        setCurrentHUN(mEntry);
-
-        // WHEN mEntry is removed from the notification collection
-        mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
-        when(mRemoteInputManager.isSpinning(any())).thenReturn(false);
-
-        // THEN heads up manager should remove the entry
-        verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
-    }
-
-    private void setCurrentHUN(NotificationEntry entry) {
-        when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
-        when(mHeadsUpManager.isAlerting(any())).thenReturn(false);
-        if (entry != null) {
-            when(mHeadsUpManager.isAlerting(entry.getKey())).thenReturn(true);
-        }
-        mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
-    }
-}
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
new file mode 100644
index 0000000..c67a233
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.BDDMockito.given
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.ArrayList
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class HeadsUpCoordinatorTest : SysuiTestCase() {
+    private lateinit var mCoordinator: HeadsUpCoordinator
+
+    // captured listeners and pluggables:
+    private lateinit var mCollectionListener: NotifCollectionListener
+    private lateinit var mNotifPromoter: NotifPromoter
+    private lateinit var mNotifLifetimeExtender: NotifLifetimeExtender
+    private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
+    private lateinit var mNotifSectioner: NotifSectioner
+
+    private val mNotifPipeline: NotifPipeline = mock()
+    private val mHeadsUpManager: HeadsUpManager = mock()
+    private val mHeadsUpViewBinder: HeadsUpViewBinder = mock()
+    private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
+    private val mRemoteInputManager: NotificationRemoteInputManager = mock()
+    private val mEndLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
+    private val mHeaderController: NodeController = mock()
+
+    private lateinit var mEntry: NotificationEntry
+    private val mExecutor = FakeExecutor(FakeSystemClock())
+    private val mHuns: ArrayList<NotificationEntry> = ArrayList()
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mCoordinator = HeadsUpCoordinator(
+            mHeadsUpManager,
+            mHeadsUpViewBinder,
+            mNotificationInterruptStateProvider,
+            mRemoteInputManager,
+            mHeaderController,
+            mExecutor)
+        mCoordinator.attach(mNotifPipeline)
+
+        // capture arguments:
+        mCollectionListener = withArgCaptor {
+            verify(mNotifPipeline).addCollectionListener(capture())
+        }
+        mNotifPromoter = withArgCaptor {
+            verify(mNotifPipeline).addPromoter(capture())
+        }
+        mNotifLifetimeExtender = withArgCaptor {
+            verify(mNotifPipeline).addNotificationLifetimeExtender(capture())
+        }
+        mOnHeadsUpChangedListener = withArgCaptor {
+            verify(mHeadsUpManager).addListener(capture())
+        }
+        given(mHeadsUpManager.allEntries).willAnswer { mHuns.stream() }
+        given(mHeadsUpManager.isAlerting(anyString())).willAnswer { invocation ->
+            val key = invocation.getArgument<String>(0)
+            mHuns.any { entry -> entry.key == key }
+        }
+        given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer { invocation ->
+            val key = invocation.getArgument<String>(0)
+            !mHuns.any { entry -> entry.key == key }
+        }
+        whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+        mNotifSectioner = mCoordinator.sectioner
+        mNotifLifetimeExtender.setCallback(mEndLifetimeExtension)
+        mEntry = NotificationEntryBuilder().build()
+    }
+
+    @Test
+    fun testCancelStickyNotification() {
+        whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
+        addHUN(mEntry)
+        whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true)
+        whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L)
+        assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+        mExecutor.advanceClockToLast()
+        mExecutor.runAllReady()
+        verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
+        verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(true))
+    }
+
+    @Test
+    fun testCancelUpdatedStickyNotification() {
+        whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
+        addHUN(mEntry)
+        whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
+        assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+        mExecutor.advanceClockToLast()
+        mExecutor.runAllReady()
+        verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
+        verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+    }
+
+    @Test
+    fun testCancelNotification() {
+        whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(false)
+        addHUN(mEntry)
+        whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
+        assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+        mExecutor.advanceClockToLast()
+        mExecutor.runAllReady()
+        verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(false))
+        verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+    }
+
+    @Test
+    fun testPromotesCurrentHUN() {
+        // GIVEN the current HUN is set to mEntry
+        addHUN(mEntry)
+
+        // THEN only promote the current HUN, mEntry
+        assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+        assertFalse(mNotifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
+            .setPkg("test-package2")
+            .build()))
+    }
+
+    @Test
+    fun testIncludeInSectionCurrentHUN() {
+        // GIVEN the current HUN is set to mEntry
+        addHUN(mEntry)
+
+        // THEN only section the current HUN, mEntry
+        assertTrue(mNotifSectioner.isInSection(mEntry))
+        assertFalse(mNotifSectioner.isInSection(NotificationEntryBuilder()
+            .setPkg("test-package")
+            .build()))
+    }
+
+    @Test
+    fun testLifetimeExtendsCurrentHUN() {
+        // GIVEN there is a HUN, mEntry
+        addHUN(mEntry)
+
+        // THEN only the current HUN, mEntry, should be lifetimeExtended
+        assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, /* cancellationReason */ 0))
+        assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(
+            NotificationEntryBuilder()
+                .setPkg("test-package")
+                .build(), /* cancellationReason */ 0))
+    }
+
+    @Test
+    fun testShowHUNOnInflationFinished() {
+        // WHEN a notification should HUN and its inflation is finished
+        whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+
+        mCollectionListener.onEntryAdded(mEntry)
+        withArgCaptor<BindCallback> {
+            verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), capture())
+        }.onBindFinished(mEntry)
+
+        // THEN we tell the HeadsUpManager to show the notification
+        verify(mHeadsUpManager).showNotification(mEntry)
+    }
+
+    @Test
+    fun testNoHUNOnInflationFinished() {
+        // WHEN a notification shouldn't HUN and its inflation is finished
+        whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false)
+        mCollectionListener.onEntryAdded(mEntry)
+
+        // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
+        verify(mHeadsUpManager, never()).showNotification(mEntry)
+    }
+
+    @Test
+    fun testOnEntryRemovedRemovesHeadsUpNotification() {
+        // GIVEN the current HUN is mEntry
+        addHUN(mEntry)
+
+        // WHEN mEntry is removed from the notification collection
+        mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0)
+        whenever(mRemoteInputManager.isSpinning(any())).thenReturn(false)
+
+        // THEN heads up manager should remove the entry
+        verify(mHeadsUpManager).removeNotification(mEntry.key, false)
+    }
+
+    private fun addHUN(entry: NotificationEntry) {
+        mHuns.add(entry)
+        whenever(mHeadsUpManager.topEntry).thenReturn(entry)
+        mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 917c049..d094749 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -41,6 +41,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -70,6 +71,7 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private HighPriorityProvider mHighPriorityProvider;
+    @Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
     @Mock private NotifPipeline mNotifPipeline;
 
     private NotificationEntry mEntry;
@@ -81,7 +83,7 @@
         KeyguardCoordinator keyguardCoordinator = new KeyguardCoordinator(
                 mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
                 mBroadcastDispatcher, mStatusBarStateController,
-                mKeyguardUpdateMonitor, mHighPriorityProvider);
+                mKeyguardUpdateMonitor, mHighPriorityProvider, mSectionHeaderVisibilityProvider);
 
         mEntry = new NotificationEntryBuilder()
                 .setUser(new UserHandle(NOTIF_USER_ID))
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 f70330d..3b034f7 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
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import static java.util.Objects.requireNonNull;
@@ -47,6 +48,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
@@ -92,6 +94,7 @@
     @Mock private NotifSection mNotifSection;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private IStatusBarService mService;
+    @Mock private BindEventManagerImpl mBindEventManagerImpl;
     @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
     private final SectionClassifier mSectionClassifier = new SectionClassifier();
     private final NotifUiAdjustmentProvider mAdjustmentProvider =
@@ -119,6 +122,7 @@
                 mock(NotifViewBarn.class),
                 mAdjustmentProvider,
                 mService,
+                mBindEventManagerImpl,
                 TEST_CHILD_BIND_CUTOFF,
                 TEST_MAX_GROUP_DELAY);
 
@@ -405,6 +409,14 @@
     }
 
     @Test
+    public void testCallConversationManagerBindWhenInflated() {
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+        mNotifInflater.getInflateCallback(mEntry).onInflationFinished(mEntry, null);
+        verify(mBindEventManagerImpl, times(1)).notifyViewBound(eq(mEntry));
+        verifyNoMoreInteractions(mBindEventManagerImpl);
+    }
+
+    @Test
     public void testPartiallyInflatedGroupsAreReleasedAfterTimeout() {
         // GIVEN a newly-posted group with a summary and two children
         final GroupEntry group = new GroupEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
index 0ce6ada..7073cc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
@@ -101,27 +101,27 @@
     @Test
     fun testRemoteInputActive() {
         `when`(remoteInputManager.isRemoteInputActive(entry1)).thenReturn(true)
-        assertThat(remoteInputActiveExtender.shouldExtendLifetime(entry1, 0)).isTrue()
-        assertThat(remoteInputHistoryExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(smartReplyHistoryExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(remoteInputActiveExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+        assertThat(remoteInputHistoryExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(smartReplyHistoryExtender.maybeExtendLifetime(entry1, 0)).isFalse()
         assertThat(listener.isNotificationKeptForRemoteInputHistory(entry1.key)).isFalse()
     }
 
     @Test
     fun testRemoteInputHistory() {
         `when`(remoteInputManager.shouldKeepForRemoteInputHistory(entry1)).thenReturn(true)
-        assertThat(remoteInputActiveExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(remoteInputHistoryExtender.shouldExtendLifetime(entry1, 0)).isTrue()
-        assertThat(smartReplyHistoryExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(remoteInputActiveExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(remoteInputHistoryExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+        assertThat(smartReplyHistoryExtender.maybeExtendLifetime(entry1, 0)).isFalse()
         assertThat(listener.isNotificationKeptForRemoteInputHistory(entry1.key)).isTrue()
     }
 
     @Test
     fun testSmartReplyHistory() {
         `when`(remoteInputManager.shouldKeepForSmartReplyHistory(entry1)).thenReturn(true)
-        assertThat(remoteInputActiveExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(remoteInputHistoryExtender.shouldExtendLifetime(entry1, 0)).isFalse()
-        assertThat(smartReplyHistoryExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(remoteInputActiveExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(remoteInputHistoryExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+        assertThat(smartReplyHistoryExtender.maybeExtendLifetime(entry1, 0)).isTrue()
         assertThat(listener.isNotificationKeptForRemoteInputHistory(entry1.key)).isTrue()
     }
 
@@ -136,7 +136,7 @@
         verify(lifetimeExtensionCallback, never()).onEndLifetimeExtension(any(), any())
 
         // Start extending lifetime & validate that the extension is ended
-        assertThat(remoteInputActiveExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(remoteInputActiveExtender.maybeExtendLifetime(entry1, 0)).isTrue()
         assertThat(remoteInputActiveExtender.isExtending(entry1.key)).isTrue()
         listener.onPanelCollapsed()
         verify(lifetimeExtensionCallback).onEndLifetimeExtension(remoteInputActiveExtender, entry1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt
new file mode 100644
index 0000000..c17fe6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.legacy
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.GroupEventDispatcher
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener
+import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class GroupEventDispatcherTest : SysuiTestCase() {
+    val groupMap = mutableMapOf<String, NotificationGroup>()
+    val groupTestHelper = NotificationGroupTestHelper(mContext)
+
+    private val dispatcher = GroupEventDispatcher(groupMap::get)
+    private val listener: OnGroupChangeListener = mock()
+
+    @Before
+    fun setup() {
+        dispatcher.registerGroupChangeListener(listener)
+    }
+
+    @Test
+    fun testOnGroupsChangedUnbuffered() {
+        dispatcher.notifyGroupsChanged()
+        verify(listener).onGroupsChanged()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testOnGroupsChangedBuffered() {
+        dispatcher.openBufferScope()
+        dispatcher.notifyGroupsChanged()
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verify(listener).onGroupsChanged()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testOnGroupsChangedDoubleBuffered() {
+        dispatcher.openBufferScope()
+        dispatcher.notifyGroupsChanged()
+        dispatcher.openBufferScope() // open a nested buffer scope
+        dispatcher.notifyGroupsChanged()
+        dispatcher.closeBufferScope() // should NOT flush events
+        dispatcher.notifyGroupsChanged()
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope() // this SHOULD flush events
+        verify(listener).onGroupsChanged()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testOnGroupsChangedBufferCoalesces() {
+        dispatcher.openBufferScope()
+        dispatcher.notifyGroupsChanged()
+        dispatcher.notifyGroupsChanged()
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verify(listener).onGroupsChanged()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testOnGroupCreatedIsNeverBuffered() {
+        val group = addGroup(1)
+
+        dispatcher.openBufferScope()
+        dispatcher.notifyGroupCreated(group)
+        verify(listener).onGroupCreated(group, group.groupKey)
+        verifyNoMoreInteractions(listener)
+
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testOnGroupRemovedIsNeverBuffered() {
+        val group = addGroup(1)
+
+        dispatcher.openBufferScope()
+        dispatcher.notifyGroupRemoved(group)
+        verify(listener).onGroupRemoved(group, group.groupKey)
+        verifyNoMoreInteractions(listener)
+
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideAddedUnbuffered() {
+        val group = addGroup(1)
+        val newAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = newAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, null)
+        verify(listener).onGroupAlertOverrideChanged(group, null, newAlertEntry)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideRemovedUnbuffered() {
+        val group = addGroup(1)
+        val oldAlertEntry = groupTestHelper.createChildNotification()
+        dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+        verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, null)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideChangedUnbuffered() {
+        val group = addGroup(1)
+        val oldAlertEntry = groupTestHelper.createChildNotification()
+        val newAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = newAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+        verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideChangedBuffered() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        val oldAlertEntry = groupTestHelper.createChildNotification()
+        val newAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = newAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideIgnoredIfRemoved() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        val oldAlertEntry = groupTestHelper.createChildNotification()
+        val newAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = newAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+        verifyNoMoreInteractions(listener)
+        groupMap.clear()
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideMultipleChangesBuffered() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        val oldAlertEntry = groupTestHelper.createChildNotification()
+        val newAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = null
+        dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+        group.alertOverride = newAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, null)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideTemporaryValueSwallowed() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        val stableAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = null
+        dispatcher.notifyAlertOverrideChanged(group, stableAlertEntry)
+        group.alertOverride = stableAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, null)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testAlertOverrideTemporaryNullSwallowed() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        val temporaryAlertEntry = groupTestHelper.createChildNotification()
+        group.alertOverride = temporaryAlertEntry
+        dispatcher.notifyAlertOverrideChanged(group, null)
+        group.alertOverride = null
+        dispatcher.notifyAlertOverrideChanged(group, temporaryAlertEntry)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testSuppressOnUnbuffered() {
+        val group = addGroup(1)
+        group.suppressed = true
+        dispatcher.notifySuppressedChanged(group)
+        verify(listener).onGroupSuppressionChanged(group, true)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testSuppressOffUnbuffered() {
+        val group = addGroup(1)
+        group.suppressed = false
+        dispatcher.notifySuppressedChanged(group)
+        verify(listener).onGroupSuppressionChanged(group, false)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testSuppressOnBuffered() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        group.suppressed = false
+        dispatcher.notifySuppressedChanged(group)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verify(listener).onGroupSuppressionChanged(group, false)
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testSuppressOnIgnoredIfRemoved() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        group.suppressed = false
+        dispatcher.notifySuppressedChanged(group)
+        verifyNoMoreInteractions(listener)
+        groupMap.clear()
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testSuppressOnOffBuffered() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        group.suppressed = true
+        dispatcher.notifySuppressedChanged(group)
+        group.suppressed = false
+        dispatcher.notifySuppressedChanged(group)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
+    fun testSuppressOnOffOnBuffered() {
+        dispatcher.openBufferScope()
+        val group = addGroup(1)
+        group.suppressed = true
+        dispatcher.notifySuppressedChanged(group)
+        group.suppressed = false
+        dispatcher.notifySuppressedChanged(group)
+        group.suppressed = true
+        dispatcher.notifySuppressedChanged(group)
+        verifyNoMoreInteractions(listener)
+        dispatcher.closeBufferScope()
+        verify(listener).onGroupSuppressionChanged(group, true)
+        verifyNoMoreInteractions(listener)
+    }
+
+    private fun addGroup(id: Int): NotificationGroup {
+        val group = NotificationGroup("group:$id")
+        groupMap[group.groupKey] = group
+        return group
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
index 37ad835..a09f3a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
@@ -77,7 +77,7 @@
     @Test
     fun testNoExtend() {
         `when`(shouldExtend.test(entry1)).thenReturn(false)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isFalse()
         assertThat(extender.isExtending(entry1.key)).isFalse()
         verify(onStarted, never()).accept(entry1)
         verify(onCanceled, never()).accept(entry1)
@@ -86,7 +86,7 @@
     @Test
     fun testExtendThenCancelForRepost() {
         `when`(shouldExtend.test(entry1)).thenReturn(true)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted).accept(entry1)
         verify(onCanceled, never()).accept(entry1)
         assertThat(extender.isExtending(entry1.key)).isTrue()
@@ -108,7 +108,7 @@
     @Test
     fun testExtendThenEnd() {
         `when`(shouldExtend.test(entry1)).thenReturn(true)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted).accept(entry1)
         assertThat(extender.isExtending(entry1.key)).isTrue()
         extender.endLifetimeExtension(entry1.key)
@@ -119,7 +119,7 @@
     @Test
     fun testExtendThenEndAfterDelay() {
         `when`(shouldExtend.test(entry1)).thenReturn(true)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted).accept(entry1)
         assertThat(extender.isExtending(entry1.key)).isTrue()
 
@@ -142,11 +142,11 @@
     fun testExtendThenEndAll() {
         `when`(shouldExtend.test(entry1)).thenReturn(true)
         `when`(shouldExtend.test(entry2)).thenReturn(true)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted).accept(entry1)
         assertThat(extender.isExtending(entry1.key)).isTrue()
         assertThat(extender.isExtending(entry2.key)).isFalse()
-        assertThat(extender.shouldExtendLifetime(entry2, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry2, 0)).isTrue()
         verify(onStarted).accept(entry2)
         assertThat(extender.isExtending(entry1.key)).isTrue()
         assertThat(extender.isExtending(entry2.key)).isTrue()
@@ -160,11 +160,11 @@
     @Test
     fun testExtendWithinEndCanReExtend() {
         `when`(shouldExtend.test(entry1)).thenReturn(true)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted, times(1)).accept(entry1)
 
         `when`(callback.onEndLifetimeExtension(extender, entry1)).thenAnswer {
-            assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+            assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         }
         extender.endLifetimeExtension(entry1.key)
         verify(onStarted, times(2)).accept(entry1)
@@ -174,11 +174,11 @@
     @Test
     fun testExtendWithinEndCanNotReExtend() {
         `when`(shouldExtend.test(entry1)).thenReturn(true, false)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted, times(1)).accept(entry1)
 
         `when`(callback.onEndLifetimeExtension(extender, entry1)).thenAnswer {
-            assertThat(extender.shouldExtendLifetime(entry1, 0)).isFalse()
+            assertThat(extender.maybeExtendLifetime(entry1, 0)).isFalse()
         }
         extender.endLifetimeExtension(entry1.key)
         verify(onStarted, times(1)).accept(entry1)
@@ -188,11 +188,11 @@
     @Test
     fun testExtendWithinEndAllCanReExtend() {
         `when`(shouldExtend.test(entry1)).thenReturn(true)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted, times(1)).accept(entry1)
 
         `when`(callback.onEndLifetimeExtension(extender, entry1)).thenAnswer {
-            assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+            assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         }
         extender.endAllLifetimeExtensions()
         verify(onStarted, times(2)).accept(entry1)
@@ -202,11 +202,11 @@
     @Test
     fun testExtendWithinEndAllCanNotReExtend() {
         `when`(shouldExtend.test(entry1)).thenReturn(true, false)
-        assertThat(extender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(extender.maybeExtendLifetime(entry1, 0)).isTrue()
         verify(onStarted, times(1)).accept(entry1)
 
         `when`(callback.onEndLifetimeExtension(extender, entry1)).thenAnswer {
-            assertThat(extender.shouldExtendLifetime(entry1, 0)).isFalse()
+            assertThat(extender.maybeExtendLifetime(entry1, 0)).isFalse()
         }
         extender.endAllLifetimeExtensions()
         verify(onStarted, times(1)).accept(entry1)
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 5271745..4e309d4 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,6 +18,8 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -31,18 +33,19 @@
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.notification.stack.PriorityBucket
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
-import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 class NodeSpecBuilderTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var viewBarn: NotifViewBarn
+    private val mediaContainerController: MediaContainerController = mock()
+    private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
+    private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
+    private val viewBarn: NotifViewBarn = mock()
 
     private var rootController: NodeController = buildFakeController("rootController")
     private var headerController0: NodeController = buildFakeController("header0")
@@ -66,17 +69,18 @@
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        `when`(viewBarn.requireNodeController(any())).thenAnswer {
+        whenever(mediaContainerController.mediaContainerView).thenReturn(mock())
+        whenever(viewBarn.requireNodeController(any())).thenAnswer {
             fakeViewBarn.getViewByEntry(it.getArgument(0))
         }
 
-        specBuilder = NodeSpecBuilder(viewBarn)
+        specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager,
+                sectionHeaderVisibilityProvider, viewBarn)
     }
 
     @Test
     fun testMultipleSectionsWithSameController() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 listOf(
                         notif(0, section0),
@@ -95,6 +99,7 @@
 
     @Test(expected = RuntimeException::class)
     fun testMultipleSectionsWithSameControllerNonConsecutive() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 listOf(
                         notif(0, section0),
@@ -108,6 +113,32 @@
 
     @Test
     fun testSimpleMapping() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
+        checkOutput(
+            // GIVEN a simple flat list of notifications all in the same headerless section
+            listOf(
+                notif(0, section0NoHeader),
+                notif(1, section0NoHeader),
+                notif(2, section0NoHeader),
+                notif(3, section0NoHeader)
+            ),
+
+            // THEN we output a similarly simple flag list of nodes
+            tree(
+                notifNode(0),
+                notifNode(1),
+                notifNode(2),
+                notifNode(3)
+            )
+        )
+    }
+
+    @Test
+    fun testSimpleMappingWithMedia() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
+        // WHEN media controls are enabled
+        whenever(sectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true)
+
         checkOutput(
                 // GIVEN a simple flat list of notifications all in the same headerless section
                 listOf(
@@ -117,8 +148,9 @@
                         notif(3, section0NoHeader)
                 ),
 
-                // THEN we output a similarly simple flag list of nodes
+                // THEN we output a similarly simple flag list of nodes, with media at the top
                 tree(
+                        node(mediaContainerController),
                         notifNode(0),
                         notifNode(1),
                         notifNode(2),
@@ -129,6 +161,8 @@
 
     @Test
     fun testHeaderInjection() {
+        // WHEN section headers are supposed to be visible
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a flat list of notifications, spread across three sections
                 listOf(
@@ -152,7 +186,31 @@
     }
 
     @Test
+    fun testHeaderSuppression() {
+        // WHEN section headers are supposed to be hidden
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(false)
+        checkOutput(
+                // GIVEN a flat list of notifications, spread across three sections
+                listOf(
+                        notif(0, section0),
+                        notif(1, section0),
+                        notif(2, section1),
+                        notif(3, section2)
+                ),
+
+                // THEN each section has its header injected
+                tree(
+                        notifNode(0),
+                        notifNode(1),
+                        notifNode(2),
+                        notifNode(3)
+                )
+        )
+    }
+
+    @Test
     fun testGroups() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a mixed list of top-level notifications and groups
                 listOf(
@@ -193,6 +251,7 @@
 
     @Test
     fun testSecondSectionWithNoHeader() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a middle section with no associated header view
                 listOf(
@@ -222,6 +281,7 @@
 
     @Test(expected = RuntimeException::class)
     fun testRepeatedSectionsThrow() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a malformed list where sections are not contiguous
                 listOf(
@@ -333,7 +393,7 @@
 
 private fun buildFakeController(name: String): NodeController {
     val controller = Mockito.mock(NodeController::class.java)
-    `when`(controller.nodeLabel).thenReturn(name)
+    whenever(controller.nodeLabel).thenReturn(name)
     return controller
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
index bbe92f6..15ff555 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
@@ -274,6 +274,18 @@
         public void removeChild(@NonNull NodeController child, boolean isTransfer) {
             view.removeView(child.getView());
         }
+
+        @Override
+        public void onViewAdded() {
+        }
+
+        @Override
+        public void onViewMoved() {
+        }
+
+        @Override
+        public void onViewRemoved() {
+        }
     }
 
     private static class SpecBuilder {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e9e1911..4ea9321 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -16,9 +16,12 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 
 import static org.junit.Assert.assertEquals;
@@ -29,6 +32,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 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;
@@ -330,4 +334,28 @@
 
         assertTrue(row.getIsNonblockable());
     }
+
+    @Test
+    public void testCanDismissNoClear() throws Exception {
+        ExpandableNotificationRow row =
+                mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
+        modifySbn(row.getEntry())
+                .setFlag(mContext, FLAG_NO_CLEAR, true)
+                .build();
+        row.performDismiss(false);
+        verify(mNotificationTestHelper.mOnUserInteractionCallback)
+                .onDismiss(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testCannotDismissOngoing() throws Exception {
+        ExpandableNotificationRow row =
+                mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
+        modifySbn(row.getEntry())
+                .setFlag(mContext, FLAG_ONGOING_EVENT, true)
+                .build();
+        row.performDismiss(false);
+        verify(mNotificationTestHelper.mOnUserInteractionCallback, never())
+                .onDismiss(any(), anyInt(), any());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index 9039e1b..1f92b0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -57,7 +57,7 @@
 
     @Test
     public void setDismissOnClick() {
-        mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
+        mView.setClearAllButtonClickListener(mock(View.OnClickListener.class));
         assertTrue(mView.findSecondaryView().hasOnClickListeners());
     }
 
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 e4721b1..4457ae0 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
@@ -115,6 +115,7 @@
     private final IconManager mIconManager;
     private StatusBarStateController mStatusBarStateController;
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+    public final OnUserInteractionCallback mOnUserInteractionCallback;
 
     public NotificationTestHelper(
             Context context,
@@ -173,6 +174,7 @@
         verify(collection).addCollectionListener(collectionListenerCaptor.capture());
         mBindPipelineEntryListener = collectionListenerCaptor.getValue();
         mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class);
+        mOnUserInteractionCallback = mock(OnUserInteractionCallback.class);
     }
 
     /**
@@ -499,7 +501,7 @@
                 new FalsingCollectorFake(),
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier,
-                mock(OnUserInteractionCallback.class),
+                mOnUserInteractionCallback,
                 Optional.of(mock(BubblesManager.class)),
                 mock(NotificationGutsManager.class));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 276f246..ac9fcc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -40,23 +41,20 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.ActivityStarterDelegate;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.MediaContainerController;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
@@ -66,6 +64,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -88,6 +87,7 @@
     @Mock private NotificationRowComponent mNotificationRowComponent;
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
     @Mock private NotificationSectionsLogger mLogger;
+    @Mock private MediaContainerController mMediaContainerController;
     @Mock private SectionHeaderController mIncomingHeaderController;
     @Mock private SectionHeaderController mPeopleHeaderController;
     @Mock private SectionHeaderController mAlertingHeaderController;
@@ -115,6 +115,8 @@
                 });
         when(mNotificationRowComponent.getActivatableNotificationViewController())
                 .thenReturn(mActivatableNotificationViewController);
+        when(mMediaContainerController.getMediaContainerView())
+                .thenReturn(mock(MediaContainerView.class));
         when(mIncomingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
         when(mPeopleHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
         when(mAlertingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
@@ -127,6 +129,7 @@
                         mSectionsFeatureManager,
                         mLogger,
                         mNotifPipelineFlags,
+                        mMediaContainerController,
                         mIncomingHeaderController,
                         mPeopleHeaderController,
                         mAlertingHeaderController,
@@ -135,7 +138,7 @@
         // Required in order for the header inflation to work properly
         when(mNssl.generateLayoutParams(any(AttributeSet.class)))
                 .thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
+        mSectionsManager.initialize(mNssl);
         when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
@@ -143,7 +146,7 @@
 
     @Test(expected =  IllegalStateException.class)
     public void testDuplicateInitializeThrows() {
-        mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
+        mSectionsManager.initialize(mNssl);
     }
 
     @Test
@@ -266,8 +269,9 @@
 
         // THEN the header is first removed from the transient parent before being added to the
         // NSSL.
-        verify(transientParent).removeTransientView(silentHeaderView);
-        verify(mNssl).addView(silentHeaderView, 1);
+        final InOrder inOrder = inOrder(silentHeaderView, mNssl);
+        inOrder.verify(silentHeaderView).removeFromTransientContainer();
+        inOrder.verify(mNssl).addView(eq(silentHeaderView), eq(1));
     }
 
     @Test
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 4cc1be6..04c6f6c 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
@@ -379,18 +379,18 @@
 
     @Test
     public void testDismissListener() {
-        ArgumentCaptor<NotificationStackScrollLayout.DismissListener>
+        ArgumentCaptor<NotificationStackScrollLayout.ClearAllListener>
                 dismissListenerArgumentCaptor = ArgumentCaptor.forClass(
-                NotificationStackScrollLayout.DismissListener.class);
+                NotificationStackScrollLayout.ClearAllListener.class);
 
         mController.attach(mNotificationStackScrollLayout);
 
-        verify(mNotificationStackScrollLayout).setDismissListener(
+        verify(mNotificationStackScrollLayout).setClearAllListener(
                 dismissListenerArgumentCaptor.capture());
-        NotificationStackScrollLayout.DismissListener dismissListener =
+        NotificationStackScrollLayout.ClearAllListener dismissListener =
                 dismissListenerArgumentCaptor.getValue();
 
-        dismissListener.onDismiss(ROWS_ALL);
+        dismissListener.onClearAll(ROWS_ALL);
         verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL));
     }
 
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 eda0e83..46ba097 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
@@ -447,7 +447,7 @@
     public void testClearNotifications_All() {
         final int[] numCalls = {0};
         final int[] selected = {-1};
-        mStackScroller.setDismissListener(selectedRows -> {
+        mStackScroller.setClearAllListener(selectedRows -> {
             numCalls[0]++;
             selected[0] = selectedRows;
         });
@@ -461,7 +461,7 @@
     public void testClearNotifications_Gentle() {
         final int[] numCalls = {0};
         final int[] selected = {-1};
-        mStackScroller.setDismissListener(selectedRows -> {
+        mStackScroller.setClearAllListener(selectedRows -> {
             numCalls[0]++;
             selected[0] = selectedRows;
         });
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
new file mode 100644
index 0000000..649dc23
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.FoldStateListener.OnFoldStateChangeListener
+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.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateListenerTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var listener: OnFoldStateChangeListener
+    private lateinit var sut: FoldStateListener
+
+    @Before
+    fun setUp() {
+        initMocks(this)
+        setFoldedStates(DEVICE_STATE_FOLDED)
+        setGoToSleepStates(DEVICE_STATE_FOLDED)
+        sut = FoldStateListener(mContext, listener)
+    }
+
+    @Test
+    fun onStateChanged_stateFolded_notifiesWithFoldedAndGoingToSleep() {
+        sut.onStateChanged(DEVICE_STATE_FOLDED)
+
+        verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+    }
+
+    @Test
+    fun onStateChanged_stateHalfFolded_notifiesWithNotFoldedAndNotGoingToSleep() {
+        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+        verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+    }
+
+    @Test
+    fun onStateChanged_stateUnfolded_notifiesWithNotFoldedAndNotGoingToSleep() {
+        sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+
+        verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+    }
+
+    @Test
+    fun onStateChanged_stateUnfoldedThenHalfFolded_notifiesOnce() {
+        sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+        verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+    }
+
+    @Test
+    fun onStateChanged_stateHalfFoldedThenUnfolded_notifiesOnce() {
+        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+
+        verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+    }
+
+    @Test
+    fun onStateChanged_stateHalfFoldedThenFolded_notifiesTwice() {
+        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+        sut.onStateChanged(DEVICE_STATE_FOLDED)
+
+        val inOrder = Mockito.inOrder(listener)
+        inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+        inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+    }
+
+    @Test
+    fun onStateChanged_stateFoldedThenHalfFolded_notifiesTwice() {
+        sut.onStateChanged(DEVICE_STATE_FOLDED)
+        sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+        val inOrder = Mockito.inOrder(listener)
+        inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+        inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+    }
+
+    private fun setGoToSleepStates(vararg states: Int) {
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_deviceStatesOnWhichToSleep,
+            states
+        )
+    }
+
+    private fun setFoldedStates(vararg states: Int) {
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_foldedDeviceStates,
+            states
+        )
+    }
+
+    companion object {
+        private const val DEVICE_STATE_FOLDED = 123
+        private const val DEVICE_STATE_HALF_FOLDED = 456
+        private const val DEVICE_STATE_UNFOLDED = 789
+
+        private const val FOLDED = true
+        private const val NOT_FOLDED = false
+
+        private const val WILL_GO_TO_SLEEP = true
+        private const val WILL_NOT_SLEEP = false
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 37cf748..36a4c1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -354,7 +354,6 @@
         TestNotificationPanelViewStateProvider() {}
 
         private float mPanelViewExpandedHeight = 100f;
-        private float mQsExpansionFraction = 0f;
         private boolean mShouldHeadsUpBeVisible = false;
         private float mLockscreenShadeDragProgress = 0f;
 
@@ -364,11 +363,6 @@
         }
 
         @Override
-        public float getQsExpansionFraction() {
-            return mQsExpansionFraction;
-        }
-
-        @Override
         public boolean shouldHeadsUpBeVisible() {
             return mShouldHeadsUpBeVisible;
         }
@@ -382,10 +376,6 @@
             this.mPanelViewExpandedHeight = panelViewExpandedHeight;
         }
 
-        public void setQsExpansionFraction(float qsExpansionFraction) {
-            this.mQsExpansionFraction = qsExpansionFraction;
-        }
-
         public void setShouldHeadsUpBeVisible(boolean shouldHeadsUpBeVisible) {
             this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 9898b4b..c13b335 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -312,10 +312,11 @@
 
     @Test
     public void testUpdateChildToSummaryDoesNotTransfer() {
+        final String tag = "fooTag";
         NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationEntry childEntry =
-                mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47);
+                mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47, tag);
         mockHasHeadsUpContentView(childEntry, false);
 
         mHeadsUpManager.showNotification(summaryEntry);
@@ -327,7 +328,7 @@
         StatusBarNotification oldNotification = childEntry.getSbn();
         childEntry.setSbn(
                 mGroupTestHelper.createSummaryNotification(
-                        Notification.GROUP_ALERT_SUMMARY, 47).getSbn());
+                        Notification.GROUP_ALERT_SUMMARY, 47, tag).getSbn());
         mGroupManager.onEntryUpdated(childEntry, oldNotification);
 
         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 1be27da..5d7b154 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -21,14 +21,19 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.Log;
 
 import androidx.test.filters.SmallTest;
 
@@ -64,8 +69,10 @@
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
 
-    @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    @Mock HeadsUpManager mHeadsUpManager;
+    @Mock
+    PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+    @Mock
+    HeadsUpManager mHeadsUpManager;
 
     @Before
     public void setup() {
@@ -177,21 +184,81 @@
         helpTestAlertOverrideWithSiblings(2);
     }
 
+    @Test
+    public void testAlertOverrideWithSiblings_3() {
+        helpTestAlertOverrideWithSiblings(3);
+    }
+
+    @Test
+    public void testAlertOverrideWithSiblings_9() {
+        helpTestAlertOverrideWithSiblings(9);
+    }
+
+    /**
+     * Helper for testing various sibling counts
+     */
+    private void helpTestAlertOverrideWithSiblings(int numSiblings) {
+        helpTestAlertOverride(
+                /* numSiblings */ numSiblings,
+                /* summaryGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* expectAlertOverride */ true);
+    }
+
+    @Test
+    public void testAlertOverrideWithParentAlertAll() {
+        // tests that summary can have GROUP_ALERT_ALL and this still works
+        helpTestAlertOverride(
+                /* numSiblings */ 1,
+                /* summaryGroupAlert */ Notification.GROUP_ALERT_ALL,
+                /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* expectAlertOverride */ true);
+    }
+
+    @Test
+    public void testAlertOverrideWithParentAlertChild() {
+        // Tests that if the summary alerts CHILDREN, there's no alertOverride
+        helpTestAlertOverride(
+                /* numSiblings */ 1,
+                /* summaryGroupAlert */ Notification.GROUP_ALERT_CHILDREN,
+                /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* expectAlertOverride */ false);
+    }
+
+    @Test
+    public void testAlertOverrideWithChildrenAlertAll() {
+        // Tests that if the children alert ALL, there's no alertOverride
+        helpTestAlertOverride(
+                /* numSiblings */ 1,
+                /* summaryGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* priorityGroupAlert */ Notification.GROUP_ALERT_ALL,
+                /* siblingGroupAlert */ Notification.GROUP_ALERT_ALL,
+                /* expectAlertOverride */ false);
+    }
+
     /**
      * This tests, for a group with a priority entry and the given number of siblings, that:
      * 1) the priority entry is identified as the alertOverride for the group
      * 2) the onAlertOverrideChanged method is called at that time
      * 3) when the priority entry is removed, these are reversed
      */
-    private void helpTestAlertOverrideWithSiblings(int numSiblings) {
-        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+    private void helpTestAlertOverride(int numSiblings,
+            @Notification.GroupAlertBehavior int summaryGroupAlert,
+            @Notification.GroupAlertBehavior int priorityGroupAlert,
+            @Notification.GroupAlertBehavior int siblingGroupAlert,
+            boolean expectAlertOverride) {
         // Create entries in an order so that the priority entry can be deemed the newest child.
         NotificationEntry[] siblings = new NotificationEntry[numSiblings];
         for (int i = 0; i < numSiblings; i++) {
-            siblings[i] = mGroupTestHelper.createChildNotification(groupAlert);
+            siblings[i] = mGroupTestHelper.createChildNotification(siblingGroupAlert, i, "sibling");
         }
-        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
-        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry priorityEntry =
+                mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority");
+        NotificationEntry summaryEntry =
+                mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary");
 
         // The priority entry is an important conversation.
         when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
@@ -208,10 +275,27 @@
         }
         mGroupManager.onEntryAdded(priorityEntry);
 
+        if (!expectAlertOverride) {
+            // Test expectation is that there will NOT be an alert, so verify that!
+            NotificationGroup summaryGroup =
+                    mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+            assertNull(summaryGroup.alertOverride);
+            return;
+        }
+        int max2Siblings = Math.min(2, numSiblings);
+
         // Verify that the summary group has the priority child as its alertOverride
         NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
         assertEquals(priorityEntry, summaryGroup.alertOverride);
         verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+        verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, true);
+        if (numSiblings > 1) {
+            verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
+        }
+        verify(groupChangeListener).onGroupCreated(any(), eq(priorityEntry.getKey()));
+        verify(groupChangeListener).onGroupCreated(any(), eq(summaryEntry.getSbn().getGroupKey()));
+        verify(groupChangeListener, times(max2Siblings + 1)).onGroupsChanged();
+        verifyNoMoreInteractions(groupChangeListener);
 
         // Verify that only the priority notification is isolated from the group
         assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
@@ -227,6 +311,92 @@
 
         // verify that the alertOverride is removed when the priority notification is
         assertNull(summaryGroup.alertOverride);
+        verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, priorityEntry, null);
+        verify(groupChangeListener).onGroupRemoved(any(), eq(priorityEntry.getKey()));
+        verify(groupChangeListener, times(max2Siblings + 2)).onGroupsChanged();
+        if (numSiblings == 0) {
+            verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
+        }
+        verifyNoMoreInteractions(groupChangeListener);
+    }
+
+    @Test
+    public void testAlertOverrideWhenUpdatingSummaryAtEnd() {
+        int numSiblings = 2;
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        // Create entries in an order so that the priority entry can be deemed the newest child.
+        NotificationEntry[] siblings = new NotificationEntry[numSiblings];
+        for (int i = 0; i < numSiblings; i++) {
+            siblings[i] = mGroupTestHelper.createChildNotification(groupAlert, i, "sibling");
+        }
+        NotificationEntry priorityEntry =
+                mGroupTestHelper.createChildNotification(groupAlert, 0, "priority");
+        NotificationEntry summaryEntry =
+                mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary");
+
+        // The priority entry is an important conversation.
+        when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+                .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+
+        // Register a listener so we can verify that the event is sent.
+        OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
+        mGroupManager.registerGroupChangeListener(groupChangeListener);
+
+        // Add all the entries.  The order here shouldn't matter.
+        mGroupManager.onEntryAdded(summaryEntry);
+        for (int i = 0; i < numSiblings; i++) {
+            mGroupManager.onEntryAdded(siblings[i]);
+        }
+        mGroupManager.onEntryAdded(priorityEntry);
+
+        int max2Siblings = Math.min(2, numSiblings);
+
+        // Verify that the summary group has the priority child as its alertOverride
+        NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+        assertEquals(priorityEntry, summaryGroup.alertOverride);
         verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+        verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, true);
+        if (numSiblings > 1) {
+            verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
+        }
+        verify(groupChangeListener).onGroupCreated(any(), eq(priorityEntry.getKey()));
+        verify(groupChangeListener).onGroupCreated(any(), eq(summaryEntry.getSbn().getGroupKey()));
+        verify(groupChangeListener, times(max2Siblings + 1)).onGroupsChanged();
+        verifyNoMoreInteractions(groupChangeListener);
+
+        // Verify that only the priority notification is isolated from the group
+        assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
+        // Verify that the siblings are NOT isolated from the group
+        for (int i = 0; i < numSiblings; i++) {
+            assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
+            assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
+        }
+
+        Log.d("NotificationGroupManagerLegacyTest",
+                "testAlertOverrideWhenUpdatingSummaryAtEnd: About to update summary");
+
+        StatusBarNotification oldSummarySbn = mGroupTestHelper.incrementPost(summaryEntry, 10000);
+        mGroupManager.onEntryUpdated(summaryEntry, oldSummarySbn);
+
+        verify(groupChangeListener, times(max2Siblings + 2)).onGroupsChanged();
+        verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, priorityEntry, null);
+        verifyNoMoreInteractions(groupChangeListener);
+
+        Log.d("NotificationGroupManagerLegacyTest",
+                "testAlertOverrideWhenUpdatingSummaryAtEnd: About to update priority child");
+
+        StatusBarNotification oldPrioritySbn = mGroupTestHelper.incrementPost(priorityEntry, 10000);
+        mGroupManager.onEntryUpdated(priorityEntry, oldPrioritySbn);
+
+        verify(groupChangeListener).onGroupRemoved(any(), eq(priorityEntry.getKey()));
+        verify(groupChangeListener, times(2)).onGroupCreated(any(), eq(priorityEntry.getKey()));
+        verify(groupChangeListener, times(2))
+                .onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+        verify(groupChangeListener, times(max2Siblings + 3)).onGroupsChanged();
+        verifyNoMoreInteractions(groupChangeListener);
+
+        Log.d("NotificationGroupManagerLegacyTest",
+                "testAlertOverrideWhenUpdatingSummaryAtEnd: Done");
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index d405fea..ac32b9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -23,8 +25,10 @@
 import android.app.Notification;
 import android.content.Context;
 import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -44,15 +48,15 @@
     }
 
     public NotificationEntry createSummaryNotification() {
-        return createSummaryNotification(Notification.GROUP_ALERT_ALL, mId++);
+        return createSummaryNotification(Notification.GROUP_ALERT_ALL, mId++, null);
     }
 
     public NotificationEntry createSummaryNotification(int groupAlertBehavior) {
-        return createSummaryNotification(groupAlertBehavior, mId++);
+        return createSummaryNotification(groupAlertBehavior, mId++, null);
     }
 
-    public NotificationEntry createSummaryNotification(int groupAlertBehavior, int id) {
-        return createEntry(id, true, groupAlertBehavior);
+    public NotificationEntry createSummaryNotification(int groupAlertBehavior, int id, String tag) {
+        return createEntry(id, tag, true, groupAlertBehavior);
     }
 
     public NotificationEntry createChildNotification() {
@@ -60,14 +64,15 @@
     }
 
     public NotificationEntry createChildNotification(int groupAlertBehavior) {
-        return createEntry(mId++, false, groupAlertBehavior);
+        return createEntry(mId++, null, false, groupAlertBehavior);
     }
 
-    public NotificationEntry createChildNotification(int groupAlertBehavior, int id) {
-        return createEntry(id, false, groupAlertBehavior);
+    public NotificationEntry createChildNotification(int groupAlertBehavior, int id, String tag) {
+        return createEntry(id, tag, false, groupAlertBehavior);
     }
 
-    public NotificationEntry createEntry(int id, boolean isSummary, int groupAlertBehavior) {
+    public NotificationEntry createEntry(int id, String tag, boolean isSummary,
+            int groupAlertBehavior) {
         Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("Title")
                 .setSmallIcon(R.drawable.ic_person)
@@ -80,6 +85,7 @@
                 .setOpPkg(TEST_PACKAGE_NAME)
                 .setId(id)
                 .setNotification(notif)
+                .setTag(tag)
                 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
                 .build();
 
@@ -88,4 +94,16 @@
         when(row.getEntry()).thenReturn(entry);
         return entry;
     }
+
+    public StatusBarNotification incrementPost(NotificationEntry entry, int increment) {
+        StatusBarNotification oldSbn = entry.getSbn();
+        final long oldPostTime = oldSbn.getPostTime();
+        final long newPostTime = oldPostTime + increment;
+        entry.setSbn(new SbnBuilder(oldSbn)
+                .setPostTime(newPostTime)
+                .build());
+        assertThat(oldSbn.getPostTime()).isEqualTo(oldPostTime);
+        assertThat(entry.getSbn().getPostTime()).isEqualTo(newPostTime);
+        return oldSbn;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 5ada6d4..35f671bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -455,7 +455,8 @@
                 mStatusBarStateController,
                 mFalsingManager,
                 mLockscreenShadeTransitionController,
-                new FalsingCollectorFake());
+                new FalsingCollectorFake(),
+                mDumpManager);
         when(mKeyguardStatusViewComponentFactory.build(any()))
                 .thenReturn(mKeyguardStatusViewComponent);
         when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index f21fca2..10f4435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -647,8 +647,15 @@
         mScrimController.setRawPanelExpansionFraction(0.3f);
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mNotificationsScrim, SEMI_TRANSPARENT,
+                mNotificationsScrim, TRANSPARENT,
                 mScrimBehind, SEMI_TRANSPARENT));
+
+        // Then, notification scrim should fade in
+        mScrimController.setRawPanelExpansionFraction(0.7f);
+        assertScrimAlpha(Map.of(
+                mScrimInFront, TRANSPARENT,
+                mNotificationsScrim, SEMI_TRANSPARENT,
+                mScrimBehind, OPAQUE));
     }
 
 
@@ -1132,6 +1139,7 @@
 
     @Test
     public void testScrimsVisible_whenShadeVisible() {
+        mScrimController.setClipsQsScrim(true);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(0.3f);
         // notifications scrim alpha change require calling setQsPosition
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 69f2bd7..bb8bad3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -44,6 +44,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.IntentFilter;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.fingerprint.FingerprintManager;
 import android.metrics.LogMaker;
@@ -162,6 +163,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -176,6 +178,9 @@
 @RunWithLooper(setAsMainLooper = true)
 public class StatusBarTest extends SysuiTestCase {
 
+    private static final int FOLD_STATE_FOLDED = 0;
+    private static final int FOLD_STATE_UNFOLDED = 1;
+
     private StatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
     private PowerManager mPowerManager;
@@ -281,6 +286,7 @@
     @Mock private NotifPipelineFlags mNotifPipelineFlags;
     @Mock private NotifLiveDataStore mNotifLiveDataStore;
     @Mock private InteractionJankMonitor mJankMonitor;
+    @Mock private DeviceStateManager mDeviceStateManager;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -466,7 +472,8 @@
                 Optional.of(mStartingSurface),
                 mActivityLaunchAnimator,
                 mNotifPipelineFlags,
-                mJankMonitor);
+                mJankMonitor,
+                mDeviceStateManager);
         when(mKeyguardViewMediator.registerStatusBar(
                 any(StatusBar.class),
                 any(NotificationPanelViewController.class),
@@ -949,6 +956,47 @@
         verify(mStatusBarKeyguardViewManager).updateResources();
     }
 
+    @Test
+    public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+        setFoldedStates(FOLD_STATE_FOLDED);
+        setGoToSleepStates(FOLD_STATE_FOLDED);
+        when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+        setDeviceState(FOLD_STATE_UNFOLDED);
+
+        verify(mStatusBarStateController).setLeaveOpenOnKeyguardHide(true);
+    }
+
+    @Test
+    public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
+        setFoldedStates(FOLD_STATE_FOLDED);
+        setGoToSleepStates(FOLD_STATE_FOLDED);
+        when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(false);
+
+        setDeviceState(FOLD_STATE_UNFOLDED);
+
+        verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+    }
+
+    private void setDeviceState(int state) {
+        ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
+                ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
+        verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture());
+        callbackCaptor.getValue().onStateChanged(state);
+    }
+
+    private void setGoToSleepStates(int... states) {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.array.config_deviceStatesOnWhichToSleep,
+                states);
+    }
+
+    private void setFoldedStates(int... states) {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.array.config_foldedDeviceStates,
+                states);
+    }
+
     public static class TestableNotificationInterruptStateProviderImpl extends
             NotificationInterruptStateProviderImpl {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index db7b2f2..6364d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -25,14 +29,15 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContentResolver;
 import android.testing.TestableResources;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.internal.view.RotationPolicy;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
 
@@ -47,63 +52,56 @@
 @SmallTest
 public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
 
-    private static final String[] DEFAULT_SETTINGS = new String[]{
-            "0:0",
-            "1:2"
-    };
+    private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"};
 
-    private final FakeSettings mFakeSettings = new FakeSettings();
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
-    @Mock DeviceStateManager mDeviceStateManager;
-    RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
-    DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+    @Mock
+    private DeviceStateManager mDeviceStateManager;
+    private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+    private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+    private DeviceStateRotationLockSettingsManager mSettingsManager;
+    private TestableContentResolver mContentResolver;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(/* testClass= */ this);
         TestableResources resources = mContext.getOrCreateTestableResources();
+        resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS);
 
         ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor =
-                ArgumentCaptor.forClass(
-                        DeviceStateManager.DeviceStateCallback.class);
+                ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
 
-        mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController(
-                mFakeSettings,
-                mFakeRotationPolicy,
-                mDeviceStateManager,
-                mFakeExecutor,
-                DEFAULT_SETTINGS
-        );
+        mContentResolver = mContext.getContentResolver();
+        mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext);
+        mDeviceStateRotationLockSettingController =
+                new DeviceStateRotationLockSettingController(
+                        mFakeRotationPolicy, mDeviceStateManager, mFakeExecutor, mSettingsManager);
 
         mDeviceStateRotationLockSettingController.setListening(true);
-        verify(mDeviceStateManager).registerCallback(any(),
-                deviceStateCallbackArgumentCaptor.capture());
+        verify(mDeviceStateManager)
+                .registerCallback(any(), deviceStateCallbackArgumentCaptor.capture());
         mDeviceStateCallback = deviceStateCallbackArgumentCaptor.getValue();
     }
 
     @Test
     public void whenSavedSettingsEmpty_defaultsLoadedAndSaved() {
-        mFakeSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, "",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith();
 
-        mDeviceStateRotationLockSettingController.initialize();
-
-        assertThat(mFakeSettings
-                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                        UserHandle.USER_CURRENT))
-                .isEqualTo("0:0:1:2");
+        assertThat(
+                        Settings.Secure.getStringForUser(
+                                mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                                UserHandle.USER_CURRENT))
+                .isEqualTo("0:1:1:2:2:0");
     }
 
     @Test
     public void whenNoSavedValueForDeviceState_assumeIgnored() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:2:1:2",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -116,83 +114,198 @@
 
     @Test
     public void whenDeviceStateSwitched_loadCorrectSetting() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:2:1:1",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED);
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(0);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+    }
 
+    @Test
+    public void whenDeviceStateSwitched_settingIsIgnored_loadsDefaultFallbackSetting() {
+        initializeSettingsWith();
+        mFakeRotationPolicy.setRotationLock(true);
+
+        // State 2 -> Ignored -> Fall back to state 1 which is unlocked
+        mDeviceStateCallback.onStateChanged(2);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+    }
+
+    @Test
+    public void whenDeviceStateSwitched_ignoredSetting_fallbackValueChanges_usesFallbackValue() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        mFakeRotationPolicy.setRotationLock(false);
+
+        // State 2 -> Ignored -> Fall back to state 1 which is locked
+        mDeviceStateCallback.onStateChanged(2);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
     }
 
     @Test
     public void whenUserChangesSetting_saveSettingForCurrentState() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:1:1:2",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mSettingsManager.onPersistedSettingsChanged();
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(0);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
 
-        mDeviceStateRotationLockSettingController
-                .onRotationLockStateChanged(/* rotationLocked= */false,
-                        /* affordanceVisible= */ true);
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ false, /* affordanceVisible= */ true);
 
-        assertThat(mFakeSettings
-                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+        assertThat(
+                        Settings.Secure.getStringForUser(
+                                mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                                UserHandle.USER_CURRENT))
+                .isEqualTo("0:2:1:2");
+    }
+
+    @Test
+    public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mFakeRotationPolicy.setRotationLock(true);
+
+        mDeviceStateCallback.onStateChanged(1);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateCallback.onStateChanged(0);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+    }
+
+    @Test
+    public void whenDeviceStateSwitchedToIgnoredState_noFallback_newSettingsSaveForPreviousState() {
+        initializeSettingsWith(
+                8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mFakeRotationPolicy.setRotationLock(true);
+
+        mDeviceStateCallback.onStateChanged(1);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateCallback.onStateChanged(8);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ true, /* affordanceVisible= */ true);
+
+        assertThat(
+                        Settings.Secure.getStringForUser(
+                                mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                                UserHandle.USER_CURRENT))
+                .isEqualTo("1:1:8:0");
+    }
+
+    @Test
+    public void whenSettingsChangedExternally_updateRotationPolicy() throws InterruptedException {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mFakeRotationPolicy.setRotationLock(false);
+        mDeviceStateCallback.onStateChanged(0);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        // Changing device state 0 to LOCKED
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+    }
+
+    @Test
+    public void onRotationLockStateChanged_newSettingIsPersisted() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mDeviceStateCallback.onStateChanged(0);
+
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ false,
+                /* affordanceVisible= */ true
+        );
+
+        assertThat(
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                         UserHandle.USER_CURRENT))
                 .isEqualTo("0:2:1:2");
     }
 
-
     @Test
-    public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:0:1:2",
-                UserHandle.USER_CURRENT);
-        mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
+    public void onRotationLockStateChanged_deviceStateIsIgnored_newSettingIsPersistedToFallback() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        mDeviceStateCallback.onStateChanged(2);
 
-        mDeviceStateCallback.onStateChanged(1);
-        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ true,
+                /* affordanceVisible= */ true
+        );
 
-        mDeviceStateCallback.onStateChanged(0);
-        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+        assertThat(
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:1:1:1:2:0");
     }
 
     @Test
-    public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:0:1:2",
-                UserHandle.USER_CURRENT);
-        mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
-
+    public void onRotationLockStateChange_stateIgnored_noFallback_settingIsPersistedToPrevious() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
         mDeviceStateCallback.onStateChanged(1);
-        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+        mDeviceStateCallback.onStateChanged(8);
 
-        mDeviceStateCallback.onStateChanged(0);
-        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ true,
+                /* affordanceVisible= */ true
+        );
 
-        mDeviceStateRotationLockSettingController
-                .onRotationLockStateChanged(/* rotationLocked= */true,
-                        /* affordanceVisible= */ true);
-
-        assertThat(mFakeSettings
-                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+        assertThat(
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                         UserHandle.USER_CURRENT))
-                .isEqualTo("0:0:1:1");
+                .isEqualTo("0:1:1:1:8:0");
+    }
+
+    private void initializeSettingsWith(int... values) {
+        if (values.length % 2 != 0) {
+            throw new IllegalArgumentException("Expecting key-value pairs");
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < values.length; ) {
+            if (i > 0) {
+                sb.append(":");
+            }
+            sb.append(values[i++]).append(":").append(values[i++]);
+        }
+
+        Settings.Secure.putStringForUser(
+                mContentResolver,
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                sb.toString(),
+                UserHandle.USER_CURRENT);
+
+        mSettingsManager.onPersistedSettingsChanged();
     }
 
     private static class FakeRotationPolicy implements RotationPolicyWrapper {
@@ -230,8 +343,8 @@
         }
 
         @Override
-        public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
-                int userHandle) {
+        public void registerRotationPolicyListener(
+                RotationPolicy.RotationPolicyListener listener, int userHandle) {
             throw new AssertionError("Not implemented");
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 087f2e6..2126dda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -22,6 +22,7 @@
 
 import android.app.AppOpsManager;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.location.LocationManager;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -68,6 +69,8 @@
         MockitoAnnotations.initMocks(this);
         when(mUserTracker.getUserId()).thenReturn(UserHandle.USER_SYSTEM);
         when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+        when(mUserTracker.getUserProfiles())
+                .thenReturn(ImmutableList.of(new UserInfo(0, "name", 0)));
         mDeviceConfigProxy = new DeviceConfigProxyFake();
 
         mTestableLooper = TestableLooper.get(this);
@@ -78,7 +81,8 @@
                 new Handler(mTestableLooper.getLooper()),
                 mock(BroadcastDispatcher.class),
                 mock(BootCompleteCache.class),
-                mUserTracker);
+                mUserTracker,
+                mContext.getPackageManager());
 
         mTestableLooper.processAllMessages();
     }
@@ -161,17 +165,7 @@
     @Test
     public void testCallbackNotified_additionalOps() {
         LocationChangeCallback callback = mock(LocationChangeCallback.class);
-
         mLocationController.addCallback(callback);
-
-        mTestableLooper.processAllMessages();
-
-        mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION));
-
-        mTestableLooper.processAllMessages();
-
-        verify(callback, times(2)).onLocationSettingsChanged(anyBoolean());
-
         mDeviceConfigProxy.setProperty(
                 DeviceConfig.NAMESPACE_PRIVACY,
                 SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
@@ -181,10 +175,11 @@
 
         when(mAppOpsController.getActiveAppOps())
                 .thenReturn(ImmutableList.of(
-                        new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "",
+                        new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0,
+                                "com.google.android.googlequicksearchbox",
                                 System.currentTimeMillis())));
         mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
-                "", true);
+                "com.google.android.googlequicksearchbox", true);
 
         mTestableLooper.processAllMessages();
 
@@ -192,7 +187,67 @@
 
         when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
         mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
-                "", false);
+                "com.google.android.googlequicksearchbox", false);
+        mTestableLooper.processAllMessages();
+
+        verify(callback, times(1)).onLocationActiveChanged(false);
+    }
+
+    @Test
+    public void testCallbackNotified_additionalOps_shouldShowSystem() {
+        LocationChangeCallback callback = mock(LocationChangeCallback.class);
+        mLocationController.addCallback(callback);
+        mDeviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
+                "true",
+                true);
+        mTestableLooper.processAllMessages();
+
+        when(mAppOpsController.getActiveAppOps())
+                .thenReturn(ImmutableList.of(
+                        new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0,
+                                "com.google.android.gms",
+                                System.currentTimeMillis())));
+        mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
+                "com.google.android.gms", true);
+
+        mTestableLooper.processAllMessages();
+
+        verify(callback, times(0)).onLocationActiveChanged(true);
+    }
+
+
+    @Test
+    public void testCallbackNotified_additionalOps_shouldNotShowSystem() {
+        LocationChangeCallback callback = mock(LocationChangeCallback.class);
+        mLocationController.addCallback(callback);
+        mDeviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
+                "true",
+                true);
+        mDeviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM,
+                "true",
+                true);
+        mTestableLooper.processAllMessages();
+
+        when(mAppOpsController.getActiveAppOps())
+                .thenReturn(ImmutableList.of(
+                        new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.google.android.gms",
+                                System.currentTimeMillis())));
+        mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
+                "com.google.android.gms", true);
+
+        mTestableLooper.processAllMessages();
+
+        verify(callback, times(1)).onLocationActiveChanged(true);
+
+        when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
+        mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
+                "com.google.android.gms", false);
         mTestableLooper.processAllMessages();
 
         verify(callback, times(1)).onLocationActiveChanged(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index ba7bbfe..20a3fda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,7 +102,8 @@
 
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
-                Handler.createAsync(Dependency.get(Dependency.BG_LOOPER)));
+                Handler.createAsync(Dependency.get(Dependency.BG_LOOPER)),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // Avoid SecurityException RemoteInputView#sendRemoteInput().
         mContext.addMockSystemService(ShortcutManager.class, mShortcutManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
index 0581264..ea620a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -23,7 +23,6 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.testing.TestableResources;
 
 import androidx.test.filters.SmallTest;
 
@@ -43,25 +42,19 @@
 @SmallTest
 public class RotationLockControllerImplTest extends SysuiTestCase {
 
-    private static final String[] DEFAULT_SETTINGS = new String[]{
-            "0:0",
-            "1:2"
-    };
+    private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
 
     @Mock RotationPolicyWrapper mRotationPolicyWrapper;
     @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
 
-    private TestableResources mResources;
-    private ArgumentCaptor<RotationPolicy.RotationPolicyListener>
-            mRotationPolicyListenerCaptor;
+    private ArgumentCaptor<RotationPolicy.RotationPolicyListener> mRotationPolicyListenerCaptor;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(/* testClass= */ this);
-        mResources = mContext.getOrCreateTestableResources();
 
-        mRotationPolicyListenerCaptor = ArgumentCaptor.forClass(
-                RotationPolicy.RotationPolicyListener.class);
+        mRotationPolicyListenerCaptor =
+                ArgumentCaptor.forClass(RotationPolicy.RotationPolicyListener.class);
     }
 
     @Test
@@ -79,14 +72,7 @@
     }
 
     @Test
-    public void whenFlagOn_initializesDeviceStateRotationController() {
-        createRotationLockController();
-
-        verify(mDeviceStateRotationLockSettingController).initialize();
-    }
-
-    @Test
-    public void whenFlagOn_dviceStateRotationControllerAddedToCallbacks() {
+    public void whenFlagOn_deviceStateRotationControllerAddedToCallbacks() {
         createRotationLockController();
         captureRotationPolicyListener().onChange();
 
@@ -103,11 +89,11 @@
     private void createRotationLockController() {
         createRotationLockController(DEFAULT_SETTINGS);
     }
+
     private void createRotationLockController(String[] deviceStateRotationLockDefaults) {
         new RotationLockControllerImpl(
                 mRotationPolicyWrapper,
                 mDeviceStateRotationLockSettingController,
-                deviceStateRotationLockDefaults
-        );
+                deviceStateRotationLockDefaults);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 32aee2b..0e25d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -30,6 +30,7 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
@@ -119,7 +120,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mReceiver = new BlockingQueueIntentReceiver();
-        mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
+        mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> action.onDismiss());
         mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
         mDependency.injectMockDependency(ShadeController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 34407b0..f804d83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -39,6 +39,7 @@
 import android.content.Intent;
 import android.content.om.FabricatedOverlay;
 import android.content.om.OverlayIdentifier;
+import android.database.ContentObserver;
 import android.graphics.Color;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -55,6 +56,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.monet.Style;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -117,6 +119,9 @@
     private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessLifecycleObserver;
     @Captor
     private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
+    @Captor
+    private ArgumentCaptor<ContentObserver> mSettingsObserver;
+    private Style mCurrentStyle;
 
     @Before
     public void setup() {
@@ -130,10 +135,11 @@
                 mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
             @Nullable
             @Override
-            protected FabricatedOverlay getOverlay(int color, int type) {
+            protected FabricatedOverlay getOverlay(int color, int type, Style style) {
                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                 when(overlay.getIdentifier())
                         .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
+                mCurrentStyle = style;
                 return overlay;
             }
         };
@@ -148,6 +154,10 @@
         verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
         verify(mDumpManager).registerDumpable(any(), any());
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
+        verify(mSecureSettings).registerContentObserverForUser(
+                eq(Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)),
+                eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
+        );
     }
 
     @Test
@@ -211,12 +221,6 @@
         verify(mThemeOverlayApplier)
                 .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
-        // Assert that we received the colors that we were expecting
-        assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
-                .isEqualTo(new OverlayIdentifier("ffff0000"));
-        assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
-                .isEqualTo(new OverlayIdentifier("ffff0000"));
-
         // Should not change theme after changing wallpapers, if intent doesn't have
         // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
         clearInvocations(mThemeOverlayApplier);
@@ -322,6 +326,40 @@
     }
 
     @Test
+    public void onSettingChanged_honorThemeStyle() {
+        when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
+        for (Style style : Style.values()) {
+            reset(mSecureSettings);
+
+            String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
+                    + "\"android.theme.customization.theme_style\":\"" + style.name() + "\"}";
+
+            when(mSecureSettings.getStringForUser(
+                    eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                    .thenReturn(jsonString);
+
+            mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
+
+            assertThat(mCurrentStyle).isEqualTo(style);
+        }
+    }
+
+    @Test
+    public void onSettingChanged_invalidStyle() {
+        when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
+        String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
+                + "\"android.theme.customization.theme_style\":\"some_invalid_name\"}";
+
+        when(mSecureSettings.getStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                .thenReturn(jsonString);
+
+        mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
+
+        assertThat(mCurrentStyle).isEqualTo(Style.TONAL_SPOT);
+    }
+
+    @Test
     public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -611,11 +649,10 @@
                 mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
             @Nullable
             @Override
-            protected FabricatedOverlay getOverlay(int color, int type) {
+            protected FabricatedOverlay getOverlay(int color, int type, Style style) {
                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                 when(overlay.getIdentifier())
                         .thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever"));
-
                 return overlay;
             }
 
@@ -648,7 +685,7 @@
                 mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
             @Nullable
             @Override
-            protected FabricatedOverlay getOverlay(int color, int type) {
+            protected FabricatedOverlay getOverlay(int color, int type, Style style) {
                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                 when(overlay.getIdentifier())
                         .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
new file mode 100644
index 0000000..8076b4e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+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.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateLoggingProviderTest : SysuiTestCase() {
+
+    @Captor
+    private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener>
+
+    @Mock private lateinit var foldStateProvider: FoldStateProvider
+
+    private val fakeClock = FakeSystemClock()
+
+    private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider
+
+    private val foldLoggingUpdates: MutableList<FoldStateChange> = arrayListOf()
+
+    private val foldStateLoggingListener =
+        object : FoldStateLoggingListener {
+            override fun onFoldUpdate(foldStateUpdate: FoldStateChange) {
+                foldLoggingUpdates.add(foldStateUpdate)
+            }
+        }
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        foldStateLoggingProvider =
+            FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply {
+                addCallback(foldStateLoggingListener)
+                init()
+            }
+
+        verify(foldStateProvider).addCallback(foldUpdatesListener.capture())
+    }
+
+    @Test
+    fun onFoldUpdate_noPreviousOne_finishHalfOpen_nothingReported() {
+        sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+        assertThat(foldLoggingUpdates).isEmpty()
+    }
+
+    @Test
+    fun onFoldUpdate_noPreviousOne_finishFullOpen_nothingReported() {
+        sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+        assertThat(foldLoggingUpdates).isEmpty()
+    }
+
+    @Test
+    fun onFoldUpdate_noPreviousOne_finishClosed_nothingReported() {
+        sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+        assertThat(foldLoggingUpdates).isEmpty()
+    }
+
+    @Test
+    fun onFoldUpdate_startOpening_fullOpen_changeReported() {
+        val dtTime = 10L
+
+        sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+        fakeClock.advanceTime(dtTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+        assertThat(foldLoggingUpdates)
+            .containsExactly(FoldStateChange(FULLY_CLOSED, FULLY_OPENED, dtTime))
+    }
+
+    @Test
+    fun onFoldUpdate_startClosingThenFinishClosed_noInitialState_nothingReported() {
+        val dtTime = 10L
+
+        sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+        fakeClock.advanceTime(dtTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+        assertThat(foldLoggingUpdates).isEmpty()
+    }
+
+    @Test
+    fun onFoldUpdate_startClosingThenFinishClosed_initiallyOpened_changeReported() {
+        val dtTime = 10L
+        sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+        sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+        fakeClock.advanceTime(dtTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+        assertThat(foldLoggingUpdates)
+            .containsExactly(FoldStateChange(FULLY_OPENED, FULLY_CLOSED, dtTime))
+    }
+
+    @Test
+    fun onFoldUpdate_startOpeningThenHalf_initiallyClosed_changeReported() {
+        val dtTime = 10L
+        sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+        sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+        fakeClock.advanceTime(dtTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+        assertThat(foldLoggingUpdates)
+            .containsExactly(FoldStateChange(FULLY_CLOSED, HALF_OPENED, dtTime))
+    }
+
+    @Test
+    fun onFoldUpdate_startClosingThenHalf_initiallyOpened_changeReported() {
+        val dtTime = 10L
+        sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+        sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+        fakeClock.advanceTime(dtTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+        assertThat(foldLoggingUpdates)
+            .containsExactly(FoldStateChange(FULLY_OPENED, HALF_OPENED, dtTime))
+    }
+
+    @Test
+    fun onFoldUpdate_foldThenUnfold_multipleReported() {
+        val foldTime = 24L
+        val unfoldTime = 42L
+        val waitingTime = 424L
+        sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+        // Fold
+        sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+        fakeClock.advanceTime(foldTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+        fakeClock.advanceTime(waitingTime)
+        // unfold
+        sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+        fakeClock.advanceTime(unfoldTime)
+        sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+        assertThat(foldLoggingUpdates)
+            .containsExactly(
+                FoldStateChange(FULLY_OPENED, FULLY_CLOSED, foldTime),
+                FoldStateChange(FULLY_CLOSED, FULLY_OPENED, unfoldTime))
+    }
+
+    @Test
+    fun uninit_removesCallback() {
+        foldStateLoggingProvider.uninit()
+
+        verify(foldStateProvider).removeCallback(foldUpdatesListener.value)
+    }
+
+    private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) {
+        foldUpdatesListener.value.onFoldUpdate(foldUpdate)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index f600b12..4e2736c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -68,7 +68,7 @@
             context.mainExecutor,
             context,
             screenLifecycle
-        )
+        ).apply { init() }
         deviceStates = FoldableTestUtils.findDeviceStates(context)
 
         verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 46e0b12..f43dc6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.unfold.util.FoldableTestUtils
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
+import java.lang.Exception
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -39,32 +40,24 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
-import java.lang.Exception
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class DeviceFoldStateProviderTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var hingeAngleProvider: HingeAngleProvider
+    @Mock private lateinit var hingeAngleProvider: HingeAngleProvider
 
-    @Mock
-    private lateinit var screenStatusProvider: ScreenStatusProvider
+    @Mock private lateinit var screenStatusProvider: ScreenStatusProvider
 
-    @Mock
-    private lateinit var deviceStateManager: DeviceStateManager
+    @Mock private lateinit var deviceStateManager: DeviceStateManager
 
-    @Mock
-    private lateinit var handler: Handler
+    @Mock private lateinit var handler: Handler
 
-    @Captor
-    private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+    @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
 
-    @Captor
-    private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+    @Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
 
-    @Captor
-    private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+    @Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
 
     private lateinit var foldStateProvider: DeviceFoldStateProvider
 
@@ -81,24 +74,25 @@
         MockitoAnnotations.initMocks(this)
         deviceStates = FoldableTestUtils.findDeviceStates(context)
 
-        foldStateProvider = DeviceFoldStateProvider(
-            context,
-            hingeAngleProvider,
-            screenStatusProvider,
-            deviceStateManager,
-            context.mainExecutor,
-            handler
-        )
+        foldStateProvider =
+            DeviceFoldStateProvider(
+                context,
+                hingeAngleProvider,
+                screenStatusProvider,
+                deviceStateManager,
+                context.mainExecutor,
+                handler)
 
-        foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
-            override fun onHingeAngleUpdate(angle: Float) {
-                hingeAngleUpdates.add(angle)
-            }
+        foldStateProvider.addCallback(
+            object : FoldStateProvider.FoldUpdatesListener {
+                override fun onHingeAngleUpdate(angle: Float) {
+                    hingeAngleUpdates.add(angle)
+                }
 
-            override fun onFoldUpdate(update: Int) {
-                foldUpdates.add(update)
-            }
-        })
+                override fun onFoldUpdate(update: Int) {
+                    foldUpdates.add(update)
+                }
+            })
         foldStateProvider.start()
 
         verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
@@ -200,9 +194,10 @@
         sendHingeAngleEvent(90)
         sendHingeAngleEvent(80)
 
-        simulateTimeout(ABORT_CLOSING_MILLIS)
+        simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS)
 
-        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+        assertThat(foldUpdates)
+            .containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_FINISH_HALF_OPEN)
     }
 
     @Test
@@ -210,7 +205,7 @@
         sendHingeAngleEvent(90)
         sendHingeAngleEvent(80)
 
-        simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+        simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
 
         assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
     }
@@ -220,7 +215,7 @@
         sendHingeAngleEvent(180)
         sendHingeAngleEvent(90)
 
-        simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+        simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
         sendHingeAngleEvent(80)
 
         assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
@@ -231,11 +226,13 @@
         sendHingeAngleEvent(180)
         sendHingeAngleEvent(90)
 
-        simulateTimeout(ABORT_CLOSING_MILLIS - 1) // The timeout should not trigger here.
+        // The timeout should not trigger here.
+        simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
         sendHingeAngleEvent(80)
-        simulateTimeout(ABORT_CLOSING_MILLIS) // The timeout should trigger here.
+        simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS) // The timeout should trigger here.
 
-        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+        assertThat(foldUpdates)
+            .containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_FINISH_HALF_OPEN)
     }
 
     @Test
@@ -261,7 +258,7 @@
         }
     }
 
-    private fun simulateTimeout(waitTime: Long = ABORT_CLOSING_MILLIS) {
+    private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
         val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
         if (waitTime >= runnableDelay) {
             scheduledRunnable?.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index e3b07b3..01769e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -21,7 +21,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -96,7 +95,7 @@
     @Mock
     UiEventLogger mUiEventLogger;
     @Captor
-    ArgumentCaptor<Intent> mIntentCaptor;
+    ArgumentCaptor<PendingIntent> mIntentCaptor;
     @Captor
     ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
     private WalletScreenController mController;
@@ -374,10 +373,12 @@
 
         mController.onCardClicked(walletCardViewInfo);
 
-        verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true));
+        verify(mActivityStarter).startPendingIntentDismissingKeyguard(mIntentCaptor.capture());
 
-        assertEquals(mWalletIntent.getAction(), mIntentCaptor.getValue().getAction());
-        assertEquals(mWalletIntent.getComponent(), mIntentCaptor.getValue().getComponent());
+        Intent actualIntent = mIntentCaptor.getValue().getIntent();
+
+        assertEquals(mWalletIntent.getAction(), actualIntent.getAction());
+        assertEquals(mWalletIntent.getComponent(), actualIntent.getComponent());
 
         verify(mUiEventLogger, times(1)).log(WalletUiEvent.QAW_CLICK_CARD);
     }
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 d27a570..9c49e98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
@@ -92,6 +94,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -111,6 +114,7 @@
 import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.bubbles.BubbleBadgeIconFactory;
 import com.android.wm.shell.bubbles.BubbleData;
 import com.android.wm.shell.bubbles.BubbleDataRepository;
 import com.android.wm.shell.bubbles.BubbleEntry;
@@ -152,6 +156,8 @@
     @Mock
     private NotificationEntryManager mNotificationEntryManager;
     @Mock
+    private CommonNotifCollection mCommonNotifCollection;
+    @Mock
     private NotificationGroupManagerLegacy mNotificationGroupManager;
     @Mock
     private WindowManager mWindowManager;
@@ -299,9 +305,8 @@
         mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
                 mNotificationTestHelper.createBubble(handle));
 
-        // Return non-null notification data from the NEM
-        when(mNotificationEntryManager
-                .getActiveNotificationUnfiltered(mRow.getKey())).thenReturn(mRow);
+        // Return non-null notification data from the CommonNotifCollection
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
 
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
@@ -371,12 +376,14 @@
                 mLockscreenUserManager,
                 mNotificationGroupManager,
                 mNotificationEntryManager,
+                mCommonNotifCollection,
                 mNotifPipeline,
                 mSysUiState,
                 mNotifPipelineFlags,
                 mDumpManager,
                 syncExecutor);
 
+        // XXX: Does *this* need to be changed?
         // Get a reference to the BubbleController's entry listener
         verify(mNotificationEntryManager, atLeastOnce())
                 .addNotificationEntryListener(mEntryListenerCaptor.capture());
@@ -421,10 +428,8 @@
     public void testPromoteBubble_autoExpand() throws Exception {
         mBubbleController.updateBubble(mBubbleEntry2);
         mBubbleController.updateBubble(mBubbleEntry);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
-                .thenReturn(mRow);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey()))
-                .thenReturn(mRow2);
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
         mBubbleController.removeBubble(
                 mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
 
@@ -452,10 +457,8 @@
         mBubbleController.updateBubble(mBubbleEntry2);
         mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */
                 false, /* showInShade */ true);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
-                .thenReturn(mRow);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey()))
-                .thenReturn(mRow2);
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
         mBubbleController.removeBubble(
                 mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
 
@@ -958,12 +961,9 @@
                 mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false);
         mBubbleController.updateBubble(
                 mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
-                .thenReturn(mRow);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey()))
-                .thenReturn(mRow2);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow3.getKey()))
-                .thenReturn(mRow3);
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
+        when(mCommonNotifCollection.getEntry(mRow3.getKey())).thenReturn(mRow3);
         assertEquals(mBubbleData.getBubbles().size(), 3);
 
         mBubbleData.setMaxOverflowBubbles(1);
@@ -1021,7 +1021,7 @@
         // GIVEN a group summary with a bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
+        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
@@ -1046,7 +1046,7 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
+        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -1069,7 +1069,7 @@
         // GIVEN a group summary with two (non-bubble) children and one bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
+        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
@@ -1220,6 +1220,7 @@
                 mBubbleController,
                 mBubbleController.getStackView(),
                 new BubbleIconFactory(mContext),
+                new BubbleBadgeIconFactory(mContext),
                 bubble,
                 true /* skipInflation */);
         verify(userContext, times(1)).getPackageManager();
@@ -1286,6 +1287,58 @@
         assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
     }
 
+    @Test
+    public void testNotificationChannelModified_channelUpdated_removesOverflowBubble()
+            throws Exception {
+        // Setup
+        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+        NotificationEntry entry = row.getEntry();
+        entry.getChannel().setConversationId(
+                row.getEntry().getChannel().getParentChannelId(),
+                "shortcutId");
+        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Overflow it
+        mBubbleData.dismissBubbleWithKey(entry.getKey(),
+                Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+        // Test
+        entry.getChannel().setDeleted(true);
+        mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+                entry.getSbn().getUser(),
+                entry.getChannel(),
+                NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+    }
+
+    @Test
+    public void testNotificationChannelModified_channelDeleted_removesOverflowBubble()
+            throws Exception {
+        // Setup
+        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+        NotificationEntry entry = row.getEntry();
+        entry.getChannel().setConversationId(
+                row.getEntry().getChannel().getParentChannelId(),
+                "shortcutId");
+        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Overflow it
+        mBubbleData.dismissBubbleWithKey(entry.getKey(),
+                Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+        // Test
+        entry.getChannel().setDeleted(true);
+        mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+                entry.getSbn().getUser(),
+                entry.getChannel(),
+                NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index ad26f01..e12a82a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -17,6 +17,8 @@
 package com.android.systemui.wmshell;
 
 import static android.app.Notification.FLAG_BUBBLE;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
@@ -77,6 +79,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -133,6 +136,8 @@
     @Mock
     private NotificationEntryManager mNotificationEntryManager;
     @Mock
+    private CommonNotifCollection mCommonNotifCollection;
+    @Mock
     private NotificationGroupManagerLegacy mNotificationGroupManager;
     @Mock
     private BubblesManager.NotifCallback mNotifCallback;
@@ -336,6 +341,7 @@
                 mLockscreenUserManager,
                 mNotificationGroupManager,
                 mNotificationEntryManager,
+                mCommonNotifCollection,
                 mNotifPipeline,
                 mSysUiState,
                 mNotifPipelineFlags,
@@ -404,8 +410,7 @@
     public void testRemoveBubble_withDismissedNotif_notInOverflow() {
         mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
-                .thenReturn(mRow);
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
 
         assertTrue(mBubbleController.hasBubbles());
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
@@ -887,7 +892,7 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
+        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -911,7 +916,7 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
+        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -935,7 +940,7 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
+        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
 
@@ -1011,10 +1016,9 @@
      */
     @Test
     public void testOverflowLoadedOnce() {
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
-                .thenReturn(mRow);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey()))
-                .thenReturn(mRow2);
+        // XXX
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
 
         mEntryListener.onEntryAdded(mRow);
         mEntryListener.onEntryAdded(mRow2);
@@ -1102,6 +1106,58 @@
         assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
     }
 
+    @Test
+    public void testNotificationChannelModified_channelUpdated_removesOverflowBubble()
+            throws Exception {
+        // Setup
+        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+        NotificationEntry entry = row.getEntry();
+        entry.getChannel().setConversationId(
+                row.getEntry().getChannel().getParentChannelId(),
+                "shortcutId");
+        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Overflow it
+        mBubbleData.dismissBubbleWithKey(entry.getKey(),
+                Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+        // Test
+        entry.getChannel().setDeleted(true);
+        mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+                entry.getSbn().getUser(),
+                entry.getChannel(),
+                NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+    }
+
+    @Test
+    public void testNotificationChannelModified_channelDeleted_removesOverflowBubble()
+            throws Exception {
+        // Setup
+        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+        NotificationEntry entry = row.getEntry();
+        entry.getChannel().setConversationId(
+                row.getEntry().getChannel().getParentChannelId(),
+                "shortcutId");
+        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Overflow it
+        mBubbleData.dismissBubbleWithKey(entry.getKey(),
+                Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+        // Test
+        entry.getChannel().setDeleted(true);
+        mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+                entry.getSbn().getUser(),
+                entry.getChannel(),
+                NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+    }
+
     /**
      * Sets the bubble metadata flags for this entry. These flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
index d07a525..0338b93 100644
--- a/proto/src/camera.proto
+++ b/proto/src/camera.proto
@@ -62,4 +62,7 @@
     // The frame counts for each histogram bins
     // Expected number of fields: 10
     repeated int64 histogram_counts = 13;
+
+    // The dynamic range profile of the stream
+    optional int32 dynamic_range_profile = 14;
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 3047c90..196c6aa 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -276,6 +276,12 @@
     // Package: android
     NOTE_UNBLOCK_CAM_TOGGLE = 66;
 
+    // Notify the user that a CA certificate is pending for the wifi connection.
+    NOTE_SERVER_CA_CERTIFICATE = 67;
+
+    // Notify the user to set up dream
+    NOTE_SETUP_DREAM = 68;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index 74d7f65..f33c8c0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -77,6 +77,7 @@
         ":services.appwidget-sources",
         ":services.autofill-sources",
         ":services.backup-sources",
+        ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex/service-bluetooth jar is ready
         ":backuplib-sources",
         ":services.companion-sources",
         ":services.contentcapture-sources",
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ad3e1d5..e93ac47 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1036,6 +1036,30 @@
         }
     }
 
+
+    @Override
+    public Region getCurrentMagnificationRegion(int displayId) {
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getCurrentMagnificationRegion", "displayId=" + displayId);
+        }
+        synchronized (mLock) {
+            final Region region = Region.obtain();
+            if (!hasRightsToCurrentUserLocked()) {
+                return region;
+            }
+            MagnificationProcessor magnificationProcessor =
+                    mSystemSupport.getMagnificationProcessor();
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                magnificationProcessor.getCurrentMagnificationRegion(displayId,
+                        region, mSecurityPolicy.canControlMagnification(this));
+                return region;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
     @Override
     public float getMagnificationCenterX(int displayId) {
         if (svcConnTracingEnabled()) {
@@ -1103,6 +1127,31 @@
     }
 
     @Override
+    public boolean resetCurrentMagnification(int displayId, boolean animate) {
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("resetCurrentMagnification",
+                    "displayId=" + displayId + ";animate=" + animate);
+        }
+        synchronized (mLock) {
+            if (!hasRightsToCurrentUserLocked()) {
+                return false;
+            }
+            if (!mSecurityPolicy.canControlMagnification(this)) {
+                return false;
+            }
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            MagnificationProcessor magnificationProcessor =
+                    mSystemSupport.getMagnificationProcessor();
+            return (magnificationProcessor.resetCurrentMagnification(displayId, animate)
+                    || !magnificationProcessor.isMagnifying(displayId));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public boolean setMagnificationConfig(int displayId,
             @NonNull MagnificationConfig config, boolean animate) {
         if (svcConnTracingEnabled()) {
@@ -1542,9 +1591,9 @@
     }
 
     public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
-            float scale, float centerX, float centerY) {
+            @NonNull MagnificationConfig config) {
         mInvocationHandler
-                .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+                .notifyMagnificationChangedLocked(displayId, region, config);
     }
 
     public void notifySoftKeyboardShowModeChangedLocked(int showState) {
@@ -1564,15 +1613,15 @@
      * state of magnification has changed.
      */
     private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
-            float scale, float centerX, float centerY) {
+            @NonNull MagnificationConfig config) {
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
-                            + scale + ", " + centerX + ", " + centerY);
+                            + config.toString());
                 }
-                listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
+                listener.onMagnificationChanged(displayId, region, config);
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
             }
@@ -1899,11 +1948,9 @@
                 case MSG_ON_MAGNIFICATION_CHANGED: {
                     final SomeArgs args = (SomeArgs) message.obj;
                     final Region region = (Region) args.arg1;
-                    final float scale = (float) args.arg2;
-                    final float centerX = (float) args.arg3;
-                    final float centerY = (float) args.arg4;
+                    final MagnificationConfig config = (MagnificationConfig) args.arg2;
                     final int displayId = args.argi1;
-                    notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY);
+                    notifyMagnificationChangedInternal(displayId, region, config);
                     args.recycle();
                 } break;
 
@@ -1932,7 +1979,7 @@
         }
 
         public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
-                float scale, float centerX, float centerY) {
+                @NonNull MagnificationConfig config) {
             synchronized (mLock) {
                 if (mMagnificationCallbackState.get(displayId) == null) {
                     return;
@@ -1941,9 +1988,7 @@
 
             final SomeArgs args = SomeArgs.obtain();
             args.arg1 = region;
-            args.arg2 = scale;
-            args.arg3 = centerX;
-            args.arg4 = centerY;
+            args.arg2 = config;
             args.argi1 = displayId;
 
             final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e59a3d6..5b580d9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,9 +44,11 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityShortcutInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.MagnificationConfig;
 import android.accessibilityservice.TouchInteractionController;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
@@ -261,6 +263,7 @@
 
     private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
     private final AccessibilityTraceManager mTraceManager;
+    private final CaptioningManagerImpl mCaptioningManagerImpl;
 
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
 
@@ -338,6 +341,7 @@
         mA11yDisplayListener = a11yDisplayListener;
         mMagnificationController = magnificationController;
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
+        mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
         if (inputFilter != null) {
             mInputFilter = inputFilter;
             mHasInputFilter = true;
@@ -372,6 +376,7 @@
         mMagnificationController = new MagnificationController(this, mLock, mContext,
                 new MagnificationScaleProvider(mContext));
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
+        mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
         init();
     }
 
@@ -1301,18 +1306,22 @@
      * Called by the MagnificationController when the state of display
      * magnification changes.
      *
-     * @param displayId The logical display id.
+     * <p>
+     * It can notify window magnification change if the service supports controlling all the
+     * magnification mode.
+     * </p>
+     *
+     * @param displayId The logical display id
      * @param region the new magnified region, may be empty if
      *               magnification is not enabled (e.g. scale is 1)
-     * @param scale the new scale
-     * @param centerX the new screen-relative center X coordinate
-     * @param centerY the new screen-relative center Y coordinate
+     * @param config The magnification config. That has magnification mode, the new scale and the
+     *              new screen-relative center position
      */
     public void notifyMagnificationChanged(int displayId, @NonNull Region region,
-            float scale, float centerX, float centerY) {
+            @NonNull MagnificationConfig config) {
         synchronized (mLock) {
             notifyClearAccessibilityCacheLocked();
-            notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+            notifyMagnificationChangedLocked(displayId, region, config);
         }
     }
 
@@ -1613,11 +1622,11 @@
     }
 
     private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
-            float scale, float centerX, float centerY) {
+            @NonNull MagnificationConfig config) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-            service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+            service.notifyMagnificationChangedLocked(displayId, region, config);
         }
     }
 
@@ -2393,6 +2402,7 @@
         somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
         somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
         somethingChanged |= readMagnificationCapabilitiesLocked(userState);
+        somethingChanged |= readMagnificationFollowTypingLocked(userState);
         return somethingChanged;
     }
 
@@ -3452,6 +3462,31 @@
     }
 
     @Override
+    @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
+    public void setSystemAudioCaptioningRequested(boolean isEnabled, int userId) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.SET_SYSTEM_AUDIO_CAPTION,
+                "setSystemAudioCaptioningRequested");
+
+        mCaptioningManagerImpl.setSystemAudioCaptioningRequested(isEnabled, userId);
+    }
+
+    @Override
+    public boolean isSystemAudioCaptioningUiRequested(int userId) {
+        return mCaptioningManagerImpl.isSystemAudioCaptioningUiRequested(userId);
+    }
+
+    @Override
+    @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
+    public void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.SET_SYSTEM_AUDIO_CAPTION,
+                "setSystemAudioCaptioningUiRequested");
+
+        mCaptioningManagerImpl.setSystemAudioCaptioningUiRequested(isEnabled, userId);
+    }
+
+    @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
         synchronized (mLock) {
@@ -3462,6 +3497,7 @@
             pw.append("hasWindowMagnificationConnection=").append(
                     String.valueOf(getWindowMagnificationMgr().isConnected()));
             pw.println();
+            mMagnificationProcessor.dump(pw, getValidDisplayList());
             final int userCount = mUserStates.size();
             for (int i = 0; i < userCount; i++) {
                 mUserStates.valueAt(i).dump(fd, pw, args);
@@ -3868,6 +3904,9 @@
         private final Uri mMagnificationCapabilityUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
 
+        private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+
         public AccessibilityContentObserver(Handler handler) {
             super(handler);
         }
@@ -3906,6 +3945,8 @@
                     mMagnificationModeUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mMagnificationCapabilityUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
+                    mMagnificationFollowTypingUri, false, this, UserHandle.USER_ALL);
         }
 
         @Override
@@ -3973,6 +4014,8 @@
                     if (readMagnificationCapabilitiesLocked(userState)) {
                         updateMagnificationCapabilitiesSettingsChangeLocked(userState);
                     }
+                } else if (mMagnificationFollowTypingUri.equals(uri)) {
+                    readMagnificationFollowTypingLocked(userState);
                 }
             }
         }
@@ -4069,6 +4112,19 @@
         return false;
     }
 
+    boolean readMagnificationFollowTypingLocked(AccessibilityUserState userState) {
+        final boolean followTypeEnabled = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+                1, userState.mUserId) == 1;
+        if (followTypeEnabled != userState.isMagnificationFollowTypingEnabled()) {
+            userState.setMagnificationFollowTypingEnabled(followTypeEnabled);
+            mMagnificationController.setMagnificationFollowTypingEnabled(followTypeEnabled);
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
         mMainHandler.sendMessage(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 9324e3e..0f35456 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -129,6 +129,8 @@
     private final SparseIntArray mMagnificationModes = new SparseIntArray();
     // The magnification capabilities used to know magnification mode could be switched.
     private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+    // Whether the following typing focus feature for magnification is enabled.
+    private boolean mMagnificationFollowTypingEnabled = true;
 
     /** The stroke width of the focus rectangle in pixels */
     private int mFocusStrokeWidth;
@@ -210,6 +212,7 @@
         mMagnificationModes.clear();
         mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
         mFocusColor = mFocusColorDefaultValue;
+        mMagnificationFollowTypingEnabled = true;
     }
 
     void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -685,6 +688,14 @@
         mMagnificationCapabilities = capabilities;
     }
 
+    public void setMagnificationFollowTypingEnabled(boolean enabled) {
+        mMagnificationFollowTypingEnabled = enabled;
+    }
+
+    public boolean isMagnificationFollowTypingEnabled() {
+        return mMagnificationFollowTypingEnabled;
+    }
+
     /**
      * Sets the magnification mode to the given display.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 09485d1..59c1461 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -735,8 +735,7 @@
                 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
                 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
                 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
-                case WindowManager.LayoutParams.TYPE_SCREENSHOT:
-                case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: {
+                case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
                     return AccessibilityWindowInfo.TYPE_SYSTEM;
                 }
 
@@ -748,6 +747,10 @@
                     return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
                 }
 
+                case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: {
+                    return AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY;
+                }
+
                 default: {
                     return -1;
                 }
diff --git a/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java b/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java
new file mode 100644
index 0000000..39780d2
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java
@@ -0,0 +1,87 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.os.Binder;
+import android.provider.Settings;
+import android.view.accessibility.CaptioningManager;
+
+/**
+ * Implementation class for CaptioningManager's interface that need system server identity.
+ */
+public class CaptioningManagerImpl implements CaptioningManager.SystemAudioCaptioningAccessing {
+    private static final boolean SYSTEM_AUDIO_CAPTIONING_UI_DEFAULT_ENABLED = false;
+
+    private final Context mContext;
+
+    public CaptioningManagerImpl(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Sets the system audio caption enabled state.
+     *
+     * @param isEnabled The system audio captioning enabled state.
+     * @param userId The user Id.
+     */
+    @Override
+    public void setSystemAudioCaptioningRequested(boolean isEnabled, int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0, userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Gets the system audio caption UI enabled state.
+     *
+     * @param userId The user Id.
+     * @return the system audio caption UI enabled state.
+     */
+    @Override
+    public boolean isSystemAudioCaptioningUiRequested(int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
+                    SYSTEM_AUDIO_CAPTIONING_UI_DEFAULT_ENABLED ? 1 : 0, userId) == 1;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Sets the system audio caption UI enabled state.
+     *
+     * @param isEnabled The system audio captioning UI enabled state.
+     * @param userId The user Id.
+     */
+    @Override
+    public void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, isEnabled ? 1 : 0, userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 72bc850..e39b979 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -17,10 +17,12 @@
 package com.android.server.accessibility.magnification;
 
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
 
+import android.accessibilityservice.MagnificationConfig;
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
@@ -89,6 +91,8 @@
     private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
 
     private final Rect mTempRect = new Rect();
+    // Whether the following typing focus feature for magnification is enabled.
+    private boolean mMagnificationFollowTypingEnabled = true;
 
     /**
      * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
@@ -363,9 +367,16 @@
             return mIdOfLastServiceToMagnify;
         }
 
+        @GuardedBy("mLock")
         void onMagnificationChangedLocked() {
-            mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion,
-                    getScale(), getCenterX(), getCenterY());
+            final MagnificationConfig config = new MagnificationConfig.Builder()
+                    .setMode(MAGNIFICATION_MODE_FULLSCREEN)
+                    .setScale(getScale())
+                    .setCenterX(getCenterX())
+                    .setCenterY(getCenterY()).build();
+            mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId,
+                    mMagnificationRegion,
+                    config);
             if (mUnregisterPending && !isMagnifying()) {
                 unregister(mDeleteAfterUnregister);
             }
@@ -735,6 +746,9 @@
     public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
             int bottom) {
         synchronized (mLock) {
+            if (!mMagnificationFollowTypingEnabled) {
+                return;
+            }
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return;
@@ -751,6 +765,10 @@
         }
     }
 
+    void setMagnificationFollowTypingEnabled(boolean enabled) {
+        mMagnificationFollowTypingEnabled = enabled;
+    }
+
     /**
      * Remove the display magnification with given id.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 037dc1f..b70ffb2 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -379,6 +380,16 @@
         mAms.changeMagnificationMode(displayId, magnificationMode);
     }
 
+    @Override
+    public void onSourceBoundsChanged(int displayId, Rect bounds) {
+        final MagnificationConfig config = new MagnificationConfig.Builder()
+                .setMode(MAGNIFICATION_MODE_WINDOW)
+                .setScale(mScaleProvider.getScale(displayId))
+                .setCenterX(bounds.exactCenterX())
+                .setCenterY(bounds.exactCenterY()).build();
+        mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
+    }
+
     private void disableFullScreenMagnificationIfNeeded(int displayId) {
         final FullScreenMagnificationController fullScreenMagnificationController =
                 getFullScreenMagnificationController();
@@ -426,6 +437,7 @@
         synchronized (mLock) {
             mImeWindowVisible = shown;
         }
+        getWindowMagnificationMgr().onImeWindowVisibilityChanged(shown);
         logMagnificationModeWithImeOnIfNeeded();
     }
 
@@ -518,6 +530,16 @@
         mMagnificationCapabilities = capabilities;
     }
 
+    /**
+     * Called when the following typing focus feature is switched.
+     *
+     * @param enabled Enable the following typing focus feature
+     */
+    public void setMagnificationFollowTypingEnabled(boolean enabled) {
+        getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+        getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
+    }
+
     private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
             int displayId) {
         return mMagnificationEndRunnableSparseArray.get(displayId);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 40f77b0..77e3ee5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -26,6 +26,10 @@
 import android.accessibilityservice.MagnificationConfig;
 import android.annotation.NonNull;
 import android.graphics.Region;
+import android.view.Display;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
 
 /**
  * Processor class for AccessibilityService connection to control magnification on the specified
@@ -203,6 +207,36 @@
     }
 
     /**
+     * Returns the region of the screen currently active for magnification if the
+     * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
+     * Returns the region of screen projected on the magnification window if the controlling
+     * magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
+     * <p>
+     * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+     * the returned region will be empty if the magnification is
+     * not active. And the magnification is active if magnification gestures are enabled
+     * or if a service is running that can control magnification.
+     * </p><p>
+     * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+     * the returned region will be empty if the magnification is not activated.
+     * </p>
+     *
+     * @param displayId The logical display id
+     * @param outRegion the region to populate
+     * @param canControlMagnification Whether the service can control magnification
+     */
+    public void getCurrentMagnificationRegion(int displayId, @NonNull Region outRegion,
+            boolean canControlMagnification) {
+        int currentMode = getControllingMode(displayId);
+        if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
+            getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
+        } else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
+            mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+                    outRegion);
+        }
+    }
+
+    /**
      * Returns the magnification bounds of full-screen magnification on the given display.
      *
      * @param displayId The logical display id
@@ -224,8 +258,8 @@
     }
 
     /**
-     * Resets the current magnification on the given display. The reset mode could be
-     * full-screen or window if it is activated.
+     * Resets the controlling magnifier on the given display.
+     * For resetting window magnifier, it disables the magnifier by setting the scale to 1.
      *
      * @param displayId The logical display id.
      * @param animate   {@code true} to animate the transition, {@code false}
@@ -330,4 +364,29 @@
     private void unregister(int displayId) {
         mController.getFullScreenMagnificationController().unregister(displayId);
     }
+
+    /** Dumps {@link MagnificationConfig} and magnification region of magnifiers on the displays. */
+    public void dump(final PrintWriter pw, ArrayList<Display> displaysList) {
+        for (int i = 0; i < displaysList.size(); i++) {
+            final int displayId = displaysList.get(i).getDisplayId();
+            final MagnificationConfig config = getMagnificationConfig(displayId);
+            pw.println("Magnifier on display#" + displayId);
+            pw.append("    " + config).println();
+            final Region region = new Region();
+            getCurrentMagnificationRegion(displayId, region, true);
+            if (!region.isEmpty()) {
+                pw.append("    Magnification region=").append(region.toString()).println();
+            }
+            pw.append("    IdOfLastServiceToMagnify="
+                    + getIdOfLastServiceToMagnify(config.getMode(), displayId)).println();
+        }
+    }
+
+    private int getIdOfLastServiceToMagnify(int mode, int displayId) {
+        return (mode == MAGNIFICATION_MODE_FULLSCREEN)
+                ? mController.getFullScreenMagnificationController()
+                .getIdOfLastServiceToMagnify(displayId)
+                : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
+                        displayId);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index c4a577d..2fbefa4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -97,6 +97,8 @@
     private ConnectionCallback mConnectionCallback;
     @GuardedBy("mLock")
     private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
+    // Whether the following typing focus feature for magnification is enabled.
+    private boolean mMagnificationFollowTypingEnabled = true;
 
     private boolean mReceiverRegistered = false;
     @VisibleForTesting
@@ -139,6 +141,14 @@
         void onWindowMagnificationActivationState(int displayId, boolean activated);
 
         /**
+         * Called when the magnification source bounds are changed.
+         *
+         * @param displayId The logical display id.
+         * @param bounds    The magnified source bounds on the display.
+         */
+        void onSourceBoundsChanged(int displayId, Rect bounds);
+
+        /**
          * Called from {@link IWindowMagnificationConnection} to request changing the magnification
          * mode on the given display.
          *
@@ -291,13 +301,89 @@
     @Override
     public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
             int bottom) {
-        // TODO(b/194668976): We will implement following typing focus in window mode after
-        //  our refactor.
+        if (!mMagnificationFollowTypingEnabled) {
+            return;
+        }
+
+        float toCenterX = (float) (left + right) / 2;
+        float toCenterY = (float) (top + bottom) / 2;
+
+        if (!isPositionInSourceBounds(displayId, toCenterX, toCenterY)
+                && isTrackingTypingFocusEnabled(displayId)) {
+            enableWindowMagnification(displayId, Float.NaN, toCenterX, toCenterY);
+        }
+    }
+
+    void setMagnificationFollowTypingEnabled(boolean enabled) {
+        mMagnificationFollowTypingEnabled = enabled;
+    }
+
+    /**
+     * Get the ID of the last service that changed the magnification config.
+     *
+     * @param displayId The logical display id.
+     * @return The id
+     */
+    public int getIdOfLastServiceToMagnify(int displayId) {
+        synchronized (mLock) {
+            final WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+            if (magnifier != null) {
+                return magnifier.mIdOfLastServiceToControl;
+            }
+        }
+        return INVALID_SERVICE_ID;
+    }
+
+    /**
+     * Enable or disable tracking typing focus for the specific magnification window.
+     *
+     * The tracking typing focus should be set to enabled with the following conditions:
+     * 1. IME is shown.
+     *
+     * The tracking typing focus should be set to disabled with the following conditions:
+     * 1. A user drags the magnification window by 1 finger.
+     * 2. A user scroll the magnification window by 2 fingers.
+     *
+     * @param displayId The logical display id.
+     * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus.
+     */
+    private void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) {
+        synchronized (mLock) {
+            WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+            if (magnifier == null) {
+                return;
+            }
+            magnifier.setTrackingTypingFocusEnabled(trackingTypingFocusEnabled);
+        }
+    }
+
+    /**
+     * Enable tracking typing focus function for all magnifications.
+     */
+    private void enableAllTrackingTypingFocus() {
+        synchronized (mLock) {
+            for (int i = 0; i < mWindowMagnifiers.size(); i++) {
+                WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
+                magnifier.setTrackingTypingFocusEnabled(true);
+            }
+        }
+    }
+
+    /**
+     * Called when the IME window visibility changed.
+     *
+     * @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden.
+     */
+    void onImeWindowVisibilityChanged(boolean shown) {
+        if (shown) {
+            enableAllTrackingTypingFocus();
+        }
     }
 
     @Override
     public boolean processScroll(int displayId, float distanceX, float distanceY) {
         moveWindowMagnification(displayId, -distanceX, -distanceY);
+        setTrackingTypingFocusEnabled(displayId, false);
         return /* event consumed: */ true;
     }
 
@@ -396,6 +482,7 @@
         boolean previousEnabled;
         synchronized (mLock) {
             if (mConnectionWrapper == null) {
+                Slog.w(TAG, "enableWindowMagnification failed: connection null");
                 return false;
             }
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
@@ -438,6 +525,7 @@
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null || mConnectionWrapper == null) {
+                Slog.w(TAG, "disableWindowMagnification failed: connection " + mConnectionWrapper);
                 return false;
             }
             disabled = magnifier.disableWindowMagnificationInternal(animationCallback);
@@ -469,6 +557,16 @@
         }
     }
 
+    boolean isPositionInSourceBounds(int displayId, float x, float y) {
+        synchronized (mLock) {
+            WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+            if (magnifier == null) {
+                return false;
+            }
+            return magnifier.isPositionInSourceBounds(x, y);
+        }
+    }
+
     /**
      * Indicates whether window magnification is enabled on specified display.
      *
@@ -598,6 +696,16 @@
         }
     }
 
+    boolean isTrackingTypingFocusEnabled(int displayId) {
+        synchronized (mLock) {
+            WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+            if (magnifier == null) {
+                return false;
+            }
+            return magnifier.isTrackingTypingFocusEnabled();
+        }
+    }
+
     /**
      * Populates magnified bounds on the screen. And the populated magnified bounds would be
      * empty If window magnifier is not activated.
@@ -689,6 +797,7 @@
                 }
                 magnifier.onSourceBoundsChanged(sourceBounds);
             }
+            mCallback.onSourceBoundsChanged(displayId, sourceBounds);
         }
 
         @Override
@@ -714,6 +823,17 @@
         }
 
         @Override
+        public void onDrag(int displayId) {
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+                mTrace.logTrace(TAG + "ConnectionCallback.onDrag",
+                        FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                        "displayId=" + displayId);
+            }
+            setTrackingTypingFocusEnabled(displayId, false);
+        }
+
+        @Override
         public void binderDied() {
             synchronized (mLock) {
                 Slog.w(TAG, "binderDied DeathRecipient :" + mExpiredDeathRecipient);
@@ -749,7 +869,9 @@
 
         private int mIdOfLastServiceToControl = INVALID_SERVICE_ID;
 
-        private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+        private final PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+
+        private boolean mTrackingTypingFocusEnabled = true;
 
         WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
             mDisplayId = displayId;
@@ -790,7 +912,6 @@
             }
         }
 
-        @GuardedBy("mLock")
         boolean disableWindowMagnificationInternal(
                 @Nullable MagnificationAnimationCallback animationResultCallback) {
             if (!mEnabled) {
@@ -800,6 +921,7 @@
                     mDisplayId, animationResultCallback)) {
                 mEnabled = false;
                 mIdOfLastServiceToControl = INVALID_SERVICE_ID;
+                mTrackingTypingFocusEnabled = false;
                 return true;
             }
             return false;
@@ -848,7 +970,18 @@
             return count;
         }
 
-        @GuardedBy("mLock")
+        boolean isPositionInSourceBounds(float x, float y) {
+            return mSourceBounds.contains((int) x, (int) y);
+        }
+
+        void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
+            mTrackingTypingFocusEnabled = trackingTypingFocusEnabled;
+        }
+
+        boolean isTrackingTypingFocusEnabled() {
+            return mTrackingTypingFocusEnabled;
+        }
+
         boolean isEnabled() {
             return mEnabled;
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 422749e..051281c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -241,9 +241,6 @@
     protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
             @NonNull ContentObserver observer) {
         resolver.registerContentObserver(Settings.Global.getUriFor(
-                Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
-                UserHandle.USER_ALL);
-        resolver.registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
                 UserHandle.USER_ALL);
         resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -274,8 +271,6 @@
                 break;
             default:
                 Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
-                // fall through
-            case Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
                 synchronized (mLock) {
                     updateCachedServiceLocked(userId);
                 }
@@ -307,6 +302,9 @@
                 case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
                     setDeviceConfigProperties();
                     break;
+                case AutofillManager.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
+                    updateCachedServices();
+                    break;
                 default:
                     Slog.i(mTag, "Ignoring change on " + key);
             }
@@ -588,6 +586,15 @@
         }
     }
 
+    private void updateCachedServices() {
+        List<UserInfo> supportedUsers = getSupportedUsers();
+        for (UserInfo userInfo : supportedUsers) {
+            synchronized (mLock) {
+                updateCachedServiceLocked(userInfo.id);
+            }
+        }
+    }
+
     // Called by Shell command.
     void calculateScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
@@ -702,31 +709,44 @@
             return;
         }
 
-        final Map<String, String[]> whiteListedPackages = getWhitelistedCompatModePackages();
+        final Map<String, String[]> allowedPackages = getAllowedCompatModePackages();
         final int compatPackageCount = compatPackages.size();
         for (int i = 0; i < compatPackageCount; i++) {
             final String packageName = compatPackages.keyAt(i);
-            if (whiteListedPackages == null || !whiteListedPackages.containsKey(packageName)) {
-                Slog.w(TAG, "Ignoring not whitelisted compat package " + packageName);
+            if (allowedPackages == null || !allowedPackages.containsKey(packageName)) {
+                Slog.w(TAG, "Ignoring not allowed compat package " + packageName);
                 continue;
             }
             final Long maxVersionCode = compatPackages.valueAt(i);
             if (maxVersionCode != null) {
                 mAutofillCompatState.addCompatibilityModeRequest(packageName,
-                        maxVersionCode, whiteListedPackages.get(packageName), userId);
+                        maxVersionCode, allowedPackages.get(packageName), userId);
             }
         }
     }
 
-    private String getWhitelistedCompatModePackagesFromSettings() {
+    private String getAllowedCompatModePackagesFromDeviceConfig() {
+        String config = DeviceConfig.getString(
+                DeviceConfig.NAMESPACE_AUTOFILL,
+                AutofillManager.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
+                /* defaultValue */ null);
+        if (!TextUtils.isEmpty(config)) {
+            return config;
+        }
+        // Fallback to Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES if
+        // the device config is null.
+        return getAllowedCompatModePackagesFromSettings();
+    }
+
+    private String getAllowedCompatModePackagesFromSettings() {
         return Settings.Global.getString(
                 getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
     }
 
     @Nullable
-    private Map<String, String[]> getWhitelistedCompatModePackages() {
-        return getWhitelistedCompatModePackages(getWhitelistedCompatModePackagesFromSettings());
+    private Map<String, String[]> getAllowedCompatModePackages() {
+        return getAllowedCompatModePackages(getAllowedCompatModePackagesFromDeviceConfig());
     }
 
     private void send(@NonNull IResultReceiver receiver, int value) {
@@ -771,7 +791,7 @@
 
     @Nullable
     @VisibleForTesting
-    static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
+    static Map<String, String[]> getAllowedCompatModePackages(String setting) {
         if (TextUtils.isEmpty(setting)) {
             return null;
         }
@@ -1756,8 +1776,8 @@
                     mUi.dump(pw);
                     pw.print("Autofill Compat State: ");
                     mAutofillCompatState.dump(prefix, pw);
-                    pw.print("from settings: ");
-                    pw.println(getWhitelistedCompatModePackagesFromSettings());
+                    pw.print("from device config: ");
+                    pw.println(getAllowedCompatModePackagesFromDeviceConfig());
                     if (mSupportedSmartSuggestionModes != 0) {
                         pw.print("Smart Suggestion modes: ");
                         pw.println(getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
diff --git a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
new file mode 100644
index 0000000..f1d98f0
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.companion.AssociationRequest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+/**
+ * A Job Service responsible for clean up the Association.
+ * The job will be executed only if the device is charging and in idle mode due to the application
+ * will be killed if association/role are revoked.
+ */
+public class AssociationCleanUpService extends JobService {
+    private static final String TAG = LOG_TAG + ".AssociationCleanUpService";
+    private static final int JOB_ID = AssociationCleanUpService.class.hashCode();
+    private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day
+    private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService(
+            CompanionDeviceManagerServiceInternal.class);
+
+    @Override
+    public boolean onStartJob(final JobParameters params) {
+        Slog.i(LOG_TAG, "Execute the Association CleanUp job");
+        // Special policy for APP_STREAMING role that need to revoke associations if the device
+        // does not connect for 3 months.
+        AsyncTask.execute(() -> {
+            mCdmServiceInternal.associationCleanUp(AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
+            jobFinished(params, false);
+        });
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(final JobParameters params) {
+        Slog.d(TAG, "Association cleanup job stopped; id=" + params.getJobId()
+                + ", reason="
+                + JobParameters.getInternalReasonCodeDescription(
+                params.getInternalStopReasonCode()));
+        return false;
+    }
+
+    static void schedule(Context context) {
+        Slog.i(LOG_TAG, "Scheduling the Association Cleanup job");
+        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        final JobInfo job = new JobInfo.Builder(JOB_ID,
+                new ComponentName(context, AssociationCleanUpService.class))
+                .setRequiresCharging(true)
+                .setRequiresDeviceIdle(true)
+                .setPeriodic(ONE_DAY_INTERVAL)
+                .build();
+        jobScheduler.schedule(job);
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 1914164..2b7b977 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -23,7 +23,7 @@
 import static android.content.ComponentName.createRelative;
 
 import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
 import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
 import static com.android.server.companion.RolesUtils.isRoleHolder;
 
@@ -31,6 +31,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.companion.AssociationInfo;
@@ -102,8 +103,9 @@
  * @see #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback,
  * ResultReceiver, MacAddress)
  */
+@SuppressLint("LongLogTag")
 class AssociationRequestsProcessor {
-    private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor";
+    private static final String TAG = "CompanionDevice_AssociationRequestsProcessor";
 
     private static final ComponentName ASSOCIATION_REQUEST_APPROVAL_ACTIVITY =
             createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".CompanionDeviceActivity");
@@ -161,7 +163,7 @@
 
         // 1. Enforce permissions and other requirements.
         enforcePermissionsForAssociation(mContext, request, packageUid);
-        mService.checkUsesFeature(packageName, userId);
+        enforceUsesCompanionDeviceFeature(mContext, userId, packageName);
 
         // 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER
         // to perform discovery NOR to collect user consent).
@@ -252,9 +254,14 @@
     private AssociationInfo createAssociationAndNotifyApplication(
             @NonNull AssociationRequest request, @NonNull String packageName, @UserIdInt int userId,
             @Nullable MacAddress macAddress, @NonNull IAssociationRequestCallback callback) {
-        final AssociationInfo association = mService.createAssociation(userId, packageName,
-                macAddress, request.getDisplayName(), request.getDeviceProfile(),
-                request.isSelfManaged());
+        final AssociationInfo association;
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            association = mService.createAssociation(userId, packageName, macAddress,
+                    request.getDisplayName(), request.getDeviceProfile(), request.isSelfManaged());
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
 
         try {
             callback.onAssociationCreated(association);
diff --git a/services/companion/java/com/android/server/companion/AssociationStore.java b/services/companion/java/com/android/server/companion/AssociationStore.java
index 58fc8f7..01905bb 100644
--- a/services/companion/java/com/android/server/companion/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/AssociationStore.java
@@ -49,7 +49,25 @@
     /**  Listener for any changes to {@link AssociationInfo}-s. */
     interface OnChangeListener {
         default void onAssociationChanged(
-                @ChangeType int changeType, AssociationInfo association) {}
+                @ChangeType int changeType, AssociationInfo association) {
+            switch (changeType) {
+                case CHANGE_TYPE_ADDED:
+                    onAssociationAdded(association);
+                    break;
+
+                case CHANGE_TYPE_REMOVED:
+                    onAssociationRemoved(association);
+                    break;
+
+                case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+                    onAssociationUpdated(association, true);
+                    break;
+
+                case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+                    onAssociationUpdated(association, false);
+                    break;
+            }
+        }
 
         default void onAssociationAdded(AssociationInfo association) {}
 
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 3f0200e..cda554e 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -37,6 +37,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.StringJoiner;
 
 /**
  * Implementation of the {@link AssociationStore}, with addition of the methods for modification.
@@ -58,33 +59,15 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private final Map<Integer, AssociationInfo> mIdMap;
+    private final Map<Integer, AssociationInfo> mIdMap = new HashMap<>();
     @GuardedBy("mLock")
-    private final Map<MacAddress, Set<Integer>> mAddressMap;
+    private final Map<MacAddress, Set<Integer>> mAddressMap = new HashMap<>();
     @GuardedBy("mLock")
     private final SparseArray<List<AssociationInfo>> mCachedPerUser = new SparseArray<>();
 
     @GuardedBy("mListeners")
     private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
 
-    AssociationStoreImpl(Collection<AssociationInfo> associations) {
-        synchronized (mLock) {
-            final int size = associations.size();
-            mIdMap = new HashMap<>(size);
-            mAddressMap = new HashMap<>(size);
-
-            for (AssociationInfo association : associations) {
-                final int id = association.getId();
-                mIdMap.put(id, association);
-
-                final MacAddress address = association.getDeviceMacAddress();
-                if (address != null) {
-                    mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
-                }
-            }
-        }
-    }
-
     void addAssociation(@NonNull AssociationInfo association) {
         final int id = association.getId();
 
@@ -136,12 +119,13 @@
 
             // Update the ID-to-Association map.
             mIdMap.put(id, updated);
+            // Invalidate the corresponding user cache entry.
+            invalidateCacheForUserLocked(current.getUserId());
 
             // Update the MacAddress-to-List<Association> map if needed.
             final MacAddress updatedAddress = updated.getDeviceMacAddress();
             final MacAddress currentAddress = current.getDeviceMacAddress();
-            macAddressChanged = Objects.equals(
-                    current.getDeviceMacAddress(), updated.getDeviceMacAddress());
+            macAddressChanged = Objects.equals(currentAddress, updatedAddress);
             if (macAddressChanged) {
                 if (currentAddress != null) {
                     mAddressMap.get(currentAddress).remove(id);
@@ -228,11 +212,9 @@
             final Set<Integer> ids = mAddressMap.get(address);
             if (ids == null) return Collections.emptyList();
 
-            final List<AssociationInfo> associations = new ArrayList<>();
-            for (AssociationInfo association : mIdMap.values()) {
-                if (address.equals(association.getDeviceMacAddress())) {
-                    associations.add(association);
-                }
+            final List<AssociationInfo> associations = new ArrayList<>(ids.size());
+            for (Integer id : ids) {
+                associations.add(mIdMap.get(id));
             }
 
             return Collections.unmodifiableList(associations);
@@ -278,25 +260,41 @@
         synchronized (mListeners) {
             for (OnChangeListener listener : mListeners) {
                 listener.onAssociationChanged(changeType, association);
-
-                switch (changeType) {
-                    case CHANGE_TYPE_ADDED:
-                        listener.onAssociationAdded(association);
-                        break;
-
-                    case CHANGE_TYPE_REMOVED:
-                        listener.onAssociationRemoved(association);
-                        break;
-
-                    case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
-                        listener.onAssociationUpdated(association, true);
-                        break;
-
-                    case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
-                        listener.onAssociationUpdated(association, false);
-                        break;
-                }
             }
         }
     }
+
+    void setAssociations(Collection<AssociationInfo> allAssociations) {
+        if (DEBUG) {
+            Log.i(TAG, "setAssociations() n=" + allAssociations.size());
+            final StringJoiner stringJoiner = new StringJoiner(", ");
+            allAssociations.forEach(assoc -> stringJoiner.add(assoc.toShortString()));
+            Log.v(TAG, "  associations=" + stringJoiner);
+        }
+        synchronized (mLock) {
+            setAssociationsLocked(allAssociations);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void setAssociationsLocked(Collection<AssociationInfo> associations) {
+        clearLocked();
+
+        for (AssociationInfo association : associations) {
+            final int id = association.getId();
+            mIdMap.put(id, association);
+
+            final MacAddress address = association.getDeviceMacAddress();
+            if (address != null) {
+                mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void clearLocked() {
+        mIdMap.clear();
+        mAddressMap.clear();
+        mCachedPerUser.clear();
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
new file mode 100644
index 0000000..be1bc79
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -0,0 +1,317 @@
+/*
+ * 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.companion;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.PerUser;
+import com.android.internal.util.CollectionUtils;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manages communication with companion applications via
+ * {@link android.companion.ICompanionDeviceService} interface, including "connecting" (binding) to
+ * the services, maintaining the connection (the binding), and invoking callback methods such as
+ * {@link CompanionDeviceService#onDeviceAppeared(AssociationInfo)} and
+ * {@link CompanionDeviceService#onDeviceDisappeared(AssociationInfo)} in the application process.
+ *
+ * <p>
+ * The following is the list of the APIs provided by {@link CompanionApplicationController} (to be
+ * utilized by {@link CompanionDeviceManagerService}):
+ * <ul>
+ * <li> {@link #bindCompanionApplication(int, String)}
+ * <li> {@link #unbindCompanionApplication(int, String)}
+ * <li> {@link #notifyCompanionApplicationDeviceAppeared(AssociationInfo)}
+ * <li> {@link #notifyCompanionApplicationDeviceDisappeared(AssociationInfo)}
+ * <li> {@link #isCompanionApplicationBound(int, String)}
+ * <li> {@link #isRebindingCompanionApplicationScheduled(int, String)}
+ * </ul>
+ *
+ * @see CompanionDeviceService
+ * @see android.companion.ICompanionDeviceService
+ * @see CompanionDeviceServiceConnector
+ */
+@SuppressLint("LongLogTag")
+class CompanionApplicationController {
+    static final boolean DEBUG = false;
+    private static final String TAG = "CompanionDevice_ApplicationController";
+
+    private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec
+
+    interface Callback {
+        /**
+         * @return {@code true} if should schedule rebinding.
+         *         {@code false} if we do not need to rebind.
+         */
+        boolean onCompanionApplicationBindingDied(
+                @UserIdInt int userId, @NonNull String packageName);
+
+        /**
+         * Callback after timeout for previously scheduled rebind has passed.
+         */
+        void onRebindCompanionApplicationTimeout(
+                @UserIdInt int userId, @NonNull String packageName);
+    }
+
+    private final @NonNull Context mContext;
+    private final @NonNull Callback mCallback;
+    private final @NonNull CompanionServicesRegister mCompanionServicesRegister;
+    @GuardedBy("mBoundCompanionApplications")
+    private final @NonNull AndroidPackageMap<List<CompanionDeviceServiceConnector>>
+            mBoundCompanionApplications;
+    private final @NonNull AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications;
+
+    CompanionApplicationController(Context context, Callback callback) {
+        mContext = context;
+        mCallback = callback;
+        mCompanionServicesRegister = new CompanionServicesRegister();
+        mBoundCompanionApplications = new AndroidPackageMap<>();
+        mScheduledForRebindingCompanionApplications = new AndroidPackageMap<>();
+    }
+
+    void onPackagesChanged(@UserIdInt int userId) {
+        mCompanionServicesRegister.invalidate(userId);
+    }
+
+    void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
+        if (DEBUG) Log.i(TAG, "bind() u" + userId + "/" + packageName);
+
+        final List<ComponentName> companionServices =
+                mCompanionServicesRegister.forPackage(userId, packageName);
+        final List<CompanionDeviceServiceConnector> serviceConnectors;
+
+        synchronized (mBoundCompanionApplications) {
+            if (mBoundCompanionApplications.containsValueForPackage(userId, packageName)) {
+                if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is ALREADY bound.");
+                return;
+            }
+
+            serviceConnectors = CollectionUtils.map(companionServices, componentName ->
+                            new CompanionDeviceServiceConnector(mContext, userId, componentName));
+            mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
+        }
+
+        // The first connector in the list is always the primary connector: set a listener to it.
+        serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
+
+        // Now "bind" all the connectors: the primary one and the rest of them.
+        for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
+            serviceConnector.connect();
+        }
+    }
+
+    void unbindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
+        if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName);
+
+        final List<CompanionDeviceServiceConnector> serviceConnectors;
+        synchronized (mBoundCompanionApplications) {
+            serviceConnectors = mBoundCompanionApplications.removePackage(userId, packageName);
+        }
+        if (serviceConnectors == null) {
+            if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is NOT bound");
+            return;
+        }
+
+        for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
+            serviceConnector.postUnbind();
+        }
+    }
+
+    boolean isCompanionApplicationBound(@UserIdInt int userId, @NonNull String packageName) {
+        synchronized (mBoundCompanionApplications) {
+            return mBoundCompanionApplications.containsValueForPackage(userId, packageName);
+        }
+    }
+
+    private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName) {
+        mScheduledForRebindingCompanionApplications.setValueForPackage(userId, packageName, true);
+
+        Handler.getMain().postDelayed(() ->
+                onRebindingCompanionApplicationTimeout(userId, packageName), REBIND_TIMEOUT);
+    }
+
+    boolean isRebindingCompanionApplicationScheduled(
+            @UserIdInt int userId, @NonNull String packageName) {
+        return mScheduledForRebindingCompanionApplications
+                .containsValueForPackage(userId, packageName);
+    }
+
+    private void onRebindingCompanionApplicationTimeout(
+            @UserIdInt int userId, @NonNull String packageName) {
+        mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
+
+        mCallback.onRebindCompanionApplicationTimeout(userId, packageName);
+    }
+
+    void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
+        final int userId = association.getUserId();
+        final String packageName = association.getPackageName();
+        if (DEBUG) {
+            Log.i(TAG, "notifyDevice_Appeared() id=" + association.getId() + " u" + userId
+                    + "/" + packageName);
+        }
+
+        final CompanionDeviceServiceConnector primaryServiceConnector =
+                getPrimaryServiceConnector(userId, packageName);
+        if (primaryServiceConnector == null) {
+            if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is NOT bound.");
+            return;
+        }
+
+        primaryServiceConnector.postOnDeviceAppeared(association);
+    }
+
+    void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
+        final int userId = association.getUserId();
+        final String packageName = association.getPackageName();
+        if (DEBUG) {
+            Log.i(TAG, "notifyDevice_Disappeared() id=" + association.getId() + " u" + userId
+                    + "/" + packageName);
+        }
+
+        final CompanionDeviceServiceConnector primaryServiceConnector =
+                getPrimaryServiceConnector(userId, packageName);
+        if (primaryServiceConnector == null) {
+            if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is NOT bound.");
+            return;
+        }
+
+        primaryServiceConnector.postOnDeviceDisappeared(association);
+    }
+
+    private void onPrimaryServiceBindingDied(@UserIdInt int userId, @NonNull String packageName) {
+        if (DEBUG) Log.i(TAG, "onPrimaryServiceBindingDied() u" + userId + "/" + packageName);
+
+        // First: mark as NOT bound.
+        synchronized (mBoundCompanionApplications) {
+            mBoundCompanionApplications.removePackage(userId, packageName);
+        }
+
+        // Second: invoke callback, schedule rebinding if needed.
+        final boolean shouldScheduleRebind =
+                mCallback.onCompanionApplicationBindingDied(userId, packageName);
+        if (shouldScheduleRebind) {
+            scheduleRebinding(userId, packageName);
+        }
+    }
+
+    private @Nullable CompanionDeviceServiceConnector getPrimaryServiceConnector(
+            @UserIdInt int userId, @NonNull String packageName) {
+        final List<CompanionDeviceServiceConnector> connectors;
+        synchronized (mBoundCompanionApplications) {
+            connectors = mBoundCompanionApplications.getValueForPackage(userId, packageName);
+        }
+        return connectors != null ? connectors.get(0) : null;
+    }
+
+    private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> {
+        @Override
+        public synchronized @NonNull Map<String, List<ComponentName>> forUser(
+                @UserIdInt int userId) {
+            return super.forUser(userId);
+        }
+
+        synchronized @NonNull List<ComponentName> forPackage(
+                @UserIdInt int userId, @NonNull String packageName) {
+            return forUser(userId).getOrDefault(packageName, Collections.emptyList());
+        }
+
+        synchronized @NonNull ComponentName primaryForPackage(
+                @UserIdInt int userId, @NonNull String packageName) {
+            // The primary service is always at the head of the list.
+            return forPackage(userId, packageName).get(0);
+        }
+
+        synchronized void invalidate(@UserIdInt int userId) {
+            remove(userId);
+        }
+
+        @Override
+        protected final @NonNull Map<String, List<ComponentName>> create(@UserIdInt int userId) {
+            return PackageUtils.getCompanionServicesForUser(mContext, userId);
+        }
+    }
+
+    /**
+     * Associates an Android package (defined by userId + packageName) with a value of type T.
+     */
+    private static class AndroidPackageMap<T> extends SparseArray<Map<String, T>> {
+
+        void setValueForPackage(
+                @UserIdInt int userId, @NonNull String packageName, @NonNull T value) {
+            Map<String, T> forUser = get(userId);
+            if (forUser == null) {
+                forUser = /* Map<String, T> */ new HashMap();
+                put(userId, forUser);
+            }
+
+            forUser.put(packageName, value);
+        }
+
+        boolean containsValueForPackage(@UserIdInt int userId, @NonNull String packageName) {
+            final Map<String, ?> forUser = get(userId);
+            return forUser != null && forUser.containsKey(packageName);
+        }
+
+        T getValueForPackage(@UserIdInt int userId, @NonNull String packageName) {
+            final Map<String, T> forUser = get(userId);
+            return forUser != null ? forUser.get(packageName) : null;
+        }
+
+        T removePackage(@UserIdInt int userId, @NonNull String packageName) {
+            final Map<String, T> forUser = get(userId);
+            if (forUser == null) return null;
+            return forUser.remove(packageName);
+        }
+
+        void dump() {
+            if (size() == 0) {
+                Log.d(TAG, "<empty>");
+                return;
+            }
+
+            for (int i = 0; i < size(); i++) {
+                final int userId = keyAt(i);
+                final Map<String, T> forUser = get(userId);
+                if (forUser.isEmpty()) {
+                    Log.d(TAG, "u" + userId + ": <empty>");
+                }
+
+                for (Map.Entry<String, T> packageValue : forUser.entrySet()) {
+                    final String packageName = packageValue.getKey();
+                    final T value = packageValue.getValue();
+                    Log.d(TAG, "u" + userId + "\\" + packageName + " -> " + value);
+                }
+            }
+        }
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 5aa1c93..cfd3798 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -19,7 +19,7 @@
 
 import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
 import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
-import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
 import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
 import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -77,11 +77,13 @@
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
 import android.net.MacAddress;
 import android.net.NetworkPolicyManager;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.Message;
 import android.os.Parcel;
 import android.os.PowerWhitelistManager;
 import android.os.RemoteCallbackList;
@@ -149,6 +151,9 @@
     private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
     private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
 
+    private static final long ASSOCIATION_CLEAN_UP_TIME_WINDOW =
+            90L * 24 * 60 * 60 * 1000; // 3 months
+
     private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     static {
         sDateFormat.setTimeZone(TimeZone.getDefault());
@@ -156,7 +161,7 @@
 
     // Persistent data store for all Associations.
     private PersistentDataStore mPersistentStore;
-    private AssociationStoreImpl mAssociationStore;
+    private final AssociationStoreImpl mAssociationStore = new AssociationStoreImpl();
     private AssociationRequestsProcessor mAssociationRequestsProcessor;
 
     private PowerWhitelistManager mPowerWhitelistManager;
@@ -179,8 +184,10 @@
             new ArrayMap<>();
     private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
             new RemoteCallbackList<>();
+    private final CompanionDeviceManagerServiceInternal mLocalService = new LocalService(this);
 
     final Handler mMainHandler = Handler.getMain();
+    private final PersistUserStateHandler mUserPersistenceHandler = new PersistUserStateHandler();
     private CompanionDevicePresenceController mCompanionDevicePresenceController;
 
     /**
@@ -208,21 +215,15 @@
         mPermissionControllerManager = requireNonNull(
                 context.getSystemService(PermissionControllerManager.class));
         mUserManager = context.getSystemService(UserManager.class);
+
+        LocalServices.addService(CompanionDeviceManagerServiceInternal.class, mLocalService);
     }
 
     @Override
     public void onStart() {
         mPersistentStore = new PersistentDataStore();
-        final Set<AssociationInfo> allAssociations = new ArraySet<>();
 
-        synchronized (mPreviouslyUsedIds) {
-            // The data is stored in DE directories, so we can read the data for all users now
-            // (which would not be possible if the data was stored to CE directories).
-            mPersistentStore.readStateForUsers(
-                    mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
-        }
-
-        mAssociationStore = new AssociationStoreImpl(allAssociations);
+        loadAssociationsFromDisk();
         mAssociationStore.registerListener(this);
 
         mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
@@ -233,6 +234,18 @@
         publishBinderService(Context.COMPANION_DEVICE_SERVICE, impl);
     }
 
+    void loadAssociationsFromDisk() {
+        final Set<AssociationInfo> allAssociations = new ArraySet<>();
+        synchronized (mPreviouslyUsedIds) {
+            // The data is stored in DE directories, so we can read the data for all users now
+            // (which would not be possible if the data was stored to CE directories).
+            mPersistentStore.readStateForUsers(
+                    mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
+        }
+
+        mAssociationStore.setAssociations(allAssociations);
+    }
+
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
@@ -250,6 +263,9 @@
             } else {
                 Slog.w(LOG_TAG, "No BluetoothAdapter available");
             }
+        } else if (phase == PHASE_BOOT_COMPLETED) {
+            // Run the Association CleanUp job service daily.
+            AssociationCleanUpService.schedule(getContext());
         }
     }
 
@@ -294,6 +310,24 @@
         return association;
     }
 
+    // Revoke associations if the selfManaged companion device does not connect for 3
+    // months for specific profile.
+    private void associationCleanUp(String profile) {
+        for (AssociationInfo ai : mAssociationStore.getAssociations()) {
+            if (ai.isSelfManaged()
+                    && profile.equals(ai.getDeviceProfile())
+                    && System.currentTimeMillis() - ai.getLastTimeConnectedMs()
+                    >= ASSOCIATION_CLEAN_UP_TIME_WINDOW) {
+                Slog.d(LOG_TAG, "Removing the association for associationId: "
+                        + ai.getId()
+                        + " due to the device does not connect for 3 months."
+                        + " Current time: "
+                        + new Date(System.currentTimeMillis()));
+                disassociateInternal(ai.getId());
+            }
+        }
+    }
+
     void maybeGrantAutoRevokeExemptions() {
         Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()");
         PackageManager pm = getContext().getPackageManager();
@@ -335,9 +369,8 @@
 
         final List<AssociationInfo> updatedAssociations =
                 mAssociationStore.getAssociationsForUser(userId);
-        final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
-        BackgroundThread.getHandler().post(() ->
-                mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser));
+
+        mUserPersistenceHandler.postPersistUserState(userId);
 
         // Notify listeners if ADDED, REMOVED or UPDATED_ADDRESS_CHANGED.
         // Do NOT notify when UPDATED_ADDRESS_UNCHANGED, which means a minor tweak in association's
@@ -350,6 +383,13 @@
         restartBleScan();
     }
 
+    private void persistStateForUser(@UserIdInt int userId) {
+        final List<AssociationInfo> updatedAssociations =
+                mAssociationStore.getAssociationsForUser(userId);
+        final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
+        mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser);
+    }
+
     private void notifyListeners(
             @UserIdInt int userId, @NonNull List<AssociationInfo> associations) {
         mListeners.broadcast((listener, callbackUserId) -> {
@@ -573,8 +613,13 @@
                 return;
             }
 
+            AssociationInfo updatedAssociationInfo = AssociationInfo.builder(association)
+                    .setLastTimeConnected(System.currentTimeMillis())
+                    .build();
+            mAssociationStore.updateAssociation(updatedAssociationInfo);
+
             mCompanionDevicePresenceController.onDeviceNotifyAppeared(
-                    association, getContext(), mMainHandler);
+                    updatedAssociationInfo, getContext(), mMainHandler);
         }
 
         @Override
@@ -621,8 +666,10 @@
                         + " for user " + userId));
             }
 
-            association.setNotifyOnDeviceNearby(active);
-            mAssociationStore.updateAssociation(association);
+            AssociationInfo updatedAssociationInfo = AssociationInfo.builder(association)
+                    .setNotifyOnDeviceNearby(active)
+                    .build();
+            mAssociationStore.updateAssociation(updatedAssociationInfo);
         }
 
         @Override
@@ -735,10 +782,17 @@
         final long timestamp = System.currentTimeMillis();
 
         final AssociationInfo association = new AssociationInfo(id, userId, packageName,
-                macAddress, displayName, deviceProfile, selfManaged, false, timestamp);
+                macAddress, displayName, deviceProfile, selfManaged, false, timestamp,
+                Long.MAX_VALUE);
         Slog.i(LOG_TAG, "New CDM association created=" + association);
         mAssociationStore.addAssociation(association);
 
+        // If the "Device Profile" is specified, make the companion application a holder of the
+        // corresponding role.
+        if (deviceProfile != null) {
+            addRoleHolderForAssociation(getContext(), association);
+        }
+
         updateSpecialAccessPermissionForAssociatedPackage(association);
 
         return association;
@@ -882,14 +936,8 @@
 
         exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
 
-        if (!association.isSelfManaged()) {
-            if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddressAsString())) {
-                addRoleHolderForAssociation(getContext(), association);
-            }
-
-            if (association.isNotifyOnDeviceNearby()) {
-                restartBleScan();
-            }
+        if (association.isNotifyOnDeviceNearby()) {
+            restartBleScan();
         }
     }
 
@@ -935,19 +983,7 @@
 
     void onDeviceConnected(String address) {
         Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
-
         mCurrentlyConnectedDevices.add(address);
-
-        for (AssociationInfo association : mAssociationStore.getAssociationsByAddress(address)) {
-            if (association.getDeviceProfile() != null) {
-                Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
-                        + " to " + association.getPackageName()
-                        + " due to device connected: " + association.getDeviceMacAddress());
-
-                addRoleHolderForAssociation(getContext(), association);
-            }
-        }
-
         onDeviceNearby(address);
     }
 
@@ -1169,7 +1205,7 @@
         } else {
             scanner.startScan(
                     filters,
-                    new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+                    new ScanSettings.Builder().setScanMode(SCAN_MODE_LOW_POWER).build(),
                     mBleScanCallback);
         }
     }
@@ -1297,4 +1333,60 @@
 
         return Collections.unmodifiableMap(copy);
     }
+
+    private final class LocalService extends CompanionDeviceManagerServiceInternal {
+        private final CompanionDeviceManagerService mService;
+
+        LocalService(CompanionDeviceManagerService service) {
+            mService = service;
+        }
+
+        @Override
+        public void associationCleanUp(String profile) {
+            mService.associationCleanUp(profile);
+        }
+    }
+
+    /**
+     * This method must only be called from {@link CompanionDeviceShellCommand} for testing
+     * purposes only!
+     */
+    void persistState() {
+        mUserPersistenceHandler.clearMessages();
+        for (UserInfo user : mUserManager.getAliveUsers()) {
+            persistStateForUser(user.id);
+        }
+    }
+
+    /**
+     * This class is dedicated to handling requests to persist user state.
+     */
+    @SuppressLint("HandlerLeak")
+    private class PersistUserStateHandler extends Handler {
+        PersistUserStateHandler() {
+            super(BackgroundThread.get().getLooper());
+        }
+
+        /**
+         * Persists user state unless there is already an outstanding request for the given user.
+         */
+        synchronized void postPersistUserState(@UserIdInt int userId) {
+            if (!hasMessages(userId)) {
+                sendMessage(obtainMessage(userId));
+            }
+        }
+
+        /**
+         * Clears *ALL* outstanding persist requests for *ALL* users.
+         */
+        synchronized void clearMessages() {
+            removeCallbacksAndMessages(null);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            final int userId = msg.what;
+            persistStateForUser(userId);
+        }
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
new file mode 100644
index 0000000..326fefe
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+/**
+ * Companion Device Manager Local System Service Interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class CompanionDeviceManagerServiceInternal {
+    /**
+     * @see CompanionDeviceManagerService#associationCleanUp
+     */
+    public abstract void associationCleanUp(String profile);
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index 4447684..fc66817 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -48,7 +48,7 @@
 public class CompanionDevicePresenceController {
     private static final String LOG_TAG = "CompanionDevicePresenceController";
     PerUser<ArrayMap<String, List<BoundService>>> mBoundServices;
-    private static final String META_DATA_KEY_PRIMARY = "primary";
+    private static final String META_DATA_KEY_PRIMARY = "android.companion.primary";
     private final CompanionDeviceManagerService mService;
 
     public CompanionDevicePresenceController(CompanionDeviceManagerService service) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
new file mode 100644
index 0000000..777917c
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -0,0 +1,119 @@
+/*
+ * 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.companion;
+
+import static android.content.Context.BIND_IMPORTANT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceService;
+import android.companion.ICompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.infra.ServiceConnector;
+
+/**
+ * Manages a connection (binding) to an instance of {@link CompanionDeviceService} running in the
+ * application process.
+ */
+@SuppressLint("LongLogTag")
+class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDeviceService> {
+    private static final String TAG = "CompanionDevice_ServiceConnector";
+    private static final boolean DEBUG = false;
+    private static final int BINDING_FLAGS = BIND_IMPORTANT;
+
+    /** Listener for changes to the state of the {@link CompanionDeviceServiceConnector}  */
+    interface Listener {
+        void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
+    }
+
+    private final @UserIdInt int mUserId;
+    private final @NonNull ComponentName mComponentName;
+    private @Nullable Listener mListener;
+
+    CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
+            @NonNull ComponentName componentName) {
+        super(context, buildIntent(componentName), BINDING_FLAGS, userId, null);
+        mUserId = userId;
+        mComponentName = componentName;
+    }
+
+    void setListener(@Nullable Listener listener) {
+        mListener = listener;
+    }
+
+    void postOnDeviceAppeared(@NonNull AssociationInfo associationInfo) {
+        post(companionService -> companionService.onDeviceAppeared(associationInfo));
+    }
+
+    void postOnDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
+        post(companionService -> companionService.onDeviceDisappeared(associationInfo));
+    }
+
+    /**
+     * Post "unbind" job, which will run *after* all previously posted jobs complete.
+     *
+     * IMPORTANT: use this method instead of invoking {@link ServiceConnector#unbind()} directly,
+     * because the latter may cause previously posted callback, such as
+     * {@link ICompanionDeviceService#onDeviceDisappeared(AssociationInfo)} to be dropped.
+     */
+    void postUnbind() {
+        post(it -> unbind());
+    }
+
+    @Override
+    protected void onServiceConnectionStatusChanged(
+            @NonNull ICompanionDeviceService service, boolean isConnected) {
+        if (DEBUG) {
+            Log.d(TAG, "onServiceConnection_StatusChanged() " + mComponentName.toShortString()
+                    + " connected=" + isConnected);
+        }
+    }
+
+    @Override
+    public void onBindingDied(@NonNull ComponentName name) {
+        // IMPORTANT: call super!
+        super.onBindingDied(name);
+
+        if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
+
+        mListener.onBindingDied(mUserId, mComponentName.getPackageName());
+    }
+
+    @Override
+    protected ICompanionDeviceService binderAsInterface(@NonNull IBinder service) {
+        return ICompanionDeviceService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        // Do NOT auto-disconnect.
+        return -1;
+    }
+
+    private static @NonNull Intent buildIntent(@NonNull ComponentName componentName) {
+        return new Intent(CompanionDeviceService.SERVICE_INTERFACE)
+                .setComponent(componentName);
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 5c0571d..f2e66077 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -73,13 +73,9 @@
                 }
                 break;
 
-                case "simulate_connect": {
-                    mService.onDeviceConnected(getNextArgRequired());
-                }
-                break;
-
-                case "simulate_disconnect": {
-                    mService.onDeviceDisconnected(getNextArgRequired());
+                case "clear-association-memory-cache": {
+                    mService.persistState();
+                    mService.loadAssociationsFromDisk();
                 }
                 break;
 
@@ -110,5 +106,8 @@
         pw.println("      Create a new Association.");
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
+        pw.println("  clear-association-memory-cache");
+        pw.println("      Clear the in-memory association cache and reload all association "
+                + "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
     }
 }
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
new file mode 100644
index 0000000..6055a81
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.companion;
+
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.util.FunctionalUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+final class DataStoreUtils {
+
+    private static final String LOG_TAG = DataStoreUtils.class.getSimpleName();
+
+    static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        return parser.getEventType() == START_TAG && tag.equals(parser.getName());
+    }
+
+    static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        return parser.getEventType() == END_TAG && tag.equals(parser.getName());
+    }
+
+    /**
+     * Creates {@link AtomicFile} object that represents the back-up for the given user.
+     *
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     *
+     * @param userId              the userId to retrieve the storage file
+     * @param fileName         the storage file name
+     * @return an AtomicFile for the user
+     */
+    @NonNull
+    static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) {
+        return new AtomicFile(getBaseStorageFileForUser(userId, fileName));
+    }
+
+    @NonNull
+    private static File getBaseStorageFileForUser(@UserIdInt int userId, String fileName) {
+        return new File(Environment.getDataSystemDeDirectory(userId), fileName);
+    }
+
+    /**
+     * Writing to file could fail, for example, if the user has been recently removed and so was
+     * their DE (/data/system_de/<user-id>/) directory.
+     */
+    static void writeToFileSafely(@NonNull AtomicFile file,
+            @NonNull FunctionalUtils.ThrowingConsumer<FileOutputStream> consumer) {
+        try {
+            file.write(consumer);
+        } catch (Exception e) {
+            Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+        }
+    }
+
+    private DataStoreUtils() {
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
new file mode 100644
index 0000000..fcb14a4
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -0,0 +1,126 @@
+/*
+ * 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.companion;
+
+import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
+import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.CompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility methods for working with {@link PackageInfo}-s.
+ */
+final class PackageUtils {
+    private static final Intent COMPANION_SERVICE_INTENT =
+            new Intent(CompanionDeviceService.SERVICE_INTERFACE);
+    private static final String META_DATA_PRIMARY_TAG = "android.companion.primary";
+
+    static @Nullable PackageInfo getPackageInfo(@NonNull Context context,
+            @UserIdInt int userId, @NonNull String packageName) {
+        final PackageManager pm = context.getPackageManager();
+        final PackageInfoFlags flags = PackageInfoFlags.of(GET_PERMISSIONS | GET_CONFIGURATIONS);
+        return Binder.withCleanCallingIdentity(() ->
+                pm.getPackageInfoAsUser(packageName, flags , userId));
+    }
+
+    static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
+            @UserIdInt int userId, @NonNull String packageName) {
+        final boolean requested = ArrayUtils.contains(
+                getPackageInfo(context, userId, packageName).reqFeatures,
+                FEATURE_COMPANION_DEVICE_SETUP);
+
+        if (requested) {
+            throw new IllegalStateException("Must declare uses-feature "
+                    + FEATURE_COMPANION_DEVICE_SETUP
+                    + " in manifest to use this API");
+        }
+    }
+
+    /**
+     * @return list of {@link CompanionDeviceService}-s per package for a given user.
+     *         Services marked as "primary" would always appear at the head of the lists, *before*
+     *         all non-primary services.
+     */
+    static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
+            @NonNull Context context, @UserIdInt int userId) {
+        final PackageManager pm = context.getPackageManager();
+        final ResolveInfoFlags flags = ResolveInfoFlags.of(GET_META_DATA);
+        final List<ResolveInfo> companionServices =
+                pm.queryIntentServicesAsUser(COMPANION_SERVICE_INTENT, flags, userId);
+
+        final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>();
+
+        for (ResolveInfo resolveInfo : companionServices) {
+            final ServiceInfo service = resolveInfo.serviceInfo;
+
+            final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE
+                    .equals(resolveInfo.serviceInfo.permission);
+            if (!requiresPermission) {
+                Slog.w(LOG_TAG, "CompanionDeviceService "
+                        + service.getComponentName().flattenToShortString() + " must require "
+                        + "android.permission.BIND_COMPANION_DEVICE_SERVICE");
+                continue;
+            }
+
+            // Use LinkedList, because we'll need to prepend "primary" services, while appending the
+            // other (non-primary) services to the list.
+            final LinkedList<ComponentName> services =
+                    (LinkedList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent(
+                            service.packageName, it -> new LinkedList<>());
+
+            final ComponentName componentName = service.getComponentName();
+            if (isPrimaryCompanionDeviceService(service)) {
+                // "Primary" service should be at the head of the list.
+                services.addFirst(componentName);
+            } else {
+                services.addLast(componentName);
+            }
+        }
+
+        return packageNameToServiceInfoList;
+    }
+
+    private static boolean isPrimaryCompanionDeviceService(ServiceInfo service) {
+        return service.metaData != null && service.metaData.getBoolean(META_DATA_PRIMARY_TAG);
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 97ec3bb..da33b44 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -25,9 +25,10 @@
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
-
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,7 +45,6 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
-import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -53,7 +53,6 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashSet;
@@ -67,17 +66,20 @@
  * The class responsible for persisting Association records and other related information (such as
  * previously used IDs) to a disk, and reading the data back from the disk.
  *
- * Before Android T the data was stored to `companion_device_manager_associations.xml` file in
- * {@link Environment#getUserSystemDirectory(int)}
- * (eg. `/data/system/users/0/companion_device_manager_associations.xml`)
- * @see #getBaseLegacyStorageFileForUser(int)
+ * <p>
+ * Before Android T the data was stored in "companion_device_manager_associations.xml" file in
+ * {@link Environment#getUserSystemDirectory(int) /data/system/user/}.
  *
- * Before Android T the data was stored using the v0 schema.
+ * See {@link #getBaseLegacyStorageFileForUser(int) getBaseLegacyStorageFileForUser()}.
  *
- * @see #readAssociationsV0(TypedXmlPullParser, int, Collection)
- * @see #readAssociationV0(TypedXmlPullParser, int, int, Collection)
+ * <p>
+ * Before Android T the data was stored using the v0 schema. See:
+ * <ul>
+ * <li>{@link #readAssociationsV0(TypedXmlPullParser, int, Collection) readAssociationsV0()}.
+ * <li>{@link #readAssociationV0(TypedXmlPullParser, int, int, Collection) readAssociationV0()}.
+ * </ul>
  *
- * The following snippet is a sample of a the file that is using v0 schema.
+ * The following snippet is a sample of a file that is using v0 schema.
  * <pre>{@code
  * <associations>
  *   <association
@@ -93,24 +95,28 @@
  * </associations>
  * }</pre>
  *
+ * <p>
+ * Since Android T the data is stored to "companion_device_manager.xml" file in
+ * {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
  *
- * Since Android T the data is stored to `companion_device_manager.xml` file in
- * {@link Environment#getDataSystemDeDirectory(int)}.
- * (eg. `/data/system_de/0/companion_device_manager.xml`)
- * @see #getBaseStorageFileForUser(int)
-
+ * See {@link #getBaseStorageFileForUser(int) getBaseStorageFileForUser()}
+ *
+ * <p>
  * Since Android T the data is stored using the v1 schema.
- * In the v1 schema, a list of the previously used IDs is storead along with the association
+ *
+ * In the v1 schema, a list of the previously used IDs is stored along with the association
  * records.
- * V1 schema adds a new optional `display_name` attribute, and makes the `mac_address` attribute
+ *
+ * V1 schema adds a new optional "display_name" attribute, and makes the "mac_address" attribute
  * optional.
+ * <ul>
+ * <li> {@link #CURRENT_PERSISTENCE_VERSION}
+ * <li> {@link #readAssociationsV1(TypedXmlPullParser, int, Collection) readAssociationsV1()}
+ * <li> {@link #readAssociationV1(TypedXmlPullParser, int, Collection) readAssociationV1()}
+ * <li> {@link #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map) readPreviouslyUsedIdsV1()}
+ * </ul>
  *
- * @see #CURRENT_PERSISTENCE_VERSION
- * @see #readAssociationsV1(TypedXmlPullParser, int, Collection)
- * @see #readAssociationV1(TypedXmlPullParser, int, Collection)
- * @see #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map)
- *
- * The following snippet is a sample of a the file that is using v0 schema.
+ * The following snippet is a sample of a file that is using v0 schema.
  * <pre>{@code
  * <state persistence-version="1">
  *     <associations>
@@ -168,6 +174,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_TIME_APPROVED = "time_approved";
+    private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
 
     private static final String LEGACY_XML_ATTR_DEVICE = "device";
 
@@ -202,9 +209,12 @@
             @NonNull Collection<AssociationInfo> associationsOut,
             @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
         Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
+
         final AtomicFile file = getStorageFileForUser(userId);
         if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
 
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
         synchronized (file) {
             File legacyBaseFile = null;
             final AtomicFile readFrom;
@@ -268,6 +278,8 @@
 
         final AtomicFile file = getStorageFileForUser(userId);
         if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
         synchronized (file) {
             persistStateToFileLocked(file, associations, previouslyUsedIdsPerPackage);
         }
@@ -328,13 +340,16 @@
         });
     }
 
+    /**
+     * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
+     * user.
+     *
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     */
     private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
         return mUserIdToStorageFile.computeIfAbsent(userId,
-                u -> new AtomicFile(getBaseStorageFileForUser(userId)));
-    }
-
-    private static @NonNull File getBaseStorageFileForUser(@UserIdInt int userId) {
-        return new File(Environment.getDataSystemDeDirectory(userId), FILE_NAME);
+                u -> createStorageFileForUser(userId, FILE_NAME));
     }
 
     private static @NonNull File getBaseLegacyStorageFileForUser(@UserIdInt int userId) {
@@ -378,7 +393,7 @@
 
         out.add(new AssociationInfo(associationId, userId, appPackage,
                 MacAddress.fromString(deviceAddress), null, profile,
-                /* managedByCompanionApp */false, notify, timeApproved));
+                /* managedByCompanionApp */false, notify, timeApproved, Long.MAX_VALUE));
     }
 
     private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
@@ -408,9 +423,12 @@
         final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED);
         final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
         final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
+        final long lastTimeConnected = readLongAttribute(
+                parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
 
         final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
-                appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved);
+                appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved,
+                lastTimeConnected);
         if (associationInfo != null) {
             out.add(associationInfo);
         }
@@ -464,6 +482,8 @@
         writeBooleanAttribute(
                 serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
         writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
+        writeLongAttribute(
+                serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
 
         serializer.endTag(null, XML_TAG_ASSOCIATION);
     }
@@ -488,16 +508,6 @@
         serializer.endTag(null, XML_TAG_PACKAGE);
     }
 
-    private static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
-            throws XmlPullParserException {
-        return parser.getEventType() == START_TAG && tag.equals(parser.getName());
-    }
-
-    private static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
-            throws XmlPullParserException {
-        return parser.getEventType() == END_TAG && tag.equals(parser.getName());
-    }
-
     private static void requireStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
             throws XmlPullParserException {
         if (isStartOfTag(parser, tag)) return;
@@ -512,23 +522,14 @@
     private static AssociationInfo createAssociationInfoNoThrow(int associationId,
             @UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress,
             @Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged,
-            boolean notify, long timeApproved) {
+            boolean notify, long timeApproved, long lastTimeConnected) {
         AssociationInfo associationInfo = null;
         try {
             associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
-                    displayName, profile, selfManaged, notify, timeApproved);
+                    displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected);
         } catch (Exception e) {
             if (DEBUG) Slog.w(LOG_TAG, "Could not create AssociationInfo", e);
         }
         return associationInfo;
     }
-
-    private static void writeToFileSafely(@NonNull AtomicFile file,
-            @NonNull ThrowingConsumer<FileOutputStream> consumer) {
-        try {
-            file.write(consumer);
-        } catch (Exception e) {
-            Slog.e(LOG_TAG, "Error while writing to file " + file, e);
-        }
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
new file mode 100644
index 0000000..38e5d16
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
@@ -0,0 +1,226 @@
+/*
+ * 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.companion;
+
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readThisListXml;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeListXml;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.SystemDataTransferRequest;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * The class is responsible for reading/writing SystemDataTransferRequest records from/to the disk.
+ *
+ * The following snippet is a sample XML file stored in the disk.
+ * <pre>{@code
+ * <requests>
+ *   <request
+ *     association_id="1"
+ *     is_permission_sync_all_packages="false">
+ *     <list name="permission_sync_packages">
+ *       <string>com.sample.app1</string>
+ *       <string>com.sample.app2</string>
+ *     </list>
+ *   </request>
+ * </requests>
+ * }</pre>
+ */
+public class SystemDataTransferRequestDataStore {
+
+    private static final String LOG_TAG = SystemDataTransferRequestDataStore.class.getSimpleName();
+
+    private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml";
+
+    private static final String XML_TAG_REQUESTS = "requests";
+    private static final String XML_TAG_REQUEST = "request";
+    private static final String XML_TAG_LIST = "list";
+
+    private static final String XML_ATTR_ASSOCIATION_ID = "association_id";
+    private static final String XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES =
+            "is_permission_sync_all_packages";
+    private static final String XML_ATTR_PERMISSION_SYNC_PACKAGES = "permission_sync_packages";
+
+    private final ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
+            new ConcurrentHashMap<>();
+
+    /**
+     * Reads previously persisted data for the given user
+     *
+     * @param userId Android UserID
+     * @return a list of SystemDataTransferRequest
+     */
+    @NonNull
+    List<SystemDataTransferRequest> readRequestsForUser(@UserIdInt int userId) {
+        final AtomicFile file = getStorageFileForUser(userId);
+        Slog.i(LOG_TAG, "Reading SystemDataTransferRequests for user " + userId + " from "
+                + "file=" + file.getBaseFile().getPath());
+
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
+        synchronized (file) {
+            if (!file.getBaseFile().exists()) {
+                Slog.d(LOG_TAG, "File does not exist -> Abort");
+                return Collections.emptyList();
+            }
+            try (FileInputStream in = file.openRead()) {
+                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
+
+                return readRequests(parser);
+            } catch (XmlPullParserException | IOException e) {
+                Slog.e(LOG_TAG, "Error while reading requests file", e);
+                return Collections.emptyList();
+            }
+        }
+    }
+
+    @NonNull
+    private List<SystemDataTransferRequest> readRequests(@NonNull TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        if (!isStartOfTag(parser, XML_TAG_REQUESTS)) {
+            throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS);
+        }
+
+        List<SystemDataTransferRequest> requests = new ArrayList<>();
+
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_REQUESTS)) break;
+            if (isStartOfTag(parser, XML_TAG_REQUEST)) {
+                requests.add(readRequest(parser));
+            }
+        }
+
+        return requests;
+    }
+
+    private SystemDataTransferRequest readRequest(@NonNull TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        if (!isStartOfTag(parser, XML_TAG_REQUEST)) {
+            throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST);
+        }
+
+        final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID);
+        final boolean isPermissionSyncAllPackages = readBooleanAttribute(parser,
+                XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES);
+        parser.nextTag();
+        List<String> permissionSyncPackages = new ArrayList<>();
+        if (isStartOfTag(parser, XML_TAG_LIST)) {
+            parser.nextTag();
+            permissionSyncPackages = readThisListXml(parser, XML_TAG_LIST,
+                    new String[1]);
+        }
+
+        return new SystemDataTransferRequest(associationId, isPermissionSyncAllPackages,
+                permissionSyncPackages);
+    }
+
+    /**
+     * Persisted user's SystemDataTransferRequest data to the disk.
+     *
+     * @param userId   Android UserID
+     * @param requests a list of user's SystemDataTransferRequest.
+     */
+    void writeRequestsForUser(@UserIdInt int userId,
+            @NonNull List<SystemDataTransferRequest> requests) {
+        final AtomicFile file = getStorageFileForUser(userId);
+        Slog.i(LOG_TAG, "Writing SystemDataTransferRequests for user " + userId + " to file="
+                + file.getBaseFile().getPath());
+
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
+        synchronized (file) {
+            writeToFileSafely(file, out -> {
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+                serializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+                serializer.startDocument(null, true);
+                writeRequests(serializer, requests);
+                serializer.endDocument();
+            });
+        }
+    }
+
+    private void writeRequests(@NonNull TypedXmlSerializer serializer,
+            @Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
+        serializer.startTag(null, XML_TAG_REQUESTS);
+
+        for (SystemDataTransferRequest request : requests) {
+            writeRequest(serializer, request);
+        }
+
+        serializer.endTag(null, XML_TAG_REQUESTS);
+    }
+
+    private void writeRequest(@NonNull TypedXmlSerializer serializer,
+            @NonNull SystemDataTransferRequest request) throws IOException {
+        serializer.startTag(null, XML_TAG_REQUEST);
+
+        writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId());
+        writeBooleanAttribute(serializer, XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES,
+                request.isPermissionSyncAllPackages());
+        try {
+            writeListXml(request.getPermissionSyncPackages(), XML_ATTR_PERMISSION_SYNC_PACKAGES,
+                    serializer);
+        } catch (XmlPullParserException e) {
+            Slog.e(LOG_TAG, "Error writing permission sync packages into XML. "
+                    + request.getPermissionSyncPackages().toString());
+        }
+
+        serializer.endTag(null, XML_TAG_REQUEST);
+    }
+
+    /**
+     * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
+     * user.
+     *
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     */
+    private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
+        return mUserIdToStorageFile.computeIfAbsent(userId,
+                u -> createStorageFileForUser(userId, FILE_NAME));
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
new file mode 100644
index 0000000..0eb6b8d
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -0,0 +1,436 @@
+/*
+ * 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.companion.presence;
+
+import static android.bluetooth.BluetoothAdapter.ACTION_BLE_STATE_CHANGED;
+import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
+import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
+import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
+import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
+import static android.bluetooth.BluetoothAdapter.STATE_ON;
+import static android.bluetooth.BluetoothAdapter.nameForState;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_FIRST_MATCH;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
+
+import static com.android.server.companion.presence.Utils.btDeviceToString;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.companion.AssociationInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.companion.AssociationStore;
+import com.android.server.companion.AssociationStore.ChangeType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressLint("LongLogTag")
+class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
+
+    /**
+     * When using {@link ScanSettings#SCAN_MODE_LOW_POWER}, it usually takes from 20 seconds to
+     * 2 minutes for the BLE scanner to find advertisements sent from the same device.
+     * On the other hand, {@link android.bluetooth.BluetoothAdapter.LeScanCallback} will report
+     * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST MATCH_LOST} 10 sec after it finds the
+     * advertisement for the first time (add reports
+     * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH FIRST_MATCH}).
+     * To avoid constantly reporting {@link Callback#onBleCompanionDeviceFound(int) onDeviceFound()}
+     * and {@link Callback#onBleCompanionDeviceLost(int) onDeviceLost()} (while the device is
+     * actually present) to its clients, {@link BleCompanionDeviceScanner}, will add 1-minute delay
+     * after it receives {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST MATCH_LOST}.
+     */
+    private static final int NOTIFY_DEVICE_LOST_DELAY = 2 * 60 * 1000; // 2 Min.
+
+    interface Callback {
+        void onBleCompanionDeviceFound(int associationId);
+
+        void onBleCompanionDeviceLost(int associationId);
+    }
+
+    private final @NonNull AssociationStore mAssociationStore;
+    private final @NonNull Callback mCallback;
+    private final @NonNull MainThreadHandler mMainThreadHandler;
+
+    // Non-null after init().
+    private @Nullable BluetoothAdapter mBtAdapter;
+    // Non-null after init() and when BLE is available. Otherwise - null.
+    private @Nullable BluetoothLeScanner mBleScanner;
+    // Only accessed from the Main thread.
+    private boolean mScanning = false;
+
+    BleCompanionDeviceScanner(
+            @NonNull AssociationStore associationStore, @NonNull Callback callback) {
+        mAssociationStore = associationStore;
+        mCallback = callback;
+        mMainThreadHandler = new MainThreadHandler();
+    }
+
+    @MainThread
+    void init(@NonNull Context context, @NonNull BluetoothAdapter btAdapter) {
+        if (DEBUG) Log.i(TAG, "init()");
+
+        if (mBtAdapter != null) {
+            throw new IllegalStateException(getClass().getSimpleName() + " is already initialized");
+        }
+        mBtAdapter = requireNonNull(btAdapter);
+
+        checkBleState();
+        registerBluetoothStateBroadcastReceiver(context);
+
+        mAssociationStore.registerListener(this);
+    }
+
+    @MainThread
+    final void restartScan() {
+        enforceInitialized();
+
+        if (DEBUG) Log.i(TAG , "restartScan()");
+        if (mBleScanner == null) {
+            if (DEBUG) Log.d(TAG, "  > BLE is not available");
+            return;
+        }
+
+        stopScanIfNeeded();
+        startScan();
+    }
+
+    @Override
+    public void onAssociationChanged(@ChangeType int changeType, AssociationInfo association) {
+        // Simply restart scanning.
+        if (Looper.getMainLooper().isCurrentThread()) {
+            restartScan();
+        } else {
+            mMainThreadHandler.post(this::restartScan);
+        }
+    }
+
+    @MainThread
+    private void checkBleState() {
+        enforceInitialized();
+
+        final boolean bleAvailable = isBleAvailable();
+        if (DEBUG) {
+            Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable);
+        }
+        if ((bleAvailable && mBleScanner != null) || (!bleAvailable && mBleScanner == null)) {
+            // Nothing changed.
+            if (DEBUG) Log.i(TAG, "  > BLE status did not change");
+            return;
+        }
+
+        if (bleAvailable) {
+            mBleScanner = mBtAdapter.getBluetoothLeScanner();
+            if (mBleScanner == null) {
+                // Oops, that's a race condition. Can return.
+                return;
+            }
+            if (DEBUG) Log.i(TAG, "  > BLE is now available");
+
+            startScan();
+        } else {
+            if (DEBUG) Log.i(TAG, "  > BLE is now unavailable");
+
+            stopScanIfNeeded();
+            mBleScanner = null;
+        }
+    }
+
+    /**
+     * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private
+     * access level, so it's not accessible from here.
+     */
+    private boolean isBleAvailable() {
+        final int state = mBtAdapter.getLeState();
+        if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state));
+        return state == STATE_ON || state == STATE_BLE_ON;
+    }
+
+    @MainThread
+    private void startScan() {
+        enforceInitialized();
+        // This method should not be called if scan is already in progress.
+        if (mScanning) throw new IllegalStateException("Scan is already in progress.");
+        // Neither should this method be called if the adapter is not available.
+        if (mBleScanner == null) throw new IllegalStateException("BLE is not available.");
+
+        if (DEBUG) Log.i(TAG, "startScan()");
+
+        // Collect MAC addresses from all associations.
+        final Set<String> macAddresses = new HashSet<>();
+        for (AssociationInfo association : mAssociationStore.getAssociations()) {
+            // Beware that BT stack does not consider low-case MAC addresses valid, while
+            // MacAddress.toString() return a low-case String.
+            final String macAddress = association.getDeviceMacAddressAsString();
+            if (macAddress != null) {
+                macAddresses.add(macAddress);
+            }
+        }
+        if (macAddresses.isEmpty()) {
+            if (DEBUG) Log.i(TAG, "  > there are no (associated) devices to Scan for.");
+            return;
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "  > addresses=(n=" + macAddresses.size() + ")"
+                        + "[" + String.join(", ", macAddresses) + "]");
+            }
+        }
+
+        final List<ScanFilter> filters = new ArrayList<>(macAddresses.size());
+        for (String macAddress : macAddresses) {
+            final ScanFilter filter = new ScanFilter.Builder()
+                    .setDeviceAddress(macAddress)
+                    .build();
+            filters.add(filter);
+        }
+
+        mBleScanner.startScan(filters, SCAN_SETTINGS, mScanCallback);
+        mScanning = true;
+    }
+
+    private void stopScanIfNeeded() {
+        enforceInitialized();
+
+        if (DEBUG) Log.i(TAG, "stopScan()");
+        if (!mScanning) {
+            Log.d(TAG, "  > not scanning.");
+            return;
+        }
+
+        mBleScanner.stopScan(mScanCallback);
+        mScanning = false;
+    }
+
+    @MainThread
+    private void notifyDeviceFound(@NonNull BluetoothDevice device) {
+        if (DEBUG) Log.i(TAG, "notifyDevice_Found()" + btDeviceToString(device));
+
+        final List<AssociationInfo> associations =
+                mAssociationStore.getAssociationsByAddress(device.getAddress());
+        if (DEBUG) Log.d(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+
+        for (AssociationInfo association : associations) {
+            mCallback.onBleCompanionDeviceFound(association.getId());
+        }
+    }
+
+    @MainThread
+    private void notifyDeviceLost(@NonNull BluetoothDevice device) {
+        if (DEBUG) Log.i(TAG, "notifyDevice_Lost()" + btDeviceToString(device));
+
+        final List<AssociationInfo> associations =
+                mAssociationStore.getAssociationsByAddress(device.getAddress());
+        if (DEBUG) Log.d(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+
+        for (AssociationInfo association : associations) {
+            mCallback.onBleCompanionDeviceLost(association.getId());
+        }
+    }
+
+    private void registerBluetoothStateBroadcastReceiver(Context context) {
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int prevState = intent.getIntExtra(EXTRA_PREVIOUS_STATE, -1);
+                final int state = intent.getIntExtra(EXTRA_STATE, -1);
+
+                if (DEBUG) {
+                    // The action is either STATE_CHANGED or BLE_STATE_CHANGED.
+                    final String action =
+                            intent.getAction().replace("android.bluetooth.adapter.", "bt.");
+                    Log.d(TAG, "on(Broadcast)Receive() " + action + ": "
+                            + nameForBtState(prevState) + "->" + nameForBtState(state));
+                }
+
+                checkBleState();
+            }
+        };
+
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_STATE_CHANGED);
+        filter.addAction(ACTION_BLE_STATE_CHANGED);
+
+        context.registerReceiver(receiver, filter);
+    }
+
+    private void enforceInitialized() {
+        if (mBtAdapter != null) return;
+        throw new IllegalStateException(getClass().getSimpleName() + " is not initialized");
+    }
+
+    private final ScanCallback mScanCallback = new ScanCallback() {
+        @MainThread
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            final BluetoothDevice device = result.getDevice();
+
+            if (DEBUG) {
+                Log.d(TAG, "onScanResult() " + nameForBleScanCallbackType(callbackType)
+                        + " device=" + btDeviceToString(device));
+                Log.v(TAG, "  > scanResult=" + result);
+
+                final List<AssociationInfo> associations =
+                        mAssociationStore.getAssociationsByAddress(device.getAddress());
+                Log.v(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+            }
+
+            switch (callbackType) {
+                case CALLBACK_TYPE_FIRST_MATCH:
+                    if (mMainThreadHandler.hasNotifyDeviceLostMessages(device)) {
+                        mMainThreadHandler.removeNotifyDeviceLostMessages(device);
+                        return;
+                    }
+
+                    notifyDeviceFound(device);
+                    break;
+
+                case CALLBACK_TYPE_MATCH_LOST:
+                    mMainThreadHandler.sendNotifyDeviceLostDelayedMessage(device);
+                    break;
+
+                default:
+                    Slog.wtf(TAG, "Unexpected callback "
+                            + nameForBleScanCallbackType(callbackType));
+                    break;
+            }
+        }
+
+        @MainThread
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (DEBUG) Log.w(TAG, "onScanFailed() " + nameForBleScanErrorCode(errorCode));
+            mScanning = false;
+        }
+    };
+
+    @SuppressLint("HandlerLeak")
+    private class MainThreadHandler extends Handler {
+        private static final int NOTIFY_DEVICE_LOST = 1;
+
+        MainThreadHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message message) {
+            if  (message.what != NOTIFY_DEVICE_LOST) return;
+
+            final BluetoothDevice device = (BluetoothDevice) message.obj;
+            notifyDeviceLost(device);
+        }
+
+        void sendNotifyDeviceLostDelayedMessage(BluetoothDevice device) {
+            final Message message = obtainMessage(NOTIFY_DEVICE_LOST, device);
+            sendMessageDelayed(message, NOTIFY_DEVICE_LOST_DELAY);
+        }
+
+        boolean hasNotifyDeviceLostMessages(BluetoothDevice device) {
+            return hasEqualMessages(NOTIFY_DEVICE_LOST, device);
+        }
+
+        void removeNotifyDeviceLostMessages(BluetoothDevice device) {
+            removeEqualMessages(NOTIFY_DEVICE_LOST, device);
+        }
+    }
+
+    private static String nameForBtState(int state) {
+        return nameForState(state) + "(" + state + ")";
+    }
+
+    private static String nameForBleScanCallbackType(int callbackType) {
+        final String name;
+        switch (callbackType) {
+            case CALLBACK_TYPE_ALL_MATCHES:
+                name = "ALL_MATCHES";
+                break;
+            case CALLBACK_TYPE_FIRST_MATCH:
+                name = "FIRST_MATCH";
+                break;
+            case CALLBACK_TYPE_MATCH_LOST:
+                name = "MATCH_LOST";
+                break;
+            default:
+                name = "Unknown";
+        }
+        return name + "(" + callbackType + ")";
+    }
+
+    private static String nameForBleScanErrorCode(int errorCode) {
+        final String name;
+        switch (errorCode) {
+            case SCAN_FAILED_ALREADY_STARTED:
+                name = "ALREADY_STARTED";
+                break;
+            case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
+                name = "APPLICATION_REGISTRATION_FAILED";
+                break;
+            case SCAN_FAILED_INTERNAL_ERROR:
+                name = "INTERNAL_ERROR";
+                break;
+            case SCAN_FAILED_FEATURE_UNSUPPORTED:
+                name = "FEATURE_UNSUPPORTED";
+                break;
+            case SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES:
+                name = "OUT_OF_HARDWARE_RESOURCES";
+                break;
+            case SCAN_FAILED_SCANNING_TOO_FREQUENTLY:
+                name = "SCANNING_TOO_FREQUENTLY";
+                break;
+            default:
+                name = "Unknown";
+        }
+        return name + "(" + errorCode + ")";
+    }
+
+    private static final ScanSettings SCAN_SETTINGS = new ScanSettings.Builder()
+            .setCallbackType(CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST)
+            .setScanMode(SCAN_MODE_LOW_POWER)
+            .build();
+}
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
new file mode 100644
index 0000000..dbe866b
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -0,0 +1,166 @@
+/*
+ * 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.companion.presence;
+
+import static com.android.server.companion.presence.Utils.btDeviceToString;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.companion.AssociationInfo;
+import android.net.MacAddress;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.util.Log;
+
+import com.android.server.companion.AssociationStore;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressLint("LongLogTag")
+class BluetoothCompanionDeviceConnectionListener
+        extends BluetoothAdapter.BluetoothConnectionCallback
+        implements AssociationStore.OnChangeListener {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "CompanionDevice_PresenceMonitor_BT";
+
+    interface Callback {
+        void onBluetoothCompanionDeviceConnected(int associationId);
+
+        void onBluetoothCompanionDeviceDisconnected(int associationId);
+    }
+
+    private final @NonNull AssociationStore mAssociationStore;
+    private final @NonNull Callback mCallback;
+    /** A set of ALL connected BT device (not only companion.) */
+    private final @NonNull Map<MacAddress, BluetoothDevice> mAllConnectedDevices = new HashMap<>();
+
+    BluetoothCompanionDeviceConnectionListener(@NonNull AssociationStore associationStore,
+            @NonNull Callback callback) {
+        mAssociationStore = associationStore;
+        mCallback = callback;
+    }
+
+    public void init(@NonNull BluetoothAdapter btAdapter) {
+        if (DEBUG) Log.i(TAG, "init()");
+
+        btAdapter.registerBluetoothConnectionCallback(
+                new HandlerExecutor(Handler.getMain()), /* callback */this);
+        mAssociationStore.registerListener(this);
+    }
+
+    /**
+     * Overrides
+     * {@link BluetoothAdapter.BluetoothConnectionCallback#onDeviceConnected(BluetoothDevice)}.
+     */
+    @Override
+    public void onDeviceConnected(@NonNull BluetoothDevice device) {
+        if (DEBUG) Log.i(TAG, "onDevice_Connected() " + btDeviceToString(device));
+
+        final MacAddress macAddress = MacAddress.fromString(device.getAddress());
+        if (mAllConnectedDevices.put(macAddress, device) != null) {
+            if (DEBUG) Log.w(TAG, "Device " + btDeviceToString(device) + " is already connected.");
+            return;
+        }
+
+        onDeviceConnectivityChanged(device, true);
+    }
+
+    /**
+     * Overrides
+     * {@link BluetoothAdapter.BluetoothConnectionCallback#onDeviceConnected(BluetoothDevice)}.
+     * Also invoked when user turns BT off while the device is connected.
+     */
+    @Override
+    public void onDeviceDisconnected(@NonNull BluetoothDevice device,
+            @DisconnectReason int reason) {
+        if (DEBUG) {
+            Log.i(TAG, "onDevice_Disconnected() " + btDeviceToString(device));
+            Log.d(TAG, "  reason=" + disconnectReasonText(reason));
+        }
+
+        final MacAddress macAddress = MacAddress.fromString(device.getAddress());
+        if (mAllConnectedDevices.remove(macAddress) == null) {
+            if (DEBUG) {
+                Log.w(TAG, "The device wasn't tracked as connected " + btDeviceToString(device));
+            }
+            return;
+        }
+
+        onDeviceConnectivityChanged(device, false);
+    }
+
+    private void onDeviceConnectivityChanged(@NonNull BluetoothDevice device, boolean connected) {
+        final List<AssociationInfo> associations =
+                mAssociationStore.getAssociationsByAddress(device.getAddress());
+
+        if (DEBUG) {
+            Log.d(TAG, "onDevice_ConnectivityChanged() " + btDeviceToString(device)
+                    + " connected=" + connected);
+            if (associations.isEmpty()) {
+                Log.d(TAG, "  > No CDM associations");
+            } else {
+                Log.d(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+            }
+        }
+
+        for (AssociationInfo association : associations) {
+            final int id = association.getId();
+            if (connected) {
+                mCallback.onBluetoothCompanionDeviceConnected(id);
+            } else {
+                mCallback.onBluetoothCompanionDeviceDisconnected(id);
+            }
+        }
+    }
+
+    @Override
+    public void onAssociationAdded(AssociationInfo association) {
+        if (DEBUG) Log.d(TAG, "onAssociation_Added() " + association);
+
+        if (mAllConnectedDevices.containsKey(association.getDeviceMacAddress())) {
+            mCallback.onBluetoothCompanionDeviceConnected(association.getId());
+        }
+    }
+
+    @Override
+    public void onAssociationRemoved(AssociationInfo association) {
+        // Intentionally do nothing: CompanionDevicePresenceMonitor will do all the bookkeeping
+        // required.
+    }
+
+    @Override
+    public void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {
+        if (DEBUG) {
+            Log.d(TAG, "onAssociation_Updated() addrChange=" + addressChanged
+                    + " " + association);
+        }
+
+        if (!addressChanged) {
+            // Don't need to do anything.
+            return;
+        }
+
+        // At the moment CDM does allow changing association addresses, so we will never come here.
+        // This will be implemented when CDM support updating addresses.
+        throw new IllegalArgumentException("Address changes are not supported.");
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/presence/Utils.java b/services/companion/java/com/android/server/companion/presence/Utils.java
new file mode 100644
index 0000000..583b443
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/Utils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.presence;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
+
+/** Utilities for working with Bluetooth and BLE devices. */
+class Utils {
+
+    /**
+     * @return short String representation of {@link BluetoothDevice}.
+     */
+    static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
+        final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+        sb.append(" [name=");
+        final String name = btDevice.getName();
+        if (name != null) {
+            sb.append('\'').append(name).append('\'');
+        } else {
+            sb.append("null");
+        }
+
+        final String alias = btDevice.getAlias();
+        if (alias != null) {
+            sb.append(", alias='").append(alias).append("'");
+        }
+
+        return sb.append(']').toString();
+    }
+
+    private Utils() {
+    }
+}
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 e98b63e..75acf81 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -16,21 +16,29 @@
 
 package com.android.server.companion.virtual;
 
+import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
+import android.companion.virtual.VirtualDeviceManager.ActivityListener;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.Display;
 import android.window.DisplayWindowPolicyController;
 
-import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 
 /**
@@ -38,6 +46,8 @@
  */
 class GenericWindowPolicyController extends DisplayWindowPolicyController {
 
+    private static final String TAG = "VirtualDeviceManager";
+
     /**
      * If required, allow the secure activity to display on remote device since
      * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
@@ -45,11 +55,38 @@
     @ChangeId
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+    @NonNull
+    private final ArraySet<UserHandle> mAllowedUsers;
+    @Nullable
+    private final ArraySet<ComponentName> mAllowedActivities;
+    @Nullable
+    private final ArraySet<ComponentName> mBlockedActivities;
 
-    @NonNull final HashSet<Integer> mRunningUids = new HashSet<>();
+    @NonNull
+    final ArraySet<Integer> mRunningUids = new ArraySet<>();
+    @Nullable private final ActivityListener mActivityListener;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
-    GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
+    /**
+     * Creates a window policy controller that is generic to the different use cases of virtual
+     * device.
+     *
+     * @param windowFlags The window flags that this controller is interested in.
+     * @param systemWindowFlags The system window flags that this controller is interested in.
+     * @param allowedUsers The set of users that are allowed to stream in this display.
+     * @param activityListener Activity listener to listen for activity changes. The display ID
+     *   is not populated in this callback and is always {@link Display#INVALID_DISPLAY}.
+     */
+    GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
+            @NonNull ArraySet<UserHandle> allowedUsers,
+            @Nullable Set<ComponentName> allowedActivities,
+            @Nullable Set<ComponentName> blockedActivities,
+            @NonNull ActivityListener activityListener) {
+        mAllowedUsers = allowedUsers;
+        mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
+        mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
         setInterestedWindowFlags(windowFlags, systemWindowFlags);
+        mActivityListener = activityListener;
     }
 
     @Override
@@ -58,7 +95,7 @@
         final int activityCount = activities.size();
         for (int i = 0; i < activityCount; i++) {
             final ActivityInfo aInfo = activities.get(i);
-            if ((aInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+            if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
                 return false;
             }
         }
@@ -68,33 +105,25 @@
     @Override
     public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
             int systemWindowFlags) {
-        if ((activityInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
-            return false;
-        }
-        if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
-                activityInfo.packageName,
-                UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid))) {
-            // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
-            if ((windowFlags & FLAG_SECURE) != 0) {
-                return false;
-            }
-            if ((systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
-                return false;
-            }
-        }
-        return true;
+        return canContainActivity(activityInfo, windowFlags, systemWindowFlags);
     }
 
     @Override
     public void onTopActivityChanged(ComponentName topActivity, int uid) {
-
+        if (mActivityListener != null) {
+            // Post callback on the main thread so it doesn't block activity launching
+            mHandler.post(() ->
+                    mActivityListener.onTopActivityChanged(Display.INVALID_DISPLAY, topActivity));
+        }
     }
 
     @Override
-    public void onRunningAppsChanged(int[] runningUids) {
+    public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
         mRunningUids.clear();
-        for (int i = 0; i < runningUids.length; i++) {
-            mRunningUids.add(runningUids[i]);
+        mRunningUids.addAll(runningUids);
+        if (mActivityListener != null && mRunningUids.isEmpty()) {
+            // Post callback on the main thread so it doesn't block activity launching
+            mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
         }
     }
 
@@ -105,4 +134,40 @@
     boolean containsUid(int uid) {
         return mRunningUids.contains(uid);
     }
+
+    private boolean canContainActivity(ActivityInfo activityInfo, int windowFlags,
+            int systemWindowFlags) {
+        if ((activityInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+            return false;
+        }
+        final UserHandle activityUser =
+                UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid);
+        if (!mAllowedUsers.contains(activityUser)) {
+            Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser);
+            return false;
+        }
+        if (mBlockedActivities != null
+                && mBlockedActivities.contains(activityInfo.getComponentName())) {
+            Slog.d(TAG,
+                    "Virtual device blocking launch of " + activityInfo.getComponentName());
+            return false;
+        }
+        if (mAllowedActivities != null
+                && !mAllowedActivities.contains(activityInfo.getComponentName())) {
+            Slog.d(TAG,
+                    activityInfo.getComponentName() + " is not in the allowed list.");
+            return false;
+        }
+        if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
+                activityInfo.packageName, activityUser)) {
+            // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
+            if ((windowFlags & FLAG_SECURE) != 0) {
+                return false;
+            }
+            if ((systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
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 067edcc..ae39d7e 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -16,8 +16,11 @@
 
 package com.android.server.companion.virtual;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
@@ -26,25 +29,40 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 /** Controls virtual input devices, including device lifecycle and event dispatch. */
 class InputController {
 
+    private static final String TAG = "VirtualInputController";
+
     private final Object mLock;
 
     /* Token -> file descriptor associations. */
     @VisibleForTesting
     @GuardedBy("mLock")
-    final Map<IBinder, Integer> mInputDeviceFds = new ArrayMap<>();
+    final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>();
 
     private final NativeWrapper mNativeWrapper;
 
+    /**
+     * Because the pointer is a singleton, it can only be targeted at one display at a time. Because
+     * multiple mice could be concurrently registered, mice that are associated with a different
+     * display than the current target display should not be allowed to affect the current target.
+     */
+    @VisibleForTesting int mActivePointerDisplayId;
+
     InputController(@NonNull Object lock) {
         this(lock, new NativeWrapper());
     }
@@ -53,32 +71,39 @@
     InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
         mLock = lock;
         mNativeWrapper = nativeWrapper;
+        mActivePointerDisplayId = Display.INVALID_DISPLAY;
     }
 
     void close() {
         synchronized (mLock) {
-            for (int fd : mInputDeviceFds.values()) {
-                mNativeWrapper.closeUinput(fd);
+            for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
+                mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
             }
-            mInputDeviceFds.clear();
+            mInputDeviceDescriptors.clear();
+            resetMouseValuesLocked();
         }
     }
 
     void createKeyboard(@NonNull String deviceName,
             int vendorId,
             int productId,
-            @NonNull IBinder deviceToken) {
+            @NonNull IBinder deviceToken,
+            int displayId) {
         final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
         if (fd < 0) {
             throw new RuntimeException(
                     "A native error occurred when creating keyboard: " + -fd);
         }
+        final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
         synchronized (mLock) {
-            mInputDeviceFds.put(deviceToken, fd);
+            mInputDeviceDescriptors.put(deviceToken,
+                    new InputDeviceDescriptor(fd, binderDeathRecipient,
+                            InputDeviceDescriptor.TYPE_KEYBOARD, displayId));
         }
         try {
-            deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
         } catch (RemoteException e) {
+            // TODO(b/215608394): remove and close InputDeviceDescriptor
             throw new RuntimeException("Could not create virtual keyboard", e);
         }
     }
@@ -86,18 +111,27 @@
     void createMouse(@NonNull String deviceName,
             int vendorId,
             int productId,
-            @NonNull IBinder deviceToken) {
+            @NonNull IBinder deviceToken,
+            int displayId) {
         final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
         if (fd < 0) {
             throw new RuntimeException(
                     "A native error occurred when creating mouse: " + -fd);
         }
+        final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
         synchronized (mLock) {
-            mInputDeviceFds.put(deviceToken, fd);
+            mInputDeviceDescriptors.put(deviceToken,
+                    new InputDeviceDescriptor(fd, binderDeathRecipient,
+                            InputDeviceDescriptor.TYPE_MOUSE, displayId));
+            final InputManagerInternal inputManagerInternal =
+                    LocalServices.getService(InputManagerInternal.class);
+            inputManagerInternal.setVirtualMousePointerDisplayId(displayId);
+            mActivePointerDisplayId = displayId;
         }
         try {
-            deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
         } catch (RemoteException e) {
+            // TODO(b/215608394): remove and close InputDeviceDescriptor
             throw new RuntimeException("Could not create virtual mouse", e);
         }
     }
@@ -106,6 +140,7 @@
             int vendorId,
             int productId,
             @NonNull IBinder deviceToken,
+            int displayId,
             @NonNull Point screenSize) {
         final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
                 screenSize.y, screenSize.x);
@@ -113,93 +148,177 @@
             throw new RuntimeException(
                     "A native error occurred when creating touchscreen: " + -fd);
         }
+        final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
         synchronized (mLock) {
-            mInputDeviceFds.put(deviceToken, fd);
+            mInputDeviceDescriptors.put(deviceToken,
+                    new InputDeviceDescriptor(fd, binderDeathRecipient,
+                            InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId));
         }
         try {
-            deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
         } catch (RemoteException e) {
+            // TODO(b/215608394): remove and close InputDeviceDescriptor
             throw new RuntimeException("Could not create virtual touchscreen", e);
         }
     }
 
     void unregisterInputDevice(@NonNull IBinder token) {
         synchronized (mLock) {
-            final Integer fd = mInputDeviceFds.remove(token);
-            if (fd == null) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove(
+                    token);
+            if (inputDeviceDescriptor == null) {
                 throw new IllegalArgumentException(
                         "Could not unregister input device for given token");
             }
-            mNativeWrapper.closeUinput(fd);
+            token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
+            mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+
+            // Reset values to the default if all virtual mice are unregistered, or set display
+            // id if there's another mouse (choose the most recent).
+            if (inputDeviceDescriptor.isMouse()) {
+                updateMouseValuesLocked();
+            }
         }
     }
 
+    @GuardedBy("mLock")
+    private void updateMouseValuesLocked() {
+        InputDeviceDescriptor mostRecentlyCreatedMouse = null;
+        for (InputDeviceDescriptor otherInputDeviceDescriptor :
+                mInputDeviceDescriptors.values()) {
+            if (otherInputDeviceDescriptor.isMouse()) {
+                if (mostRecentlyCreatedMouse == null
+                        || (otherInputDeviceDescriptor.getCreationOrderNumber()
+                        > mostRecentlyCreatedMouse.getCreationOrderNumber())) {
+                    mostRecentlyCreatedMouse = otherInputDeviceDescriptor;
+                }
+            }
+        }
+        if (mostRecentlyCreatedMouse != null) {
+            final InputManagerInternal inputManagerInternal =
+                    LocalServices.getService(InputManagerInternal.class);
+            inputManagerInternal.setVirtualMousePointerDisplayId(
+                    mostRecentlyCreatedMouse.getDisplayId());
+            mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
+        } else {
+            // All mice have been unregistered; reset all values.
+            resetMouseValuesLocked();
+        }
+    }
+
+    private void resetMouseValuesLocked() {
+        final InputManagerInternal inputManagerInternal =
+                LocalServices.getService(InputManagerInternal.class);
+        inputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
+        mActivePointerDisplayId = Display.INVALID_DISPLAY;
+    }
+
     boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
         synchronized (mLock) {
-            final Integer fd = mInputDeviceFds.get(token);
-            if (fd == null) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+                    token);
+            if (inputDeviceDescriptor == null) {
                 throw new IllegalArgumentException(
                         "Could not send key event to input device for given token");
             }
-            return mNativeWrapper.writeKeyEvent(fd, event.getKeyCode(), event.getAction());
+            return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
+                    event.getKeyCode(), event.getAction());
         }
     }
 
     boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) {
         synchronized (mLock) {
-            final Integer fd = mInputDeviceFds.get(token);
-            if (fd == null) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+                    token);
+            if (inputDeviceDescriptor == null) {
                 throw new IllegalArgumentException(
                         "Could not send button event to input device for given token");
             }
-            return mNativeWrapper.writeButtonEvent(fd, event.getButtonCode(), event.getAction());
+            if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+                throw new IllegalStateException(
+                        "Display id associated with this mouse is not currently targetable");
+            }
+            return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getFileDescriptor(),
+                    event.getButtonCode(), event.getAction());
         }
     }
 
     boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) {
         synchronized (mLock) {
-            final Integer fd = mInputDeviceFds.get(token);
-            if (fd == null) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+                    token);
+            if (inputDeviceDescriptor == null) {
                 throw new IllegalArgumentException(
                         "Could not send touch event to input device for given token");
             }
-            return mNativeWrapper.writeTouchEvent(fd, event.getPointerId(), event.getToolType(),
-                    event.getAction(), event.getX(), event.getY(), event.getPressure(),
-                    event.getMajorAxisSize());
+            return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getFileDescriptor(),
+                    event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
+                    event.getY(), event.getPressure(), event.getMajorAxisSize());
         }
     }
 
     boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) {
         synchronized (mLock) {
-            final Integer fd = mInputDeviceFds.get(token);
-            if (fd == null) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+                    token);
+            if (inputDeviceDescriptor == null) {
                 throw new IllegalArgumentException(
                         "Could not send relative event to input device for given token");
             }
-            return mNativeWrapper.writeRelativeEvent(fd, event.getRelativeX(),
-                    event.getRelativeY());
+            if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+                throw new IllegalStateException(
+                        "Display id associated with this mouse is not currently targetable");
+            }
+            return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getFileDescriptor(),
+                    event.getRelativeX(), event.getRelativeY());
         }
     }
 
     boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) {
         synchronized (mLock) {
-            final Integer fd = mInputDeviceFds.get(token);
-            if (fd == null) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+                    token);
+            if (inputDeviceDescriptor == null) {
                 throw new IllegalArgumentException(
                         "Could not send scroll event to input device for given token");
             }
-            return mNativeWrapper.writeScrollEvent(fd, event.getXAxisMovement(),
-                    event.getYAxisMovement());
+            if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+                throw new IllegalStateException(
+                        "Display id associated with this mouse is not currently targetable");
+            }
+            return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getFileDescriptor(),
+                    event.getXAxisMovement(), event.getYAxisMovement());
+        }
+    }
+
+    public PointF getCursorPosition(@NonNull IBinder token) {
+        synchronized (mLock) {
+            final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+                    token);
+            if (inputDeviceDescriptor == null) {
+                throw new IllegalArgumentException(
+                        "Could not get cursor position for input device for given token");
+            }
+            if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+                throw new IllegalStateException(
+                        "Display id associated with this mouse is not currently targetable");
+            }
+            return LocalServices.getService(InputManagerInternal.class).getCursorPosition();
         }
     }
 
     public void dump(@NonNull PrintWriter fout) {
         fout.println("    InputController: ");
         synchronized (mLock) {
-            fout.println("      Active file descriptors: ");
-            for (int inputDeviceFd : mInputDeviceFds.values()) {
-                fout.println(inputDeviceFd);
+            fout.println("      Active descriptors: ");
+            for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
+                fout.println("        fd: " + inputDeviceDescriptor.getFileDescriptor());
+                fout.println("          displayId: " + inputDeviceDescriptor.getDisplayId());
+                fout.println("          creationOrder: "
+                        + inputDeviceDescriptor.getCreationOrderNumber());
+                fout.println("          type: " + inputDeviceDescriptor.getType());
             }
+            fout.println("      Active mouse display id: " + mActivePointerDisplayId);
         }
     }
 
@@ -267,6 +386,63 @@
         }
     }
 
+    @VisibleForTesting static final class InputDeviceDescriptor {
+
+        static final int TYPE_KEYBOARD = 1;
+        static final int TYPE_MOUSE = 2;
+        static final int TYPE_TOUCHSCREEN = 3;
+        @IntDef(prefix = { "TYPE_" }, value = {
+                TYPE_KEYBOARD,
+                TYPE_MOUSE,
+                TYPE_TOUCHSCREEN,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface Type {
+        }
+
+        private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1);
+
+        private final int mFd;
+        private final IBinder.DeathRecipient mDeathRecipient;
+        private final @Type int mType;
+        private final int mDisplayId;
+        // Monotonically increasing number; devices with lower numbers were created earlier.
+        private final long mCreationOrderNumber;
+
+        InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient,
+                @Type int type, int displayId) {
+            mFd = fd;
+            mDeathRecipient = deathRecipient;
+            mType = type;
+            mDisplayId = displayId;
+            mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
+        }
+
+        public int getFileDescriptor() {
+            return mFd;
+        }
+
+        public int getType() {
+            return mType;
+        }
+
+        public boolean isMouse() {
+            return mType == TYPE_MOUSE;
+        }
+
+        public IBinder.DeathRecipient getDeathRecipient() {
+            return mDeathRecipient;
+        }
+
+        public int getDisplayId() {
+            return mDisplayId;
+        }
+
+        public long getCreationOrderNumber() {
+            return mCreationOrderNumber;
+        }
+    }
+
     private final class BinderDeathRecipient implements IBinder.DeathRecipient {
 
         private final IBinder mDeviceToken;
@@ -277,6 +453,10 @@
 
         @Override
         public void binderDied() {
+            // All callers are expected to call {@link VirtualDevice#unregisterInputDevice} before
+            // quitting, which removes this death recipient. If this is invoked, the remote end
+            // died, or they disposed of the object without properly unregistering.
+            Slog.e(TAG, "Virtual input controller binder died");
             unregisterInputDevice(mDeviceToken);
         }
     }
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 ca35e03..5c8fb2e 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -16,14 +16,28 @@
 
 package com.android.server.companion.virtual;
 
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.companion.AssociationInfo;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.VirtualDeviceManager.ActivityListener;
+import android.companion.virtual.VirtualDeviceParams;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.display.DisplayManager;
+import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
@@ -32,30 +46,62 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.window.DisplayWindowPolicyController;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
 
 
 final class VirtualDeviceImpl extends IVirtualDevice.Stub
         implements IBinder.DeathRecipient {
 
+    private static final String TAG = "VirtualDeviceImpl";
     private final Object mVirtualDeviceLock = new Object();
 
     private final Context mContext;
     private final AssociationInfo mAssociationInfo;
+    private final PendingTrampolineCallback mPendingTrampolineCallback;
     private final int mOwnerUid;
     private final InputController mInputController;
     @VisibleForTesting
-    final List<Integer> mVirtualDisplayIds = new ArrayList<>();
+    final Set<Integer> mVirtualDisplayIds = new ArraySet<>();
     private final OnDeviceCloseListener mListener;
     private final IBinder mAppToken;
+    private final VirtualDeviceParams mParams;
+    private final IVirtualDeviceActivityListener mActivityListener;
+
+    private ActivityListener createListenerAdapter(int displayId) {
+        return new ActivityListener() {
+
+            @Override
+            public void onTopActivityChanged(int unusedDisplayId, ComponentName topActivity) {
+                try {
+                    mActivityListener.onTopActivityChanged(displayId, topActivity);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to call mActivityListener", e);
+                }
+            }
+
+            @Override
+            public void onDisplayEmpty(int unusedDisplayId) {
+                try {
+                    mActivityListener.onDisplayEmpty(displayId);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to call mActivityListener", e);
+                }
+            }
+        };
+    }
 
     /**
      * A mapping from the virtual display ID to its corresponding
@@ -65,17 +111,26 @@
             new SparseArray<>();
 
     VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
-            IBinder token, int ownerUid, OnDeviceCloseListener listener) {
-        this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
+            IBinder token, int ownerUid, OnDeviceCloseListener listener,
+            PendingTrampolineCallback pendingTrampolineCallback,
+            IVirtualDeviceActivityListener activityListener,
+            VirtualDeviceParams params) {
+        this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener,
+                pendingTrampolineCallback, activityListener, params);
     }
 
     @VisibleForTesting
     VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
-            int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
+            int ownerUid, InputController inputController, OnDeviceCloseListener listener,
+            PendingTrampolineCallback pendingTrampolineCallback,
+            IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) {
         mContext = context;
         mAssociationInfo = associationInfo;
+        mPendingTrampolineCallback = pendingTrampolineCallback;
+        mActivityListener = activityListener;
         mOwnerUid = ownerUid;
         mAppToken = token;
+        mParams = params;
         if (inputController == null) {
             mInputController = new InputController(mVirtualDeviceLock);
         } else {
@@ -89,12 +144,67 @@
         }
     }
 
-    @Override
+    /**
+     * Returns the flags that should be added to any virtual displays created on this virtual
+     * device.
+     */
+    int getBaseVirtualDisplayFlags() {
+        int flags = 0;
+        if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+        }
+        return flags;
+    }
+
+    @Override // Binder call
     public int getAssociationId() {
         return mAssociationInfo.getId();
     }
 
     @Override // Binder call
+    public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
+            ResultReceiver resultReceiver) {
+        if (!mVirtualDisplayIds.contains(displayId)) {
+            throw new SecurityException("Display ID " + displayId
+                    + " not found for this virtual device");
+        }
+        if (pendingIntent.isActivity()) {
+            try {
+                sendPendingIntent(displayId, pendingIntent);
+                resultReceiver.send(Activity.RESULT_OK, null);
+            } catch (PendingIntent.CanceledException e) {
+                Slog.w(TAG, "Pending intent canceled", e);
+                resultReceiver.send(Activity.RESULT_CANCELED, null);
+            }
+        } else {
+            PendingTrampoline pendingTrampoline = new PendingTrampoline(pendingIntent,
+                    resultReceiver, displayId);
+            mPendingTrampolineCallback.startWaitingForPendingTrampoline(pendingTrampoline);
+            try {
+                sendPendingIntent(displayId, pendingIntent);
+            } catch (PendingIntent.CanceledException e) {
+                Slog.w(TAG, "Pending intent canceled", e);
+                resultReceiver.send(Activity.RESULT_CANCELED, null);
+                mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline);
+            }
+        }
+    }
+
+    private void sendPendingIntent(int displayId, PendingIntent pendingIntent)
+            throws PendingIntent.CanceledException {
+        pendingIntent.send(
+                mContext,
+                /* code= */ 0,
+                /* intent= */ null,
+                /* onFinished= */ null,
+                /* handler= */ null,
+                /* requiredPermission= */ null,
+                ActivityOptions.makeBasic()
+                        .setLaunchDisplayId(displayId)
+                        .toBundle());
+    }
+
+    @Override // Binder call
     public void close() {
         mListener.onClose(mAssociationInfo.getId());
         mAppToken.unlinkToDeath(this, 0);
@@ -125,7 +235,8 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken);
+            mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken,
+                    displayId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -150,7 +261,7 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            mInputController.createMouse(deviceName, vendorId, productId, deviceToken);
+            mInputController.createMouse(deviceName, vendorId, productId, deviceToken, displayId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -177,7 +288,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             mInputController.createTouchscreen(deviceName, vendorId, productId,
-                    deviceToken, screenSize);
+                    deviceToken, displayId, screenSize);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -247,9 +358,21 @@
         }
     }
 
+    @Override // Binder call
+    public PointF getCursorPosition(IBinder token) {
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return mInputController.getCursorPosition(token);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         fout.println("  VirtualDevice: ");
+        fout.println("    mAssociationId: " + mAssociationInfo.getId());
+        fout.println("    mParams: " + mParams);
         fout.println("    mVirtualDisplayIds: ");
         synchronized (mVirtualDeviceLock) {
             for (int id : mVirtualDisplayIds) {
@@ -265,19 +388,45 @@
                     "Virtual device already have a virtual display with ID " + displayId);
         }
         mVirtualDisplayIds.add(displayId);
+        LocalServices.getService(InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
+                displayId, false);
         final GenericWindowPolicyController dwpc =
                 new GenericWindowPolicyController(FLAG_SECURE,
-                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+                        getAllowedUserHandles(),
+                        mParams.getAllowedActivities(),
+                        mParams.getBlockedActivities(),
+                        createListenerAdapter(displayId));
         mWindowPolicyControllers.put(displayId, dwpc);
         return dwpc;
     }
 
+    private ArraySet<UserHandle> getAllowedUserHandles() {
+        ArraySet<UserHandle> result = new ArraySet<>();
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        for (UserHandle profile : userManager.getAllProfiles()) {
+            int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(profile.getIdentifier());
+            if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED
+                    || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) {
+                result.add(profile);
+            } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
+                if (mParams.getUsersWithMatchingAccounts().contains(profile)) {
+                    result.add(profile);
+                }
+            }
+        }
+        return result;
+    }
+
     void onVirtualDisplayRemovedLocked(int displayId) {
         if (!mVirtualDisplayIds.contains(displayId)) {
             throw new IllegalStateException(
                     "Virtual device doesn't have a virtual display with ID " + displayId);
         }
         mVirtualDisplayIds.remove(displayId);
+        LocalServices.getService(InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
+                displayId, true);
         mWindowPolicyControllers.remove(displayId);
     }
 
@@ -302,4 +451,58 @@
     interface OnDeviceCloseListener {
         void onClose(int associationId);
     }
+
+    interface PendingTrampolineCallback {
+        /**
+         * Called when the callback should start waiting for the given pending trampoline.
+         * Implementations should try to listen for activity starts associated with the given
+         * {@code pendingTrampoline}, and launch the activity on the display with
+         * {@link PendingTrampoline#mDisplayId}.
+         */
+        void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline);
+
+        /**
+         * Called when the callback should stop waiting for the given pending trampoline. This can
+         * happen, for example, when the pending intent failed to send.
+         */
+        void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline);
+    }
+
+    /**
+     * A data class storing a pending trampoline this device is expecting.
+     */
+    static class PendingTrampoline {
+
+        /**
+         * The original pending intent sent, for which a trampoline activity launch is expected.
+         */
+        final PendingIntent mPendingIntent;
+
+        /**
+         * The result receiver associated with this pending call. {@link Activity#RESULT_OK} will
+         * be sent to the receiver if the trampoline activity was captured successfully.
+         * {@link Activity#RESULT_CANCELED} is sent otherwise.
+         */
+        final ResultReceiver mResultReceiver;
+
+        /**
+         * The display ID to send the captured trampoline activity launch to.
+         */
+        final int mDisplayId;
+
+        private PendingTrampoline(PendingIntent pendingIntent, ResultReceiver resultReceiver,
+                int displayId) {
+            mPendingIntent = pendingIntent;
+            mResultReceiver = resultReceiver;
+            mDisplayId = displayId;
+        }
+
+        @Override
+        public String toString() {
+            return "PendingTrampoline{"
+                    + "pendingIntent=" + mPendingIntent
+                    + ", resultReceiver=" + mResultReceiver
+                    + ", displayId=" + mDisplayId + "}";
+        }
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 0db670e..b507110 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -16,16 +16,24 @@
 
 package com.android.server.companion.virtual;
 
+import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityOptions;
 import android.companion.AssociationInfo;
 import android.companion.CompanionDeviceManager;
 import android.companion.CompanionDeviceManager.OnAssociationsChangedListener;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceActivityListener;
 import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
 import android.content.Context;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.ExceptionUtils;
@@ -36,6 +44,9 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline;
+import com.android.server.wm.ActivityInterceptorCallback;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -47,10 +58,12 @@
 public class VirtualDeviceManagerService extends SystemService {
 
     private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "VirtualDeviceManagerService";
+    private static final String TAG = "VirtualDeviceManagerService";
 
     private final Object mVirtualDeviceManagerLock = new Object();
     private final VirtualDeviceManagerImpl mImpl;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
 
     /**
      * Mapping from CDM association IDs to virtual devices. Only one virtual device is allowed for
@@ -79,10 +92,39 @@
         mImpl = new VirtualDeviceManagerImpl();
     }
 
+    private final ActivityInterceptorCallback mActivityInterceptorCallback =
+            new ActivityInterceptorCallback() {
+
+        @Nullable
+        @Override
+        public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+            if (info.callingPackage == null) {
+                return null;
+            }
+            PendingTrampoline pt = mPendingTrampolines.remove(info.callingPackage);
+            if (pt == null) {
+                return null;
+            }
+            pt.mResultReceiver.send(Activity.RESULT_OK, null);
+            ActivityOptions options = info.checkedOptions;
+            if (options == null) {
+                options = ActivityOptions.makeBasic();
+            }
+            return new ActivityInterceptResult(
+                    info.intent, options.setLaunchDisplayId(pt.mDisplayId));
+        }
+    };
+
     @Override
     public void onStart() {
         publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
         publishLocalService(VirtualDeviceManagerInternal.class, new LocalService());
+
+        ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
+                ActivityTaskManagerInternal.class);
+        activityTaskManagerInternal.registerActivityStartInterceptor(
+                VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
+                mActivityInterceptorCallback);
     }
 
     @GuardedBy("mVirtualDeviceManagerLock")
@@ -127,11 +169,16 @@
         }
     }
 
-    class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
+    class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
+            VirtualDeviceImpl.PendingTrampolineCallback {
 
         @Override // Binder call
         public IVirtualDevice createVirtualDevice(
-                IBinder token, String packageName, int associationId) {
+                IBinder token,
+                String packageName,
+                int associationId,
+                @NonNull VirtualDeviceParams params,
+                @NonNull IVirtualDeviceActivityListener activityListener) {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
                     "createVirtualDevice");
@@ -160,7 +207,8 @@
                                     mVirtualDevices.remove(associationId);
                                 }
                             }
-                        });
+                        },
+                        this, activityListener, params);
                 mVirtualDevices.put(associationInfo.getId(), virtualDevice);
                 return virtualDevice;
             }
@@ -181,7 +229,7 @@
                     }
                 }
             } else {
-                Slog.w(LOG_TAG, "No associations for user " + callingUserId);
+                Slog.w(TAG, "No associations for user " + callingUserId);
             }
             return null;
         }
@@ -192,7 +240,7 @@
             try {
                 return super.onTransact(code, data, reply, flags);
             } catch (Throwable e) {
-                Slog.e(LOG_TAG, "Error during IPC", e);
+                Slog.e(TAG, "Error during IPC", e);
                 throw ExceptionUtils.propagate(e, RemoteException.class);
             }
         }
@@ -201,7 +249,7 @@
         public void dump(@NonNull FileDescriptor fd,
                 @NonNull PrintWriter fout,
                 @Nullable String[] args) {
-            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, fout)) {
                 return;
             }
             fout.println("Created virtual devices: ");
@@ -211,10 +259,24 @@
                 }
             }
         }
+
+        @Override
+        public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
+            PendingTrampoline existing = mPendingTrampolines.put(
+                    pendingTrampoline.mPendingIntent.getCreatorPackage(),
+                    pendingTrampoline);
+            if (existing != null) {
+                existing.mResultReceiver.send(Activity.RESULT_CANCELED, null);
+            }
+        }
+
+        @Override
+        public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
+            mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage());
+        }
     }
 
     private final class LocalService extends VirtualDeviceManagerInternal {
-
         @Override
         public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
             synchronized (mVirtualDeviceManagerLock) {
@@ -238,6 +300,11 @@
         }
 
         @Override
+        public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) {
+            return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags();
+        }
+
+        @Override
         public boolean isAppOwnerOfAnyVirtualDevice(int uid) {
             synchronized (mVirtualDeviceManagerLock) {
                 int size = mVirtualDevices.size();
@@ -263,4 +330,42 @@
             return false;
         }
     }
+
+    private static final class PendingTrampolineMap {
+        /**
+         * The maximum duration, in milliseconds, to wait for a trampoline activity launch after
+         * invoking a pending intent.
+         */
+        private static final int TRAMPOLINE_WAIT_MS = 5000;
+
+        private final ConcurrentHashMap<String, PendingTrampoline> mMap = new ConcurrentHashMap<>();
+        private final Handler mHandler;
+
+        PendingTrampolineMap(Handler handler) {
+            mHandler = handler;
+        }
+
+        PendingTrampoline put(
+                @NonNull String packageName, @NonNull PendingTrampoline pendingTrampoline) {
+            PendingTrampoline existing = mMap.put(packageName, pendingTrampoline);
+            mHandler.removeCallbacksAndMessages(existing);
+            mHandler.postDelayed(
+                    () -> {
+                        final String creatorPackage =
+                                pendingTrampoline.mPendingIntent.getCreatorPackage();
+                        if (creatorPackage != null) {
+                            remove(creatorPackage);
+                        }
+                    },
+                    pendingTrampoline,
+                    TRAMPOLINE_WAIT_MS);
+            return existing;
+        }
+
+        PendingTrampoline remove(@NonNull String packageName) {
+            PendingTrampoline pendingTrampoline = mMap.remove(packageName);
+            mHandler.removeCallbacksAndMessages(pendingTrampoline);
+            return pendingTrampoline;
+        }
+    }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7b17162..094ed37 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,9 +100,10 @@
     name: "services.core.unboosted",
     defaults: ["platform_service_defaults"],
     srcs: [
-        ":android.hardware.biometrics.face-V1-java-source",
+        ":android.hardware.biometrics.face-V2-java-source",
         ":statslog-art-java-gen",
         ":statslog-contexthub-java-gen",
+        ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex is ready
         ":services.core-sources",
         ":services.core.protologsrc",
         ":dumpstate_aidl",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9b2948f..f56bfab 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,7 +30,6 @@
 import android.content.IntentSender;
 import android.content.pm.SigningDetails.CertCapabilities;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.component.ParsedMainComponent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -50,6 +49,7 @@
 import com.android.server.pm.pkg.AndroidPackageApi;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
 
 import java.io.IOException;
@@ -70,6 +70,7 @@
             PACKAGE_SYSTEM,
             PACKAGE_SETUP_WIZARD,
             PACKAGE_INSTALLER,
+            PACKAGE_UNINSTALLER,
             PACKAGE_VERIFIER,
             PACKAGE_BROWSER,
             PACKAGE_SYSTEM_TEXT_CLASSIFIER,
@@ -89,23 +90,25 @@
     public static final int PACKAGE_SYSTEM = 0;
     public static final int PACKAGE_SETUP_WIZARD = 1;
     public static final int PACKAGE_INSTALLER = 2;
-    public static final int PACKAGE_VERIFIER = 3;
-    public static final int PACKAGE_BROWSER = 4;
-    public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
-    public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
-    public static final int PACKAGE_WELLBEING = 7;
-    public static final int PACKAGE_DOCUMENTER = 8;
-    public static final int PACKAGE_CONFIGURATOR = 9;
-    public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
-    public static final int PACKAGE_APP_PREDICTOR = 11;
-    public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 12;
-    public static final int PACKAGE_WIFI = 13;
-    public static final int PACKAGE_COMPANION = 14;
-    public static final int PACKAGE_RETAIL_DEMO = 15;
-    public static final int PACKAGE_RECENTS = 16;
+    public static final int PACKAGE_UNINSTALLER = 3;
+    public static final int PACKAGE_VERIFIER = 4;
+    public static final int PACKAGE_BROWSER = 5;
+    public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 6;
+    public static final int PACKAGE_PERMISSION_CONTROLLER = 7;
+    public static final int PACKAGE_WELLBEING = 8;
+    public static final int PACKAGE_DOCUMENTER = 9;
+    public static final int PACKAGE_CONFIGURATOR = 10;
+    public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 11;
+    public static final int PACKAGE_APP_PREDICTOR = 12;
+    public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 13;
+    public static final int PACKAGE_WIFI = 14;
+    public static final int PACKAGE_COMPANION = 15;
+    public static final int PACKAGE_RETAIL_DEMO = 16;
+    public static final int PACKAGE_RECENTS = 17;
+    public static final int PACKAGE_AMBIENT_CONTEXT_DETECTION = 18;
     // Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
     // Please note the numbers should be continuous.
-    public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS;
+    public static final int LAST_KNOWN_PACKAGE = PACKAGE_AMBIENT_CONTEXT_DETECTION;
 
     @LongDef(flag = true, prefix = "RESOLVE_", value = {
             RESOLVE_NON_BROWSER_ONLY,
@@ -1141,6 +1144,8 @@
                 return "Setup Wizard";
             case PACKAGE_INSTALLER:
                 return "Installer";
+            case PACKAGE_UNINSTALLER:
+                return "Uninstaller";
             case PACKAGE_VERIFIER:
                 return "Verifier";
             case PACKAGE_BROWSER:
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 844ac86..5d48d78 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -853,7 +853,9 @@
         pw.println("Battery service (battery) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>");
+        pw.println("  get [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid]");
+        pw.println(
+                "  set [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid] <value>");
         pw.println("    Force a battery property value, freezing battery state.");
         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
         pw.println("  unplug [-f]");
@@ -863,7 +865,7 @@
         pw.println("    Unfreeze battery state, returning to current hardware values.");
         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
         if (Build.IS_DEBUGGABLE) {
-            pw.println("  disable_charge");
+            pw.println("  suspend_input");
             pw.println("    Suspend charging even if plugged in. ");
         }
     }
@@ -893,6 +895,46 @@
                         android.Manifest.permission.DEVICE_POWER, null);
                 unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
             } break;
+            case "get": {
+                final String key = shell.getNextArg();
+                if (key == null) {
+                    pw.println("No property specified");
+                    return -1;
+
+                }
+                switch (key) {
+                    case "present":
+                        pw.println(mHealthInfo.batteryPresent);
+                        break;
+                    case "ac":
+                        pw.println(mHealthInfo.chargerAcOnline);
+                        break;
+                    case "usb":
+                        pw.println(mHealthInfo.chargerUsbOnline);
+                        break;
+                    case "wireless":
+                        pw.println(mHealthInfo.chargerWirelessOnline);
+                        break;
+                    case "status":
+                        pw.println(mHealthInfo.batteryStatus);
+                        break;
+                    case "level":
+                        pw.println(mHealthInfo.batteryLevel);
+                        break;
+                    case "counter":
+                        pw.println(mHealthInfo.batteryChargeCounterUah);
+                        break;
+                    case "temp":
+                        pw.println(mHealthInfo.batteryTemperatureTenthsCelsius);
+                        break;
+                    case "invalid":
+                        pw.println(mInvalidCharger);
+                        break;
+                    default:
+                        pw.println("Unknown get option: " + key);
+                        break;
+                }
+            } break;
             case "set": {
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
deleted file mode 100644
index 380b1f3..0000000
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ /dev/null
@@ -1,141 +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.server;
-
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks
- * whether we need to inform BluetoothManagerService on this change.
- *
- * The information of airplane mode turns on would not be passed to the BluetoothManagerService
- * when Bluetooth is on and Bluetooth is in one of the following situations:
- *   1. Bluetooth A2DP is connected.
- *   2. Bluetooth Hearing Aid profile is connected.
- *   3. Bluetooth LE Audio is connected
- */
-class BluetoothAirplaneModeListener {
-    private static final String TAG = "BluetoothAirplaneModeListener";
-    @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count";
-
-    private static final int MSG_AIRPLANE_MODE_CHANGED = 0;
-
-    @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times
-
-    private final BluetoothManagerService mBluetoothManager;
-    private final BluetoothAirplaneModeHandler mHandler;
-    private BluetoothModeChangeHelper mAirplaneHelper;
-
-    @VisibleForTesting int mToastCount = 0;
-
-    BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) {
-        mBluetoothManager = service;
-
-        mHandler = new BluetoothAirplaneModeHandler(looper);
-        context.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
-                mAirplaneModeObserver);
-    }
-
-    private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
-        @Override
-        public void onChange(boolean unused) {
-            // Post from system main thread to android_io thread.
-            Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED);
-            mHandler.sendMessage(msg);
-        }
-    };
-
-    private class BluetoothAirplaneModeHandler extends Handler {
-        BluetoothAirplaneModeHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_AIRPLANE_MODE_CHANGED:
-                    handleAirplaneModeChange();
-                    break;
-                default:
-                    Log.e(TAG, "Invalid message: " + msg.what);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Call after boot complete
-     */
-    @VisibleForTesting
-    void start(BluetoothModeChangeHelper helper) {
-        Log.i(TAG, "start");
-        mAirplaneHelper = helper;
-        mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
-    }
-
-    @VisibleForTesting
-    boolean shouldPopToast() {
-        if (mToastCount >= MAX_TOAST_COUNT) {
-            return false;
-        }
-        mToastCount++;
-        mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount);
-        return true;
-    }
-
-    @VisibleForTesting
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    void handleAirplaneModeChange() {
-        if (shouldSkipAirplaneModeChange()) {
-            Log.i(TAG, "Ignore airplane mode change");
-            // Airplane mode enabled when Bluetooth is being used for audio/headering aid.
-            // Bluetooth is not disabled in such case, only state is changed to
-            // BLUETOOTH_ON_AIRPLANE mode.
-            mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
-                    BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
-            if (shouldPopToast()) {
-                mAirplaneHelper.showToastMessage();
-            }
-            return;
-        }
-        if (mAirplaneHelper != null) {
-            mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager);
-        }
-    }
-
-    @VisibleForTesting
-    boolean shouldSkipAirplaneModeChange() {
-        if (mAirplaneHelper == null) {
-            return false;
-        }
-        if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn()
-                || !mAirplaneHelper.isMediaProfileConnected()) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
deleted file mode 100644
index 611a37d..0000000
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.provider.DeviceConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-
-/**
- * The BluetoothDeviceConfigListener handles system device config change callback and checks
- * whether we need to inform BluetoothManagerService on this change.
- *
- * The information of device config change would not be passed to the BluetoothManagerService
- * when Bluetooth is on and Bluetooth is in one of the following situations:
- *   1. Bluetooth A2DP is connected.
- *   2. Bluetooth Hearing Aid profile is connected.
- */
-class BluetoothDeviceConfigListener {
-    private static final String TAG = "BluetoothDeviceConfigListener";
-
-    private final BluetoothManagerService mService;
-    private final boolean mLogDebug;
-
-    BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
-        mService = service;
-        mLogDebug = logDebug;
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_BLUETOOTH,
-                (Runnable r) -> r.run(),
-                mDeviceConfigChangedListener);
-    }
-
-    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
-            new DeviceConfig.OnPropertiesChangedListener() {
-                @Override
-                public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                    if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
-                        return;
-                    }
-                    if (mLogDebug) {
-                        ArrayList<String> flags = new ArrayList<>();
-                        for (String name : properties.getKeyset()) {
-                            flags.add(name + "='" + properties.getString(name, "") + "'");
-                        }
-                        Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
-                    }
-                    boolean foundInit = false;
-                    for (String name : properties.getKeyset()) {
-                        if (name.startsWith("INIT_")) {
-                            foundInit = true;
-                            break;
-                        }
-                    }
-                    if (!foundInit) {
-                        return;
-                    }
-                    mService.onInitFlagsChanged();
-                }
-            };
-
-}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
deleted file mode 100644
index 262933d..0000000
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ /dev/null
@@ -1,2962 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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 android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static android.os.UserHandle.USER_SYSTEM;
-import static android.permission.PermissionCheckerManager.PERMISSION_HARD_DENIED;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProtoEnums;
-import android.bluetooth.IBluetooth;
-import android.bluetooth.IBluetoothCallback;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothHeadset;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.IBluetoothManagerCallback;
-import android.bluetooth.IBluetoothProfileServiceConnection;
-import android.bluetooth.IBluetoothStateChangeCallback;
-import android.bluetooth.IBluetoothLeCallControl;
-import android.content.ActivityNotFoundException;
-import android.content.AttributionSource;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerExemptionManager;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.permission.PermissionManager;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
-import android.util.Log;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
-import com.android.server.pm.UserRestrictionsUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Locale;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-class BluetoothManagerService extends IBluetoothManager.Stub {
-    private static final String TAG = "BluetoothManagerService";
-    private static final boolean DBG = true;
-
-    private static final String BLUETOOTH_PRIVILEGED =
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-
-    private static final int ACTIVE_LOG_MAX_SIZE = 20;
-    private static final int CRASH_LOG_MAX_SIZE = 100;
-
-    private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
-    //Maximum msec to wait for service restart
-    private static final int SERVICE_RESTART_TIME_MS = 400;
-    //Maximum msec to wait for restart due to error
-    private static final int ERROR_RESTART_TIME_MS = 3000;
-    //Maximum msec to delay MESSAGE_USER_SWITCHED
-    private static final int USER_SWITCHED_TIME_MS = 200;
-    // Delay for the addProxy function in msec
-    private static final int ADD_PROXY_DELAY_MS = 100;
-    // Delay for retrying enable and disable in msec
-    private static final int ENABLE_DISABLE_DELAY_MS = 300;
-    private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300;
-    private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400;
-
-    private static final int MESSAGE_ENABLE = 1;
-    private static final int MESSAGE_DISABLE = 2;
-    private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
-    private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
-    private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
-    private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
-    private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
-    private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
-    private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
-    private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60;
-    private static final int MESSAGE_TIMEOUT_BIND = 100;
-    private static final int MESSAGE_TIMEOUT_UNBIND = 101;
-    private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200;
-    private static final int MESSAGE_USER_SWITCHED = 300;
-    private static final int MESSAGE_USER_UNLOCKED = 301;
-    private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
-    private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
-    private static final int MESSAGE_RESTORE_USER_SETTING = 500;
-    private static final int MESSAGE_INIT_FLAGS_CHANGED = 600;
-
-    private static final int RESTORE_SETTING_TO_ON = 1;
-    private static final int RESTORE_SETTING_TO_OFF = 0;
-
-    private static final int MAX_ERROR_RESTART_RETRIES = 6;
-    private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10;
-
-    // Bluetooth persisted setting is off
-    private static final int BLUETOOTH_OFF = 0;
-    // Bluetooth persisted setting is on
-    // and Airplane mode won't affect Bluetooth state at start up
-    private static final int BLUETOOTH_ON_BLUETOOTH = 1;
-    // Bluetooth persisted setting is on
-    // but Airplane mode will affect Bluetooth state at start up
-    // and Airplane mode will have higher priority.
-    @VisibleForTesting
-    static final int BLUETOOTH_ON_AIRPLANE = 2;
-
-    private static final int SERVICE_IBLUETOOTH = 1;
-    private static final int SERVICE_IBLUETOOTHGATT = 2;
-
-    private final Context mContext;
-
-    // Locks are not provided for mName and mAddress.
-    // They are accessed in handler or broadcast receiver, same thread context.
-    private String mAddress;
-    private String mName;
-    private final ContentResolver mContentResolver;
-    private final int mUserId;
-    private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
-    private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
-    private IBinder mBluetoothBinder;
-    private IBluetooth mBluetooth;
-    private IBluetoothGatt mBluetoothGatt;
-    private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
-    private boolean mBinding;
-    private boolean mUnbinding;
-
-    private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
-
-    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
-
-    private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener;
-
-    // used inside handler thread
-    private boolean mQuietEnable = false;
-    private boolean mEnable;
-
-    private static CharSequence timeToLog(long timestamp) {
-        return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp);
-    }
-
-    /**
-     * Used for tracking apps that enabled / disabled Bluetooth.
-     */
-    private class ActiveLog {
-        private int mReason;
-        private String mPackageName;
-        private boolean mEnable;
-        private long mTimestamp;
-
-        ActiveLog(int reason, String packageName, boolean enable, long timestamp) {
-            mReason = reason;
-            mPackageName = packageName;
-            mEnable = enable;
-            mTimestamp = timestamp;
-        }
-
-        public String toString() {
-            return timeToLog(mTimestamp) + (mEnable ? "  Enabled " : " Disabled ")
-                    + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName;
-        }
-
-        void dump(ProtoOutputStream proto) {
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp);
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable);
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName);
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason);
-        }
-    }
-
-    private final LinkedList<ActiveLog> mActiveLogs = new LinkedList<>();
-    private final LinkedList<Long> mCrashTimestamps = new LinkedList<>();
-    private int mCrashes;
-    private long mLastEnabledTime;
-
-    // configuration from external IBinder call which is used to
-    // synchronize with broadcast receiver.
-    private boolean mQuietEnableExternal;
-    private boolean mEnableExternal;
-
-    // Map of apps registered to keep BLE scanning on.
-    private Map<IBinder, ClientDeathRecipient> mBleApps =
-            new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
-
-    private int mState;
-    private final BluetoothHandler mHandler;
-    private int mErrorRecoveryRetryCounter;
-    private final int mSystemUiUid;
-
-    private boolean mIsHearingAidProfileSupported;
-
-    private AppOpsManager mAppOps;
-
-    // Save a ProfileServiceConnections object for each of the bound
-    // bluetooth profile services
-    private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
-
-    private final boolean mWirelessConsentRequired;
-
-    private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
-        @Override
-        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
-            Message msg =
-                    mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);
-            mHandler.sendMessage(msg);
-        }
-    };
-
-    private final UserRestrictionsListener mUserRestrictionsListener =
-            new UserRestrictionsListener() {
-                @Override
-                public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
-                        Bundle prevRestrictions) {
-
-                    if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
-                            UserManager.DISALLOW_BLUETOOTH_SHARING)) {
-                        updateOppLauncherComponentState(userId,
-                                newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING));
-                    }
-
-                    // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
-                    if (userId == USER_SYSTEM
-                            && UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
-                            newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
-                        if (userId == USER_SYSTEM && newRestrictions.getBoolean(
-                                UserManager.DISALLOW_BLUETOOTH)) {
-                            updateOppLauncherComponentState(userId, true); // Sharing disallowed
-                            sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED,
-                                    mContext.getPackageName());
-                        } else {
-                            updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
-                                    UserManager.DISALLOW_BLUETOOTH_SHARING));
-                        }
-                    }
-                }
-            };
-
-    @VisibleForTesting
-    public void onInitFlagsChanged() {
-        mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
-        mHandler.sendEmptyMessageDelayed(
-                MESSAGE_INIT_FLAGS_CHANGED,
-                DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
-    }
-
-    public boolean onFactoryReset(AttributionSource attributionSource) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
-                "Need BLUETOOTH_PRIVILEGED permission");
-
-        // Wait for stable state if bluetooth is temporary state.
-        int state = getState();
-        if (state == BluetoothAdapter.STATE_BLE_TURNING_ON
-                || state == BluetoothAdapter.STATE_TURNING_ON
-                || state == BluetoothAdapter.STATE_TURNING_OFF) {
-            if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) {
-                return false;
-            }
-        }
-
-        // Clear registered LE apps to force shut-off Bluetooth
-        clearBleApps();
-        state = getState();
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth == null) {
-                return false;
-            }
-            if (state == BluetoothAdapter.STATE_BLE_ON) {
-                addActiveLog(
-                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
-                        mContext.getPackageName(), false);
-                mBluetooth.onBrEdrDown(attributionSource);
-                return true;
-            } else if (state == BluetoothAdapter.STATE_ON) {
-                addActiveLog(
-                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
-                        mContext.getPackageName(), false);
-                mBluetooth.disable(attributionSource);
-                return true;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to shutdown Bluetooth", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onAirplaneModeChanged() {
-        synchronized (this) {
-            if (isBluetoothPersistedStateOn()) {
-                if (isAirplaneModeOn()) {
-                    persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
-                } else {
-                    persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                }
-            }
-
-            int st = BluetoothAdapter.STATE_OFF;
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    st = mBluetooth.getState();
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to call getState", e);
-                return;
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-
-            Slog.d(TAG,
-                    "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
-                            st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
-
-            if (isAirplaneModeOn()) {
-                // Clear registered LE apps to force shut-off
-                clearBleApps();
-
-                // If state is BLE_ON make sure we trigger disableBLE
-                if (st == BluetoothAdapter.STATE_BLE_ON) {
-                    try {
-                        mBluetoothLock.readLock().lock();
-                        if (mBluetooth != null) {
-                            addActiveLog(
-                                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                                    mContext.getPackageName(), false);
-                            mBluetooth.onBrEdrDown(mContext.getAttributionSource());
-                            mEnable = false;
-                            mEnableExternal = false;
-                        }
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to call onBrEdrDown", e);
-                    } finally {
-                        mBluetoothLock.readLock().unlock();
-                    }
-                } else if (st == BluetoothAdapter.STATE_ON) {
-                    sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                            mContext.getPackageName());
-                }
-            } else if (mEnableExternal) {
-                sendEnableMsg(mQuietEnableExternal,
-                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                        mContext.getPackageName());
-            }
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
-                String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
-                if (DBG) {
-                    Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by "
-                            + mContext.getPackageName());
-                }
-                if (newName != null) {
-                    storeNameAndAddress(newName, null);
-                }
-            } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) {
-                String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS);
-                if (newAddress != null) {
-                    if (DBG) {
-                        Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
-                    }
-                    storeNameAndAddress(null, newAddress);
-                } else {
-                    if (DBG) {
-                        Slog.e(TAG, "No Bluetooth Adapter address parameter found");
-                    }
-                }
-            } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
-                final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
-                if (Settings.Global.BLUETOOTH_ON.equals(name)) {
-                    // The Bluetooth On state may be changed during system restore.
-                    final String prevValue =
-                            intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
-                    final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
-
-                    if (DBG) {
-                        Slog.d(TAG,
-                                "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue
-                                        + ", newValue=" + newValue);
-                    }
-
-                    if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) {
-                        Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING,
-                                newValue.equals("0") ? RESTORE_SETTING_TO_OFF
-                                        : RESTORE_SETTING_TO_ON, 0);
-                        mHandler.sendMessage(msg);
-                    }
-                }
-            } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
-                    || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)
-                    || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) {
-                final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
-                        BluetoothProfile.STATE_CONNECTED);
-                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
-                        && state == BluetoothProfile.STATE_DISCONNECTED
-                        && !mBluetoothModeChangeHelper.isMediaProfileConnected()) {
-                    Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
-                    onInitFlagsChanged();
-                }
-            }
-        }
-    };
-
-    BluetoothManagerService(Context context) {
-        mHandler = new BluetoothHandler(IoThread.get().getLooper());
-
-        mContext = context;
-
-        mWirelessConsentRequired = context.getResources()
-                .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired);
-
-        mCrashes = 0;
-        mBluetooth = null;
-        mBluetoothBinder = null;
-        mBluetoothGatt = null;
-        mBinding = false;
-        mUnbinding = false;
-        mEnable = false;
-        mState = BluetoothAdapter.STATE_OFF;
-        mQuietEnableExternal = false;
-        mEnableExternal = false;
-        mAddress = null;
-        mName = null;
-        mErrorRecoveryRetryCounter = 0;
-        mContentResolver = context.getContentResolver();
-        mUserId = mContentResolver.getUserId();
-        // Observe BLE scan only mode settings change.
-        registerForBleScanModeChange();
-        mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
-        mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
-
-        mIsHearingAidProfileSupported = context.getResources()
-                .getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported);
-
-        // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils
-        String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);
-        if (!TextUtils.isEmpty(value)) {
-            boolean isHearingAidEnabled = Boolean.parseBoolean(value);
-            Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);
-            FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);
-            if (isHearingAidEnabled && !mIsHearingAidProfileSupported) {
-                // Overwrite to enable support by FeatureFlag
-                mIsHearingAidProfileSupported = true;
-            }
-        }
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
-        filter.addAction(Intent.ACTION_SETTING_RESTORED);
-        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        mContext.registerReceiver(mReceiver, filter);
-
-        loadStoredNameAndAddress();
-        if (isBluetoothPersistedStateOn()) {
-            if (DBG) {
-                Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
-            }
-            mEnableExternal = true;
-        }
-
-        String airplaneModeRadios =
-                Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
-        if (airplaneModeRadios == null || airplaneModeRadios.contains(
-                Settings.Global.RADIO_BLUETOOTH)) {
-            mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
-                    this, IoThread.get().getLooper(), context);
-        }
-
-        int systemUiUid = -1;
-        // Check if device is configured with no home screen, which implies no SystemUI.
-        boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
-        if (!noHome) {
-            PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
-            systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
-                    MATCH_SYSTEM_ONLY, USER_SYSTEM);
-        }
-        if (systemUiUid >= 0) {
-            Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
-        } else {
-            // Some platforms, such as wearables do not have a system ui.
-            Slog.w(TAG, "Unable to resolve SystemUI's UID.");
-        }
-        mSystemUiUid = systemUiUid;
-    }
-
-    /**
-     *  Returns true if airplane mode is currently on
-     */
-    private boolean isAirplaneModeOn() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
-    private boolean supportBluetoothPersistedState() {
-        return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState);
-    }
-
-    /**
-     *  Returns true if the Bluetooth saved state is "on"
-     */
-    private boolean isBluetoothPersistedStateOn() {
-        if (!supportBluetoothPersistedState()) {
-            return false;
-        }
-        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
-        if (DBG) {
-            Slog.d(TAG, "Bluetooth persisted state: " + state);
-        }
-        return state != BLUETOOTH_OFF;
-    }
-
-    private boolean isBluetoothPersistedStateOnAirplane() {
-        if (!supportBluetoothPersistedState()) {
-            return false;
-        }
-        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
-        if (DBG) {
-            Slog.d(TAG, "Bluetooth persisted state: " + state);
-        }
-        return state == BLUETOOTH_ON_AIRPLANE;
-    }
-
-    /**
-     *  Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
-     */
-    private boolean isBluetoothPersistedStateOnBluetooth() {
-        if (!supportBluetoothPersistedState()) {
-            return false;
-        }
-        return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
-                BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
-    }
-
-    /**
-     *  Save the Bluetooth on/off state
-     */
-    private void persistBluetoothSetting(int value) {
-        if (DBG) {
-            Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
-        }
-        // waive WRITE_SECURE_SETTINGS permission check
-        final long callingIdentity = Binder.clearCallingIdentity();
-        try {
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.BLUETOOTH_ON, value);
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-    }
-
-    /**
-     * Returns true if the Bluetooth Adapter's name and address is
-     * locally cached
-     * @return
-     */
-    private boolean isNameAndAddressSet() {
-        return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0;
-    }
-
-    /**
-     * Retrieve the Bluetooth Adapter's name and address and save it in
-     * in the local cache
-     */
-    private void loadStoredNameAndAddress() {
-        if (DBG) {
-            Slog.d(TAG, "Loading stored name and address");
-        }
-        if (mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
-                && Settings.Secure.getIntForUser(mContentResolver,
-                Settings.Secure.BLUETOOTH_NAME, 0, mUserId)
-                == 0) {
-            // if the valid flag is not set, don't load the address and name
-            if (DBG) {
-                Slog.d(TAG, "invalid bluetooth name and address stored");
-            }
-            return;
-        }
-        mName = Settings.Secure.getStringForUser(
-                mContentResolver, Settings.Secure.BLUETOOTH_NAME, mUserId);
-        mAddress = Settings.Secure.getStringForUser(
-                mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS, mUserId);
-        if (DBG) {
-            Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
-        }
-    }
-
-    /**
-     * Save the Bluetooth name and address in the persistent store.
-     * Only non-null values will be saved.
-     * @param name
-     * @param address
-     */
-    private void storeNameAndAddress(String name, String address) {
-        if (name != null) {
-            Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_NAME, name,
-                    mUserId);
-            mName = name;
-            if (DBG) {
-                Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
-                        mContentResolver, Settings.Secure.BLUETOOTH_NAME,
-                        mUserId));
-            }
-        }
-
-        if (address != null) {
-            Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
-                    address, mUserId);
-            mAddress = address;
-            if (DBG) {
-                Slog.d(TAG,
-                        "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
-                                mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
-                                mUserId));
-            }
-        }
-
-        if ((name != null) && (address != null)) {
-            Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDR_VALID, 1,
-                    mUserId);
-        }
-    }
-
-    public IBluetooth registerAdapter(IBluetoothManagerCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "Callback is null in registerAdapter");
-            return null;
-        }
-        synchronized (mCallbacks) {
-            mCallbacks.register(callback);
-        }
-        return mBluetooth;
-    }
-
-    public void unregisterAdapter(IBluetoothManagerCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "Callback is null in unregisterAdapter");
-            return;
-        }
-        synchronized (mCallbacks) {
-            mCallbacks.unregister(callback);
-        }
-    }
-
-    public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
-            return;
-        }
-        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
-    }
-
-    public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
-            return;
-        }
-        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
-    }
-
-    public boolean isEnabled() {
-        return getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    public int getState() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getState(): report OFF for non-active and non system user");
-            return BluetoothAdapter.STATE_OFF;
-        }
-
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                return mBluetooth.getState();
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "getState()", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-        return BluetoothAdapter.STATE_OFF;
-    }
-
-    class ClientDeathRecipient implements IBinder.DeathRecipient {
-        private String mPackageName;
-
-        ClientDeathRecipient(String packageName) {
-            mPackageName = packageName;
-        }
-
-        public void binderDied() {
-            if (DBG) {
-                Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
-            }
-
-            for (Map.Entry<IBinder, ClientDeathRecipient> entry : mBleApps.entrySet()) {
-                IBinder token = entry.getKey();
-                ClientDeathRecipient deathRec = entry.getValue();
-                if (deathRec.equals(this)) {
-                    updateBleAppCount(token, false, mPackageName);
-                    break;
-                }
-            }
-        }
-
-        public String getPackageName() {
-            return mPackageName;
-        }
-    }
-
-    @Override
-    public boolean isBleScanAlwaysAvailable() {
-        if (isAirplaneModeOn() && !mEnable) {
-            return false;
-        }
-        try {
-            return Settings.Global.getInt(mContentResolver,
-                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE) != 0;
-        } catch (SettingNotFoundException e) {
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isHearingAidProfileSupported() {
-        return mIsHearingAidProfileSupported;
-    }
-
-    private boolean isDeviceProvisioned() {
-        return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
-                0) != 0;
-    }
-
-    // Monitor change of BLE scan only mode settings.
-    private void registerForProvisioningStateChange() {
-        ContentObserver contentObserver = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                if (!isDeviceProvisioned()) {
-                    if (DBG) {
-                        Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
-                                + "provisioned");
-                    }
-                    return;
-                }
-                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
-                    Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
-                    onInitFlagsChanged();
-                }
-            }
-        };
-
-        mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
-                contentObserver);
-    }
-
-    // Monitor change of BLE scan only mode settings.
-    private void registerForBleScanModeChange() {
-        ContentObserver contentObserver = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                if (isBleScanAlwaysAvailable()) {
-                    // Nothing to do
-                    return;
-                }
-                // BLE scan is not available.
-                disableBleScanMode();
-                clearBleApps();
-                try {
-                    mBluetoothLock.readLock().lock();
-                    if (mBluetooth != null) {
-                        addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                                mContext.getPackageName(), false);
-                        mBluetooth.onBrEdrDown(mContext.getAttributionSource());
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "error when disabling bluetooth", e);
-                } finally {
-                    mBluetoothLock.readLock().unlock();
-                }
-            }
-        };
-
-        mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false,
-                contentObserver);
-    }
-
-    // Disable ble scan only mode.
-    private void disableBleScanMode() {
-        try {
-            mBluetoothLock.writeLock().lock();
-            if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
-                if (DBG) {
-                    Slog.d(TAG, "Reseting the mEnable flag for clean disable");
-                }
-                mEnable = false;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "getState()", e);
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-    }
-
-    private int updateBleAppCount(IBinder token, boolean enable, String packageName) {
-        ClientDeathRecipient r = mBleApps.get(token);
-        if (r == null && enable) {
-            ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName);
-            try {
-                token.linkToDeath(deathRec, 0);
-            } catch (RemoteException ex) {
-                throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!");
-            }
-            mBleApps.put(token, deathRec);
-            if (DBG) {
-                Slog.d(TAG, "Registered for death of " + packageName);
-            }
-        } else if (!enable && r != null) {
-            // Unregister death recipient as the app goes away.
-            token.unlinkToDeath(r, 0);
-            mBleApps.remove(token);
-            if (DBG) {
-                Slog.d(TAG, "Unregistered for death of " + packageName);
-            }
-        }
-        int appCount = mBleApps.size();
-        if (DBG) {
-            Slog.d(TAG, appCount + " registered Ble Apps");
-        }
-        return appCount;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean checkBluetoothPermissions(AttributionSource attributionSource, String message,
-            boolean requireForeground) {
-        if (isBluetoothDisallowed()) {
-            if (DBG) {
-                Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed");
-            }
-            return false;
-        }
-        // Check if packageName belongs to callingUid
-        final int callingUid = Binder.getCallingUid();
-        final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!isCallerSystem) {
-            checkPackage(callingUid, attributionSource.getPackageName());
-
-            if (requireForeground && !checkIfCallerIsForegroundUser()) {
-                Slog.w(TAG, "Not allowed for non-active and non system user");
-                return false;
-            }
-
-            if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, message)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public boolean enableBle(AttributionSource attributionSource, IBinder token)
-            throws RemoteException {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "enableBle", false)) {
-            if (DBG) {
-                Slog.d(TAG, "enableBle(): bluetooth disallowed");
-            }
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "enableBle(" + packageName + "):  mBluetooth =" + mBluetooth
-                    + " mBinding = " + mBinding + " mState = "
-                    + BluetoothAdapter.nameForState(mState));
-        }
-        updateBleAppCount(token, true, packageName);
-
-        if (mState == BluetoothAdapter.STATE_ON
-                || mState == BluetoothAdapter.STATE_BLE_ON
-                || mState == BluetoothAdapter.STATE_TURNING_ON
-                || mState == BluetoothAdapter.STATE_TURNING_OFF
-                || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) {
-            Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on");
-            return true;
-        }
-        synchronized (mReceiver) {
-            // waive WRITE_SECURE_SETTINGS permission check
-            sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                    packageName, true);
-        }
-        return true;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean disableBle(AttributionSource attributionSource, IBinder token)
-            throws RemoteException {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "disableBle", false)) {
-            if (DBG) {
-                Slog.d(TAG, "disableBLE(): bluetooth disallowed");
-            }
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "disableBle(" + packageName + "):  mBluetooth =" + mBluetooth
-                    + " mBinding = " + mBinding + " mState = "
-                    + BluetoothAdapter.nameForState(mState));
-        }
-
-        if (mState == BluetoothAdapter.STATE_OFF) {
-            Slog.d(TAG, "disableBLE(): Already disabled");
-            return false;
-        }
-        updateBleAppCount(token, false, packageName);
-
-        if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) {
-            if (mEnable) {
-                disableBleScanMode();
-            }
-            if (!mEnableExternal) {
-                addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                        packageName, false);
-                sendBrEdrDownCallback(attributionSource);
-            }
-        }
-        return true;
-    }
-
-    // Clear all apps using BLE scan only mode.
-    private void clearBleApps() {
-        mBleApps.clear();
-    }
-
-    /** @hide */
-    public boolean isBleAppPresent() {
-        if (DBG) {
-            Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
-        }
-        return mBleApps.size() > 0;
-    }
-
-    /**
-     * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on,
-     * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off.
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    private void continueFromBleOnState() {
-        if (DBG) {
-            Slog.d(TAG, "continueFromBleOnState()");
-        }
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth == null) {
-                Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!");
-                return;
-            }
-            if (!mEnableExternal && !isBleAppPresent()) {
-                Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now");
-                mEnable = false;
-                mBluetooth.onBrEdrDown(mContext.getAttributionSource());
-                return;
-            }
-            if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
-                // This triggers transition to STATE_ON
-                mBluetooth.onLeServiceUp(mContext.getAttributionSource());
-                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to call onServiceUp", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-    }
-
-    /**
-     * Inform BluetoothAdapter instances that BREDR part is down
-     * and turn off all service and stack if no LE app needs it
-     */
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    private void sendBrEdrDownCallback(AttributionSource attributionSource) {
-        if (DBG) {
-            Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks");
-        }
-
-        if (mBluetooth == null) {
-            Slog.w(TAG, "Bluetooth handle is null");
-            return;
-        }
-
-        if (isBleAppPresent()) {
-            // Need to stay at BLE ON. Disconnect all Gatt connections
-            try {
-                mBluetoothGatt.unregAll(attributionSource);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to disconnect all apps.", e);
-            }
-        } else {
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    mBluetooth.onBrEdrDown(attributionSource);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-        }
-
-    }
-
-    public boolean enableNoAutoConnect(AttributionSource attributionSource) {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "enableNoAutoConnect", false)) {
-            if (DBG) {
-                Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed");
-            }
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "enableNoAutoConnect():  mBluetooth =" + mBluetooth + " mBinding = "
-                    + mBinding);
-        }
-
-        int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
-        if (callingAppId != Process.NFC_UID) {
-            throw new SecurityException("no permission to enable Bluetooth quietly");
-        }
-
-        synchronized (mReceiver) {
-            mQuietEnableExternal = true;
-            mEnableExternal = true;
-            sendEnableMsg(true,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
-        }
-        return true;
-    }
-
-    public boolean enable(AttributionSource attributionSource) throws RemoteException {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "enable", true)) {
-            if (DBG) {
-                Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
-            }
-            return false;
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!callerSystem && !isEnabled() && mWirelessConsentRequired
-                && startConsentUiIfNeeded(packageName,
-                callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "enable(" + packageName + "):  mBluetooth =" + mBluetooth + " mBinding = "
-                    + mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
-        }
-
-        synchronized (mReceiver) {
-            mQuietEnableExternal = false;
-            mEnableExternal = true;
-            // waive WRITE_SECURE_SETTINGS permission check
-            sendEnableMsg(false,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
-        }
-        if (DBG) {
-            Slog.d(TAG, "enable returning");
-        }
-        return true;
-    }
-
-    public boolean disable(AttributionSource attributionSource, boolean persist)
-            throws RemoteException {
-        if (!persist) {
-            mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
-                    "Need BLUETOOTH_PRIVILEGED permission");
-        }
-
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "disable", true)) {
-            if (DBG) {
-                Slog.d(TAG, "disable(): not disabling - bluetooth disallowed");
-            }
-            return false;
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!callerSystem && isEnabled() && mWirelessConsentRequired
-                && startConsentUiIfNeeded(packageName,
-                callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding);
-        }
-
-        synchronized (mReceiver) {
-            if (!isBluetoothPersistedStateOnAirplane()) {
-                if (persist) {
-                    persistBluetoothSetting(BLUETOOTH_OFF);
-                }
-                mEnableExternal = false;
-            }
-            sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                    packageName);
-        }
-        return true;
-    }
-
-    private boolean startConsentUiIfNeeded(String packageName,
-            int callingUid, String intentAction) throws RemoteException {
-        if (checkBluetoothPermissionWhenWirelessConsentRequired()) {
-            return false;
-        }
-        try {
-            // Validate the package only if we are going to use it
-            ApplicationInfo applicationInfo = mContext.getPackageManager()
-                    .getApplicationInfoAsUser(packageName,
-                            PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                            UserHandle.getUserId(callingUid));
-            if (applicationInfo.uid != callingUid) {
-                throw new SecurityException("Package " + packageName
-                        + " not in uid " + callingUid);
-            }
-
-            Intent intent = new Intent(intentAction);
-            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            try {
-                mContext.startActivity(intent);
-            } catch (ActivityNotFoundException e) {
-                // Shouldn't happen
-                Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
-                return false;
-            }
-            return true;
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RemoteException(e.getMessage());
-        }
-    }
-
-    /**
-     * Check if AppOpsManager is available and the packageName belongs to uid
-     *
-     * A null package belongs to any uid
-     */
-    private void checkPackage(int uid, String packageName) {
-        if (mAppOps == null) {
-            Slog.w(TAG, "checkPackage(): called before system boot up, uid "
-                    + uid + ", packageName " + packageName);
-            throw new IllegalStateException("System has not boot yet");
-        }
-        if (packageName == null) {
-            Slog.w(TAG, "checkPackage(): called with null packageName from " + uid);
-            return;
-        }
-        try {
-            mAppOps.checkPackage(uid, packageName);
-        } catch (SecurityException e) {
-            Slog.w(TAG, "checkPackage(): " + packageName + " does not belong to uid " + uid);
-            throw new SecurityException(e.getMessage());
-        }
-    }
-
-    /**
-     * Check if the caller must still pass permission check or if the caller is exempted
-     * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check.
-     *
-     * Commands from some callers may be exempted from triggering the consent UI when
-     * enabling bluetooth. This exemption is checked via the
-     * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip
-     * the consent UI where it may otherwise be required.
-     *
-     * @hide
-     */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private boolean checkBluetoothPermissionWhenWirelessConsentRequired() {
-        int result = mContext.checkCallingPermission(
-                android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED);
-        return result == PackageManager.PERMISSION_GRANTED;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void unbindAndFinish() {
-        if (DBG) {
-            Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding
-                    + " mUnbinding = " + mUnbinding);
-        }
-
-        try {
-            mBluetoothLock.writeLock().lock();
-            if (mUnbinding) {
-                return;
-            }
-            mUnbinding = true;
-            mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-            mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE);
-            if (mBluetooth != null) {
-                //Unregister callback object
-                try {
-                    mBluetooth.unregisterCallback(mBluetoothCallback,
-                            mContext.getAttributionSource());
-                } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to unregister BluetoothCallback", re);
-                }
-                mBluetoothBinder = null;
-                mBluetooth = null;
-                mContext.unbindService(mConnection);
-                mUnbinding = false;
-                mBinding = false;
-            } else {
-                mUnbinding = false;
-            }
-            mBluetoothGatt = null;
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-    }
-
-    public IBluetoothGatt getBluetoothGatt() {
-        // sync protection
-        return mBluetoothGatt;
-    }
-
-    @Override
-    public boolean bindBluetoothProfileService(int bluetoothProfile,
-            IBluetoothProfileServiceConnection proxy) {
-        if (mState != BluetoothAdapter.STATE_ON) {
-            if (DBG) {
-                Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile
-                        + ", while Bluetooth was disabled");
-            }
-            return false;
-        }
-        synchronized (mProfileServices) {
-            ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
-            if (psc == null) {
-                if (DBG) {
-                    Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: "
-                            + bluetoothProfile);
-                }
-
-                Intent intent;
-                if (bluetoothProfile == BluetoothProfile.HEADSET) {
-                    intent = new Intent(IBluetoothHeadset.class.getName());
-                } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
-                    intent = new Intent(IBluetoothLeCallControl.class.getName());
-                } else {
-                    return false;
-                }
-
-                psc = new ProfileServiceConnections(intent);
-                if (!psc.bindService()) {
-                    return false;
-                }
-
-                mProfileServices.put(new Integer(bluetoothProfile), psc);
-            }
-        }
-
-        // Introducing a delay to give the client app time to prepare
-        Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED);
-        addProxyMsg.arg1 = bluetoothProfile;
-        addProxyMsg.obj = proxy;
-        mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS);
-        return true;
-    }
-
-    @Override
-    public void unbindBluetoothProfileService(int bluetoothProfile,
-            IBluetoothProfileServiceConnection proxy) {
-        synchronized (mProfileServices) {
-            Integer profile = new Integer(bluetoothProfile);
-            ProfileServiceConnections psc = mProfileServices.get(profile);
-            if (psc == null) {
-                return;
-            }
-            psc.removeProxy(proxy);
-            if (psc.isEmpty()) {
-                // All prxoies are disconnected, unbind with the service.
-                try {
-                    mContext.unbindService(psc);
-                } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
-                }
-                mProfileServices.remove(profile);
-            }
-        }
-    }
-
-    private void unbindAllBluetoothProfileServices() {
-        synchronized (mProfileServices) {
-            for (Integer i : mProfileServices.keySet()) {
-                ProfileServiceConnections psc = mProfileServices.get(i);
-                try {
-                    mContext.unbindService(psc);
-                } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
-                }
-                psc.removeAllProxies();
-            }
-            mProfileServices.clear();
-        }
-    }
-
-    /**
-     * Send enable message and set adapter name and address. Called when the boot phase becomes
-     * PHASE_SYSTEM_SERVICES_READY.
-     */
-    public void handleOnBootPhase() {
-        if (DBG) {
-            Slog.d(TAG, "Bluetooth boot completed");
-        }
-        mAppOps = mContext.getSystemService(AppOpsManager.class);
-        UserManagerInternal userManagerInternal =
-                LocalServices.getService(UserManagerInternal.class);
-        userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
-        final boolean isBluetoothDisallowed = isBluetoothDisallowed();
-        if (isBluetoothDisallowed) {
-            return;
-        }
-        final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
-        if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) {
-            if (DBG) {
-                Slog.d(TAG, "Auto-enabling Bluetooth.");
-            }
-            sendEnableMsg(mQuietEnableExternal,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,
-                    mContext.getPackageName());
-        } else if (!isNameAndAddressSet()) {
-            if (DBG) {
-                Slog.d(TAG, "Getting adapter name and address");
-            }
-            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
-            mHandler.sendMessage(getMsg);
-        }
-
-        mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
-        if (mBluetoothAirplaneModeListener != null) {
-            mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
-        }
-        registerForProvisioningStateChange();
-        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
-    }
-
-    /**
-     * Called when switching to a different foreground user.
-     */
-    public void handleOnSwitchUser(int userHandle) {
-        if (DBG) {
-            Slog.d(TAG, "User " + userHandle + " switched");
-        }
-        mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget();
-    }
-
-    /**
-     * Called when user is unlocked.
-     */
-    public void handleOnUnlockUser(int userHandle) {
-        if (DBG) {
-            Slog.d(TAG, "User " + userHandle + " unlocked");
-        }
-        mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget();
-    }
-
-    /**
-     * This class manages the clients connected to a given ProfileService
-     * and maintains the connection with that service.
-     */
-    private final class ProfileServiceConnections
-            implements ServiceConnection, IBinder.DeathRecipient {
-        final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
-                new RemoteCallbackList<IBluetoothProfileServiceConnection>();
-        IBinder mService;
-        ComponentName mClassName;
-        Intent mIntent;
-        boolean mInvokingProxyCallbacks = false;
-
-        ProfileServiceConnections(Intent intent) {
-            mService = null;
-            mClassName = null;
-            mIntent = intent;
-        }
-
-        private boolean bindService() {
-            int state = BluetoothAdapter.STATE_OFF;
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    state = mBluetooth.getState();
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to call getState", e);
-                return false;
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-
-            if (state != BluetoothAdapter.STATE_ON) {
-                if (DBG) {
-                    Slog.d(TAG, "Unable to bindService while Bluetooth is disabled");
-                }
-                return false;
-            }
-
-            if (mIntent != null && mService == null && doBind(mIntent, this, 0,
-                    UserHandle.CURRENT_OR_SELF)) {
-                Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
-                msg.obj = this;
-                mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
-                return true;
-            }
-            Slog.w(TAG, "Unable to bind with intent: " + mIntent);
-            return false;
-        }
-
-        private void addProxy(IBluetoothProfileServiceConnection proxy) {
-            mProxies.register(proxy);
-            if (mService != null) {
-                try {
-                    proxy.onServiceConnected(mClassName, mService);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to connect to proxy", e);
-                }
-            } else {
-                if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
-                    Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
-                    msg.obj = this;
-                    mHandler.sendMessage(msg);
-                }
-            }
-        }
-
-        private void removeProxy(IBluetoothProfileServiceConnection proxy) {
-            if (proxy != null) {
-                if (mProxies.unregister(proxy)) {
-                    try {
-                        proxy.onServiceDisconnected(mClassName);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to disconnect proxy", e);
-                    }
-                }
-            } else {
-                Slog.w(TAG, "Trying to remove a null proxy");
-            }
-        }
-
-        private void removeAllProxies() {
-            onServiceDisconnected(mClassName);
-            mProxies.kill();
-        }
-
-        private boolean isEmpty() {
-            return mProxies.getRegisteredCallbackCount() == 0;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            // remove timeout message
-            mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this);
-            mService = service;
-            mClassName = className;
-            try {
-                mService.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to linkToDeath", e);
-            }
-
-            if (mInvokingProxyCallbacks) {
-                Slog.e(TAG, "Proxy callbacks already in progress.");
-                return;
-            }
-            mInvokingProxyCallbacks = true;
-
-            final int n = mProxies.beginBroadcast();
-            try {
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mProxies.getBroadcastItem(i).onServiceConnected(className, service);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to connect to proxy", e);
-                    }
-                }
-            } finally {
-                mProxies.finishBroadcast();
-                mInvokingProxyCallbacks = false;
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (mService == null) {
-                return;
-            }
-            try {
-                mService.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                Log.e(TAG, "error unlinking to death", e);
-            }
-            mService = null;
-            mClassName = null;
-
-            if (mInvokingProxyCallbacks) {
-                Slog.e(TAG, "Proxy callbacks already in progress.");
-                return;
-            }
-            mInvokingProxyCallbacks = true;
-
-            final int n = mProxies.beginBroadcast();
-            try {
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mProxies.getBroadcastItem(i).onServiceDisconnected(className);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to disconnect from proxy", e);
-                    }
-                }
-            } finally {
-                mProxies.finishBroadcast();
-                mInvokingProxyCallbacks = false;
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            if (DBG) {
-                Slog.w(TAG, "Profile service for profile: " + mClassName + " died.");
-            }
-            onServiceDisconnected(mClassName);
-            // Trigger rebind
-            Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
-            msg.obj = this;
-            mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
-        }
-    }
-
-    private void sendBluetoothStateCallback(boolean isUp) {
-        try {
-            int n = mStateChangeCallbacks.beginBroadcast();
-            if (DBG) {
-                Slog.d(TAG, "Broadcasting onBluetoothStateChange(" + isUp + ") to " + n
-                        + " receivers.");
-            }
-            for (int i = 0; i < n; i++) {
-                try {
-                    mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i, e);
-                }
-            }
-        } finally {
-            mStateChangeCallbacks.finishBroadcast();
-        }
-    }
-
-    /**
-     * Inform BluetoothAdapter instances that Adapter service is up
-     */
-    private void sendBluetoothServiceUpCallback() {
-        synchronized (mCallbacks) {
-            try {
-                int n = mCallbacks.beginBroadcast();
-                Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
-                    }
-                }
-            } finally {
-                mCallbacks.finishBroadcast();
-            }
-        }
-    }
-
-    /**
-     * Inform BluetoothAdapter instances that Adapter service is down
-     */
-    private void sendBluetoothServiceDownCallback() {
-        synchronized (mCallbacks) {
-            try {
-                int n = mCallbacks.beginBroadcast();
-                Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
-                    }
-                }
-            } finally {
-                mCallbacks.finishBroadcast();
-            }
-        }
-    }
-
-    public String getAddress(AttributionSource attributionSource) {
-        if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) {
-            return null;
-        }
-
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getAddress(): not allowed for non-active and non system user");
-            return null;
-        }
-
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS)
-                != PackageManager.PERMISSION_GRANTED) {
-            return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
-        }
-
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                return mBluetooth.getAddressWithAttribution(attributionSource);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG,
-                    "getAddress(): Unable to retrieve address remotely. Returning cached address",
-                    e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-
-        // mAddress is accessed from outside.
-        // It is alright without a lock. Here, bluetooth is off, no other thread is
-        // changing mAddress
-        return mAddress;
-    }
-
-    public String getName(AttributionSource attributionSource) {
-        if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) {
-            return null;
-        }
-
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getName(): not allowed for non-active and non system user");
-            return null;
-        }
-
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                return mBluetooth.getName(attributionSource);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-
-        // mName is accessed from outside.
-        // It alright without a lock. Here, bluetooth is off, no other thread is
-        // changing mName
-        return mName;
-    }
-
-    private class BluetoothServiceConnection implements ServiceConnection {
-        public void onServiceConnected(ComponentName componentName, IBinder service) {
-            String name = componentName.getClassName();
-            if (DBG) {
-                Slog.d(TAG, "BluetoothServiceConnection: " + name);
-            }
-            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
-            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
-                msg.arg1 = SERVICE_IBLUETOOTH;
-            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
-                msg.arg1 = SERVICE_IBLUETOOTHGATT;
-            } else {
-                Slog.e(TAG, "Unknown service connected: " + name);
-                return;
-            }
-            msg.obj = service;
-            mHandler.sendMessage(msg);
-        }
-
-        public void onServiceDisconnected(ComponentName componentName) {
-            // Called if we unexpectedly disconnect.
-            String name = componentName.getClassName();
-            if (DBG) {
-                Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
-            }
-            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
-            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
-                msg.arg1 = SERVICE_IBLUETOOTH;
-            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
-                msg.arg1 = SERVICE_IBLUETOOTHGATT;
-            } else {
-                Slog.e(TAG, "Unknown service disconnected: " + name);
-                return;
-            }
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
-
-    private class BluetoothHandler extends Handler {
-        boolean mGetNameAddressOnly = false;
-        private int mWaitForEnableRetry;
-        private int mWaitForDisableRetry;
-
-        BluetoothHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_GET_NAME_AND_ADDRESS:
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
-                    }
-                    try {
-                        mBluetoothLock.writeLock().lock();
-                        if ((mBluetooth == null) && (!mBinding)) {
-                            if (DBG) {
-                                Slog.d(TAG, "Binding to service to get name and address");
-                            }
-                            mGetNameAddressOnly = true;
-                            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
-                            mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
-                            Intent i = new Intent(IBluetooth.class.getName());
-                            if (!doBind(i, mConnection,
-                                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                                    UserHandle.CURRENT)) {
-                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-                            } else {
-                                mBinding = true;
-                            }
-                        } else if (mBluetooth != null) {
-                            try {
-                                storeNameAndAddress(
-                                        mBluetooth.getName(mContext.getAttributionSource()),
-                                        mBluetooth.getAddressWithAttribution(
-                                                mContext.getAttributionSource()));
-                            } catch (RemoteException re) {
-                                Slog.e(TAG, "Unable to grab names", re);
-                            }
-                            if (mGetNameAddressOnly && !mEnable) {
-                                unbindAndFinish();
-                            }
-                            mGetNameAddressOnly = false;
-                        }
-                    } finally {
-                        mBluetoothLock.writeLock().unlock();
-                    }
-                    break;
-
-                case MESSAGE_ENABLE:
-                    int quietEnable = msg.arg1;
-                    int isBle  = msg.arg2;
-                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
-                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
-                        // We are handling enable or disable right now, wait for it.
-                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
-                                quietEnable, isBle), ENABLE_DISABLE_DELAY_MS);
-                        break;
-                    }
-
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = "
-                                + mBluetooth);
-                    }
-                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                    mEnable = true;
-
-                    if (isBle == 0) {
-                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                    }
-
-                    // Use service interface to get the exact state
-                    try {
-                        mBluetoothLock.readLock().lock();
-                        if (mBluetooth != null) {
-                            boolean isHandled = true;
-                            int state = mBluetooth.getState();
-                            switch (state) {
-                                case BluetoothAdapter.STATE_BLE_ON:
-                                    if (isBle == 1) {
-                                        Slog.i(TAG, "Already at BLE_ON State");
-                                    } else {
-                                        Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
-                                        mBluetooth.onLeServiceUp(mContext.getAttributionSource());
-                                    }
-                                    break;
-                                case BluetoothAdapter.STATE_BLE_TURNING_ON:
-                                case BluetoothAdapter.STATE_TURNING_ON:
-                                case BluetoothAdapter.STATE_ON:
-                                    Slog.i(TAG, "MESSAGE_ENABLE: already enabled");
-                                    break;
-                                default:
-                                    isHandled = false;
-                                    break;
-                            }
-                            if (isHandled) break;
-                        }
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "", e);
-                    } finally {
-                        mBluetoothLock.readLock().unlock();
-                    }
-
-                    mQuietEnable = (quietEnable == 1);
-                    if (mBluetooth == null) {
-                        handleEnable(mQuietEnable);
-                    } else {
-                        //
-                        // We need to wait until transitioned to STATE_OFF and
-                        // the previous Bluetooth process has exited. The
-                        // waiting period has three components:
-                        // (a) Wait until the local state is STATE_OFF. This
-                        //     is accomplished by sending delay a message
-                        //     MESSAGE_HANDLE_ENABLE_DELAYED
-                        // (b) Wait until the STATE_OFF state is updated to
-                        //     all components.
-                        // (c) Wait until the Bluetooth process exits, and
-                        //     ActivityManager detects it.
-                        // The waiting for (b) and (c) is accomplished by
-                        // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE
-                        // message. The delay time is backed off if Bluetooth
-                        // continuously failed to turn on itself.
-                        //
-                        mWaitForEnableRetry = 0;
-                        Message enableDelayedMsg =
-                                mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
-                        mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                    }
-                    break;
-
-                case MESSAGE_DISABLE:
-                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding
-                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
-                        // We are handling enable or disable right now, wait for it.
-                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE),
-                                ENABLE_DISABLE_DELAY_MS);
-                        break;
-                    }
-
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth
-                                + ", mBinding = " + mBinding);
-                    }
-                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-
-                    if (mEnable && mBluetooth != null) {
-                        mWaitForDisableRetry = 0;
-                        Message disableDelayedMsg =
-                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
-                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                    } else {
-                        mEnable = false;
-                        handleDisable();
-                    }
-                    break;
-
-                case MESSAGE_HANDLE_ENABLE_DELAYED: {
-                    // The Bluetooth is turning off, wait for STATE_OFF
-                    if (mState != BluetoothAdapter.STATE_OFF) {
-                        if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
-                            mWaitForEnableRetry++;
-                            Message enableDelayedMsg =
-                                    mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
-                            mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                            break;
-                        } else {
-                            Slog.e(TAG, "Wait for STATE_OFF timeout");
-                        }
-                    }
-                    // Either state is changed to STATE_OFF or reaches the maximum retry, we
-                    // should move forward to the next step.
-                    mWaitForEnableRetry = 0;
-                    Message restartMsg =
-                            mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                    mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
-                    Slog.d(TAG, "Handle enable is finished");
-                    break;
-                }
-
-                case MESSAGE_HANDLE_DISABLE_DELAYED: {
-                    boolean disabling = (msg.arg1 == 1);
-                    Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling);
-                    if (!disabling) {
-                        // The Bluetooth is turning on, wait for STATE_ON
-                        if (mState != BluetoothAdapter.STATE_ON) {
-                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
-                                mWaitForDisableRetry++;
-                                Message disableDelayedMsg = mHandler.obtainMessage(
-                                        MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
-                                mHandler.sendMessageDelayed(disableDelayedMsg,
-                                        ENABLE_DISABLE_DELAY_MS);
-                                break;
-                            } else {
-                                Slog.e(TAG, "Wait for STATE_ON timeout");
-                            }
-                        }
-                        // Either state is changed to STATE_ON or reaches the maximum retry, we
-                        // should move forward to the next step.
-                        mWaitForDisableRetry = 0;
-                        mEnable = false;
-                        handleDisable();
-                        // Wait for state exiting STATE_ON
-                        Message disableDelayedMsg =
-                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
-                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                    } else {
-                        // The Bluetooth is turning off, wait for exiting STATE_ON
-                        if (mState == BluetoothAdapter.STATE_ON) {
-                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
-                                mWaitForDisableRetry++;
-                                Message disableDelayedMsg = mHandler.obtainMessage(
-                                        MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
-                                mHandler.sendMessageDelayed(disableDelayedMsg,
-                                        ENABLE_DISABLE_DELAY_MS);
-                                break;
-                            } else {
-                                Slog.e(TAG, "Wait for exiting STATE_ON timeout");
-                            }
-                        }
-                        // Either state is exited from STATE_ON or reaches the maximum retry, we
-                        // should move forward to the next step.
-                        Slog.d(TAG, "Handle disable is finished");
-                    }
-                    break;
-                }
-
-                case MESSAGE_RESTORE_USER_SETTING:
-                    if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
-                        if (DBG) {
-                            Slog.d(TAG, "Restore Bluetooth state to disabled");
-                        }
-                        persistBluetoothSetting(BLUETOOTH_OFF);
-                        mEnableExternal = false;
-                        sendDisableMsg(
-                                BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING,
-                                mContext.getPackageName());
-                    } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) {
-                        if (DBG) {
-                            Slog.d(TAG, "Restore Bluetooth state to enabled");
-                        }
-                        mQuietEnableExternal = false;
-                        mEnableExternal = true;
-                        // waive WRITE_SECURE_SETTINGS permission check
-                        sendEnableMsg(false,
-                                BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING,
-                                mContext.getPackageName());
-                    }
-                    break;
-                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
-                    IBluetoothStateChangeCallback callback =
-                            (IBluetoothStateChangeCallback) msg.obj;
-                    mStateChangeCallbacks.register(callback);
-                    break;
-                }
-                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: {
-                    IBluetoothStateChangeCallback callback =
-                            (IBluetoothStateChangeCallback) msg.obj;
-                    mStateChangeCallbacks.unregister(callback);
-                    break;
-                }
-                case MESSAGE_ADD_PROXY_DELAYED: {
-                    ProfileServiceConnections psc = mProfileServices.get(msg.arg1);
-                    if (psc == null) {
-                        break;
-                    }
-                    IBluetoothProfileServiceConnection proxy =
-                            (IBluetoothProfileServiceConnection) msg.obj;
-                    psc.addProxy(proxy);
-                    break;
-                }
-                case MESSAGE_BIND_PROFILE_SERVICE: {
-                    ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
-                    removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
-                    if (psc == null) {
-                        break;
-                    }
-                    psc.bindService();
-                    break;
-                }
-                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
-                    }
-
-                    IBinder service = (IBinder) msg.obj;
-                    try {
-                        mBluetoothLock.writeLock().lock();
-                        if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
-                            mBluetoothGatt =
-                                    IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));
-                            continueFromBleOnState();
-                            break;
-                        } // else must be SERVICE_IBLUETOOTH
-
-                        //Remove timeout
-                        mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-
-                        mBinding = false;
-                        mBluetoothBinder = service;
-                        mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));
-
-                        if (!isNameAndAddressSet()) {
-                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
-                            mHandler.sendMessage(getMsg);
-                            if (mGetNameAddressOnly) {
-                                return;
-                            }
-                        }
-
-                        //Register callback object
-                        try {
-                            mBluetooth.registerCallback(mBluetoothCallback,
-                                    mContext.getAttributionSource());
-                        } catch (RemoteException re) {
-                            Slog.e(TAG, "Unable to register BluetoothCallback", re);
-                        }
-                        //Inform BluetoothAdapter instances that service is up
-                        sendBluetoothServiceUpCallback();
-
-                        //Do enable request
-                        try {
-                            if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
-                                Slog.e(TAG, "IBluetooth.enable() returned false");
-                            }
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "Unable to call enable()", e);
-                        }
-                    } finally {
-                        mBluetoothLock.writeLock().unlock();
-                    }
-
-                    if (!mEnable) {
-                        waitForState(Set.of(BluetoothAdapter.STATE_ON));
-                        handleDisable();
-                        waitForState(Set.of(BluetoothAdapter.STATE_OFF,
-                                BluetoothAdapter.STATE_TURNING_ON,
-                                BluetoothAdapter.STATE_TURNING_OFF,
-                                BluetoothAdapter.STATE_BLE_TURNING_ON,
-                                BluetoothAdapter.STATE_BLE_ON,
-                                BluetoothAdapter.STATE_BLE_TURNING_OFF));
-                    }
-                    break;
-                }
-                case MESSAGE_BLUETOOTH_STATE_CHANGE: {
-                    int prevState = msg.arg1;
-                    int newState = msg.arg2;
-                    if (DBG) {
-                        Slog.d(TAG,
-                                "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(
-                                        prevState) + " > " + BluetoothAdapter.nameForState(
-                                        newState));
-                    }
-                    mState = newState;
-                    bluetoothStateChangeHandler(prevState, newState);
-                    // handle error state transition case from TURNING_ON to OFF
-                    // unbind and rebind bluetooth service and enable bluetooth
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState
-                            == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) {
-                        recoverBluetoothServiceFromError(false);
-                    }
-                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState
-                            == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) {
-                        recoverBluetoothServiceFromError(true);
-                    }
-                    // If we tried to enable BT while BT was in the process of shutting down,
-                    // wait for the BT process to fully tear down and then force a restart
-                    // here.  This is a bit of a hack (b/29363429).
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState
-                            == BluetoothAdapter.STATE_OFF)) {
-                        if (mEnable) {
-                            Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
-                            waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-                            Message restartMsg =
-                                    mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                            mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
-                        }
-                    }
-                    if (newState == BluetoothAdapter.STATE_ON
-                            || newState == BluetoothAdapter.STATE_BLE_ON) {
-                        // bluetooth is working, reset the counter
-                        if (mErrorRecoveryRetryCounter != 0) {
-                            Slog.w(TAG, "bluetooth is recovered from error");
-                            mErrorRecoveryRetryCounter = 0;
-                        }
-                    }
-                    break;
-                }
-                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: {
-                    Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
-                    try {
-                        mBluetoothLock.writeLock().lock();
-                        if (msg.arg1 == SERVICE_IBLUETOOTH) {
-                            // if service is unbinded already, do nothing and return
-                            if (mBluetooth == null) {
-                                break;
-                            }
-                            mBluetooth = null;
-                        } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
-                            mBluetoothGatt = null;
-                            break;
-                        } else {
-                            Slog.e(TAG, "Unknown argument for service disconnect!");
-                            break;
-                        }
-                    } finally {
-                        mBluetoothLock.writeLock().unlock();
-                    }
-
-                    // log the unexpected crash
-                    addCrashLog();
-                    addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH,
-                            mContext.getPackageName(), false);
-                    if (mEnable) {
-                        mEnable = false;
-                        // Send a Bluetooth Restart message
-                        Message restartMsg =
-                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
-                    }
-
-                    sendBluetoothServiceDownCallback();
-
-                    // Send BT state broadcast to update
-                    // the BT icon correctly
-                    if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState
-                            == BluetoothAdapter.STATE_ON)) {
-                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                                BluetoothAdapter.STATE_TURNING_OFF);
-                        mState = BluetoothAdapter.STATE_TURNING_OFF;
-                    }
-                    if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
-                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                                BluetoothAdapter.STATE_OFF);
-                    }
-
-                    mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-                    mState = BluetoothAdapter.STATE_OFF;
-                    break;
-                }
-                case MESSAGE_RESTART_BLUETOOTH_SERVICE: {
-                    mErrorRecoveryRetryCounter++;
-                    Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE: retry count="
-                            + mErrorRecoveryRetryCounter);
-                    if (mErrorRecoveryRetryCounter < MAX_ERROR_RESTART_RETRIES) {
-                        /* Enable without persisting the setting as
-                         it doesnt change when IBluetooth
-                         service restarts */
-                        mEnable = true;
-                        addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED,
-                                mContext.getPackageName(), true);
-                        handleEnable(mQuietEnable);
-                    } else {
-                        Slog.e(TAG, "Reach maximum retry to restart Bluetooth!");
-                    }
-                    break;
-                }
-                case MESSAGE_TIMEOUT_BIND: {
-                    Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
-                    mBluetoothLock.writeLock().lock();
-                    mBinding = false;
-                    mBluetoothLock.writeLock().unlock();
-                    break;
-                }
-                case MESSAGE_TIMEOUT_UNBIND: {
-                    Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
-                    mBluetoothLock.writeLock().lock();
-                    mUnbinding = false;
-                    mBluetoothLock.writeLock().unlock();
-                    break;
-                }
-
-                case MESSAGE_USER_SWITCHED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_USER_SWITCHED");
-                    }
-                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
-
-                    /* disable and enable BT when detect a user switch */
-                    if (mBluetooth != null && isEnabled()) {
-                        restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH);
-                    } else if (mBinding || mBluetooth != null) {
-                        Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
-                        userMsg.arg2 = 1 + msg.arg2;
-                        // if user is switched when service is binding retry after a delay
-                        mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
-                        if (DBG) {
-                            Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2);
-                        }
-                    }
-                    break;
-                }
-                case MESSAGE_USER_UNLOCKED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
-                    }
-                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
-
-                    if (mEnable && !mBinding && (mBluetooth == null)) {
-                        // We should be connected, but we gave up for some
-                        // reason; maybe the Bluetooth service wasn't encryption
-                        // aware, so try binding again.
-                        if (DBG) {
-                            Slog.d(TAG, "Enabled but not bound; retrying after unlock");
-                        }
-                        handleEnable(mQuietEnable);
-                    }
-                    break;
-                }
-                case MESSAGE_INIT_FLAGS_CHANGED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
-                    }
-                    mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
-                    if (mBluetoothModeChangeHelper.isMediaProfileConnected()) {
-                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
-                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
-                                + " ms due to existing connections");
-                        mHandler.sendEmptyMessageDelayed(
-                                MESSAGE_INIT_FLAGS_CHANGED,
-                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
-                        break;
-                    }
-                    if (!isDeviceProvisioned()) {
-                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
-                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
-                                +  "ms because device is not provisioned");
-                        mHandler.sendEmptyMessageDelayed(
-                                MESSAGE_INIT_FLAGS_CHANGED,
-                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
-                        break;
-                    }
-                    if (mBluetooth != null && isEnabled()) {
-                        Slog.i(TAG, "Restarting Bluetooth due to init flag change");
-                        restartForReason(
-                                BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
-                    }
-                    break;
-                }
-            }
-        }
-
-        @RequiresPermission(allOf = {
-                android.Manifest.permission.BLUETOOTH_CONNECT,
-                android.Manifest.permission.BLUETOOTH_PRIVILEGED
-        })
-        private void restartForReason(int reason) {
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    mBluetooth.unregisterCallback(mBluetoothCallback,
-                            mContext.getAttributionSource());
-                }
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Unable to unregister", re);
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-
-            if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
-                // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE
-                bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF);
-                mState = BluetoothAdapter.STATE_OFF;
-            }
-            if (mState == BluetoothAdapter.STATE_OFF) {
-                bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON);
-                mState = BluetoothAdapter.STATE_TURNING_ON;
-            }
-
-            waitForState(Set.of(BluetoothAdapter.STATE_ON));
-
-            if (mState == BluetoothAdapter.STATE_TURNING_ON) {
-                bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
-            }
-
-            unbindAllBluetoothProfileServices();
-            // disable
-            addActiveLog(reason, mContext.getPackageName(), false);
-            handleDisable();
-            // Pbap service need receive STATE_TURNING_OFF intent to close
-            bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                    BluetoothAdapter.STATE_TURNING_OFF);
-
-            boolean didDisableTimeout =
-                    !waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-
-            bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                    BluetoothAdapter.STATE_OFF);
-            sendBluetoothServiceDownCallback();
-
-            try {
-                mBluetoothLock.writeLock().lock();
-                if (mBluetooth != null) {
-                    mBluetooth = null;
-                    // Unbind
-                    mContext.unbindService(mConnection);
-                }
-                mBluetoothGatt = null;
-            } finally {
-                mBluetoothLock.writeLock().unlock();
-            }
-
-            //
-            // If disabling Bluetooth times out, wait for an
-            // additional amount of time to ensure the process is
-            // shut down completely before attempting to restart.
-            //
-            if (didDisableTimeout) {
-                SystemClock.sleep(3000);
-            } else {
-                SystemClock.sleep(100);
-            }
-
-            mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-            mState = BluetoothAdapter.STATE_OFF;
-            // enable
-            addActiveLog(reason, mContext.getPackageName(), true);
-            // mEnable flag could have been reset on disableBLE. Reenable it.
-            mEnable = true;
-            handleEnable(mQuietEnable);
-        }
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void handleEnable(boolean quietMode) {
-        mQuietEnable = quietMode;
-
-        try {
-            mBluetoothLock.writeLock().lock();
-            if ((mBluetooth == null) && (!mBinding)) {
-                Slog.d(TAG, "binding Bluetooth service");
-                //Start bind timeout and bind
-                Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
-                mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
-                Intent i = new Intent(IBluetooth.class.getName());
-                if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                        UserHandle.CURRENT)) {
-                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-                } else {
-                    mBinding = true;
-                }
-            } else if (mBluetooth != null) {
-                //Enable bluetooth
-                try {
-                    if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
-                        Slog.e(TAG, "IBluetooth.enable() returned false");
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call enable()", e);
-                }
-            }
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-    }
-
-    boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
-            Slog.e(TAG, "Fail to bind to: " + intent);
-            return false;
-        }
-        return true;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void handleDisable() {
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                if (DBG) {
-                    Slog.d(TAG, "Sending off request.");
-                }
-                if (!mBluetooth.disable(mContext.getAttributionSource())) {
-                    Slog.e(TAG, "IBluetooth.disable() returned false");
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to call disable()", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-    }
-
-    private boolean checkIfCallerIsForegroundUser() {
-        int foregroundUser;
-        int callingUser = UserHandle.getCallingUserId();
-        int callingUid = Binder.getCallingUid();
-        final long callingIdentity = Binder.clearCallingIdentity();
-        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        UserInfo ui = um.getProfileParent(callingUser);
-        int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
-        int callingAppId = UserHandle.getAppId(callingUid);
-        boolean valid = false;
-        try {
-            foregroundUser = ActivityManager.getCurrentUser();
-            valid = (callingUser == foregroundUser) || parentUser == foregroundUser
-                    || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid
-                    || callingAppId == Process.SHELL_UID;
-            if (DBG && !valid) {
-                Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser="
-                        + callingUser + " parentUser=" + parentUser + " foregroundUser="
-                        + foregroundUser);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-        return valid;
-    }
-
-    private void sendBleStateChanged(int prevState, int newState) {
-        if (DBG) {
-            Slog.d(TAG,
-                    "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
-                            + BluetoothAdapter.nameForState(newState));
-        }
-        // Send broadcast message to everyone else
-        Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
-        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions());
-    }
-
-    private boolean isBleState(int state) {
-        switch (state) {
-            case BluetoothAdapter.STATE_BLE_ON:
-            case BluetoothAdapter.STATE_BLE_TURNING_ON:
-            case BluetoothAdapter.STATE_BLE_TURNING_OFF:
-                return true;
-        }
-        return false;
-    }
-
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    private void bluetoothStateChangeHandler(int prevState, int newState) {
-        boolean isStandardBroadcast = true;
-        if (prevState == newState) { // No change. Nothing to do.
-            return;
-        }
-        // Notify all proxy objects first of adapter state change
-        if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) {
-            boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
-                    && newState == BluetoothAdapter.STATE_BLE_ON);
-
-            if (newState == BluetoothAdapter.STATE_OFF) {
-                // If Bluetooth is off, send service down event to proxy objects, and unbind
-                if (DBG) {
-                    Slog.d(TAG, "Bluetooth is complete send Service Down");
-                }
-                sendBluetoothServiceDownCallback();
-                unbindAndFinish();
-                sendBleStateChanged(prevState, newState);
-
-                /* Currently, the OFF intent is broadcasted externally only when we transition
-                 * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state,
-                 * we are guaranteed that the OFF intent has been broadcasted earlier and we
-                 * can safely skip it.
-                 * Conversely, if the previous state is not a BLE state, it indicates that some
-                 * sort of crash has occurred, moving us directly to STATE_OFF without ever
-                 * passing through BLE_ON. We should broadcast the OFF intent in this case. */
-                isStandardBroadcast = !isBleState(prevState);
-
-            } else if (!intermediate_off) {
-                // connect to GattService
-                if (DBG) {
-                    Slog.d(TAG, "Bluetooth is in LE only mode");
-                }
-                if (mBluetoothGatt != null || !mContext.getPackageManager()
-                            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
-                    continueFromBleOnState();
-                } else {
-                    if (DBG) {
-                        Slog.d(TAG, "Binding Bluetooth GATT service");
-                    }
-                    Intent i = new Intent(IBluetoothGatt.class.getName());
-                    doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                            UserHandle.CURRENT);
-                }
-                sendBleStateChanged(prevState, newState);
-                //Don't broadcase this as std intent
-                isStandardBroadcast = false;
-
-            } else if (intermediate_off) {
-                if (DBG) {
-                    Slog.d(TAG, "Intermediate off, back to LE only mode");
-                }
-                // For LE only mode, broadcast as is
-                sendBleStateChanged(prevState, newState);
-                sendBluetoothStateCallback(false); // BT is OFF for general users
-                // Broadcast as STATE_OFF
-                newState = BluetoothAdapter.STATE_OFF;
-                sendBrEdrDownCallback(mContext.getAttributionSource());
-            }
-        } else if (newState == BluetoothAdapter.STATE_ON) {
-            boolean isUp = (newState == BluetoothAdapter.STATE_ON);
-            sendBluetoothStateCallback(isUp);
-            sendBleStateChanged(prevState, newState);
-
-        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
-                || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            sendBleStateChanged(prevState, newState);
-            isStandardBroadcast = false;
-
-        } else if (newState == BluetoothAdapter.STATE_TURNING_ON
-                || newState == BluetoothAdapter.STATE_TURNING_OFF) {
-            sendBleStateChanged(prevState, newState);
-        }
-
-        if (isStandardBroadcast) {
-            if (prevState == BluetoothAdapter.STATE_BLE_ON) {
-                // Show prevState of BLE_ON as OFF to standard users
-                prevState = BluetoothAdapter.STATE_OFF;
-            }
-            if (DBG) {
-                Slog.d(TAG,
-                        "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
-                                + BluetoothAdapter.nameForState(newState));
-            }
-            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null,
-                    getTempAllowlistBroadcastOptions());
-        }
-    }
-
-    private boolean waitForState(Set<Integer> states) {
-        int i = 0;
-        while (i < 10) {
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth == null) {
-                    break;
-                }
-                if (states.contains(mBluetooth.getState())) {
-                    return true;
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "getState()", e);
-                break;
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-            SystemClock.sleep(300);
-            i++;
-        }
-        Slog.e(TAG, "waitForState " + states + " time out");
-        return false;
-    }
-
-    private void sendDisableMsg(int reason, String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE));
-        addActiveLog(reason, packageName, false);
-    }
-
-    private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
-        sendEnableMsg(quietMode, reason, packageName, false);
-    }
-
-    private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0,
-                  isBle ? 1 : 0));
-        addActiveLog(reason, packageName, true);
-        mLastEnabledTime = SystemClock.elapsedRealtime();
-    }
-
-    private void addActiveLog(int reason, String packageName, boolean enable) {
-        synchronized (mActiveLogs) {
-            if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) {
-                mActiveLogs.remove();
-            }
-            mActiveLogs.add(
-                    new ActiveLog(reason, packageName, enable, System.currentTimeMillis()));
-        }
-
-        int state = enable ? FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED :
-                             FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
-        FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
-                Binder.getCallingUid(), null, state, reason, packageName);
-    }
-
-    private void addCrashLog() {
-        synchronized (mCrashTimestamps) {
-            if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) {
-                mCrashTimestamps.removeFirst();
-            }
-            mCrashTimestamps.add(System.currentTimeMillis());
-            mCrashes++;
-        }
-    }
-
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    private void recoverBluetoothServiceFromError(boolean clearBle) {
-        Slog.e(TAG, "recoverBluetoothServiceFromError");
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                //Unregister callback object
-                mBluetooth.unregisterCallback(mBluetoothCallback, mContext.getAttributionSource());
-            }
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to unregister", re);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-
-        SystemClock.sleep(500);
-
-        // disable
-        addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR,
-                mContext.getPackageName(), false);
-        handleDisable();
-
-        waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-
-        sendBluetoothServiceDownCallback();
-
-        try {
-            mBluetoothLock.writeLock().lock();
-            if (mBluetooth != null) {
-                mBluetooth = null;
-                // Unbind
-                mContext.unbindService(mConnection);
-            }
-            mBluetoothGatt = null;
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-
-        mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-        mState = BluetoothAdapter.STATE_OFF;
-
-        if (clearBle) {
-            clearBleApps();
-        }
-
-        mEnable = false;
-
-        // Send a Bluetooth Restart message to reenable bluetooth
-        Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-        mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS);
-    }
-
-    private boolean isBluetoothDisallowed() {
-        final long callingIdentity = Binder.clearCallingIdentity();
-        try {
-            return mContext.getSystemService(UserManager.class)
-                    .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM);
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-    }
-
-    /**
-     * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not
-     * offered to the user if Bluetooth or sharing is disallowed. Puts the component to its default
-     * state if Bluetooth is not disallowed.
-     *
-     * @param userId user to disable bluetooth sharing for.
-     * @param bluetoothSharingDisallowed whether bluetooth sharing is disallowed.
-     */
-    private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) {
-        final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
-                "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
-        final int newState =
-                bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-        try {
-            final IPackageManager imp = AppGlobals.getPackageManager();
-            imp.setComponentEnabledSetting(oppLauncherComponent, newState,
-                    PackageManager.DONT_KILL_APP, userId);
-        } catch (Exception e) {
-            // The component was not found, do nothing.
-        }
-    }
-
-    private int getServiceRestartMs() {
-        return (mErrorRecoveryRetryCounter + 1) * SERVICE_RESTART_TIME_MS;
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
-            return;
-        }
-        if ((args.length > 0) && args[0].startsWith("--proto")) {
-            dumpProto(fd);
-            return;
-        }
-        String errorMsg = null;
-
-        writer.println("Bluetooth Status");
-        writer.println("  enabled: " + isEnabled());
-        writer.println("  state: " + BluetoothAdapter.nameForState(mState));
-        writer.println("  address: " + mAddress);
-        writer.println("  name: " + mName);
-        if (mEnable) {
-            long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
-            String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
-                    (int) (onDuration / (1000 * 60 * 60)),
-                    (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
-                    (int) (onDuration % 1000));
-            writer.println("  time since enabled: " + onDurationString);
-        }
-
-        if (mActiveLogs.size() == 0) {
-            writer.println("\nBluetooth never enabled!");
-        } else {
-            writer.println("\nEnable log:");
-            for (ActiveLog log : mActiveLogs) {
-                writer.println("  " + log);
-            }
-        }
-
-        writer.println(
-                "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
-        if (mCrashes == CRASH_LOG_MAX_SIZE) {
-            writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
-        }
-        for (Long time : mCrashTimestamps) {
-            writer.println("  " + timeToLog(time));
-        }
-
-        writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
-                + " registered");
-        for (ClientDeathRecipient app : mBleApps.values()) {
-            writer.println("  " + app.getPackageName());
-        }
-
-        writer.println("\nBluetoothManagerService:");
-        writer.println("  mEnable:" + mEnable);
-        writer.println("  mQuietEnable:" + mQuietEnable);
-        writer.println("  mEnableExternal:" + mEnableExternal);
-        writer.println("  mQuietEnableExternal:" + mQuietEnableExternal);
-
-        writer.println("");
-        writer.flush();
-        if (args.length == 0) {
-            // Add arg to produce output
-            args = new String[1];
-            args[0] = "--print";
-        }
-
-        if (mBluetoothBinder == null) {
-            errorMsg = "Bluetooth Service not connected";
-        } else {
-            try {
-                mBluetoothBinder.dump(fd, args);
-            } catch (RemoteException re) {
-                errorMsg = "RemoteException while dumping Bluetooth Service";
-            }
-        }
-        if (errorMsg != null) {
-            writer.println(errorMsg);
-        }
-    }
-
-    private void dumpProto(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
-        proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled());
-        proto.write(BluetoothManagerServiceDumpProto.STATE, mState);
-        proto.write(BluetoothManagerServiceDumpProto.STATE_NAME,
-                BluetoothAdapter.nameForState(mState));
-        proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress);
-        proto.write(BluetoothManagerServiceDumpProto.NAME, mName);
-        if (mEnable) {
-            proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime);
-        }
-        proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS,
-                SystemClock.elapsedRealtime());
-        for (ActiveLog log : mActiveLogs) {
-            long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS);
-            log.dump(proto);
-            proto.end(token);
-        }
-        proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes);
-        proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED,
-                mCrashes == CRASH_LOG_MAX_SIZE);
-        for (Long time : mCrashTimestamps) {
-            proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time);
-        }
-        proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size());
-        for (ClientDeathRecipient app : mBleApps.values()) {
-            proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES,
-                    app.getPackageName());
-        }
-        proto.flush();
-    }
-
-    private static String getEnableDisableReasonString(int reason) {
-        switch (reason) {
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST:
-                return "APPLICATION_REQUEST";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE:
-                return "AIRPLANE_MODE";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED:
-                return "DISALLOWED";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED:
-                return "RESTARTED";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR:
-                return "START_ERROR";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT:
-                return "SYSTEM_BOOT";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH:
-                return "CRASH";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH:
-                return "USER_SWITCH";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING:
-                return "RESTORE_USER_SETTING";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET:
-                return "FACTORY_RESET";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED:
-                return "INIT_FLAGS_CHANGED";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED:
-            default: return "UNKNOWN[" + reason + "]";
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private static boolean checkPermissionForDataDelivery(Context context, String permission,
-            AttributionSource attributionSource, String message) {
-        PermissionManager pm = context.getSystemService(PermissionManager.class);
-        if (pm == null) {
-            return false;
-        }
-        AttributionSource currentAttribution = new AttributionSource
-                .Builder(context.getAttributionSource())
-                .setNext(attributionSource)
-                .build();
-        final int result = pm.checkPermissionForDataDeliveryFromDataSource(permission,
-                currentAttribution, message);
-        if (result == PERMISSION_GRANTED) {
-            return true;
-        }
-
-        final String msg = "Need " + permission + " permission for " + attributionSource + ": "
-                + message;
-        if (result == PERMISSION_HARD_DENIED) {
-            throw new SecurityException(msg);
-        } else {
-            Log.w(TAG, msg);
-            return false;
-        }
-    }
-
-    /**
-     * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns
-     * false if the result is a soft denial. Throws SecurityException if the result is a hard
-     * denial.
-     *
-     * <p>Should be used in situations where the app op should not be noted.
-     */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public static boolean checkConnectPermissionForDataDelivery(
-            Context context, AttributionSource attributionSource, String message) {
-        return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT,
-                attributionSource, message);
-    }
-
-    static @NonNull Bundle getTempAllowlistBroadcastOptions() {
-        final long duration = 10_000;
-        final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
-        bOptions.setTemporaryAppAllowlist(duration,
-                TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
-                PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, "");
-        return bOptions.toBundle();
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
deleted file mode 100644
index e5854c9..0000000
--- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
-import android.content.Context;
-import android.content.res.Resources;
-import android.provider.Settings;
-import android.widget.Toast;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Helper class that handles callout and callback methods without
- * complex logic.
- */
-public class BluetoothModeChangeHelper {
-    private volatile BluetoothA2dp mA2dp;
-    private volatile BluetoothHearingAid mHearingAid;
-    private volatile BluetoothLeAudio mLeAudio;
-    private final BluetoothAdapter mAdapter;
-    private final Context mContext;
-
-    BluetoothModeChangeHelper(Context context) {
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mContext = context;
-
-        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
-        mAdapter.getProfileProxy(mContext, mProfileServiceListener,
-                BluetoothProfile.HEARING_AID);
-        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO);
-    }
-
-    private final ServiceListener mProfileServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            // Setup Bluetooth profile proxies
-            switch (profile) {
-                case BluetoothProfile.A2DP:
-                    mA2dp = (BluetoothA2dp) proxy;
-                    break;
-                case BluetoothProfile.HEARING_AID:
-                    mHearingAid = (BluetoothHearingAid) proxy;
-                    break;
-                case BluetoothProfile.LE_AUDIO:
-                    mLeAudio = (BluetoothLeAudio) proxy;
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(int profile) {
-            // Clear Bluetooth profile proxies
-            switch (profile) {
-                case BluetoothProfile.A2DP:
-                    mA2dp = null;
-                    break;
-                case BluetoothProfile.HEARING_AID:
-                    mHearingAid = null;
-                    break;
-                case BluetoothProfile.LE_AUDIO:
-                    mLeAudio = null;
-                    break;
-                default:
-                    break;
-            }
-        }
-    };
-
-    @VisibleForTesting
-    public boolean isMediaProfileConnected() {
-        return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected();
-    }
-
-    @VisibleForTesting
-    public boolean isBluetoothOn() {
-        final BluetoothAdapter adapter = mAdapter;
-        if (adapter == null) {
-            return false;
-        }
-        return adapter.getLeState() == BluetoothAdapter.STATE_ON;
-    }
-
-    @VisibleForTesting
-    public boolean isAirplaneModeOn() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
-    @VisibleForTesting
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onAirplaneModeChanged(BluetoothManagerService managerService) {
-        managerService.onAirplaneModeChanged();
-    }
-
-    @VisibleForTesting
-    public int getSettingsInt(String name) {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                name, 0);
-    }
-
-    @VisibleForTesting
-    public void setSettingsInt(String name, int value) {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                name, value);
-    }
-
-    @VisibleForTesting
-    public void showToastMessage() {
-        Resources r = mContext.getResources();
-        final CharSequence text = r.getString(
-                R.string.bluetooth_airplane_mode_toast, 0);
-        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
-    }
-
-    private boolean isA2dpConnected() {
-        final BluetoothA2dp a2dp = mA2dp;
-        if (a2dp == null) {
-            return false;
-        }
-        return a2dp.getConnectedDevices().size() > 0;
-    }
-
-    private boolean isHearingAidConnected() {
-        final BluetoothHearingAid hearingAid = mHearingAid;
-        if (hearingAid == null) {
-            return false;
-        }
-        return hearingAid.getConnectedDevices().size() > 0;
-    }
-
-    private boolean isLeAudioConnected() {
-        final BluetoothLeAudio leAudio = mLeAudio;
-        if (leAudio == null) {
-            return false;
-        }
-        return leAudio.getConnectedDevices().size() > 0;
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
deleted file mode 100644
index 1a1eecd..0000000
--- a/services/core/java/com/android/server/BluetoothService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.os.UserManager;
-
-import com.android.server.SystemService.TargetUser;
-
-class BluetoothService extends SystemService {
-    private BluetoothManagerService mBluetoothManagerService;
-    private boolean mInitialized = false;
-
-    public BluetoothService(Context context) {
-        super(context);
-        mBluetoothManagerService = new BluetoothManagerService(context);
-    }
-
-    private void initialize() {
-        if (!mInitialized) {
-            mBluetoothManagerService.handleOnBootPhase();
-            mInitialized = true;
-        }
-    }
-
-    @Override
-    public void onStart() {
-    }
-
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,
-                    mBluetoothManagerService);
-        } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY &&
-                !UserManager.isHeadlessSystemUserMode()) {
-            initialize();
-        }
-    }
-
-    @Override
-    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
-        if (!mInitialized) {
-            initialize();
-        } else {
-            mBluetoothManagerService.handleOnSwitchUser(to.getUserIdentifier());
-        }
-    }
-
-    @Override
-    public void onUserUnlocking(@NonNull TargetUser user) {
-        mBluetoothManagerService.handleOnUnlockUser(user.getUserIdentifier());
-    }
-}
diff --git a/services/core/java/com/android/server/Dumpable.java b/services/core/java/com/android/server/Dumpable.java
index 866f81c..004f923 100644
--- a/services/core/java/com/android/server/Dumpable.java
+++ b/services/core/java/com/android/server/Dumpable.java
@@ -24,6 +24,8 @@
  *
  * <p>See {@link SystemServer.SystemServerDumper} for usage example.
  */
+// TODO(b/149254050): replace / merge with package android.util.Dumpable (it would require
+// exporting IndentingPrintWriter as @SystemApi) and/or changing the method to use a prefix
 public interface Dumpable {
 
     /**
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 513d86e7..be2b7f7 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -16,11 +16,14 @@
 
 package com.android.server;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.ProgressDialog;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -126,8 +129,8 @@
 
     private boolean wipeUser(Context context, @UserIdInt int userId, String wipeReason) {
         final UserManager userManager = context.getSystemService(UserManager.class);
-        final int result = userManager.removeUserOrSetEphemeral(
-                userId, /* evenWhenDisallowed= */ false);
+        final int result = userManager.removeUserWhenPossible(
+                UserHandle.of(userId), /* overrideDevicePolicy= */ false);
         if (result == UserManager.REMOVE_RESULT_ERROR) {
             Slogf.e(TAG, "Can't remove user %d", userId);
             return false;
@@ -155,7 +158,7 @@
         final Notification notification =
                 new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
                         .setSmallIcon(android.R.drawable.stat_sys_warning)
-                        .setContentTitle(context.getString(R.string.work_profile_deleted))
+                        .setContentTitle(getWorkProfileDeletedTitle(context))
                         .setContentText(wipeReason)
                         .setColor(context.getColor(R.color.system_notification_accent_color))
                         .setStyle(new Notification.BigTextStyle().bigText(wipeReason))
@@ -164,6 +167,12 @@
                 SystemMessageProto.SystemMessage.NOTE_PROFILE_WIPED, notification);
     }
 
+    private String getWorkProfileDeletedTitle(Context context) {
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(WORK_PROFILE_DELETED_TITLE,
+                () -> context.getString(R.string.work_profile_deleted));
+    }
+
     private @UserIdInt int getCurrentForegroundUserId() {
         try {
             return ActivityManager.getCurrentUser();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a2c2dbd..3951680 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -39,6 +39,8 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.TrafficStats.UID_TETHERING;
 
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
+
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -133,12 +135,6 @@
 
     private static final int MAX_UID_RANGES_PER_COMMAND = 10;
 
-    /**
-     * Name representing {@link #setGlobalAlert(long)} limit when delivered to
-     * {@link INetworkManagementEventObserver#limitReached(String, String)}.
-     */
-    public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
-
     static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
 
     static final boolean MODIFY_OPERATION_ADD = true;
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index 1abe458..e915fa1 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.annotation.EnforcePermission;
 import android.content.Context;
 import android.hardware.ISerialManager;
 import android.os.ParcelFileDescriptor;
@@ -34,9 +35,8 @@
                 com.android.internal.R.array.config_serialPorts);
     }
 
+    @EnforcePermission(android.Manifest.permission.SERIAL_PORT)
     public String[] getSerialPorts() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
-
         ArrayList<String> ports = new ArrayList<String>();
         for (int i = 0; i < mSerialPorts.length; i++) {
             String path = mSerialPorts[i];
@@ -49,8 +49,8 @@
         return result;
     }
 
+    @EnforcePermission(android.Manifest.permission.SERIAL_PORT)
     public ParcelFileDescriptor openSerialPort(String path) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
         for (int i = 0; i < mSerialPorts.length; i++) {
             if (mSerialPorts[i].equals(path)) {
                 return native_open(path);
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
new file mode 100644
index 0000000..2dff72f
--- /dev/null
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+public class SmartStorageMaintIdler extends JobService {
+    private static final String TAG = "SmartStorageMaintIdler";
+
+    private static final ComponentName SMART_STORAGE_MAINT_SERVICE =
+            new ComponentName("android", SmartStorageMaintIdler.class.getName());
+
+    private static final int SMART_MAINT_JOB_ID = 2808;
+
+    private boolean mStarted;
+    private JobParameters mJobParams;
+    private final Runnable mFinishCallback = new Runnable() {
+        @Override
+        public void run() {
+            Slog.i(TAG, "Got smart storage maintenance service completion callback");
+            if (mStarted) {
+                jobFinished(mJobParams, false);
+                mStarted = false;
+            }
+            // ... and try again in a next period
+            scheduleSmartIdlePass(SmartStorageMaintIdler.this,
+                StorageManagerService.SMART_IDLE_MAINT_PERIOD);
+        }
+    };
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        mJobParams = params;
+        StorageManagerService ms = StorageManagerService.sSelf;
+        if (ms != null) {
+            mStarted = true;
+            ms.runSmartIdleMaint(mFinishCallback);
+        }
+        return ms != null;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        mStarted = false;
+        return false;
+    }
+
+    /**
+     * Schedule the smart storage idle maintenance job
+     */
+    public static void scheduleSmartIdlePass(Context context, int nHours) {
+        StorageManagerService ms = StorageManagerService.sSelf;
+        if ((ms == null) || ms.isPassedLifetimeThresh()) {
+            return;
+        }
+
+        JobScheduler tm = context.getSystemService(JobScheduler.class);
+
+        long nextScheduleTime = TimeUnit.HOURS.toMillis(nHours);
+
+        JobInfo.Builder builder = new JobInfo.Builder(SMART_MAINT_JOB_ID,
+            SMART_STORAGE_MAINT_SERVICE);
+
+        builder.setMinimumLatency(nextScheduleTime);
+        tm.schedule(builder.build());
+    }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 780afd8..f71f02a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -79,6 +79,7 @@
 import android.content.res.ObbInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.DropBoxManager;
@@ -170,6 +171,8 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.PrintWriter;
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
@@ -340,7 +343,44 @@
 
     @Nullable public static String sMediaStoreAuthorityProcessName;
 
+    // Run period in hour for smart idle maintenance
+    static final int SMART_IDLE_MAINT_PERIOD = 1;
+
     private final AtomicFile mSettingsFile;
+    private final AtomicFile mHourlyWriteFile;
+
+    private static final int MAX_HOURLY_WRITE_RECORDS = 72;
+
+    /**
+     * Default config values for smart idle maintenance
+     * Actual values will be controlled by DeviceConfig
+     */
+    // Decide whether smart idle maintenance is enabled or not
+    private static final boolean DEFAULT_SMART_IDLE_MAINT_ENABLED = false;
+    // Storage lifetime percentage threshold to decide to turn off the feature
+    private static final int DEFAULT_LIFETIME_PERCENT_THRESHOLD = 70;
+    // Minimum required number of dirty + free segments to trigger GC
+    private static final int DEFAULT_MIN_SEGMENTS_THRESHOLD = 512;
+    // Determine how much portion of current dirty segments will be GCed
+    private static final float DEFAULT_DIRTY_RECLAIM_RATE = 0.5F;
+    // Multiplier to amplify the target segment number for GC
+    private static final float DEFAULT_SEGMENT_RECLAIM_WEIGHT = 1.0F;
+    // Low battery level threshold to decide to turn off the feature
+    private static final float DEFAULT_LOW_BATTERY_LEVEL = 20F;
+    // Decide whether charging is required to turn on the feature
+    private static final boolean DEFAULT_CHARGING_REQUIRED = true;
+
+    private volatile int mLifetimePercentThreshold;
+    private volatile int mMinSegmentsThreshold;
+    private volatile float mDirtyReclaimRate;
+    private volatile float mSegmentReclaimWeight;
+    private volatile float mLowBatteryLevel;
+    private volatile boolean mChargingRequired;
+    private volatile boolean mNeedGC;
+
+    private volatile boolean mPassedLifetimeThresh;
+    // Tracking storage hourly write amounts
+    private volatile int[] mStorageHourlyWrites;
 
     /**
      * <em>Never</em> hold the lock while performing downcalls into vold, since
@@ -896,6 +936,10 @@
     }
 
     private void handleSystemReady() {
+        if (prepareSmartIdleMaint()) {
+            SmartStorageMaintIdler.scheduleSmartIdlePass(mContext, SMART_IDLE_MAINT_PERIOD);
+        }
+
         // Start scheduling nominally-daily fstrim operations
         MountServiceIdler.scheduleIdlePass(mContext);
 
@@ -1912,6 +1956,10 @@
 
         mSettingsFile = new AtomicFile(
                 new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");
+        mHourlyWriteFile = new AtomicFile(
+                new File(Environment.getDataSystemDirectory(), "storage-hourly-writes"));
+
+        mStorageHourlyWrites = new int[MAX_HOURLY_WRITE_RECORDS];
 
         synchronized (mLock) {
             readSettingsLocked();
@@ -2574,7 +2622,7 @@
             // fstrim time is still updated. If file based checkpoints are used, we run
             // idle maintenance (GC + fstrim) regardless of checkpoint status.
             if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
-                mVold.runIdleMaint(new IVoldTaskListener.Stub() {
+                mVold.runIdleMaint(mNeedGC, new IVoldTaskListener.Stub() {
                     @Override
                     public void onStatus(int status, PersistableBundle extras) {
                         // Not currently used
@@ -2625,6 +2673,176 @@
         abortIdleMaint(null);
     }
 
+    private boolean prepareSmartIdleMaint() {
+        /**
+         * We can choose whether going with a new storage smart idle maintenance job
+         * or falling back to the traditional way using DeviceConfig
+         */
+        boolean smartIdleMaintEnabled = DeviceConfig.getBoolean(
+            DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+            "smart_idle_maint_enabled",
+            DEFAULT_SMART_IDLE_MAINT_ENABLED);
+        if (smartIdleMaintEnabled) {
+            mLifetimePercentThreshold = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "lifetime_threshold", DEFAULT_LIFETIME_PERCENT_THRESHOLD);
+            mMinSegmentsThreshold = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "min_segments_threshold", DEFAULT_MIN_SEGMENTS_THRESHOLD);
+            mDirtyReclaimRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "dirty_reclaim_rate", DEFAULT_DIRTY_RECLAIM_RATE);
+            mSegmentReclaimWeight = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "segment_reclaim_weight", DEFAULT_SEGMENT_RECLAIM_WEIGHT);
+            mLowBatteryLevel = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "low_battery_level", DEFAULT_LOW_BATTERY_LEVEL);
+            mChargingRequired = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "charging_required", DEFAULT_CHARGING_REQUIRED);
+
+            // If we use the smart idle maintenance, we need to turn off GC in the traditional idle
+            // maintenance to avoid the conflict
+            mNeedGC = false;
+
+            loadStorageHourlyWrites();
+            try {
+                mVold.refreshLatestWrite();
+            } catch (Exception e) {
+                Slog.wtf(TAG, e);
+            }
+            refreshLifetimeConstraint();
+        }
+        return smartIdleMaintEnabled;
+    }
+
+    // Return whether storage lifetime exceeds the threshold
+    public boolean isPassedLifetimeThresh() {
+        return mPassedLifetimeThresh;
+    }
+
+    private void loadStorageHourlyWrites() {
+        FileInputStream fis = null;
+
+        try {
+            fis = mHourlyWriteFile.openRead();
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            mStorageHourlyWrites = (int[])ois.readObject();
+        } catch (FileNotFoundException e) {
+            // Missing data is okay, probably first boot
+        } catch (Exception e) {
+            Slog.wtf(TAG, "Failed reading write records", e);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    private int getAverageHourlyWrite() {
+        return Arrays.stream(mStorageHourlyWrites).sum() / MAX_HOURLY_WRITE_RECORDS;
+    }
+
+    private void updateStorageHourlyWrites(int latestWrite) {
+        FileOutputStream fos = null;
+
+        System.arraycopy(mStorageHourlyWrites,0, mStorageHourlyWrites, 1,
+                     MAX_HOURLY_WRITE_RECORDS - 1);
+        mStorageHourlyWrites[0] = latestWrite;
+        try {
+            fos = mHourlyWriteFile.startWrite();
+            ObjectOutputStream oos = new ObjectOutputStream(fos);
+            oos.writeObject(mStorageHourlyWrites);
+            mHourlyWriteFile.finishWrite(fos);
+        } catch (IOException e) {
+            if (fos != null) {
+                mHourlyWriteFile.failWrite(fos);
+            }
+        }
+    }
+
+    private boolean checkChargeStatus() {
+        IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        Intent batteryStatus = mContext.registerReceiver(null, ifilter);
+
+        if (mChargingRequired) {
+            int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+            if (status != BatteryManager.BATTERY_STATUS_CHARGING &&
+                status != BatteryManager.BATTERY_STATUS_FULL) {
+                Slog.w(TAG, "Battery is not being charged");
+                return false;
+            }
+        }
+
+        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+        float chargePercent = level * 100f / (float)scale;
+
+        if (chargePercent < mLowBatteryLevel) {
+            Slog.w(TAG, "Battery level is " + chargePercent + ", which is lower than threshold: " +
+                        mLowBatteryLevel);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean refreshLifetimeConstraint() {
+        int storageLifeTime = 0;
+
+        try {
+            storageLifeTime = mVold.getStorageLifeTime();
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+            return false;
+        }
+
+        if (storageLifeTime == -1) {
+            Slog.w(TAG, "Failed to get storage lifetime");
+            return false;
+        } else if (storageLifeTime > mLifetimePercentThreshold) {
+            Slog.w(TAG, "Ended smart idle maintenance, because of lifetime(" + storageLifeTime +
+                        ")" + ", lifetime threshold(" + mLifetimePercentThreshold + ")");
+            mPassedLifetimeThresh = true;
+            return false;
+        }
+        Slog.i(TAG, "Storage lifetime: " + storageLifeTime);
+        return true;
+    }
+
+    void runSmartIdleMaint(Runnable callback) {
+        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+
+        try {
+            // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
+            // (first boot after OTA), We skip the smart idle maintenance
+            if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
+                if (!refreshLifetimeConstraint() || !checkChargeStatus()) {
+                    return;
+                }
+
+                int latestHourlyWrite = mVold.getWriteAmount();
+                if (latestHourlyWrite == -1) {
+                    Slog.w(TAG, "Failed to get storage hourly write");
+                    return;
+                }
+
+                updateStorageHourlyWrites(latestHourlyWrite);
+                int avgHourlyWrite = getAverageHourlyWrite();
+
+                Slog.i(TAG, "Set smart idle maintenance: " + "latest hourly write: " +
+                            latestHourlyWrite + ", average hourly write: " + avgHourlyWrite +
+                            ", min segment threshold: " + mMinSegmentsThreshold +
+                            ", dirty reclaim rate: " + mDirtyReclaimRate +
+                            ", segment reclaim weight:" + mSegmentReclaimWeight);
+                mVold.setGCUrgentPace(avgHourlyWrite, mMinSegmentsThreshold, mDirtyReclaimRate,
+                                      mSegmentReclaimWeight);
+            } else {
+                Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
+            }
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+        } finally {
+            if (callback != null) {
+                callback.run();
+            }
+        }
+    }
+
     @Override
     public void setDebugFlags(int flags, int mask) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
@@ -3480,16 +3698,29 @@
     @Nullable
     public PendingIntent getManageSpaceActivityIntent(
             @NonNull String packageName, int requestCode) {
-        // Only Apps with MANAGE_EXTERNAL_STORAGE permission should be able to call this API.
-        enforcePermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE);
-
-        // We want to call the manageSpaceActivity as a SystemService and clear identity
-        // of the calling App
+        // Only Apps with MANAGE_EXTERNAL_STORAGE permission which have package visibility for
+        // packageName should be able to call this API.
         int originalUid = Binder.getCallingUidOrThrow();
-        final long token = Binder.clearCallingIdentity();
-
         try {
-            ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
+            // Get package name for calling app and verify it has MANAGE_EXTERNAL_STORAGE permission
+            final String[] packagesFromUid = mIPackageManager.getPackagesForUid(originalUid);
+            if (packagesFromUid == null) {
+                throw new SecurityException("Unknown uid " + originalUid);
+            }
+            // Checking first entry in packagesFromUid is enough as using "sharedUserId"
+            // mechanism is rare and discouraged. Also, Apps that share same UID share the same
+            // permissions.
+            if (!mStorageManagerInternal.hasExternalStorageAccess(originalUid,
+                    packagesFromUid[0])) {
+                throw new SecurityException("Only File Manager Apps permitted");
+            }
+        } catch (RemoteException re) {
+            throw new SecurityException("Unknown uid " + originalUid, re);
+        }
+
+        ApplicationInfo appInfo;
+        try {
+            appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
                     UserHandle.getUserId(originalUid));
             if (appInfo == null) {
                 throw new IllegalArgumentException(
@@ -3499,8 +3730,15 @@
                 Log.i(TAG, packageName + " doesn't have a manageSpaceActivity");
                 return null;
             }
-            Context targetAppContext = mContext.createPackageContext(packageName, 0);
+        } catch (RemoteException e) {
+            throw new SecurityException("Only File Manager Apps permitted");
+        }
 
+        // We want to call the manageSpaceActivity as a SystemService and clear identity
+        // of the calling App
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Context targetAppContext = mContext.createPackageContext(packageName, 0);
             Intent intent = new Intent(Intent.ACTION_DEFAULT);
             intent.setClassName(packageName,
                     appInfo.manageSpaceActivityName);
@@ -3510,8 +3748,6 @@
                     intent,
                     FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
             return activity;
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
         } catch (PackageManager.NameNotFoundException e) {
             throw new IllegalArgumentException(
                     "packageName not found");
@@ -3846,7 +4082,7 @@
             final boolean primary = false;
             final boolean removable = false;
             final boolean emulated = true;
-            final boolean stub = false;
+            final boolean externallyManaged = false;
             final boolean allowMassStorage = false;
             final long maxFileSize = 0;
             final UserHandle user = new UserHandle(userId);
@@ -3854,7 +4090,8 @@
             final String description = mContext.getString(android.R.string.unknownName);
 
             res.add(new StorageVolume(id, path, path, description, primary, removable, emulated,
-                    stub, allowMassStorage, maxFileSize, user, null /*uuid */, id, envState));
+                    externallyManaged, allowMassStorage, maxFileSize, user, null /*uuid */, id,
+                    envState));
         }
 
         if (!foundPrimary) {
@@ -3869,7 +4106,7 @@
             final boolean primary = true;
             final boolean removable = primaryPhysical;
             final boolean emulated = !primaryPhysical;
-            final boolean stub = false;
+            final boolean externallyManaged = false;
             final boolean allowMassStorage = false;
             final long maxFileSize = 0L;
             final UserHandle owner = new UserHandle(userId);
@@ -3878,7 +4115,7 @@
             final String state = Environment.MEDIA_REMOVED;
 
             res.add(0, new StorageVolume(id, path, path,
-                    description, primary, removable, emulated, stub,
+                    description, primary, removable, emulated, externallyManaged,
                     allowMassStorage, maxFileSize, owner, uuid, fsUuid, state));
         }
 
@@ -4736,19 +4973,17 @@
         @Override
         public boolean hasExternalStorageAccess(int uid, String packageName) {
             try {
-                if (mIPackageManager.checkUidPermission(
-                                MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
-                    return true;
+                final int opMode = mIAppOpsService.checkOperation(
+                        OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
+                if (opMode == AppOpsManager.MODE_DEFAULT) {
+                    return mIPackageManager.checkUidPermission(
+                            MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED;
                 }
 
-                if (mIAppOpsService.checkOperation(
-                                OP_MANAGE_EXTERNAL_STORAGE, uid, packageName) == MODE_ALLOWED) {
-                    return true;
-                }
+                return opMode == AppOpsManager.MODE_ALLOWED;
             } catch (RemoteException e) {
                 Slog.w("Failed to check MANAGE_EXTERNAL_STORAGE access for " + packageName, e);
             }
-
             return false;
         }
 
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 1208cb7..e7f4de2 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -26,6 +26,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -45,6 +46,9 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -91,7 +95,8 @@
     private long mRuntimeStartUptime;
 
     // Services that should receive lifecycle events.
-    private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
+    private List<SystemService> mServices;
+    private Set<String> mServiceClassnames;
 
     private int mCurrentPhase = -1;
 
@@ -113,11 +118,13 @@
 
     SystemServiceManager(Context context) {
         mContext = context;
+        mServices = new ArrayList<>();
+        mServiceClassnames = new ArraySet<>();
         // Disable using the thread pool for low ram devices
         sUseLifecycleThreadPool = sUseLifecycleThreadPool
-                                    && !ActivityManager.isLowRamDeviceStatic();
+                && !ActivityManager.isLowRamDeviceStatic();
         mNumUserPoolThreads = Math.min(Runtime.getRuntime().availableProcessors(),
-                                       DEFAULT_MAX_USER_POOL_THREADS);
+                DEFAULT_MAX_USER_POOL_THREADS);
     }
 
     /**
@@ -207,8 +214,17 @@
     }
 
     public void startService(@NonNull final SystemService service) {
+        // Check if already started
+        String className = service.getClass().getName();
+        if (mServiceClassnames.contains(className)) {
+            Slog.i(TAG, "Not starting an already started service " + className);
+            return;
+        }
+        mServiceClassnames.add(className);
+
         // Register it.
         mServices.add(service);
+
         // Start it.
         long time = SystemClock.elapsedRealtime();
         try {
@@ -220,11 +236,17 @@
         warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
     }
 
+    /** Disallow starting new services after this call. */
+    void sealStartedServices() {
+        mServiceClassnames = Collections.emptySet();
+        mServices = Collections.unmodifiableList(mServices);
+    }
+
     /**
      * Starts the specified boot phase for all system services that have been started up to
      * this point.
      *
-     * @param t trace logger
+     * @param t     trace logger
      * @param phase The boot phase to start.
      */
     public void startBootPhase(@NonNull TimingsTraceAndSlog t, int phase) {
@@ -398,8 +420,8 @@
         // Limit the lifecycle parallelization to all users other than the system user
         // and only for the user start lifecycle phase for now.
         final boolean useThreadPool = sUseLifecycleThreadPool
-                                        && curUserId != UserHandle.USER_SYSTEM
-                                        && onWhat.equals(USER_STARTING);
+                && curUserId != UserHandle.USER_SYSTEM
+                && onWhat.equals(USER_STARTING);
         final ExecutorService threadPool =
                 useThreadPool ? Executors.newFixedThreadPool(mNumUserPoolThreads) : null;
         for (int i = 0; i < serviceLen; i++) {
@@ -419,7 +441,7 @@
                             + serviceName + " because it's not supported (curUser: "
                             + curUser + ", prevUser:" + prevUser + ")");
                 } else {
-                    Slog.i(TAG,  "Skipping " + onWhat + "User-" + curUserId + " on "
+                    Slog.i(TAG, "Skipping " + onWhat + "User-" + curUserId + " on "
                             + serviceName);
                 }
                 continue;
@@ -516,6 +538,7 @@
 
     /**
      * Returns whether we are booting into safe mode.
+     *
      * @return safe mode flag
      */
     public boolean isSafeMode() {
@@ -559,9 +582,10 @@
 
     /**
      * Ensures that the system directory exist creating one if needed.
+     *
+     * @return The system directory.
      * @deprecated Use {@link Environment#getDataSystemCeDirectory()}
      * or {@link Environment#getDataSystemDeDirectory()} instead.
-     * @return The system directory.
      */
     @Deprecated
     public static File ensureSystemDir() {
@@ -578,7 +602,9 @@
         pw.printf("Current phase: %d\n", mCurrentPhase);
         synchronized (mTargetUsers) {
             if (mCurrentUser != null) {
-                pw.print("Current user: "); mCurrentUser.dump(pw); pw.println();
+                pw.print("Current user: ");
+                mCurrentUser.dump(pw);
+                pw.println();
             } else {
                 pw.println("Current user not set!");
             }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 81627a0..c236a7f 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -19,6 +19,9 @@
 import static android.app.UiModeManager.DEFAULT_PRIORITY;
 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
 import static android.app.UiModeManager.MODE_NIGHT_NO;
 import static android.app.UiModeManager.MODE_NIGHT_YES;
 import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
@@ -40,6 +43,7 @@
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.UiModeManager;
+import android.app.UiModeManager.NightModeCustomType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -115,6 +119,7 @@
 
     private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+    private int mNightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
     private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
     private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
     private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
@@ -136,6 +141,7 @@
     private boolean mWatch;
     private boolean mVrHeadset;
     private boolean mComputedNightMode;
+    private boolean mLastBedtimeRequestedNightMode = false;
     private int mCarModeEnableFlags;
     private boolean mSetupWizardComplete;
 
@@ -541,7 +547,9 @@
             mNightMode = Secure.getIntForUser(context.getContentResolver(),
                     Secure.UI_NIGHT_MODE, res.getInteger(
                             com.android.internal.R.integer.config_defaultNightMode), userId);
-            mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
+            mNightModeCustomType = Secure.getIntForUser(context.getContentResolver(),
+                    Secure.UI_NIGHT_MODE_CUSTOM_TYPE, MODE_NIGHT_CUSTOM_TYPE_UNKNOWN, userId);
+                    mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
                     Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
             mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
                     Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0;
@@ -702,6 +710,14 @@
 
         @Override
         public void setNightMode(int mode) {
+            // MODE_NIGHT_CUSTOM_TYPE_SCHEDULE is the default for MODE_NIGHT_CUSTOM.
+            int customModeType = mode == MODE_NIGHT_CUSTOM
+                    ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE
+                    : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
+            setNightModeInternal(mode, customModeType);
+        }
+
+        private void setNightModeInternal(int mode, int customModeType) {
             if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
                     != PackageManager.PERMISSION_GRANTED)) {
@@ -722,12 +738,14 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    if (mNightMode != mode) {
+                    if (mNightMode != mode || mNightModeCustomType != customModeType) {
                         if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                             unregisterScreenOffEventLocked();
                             cancelCustomAlarm();
                         }
-
+                        mNightModeCustomType = mode == MODE_NIGHT_CUSTOM
+                                ? customModeType
+                                : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
                         mNightMode = mode;
                         resetNightModeOverrideLocked();
                         persistNightMode(user);
@@ -754,6 +772,30 @@
         }
 
         @Override
+        public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) {
+            if (getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "setNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission");
+            }
+            setNightModeInternal(MODE_NIGHT_CUSTOM, nightModeCustomType);
+        }
+
+        @Override
+        public int getNightModeCustomType() {
+            if (getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "getNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission");
+            }
+            synchronized (mLock) {
+                return mNightModeCustomType;
+            }
+        }
+
+        @Override
         public void setApplicationNightMode(@UiModeManager.NightMode int mode) {
             switch (mode) {
                 case UiModeManager.MODE_NIGHT_NO:
@@ -808,10 +850,19 @@
         }
 
         @Override
+        public boolean setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active) {
+            return setNightModeActivatedForModeInternal(modeNightCustomType, active);
+        }
+
+        @Override
         public boolean setNightModeActivated(boolean active) {
-            if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
+            return setNightModeActivatedForModeInternal(mNightModeCustomType, active);
+        }
+
+        private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {
+            if (getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
-                    != PackageManager.PERMISSION_GRANTED)) {
+                    != PackageManager.PERMISSION_GRANTED) {
                 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
                 return false;
             }
@@ -824,6 +875,14 @@
                 return false;
 
             }
+            // Store the last requested bedtime night mode state so that we don't need to notify
+            // anyone if the user decides to switch to the night mode to bedtime.
+            if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+                mLastBedtimeRequestedNightMode = active;
+            }
+            if (modeCustomType != mNightModeCustomType) {
+                return false;
+            }
             synchronized (mLock) {
                 final long ident = Binder.clearCallingIdentity();
                 try {
@@ -1422,6 +1481,8 @@
         Secure.putIntForUser(getContext().getContentResolver(),
                 Secure.UI_NIGHT_MODE, mNightMode, user);
         Secure.putLongForUser(getContext().getContentResolver(),
+                Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mNightModeCustomType, user);
+        Secure.putLongForUser(getContext().getContentResolver(),
                 Secure.DARK_THEME_CUSTOM_START_TIME,
                 mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user);
         Secure.putLongForUser(getContext().getContentResolver(),
@@ -1473,10 +1534,14 @@
         }
 
         if (mNightMode == MODE_NIGHT_CUSTOM) {
-            registerTimeChangeEvent();
-            final boolean activate = computeCustomNightMode();
-            updateComputedNightModeLocked(activate);
-            scheduleNextCustomTimeListener();
+            if (mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+                updateComputedNightModeLocked(mLastBedtimeRequestedNightMode);
+            } else {
+                registerTimeChangeEvent();
+                final boolean activate = computeCustomNightMode();
+                updateComputedNightModeLocked(activate);
+                scheduleNextCustomTimeListener();
+            }
         } else {
             unregisterTimeChangeEvent();
         }
@@ -1494,6 +1559,7 @@
                     "updateConfigurationLocked: mDockState=" + mDockState
                     + "; mCarMode=" + mCarModeEnabled
                     + "; mNightMode=" + mNightMode
+                    + "; mNightModeCustomType=" + mNightModeCustomType
                     + "; uiMode=" + uiMode);
         }
 
@@ -1534,7 +1600,8 @@
     }
 
     private boolean shouldApplyAutomaticChangesImmediately() {
-        return mCar || !mPowerManager.isInteractive();
+        return mCar || !mPowerManager.isInteractive()
+                || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
     }
 
     private void scheduleNextCustomTimeListener() {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 0c990ec..6a7afd9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -24,7 +24,6 @@
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
-import static android.telephony.SubscriptionManager.isValidSubscriptionId;
 
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -164,10 +163,6 @@
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
 
-    // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
-    static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
-
     /* Binder context for this service */
     @NonNull private final Context mContext;
     @NonNull private final Dependencies mDeps;
@@ -372,12 +367,15 @@
 
     /** Notifies the VcnManagementService that external dependencies can be set up. */
     public void systemReady() {
-        mNetworkProvider.register();
-        mContext.getSystemService(ConnectivityManager.class)
-                .registerNetworkCallback(
-                        new NetworkRequest.Builder().clearCapabilities().build(),
-                        mTrackingNetworkCallback);
-        mTelephonySubscriptionTracker.register();
+        // Always run on the handler thread to ensure consistency.
+        mHandler.post(() -> {
+            mNetworkProvider.register();
+            mContext.getSystemService(ConnectivityManager.class)
+                    .registerNetworkCallback(
+                            new NetworkRequest.Builder().clearCapabilities().build(),
+                            mTrackingNetworkCallback);
+            mTelephonySubscriptionTracker.register();
+        });
     }
 
     private void enforcePrimaryUser() {
@@ -471,22 +469,15 @@
                         if (!mVcns.containsKey(subGrp)) {
                             startVcnLocked(subGrp, entry.getValue());
                         }
-
-                        // Cancel any scheduled teardowns for active subscriptions
-                        mHandler.removeCallbacksAndMessages(mVcns.get(subGrp));
                     }
                 }
 
-                // Schedule teardown of any VCN instances that have lost carrier privileges (after a
-                // delay)
+                // Schedule teardown of any VCN instances that have lost carrier privileges
                 for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
                     final ParcelUuid subGrp = entry.getKey();
                     final VcnConfig config = mConfigs.get(subGrp);
 
                     final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot);
-                    final boolean isValidActiveDataSubIdNotInVcnSubGrp =
-                            isValidSubscriptionId(snapshot.getActiveDataSubscriptionId())
-                                    && !isActiveSubGroup(subGrp, snapshot);
 
                     // TODO(b/193687515): Support multiple VCNs active at the same time
                     if (config == null
@@ -496,31 +487,12 @@
                         final ParcelUuid uuidToTeardown = subGrp;
                         final Vcn instanceToTeardown = entry.getValue();
 
-                        // TODO(b/193687515): Support multiple VCNs active at the same time
-                        // If directly switching to a subscription not in the current group,
-                        // teardown immediately to prevent other subscription's network from being
-                        // outscored by the VCN. Otherwise, teardown after a delay to ensure that
-                        // SIM profile switches do not trigger the VCN to cycle.
-                        final long teardownDelayMs =
-                                isValidActiveDataSubIdNotInVcnSubGrp
-                                        ? 0
-                                        : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS;
-                        mHandler.postDelayed(() -> {
-                            synchronized (mLock) {
-                                // Guard against case where this is run after a old instance was
-                                // torn down, and a new instance was started. Verify to ensure
-                                // correct instance is torn down. This could happen as a result of a
-                                // Carrier App manually removing/adding a VcnConfig.
-                                if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
-                                    stopVcnLocked(uuidToTeardown);
+                        stopVcnLocked(uuidToTeardown);
 
-                                    // TODO(b/181789060): invoke asynchronously after Vcn notifies
-                                    // through VcnCallback
-                                    notifyAllPermissionedStatusCallbacksLocked(
-                                            uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
-                                }
-                            }
-                        }, instanceToTeardown, teardownDelayMs);
+                        // TODO(b/181789060): invoke asynchronously after Vcn notifies
+                        // through VcnCallback
+                        notifyAllPermissionedStatusCallbacksLocked(
+                                uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
                     } else {
                         // If this VCN's status has not changed, update it with the new snapshot
                         entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f591b26..297d28d 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.Notification;
@@ -170,6 +171,12 @@
         mAdbConnectionInfo = new AdbConnectionInfo();
     }
 
+    static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent,
+            @NonNull UserHandle userHandle) {
+        context.sendBroadcastAsUser(intent, userHandle,
+                android.Manifest.permission.MANAGE_DEBUGGING);
+    }
+
     class PairingThread extends Thread implements NsdManager.RegistrationListener {
         private NsdManager mNsdManager;
         private String mPublicKey;
@@ -1278,7 +1285,7 @@
                     ? AdbManager.WIRELESS_STATUS_CONNECTED
                     : AdbManager.WIRELESS_STATUS_DISCONNECTED);
             intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
 
         private void onAdbdWifiServerConnected(int port) {
@@ -1350,7 +1357,8 @@
             if (publicKey == null) {
                 Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
                 intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
+                        UserHandle.ALL);
             } else {
                 Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
                 intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
@@ -1366,7 +1374,8 @@
                 device.guid = hostname;
                 device.connected = false;
                 intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
+                        UserHandle.ALL);
                 // Add the key into the keystore
                 mAdbKeyStore.setLastConnectionTime(publicKey,
                         System.currentTimeMillis());
@@ -1380,14 +1389,14 @@
             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
                     AdbManager.WIRELESS_STATUS_CONNECTED);
             intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
 
         private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
             // Map is not serializable, so need to downcast
             intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
 
         private void updateUIPairCode(String code) {
@@ -1397,7 +1406,7 @@
             intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
                     AdbManager.WIRELESS_STATUS_PAIRING_CODE);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
     }
 
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7a4d2ce..2845fbf 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -459,7 +459,7 @@
                 ? AdbManager.WIRELESS_STATUS_CONNECTED
                 : AdbManager.WIRELESS_STATUS_DISCONNECTED);
         intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         Slog.i(TAG, "sent port broadcast port=" + port);
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7993936..8887108 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3472,11 +3472,9 @@
     }
 
     private int getAllowMode(Intent service, @Nullable String callingPackage) {
-        if (callingPackage == null || service.getComponent() == null) {
-            return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
-        }
-        if (callingPackage.equals(service.getComponent().getPackageName())) {
-            return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
+        if (callingPackage != null && service.getComponent() != null
+                && callingPackage.equals(service.getComponent().getPackageName())) {
+            return ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
         } else {
             return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
         }
@@ -4140,7 +4138,7 @@
             // for a previous process to come up.  To deal with this, we store
             // in the service any current isolated process it is running in or
             // waiting to have come up.
-            app = r.isolatedProc;
+            app = r.isolationHostProc;
             if (WebViewZygote.isMultiprocessEnabled()
                     && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                 hostingRecord = HostingRecord.byWebviewZygote(r.instanceName);
@@ -4167,7 +4165,7 @@
                 return msg;
             }
             if (isolated) {
-                r.isolatedProc = app;
+                r.isolationHostProc = app;
             }
         }
 
@@ -4978,7 +4976,7 @@
             try {
                 for (int i=0; i<mPendingServices.size(); i++) {
                     sr = mPendingServices.get(i);
-                    if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+                    if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
                             || !processName.equals(sr.processName))) {
                         continue;
                     }
@@ -5018,7 +5016,7 @@
             boolean didImmediateRestart = false;
             for (int i=0; i<mRestartingServices.size(); i++) {
                 sr = mRestartingServices.get(i);
-                if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+                if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
                         || !processName.equals(sr.processName))) {
                     continue;
                 }
@@ -5050,9 +5048,9 @@
             ServiceRecord sr = mPendingServices.get(i);
             if ((proc.uid == sr.appInfo.uid
                     && proc.processName.equals(sr.processName))
-                    || sr.isolatedProc == proc) {
+                    || sr.isolationHostProc == proc) {
                 Slog.w(TAG, "Forcing bringing down service: " + sr);
-                sr.isolatedProc = null;
+                sr.isolationHostProc = null;
                 mPendingServices.remove(i);
                 size = mPendingServices.size();
                 i--;
@@ -5085,7 +5083,7 @@
                     stopServiceAndUpdateAllowlistManagerLocked(service);
                 }
                 service.setProcess(null, null, 0, null);
-                service.isolatedProc = null;
+                service.isolationHostProc = null;
                 if (mTmpCollectionResults == null) {
                     mTmpCollectionResults = new ArrayList<>();
                 }
@@ -5323,7 +5321,7 @@
                 sr.app.mServices.updateBoundClientUids();
             }
             sr.setProcess(null, null, 0, null);
-            sr.isolatedProc = null;
+            sr.isolationHostProc = null;
             sr.executeNesting = 0;
             synchronized (mAm.mProcessStats.mLock) {
                 sr.forceClearTracker();
@@ -6794,7 +6792,8 @@
                 r.mFgsNotificationShown,
                 durationMs,
                 r.mStartForegroundCount,
-                ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName));
+                ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+                r.mFgsHasNotificationPermission);
     }
 
     boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ebce30a..b1b4c44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -189,7 +189,6 @@
 import android.app.PropertyInvalidatedCache;
 import android.app.SyncNotedAppOp;
 import android.app.WaitResult;
-import android.app.WtfException;
 import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManager;
 import android.app.compat.CompatChanges;
@@ -234,11 +233,11 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ProviderInfoList;
 import android.content.pm.ResolveInfo;
-import android.content.pm.SELinuxUtil;
+import com.android.server.pm.pkg.SELinuxUtil;
 import android.content.pm.ServiceInfo;
 import android.content.pm.TestUtilityService;
 import android.content.pm.UserInfo;
-import android.content.pm.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -394,6 +393,7 @@
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.os.NativeTombstoneManager;
 import com.android.server.pm.Installer;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.uri.GrantUri;
@@ -2334,7 +2334,7 @@
         offloadConstants.SLOW_TIME = Integer.MAX_VALUE;
 
         mEnableOffloadQueue = SystemProperties.getBoolean(
-                "persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
+                "persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
 
         mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", foreConstants, false);
@@ -2871,13 +2871,51 @@
         return mode == AppOpsManager.MODE_ALLOWED;
     }
 
-    @Override
-    public int getPackageProcessState(String packageName, String callingPackage) {
-        if (!hasUsageStatsPermission(callingPackage)) {
-            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
-                    "getPackageProcessState");
+    /**
+     * Checks whether the calling package is trusted.
+     *
+     * The calling package is trusted if it's from system or the supposed package name matches the
+     * UID making the call.
+     *
+     * @throws SecurityException if the package name and UID don't match.
+     */
+    private void verifyCallingPackage(String callingPackage) {
+        final int callingUid = Binder.getCallingUid();
+        // The caller is System or Shell.
+        if (callingUid == SYSTEM_UID || isCallerShell()) {
+            return;
         }
 
+        // Handle the special UIDs that don't have real package (audioserver, cameraserver, etc).
+        final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid,
+                null /* packageName */);
+        if (resolvedPackage != null && resolvedPackage.equals(callingPackage)) {
+            return;
+        }
+
+        final int claimedUid = getPackageManagerInternal().getPackageUid(callingPackage,
+                0 /* flags */, UserHandle.getUserId(callingUid));
+        if (callingUid == claimedUid) {
+            return;
+        }
+
+        throw new SecurityException(
+                "Claimed calling package " + callingPackage + " does not match the calling UID "
+                        + Binder.getCallingUid());
+    }
+
+    private void enforceUsageStatsPermission(String callingPackage, String func) {
+        verifyCallingPackage(callingPackage);
+        // Since the protection level of PACKAGE_USAGE_STATS has 'appop', apps may grant this
+        // permission via that way. We need to check both app-ops and permission.
+        if (!hasUsageStatsPermission(callingPackage)) {
+            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, func);
+        }
+    }
+
+    @Override
+    public int getPackageProcessState(String packageName, String callingPackage) {
+        enforceUsageStatsPermission(callingPackage, "getPackageProcessState");
         final int[] procState = {PROCESS_STATE_NONEXISTENT};
         synchronized (mProcLock) {
             mProcessList.forEachLruProcessesLOSP(false, proc -> {
@@ -5823,7 +5861,7 @@
 
         return Arrays.binarySearch(allowlist, appId) >= 0
                 || Arrays.binarySearch(mDeviceIdleTempAllowlist, appId) >= 0
-                || mPendingTempAllowlist.indexOfKey(uid) >= 0;
+                || mPendingTempAllowlist.get(uid) != null;
     }
 
     /**
@@ -6938,11 +6976,7 @@
 
     @Override
     public int getUidProcessState(int uid, String callingPackage) {
-        if (!hasUsageStatsPermission(callingPackage)) {
-            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
-                    "getUidProcessState");
-        }
-
+        enforceUsageStatsPermission(callingPackage, "getUidProcessState");
         synchronized (mProcLock) {
             return mProcessList.getUidProcStateLOSP(uid);
         }
@@ -6950,11 +6984,7 @@
 
     @Override
     public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) {
-        if (!hasUsageStatsPermission(callingPackage)) {
-            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
-                    "getUidProcessState");
-        }
-
+        enforceUsageStatsPermission(callingPackage, "getUidProcessCapabilities");
         synchronized (mProcLock) {
             return mProcessList.getUidProcessCapabilityLOSP(uid);
         }
@@ -6963,10 +6993,7 @@
     @Override
     public void registerUidObserver(IUidObserver observer, int which, int cutpoint,
             String callingPackage) {
-        if (!hasUsageStatsPermission(callingPackage)) {
-            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
-                    "registerUidObserver");
-        }
+        enforceUsageStatsPermission(callingPackage, "registerUidObserver");
         mUidObserverController.register(observer, which, cutpoint, callingPackage,
                 Binder.getCallingUid());
     }
@@ -6978,10 +7005,7 @@
 
     @Override
     public boolean isUidActive(int uid, String callingPackage) {
-        if (!hasUsageStatsPermission(callingPackage)) {
-            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
-                    "isUidActive");
-        }
+        enforceUsageStatsPermission(callingPackage, "isUidActive");
         synchronized (mProcLock) {
             if (isUidActiveLOSP(uid)) {
                 return true;
@@ -10360,10 +10384,6 @@
             if (thread != null) {
                 pw.println("\n\n** Cache info for pid " + pid + " [" + r.processName + "] **");
                 pw.flush();
-                if (pid == MY_PID) {
-                    PropertyInvalidatedCache.dumpCacheInfo(fd, args);
-                    continue;
-                }
                 try {
                     TransferPipe tp = new TransferPipe();
                     try {
@@ -12635,7 +12655,6 @@
         int callingUid;
         int callingPid;
         boolean instantApp;
-        boolean throwWtfException = false;
         synchronized(this) {
             if (caller != null) {
                 callerApp = getRecordForAppLOSP(caller);
@@ -12730,9 +12749,13 @@
                                         + "RECEIVER_NOT_EXPORTED be specified when registering a "
                                         + "receiver");
                     } else {
-                        // will be removed when enforcement is required
+                        Slog.wtf(TAG,
+                                callerPackage + ": Targeting T+ (version "
+                                        + Build.VERSION_CODES.TIRAMISU
+                                        + " and above) requires that one of RECEIVER_EXPORTED or "
+                                        + "RECEIVER_NOT_EXPORTED be specified when registering a "
+                                        + "receiver");
                         // Assume default behavior-- flag check is not enforced
-                        throwWtfException = true;
                         flags |= Context.RECEIVER_EXPORTED;
                     }
                 } else if (!requireExplicitFlagForDynamicReceivers) {
@@ -12863,15 +12886,6 @@
                 }
             }
 
-            if (throwWtfException) {
-                throw new WtfException(
-                        callerPackage + ": Targeting T+ (version "
-                                + Build.VERSION_CODES.TIRAMISU
-                                + " and above) requires that one of RECEIVER_EXPORTED or "
-                                + "RECEIVER_NOT_EXPORTED be specified when registering a "
-                                + "receiver");
-            }
-
             return sticky;
         }
     }
@@ -13319,6 +13333,14 @@
             }
 
             switch (action) {
+                case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
+                    UserManagerInternal umInternal = LocalServices.getService(
+                            UserManagerInternal.class);
+                    UserInfo userInfo = umInternal.getUserInfo(userId);
+                    if (userInfo != null && userInfo.isCloneProfile()) {
+                        userId = umInternal.getProfileParentId(userId);
+                    }
+                    break;
                 case Intent.ACTION_UID_REMOVED:
                 case Intent.ACTION_PACKAGE_REMOVED:
                 case Intent.ACTION_PACKAGE_CHANGED:
@@ -13385,7 +13407,12 @@
                                         !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
                                 final boolean fullUninstall = removed && !replacing;
                                 if (removed) {
-                                    if (!killProcess) {
+                                    if (killProcess) {
+                                        forceStopPackageLocked(ssp, UserHandle.getAppId(
+                                                intent.getIntExtra(Intent.EXTRA_UID, -1)),
+                                                false, true, true, false, fullUninstall, userId,
+                                                removed ? "pkg removed" : "pkg changed");
+                                    } else {
                                         // Kill any app zygotes always, since they can't fork new
                                         // processes with references to the old code
                                         forceStopAppZygoteLocked(ssp, UserHandle.getAppId(
@@ -15122,11 +15149,13 @@
 
         // First copy out the pending changes...  we need to leave them in the map for now,
         // in case someone needs to check what is coming up while we don't have the lock held.
-        synchronized (mProcLock) {
-            N = mPendingTempAllowlist.size();
-            list = new PendingTempAllowlist[N];
-            for (int i = 0; i < N; i++) {
-                list[i] = mPendingTempAllowlist.valueAt(i);
+        synchronized (this) {
+            synchronized (mProcLock) {
+                N = mPendingTempAllowlist.size();
+                list = new PendingTempAllowlist[N];
+                for (int i = 0; i < N; i++) {
+                    list[i] = mPendingTempAllowlist.valueAt(i);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 878ef31f..5fe8427 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -163,7 +163,9 @@
 
                         synchronized (mService.mProcLock) {
                             errState.setNotResponding(false);
-                            errState.setNotRespondingReport(null);
+                            // We're not clearing the ANR report here, in case we'd need to report
+                            // it again when the ANR dialog shows again.
+                            // errState.setNotRespondingReport(null);
                             errState.getDialogController().clearAnrDialogs();
                         }
                         mService.mServices.scheduleServiceTimeoutLocked(app);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 336572f..8561b61 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -16,6 +16,7 @@
 package com.android.server.am;
 
 import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
@@ -712,8 +713,10 @@
             if (wifiInfo.isValid()) {
                 final long wifiChargeUC = measuredEnergyDeltas != null ?
                         measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
-                mStats.updateWifiState(
-                        extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
+                final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+                        NetworkStatsManager.class);
+                mStats.updateWifiState(extractDeltaLocked(wifiInfo),
+                        wifiChargeUC, elapsedRealtime, uptime, networkStatsManager);
             } else {
                 Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
             }
@@ -722,8 +725,10 @@
         if (modemInfo != null) {
             final long mobileRadioChargeUC = measuredEnergyDeltas != null
                     ? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+            final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+                    NetworkStatsManager.class);
             mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
-                    uptime);
+                    uptime, networkStatsManager);
         }
 
         if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5fc11e8..0b92954 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.app.StatsManager;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -41,6 +42,7 @@
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.Binder;
+import android.os.BluetoothBatteryStats;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -56,6 +58,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.WakeLockStats;
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
@@ -2028,8 +2031,11 @@
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
+            final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+                    NetworkStatsManager.class);
             mHandler.post(() -> {
-                mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
+                mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime,
+                        networkStatsManager);
             });
         }
     }
@@ -2066,9 +2072,11 @@
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
+            final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+                    NetworkStatsManager.class);
             mHandler.post(() -> {
                 mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
-                        uptime);
+                        uptime, networkStatsManager);
             });
         }
     }
@@ -2226,6 +2234,7 @@
         pw.println("  --read-daily: read-load last written daily stats.");
         pw.println("  --settings: dump the settings key/values related to batterystats");
         pw.println("  --cpu: dump cpu stats for debugging purpose");
+        pw.println("  --power-profile: dump the power profile constants");
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -2263,6 +2272,12 @@
         }
     }
 
+    private void dumpPowerProfile(PrintWriter pw) {
+        synchronized (mStats) {
+            mStats.dumpPowerProfileLocked(pw);
+        }
+    }
+
     private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
         i++;
         if (i >= args.length) {
@@ -2405,6 +2420,9 @@
                 } else  if ("--measured-energy".equals(arg)) {
                     dumpMeasuredEnergyStats(pw);
                     return;
+                } else if ("--power-profile".equals(arg)) {
+                    dumpPowerProfile(pw);
+                    return;
                 } else if ("-a".equals(arg)) {
                     flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2596,6 +2614,34 @@
     }
 
     /**
+     * Gets a snapshot of wake lock stats
+     * @hide
+     */
+    public WakeLockStats getWakeLockStats() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
+        synchronized (mStats) {
+            return mStats.getWakeLockStats();
+        }
+    }
+
+    /**
+     * Gets a snapshot of Bluetooth stats
+     * @hide
+     */
+    public BluetoothBatteryStats getBluetoothBatteryStats() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
+        synchronized (mStats) {
+            return mStats.getBluetoothBatteryStats();
+        }
+    }
+
+    /**
      * Gets a snapshot of the system health for a particular uid.
      */
     @Override
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2ec4a02..e36ea20 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -349,6 +349,11 @@
                 prr.removeCurReceiver(r);
             }
         }
+
+        // if something bad happens here, launch the app and try again
+        if (app.isKilled()) {
+            throw new RemoteException("app gets killed during broadcasting");
+        }
     }
 
     public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
@@ -776,6 +781,54 @@
                 skip = true;
             }
         }
+        // Check that the receiver does *not* have any excluded permissions
+        if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+            for (int i = 0; i < r.excludedPermissions.length; i++) {
+                String excludedPermission = r.excludedPermissions[i];
+                final int perm = mService.checkComponentPermission(excludedPermission,
+                        filter.receiverList.pid, filter.receiverList.uid, -1, true);
+
+                int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+                if (appOp != AppOpsManager.OP_NONE) {
+                    // When there is an app op associated with the permission,
+                    // skip when both the permission and the app op are
+                    // granted.
+                    if ((perm == PackageManager.PERMISSION_GRANTED) && (
+                            mService.getAppOpsManager().checkOpNoThrow(appOp,
+                                    filter.receiverList.uid,
+                                    filter.packageName)
+                                    == AppOpsManager.MODE_ALLOWED)) {
+                        Slog.w(TAG, "Appop Denial: receiving "
+                                + r.intent.toString()
+                                + " to " + filter.receiverList.app
+                                + " (pid=" + filter.receiverList.pid
+                                + ", uid=" + filter.receiverList.uid + ")"
+                                + " excludes appop " + AppOpsManager.permissionToOp(
+                                excludedPermission)
+                                + " due to sender " + r.callerPackage
+                                + " (uid " + r.callingUid + ")");
+                        skip = true;
+                        break;
+                    }
+                } else {
+                    // When there is no app op associated with the permission,
+                    // skip when permission is granted.
+                    if (perm == PackageManager.PERMISSION_GRANTED) {
+                        Slog.w(TAG, "Permission Denial: receiving "
+                                + r.intent.toString()
+                                + " to " + filter.receiverList.app
+                                + " (pid=" + filter.receiverList.pid
+                                + ", uid=" + filter.receiverList.uid + ")"
+                                + " excludes " + excludedPermission
+                                + " due to sender " + r.callerPackage
+                                + " (uid " + r.callingUid + ")");
+                        skip = true;
+                        break;
+                    }
+                }
+            }
+        }
+
         // If the broadcast also requires an app op check that as well.
         if (!skip && r.appOp != AppOpsManager.OP_NONE
                 && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c55bbe8..6f22c61 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -539,6 +539,8 @@
      */
     static private native void compactProcess(int pid, int compactionFlags);
 
+    static private native void cancelCompaction();
+
     /**
      * Reads the flag value from DeviceConfig to determine whether app compaction
      * should be enabled, and starts the freeze/compaction thread if needed.
@@ -960,7 +962,7 @@
         }
 
         opt.setFreezerOverride(false);
-        if (!opt.isFrozen()) {
+        if (pid == 0 || !opt.isFrozen()) {
             return;
         }
 
@@ -1049,6 +1051,26 @@
         }
     }
 
+    @GuardedBy({"mService", "mProcLock"})
+    void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
+        // Cancel any currently executing compactions
+        // if the process moved out of cached state
+        if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj
+                && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+            cancelCompaction();
+        }
+
+        // Perform a minor compaction when a perceptible app becomes the prev/home app
+        // Perform a major compaction when any app enters cached
+        if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
+                && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
+            compactAppSome(app);
+        } else if (newAdj >= ProcessList.CACHED_APP_MIN_ADJ
+                && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
+            compactAppFull(app);
+        }
+    }
+
     @VisibleForTesting
     static final class LastCompactionStats {
         private final long[] mRssAfterCompaction;
@@ -1091,6 +1113,13 @@
                         name = proc.processName;
                         opt.setHasPendingCompact(false);
 
+                        if (mAm.mInternal.isPendingTopUid(proc.uid)) {
+                            // In case the OOM Adjust has not yet been propagated we see if this is
+                            // pending on becoming top app in which case we should not compact.
+                            Slog.e(TAG_AM, "Skip compaction since UID is active for  " + name);
+                            return;
+                        }
+
                         // don't compact if the process has returned to perceptible
                         // and this is only a cached/home/prev compaction
                         if ((pendingAction == COMPACT_PROCESS_SOME
@@ -1500,6 +1529,8 @@
      * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class.
      */
     private static final class DefaultProcessDependencies implements ProcessDependencies {
+        public static int mPidCompacting = -1;
+
         // Get memory RSS from process.
         @Override
         public long[] getRss(int pid) {
@@ -1509,6 +1540,7 @@
         // Compact process.
         @Override
         public void performCompaction(String action, int pid) throws IOException {
+            mPidCompacting = pid;
             if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
                 compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
             } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
@@ -1516,6 +1548,7 @@
             } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
                 compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
             }
+            mPidCompacting = -1;
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 625dd63..3c5b872 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.am;
 
+import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
 import static android.os.Process.PROC_CHAR;
 import static android.os.Process.PROC_OUT_LONG;
 import static android.os.Process.PROC_PARENS;
@@ -46,6 +47,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
@@ -72,6 +74,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 import com.android.server.RescueParty;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.FileDescriptor;
@@ -177,12 +180,22 @@
                 cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
                 if (cpr != null) {
                     cpi = cpr.info;
+
                     if (mService.isSingleton(
                             cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
                                 && mService.isValidSingletonCall(
                                         r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
                         userId = UserHandle.USER_SYSTEM;
                         checkCrossUser = false;
+                    } else if (isAuthorityRedirectedForCloneProfile(name)) {
+                        UserManagerInternal umInternal = LocalServices.getService(
+                                UserManagerInternal.class);
+                        UserInfo userInfo = umInternal.getUserInfo(userId);
+
+                        if (userInfo != null && userInfo.isCloneProfile()) {
+                            userId = umInternal.getProfileParentId(userId);
+                            checkCrossUser = false;
+                        }
                     } else {
                         cpr = null;
                         cpi = null;
@@ -1026,6 +1039,7 @@
      * at the given authority and user.
      */
     String checkContentProviderAccess(String authority, int userId) {
+        boolean checkUser = true;
         if (userId == UserHandle.USER_ALL) {
             mService.mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
@@ -1041,6 +1055,17 @@
                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                     userId);
+            if (cpi == null && isAuthorityRedirectedForCloneProfile(authority)) {
+                // This might be a provider that's running only in the system user that's
+                // also serving clone profiles
+                cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
+                        ActivityManagerService.STOCK_PM_FLAGS
+                                | PackageManager.GET_URI_PERMISSION_PATTERNS
+                                | PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                        UserHandle.USER_SYSTEM);
+            }
         } catch (RemoteException ignored) {
         }
         if (cpi == null) {
@@ -1048,6 +1073,16 @@
                     + "; expected to find a valid ContentProvider for this authority";
         }
 
+        if (isAuthorityRedirectedForCloneProfile(authority)) {
+            UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+            UserInfo userInfo = umInternal.getUserInfo(userId);
+
+            if (userInfo != null && userInfo.isCloneProfile()) {
+                userId = umInternal.getProfileParentId(userId);
+                checkUser = false;
+            }
+        }
+
         final int callingPid = Binder.getCallingPid();
         ProcessRecord r;
         final String appName;
@@ -1060,7 +1095,7 @@
         }
 
         return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(),
-                userId, true, appName);
+                userId, checkUser, appName);
     }
 
     int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index af4ff58..bdfd02e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -598,7 +598,7 @@
             for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
                 ConnectionRecord cr = psr.getConnectionAt(i);
                 ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
-                        ? cr.binding.service.isolatedProc : cr.binding.service.app;
+                        ? cr.binding.service.isolationHostProc : cr.binding.service.app;
                 if (service == null || service == pr) {
                     continue;
                 }
@@ -2486,18 +2486,9 @@
         // don't compact during bootup
         if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
             // Cached and prev/home compaction
+            // reminder: here, setAdj is previous state, curAdj is upcoming state
             if (state.getCurAdj() != state.getSetAdj()) {
-                // Perform a minor compaction when a perceptible app becomes the prev/home app
-                // Perform a major compaction when any app enters cached
-                // reminder: here, setAdj is previous state, curAdj is upcoming state
-                if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ
-                        && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
-                            || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
-                    mCachedAppOptimizer.compactAppSome(app);
-                } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
-                        && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
-                    mCachedAppOptimizer.compactAppFull(app);
-                }
+                mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
             } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
                     && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
                     // Because these can fire independent of oom_adj/procstate changes, we need
diff --git a/services/core/java/com/android/server/am/PendingTempAllowlists.java b/services/core/java/com/android/server/am/PendingTempAllowlists.java
index 75935c4..0263de7 100644
--- a/services/core/java/com/android/server/am/PendingTempAllowlists.java
+++ b/services/core/java/com/android/server/am/PendingTempAllowlists.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static android.os.Process.INVALID_UID;
+
 import android.util.SparseArray;
 
 /** Allowlists of uids to temporarily bypass Power Save mode. */
@@ -31,29 +33,42 @@
     }
 
     void put(int uid, ActivityManagerService.PendingTempAllowlist value) {
-        mPendingTempAllowlist.put(uid, value);
+        synchronized (mPendingTempAllowlist) {
+            mPendingTempAllowlist.put(uid, value);
+        }
         mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag);
     }
 
     void removeAt(int index) {
-        final int uid = mPendingTempAllowlist.keyAt(index);
-        mPendingTempAllowlist.removeAt(index);
+        int uid = INVALID_UID;
+        synchronized (mPendingTempAllowlist) {
+            uid = mPendingTempAllowlist.keyAt(index);
+            mPendingTempAllowlist.removeAt(index);
+        }
         mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid);
     }
 
     ActivityManagerService.PendingTempAllowlist get(int uid) {
-        return mPendingTempAllowlist.get(uid);
+        synchronized (mPendingTempAllowlist) {
+            return mPendingTempAllowlist.get(uid);
+        }
     }
 
     int size() {
-        return mPendingTempAllowlist.size();
+        synchronized (mPendingTempAllowlist) {
+            return mPendingTempAllowlist.size();
+        }
     }
 
     ActivityManagerService.PendingTempAllowlist valueAt(int index) {
-        return mPendingTempAllowlist.valueAt(index);
+        synchronized (mPendingTempAllowlist) {
+            return mPendingTempAllowlist.valueAt(index);
+        }
     }
 
     int indexOfKey(int key) {
-        return mPendingTempAllowlist.indexOfKey(key);
+        synchronized (mPendingTempAllowlist) {
+            return mPendingTempAllowlist.indexOfKey(key);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 2def50e..1ad0bce 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -135,6 +135,7 @@
 import com.android.server.am.ActivityManagerService.ProcessChangeItem;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowManagerService;
@@ -2379,6 +2380,8 @@
             final String[] targetPackagesList = sharedPackages.length == 0
                     ? new String[]{app.info.packageName} : sharedPackages;
 
+            final boolean hasAppStorage = hasAppStorage(pmInt, app.info.packageName);
+
             pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
             if (pkgDataInfoMap == null) {
                 // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
@@ -2401,6 +2404,12 @@
                 bindMountAppsData = false;
             }
 
+            if (!hasAppStorage) {
+                bindMountAppsData = false;
+                pkgDataInfoMap = null;
+                allowlistedAppDataInfoMap = null;
+            }
+
             int userId = UserHandle.getUserId(uid);
             StorageManagerInternal storageManagerInternal = LocalServices.getService(
                     StorageManagerInternal.class);
@@ -2488,6 +2497,17 @@
         }
     }
 
+    private boolean hasAppStorage(PackageManagerInternal pmInt, String packageName) {
+        final AndroidPackage pkg = pmInt.getPackage(packageName);
+        if (pkg == null) {
+            Slog.w(TAG, "Unknown package " + packageName);
+            return false;
+        }
+        final PackageManager.Property noAppStorageProp =
+                    pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+        return noAppStorageProp == null || !noAppStorageProp.getBoolean();
+    }
+
     @GuardedBy("mService")
     void startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags) {
         startProcessLocked(app, hostingRecord, zygotePolicyFlags, null /* abiOverride */);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c830554..be187e2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -513,7 +513,7 @@
             }
         }
         processInfo = procInfo;
-        isolated = _info.uid != _uid;
+        isolated = Process.isIsolated(_uid);
         appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
                 && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
         uid = _uid;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 9b731d5..24e7ba4 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -102,7 +102,8 @@
                             // IBinder -> ConnectionRecord of all bound clients
 
     ProcessRecord app;      // where this service is running or null.
-    ProcessRecord isolatedProc; // keep track of isolated process, if requested
+    ProcessRecord isolationHostProc; // process which we've started for this service (used for
+                                     // isolated and supplemental processes)
     ServiceState tracker; // tracking service execution, may be null
     ServiceState restartTracker; // tracking service restart
     boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
@@ -174,6 +175,8 @@
     boolean mFgsNotificationWasDeferred;
     // FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place.
     boolean mFgsNotificationShown;
+    // Whether FGS package has permissions to show notifications.
+    boolean mFgsHasNotificationPermission;
 
     // allow the service becomes foreground service? Service started from background may not be
     // allowed to become a foreground service.
@@ -350,8 +353,8 @@
         if (app != null) {
             app.dumpDebug(proto, ServiceRecordProto.APP);
         }
-        if (isolatedProc != null) {
-            isolatedProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
+        if (isolationHostProc != null) {
+            isolationHostProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
         }
         proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager);
         proto.write(ServiceRecordProto.DELAYED, delayed);
@@ -453,8 +456,8 @@
             pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
         }
         pw.print(prefix); pw.print("app="); pw.println(app);
-        if (isolatedProc != null) {
-            pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
+        if (isolationHostProc != null) {
+            pw.print(prefix); pw.print("isolationHostProc="); pw.println(isolationHostProc);
         }
         if (allowlistManager) {
             pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager);
@@ -590,6 +593,10 @@
         userId = UserHandle.getUserId(appInfo.uid);
         createdFromFg = callerIsFg;
         updateKeepWarmLocked();
+        // initialize notification permission state; this'll be updated whenever there's an attempt
+        // to post or update a notification, but that doesn't cover the time before the first
+        // notification
+        updateFgsHasNotificationPermission();
     }
 
     public ServiceState getTracker() {
@@ -947,6 +954,25 @@
         return lastStartId;
     }
 
+    private void updateFgsHasNotificationPermission() {
+        // Do asynchronous communication with notification manager to avoid deadlocks.
+        final String localPackageName = packageName;
+        final int appUid = appInfo.uid;
+
+        ams.mHandler.post(new Runnable() {
+            public void run() {
+                NotificationManagerInternal nm = LocalServices.getService(
+                        NotificationManagerInternal.class);
+                if (nm == null) {
+                    return;
+                }
+                // Record whether the package has permission to notify the user
+                mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage(
+                        localPackageName, appUid);
+            }
+        });
+    }
+
     public void postNotification() {
         if (isForeground && foregroundNoti != null && app != null) {
             final int appUid = appInfo.uid;
@@ -968,6 +994,9 @@
                     if (nm == null) {
                         return;
                     }
+                    // Record whether the package has permission to notify the user
+                    mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage(
+                            localPackageName, appUid);
                     Notification localForegroundNoti = _foregroundNoti;
                     try {
                         if (localForegroundNoti.getSmallIcon() == null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a7864b9..252584c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -26,10 +26,10 @@
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
 import static android.os.PowerWhitelistManager.REASON_BOOT_COMPLETED;
 import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -2153,11 +2153,10 @@
                     callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
                 // If the caller does not have either permission, they are always doomed.
                 allow = false;
-            } else if (allowMode == ALLOW_NON_FULL) {
+            } else if (allowMode == ALLOW_NON_FULL || allowMode == ALLOW_PROFILES_OR_NON_FULL) {
                 // We are blanket allowing non-full access, you lucky caller!
                 allow = true;
-            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
-                        || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
                 // We may or may not allow this depending on whether the two users are
                 // in the same profile.
                 allow = isSameProfileGroup;
@@ -2184,12 +2183,13 @@
                     builder.append("; this requires ");
                     builder.append(INTERACT_ACROSS_USERS_FULL);
                     if (allowMode != ALLOW_FULL_ONLY) {
-                        if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) {
+                        if (allowMode == ALLOW_NON_FULL
+                                || allowMode == ALLOW_PROFILES_OR_NON_FULL
+                                || (allowMode == ALLOW_NON_FULL_IN_PROFILE && isSameProfileGroup)) {
                             builder.append(" or ");
                             builder.append(INTERACT_ACROSS_USERS);
                         }
-                        if (isSameProfileGroup
-                                && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+                        if (isSameProfileGroup && allowMode == ALLOW_PROFILES_OR_NON_FULL) {
                             builder.append(" or ");
                             builder.append(INTERACT_ACROSS_PROFILES);
                         }
@@ -2216,19 +2216,14 @@
     private boolean canInteractWithAcrossProfilesPermission(
             int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
             String callingPackage) {
-        if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+        if (allowMode != ALLOW_PROFILES_OR_NON_FULL) {
             return false;
         }
         if (!isSameProfileGroup) {
             return false;
         }
-        return  PermissionChecker.PERMISSION_GRANTED
-                == PermissionChecker.checkPermissionForPreflight(
-                        mInjector.getContext(),
-                        INTERACT_ACROSS_PROFILES,
-                        callingPid,
-                        callingUid,
-                        callingPackage);
+        return mInjector.checkPermissionForPreflight(INTERACT_ACROSS_PROFILES, callingPid,
+                callingUid, callingPackage);
     }
 
     int unsafeConvertIncomingUser(@UserIdInt int userId) {
@@ -2908,7 +2903,7 @@
              */
             mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG);
             mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG,
-                    target.id), USER_JOURNEY_TIMEOUT_MS);
+                    target.id, /* arg2= */ 0), USER_JOURNEY_TIMEOUT_MS);
         }
 
         FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId,
@@ -3157,6 +3152,12 @@
             return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
         }
 
+        boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
+            return  PermissionChecker.PERMISSION_GRANTED
+                    == PermissionChecker.checkPermissionForPreflight(
+                            getContext(), permission, pid, uid, pkg);
+        }
+
         protected void startHomeActivity(@UserIdInt int userId, String reason) {
             mService.mAtmInternal.startHomeActivity(userId, reason);
         }
@@ -3234,7 +3235,7 @@
             mService.mAtmInternal.clearLockedTasks(reason);
         }
 
-        protected boolean isCallerRecents(int callingUid) {
+        boolean isCallerRecents(int callingUid) {
             return mService.mAtmInternal.isCallerRecents(callingUid);
         }
 
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
new file mode 100644
index 0000000..6982513
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ambientcontext;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.app.BroadcastOptions;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.app.ambientcontext.AmbientContextManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.service.ambientcontext.AmbientContextDetectionService;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Per-user manager service for {@link AmbientContextEvent}s.
+ */
+final class AmbientContextManagerPerUserService extends
+        AbstractPerUserSystemService<AmbientContextManagerPerUserService,
+                AmbientContextManagerService> {
+    private static final String TAG = AmbientContextManagerPerUserService.class.getSimpleName();
+
+    @Nullable
+    @VisibleForTesting
+    RemoteAmbientContextDetectionService mRemoteService;
+
+    private ComponentName mComponentName;
+    private Context mContext;
+    private Set<PendingIntent> mExistingPendingIntents;
+
+    AmbientContextManagerPerUserService(
+            @NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) {
+        super(master, lock, userId);
+        mContext = master.getContext();
+        mExistingPendingIntents = new HashSet<>();
+    }
+
+    void destroyLocked() {
+        if (isVerbose()) {
+            Slog.v(TAG, "destroyLocked()");
+        }
+
+        Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
+        if (mRemoteService != null) {
+            synchronized (mLock) {
+                mRemoteService.unbind();
+                mRemoteService = null;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void ensureRemoteServiceInitiated() {
+        if (mRemoteService == null) {
+            mRemoteService = new RemoteAmbientContextDetectionService(
+                    getContext(), mComponentName, getUserId());
+        }
+    }
+
+    /**
+     * get the currently bound component name.
+     */
+    @VisibleForTesting
+    ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+
+    /**
+     * Resolves and sets up the service if it had not been done yet. Returns true if the service
+     * is available.
+     */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    boolean setUpServiceIfNeeded() {
+        if (mComponentName == null) {
+            mComponentName = updateServiceInfoLocked();
+        }
+        return mComponentName != null;
+    }
+
+    @Override
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        ServiceInfo serviceInfo;
+        try {
+            serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    0, mUserId);
+            if (serviceInfo != null) {
+                final String permission = serviceInfo.permission;
+                if (!Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE.equals(
+                        permission)) {
+                    throw new SecurityException(String.format(
+                            "Service %s requires %s permission. Found %s permission",
+                            serviceInfo.getComponentName(),
+                            Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE,
+                            serviceInfo.permission));
+                }
+            }
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+        return serviceInfo;
+    }
+
+    @Override
+    protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        synchronized (super.mLock) {
+            super.dumpLocked(prefix, pw);
+        }
+        if (mRemoteService != null) {
+            mRemoteService.dump("", new IndentingPrintWriter(pw, "  "));
+        }
+    }
+
+    /**
+     * Handles client registering as an observer. Only one registration is supported per app
+     * package. A new registration from the same package will overwrite the previous registration.
+     */
+    public void onRegisterObserver(AmbientContextEventRequest request,
+            PendingIntent pendingIntent) {
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Service is not available at this moment.");
+                sendStatusUpdateIntent(
+                        pendingIntent, AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+
+            // Remove any existing intent and unregister for this package before adding a new one.
+            String callingPackage = pendingIntent.getCreatorPackage();
+            PendingIntent duplicatePendingIntent = findExistingRequestByPackage(callingPackage);
+            if (duplicatePendingIntent != null) {
+                Slog.d(TAG, "Unregister duplicate request from " + callingPackage);
+                onUnregisterObserver(callingPackage);
+                mExistingPendingIntents.remove(duplicatePendingIntent);
+            }
+
+            // Register new package and add request to mExistingRequests
+            startDetection(request, callingPackage, createRemoteCallback());
+            mExistingPendingIntents.add(pendingIntent);
+        }
+    }
+
+    @VisibleForTesting
+    void startDetection(AmbientContextEventRequest request, String callingPackage,
+            RemoteCallback callback) {
+        Slog.d(TAG, "Requested detection of " + request.getEventTypes());
+        synchronized (mLock) {
+            ensureRemoteServiceInitiated();
+            mRemoteService.startDetection(request, callingPackage, callback);
+        }
+    }
+
+    /**
+     * Sends an intent with a status code and empty events.
+     */
+    void sendStatusUpdateIntent(PendingIntent pendingIntent, int statusCode) {
+        AmbientContextEventResponse response = new AmbientContextEventResponse.Builder()
+                .setStatusCode(statusCode)
+                .build();
+        sendResponseIntent(pendingIntent, response);
+    }
+
+    /**
+     * Unregisters the client from all previously registered events by removing from the
+     * mExistingRequests map, and unregister events from the service if those events are not
+     * requested by other apps.
+     */
+    public void onUnregisterObserver(String callingPackage) {
+        synchronized (mLock) {
+            PendingIntent pendingIntent = findExistingRequestByPackage(callingPackage);
+            if (pendingIntent == null) {
+                Slog.d(TAG, "No registration found for " + callingPackage);
+                return;
+            }
+
+            // Remove from existing requests
+            mExistingPendingIntents.remove(pendingIntent);
+            stopDetection(pendingIntent.getCreatorPackage());
+        }
+    }
+
+    @VisibleForTesting
+    void stopDetection(String packageName) {
+        Slog.d(TAG, "Stop detection for " + packageName);
+        synchronized (mLock) {
+            ensureRemoteServiceInitiated();
+            mRemoteService.stopDetection(packageName);
+        }
+    }
+
+    @Nullable
+    private PendingIntent findExistingRequestByPackage(String callingPackage) {
+        for (PendingIntent pendingIntent : mExistingPendingIntents) {
+            if (pendingIntent.getCreatorPackage().equals(callingPackage)) {
+                return pendingIntent;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sends out the Intent to the client after the event is detected.
+     *
+     * @param pendingIntent Client's PendingIntent for callback
+     * @param response Response with status code and detection result
+     */
+    private void sendResponseIntent(PendingIntent pendingIntent,
+            AmbientContextEventResponse response) {
+        Intent intent = new Intent();
+        intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE, response);
+        // Explicitly disallow the receiver from starting activities, to prevent apps from utilizing
+        // the PendingIntent as a backdoor to do this.
+        BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+        try {
+            pendingIntent.send(getContext(), 0, intent, null, null, null,
+                    options.toBundle());
+            Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
+                    + response);
+        } catch (PendingIntent.CanceledException e) {
+            Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent);
+        }
+    }
+
+    @NonNull
+    private RemoteCallback createRemoteCallback() {
+        return new RemoteCallback(result -> {
+            AmbientContextEventResponse response = (AmbientContextEventResponse) result.get(
+                            AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Set<PendingIntent> pendingIntentForFailedRequests = new HashSet<>();
+                for (PendingIntent pendingIntent : mExistingPendingIntents) {
+                    // Send PendingIntent if a requesting package matches the response packages.
+                    if (response.getPackageName().equals(pendingIntent.getCreatorPackage())) {
+                        sendResponseIntent(pendingIntent, response);
+
+                        int statusCode = response.getStatusCode();
+                        if (statusCode != AmbientContextEventResponse.STATUS_SUCCESS) {
+                            pendingIntentForFailedRequests.add(pendingIntent);
+                        }
+                        Slog.i(TAG, "Got response of " + response.getEvents() + " for "
+                                + pendingIntent.getCreatorPackage() + ". Status: " + statusCode);
+                    }
+                }
+
+                // Removes the failed requests from the existing requests.
+                for (PendingIntent pendingIntent : pendingIntentForFailedRequests) {
+                    mExistingPendingIntents.remove(pendingIntent);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
new file mode 100644
index 0000000..33905f2
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ambientcontext;
+
+import static android.provider.DeviceConfig.NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.app.ambientcontext.IAmbientContextEventObserver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.RemoteCallback;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * System service for managing {@link AmbientContextEvent}s.
+ */
+public class AmbientContextManagerService extends
+        AbstractMasterSystemService<AmbientContextManagerService,
+                AmbientContextManagerPerUserService> {
+    private static final String TAG = AmbientContextManagerService.class.getSimpleName();
+    private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    private static final boolean DEFAULT_SERVICE_ENABLED = true;
+
+    private final Context mContext;
+    boolean mIsServiceEnabled;
+
+    public AmbientContextManagerService(Context context) {
+        super(context,
+                new FrameworkResourcesServiceNameResolver(
+                        context,
+                        R.string.config_defaultAmbientContextDetectionService),
+                        /*disallowProperty=*/null,
+                PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+                        | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.AMBIENT_CONTEXT_SERVICE, new AmbientContextEventObserver());
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            DeviceConfig.addOnPropertiesChangedListener(
+                    NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+                    getContext().getMainExecutor(),
+                    (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+            mIsServiceEnabled = DeviceConfig.getBoolean(
+                    NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+                    KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+        }
+    }
+
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        if (keys.contains(KEY_SERVICE_ENABLED)) {
+            mIsServiceEnabled = DeviceConfig.getBoolean(
+                    NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+                    KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+        }
+    }
+
+    @Override
+    protected AmbientContextManagerPerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new AmbientContextManagerPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    protected void onServiceRemoved(
+            AmbientContextManagerPerUserService service, @UserIdInt int userId) {
+        service.destroyLocked();
+    }
+
+    /** Returns {@code true} if the detection service is configured on this device. */
+    public static boolean isDetectionServiceConfigured() {
+        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+        final String[] packageNames = pmi.getKnownPackageNames(
+                PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION, UserHandle.USER_SYSTEM);
+        boolean isServiceConfigured = (packageNames.length != 0);
+        Slog.i(TAG, "Detection service configured: " + isServiceConfigured);
+        return isServiceConfigured;
+    }
+
+    /**
+     * Send request to the remote AmbientContextDetectionService impl to start detecting the
+     * specified events. Intended for use by shell command for testing.
+     * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+     */
+    void startAmbientContextEvent(@UserIdInt int userId, AmbientContextEventRequest request,
+            String packageName, RemoteCallback callback) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+        synchronized (mLock) {
+            final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+            if (service != null) {
+                service.startDetection(request, packageName, callback);
+            } else {
+                Slog.i(TAG, "service not available for user_id: " + userId);
+            }
+        }
+    }
+
+    /**
+     * Send request to the remote AmbientContextDetectionService impl to stop detecting the
+     * specified events. Intended for use by shell command for testing.
+     * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+     */
+    void stopAmbientContextEvent(@UserIdInt int userId, String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+        synchronized (mLock) {
+            final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+            if (service != null) {
+                service.stopDetection(packageName);
+            } else {
+                Slog.i(TAG, "service not available for user_id: " + userId);
+            }
+        }
+    }
+
+    /**
+     * Returns the AmbientContextManagerPerUserService component for this user.
+     */
+    public ComponentName getComponentName(@UserIdInt int userId) {
+        synchronized (mLock) {
+            final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+            if (service != null) {
+                return service.getComponentName();
+            }
+        }
+        return null;
+    }
+
+    private final class AmbientContextEventObserver extends IAmbientContextEventObserver.Stub {
+        final AmbientContextManagerPerUserService mService = getServiceForUserLocked(
+                UserHandle.getCallingUserId());
+
+        @Override
+        public void registerObserver(
+                AmbientContextEventRequest request, PendingIntent pendingIntent) {
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(pendingIntent);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available.");
+                mService.sendStatusUpdateIntent(pendingIntent,
+                        AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            mService.onRegisterObserver(request, pendingIntent);
+        }
+
+        @Override
+        public void unregisterObserver(String callingPackage) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+            mService.onUnregisterObserver(callingPackage);
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+                return;
+            }
+            synchronized (mLock) {
+                dumpLocked("", pw);
+            }
+        }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+            new AmbientContextShellCommand(AmbientContextManagerService.this).exec(
+                    this, in, out, err, args, callback, resultReceiver);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
new file mode 100644
index 0000000..b5cd985
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ambientcontext;
+
+import static java.lang.System.out;
+
+import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.content.ComponentName;
+import android.os.Binder;
+import android.os.RemoteCallback;
+import android.os.ShellCommand;
+import android.service.ambientcontext.AmbientContextDetectionService;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command for {@link AmbientContextManagerService}.
+ */
+final class AmbientContextShellCommand extends ShellCommand {
+
+    @NonNull
+    private final AmbientContextManagerService mService;
+
+    AmbientContextShellCommand(@NonNull AmbientContextManagerService service) {
+        mService = service;
+    }
+
+    /** Callbacks for AmbientContextEventService results used internally for testing. */
+    static class TestableCallbackInternal {
+        private AmbientContextEventResponse mLastResponse;
+
+        public AmbientContextEventResponse getLastResponse() {
+            return mLastResponse;
+        }
+
+        @NonNull
+        private RemoteCallback createRemoteCallback() {
+            return new RemoteCallback(result -> {
+                AmbientContextEventResponse response =
+                        (AmbientContextEventResponse) result.get(
+                                AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mLastResponse = response;
+                    out.println("Response available: " + response);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            });
+        }
+    }
+
+    static final TestableCallbackInternal sTestableCallbackInternal =
+            new TestableCallbackInternal();
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        switch (cmd) {
+            case "start-detection":
+                return runStartDetection();
+            case "stop-detection":
+                return runStopDetection();
+            case "get-last-status-code":
+                return getLastStatusCode();
+            case "get-bound-package":
+                return getBoundPackageName();
+            case "set-temporary-service":
+                return setTemporaryService();
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    private int runStartDetection() {
+        final int userId = Integer.parseInt(getNextArgRequired());
+        final String packageName = getNextArgRequired();
+        AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+                .addEventType(AmbientContextEvent.EVENT_COUGH)
+                .addEventType(AmbientContextEvent.EVENT_SNORE)
+                .build();
+
+        mService.startAmbientContextEvent(userId, request, packageName,
+                sTestableCallbackInternal.createRemoteCallback());
+        return 0;
+    }
+
+    private int runStopDetection() {
+        final int userId = Integer.parseInt(getNextArgRequired());
+        final String packageName = getNextArgRequired();
+        mService.stopAmbientContextEvent(userId, packageName);
+        return 0;
+    }
+
+    private int getLastStatusCode() {
+        AmbientContextEventResponse lastResponse = sTestableCallbackInternal.getLastResponse();
+        if (lastResponse == null) {
+            return -1;
+        }
+        return lastResponse.getStatusCode();
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("AmbientContextEvent commands: ");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println("  start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
+        pw.println("  stop-detection USER_ID: Stops AmbientContextEvent detection.");
+        pw.println("  get-last-status-code: Prints the latest request status code.");
+        pw.println("  get-bound-package USER_ID:"
+                + "     Print the bound package that implements the service.");
+        pw.println("  set-temporary-service USER_ID [COMPONENT_NAME DURATION]");
+        pw.println("    Temporarily (for DURATION ms) changes the service implementation.");
+        pw.println("    To reset, call with just the USER_ID argument.");
+    }
+
+    private int getBoundPackageName() {
+        final PrintWriter out = getOutPrintWriter();
+        final int userId = Integer.parseInt(getNextArgRequired());
+        final ComponentName componentName = mService.getComponentName(userId);
+        out.println(componentName == null ? "" : componentName.getPackageName());
+        return 0;
+    }
+
+    private int setTemporaryService() {
+        final PrintWriter out = getOutPrintWriter();
+        final int userId = Integer.parseInt(getNextArgRequired());
+        final String serviceName = getNextArg();
+        if (serviceName == null) {
+            mService.resetTemporaryService(userId);
+            out.println("AmbientContextDetectionService temporary reset. ");
+            return 0;
+        }
+
+        final int duration = Integer.parseInt(getNextArgRequired());
+        mService.setTemporaryService(userId, serviceName, duration);
+        out.println("AmbientContextDetectionService temporarily set to " + serviceName
+                + " for " + duration + "ms");
+        return 0;
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/OWNERS b/services/core/java/com/android/server/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
diff --git a/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
new file mode 100644
index 0000000..5cc29b3
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ambientcontext;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteCallback;
+import android.service.ambientcontext.AmbientContextDetectionService;
+import android.service.ambientcontext.IAmbientContextDetectionService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+/** Manages the connection to the remote service. */
+final class RemoteAmbientContextDetectionService
+        extends ServiceConnector.Impl<IAmbientContextDetectionService> {
+    private static final String TAG =
+            RemoteAmbientContextDetectionService.class.getSimpleName();
+
+    RemoteAmbientContextDetectionService(Context context, ComponentName serviceName,
+            int userId) {
+        super(context, new Intent(
+                AmbientContextDetectionService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+                IAmbientContextDetectionService.Stub::asInterface);
+
+        // Bind right away
+        connect();
+    }
+
+    /**
+     * Asks the implementation to start detection.
+     *
+     * @param request The request with events to detect, and optional detection options.
+     * @param packageName The app package that requested the detection
+     * @param callback callback for detection results
+     */
+    public void startDetection(
+            @NonNull AmbientContextEventRequest request, String packageName,
+            RemoteCallback callback) {
+        Slog.i(TAG, "Start detection for " + request.getEventTypes());
+        post(service -> service.startDetection(request, packageName, callback));
+    }
+
+    /**
+     * Asks the implementation to stop detection.
+     *
+     * @param packageName stop detection for the given package
+     */
+    public void stopDetection(String packageName) {
+        Slog.i(TAG, "Stop detection for " + packageName);
+        post(service -> service.stopDetection(packageName));
+    }
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 0980f40..f5f7bb3 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -39,9 +39,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.GameManager.GameMode;
+import android.app.GameModeInfo;
 import android.app.GameState;
 import android.app.IGameManagerService;
 import android.app.compat.PackageOverride;
@@ -81,6 +83,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.SystemService.TargetUser;
+import com.android.server.app.GameManagerService.GamePackageConfiguration.GameModeConfiguration;
 
 import java.io.FileDescriptor;
 import java.util.List;
@@ -113,6 +116,7 @@
     private final Context mContext;
     private final Object mLock = new Object();
     private final Object mDeviceConfigLock = new Object();
+    private final Object mOverrideConfigLock = new Object();
     private final Handler mHandler;
     private final PackageManager mPackageManager;
     private final IPlatformCompat mPlatformCompat;
@@ -122,6 +126,8 @@
     private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
     @GuardedBy("mDeviceConfigLock")
     private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
+    @GuardedBy("mOverrideConfigLock")
+    private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = new ArrayMap<>();
 
     public GameManagerService(Context context) {
         this(context, createServiceThread().getLooper());
@@ -281,13 +287,51 @@
         return 0;
     }
 
+    public enum FrameRate {
+        FPS_DEFAULT(0),
+        FPS_30(30),
+        FPS_45(45),
+        FPS_60(60),
+        FPS_90(90),
+        FPS_120(120),
+        FPS_INVALID(-1);
+
+        public final int fps;
+
+        FrameRate(int fps) {
+            this.fps = fps;
+        }
+    }
+
+    // Turn the raw string to the corresponding fps int.
+    // Return 0 when disabling, -1 for invalid fps.
+    static int getFpsInt(String raw) {
+        switch (raw) {
+            case "30":
+                return FrameRate.FPS_30.fps;
+            case "45":
+                return FrameRate.FPS_45.fps;
+            case "60":
+                return FrameRate.FPS_60.fps;
+            case "90":
+                return FrameRate.FPS_90.fps;
+            case "120":
+                return FrameRate.FPS_120.fps;
+            case "disable":
+            case "":
+                return FrameRate.FPS_DEFAULT.fps;
+        }
+        return FrameRate.FPS_INVALID.fps;
+    }
+
     /**
      * Called by games to communicate the current state to the platform.
      * @param packageName The client package name.
      * @param gameState An object set to the current state.
      * @param userId The user associated with this state.
      */
-    public void setGameState(String packageName, @NonNull GameState gameState, int userId) {
+    public void setGameState(String packageName, @NonNull GameState gameState,
+            @UserIdInt int userId) {
         if (!isPackageGame(packageName, userId)) {
             // Restrict to games only.
             return;
@@ -399,11 +443,14 @@
             public static final String TAG = "GameManagerService_GameModeConfiguration";
             public static final String MODE_KEY = "mode";
             public static final String SCALING_KEY = "downscaleFactor";
+            public static final String FPS_KEY = "fps";
             public static final String DEFAULT_SCALING = "1.0";
+            public static final String DEFAULT_FPS = "";
             public static final String ANGLE_KEY = "useAngle";
 
             private final @GameMode int mGameMode;
-            private final String mScaling;
+            private String mScaling;
+            private String mFps;
             private final boolean mUseAngle;
 
             GameModeConfiguration(KeyValueListParser parser) {
@@ -414,6 +461,8 @@
                 // using ANGLE).
                 mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
                         ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+
+                mFps = parser.getString(FPS_KEY, DEFAULT_FPS);
                 // We only want to use ANGLE if:
                 // - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
                 // - The app has not opted in to performing the work itself AND
@@ -430,14 +479,27 @@
                 return mScaling;
             }
 
+            public int getFps() {
+                return GameManagerService.getFpsInt(mFps);
+            }
+
             public boolean getUseAngle() {
                 return mUseAngle;
             }
 
+            public void setScaling(String scaling) {
+                mScaling = scaling;
+            }
+
+            public void setFpsStr(String fpsStr) {
+                mFps = fpsStr;
+            }
+
             public boolean isValid() {
-                return mGameMode == GameManager.GAME_MODE_STANDARD
+                return (mGameMode == GameManager.GAME_MODE_STANDARD
                         || mGameMode == GameManager.GAME_MODE_PERFORMANCE
-                        || mGameMode == GameManager.GAME_MODE_BATTERY;
+                        || mGameMode == GameManager.GAME_MODE_BATTERY)
+                        && !willGamePerformOptimizations(mGameMode);
             }
 
             /**
@@ -445,7 +507,7 @@
              */
             public String toString() {
                 return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:"
-                        + mUseAngle + "]";
+                        + mUseAngle + ",Fps:" + mFps + "]";
             }
 
             /**
@@ -633,6 +695,32 @@
         }
     }
 
+    private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
+        GamePackageConfiguration config = null;
+        synchronized (mOverrideConfigLock) {
+            config = mOverrideConfigs.get(packageName);
+        }
+        if (config == null) {
+            synchronized (mDeviceConfigLock) {
+                config = mConfigs.get(packageName);
+            }
+        }
+        if (config == null) {
+            return new int[]{};
+        }
+        return config.getAvailableGameModes();
+    }
+
+    private boolean isPackageGame(String packageName, @UserIdInt int userId) {
+        try {
+            final ApplicationInfo applicationInfo = mPackageManager
+                    .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
+            return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /**
      * Get an array of game modes available for a given package.
      * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
@@ -641,16 +729,10 @@
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public @GameMode int[] getAvailableGameModes(String packageName) throws SecurityException {
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
-        synchronized (mDeviceConfigLock) {
-            final GamePackageConfiguration config = mConfigs.get(packageName);
-            if (config == null) {
-                return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
-            }
-            return config.getAvailableGameModes();
-        }
+        return getAvailableGameModesUnchecked(packageName);
     }
 
-    private @GameMode int getGameModeFromSettings(String packageName, int userId) {
+    private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
                 Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
@@ -668,28 +750,22 @@
      * {@link android.Manifest.permission#MANAGE_GAME_MODE}.
      */
     @Override
-    public @GameMode int getGameMode(String packageName, int userId)
+    public @GameMode int getGameMode(@NonNull String packageName, @UserIdInt int userId)
             throws SecurityException {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), userId, false, true, "getGameMode",
                 "com.android.server.app.GameManagerService");
 
         // Restrict to games only.
-        try {
-            final ApplicationInfo applicationInfo = mPackageManager
-                    .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
-            if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
-                // The game mode for applications that are not identified as game is always
-                // UNSUPPORTED. See {@link PackageManager#setApplicationCategoryHint(String, int)}
-                return GameManager.GAME_MODE_UNSUPPORTED;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
+        if (!isPackageGame(packageName, userId)) {
+            // The game mode for applications that are not identified as game is always
+            // UNSUPPORTED. See {@link PackageManager#setApplicationCategoryHint(String, int)}
             return GameManager.GAME_MODE_UNSUPPORTED;
         }
 
         // This function handles two types of queries:
-        // 1.) A normal, non-privileged app querying its own Game Mode.
-        // 2.) A privileged system service querying the Game Mode of another package.
+        // 1) A normal, non-privileged app querying its own Game Mode.
+        // 2) A privileged system service querying the Game Mode of another package.
         // The least privileged case is a normal app performing a query, so check that first and
         // return a value if the package name is valid. Next, check if the caller has the necessary
         // permission and return a value. Do this check last, since it can throw an exception.
@@ -702,14 +778,32 @@
         return getGameModeFromSettings(packageName, userId);
     }
 
-    private boolean isPackageGame(String packageName, int userId) {
-        try {
-            final ApplicationInfo applicationInfo = mPackageManager
-                    .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
-            return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
+    /**
+     * Get the GameModeInfo for the package name.
+     * Verifies that the calling process is for the matching package UID or has
+     * {@link android.Manifest.permission#MANAGE_GAME_MODE}. If the package is not a game,
+     * null is always returned.
+     */
+    @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    @Nullable
+    public GameModeInfo getGameModeInfo(@NonNull String packageName, @UserIdInt int userId) {
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "getGameModeInfo",
+                "com.android.server.app.GameManagerService");
+
+        // Check the caller has the necessary permission.
+        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+
+        // Restrict to games only.
+        if (!isPackageGame(packageName, userId)) {
+            return null;
         }
+
+        final @GameMode int activeGameMode = getGameModeFromSettings(packageName, userId);
+        final @GameMode int[] availableGameModes = getAvailableGameModesUnchecked(packageName);
+
+        return new GameModeInfo(activeGameMode, availableGameModes);
     }
 
     /**
@@ -743,7 +837,7 @@
                 mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
             }
         }
-        updateInterventions(packageName, gameMode);
+        updateInterventions(packageName, gameMode, userId);
     }
 
     /**
@@ -876,41 +970,26 @@
         }
     }
 
-    private void updateCompatModeDownscale(String packageName, @GameMode int gameMode) {
-        synchronized (mDeviceConfigLock) {
-            if (gameMode == GameManager.GAME_MODE_STANDARD
-                    || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
-                disableCompatScale(packageName);
-                return;
-            }
-            final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
-            if (packageConfig == null) {
-                disableCompatScale(packageName);
-                Slog.v(TAG, "Package configuration not found for " + packageName);
-                return;
-            }
-            if (DEBUG) {
-                Slog.v(TAG, dumpDeviceConfigs());
-            }
-            if (packageConfig.willGamePerformOptimizations(gameMode)) {
-                disableCompatScale(packageName);
-                return;
-            }
-            final GamePackageConfiguration.GameModeConfiguration modeConfig =
-                    packageConfig.getGameModeConfiguration(gameMode);
-            if (modeConfig == null) {
-                Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
-                return;
-            }
-            long scaleId = modeConfig.getCompatChangeId();
-            if (scaleId == 0) {
-                Slog.i(TAG, "Invalid downscaling change id " + scaleId + " for "
-                        + packageName);
-                return;
-            }
+    private void updateCompatModeDownscale(GamePackageConfiguration packageConfig,
+            String packageName, @GameMode int gameMode) {
 
-            enableCompatScale(packageName, scaleId);
+        if (DEBUG) {
+            Slog.v(TAG, dumpDeviceConfigs());
         }
+        final GamePackageConfiguration.GameModeConfiguration modeConfig =
+                packageConfig.getGameModeConfiguration(gameMode);
+        if (modeConfig == null) {
+            Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
+            return;
+        }
+        long scaleId = modeConfig.getCompatChangeId();
+        if (scaleId == 0) {
+            Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+                    + packageName);
+            return;
+        }
+
+        enableCompatScale(packageName, scaleId);
     }
 
     private int modeToBitmask(@GameMode int gameMode) {
@@ -927,16 +1006,233 @@
         // ship.
     }
 
-    private void updateInterventions(String packageName, @GameMode int gameMode) {
-        updateCompatModeDownscale(packageName, gameMode);
+
+    private void updateFps(GamePackageConfiguration packageConfig, String packageName,
+            @GameMode int gameMode, @UserIdInt int userId) {
+        final GamePackageConfiguration.GameModeConfiguration modeConfig =
+                packageConfig.getGameModeConfiguration(gameMode);
+        if (modeConfig == null) {
+            Slog.d(TAG, "Game mode " + gameMode + " not found for " + packageName);
+            return;
+        }
+        try {
+            final float fps = modeConfig.getFps();
+            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+            nativeSetOverrideFrameRate(uid, fps);
+        } catch (PackageManager.NameNotFoundException e) {
+            return;
+        }
+    }
+
+
+    private void updateInterventions(String packageName,
+            @GameMode int gameMode, @UserIdInt int userId) {
+        if (gameMode == GameManager.GAME_MODE_STANDARD
+                || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+            disableCompatScale(packageName);
+            return;
+        }
+        GamePackageConfiguration packageConfig = null;
+
+        synchronized (mOverrideConfigLock) {
+            packageConfig = mOverrideConfigs.get(packageName);
+        }
+
+        if (packageConfig == null) {
+            synchronized (mDeviceConfigLock) {
+                packageConfig = mConfigs.get(packageName);
+            }
+        }
+
+        if (packageConfig == null) {
+            disableCompatScale(packageName);
+            Slog.v(TAG, "Package configuration not found for " + packageName);
+            return;
+        }
+        if (packageConfig.willGamePerformOptimizations(gameMode)) {
+            return;
+        }
+        updateCompatModeDownscale(packageConfig, packageName, gameMode);
+        updateFps(packageConfig, packageName, gameMode, userId);
         updateUseAngle(packageName, gameMode);
     }
 
     /**
+     * Set the override Game Mode Configuration.
+     * Update the config if exists, create one if not.
+     */
+    @VisibleForTesting
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public void setGameModeConfigOverride(String packageName, @UserIdInt int userId,
+            @GameMode int gameMode, String fpsStr, String scaling) throws SecurityException {
+        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        synchronized (mLock) {
+            if (!mSettings.containsKey(userId)) {
+                return;
+            }
+        }
+        // Adding override game mode configuration of the given package name
+        synchronized (mOverrideConfigLock) {
+            // look for the existing override GamePackageConfiguration
+            GamePackageConfiguration overrideConfig = mOverrideConfigs.get(packageName);
+            if (overrideConfig == null) {
+                overrideConfig = new GamePackageConfiguration(packageName, userId);
+                mOverrideConfigs.put(packageName, overrideConfig);
+            }
+
+            // modify GameModeConfiguration intervention settings
+            GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
+                    overrideConfig.getGameModeConfiguration(gameMode);
+
+            if (fpsStr != null) {
+                overrideModeConfig.setFpsStr(fpsStr);
+            } else {
+                overrideModeConfig.setFpsStr(
+                        GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
+            }
+            if (scaling != null) {
+                overrideModeConfig.setScaling(scaling);
+            } else {
+                overrideModeConfig.setScaling(
+                        GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING);
+            }
+            Slog.i(TAG, "Package Name: " + packageName
+                    + " FPS: " + String.valueOf(overrideModeConfig.getFps())
+                    + " Scaling: " + overrideModeConfig.getScaling());
+        }
+        setGameMode(packageName, gameMode, userId);
+    }
+
+    /**
+     * Reset the overridden gameModeConfiguration of the given mode.
+     * Remove the override config if game mode is not specified.
+     */
+    @VisibleForTesting
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public void resetGameModeConfigOverride(String packageName, @UserIdInt int userId,
+            @GameMode int gameModeToReset) throws SecurityException {
+        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        synchronized (mLock) {
+            if (!mSettings.containsKey(userId)) {
+                return;
+            }
+        }
+
+        // resets GamePackageConfiguration of a given packageName.
+        // If a gameMode is specified, only reset the GameModeConfiguration of the gameMode.
+        if (gameModeToReset != -1) {
+            GamePackageConfiguration overrideConfig = null;
+            synchronized (mOverrideConfigLock) {
+                overrideConfig = mOverrideConfigs.get(packageName);
+            }
+
+            GamePackageConfiguration config = null;
+            synchronized (mDeviceConfigLock) {
+                config = mConfigs.get(packageName);
+            }
+
+            int[] modes = overrideConfig.getAvailableGameModes();
+
+            // First check if the mode to reset exists
+            boolean isGameModeExist = false;
+            for (int mode : modes) {
+                if (gameModeToReset == mode) {
+                    isGameModeExist = true;
+                }
+            }
+            if (!isGameModeExist) {
+                return;
+            }
+
+            // If the game mode to reset is the only mode other than standard mode,
+            // The override config is removed.
+            if (modes.length <= 2) {
+                synchronized (mOverrideConfigLock) {
+                    mOverrideConfigs.remove(packageName);
+                }
+            } else {
+                // otherwise we reset the mode by copying the original config.
+                overrideConfig.addModeConfig(config.getGameModeConfiguration(gameModeToReset));
+            }
+        } else {
+            synchronized (mOverrideConfigLock) {
+                // remove override config if there is one
+                mOverrideConfigs.remove(packageName);
+            }
+        }
+
+        // Make sure after resetting the game mode is still supported.
+        // If not, set the game mode to standard
+        int gameMode = getGameMode(packageName, userId);
+        int newGameMode = gameMode;
+
+        GamePackageConfiguration config = null;
+        synchronized (mOverrideConfigLock) {
+            config = mOverrideConfigs.get(packageName);
+        }
+        synchronized (mDeviceConfigLock) {
+            config = mConfigs.get(packageName);
+        }
+        if (config != null) {
+            int modesBitfield = config.getAvailableGameModesBitfield();
+            // Remove UNSUPPORTED to simplify the logic here, since we really just
+            // want to check if we support selectable game modes
+            modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
+            if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
+                if (bitFieldContainsModeBitmask(modesBitfield,
+                        GameManager.GAME_MODE_STANDARD)) {
+                    // If the current set mode isn't supported,
+                    // but we support STANDARD, then set the mode to STANDARD.
+                    newGameMode = GameManager.GAME_MODE_STANDARD;
+                } else {
+                    // If we don't support any game modes, then set to UNSUPPORTED
+                    newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+                }
+            }
+        } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
+            // If we have no config for the package, but the configured mode is not
+            // UNSUPPORTED, then set to UNSUPPORTED
+            newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+        }
+        if (gameMode != newGameMode) {
+            setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
+            return;
+        }
+        setGameMode(packageName, gameMode, userId);
+    }
+
+    /**
+     * Returns the string listing all the interventions currently set to a game.
+     */
+    public String getInterventionList(String packageName) {
+        GamePackageConfiguration packageConfig = null;
+        synchronized (mOverrideConfigLock) {
+            packageConfig = mOverrideConfigs.get(packageName);
+        }
+
+        if (packageConfig == null) {
+            synchronized (mDeviceConfigLock) {
+                packageConfig = mConfigs.get(packageName);
+            }
+        }
+
+        StringBuilder listStrSb = new StringBuilder();
+        if (packageConfig == null) {
+            listStrSb.append("\n No intervention found for package ")
+                    .append(packageName);
+            return listStrSb.toString();
+        }
+        listStrSb.append("\nPackage name: ")
+                .append(packageName)
+                .append(packageConfig.toString());
+        return listStrSb.toString();
+    }
+
+    /**
      * @hide
      */
     @VisibleForTesting
-    void updateConfigsForUser(int userId, String ...packageNames) {
+    void updateConfigsForUser(@UserIdInt int userId, String ...packageNames) {
         try {
             synchronized (mDeviceConfigLock) {
                 for (final String packageName : packageNames) {
@@ -954,44 +1250,48 @@
                     }
                 }
             }
-            for (final String packageName : packageNames) {
-                if (mSettings.containsKey(userId)) {
-                    int gameMode = getGameMode(packageName, userId);
-                    int newGameMode = gameMode;
-                    // Make sure the user settings and package configs don't conflict. I.e. the
-                    // user setting is set to a mode that no longer available due to config/manifest
-                    // changes. Most of the time we won't have to change anything.
-                    GamePackageConfiguration config;
-                    synchronized (mDeviceConfigLock) {
-                        config = mConfigs.get(packageName);
-                    }
-                    if (config != null) {
-                        int modesBitfield = config.getAvailableGameModesBitfield();
-                        // Remove UNSUPPORTED to simplify the logic here, since we really just
-                        // want to check if we support selectable game modes
-                        modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
-                        if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
-                            if (bitFieldContainsModeBitmask(modesBitfield,
-                                    GameManager.GAME_MODE_STANDARD)) {
-                                // If the current set mode isn't supported, but we support STANDARD,
-                                // then set the mode to STANDARD.
-                                newGameMode = GameManager.GAME_MODE_STANDARD;
-                            } else {
-                                // If we don't support any game modes, then set to UNSUPPORTED
-                                newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
-                            }
-                        }
-                    } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
-                        // If we have no config for the package, but the configured mode is not
-                        // UNSUPPORTED, then set to UNSUPPORTED
-                        newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
-                    }
-                    if (newGameMode != gameMode) {
-                        setGameMode(packageName, newGameMode, userId);
-                    }
-                    updateInterventions(packageName, gameMode);
+            synchronized (mLock) {
+                if (!mSettings.containsKey(userId)) {
+                    return;
                 }
             }
+            for (final String packageName : packageNames) {
+                int gameMode = getGameMode(packageName, userId);
+                int newGameMode = gameMode;
+                // Make sure the user settings and package configs don't conflict.
+                // I.e. the user setting is set to a mode that no longer available due to
+                // config/manifest changes.
+                // Most of the time we won't have to change anything.
+                GamePackageConfiguration config = null;
+                synchronized (mDeviceConfigLock) {
+                    config = mConfigs.get(packageName);
+                }
+                if (config != null) {
+                    int modesBitfield = config.getAvailableGameModesBitfield();
+                    // Remove UNSUPPORTED to simplify the logic here, since we really just
+                    // want to check if we support selectable game modes
+                    modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
+                    if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
+                        if (bitFieldContainsModeBitmask(modesBitfield,
+                                GameManager.GAME_MODE_STANDARD)) {
+                            // If the current set mode isn't supported,
+                            // but we support STANDARD, then set the mode to STANDARD.
+                            newGameMode = GameManager.GAME_MODE_STANDARD;
+                        } else {
+                            // If we don't support any game modes, then set to UNSUPPORTED
+                            newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+                        }
+                    }
+                } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
+                    // If we have no config for the package, but the configured mode is not
+                    // UNSUPPORTED, then set to UNSUPPORTED
+                    newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+                }
+                if (newGameMode != gameMode) {
+                    setGameMode(packageName, newGameMode, userId);
+                }
+                updateInterventions(packageName, gameMode, userId);
+            }
         } catch (Exception e) {
             Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
         }
@@ -1011,7 +1311,16 @@
      */
     @VisibleForTesting
     public GamePackageConfiguration getConfig(String packageName) {
-        return mConfigs.get(packageName);
+        GamePackageConfiguration packageConfig = null;
+        synchronized (mOverrideConfigLock) {
+            packageConfig = mOverrideConfigs.get(packageName);
+        }
+        if (packageConfig == null) {
+            synchronized (mDeviceConfigLock) {
+                packageConfig = mConfigs.get(packageName);
+            }
+        }
+        return packageConfig;
     }
 
     private void registerPackageReceiver() {
@@ -1047,6 +1356,9 @@
                             break;
                         case ACTION_PACKAGE_REMOVED:
                             disableCompatScale(packageName);
+                            synchronized (mOverrideConfigLock) {
+                                mOverrideConfigs.remove(packageName);
+                            }
                             synchronized (mDeviceConfigLock) {
                                 mConfigs.remove(packageName);
                             }
@@ -1083,4 +1395,9 @@
         handlerThread.start();
         return handlerThread;
     }
+
+    /**
+     * load dynamic library for frame rate overriding JNI calls
+     */
+    private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
 }
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index f07d207..470c320 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -34,7 +34,6 @@
 import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.IGameManagerService;
-import android.compat.Compatibility;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -42,12 +41,8 @@
 import android.os.ShellCommand;
 import android.util.ArraySet;
 
-import com.android.internal.compat.CompatibilityChangeConfig;
-import com.android.server.compat.PlatformCompat;
-
 import java.io.PrintWriter;
-import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.Locale;
 
 /**
  * ShellCommands for GameManagerService.
@@ -83,42 +78,11 @@
         final PrintWriter pw = getOutPrintWriter();
         try {
             switch (cmd) {
-                case "downscale": {
-                    final String ratio = getNextArgRequired();
-                    final String packageName = getNextArgRequired();
-
-                    final long changeId = GameManagerService.getCompatChangeId(ratio);
-                    if (changeId == 0 && !ratio.equals("disable")) {
-                        pw.println("Invalid scaling ratio '" + ratio + "'");
-                        break;
-                    }
-
-                    Set<Long> enabled = new ArraySet<>();
-                    Set<Long> disabled;
-                    if (changeId == 0) {
-                        disabled = DOWNSCALE_CHANGE_IDS;
-                    } else {
-                        enabled.add(DOWNSCALED);
-                        enabled.add(changeId);
-                        disabled = DOWNSCALE_CHANGE_IDS.stream()
-                          .filter(it -> it != DOWNSCALED && it != changeId)
-                          .collect(Collectors.toSet());
-                    }
-
-                    final PlatformCompat platformCompat = (PlatformCompat)
-                            ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
-                    final CompatibilityChangeConfig overrides =
-                            new CompatibilityChangeConfig(
-                                new Compatibility.ChangeConfig(enabled, disabled));
-
-                    platformCompat.setOverrides(overrides, packageName);
-                    if (changeId == 0) {
-                        pw.println("Disable downscaling for " + packageName + ".");
-                    } else {
-                        pw.println("Enable downscaling ratio for " + packageName + " to " + ratio);
-                    }
-
-                    return 0;
+                case "set": {
+                    return runSetGameMode(pw);
+                }
+                case "reset": {
+                    return runResetGameMode(pw);
                 }
                 case "mode": {
                     /** The "mode" command allows setting a package's current game mode outside of
@@ -132,6 +96,9 @@
                      */
                     return runGameMode(pw);
                 }
+                case "list": {
+                    return runGameList(pw);
+                }
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -141,6 +108,22 @@
         return -1;
     }
 
+    private int runGameList(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+        final String packageName = getNextArgRequired();
+
+        final GameManagerService gameManagerService = (GameManagerService)
+                ServiceManager.getService(Context.GAME_SERVICE);
+
+        final String listStr = gameManagerService.getInterventionList(packageName);
+
+        if (listStr == null) {
+            pw.println("No interventions found for " + packageName);
+        } else {
+            pw.println(packageName + " interventions: " + listStr);
+        }
+        return 0;
+    }
+
     private int runGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
         final String option = getNextOption();
         String userIdStr = null;
@@ -200,6 +183,172 @@
         return 0;
     }
 
+    private int runSetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+        String option = getNextArgRequired();
+        if (!option.equals("--mode")) {
+            pw.println("Invalid option '" + option + "'");
+            return -1;
+        }
+
+        final String gameMode = getNextArgRequired();
+
+        /**
+         * handling optional input
+         * "--user", "--downscale" and "--fps" can come in any order
+         */
+        String userIdStr = null;
+        String fpsStr = null;
+        String downscaleRatio = null;
+        while ((option = getNextOption()) != null) {
+            switch (option) {
+                case "--user":
+                    if (userIdStr == null) {
+                        userIdStr = getNextArgRequired();
+                    } else {
+                        pw.println("Duplicate option '" + option + "'");
+                        return -1;
+                    }
+                    break;
+                case "--downscale":
+                    if (downscaleRatio == null) {
+                        downscaleRatio = getNextArgRequired();
+                        if (downscaleRatio != null
+                                && GameManagerService.getCompatChangeId(downscaleRatio) == 0
+                                && !downscaleRatio.equals("disable")) {
+                            pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
+                            return -1;
+                        }
+                    } else {
+                        pw.println("Duplicate option '" + option + "'");
+                        return -1;
+                    }
+                    break;
+                case "--fps":
+                    if (fpsStr == null) {
+                        fpsStr = getNextArgRequired();
+                        if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
+                            pw.println("Invalid frame rate '" + fpsStr + "'");
+                            return -1;
+                        }
+                    } else {
+                        pw.println("Duplicate option '" + option + "'");
+                        return -1;
+                    }
+                    break;
+                default:
+                    pw.println("Invalid option '" + option + "'");
+                    return -1;
+            }
+        }
+
+        final String packageName = getNextArgRequired();
+
+        int userId = userIdStr != null ? Integer.parseInt(userIdStr)
+                : ActivityManager.getCurrentUser();
+
+        final GameManagerService gameManagerService = (GameManagerService)
+                ServiceManager.getService(Context.GAME_SERVICE);
+
+        boolean batteryModeSupported = false;
+        boolean perfModeSupported = false;
+        int [] modes = gameManagerService.getAvailableGameModes(packageName);
+
+        for (int mode : modes) {
+            if (mode == GameManager.GAME_MODE_PERFORMANCE) {
+                perfModeSupported = true;
+            } else if (mode == GameManager.GAME_MODE_BATTERY) {
+                batteryModeSupported = true;
+            }
+        }
+
+        switch (gameMode.toLowerCase(Locale.getDefault())) {
+            case "2":
+            case "performance":
+                if (perfModeSupported) {
+                    gameManagerService.setGameModeConfigOverride(packageName, userId,
+                            GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
+                } else {
+                    pw.println("Game mode: " + gameMode + " not supported by "
+                            + packageName);
+                    return -1;
+                }
+                break;
+            case "3":
+            case "battery":
+                if (batteryModeSupported) {
+                    gameManagerService.setGameModeConfigOverride(packageName, userId,
+                            GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
+                } else {
+                    pw.println("Game mode: " + gameMode + " not supported by "
+                            + packageName);
+                    return -1;
+                }
+                break;
+            default:
+                pw.println("Invalid game mode: " + gameMode);
+                return -1;
+        }
+        return 0;
+    }
+
+    private int runResetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+        String option = null;
+        String gameMode = null;
+        String userIdStr = null;
+        while ((option = getNextOption()) != null) {
+            switch (option) {
+                case "--user":
+                    if (userIdStr == null) {
+                        userIdStr = getNextArgRequired();
+                    } else {
+                        pw.println("Duplicate option '" + option + "'");
+                        return -1;
+                    }
+                    break;
+                case "--mode":
+                    if (gameMode == null) {
+                        gameMode = getNextArgRequired();
+                    } else {
+                        pw.println("Duplicate option '" + option + "'");
+                        return -1;
+                    }
+                    break;
+                default:
+                    pw.println("Invalid option '" + option + "'");
+                    return -1;
+            }
+        }
+
+        final String packageName = getNextArgRequired();
+
+        final GameManagerService gameManagerService = (GameManagerService)
+                ServiceManager.getService(Context.GAME_SERVICE);
+
+        int userId = userIdStr != null ? Integer.parseInt(userIdStr)
+                : ActivityManager.getCurrentUser();
+
+        if (gameMode == null) {
+            gameManagerService.resetGameModeConfigOverride(packageName, userId, -1);
+            return 0;
+        }
+
+        switch (gameMode.toLowerCase(Locale.getDefault())) {
+            case "2":
+            case "performance":
+                gameManagerService.resetGameModeConfigOverride(packageName, userId,
+                        GameManager.GAME_MODE_PERFORMANCE);
+                break;
+            case "3":
+            case "battery":
+                gameManagerService.resetGameModeConfigOverride(packageName, userId,
+                        GameManager.GAME_MODE_BATTERY);
+                break;
+            default:
+                pw.println("Invalid game mode: " + gameMode);
+                return -1;
+        }
+        return 0;
+    }
 
     @Override
     public void onHelp() {
@@ -207,10 +356,25 @@
         pw.println("Game manager (game) commands:");
         pw.println("  help");
         pw.println("      Print this help text.");
-        pw.println("  downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] <PACKAGE_NAME>");
-        pw.println("      Force app to run at the specified scaling ratio.");
+        pw.println("  downscale");
+        pw.println("      Deprecated. Please use `set` command.");
         pw.println("  mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
-        pw.println("      Force app to run in the specified game mode, if supported.");
-        pw.println("      --user <USER_ID>: apply for the given user, the current user is used when unspecified.");
+        pw.println("      Set app to run in the specified game mode, if supported.");
+        pw.println("      --user <USER_ID>: apply for the given user,");
+        pw.println("                        the current user is used when unspecified.");
+        pw.println("  set --mode [2|3|performance|battery] [intervention configs] <PACKAGE_NAME>");
+        pw.println("      Set app to run at given game mode with configs, if supported.");
+        pw.println("      Intervention configs consists of:");
+        pw.println("      --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
+        pw.println("                  |0.7|0.75|0.8|0.85|0.9|disable]");
+        pw.println("      Set app to run at the specified scaling ratio.");
+        pw.println("      --fps [30|45|60|90|120|disable]");
+        pw.println("      Set app to run at the specified fps, if supported.");
+        pw.println("  reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
+        pw.println("      Resets the game mode of the app to device configuration.");
+        pw.println("      --mode [2|3|performance|battery]: apply for the given mode,");
+        pw.println("                                        resets all modes when unspecified.");
+        pw.println("      --user <USER_ID>: apply for the given user,");
+        pw.println("                        the current user is used when unspecified.");
     }
 }
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
index d5ac03a..b4c43f6 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
@@ -20,6 +20,7 @@
 import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.Intent;
+import android.os.ServiceManager;
 import android.service.games.GameService;
 import android.service.games.GameSessionService;
 import android.service.games.IGameService;
@@ -27,6 +28,9 @@
 
 import com.android.internal.infra.ServiceConnector;
 import com.android.internal.os.BackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerService;
 
 final class GameServiceProviderInstanceFactoryImpl implements GameServiceProviderInstanceFactory {
     private final Context mContext;
@@ -44,6 +48,8 @@
                 BackgroundThread.getExecutor(),
                 new GameClassifierImpl(mContext.getPackageManager()),
                 ActivityTaskManager.getService(),
+                (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
+                LocalServices.getService(WindowManagerInternal.class),
                 new GameServiceConnector(mContext, gameServiceProviderConfiguration),
                 new GameSessionServiceConnector(mContext, gameServiceProviderConfiguration));
     }
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 3f3f257..43c9839 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -17,22 +17,37 @@
 package com.android.server.app;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
-import android.os.IBinder;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.games.CreateGameSessionRequest;
+import android.service.games.CreateGameSessionResult;
+import android.service.games.GameScreenshotResult;
+import android.service.games.GameSessionViewHostConfiguration;
+import android.service.games.GameStartedEvent;
 import android.service.games.IGameService;
+import android.service.games.IGameServiceController;
 import android.service.games.IGameSession;
+import android.service.games.IGameSessionController;
 import android.service.games.IGameSessionService;
 import android.util.Slog;
+import android.view.SurfaceControlViewHost.SurfacePackage;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.infra.ServiceConnector;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerService;
 
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -60,12 +75,50 @@
                 GameServiceProviderInstanceImpl.this.onTaskRemoved(taskId);
             });
         }
+
+        @Override
+        public void onTaskFocusChanged(int taskId, boolean focused) {
+            mBackgroundExecutor.execute(() -> {
+                GameServiceProviderInstanceImpl.this.onTaskFocusChanged(taskId, focused);
+            });
+        }
+
+        // TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
+        // to only when the associated task is running. Right now it is possible for a task to
+        // move into the background and for all associated processes to die and for the Game Session
+        // provider's GameSessionService to continue to be running. Ideally we could unbind the
+        // service when this happens.
     };
+
+    private final IGameServiceController mGameServiceController =
+            new IGameServiceController.Stub() {
+                @Override
+                public void createGameSession(int taskId) {
+                    mBackgroundExecutor.execute(() -> {
+                        GameServiceProviderInstanceImpl.this.createGameSession(taskId);
+                    });
+                }
+            };
+
+    private final IGameSessionController mGameSessionController =
+            new IGameSessionController.Stub() {
+                @Override
+                public void takeScreenshot(int taskId,
+                        @NonNull AndroidFuture gameScreenshotResultFuture) {
+                    mBackgroundExecutor.execute(() -> {
+                        GameServiceProviderInstanceImpl.this.takeScreenshot(taskId,
+                                gameScreenshotResultFuture);
+                    });
+                }
+            };
+
     private final Object mLock = new Object();
     private final UserHandle mUserHandle;
     private final Executor mBackgroundExecutor;
     private final GameClassifier mGameClassifier;
     private final IActivityTaskManager mActivityTaskManager;
+    private final WindowManagerService mWindowManagerService;
+    private final WindowManagerInternal mWindowManagerInternal;
     private final ServiceConnector<IGameService> mGameServiceConnector;
     private final ServiceConnector<IGameSessionService> mGameSessionServiceConnector;
 
@@ -76,16 +129,20 @@
     private volatile boolean mIsRunning;
 
     GameServiceProviderInstanceImpl(
-            UserHandle userHandle,
+            @NonNull UserHandle userHandle,
             @NonNull Executor backgroundExecutor,
             @NonNull GameClassifier gameClassifier,
             @NonNull IActivityTaskManager activityTaskManager,
+            @NonNull WindowManagerService windowManagerService,
+            @NonNull WindowManagerInternal windowManagerInternal,
             @NonNull ServiceConnector<IGameService> gameServiceConnector,
             @NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector) {
         mUserHandle = userHandle;
         mBackgroundExecutor = backgroundExecutor;
         mGameClassifier = gameClassifier;
         mActivityTaskManager = activityTaskManager;
+        mWindowManagerService = windowManagerService;
+        mWindowManagerInternal = windowManagerInternal;
         mGameServiceConnector = gameServiceConnector;
         mGameSessionServiceConnector = gameSessionServiceConnector;
     }
@@ -114,7 +171,7 @@
         // TODO(b/204503192): In cases where the connection to the game service fails retry with
         //  back off mechanism.
         AndroidFuture<Void> unusedPostConnectedFuture = mGameServiceConnector.post(gameService -> {
-            gameService.connected();
+            gameService.connected(mGameServiceController);
         });
 
         try {
@@ -138,16 +195,7 @@
         }
 
         for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
-            IGameSession gameSession = gameSessionRecord.getGameSession();
-            if (gameSession == null) {
-                continue;
-            }
-
-            try {
-                gameSession.destroy();
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
-            }
+            destroyGameSessionFromRecord(gameSessionRecord);
         }
         mGameSessions.clear();
 
@@ -168,10 +216,62 @@
         }
 
         synchronized (mLock) {
-            createGameSessionLocked(taskId, componentName);
+            gameTaskStartedLocked(taskId, componentName);
         }
     }
 
+    private void onTaskFocusChanged(int taskId, boolean focused) {
+        synchronized (mLock) {
+            onTaskFocusChangedLocked(taskId, focused);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onTaskFocusChangedLocked(int taskId, boolean focused) {
+        if (DEBUG) {
+            Slog.d(TAG, "onTaskFocusChangedLocked() id: " + taskId + " focused: " + focused);
+        }
+
+        final GameSessionRecord gameSessionRecord = mGameSessions.get(taskId);
+        if (gameSessionRecord == null || gameSessionRecord.getGameSession() == null) {
+            return;
+        }
+
+        try {
+            gameSessionRecord.getGameSession().onTaskFocusChanged(focused);
+        } catch (RemoteException ex) {
+            Slog.w(TAG, "Failed to notify session of task focus change: " + gameSessionRecord);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void gameTaskStartedLocked(int taskId, @NonNull ComponentName componentName) {
+        if (DEBUG) {
+            Slog.i(TAG, "gameStartedLocked() id: " + taskId + " component: " + componentName);
+        }
+
+        if (!mIsRunning) {
+            return;
+        }
+
+        GameSessionRecord existingGameSessionRecord = mGameSessions.get(taskId);
+        if (existingGameSessionRecord != null) {
+            Slog.w(TAG, "Existing game session found for task (id: " + taskId
+                    + ") creation. Ignoring.");
+            return;
+        }
+
+        GameSessionRecord gameSessionRecord = GameSessionRecord.awaitingGameSessionRequest(
+                taskId, componentName);
+        mGameSessions.put(taskId, gameSessionRecord);
+
+        AndroidFuture<Void> unusedPostGameStartedFuture = mGameServiceConnector.post(
+                gameService -> {
+                    gameService.gameStarted(
+                            new GameStartedEvent(taskId, componentName.getPackageName()));
+                });
+    }
+
     private void onTaskRemoved(int taskId) {
         synchronized (mLock) {
             boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
@@ -179,103 +279,186 @@
                 return;
             }
 
-            destroyGameSessionLocked(taskId);
+            removeAndDestroyGameSessionIfNecessaryLocked(taskId);
+        }
+    }
+
+    private void createGameSession(int taskId) {
+        synchronized (mLock) {
+            createGameSessionLocked(taskId);
         }
     }
 
     @GuardedBy("mLock")
-    private void createGameSessionLocked(int sessionId, @NonNull ComponentName componentName) {
+    private void createGameSessionLocked(int taskId) {
         if (DEBUG) {
-            Slog.i(TAG, "createGameSession() id: " + sessionId + " component: " + componentName);
+            Slog.i(TAG, "createGameSessionLocked() id: " + taskId);
         }
 
         if (!mIsRunning) {
             return;
         }
 
-        GameSessionRecord existingGameSessionRecord = mGameSessions.get(sessionId);
-        if (existingGameSessionRecord != null) {
-            Slog.w(TAG, "Existing game session found for task (id: " + sessionId
+        GameSessionRecord existingGameSessionRecord = mGameSessions.get(taskId);
+        if (existingGameSessionRecord == null) {
+            Slog.w(TAG, "No existing game session record found for task (id: " + taskId
+                    + ") creation. Ignoring.");
+            return;
+        }
+        if (!existingGameSessionRecord.isAwaitingGameSessionRequest()) {
+            Slog.w(TAG, "Existing game session for task (id: " + taskId
+                    + ") is not awaiting game session request. Ignoring.");
+            return;
+        }
+
+        GameSessionViewHostConfiguration gameSessionViewHostConfiguration =
+                createViewHostConfigurationForTask(taskId);
+        if (gameSessionViewHostConfiguration == null) {
+            Slog.w(TAG, "Failed to create view host configuration for task (id" + taskId
                     + ") creation. Ignoring.");
             return;
         }
 
-        GameSessionRecord gameSessionRecord = GameSessionRecord.pendingGameSession(sessionId,
-                componentName);
-        mGameSessions.put(sessionId, gameSessionRecord);
+        if (DEBUG) {
+            Slog.d(TAG, "Determined initial view host configuration for task (id: " + taskId + "): "
+                    + gameSessionViewHostConfiguration);
+        }
 
-        // TODO(b/207035150): Allow the game service provider to determine if a game session
-        //  should be created. For now we will assume all games should have a session.
-        AndroidFuture<IBinder> gameSessionFuture = new AndroidFuture<IBinder>()
-                .orTimeout(CREATE_GAME_SESSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
-                .whenCompleteAsync((gameSessionIBinder, exception) -> {
-                    IGameSession gameSession = IGameSession.Stub.asInterface(gameSessionIBinder);
-                    if (exception != null || gameSession == null) {
-                        Slog.w(TAG, "Failed to create GameSession: " + gameSessionRecord,
-                                exception);
-                        synchronized (mLock) {
-                            destroyGameSessionLocked(sessionId);
-                        }
-                        return;
-                    }
+        mGameSessions.put(taskId, existingGameSessionRecord.withGameSessionRequested());
 
-                    synchronized (mLock) {
-                        attachGameSessionLocked(sessionId, gameSession);
-                    }
-                }, mBackgroundExecutor);
+        AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture =
+                new AndroidFuture<CreateGameSessionResult>()
+                        .orTimeout(CREATE_GAME_SESSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+                        .whenCompleteAsync((createGameSessionResult, exception) -> {
+                            if (exception != null || createGameSessionResult == null) {
+                                Slog.w(TAG, "Failed to create GameSession: "
+                                                + existingGameSessionRecord,
+                                        exception);
+                                synchronized (mLock) {
+                                    removeAndDestroyGameSessionIfNecessaryLocked(taskId);
+                                }
+                                return;
+                            }
+
+                            synchronized (mLock) {
+                                attachGameSessionLocked(taskId, createGameSessionResult);
+                            }
+
+                            // The TaskStackListener may have made its task focused call for the
+                            // game session's task before the game session was created, so check if
+                            // the task is already focused so that the game session can be notified.
+                            setGameSessionFocusedIfNecessary(taskId,
+                                    createGameSessionResult.getGameSession());
+                        }, mBackgroundExecutor);
 
         AndroidFuture<Void> unusedPostCreateGameSessionFuture =
                 mGameSessionServiceConnector.post(gameService -> {
                     CreateGameSessionRequest createGameSessionRequest =
-                            new CreateGameSessionRequest(sessionId, componentName.getPackageName());
-                    gameService.create(createGameSessionRequest, gameSessionFuture);
+                            new CreateGameSessionRequest(
+                                    taskId,
+                                    existingGameSessionRecord.getComponentName().getPackageName());
+                    gameService.create(
+                            mGameSessionController,
+                            createGameSessionRequest,
+                            gameSessionViewHostConfiguration,
+                            createGameSessionResultFuture);
                 });
     }
 
-    @GuardedBy("mLock")
-    private void attachGameSessionLocked(int sessionId, @NonNull IGameSession gameSession) {
-        if (DEBUG) {
-            Slog.i(TAG, "attachGameSession() id: " + sessionId);
-        }
-
-        GameSessionRecord gameSessionRecord = mGameSessions.get(sessionId);
-        if (gameSessionRecord == null) {
-            Slog.w(TAG, "No associated game session record. Destroying id: " + sessionId);
-
-            try {
-                gameSession.destroy();
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
+    private void setGameSessionFocusedIfNecessary(int taskId, IGameSession gameSession) {
+        try {
+            final ActivityTaskManager.RootTaskInfo rootTaskInfo =
+                    mActivityTaskManager.getFocusedRootTaskInfo();
+            if (rootTaskInfo != null && rootTaskInfo.taskId == taskId) {
+                gameSession.onTaskFocusChanged(true);
             }
-            return;
+        } catch (RemoteException ex) {
+            Slog.w(TAG, "Failed to set task focused for ID: " + taskId);
         }
-
-        mGameSessions.put(sessionId, gameSessionRecord.withGameSession(gameSession));
     }
 
     @GuardedBy("mLock")
-    private void destroyGameSessionLocked(int sessionId) {
-        // TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
-        // to only when the associated task is running. Right now it is possible for a task to
-        // move into the background and for all associated processes to die and for the Game Session
-        // provider's GameSessionService to continue to be running. Ideally we could unbind the
-        // service when this happens.
+    private void attachGameSessionLocked(
+            int taskId,
+            @NonNull CreateGameSessionResult createGameSessionResult) {
         if (DEBUG) {
-            Slog.i(TAG, "destroyGameSession() id: " + sessionId);
+            Slog.d(TAG, "attachGameSession() id: " + taskId);
         }
 
-        GameSessionRecord gameSessionRecord = mGameSessions.remove(sessionId);
+        GameSessionRecord gameSessionRecord = mGameSessions.get(taskId);
+
+        if (gameSessionRecord == null) {
+            Slog.w(TAG, "No associated game session record. Destroying id: " + taskId);
+            destroyGameSessionDuringAttach(taskId, createGameSessionResult);
+            return;
+        }
+
+        if (!gameSessionRecord.isGameSessionRequested()) {
+            destroyGameSessionDuringAttach(taskId, createGameSessionResult);
+            return;
+        }
+
+        try {
+            mWindowManagerInternal.addTaskOverlay(
+                    taskId,
+                    createGameSessionResult.getSurfacePackage());
+        } catch (IllegalArgumentException ex) {
+            Slog.w(TAG, "Failed to add task overlay. Destroying id: " + taskId);
+            destroyGameSessionDuringAttach(taskId, createGameSessionResult);
+            return;
+        }
+
+        mGameSessions.put(taskId,
+                gameSessionRecord.withGameSession(
+                        createGameSessionResult.getGameSession(),
+                        createGameSessionResult.getSurfacePackage()));
+    }
+
+    private void destroyGameSessionDuringAttach(
+            int taskId,
+            CreateGameSessionResult createGameSessionResult) {
+        try {
+            createGameSessionResult.getGameSession().onDestroyed();
+        } catch (RemoteException ex) {
+            Slog.w(TAG, "Failed to destroy session: " + taskId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void removeAndDestroyGameSessionIfNecessaryLocked(int taskId) {
+        if (DEBUG) {
+            Slog.d(TAG, "destroyGameSession() id: " + taskId);
+        }
+
+        GameSessionRecord gameSessionRecord = mGameSessions.remove(taskId);
         if (gameSessionRecord == null) {
             if (DEBUG) {
-                Slog.w(TAG, "No game session found for id: " + sessionId);
+                Slog.w(TAG, "No game session found for id: " + taskId);
             }
             return;
         }
+        destroyGameSessionFromRecord(gameSessionRecord);
+    }
+
+    private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) {
+        SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage();
+        if (surfacePackage != null) {
+            try {
+                mWindowManagerInternal.removeTaskOverlay(
+                        gameSessionRecord.getTaskId(),
+                        surfacePackage);
+            } catch (IllegalArgumentException ex) {
+                Slog.i(TAG,
+                        "Failed to remove task overlay. This is expected if the task is already "
+                                + "destroyed: "
+                                + gameSessionRecord);
+            }
+        }
 
         IGameSession gameSession = gameSessionRecord.getGameSession();
         if (gameSession != null) {
             try {
-                gameSession.destroy();
+                gameSession.onDestroyed();
             } catch (RemoteException ex) {
                 Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
             }
@@ -283,7 +466,7 @@
 
         if (mGameSessions.isEmpty()) {
             if (DEBUG) {
-                Slog.i(TAG, "No active game sessions. Disconnecting GameSessionService");
+                Slog.d(TAG, "No active game sessions. Disconnecting GameSessionService");
             }
 
             if (mGameSessionServiceConnector != null) {
@@ -291,4 +474,62 @@
             }
         }
     }
+
+    @Nullable
+    private GameSessionViewHostConfiguration createViewHostConfigurationForTask(int taskId) {
+        RunningTaskInfo runningTaskInfo = getRunningTaskInfoForTask(taskId);
+        if (runningTaskInfo == null) {
+            return null;
+        }
+
+        Rect bounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
+        return new GameSessionViewHostConfiguration(
+                runningTaskInfo.displayId,
+                bounds.width(),
+                bounds.height());
+    }
+
+    @Nullable
+    private RunningTaskInfo getRunningTaskInfoForTask(int taskId) {
+        List<RunningTaskInfo> runningTaskInfos;
+        try {
+            runningTaskInfos = mActivityTaskManager.getTasks(
+                    /* maxNum= */ Integer.MAX_VALUE,
+                    /* filterOnlyVisibleRecents= */ true,
+                    /* keepIntentExtra= */ false);
+        } catch (RemoteException ex) {
+            Slog.w(TAG, "Failed to fetch running tasks");
+            return null;
+        }
+
+        for (RunningTaskInfo taskInfo : runningTaskInfos) {
+            if (taskInfo.taskId == taskId) {
+                return taskInfo;
+            }
+        }
+
+        return null;
+    }
+
+    @VisibleForTesting
+    void takeScreenshot(int taskId, @NonNull AndroidFuture callback) {
+        synchronized (mLock) {
+            boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
+            if (!isTaskAssociatedWithGameSession) {
+                Slog.w(TAG, "No game session found for id: " + taskId);
+                callback.complete(GameScreenshotResult.createInternalErrorResult());
+                return;
+            }
+        }
+
+        mBackgroundExecutor.execute(() -> {
+            final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId);
+            if (bitmap == null) {
+                Slog.w(TAG, "Could not get bitmap for id: " + taskId);
+                callback.complete(GameScreenshotResult.createInternalErrorResult());
+            } else {
+                callback.complete(GameScreenshotResult.createSuccessResult(bitmap));
+            }
+        });
+    }
 }
diff --git a/services/core/java/com/android/server/app/GameSessionRecord.java b/services/core/java/com/android/server/app/GameSessionRecord.java
index 329e9e8..a241812 100644
--- a/services/core/java/com/android/server/app/GameSessionRecord.java
+++ b/services/core/java/com/android/server/app/GameSessionRecord.java
@@ -20,33 +20,92 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.service.games.IGameSession;
+import android.view.SurfaceControlViewHost.SurfacePackage;
 
 import java.util.Objects;
 
 final class GameSessionRecord {
 
+    private enum State {
+        // Game task is running, but GameSession not created.
+        NO_GAME_SESSION_REQUESTED,
+        // Game Service provider requested a Game Session and we are in the
+        // process of creating it. GameSessionRecord.getGameSession() == null;
+        GAME_SESSION_REQUESTED,
+        // A Game Session is created and attached.
+        // GameSessionRecord.getGameSession() != null.
+        GAME_SESSION_ATTACHED,
+    }
+
     private final int mTaskId;
+    private final State mState;
     private final ComponentName mRootComponentName;
     @Nullable
     private final IGameSession mIGameSession;
+    @Nullable
+    private final SurfacePackage mSurfacePackage;
 
-    static GameSessionRecord pendingGameSession(int taskId, ComponentName rootComponentName) {
-        return new GameSessionRecord(taskId, rootComponentName, /* gameSession= */ null);
+    static GameSessionRecord awaitingGameSessionRequest(int taskId,
+            ComponentName rootComponentName) {
+        return new GameSessionRecord(
+                taskId,
+                State.NO_GAME_SESSION_REQUESTED,
+                rootComponentName,
+                /* gameSession= */ null,
+                /* surfacePackage= */ null);
     }
 
     private GameSessionRecord(
             int taskId,
+            @NonNull State state,
             @NonNull ComponentName rootComponentName,
-            @Nullable IGameSession gameSession) {
+            @Nullable IGameSession gameSession,
+            @Nullable SurfacePackage surfacePackage) {
         this.mTaskId = taskId;
+        this.mState = state;
         this.mRootComponentName = rootComponentName;
         this.mIGameSession = gameSession;
+        this.mSurfacePackage = surfacePackage;
+    }
+
+    public boolean isAwaitingGameSessionRequest() {
+        return mState == State.NO_GAME_SESSION_REQUESTED;
     }
 
     @NonNull
-    public GameSessionRecord withGameSession(@NonNull IGameSession gameSession) {
+    public GameSessionRecord withGameSessionRequested() {
+        return new GameSessionRecord(
+                mTaskId,
+                State.GAME_SESSION_REQUESTED,
+                mRootComponentName,
+                /* gameSession=*/ null,
+                /* surfacePackage=*/ null);
+    }
+
+    public boolean isGameSessionRequested() {
+        return mState == State.GAME_SESSION_REQUESTED;
+    }
+
+    @NonNull
+    public GameSessionRecord withGameSession(
+            @NonNull IGameSession gameSession,
+            @NonNull SurfacePackage surfacePackage) {
         Objects.requireNonNull(gameSession);
-        return new GameSessionRecord(mTaskId, mRootComponentName, gameSession);
+        return new GameSessionRecord(mTaskId,
+                State.GAME_SESSION_ATTACHED,
+                mRootComponentName,
+                gameSession,
+                surfacePackage);
+    }
+
+    @NonNull
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    @NonNull
+    public ComponentName getComponentName() {
+        return mRootComponentName;
     }
 
     @Nullable
@@ -54,15 +113,24 @@
         return mIGameSession;
     }
 
+    @Nullable
+    public SurfacePackage getSurfacePackage() {
+        return mSurfacePackage;
+    }
+
     @Override
     public String toString() {
         return "GameSessionRecord{"
                 + "mTaskId="
                 + mTaskId
+                + ", mState="
+                + mState
                 + ", mRootComponentName="
                 + mRootComponentName
                 + ", mIGameSession="
                 + mIGameSession
+                + ", mSurfacePackage="
+                + mSurfacePackage
                 + '}';
     }
 
@@ -77,12 +145,16 @@
         }
 
         GameSessionRecord that = (GameSessionRecord) o;
-        return mTaskId == that.mTaskId && mRootComponentName.equals(that.mRootComponentName)
-                && Objects.equals(mIGameSession, that.mIGameSession);
+        return mTaskId == that.mTaskId
+                && mState == that.mState
+                && mRootComponentName.equals(that.mRootComponentName)
+                && Objects.equals(mIGameSession, that.mIGameSession)
+                && Objects.equals(mSurfacePackage, that.mSurfacePackage);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mTaskId, mRootComponentName, mIGameSession);
+        return Objects.hash(
+                mTaskId, mState, mRootComponentName, mIGameSession, mState, mSurfacePackage);
     }
 }
diff --git a/services/core/java/com/android/server/app/OWNERS b/services/core/java/com/android/server/app/OWNERS
index aaebbfa..221e06c 100644
--- a/services/core/java/com/android/server/app/OWNERS
+++ b/services/core/java/com/android/server/app/OWNERS
@@ -1 +1 @@
-per-file GameManager* = file:/GAME_MANAGER_OWNERS
+per-file Game* = file:/GAME_MANAGER_OWNERS
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 4d025c9..9d4d1c1 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -223,10 +223,10 @@
                 android.Manifest.permission.MANAGE_APP_HIBERNATION,
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         userId = handleIncomingUser(userId, methodName);
-        if (!checkUserStatesExist(userId, methodName)) {
-            return false;
-        }
         synchronized (mLock) {
+            if (!checkUserStatesExist(userId, methodName)) {
+                return false;
+            }
             final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
             final UserLevelState pkgState = packageStates.get(packageName);
             if (pkgState == null) {
@@ -278,10 +278,10 @@
                 android.Manifest.permission.MANAGE_APP_HIBERNATION,
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         final int realUserId = handleIncomingUser(userId, methodName);
-        if (!checkUserStatesExist(realUserId, methodName)) {
-            return;
-        }
         synchronized (mLock) {
+            if (!checkUserStatesExist(realUserId, methodName)) {
+                return;
+            }
             final Map<String, UserLevelState> packageStates = mUserStates.get(realUserId);
             final UserLevelState pkgState = packageStates.get(packageName);
             if (pkgState == null) {
@@ -365,10 +365,10 @@
                 android.Manifest.permission.MANAGE_APP_HIBERNATION,
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         userId = handleIncomingUser(userId, methodName);
-        if (!checkUserStatesExist(userId, methodName)) {
-            return hibernatingPackages;
-        }
         synchronized (mLock) {
+            if (!checkUserStatesExist(userId, methodName)) {
+                return hibernatingPackages;
+            }
             Map<String, UserLevelState> userStates = mUserStates.get(userId);
             for (UserLevelState state : userStates.values()) {
                 if (state.hibernated) {
@@ -658,6 +658,14 @@
         }
     }
 
+    /**
+     * Check that user states exist.
+     *
+     * @param userId user to check
+     * @param methodName method name that is calling. Used for logging purposes.
+     * @return true if user states exist
+     */
+    @GuardedBy("mLock")
     private boolean checkUserStatesExist(int userId, String methodName) {
         if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
             Slog.e(TAG, String.format(
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3c557d0..40fda4c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -113,7 +113,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.UserInfo;
-import android.content.pm.parsing.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedAttribution;
 import android.database.ContentObserver;
 import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
 import android.net.Uri;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2fcdd61..565e295 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1219,6 +1219,7 @@
                         mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj,
                                 mAudioService.getBluetoothContextualVolumeStream());
                     }
+                    break;
                 case MSG_BT_HEADSET_CNCT_FAILED:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index a27e4b77..2dd6bf5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -238,6 +238,9 @@
 
     //------------------------------------------------------------
     /*package*/ void dump(PrintWriter pw, String prefix) {
+        pw.println("\n" + prefix + "BECOMING_NOISY_INTENT_DEVICES_SET=");
+        BECOMING_NOISY_INTENT_DEVICES_SET.forEach(device -> {
+            pw.print(" 0x" +  Integer.toHexString(device)); });
         pw.println("\n" + prefix + "Preferred devices for strategy:");
         mPreferredDevices.forEach((strategy, device) -> {
             pw.println("  " + prefix + "strategy:" + strategy + " device:" + device); });
@@ -1121,6 +1124,12 @@
     private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
             String eventSource) {
         if (device != AudioSystem.DEVICE_NONE) {
+
+            /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
+             * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
+             */
+            mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
             AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
                     address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
@@ -1204,10 +1213,13 @@
                         state == AudioService.CONNECTION_STATE_CONNECTED
                                 ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED);
         if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
+            Log.i(TAG, "not sending NOISY: state=" + state);
             mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
             return 0;
         }
         if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) {
+            Log.i(TAG, "not sending NOISY: device=0x" + Integer.toHexString(device)
+                    + " not in set " + BECOMING_NOISY_INTENT_DEVICES_SET);
             mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
             return 0;
         }
@@ -1217,18 +1229,24 @@
             if (((di.mDeviceType & AudioSystem.DEVICE_BIT_IN) == 0)
                     && BECOMING_NOISY_INTENT_DEVICES_SET.contains(di.mDeviceType)) {
                 devices.add(di.mDeviceType);
+                Log.i(TAG, "NOISY: adding 0x" + Integer.toHexString(di.mDeviceType));
             }
         }
         if (musicDevice == AudioSystem.DEVICE_NONE) {
             musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
+            Log.i(TAG, "NOISY: musicDevice changing from NONE to 0x"
+                    + Integer.toHexString(musicDevice));
         }
 
         // always ignore condition on device being actually used for music when in communication
         // because music routing is altered in this case.
         // also checks whether media routing if affected by a dynamic policy or mirroring
-        if (((device == musicDevice) || mDeviceBroker.isInCommunication())
-                && AudioSystem.isSingleAudioDeviceType(devices, device)
-                && !mDeviceBroker.hasMediaDynamicPolicy()
+        final boolean inCommunication = mDeviceBroker.isInCommunication();
+        final boolean singleAudioDeviceType = AudioSystem.isSingleAudioDeviceType(devices, device);
+        final boolean hasMediaDynamicPolicy = mDeviceBroker.hasMediaDynamicPolicy();
+        if (((device == musicDevice) || inCommunication)
+                && singleAudioDeviceType
+                && !hasMediaDynamicPolicy
                 && (musicDevice != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)) {
             if (!mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/)
                     && !mDeviceBroker.hasAudioFocusUsers()) {
@@ -1241,6 +1259,12 @@
             }
             mDeviceBroker.postBroadcastBecomingNoisy();
             delay = AudioService.BECOMING_NOISY_DELAY_MS;
+        } else {
+            Log.i(TAG, "not sending NOISY: device:0x" + Integer.toHexString(device)
+                    + " musicDevice:0x" + Integer.toHexString(musicDevice)
+                    + " inComm:" + inCommunication
+                    + " mediaPolicy:" + hasMediaDynamicPolicy
+                    + " singleDevice:" + singleAudioDeviceType);
         }
 
         mmi.set(MediaMetrics.Property.DELAY_MS, delay).record();
@@ -1336,6 +1360,9 @@
             case AudioSystem.DEVICE_OUT_USB_HEADSET:
                 connType = AudioRoutesInfo.MAIN_USB;
                 break;
+            case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET:
+                connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
+                break;
         }
 
         synchronized (mCurAudioRoutes) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0879bec..0ea936e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2611,6 +2611,16 @@
         return getDevicesForAttributesInt(attributes);
     }
 
+    /** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
+     * This method is similar with AudioService#getDevicesForAttributes,
+     * only it doesn't enforce permissions because it is used by an unprivileged public API
+     * instead of the system API.
+     */
+    public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
+            @NonNull AudioAttributes attributes) {
+        return getDevicesForAttributesInt(attributes);
+    }
+
     /**
      * @see AudioManager#isMusicActive()
      * @param remotely true if query is for remote playback (cast), false for local playback.
@@ -4374,12 +4384,6 @@
         if (!mHasVibrator) {
             return false;
         }
-        final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
-        if (hapticsDisabled) {
-            return false;
-        }
-
         if (effect == null) {
             return false;
         }
@@ -8541,7 +8545,7 @@
 
     /** @see Spatializer#getSpatializerCompatibleAudioDevices() */
     public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
-        enforceModifyAudioRoutingPermission();
+        enforceModifyDefaultAudioEffectsPermission();
         return mSpatializerHelper.getCompatibleAudioDevices();
     }
 
@@ -9319,8 +9323,6 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        mAudioSystem.dump(pw);
-
         sLifecycleLogger.dump(pw);
         if (mAudioHandler != null) {
             pw.println("\nMessage handler (watch for unhandled messages):");
@@ -9400,6 +9402,9 @@
         pw.println("mHasSpatializerEffect:" + mHasSpatializerEffect);
         pw.println("isSpatializerEnabled:" + isSpatializerEnabled());
         pw.println("isSpatialAudioEnabled:" + isSpatialAudioEnabled());
+        mSpatializerHelper.dump(pw);
+
+        mAudioSystem.dump(pw);
     }
 
     private void dumpSupportedSystemUsage(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index ac212ee..a2ba76b 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -524,11 +524,28 @@
      * @param pw
      */
     public void dump(PrintWriter pw) {
+        pw.println("\nAudioSystemAdapter:");
+        pw.println(" mDevicesForStreamCache:");
+        if (mDevicesForStreamCache != null) {
+            for (Integer stream : mDevicesForStreamCache.keySet()) {
+                pw.println("\t stream:" + stream + " device:"
+                        + AudioSystem.getOutputDeviceName(mDevicesForStreamCache.get(stream)));
+            }
+        }
+        pw.println(" mDevicesForAttrCache:");
+        if (mDevicesForAttrCache != null) {
+            for (AudioAttributes attr : mDevicesForAttrCache.keySet()) {
+                pw.println("\t" + attr);
+                for (AudioDeviceAttributes devAttr : mDevicesForAttrCache.get(attr)) {
+                    pw.println("\t\t" + devAttr);
+                }
+            }
+        }
+
         if (!ENABLE_GETDEVICES_STATS) {
-            // only stats in this dump
+            // only stats in the rest of this dump
             return;
         }
-        pw.println("\nAudioSystemAdapter:");
         for (int i = 0; i < NB_MEASUREMENTS; i++) {
             pw.println(mMethodNames[i]
                     + ": counter=" + mMethodCallCounter[i]
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9273a5d..6ec9836 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -102,8 +102,6 @@
     /*package*/  static final int SCO_MODE_UNDEFINED = -1;
     // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
     /*package*/  static final int SCO_MODE_VIRTUAL_CALL = 0;
-    // SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
-    private  static final int SCO_MODE_RAW = 1;
     // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
     private  static final int SCO_MODE_VR = 2;
     // max valid SCO audio mode values
@@ -122,8 +120,6 @@
                 return "SCO_MODE_UNDEFINED";
             case SCO_MODE_VIRTUAL_CALL:
                 return "SCO_MODE_VIRTUAL_CALL";
-            case SCO_MODE_RAW:
-                return "SCO_MODE_RAW";
             case SCO_MODE_VR:
                 return "SCO_MODE_VR";
             default:
@@ -812,8 +808,6 @@
     private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
             BluetoothDevice device, int scoAudioMode) {
         switch (scoAudioMode) {
-            case SCO_MODE_RAW:
-                return bluetoothHeadset.disconnectAudio();
             case SCO_MODE_VIRTUAL_CALL:
                 return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
             case SCO_MODE_VR:
@@ -826,8 +820,6 @@
     private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
             BluetoothDevice device, int scoAudioMode) {
         switch (scoAudioMode) {
-            case SCO_MODE_RAW:
-                return bluetoothHeadset.connectAudio();
             case SCO_MODE_VIRTUAL_CALL:
                 return bluetoothHeadset.startScoUsingVirtualVoiceCall();
             case SCO_MODE_VR:
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 406b2dd2..74c8999 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -252,6 +252,9 @@
             AudioAttributes.FLAG_BYPASS_MUTE;
 
     private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
+        if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID) {
+            return;
+        }
         if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
                 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
             if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index b47ea4f..e6789d5 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -84,7 +85,7 @@
     private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
-    private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+    private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
     private int mSpatOutput = 0;
     private @Nullable ISpatializer mSpat;
     private @Nullable SpatializerCallback mSpatCallback;
@@ -681,12 +682,13 @@
             return;
         }
         try {
-            if (mode != mDesiredHeadTrackingMode) {
-                mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode));
+            if (mDesiredHeadTrackingMode != mode) {
                 mDesiredHeadTrackingMode = mode;
                 dispatchDesiredHeadTrackingMode(mode);
             }
-
+            if (mode != headTrackingModeTypeToSpatializerInt(mSpat.getActualHeadTrackingMode())) {
+                mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode));
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling setDesiredHeadTrackingMode", e);
         }
@@ -937,6 +939,7 @@
         } catch (Exception e) {
             Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e);
         }
+        setDesiredHeadTrackingMode(mDesiredHeadTrackingMode);
     }
 
     //------------------------------------------------------
@@ -983,4 +986,22 @@
                 throw(new IllegalArgumentException("Unexpected spatializer level:" + level));
         }
     }
+
+    void dump(PrintWriter pw) {
+        pw.println("SpatializerHelper:");
+        pw.println("\tmState:" + mState);
+        pw.println("\tmSpatLevel:" + mSpatLevel);
+        pw.println("\tmCapableSpatLevel:" + mCapableSpatLevel);
+        pw.println("\tmActualHeadTrackingMode:"
+                + Spatializer.headtrackingModeToString(mActualHeadTrackingMode));
+        pw.println("\tmDesiredHeadTrackingMode:"
+                + Spatializer.headtrackingModeToString(mDesiredHeadTrackingMode));
+        String modesString = "";
+        int[] modes = getSupportedHeadTrackingModes();
+        for (int mode : modes) {
+            modesString += Spatializer.headtrackingModeToString(mode) + " ";
+        }
+        pw.println("\tsupported head tracking modes:" + modesString);
+        pw.println("\tmSpatOutput:" + mSpatOutput);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
new file mode 100644
index 0000000..c3471bd
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -0,0 +1,126 @@
+/*
+ * 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.biometrics.log;
+
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Wrapper for {@link FrameworkStatsLog} to isolate the testable parts.
+ */
+public class BiometricFrameworkStatsLogger {
+
+    private static final String TAG = "BiometricFrameworkStatsLogger";
+
+    private static final BiometricFrameworkStatsLogger sInstance =
+            new BiometricFrameworkStatsLogger();
+
+    private BiometricFrameworkStatsLogger() {}
+
+    public static BiometricFrameworkStatsLogger getInstance() {
+        return sInstance;
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
+    public void acquired(
+            int statsModality, int statsAction, int statsClient, boolean isDebug,
+            int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
+                statsModality,
+                targetUserId,
+                isCrypto,
+                statsAction,
+                statsClient,
+                acquiredInfo,
+                vendorCode,
+                isDebug,
+                -1 /* sensorId */);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
+    public void authenticate(
+            int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
+            boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto,
+            int targetUserId, boolean isBiometricPrompt, float ambientLightLux) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
+                statsModality,
+                targetUserId,
+                isCrypto,
+                statsClient,
+                requireConfirmation,
+                authState,
+                sanitizeLatency(latency),
+                isDebug,
+                -1 /* sensorId */,
+                ambientLightLux);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
+    public void enroll(int statsModality, int statsAction, int statsClient,
+            int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
+                statsModality,
+                targetUserId,
+                sanitizeLatency(latency),
+                enrollSuccessful,
+                -1, /* sensorId */
+                ambientLightLux);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
+    public void error(
+            int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
+            int error, int vendorCode, boolean isCrypto, int targetUserId) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
+                statsModality,
+                targetUserId,
+                isCrypto,
+                statsAction,
+                statsClient,
+                error,
+                vendorCode,
+                isDebug,
+                sanitizeLatency(latency),
+                -1 /* sensorId */);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
+    public void reportUnknownTemplateEnrolledHal(int statsModality) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                statsModality,
+                BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
+                -1 /* sensorId */);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
+    public void reportUnknownTemplateEnrolledFramework(int statsModality) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                statsModality,
+                BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
+                -1 /* sensorId */);
+    }
+
+    private long sanitizeLatency(long latency) {
+        if (latency < 0) {
+            Slog.w(TAG, "found a negative latency : " + latency);
+            return -1;
+        }
+        return latency;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
new file mode 100644
index 0000000..d029af3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -0,0 +1,334 @@
+/*
+ * 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.biometrics.log;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Utils;
+
+/**
+ * Logger for all reported Biometric framework events.
+ */
+public class BiometricLogger {
+
+    public static final String TAG = "BiometricLogger";
+    public static final boolean DEBUG = false;
+
+    private final int mStatsModality;
+    private final int mStatsAction;
+    private final int mStatsClient;
+    private final BiometricFrameworkStatsLogger mSink;
+    @NonNull private final SensorManager mSensorManager;
+
+    private long mFirstAcquireTimeMs;
+    private boolean mLightSensorEnabled = false;
+    private boolean mShouldLogMetrics = true;
+
+    private class ALSProbe implements Probe {
+        @Override
+        public void enable() {
+            setLightSensorLoggingEnabled(getAmbientLightSensor(mSensorManager));
+        }
+
+        @Override
+        public void disable() {
+            setLightSensorLoggingEnabled(null);
+        }
+    }
+
+    // report only the most recent value
+    // consider com.android.server.display.utils.AmbientFilter or similar if need arises
+    private volatile float mLastAmbientLux = 0;
+
+    private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            mLastAmbientLux = event.values[0];
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // Not used.
+        }
+    };
+
+    /**
+     * @param context system_server context
+     * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
+     * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
+     * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
+     */
+    public BiometricLogger(
+            @NonNull Context context, int statsModality, int statsAction, int statsClient) {
+        this(statsModality, statsAction, statsClient,
+                BiometricFrameworkStatsLogger.getInstance(),
+                context.getSystemService(SensorManager.class));
+    }
+
+    @VisibleForTesting
+    BiometricLogger(
+            int statsModality, int statsAction, int statsClient,
+            BiometricFrameworkStatsLogger logSink, SensorManager sensorManager) {
+        mStatsModality = statsModality;
+        mStatsAction = statsAction;
+        mStatsClient = statsClient;
+        mSink = logSink;
+        mSensorManager = sensorManager;
+    }
+
+    /** Disable logging metrics and only log critical events, such as system health issues. */
+    public void disableMetrics() {
+        mShouldLogMetrics = false;
+    }
+
+    /** {@link BiometricsProtoEnums} CLIENT_* constants */
+    public int getStatsClient() {
+        return mStatsClient;
+    }
+
+    private boolean shouldSkipLogging() {
+        boolean shouldSkipLogging = (mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
+                || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN);
+
+        if (mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN) {
+            Slog.w(TAG, "Unknown field detected: MODALITY_UNKNOWN, will not report metric");
+        }
+
+        if (mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN) {
+            Slog.w(TAG, "Unknown field detected: ACTION_UNKNOWN, will not report metric");
+        }
+
+        if (mStatsClient == BiometricsProtoEnums.CLIENT_UNKNOWN) {
+            Slog.w(TAG, "Unknown field detected: CLIENT_UNKNOWN");
+        }
+
+        return shouldSkipLogging;
+    }
+
+    /** Log an acquisition event. */
+    public void logOnAcquired(Context context,
+            int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+        if (!mShouldLogMetrics) {
+            return;
+        }
+
+        final boolean isFace = mStatsModality == BiometricsProtoEnums.MODALITY_FACE;
+        final boolean isFingerprint = mStatsModality == BiometricsProtoEnums.MODALITY_FINGERPRINT;
+        if (isFace || isFingerprint) {
+            if ((isFingerprint && acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_START)
+                    || (isFace && acquiredInfo == FaceManager.FACE_ACQUIRED_START)) {
+                mFirstAcquireTimeMs = System.currentTimeMillis();
+            }
+        } else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+            if (mFirstAcquireTimeMs == 0) {
+                mFirstAcquireTimeMs = System.currentTimeMillis();
+            }
+        }
+        if (DEBUG) {
+            Slog.v(TAG, "Acquired! Modality: " + mStatsModality
+                    + ", User: " + targetUserId
+                    + ", IsCrypto: " + isCrypto
+                    + ", Action: " + mStatsAction
+                    + ", Client: " + mStatsClient
+                    + ", AcquiredInfo: " + acquiredInfo
+                    + ", VendorCode: " + vendorCode);
+        }
+
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.acquired(mStatsModality, mStatsAction, mStatsClient,
+                Utils.isDebugEnabled(context, targetUserId),
+                acquiredInfo, vendorCode, isCrypto, targetUserId);
+    }
+
+    /** Log an error during an operation. */
+    public void logOnError(Context context,
+            int error, int vendorCode, boolean isCrypto, int targetUserId) {
+        if (!mShouldLogMetrics) {
+            return;
+        }
+
+        final long latency = mFirstAcquireTimeMs != 0
+                ? (System.currentTimeMillis() - mFirstAcquireTimeMs) : -1;
+
+        if (DEBUG) {
+            Slog.v(TAG, "Error! Modality: " + mStatsModality
+                    + ", User: " + targetUserId
+                    + ", IsCrypto: " + isCrypto
+                    + ", Action: " + mStatsAction
+                    + ", Client: " + mStatsClient
+                    + ", Error: " + error
+                    + ", VendorCode: " + vendorCode
+                    + ", Latency: " + latency);
+        } else {
+            Slog.v(TAG, "Error latency: " + latency);
+        }
+
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.error(mStatsModality, mStatsAction, mStatsClient,
+                Utils.isDebugEnabled(context, targetUserId), latency,
+                error, vendorCode, isCrypto, targetUserId);
+    }
+
+    /** Log authentication attempt. */
+    public void logOnAuthenticated(Context context,
+            boolean authenticated, boolean requireConfirmation, boolean isCrypto,
+            int targetUserId, boolean isBiometricPrompt) {
+        if (!mShouldLogMetrics) {
+            return;
+        }
+
+        int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
+        if (!authenticated) {
+            authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
+        } else {
+            // Authenticated
+            if (isBiometricPrompt && requireConfirmation) {
+                authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
+            } else {
+                authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
+            }
+        }
+
+        // Only valid if we have a first acquired time, otherwise set to -1
+        final long latency = mFirstAcquireTimeMs != 0
+                ? (System.currentTimeMillis() - mFirstAcquireTimeMs)
+                : -1;
+
+        if (DEBUG) {
+            Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
+                    + ", User: " + targetUserId
+                    + ", IsCrypto: " + isCrypto
+                    + ", Client: " + mStatsClient
+                    + ", RequireConfirmation: " + requireConfirmation
+                    + ", State: " + authState
+                    + ", Latency: " + latency
+                    + ", Lux: " + mLastAmbientLux);
+        } else {
+            Slog.v(TAG, "Authentication latency: " + latency);
+        }
+
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.authenticate(mStatsModality, mStatsAction, mStatsClient,
+                Utils.isDebugEnabled(context, targetUserId),
+                latency, authenticated, authState, requireConfirmation, isCrypto,
+                targetUserId, isBiometricPrompt, mLastAmbientLux);
+    }
+
+    /** Log enrollment outcome. */
+    public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
+        if (!mShouldLogMetrics) {
+            return;
+        }
+
+        if (DEBUG) {
+            Slog.v(TAG, "Enrolled! Modality: " + mStatsModality
+                    + ", User: " + targetUserId
+                    + ", Client: " + mStatsClient
+                    + ", Latency: " + latency
+                    + ", Lux: " + mLastAmbientLux
+                    + ", Success: " + enrollSuccessful);
+        } else {
+            Slog.v(TAG, "Enroll latency: " + latency);
+        }
+
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
+                targetUserId, latency, enrollSuccessful, mLastAmbientLux);
+    }
+
+    /** Report unexpected enrollment reported by the HAL. */
+    public void logUnknownEnrollmentInHal() {
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.reportUnknownTemplateEnrolledHal(mStatsModality);
+    }
+
+    /** Report unknown enrollment in framework settings */
+    public void logUnknownEnrollmentInFramework() {
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.reportUnknownTemplateEnrolledFramework(mStatsModality);
+    }
+
+    /**
+     * Get a callback to start/stop ALS capture when a client runs.
+     *
+     * If the probe should not run for the entire operation, do not set startWithClient and
+     * start/stop the problem when needed.
+     *
+     * @param startWithClient if probe should start automatically when the operation starts.
+     */
+    @NonNull
+    public CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
+        return new CallbackWithProbe<>(new ALSProbe(), startWithClient);
+    }
+
+    /** The sensor to use for ALS logging. */
+    @Nullable
+    protected Sensor getAmbientLightSensor(@NonNull SensorManager sensorManager) {
+        return mShouldLogMetrics ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null;
+    }
+
+    private void setLightSensorLoggingEnabled(@Nullable Sensor lightSensor) {
+        if (DEBUG) {
+            Slog.v(TAG, "capturing ambient light using: "
+                    + (lightSensor != null ? lightSensor : "[disabled]"));
+        }
+
+        if (lightSensor != null) {
+            if (!mLightSensorEnabled) {
+                mLightSensorEnabled = true;
+                mLastAmbientLux = 0;
+                mSensorManager.registerListener(mLightSensorListener, lightSensor,
+                        SensorManager.SENSOR_DELAY_NORMAL);
+            }
+        } else {
+            mLightSensorEnabled = false;
+            mLastAmbientLux = 0;
+            mSensorManager.unregisterListener(mLightSensorListener);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java b/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
new file mode 100644
index 0000000..f7b73688
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
@@ -0,0 +1,56 @@
+/*
+ * 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.biometrics.log;
+
+import android.annotation.NonNull;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+/**
+ * Client monitor callback that exposes a probe.
+ *
+ * Disables the probe when the operation completes.
+ *
+ * @param <T> probe type
+ */
+public class CallbackWithProbe<T extends Probe> implements ClientMonitorCallback {
+    private final boolean mStartWithClient;
+    private final T mProbe;
+
+    public CallbackWithProbe(@NonNull T probe, boolean startWithClient) {
+        mProbe = probe;
+        mStartWithClient = startWithClient;
+    }
+
+    @Override
+    public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+        if (mStartWithClient) {
+            mProbe.enable();
+        }
+    }
+
+    @Override
+    public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+        mProbe.disable();
+    }
+
+    @NonNull
+    public T getProbe() {
+        return mProbe;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/Probe.java b/services/core/java/com/android/server/biometrics/log/Probe.java
new file mode 100644
index 0000000..9e6fc6b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/Probe.java
@@ -0,0 +1,30 @@
+/*
+ * 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.biometrics.log;
+
+/**
+ * Probe for loggable attributes that can be continuously monitored, such as ambient light.
+ *
+ * Disable probes when the sensors are in states that are not interesting for monitoring
+ * purposes to save power.
+ */
+public interface Probe {
+    /** Ensure the probe is actively sampling for new data. */
+    void enable();
+    /** Stop sampling data. */
+    void disable();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 6f71768..86d72ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -105,7 +105,8 @@
         // that do not handle lockout under the HAL. In these cases, ensure that the framework only
         // sends errors once per ClientMonitor.
         if (mShouldSendErrorToClient) {
-            logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
+            getLogger().logOnError(getContext(), errorCode, vendorCode,
+                    isCryptoOperation(), getTargetUserId());
             try {
                 if (getListener() != null) {
                     mShouldSendErrorToClient = false;
@@ -137,7 +138,7 @@
     }
 
     @Override
-    public void cancelWithoutStarting(@NonNull Callback callback) {
+    public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
         Slog.d(TAG, "cancelWithoutStarting: " + this);
 
         final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
@@ -163,7 +164,8 @@
 
     protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
             boolean shouldSend) {
-        super.logOnAcquired(getContext(), acquiredInfo, vendorCode, getTargetUserId());
+        getLogger().logOnAcquired(getContext(), acquiredInfo, vendorCode,
+                isCryptoOperation(), getTargetUserId());
         if (DEBUG) {
             Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
                     + ", shouldSend: " + shouldSend);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 358263d..35a0f57 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -91,7 +91,7 @@
 
     /**
      * Handles lifecycle, e.g. {@link BiometricScheduler},
-     * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication
+     * {@link ClientMonitorCallback} after authentication
      * results are known. Note that this happens asynchronously from (but shortly after)
      * {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows
      * {@link CoexCoordinator} a chance to invoke/delay this event.
@@ -180,8 +180,8 @@
     @Override
     public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
-        super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
-                getTargetUserId(), isBiometricPrompt());
+        getLogger().logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
+                isCryptoOperation(), getTargetUserId(), isBiometricPrompt());
 
         final ClientMonitorCallbackConverter listener = getListener();
 
@@ -440,7 +440,7 @@
      * Start authentication
      */
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         final @LockoutTracker.LockoutMode int lockoutMode =
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index b73e911..e1f7e2a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -25,18 +27,16 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.log.BiometricLogger;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.NoSuchElementException;
 
 /**
- * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
+ * Abstract base class for keeping track and dispatching events from the biometric's HAL to
  * the current client.  Subclasses are responsible for coordinating the interaction with
  * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
  */
-public abstract class BaseClientMonitor extends LoggableMonitor
-        implements IBinder.DeathRecipient {
+public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
 
     private static final String TAG = "Biometrics/ClientMonitor";
     protected static final boolean DEBUG = true;
@@ -44,69 +44,12 @@
     // Counter used to distinguish between ClientMonitor instances to help debugging.
     private static int sCount = 0;
 
-    /**
-     * Interface that ClientMonitor holders should use to receive callbacks.
-     */
-    public interface Callback {
-
-        /**
-         * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
-         * the queue and becomes the current operation).
-         *
-         * @param clientMonitor Reference of the ClientMonitor that is starting.
-         */
-        default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-        }
-
-        /**
-         * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
-         * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
-         * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
-         * implementation.
-         *
-         * @param clientMonitor Reference of the ClientMonitor that finished.
-         * @param success True if the operation completed successfully.
-         */
-        default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
-        }
-    }
-
-    /** Holder for wrapping multiple handlers into a single Callback. */
-    public static class CompositeCallback implements Callback {
-        @NonNull
-        private final List<Callback> mCallbacks;
-
-        public CompositeCallback(@NonNull Callback... callbacks) {
-            mCallbacks = new ArrayList<>();
-
-            for (Callback callback : callbacks) {
-                if (callback != null) {
-                    mCallbacks.add(callback);
-                }
-            }
-        }
-
-        @Override
-        public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                mCallbacks.get(i).onClientStarted(clientMonitor);
-            }
-        }
-
-        @Override
-        public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
-                boolean success) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).onClientFinished(clientMonitor, success);
-            }
-        }
-    }
-
     private final int mSequentialId;
     @NonNull private final Context mContext;
     private final int mTargetUserId;
     @NonNull private final String mOwner;
     private final int mSensorId; // sensorId as configured by the framework
+    @NonNull private final BiometricLogger mLogger;
 
     @Nullable private IBinder mToken;
     private long mRequestId;
@@ -118,7 +61,7 @@
 
     // Use an empty callback by default since delayed operations can receive events
     // before they are started and cause NPE in subclasses that access this field directly.
-    @NonNull protected Callback mCallback = new Callback() {
+    @NonNull protected ClientMonitorCallback mCallback = new ClientMonitorCallback() {
         @Override
         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
             Slog.e(TAG, "mCallback onClientStarted: called before set (should not happen)");
@@ -132,18 +75,6 @@
     };
 
     /**
-     * @return A ClientMonitorEnum constant defined in biometrics.proto
-     */
-    public abstract int getProtoEnum();
-
-    /**
-     * @return True if the ClientMonitor should cancel any current and pending interruptable clients
-     */
-    public boolean interruptsPrecedingClients() {
-        return false;
-    }
-
-    /**
      * @param context    system_server context
      * @param token      a unique token for the client
      * @param listener   recipient of related events (e.g. authentication)
@@ -159,7 +90,14 @@
             @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
             int statsClient) {
-        super(context, statsModality, statsAction, statsClient);
+        this(context, token, listener, userId, owner, cookie, sensorId,
+                new BiometricLogger(context, statsModality, statsAction, statsClient));
+    }
+
+    @VisibleForTesting
+    BaseClientMonitor(@NonNull Context context,
+            @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger) {
         mSequentialId = sCount++;
         mContext = context;
         mToken = token;
@@ -169,6 +107,7 @@
         mOwner = owner;
         mCookie = cookie;
         mSensorId = sensorId;
+        mLogger = logger;
 
         try {
             if (token != null) {
@@ -179,15 +118,19 @@
         }
     }
 
-    public int getCookie() {
-        return mCookie;
+    /** A ClientMonitorEnum constant defined in biometrics.proto */
+    public abstract int getProtoEnum();
+
+    /** True if the ClientMonitor should cancel any current and pending interruptable clients. */
+    public boolean interruptsPrecedingClients() {
+        return false;
     }
 
     /**
      * Starts the ClientMonitor's lifecycle.
      * @param callback invoked when the operation is complete (succeeds, fails, etc)
      */
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         mCallback = wrapCallbackForStart(callback);
         mCallback.onClientStarted(this);
     }
@@ -198,12 +141,13 @@
      * Returns the original callback unless overridden.
      */
     @NonNull
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
         return callback;
     }
 
     /** Signals this operation has completed its lifecycle and should no longer be used. */
-    void destroy() {
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public void destroy() {
         mAlreadyDone = true;
         if (mToken != null) {
             try {
@@ -255,6 +199,20 @@
         }
     }
 
+    /**
+     * Only valid for AuthenticationClient.
+     * @return true if the client is authenticating for a crypto operation.
+     */
+    protected boolean isCryptoOperation() {
+        return false;
+    }
+
+    /** Logger for this client */
+    @NonNull
+    public BiometricLogger getLogger() {
+        return mLogger;
+    }
+
     public final Context getContext() {
         return mContext;
     }
@@ -279,6 +237,11 @@
         return mSensorId;
     }
 
+    /** Cookie set when this monitor was created. */
+    public int getCookie() {
+        return mCookie;
+    }
+
     /** Unique request id. */
     public final long getRequestId() {
         return mRequestId;
@@ -303,7 +266,7 @@
     }
 
     @VisibleForTesting
-    public Callback getCallback() {
+    public ClientMonitorCallback getCallback() {
         return mCallback;
     }
 
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 a358bc2..1a6da94 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -17,10 +17,10 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.IntDef;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Handler;
@@ -55,6 +55,7 @@
  * We currently assume (and require) that each biometric sensor have its own instance of a
  * {@link BiometricScheduler}. See {@link CoexCoordinator}.
  */
+@MainThread
 public class BiometricScheduler {
 
     private static final String BASE_TAG = "BiometricScheduler";
@@ -110,123 +111,6 @@
         }
     }
 
-    /**
-     * Contains all the necessary information for a HAL operation.
-     */
-    @VisibleForTesting
-    static final class Operation {
-
-        /**
-         * The operation is added to the list of pending operations and waiting for its turn.
-         */
-        static final int STATE_WAITING_IN_QUEUE = 0;
-
-        /**
-         * The operation is added to the list of pending operations, but a subsequent operation
-         * has been added. This state only applies to {@link Interruptable} operations. When this
-         * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
-         */
-        static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
-
-        /**
-         * The operation has reached the front of the queue and has started.
-         */
-        static final int STATE_STARTED = 2;
-
-        /**
-         * The operation was started, but is now canceling. Operations should wait for the HAL to
-         * acknowledge that the operation was canceled, at which point it finishes.
-         */
-        static final int STATE_STARTED_CANCELING = 3;
-
-        /**
-         * The operation has reached the head of the queue but is waiting for BiometricService
-         * to acknowledge and start the operation.
-         */
-        static final int STATE_WAITING_FOR_COOKIE = 4;
-
-        /**
-         * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
-         */
-        static final int STATE_FINISHED = 5;
-
-        @IntDef({STATE_WAITING_IN_QUEUE,
-                STATE_WAITING_IN_QUEUE_CANCELING,
-                STATE_STARTED,
-                STATE_STARTED_CANCELING,
-                STATE_WAITING_FOR_COOKIE,
-                STATE_FINISHED})
-        @Retention(RetentionPolicy.SOURCE)
-        @interface OperationState {}
-
-        @NonNull final BaseClientMonitor mClientMonitor;
-        @Nullable final BaseClientMonitor.Callback mClientCallback;
-        @OperationState int mState;
-
-        Operation(
-                @NonNull BaseClientMonitor clientMonitor,
-                @Nullable BaseClientMonitor.Callback callback
-        ) {
-            this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
-        }
-
-        protected Operation(
-                @NonNull BaseClientMonitor clientMonitor,
-                @Nullable BaseClientMonitor.Callback callback,
-                @OperationState int state
-        ) {
-            mClientMonitor = clientMonitor;
-            mClientCallback = callback;
-            mState = state;
-        }
-
-        public boolean isHalOperation() {
-            return mClientMonitor instanceof HalClientMonitor<?>;
-        }
-
-        /**
-         * @return true if the operation requires the HAL, and the HAL is null.
-         */
-        public boolean isUnstartableHalOperation() {
-            if (isHalOperation()) {
-                final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
-                if (client.getFreshDaemon() == null) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            return mClientMonitor + ", State: " + mState;
-        }
-    }
-
-    /**
-     * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
-     * kill the current operation and forcibly start the next.
-     */
-    private static final class CancellationWatchdog implements Runnable {
-        static final int DELAY_MS = 3000;
-
-        final String tag;
-        final Operation operation;
-        CancellationWatchdog(String tag, Operation operation) {
-            this.tag = tag;
-            this.operation = operation;
-        }
-
-        @Override
-        public void run() {
-            if (operation.mState != Operation.STATE_FINISHED) {
-                Slog.e(tag, "[Watchdog Triggered]: " + operation);
-                operation.mClientMonitor.mCallback
-                        .onClientFinished(operation.mClientMonitor, false /* success */);
-            }
-        }
-    }
-
     private static final class CrashState {
         static final int NUM_ENTRIES = 10;
         final String timestamp;
@@ -263,10 +147,9 @@
     private final @SensorType int mSensorType;
     @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
     @NonNull private final IBiometricService mBiometricService;
-    @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
-    @NonNull private final InternalCallback mInternalCallback;
-    @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations;
-    @VisibleForTesting @Nullable Operation mCurrentOperation;
+    @NonNull protected final Handler mHandler;
+    @VisibleForTesting @NonNull final Deque<BiometricSchedulerOperation> mPendingOperations;
+    @VisibleForTesting @Nullable BiometricSchedulerOperation mCurrentOperation;
     @NonNull private final ArrayDeque<CrashState> mCrashStates;
 
     private int mTotalOperationsHandled;
@@ -277,7 +160,7 @@
     // Internal callback, notified when an operation is complete. Notifies the requester
     // that the operation is complete, before performing internal scheduler work (such as
     // starting the next client).
-    public class InternalCallback implements BaseClientMonitor.Callback {
+    private final ClientMonitorCallback mInternalCallback = new ClientMonitorCallback() {
         @Override
         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
             Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -286,16 +169,11 @@
                 mCoexCoordinator.addAuthenticationClient(mSensorType,
                         (AuthenticationClient<?>) clientMonitor);
             }
-
-            if (mCurrentOperation.mClientCallback != null) {
-                mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
-            }
         }
 
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             mHandler.post(() -> {
-                clientMonitor.destroy();
                 if (mCurrentOperation == null) {
                     Slog.e(getTag(), "[Finishing] " + clientMonitor
                             + " but current operation is null, success: " + success
@@ -303,9 +181,9 @@
                     return;
                 }
 
-                if (clientMonitor != mCurrentOperation.mClientMonitor) {
+                if (!mCurrentOperation.isFor(clientMonitor)) {
                     Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
-                            + " current: " + mCurrentOperation.mClientMonitor);
+                            + " current: " + mCurrentOperation);
                     return;
                 }
 
@@ -315,36 +193,33 @@
                             (AuthenticationClient<?>) clientMonitor);
                 }
 
-                mCurrentOperation.mState = Operation.STATE_FINISHED;
-
-                if (mCurrentOperation.mClientCallback != null) {
-                    mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
-                }
-
                 if (mGestureAvailabilityDispatcher != null) {
                     mGestureAvailabilityDispatcher.markSensorActive(
-                            mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
+                            mCurrentOperation.getSensorId(), false /* active */);
                 }
 
                 if (mRecentOperations.size() >= mRecentOperationsLimit) {
                     mRecentOperations.remove(0);
                 }
-                mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
+                mRecentOperations.add(mCurrentOperation.getProtoEnum());
                 mCurrentOperation = null;
                 mTotalOperationsHandled++;
                 startNextOperationIfIdle();
             });
         }
-    }
+    };
 
     @VisibleForTesting
-    BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+    BiometricScheduler(@NonNull String tag,
+            @NonNull Handler handler,
+            @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
-            @NonNull IBiometricService biometricService, int recentOperationsLimit,
+            @NonNull IBiometricService biometricService,
+            int recentOperationsLimit,
             @NonNull CoexCoordinator coexCoordinator) {
         mBiometricTag = tag;
+        mHandler = handler;
         mSensorType = sensorType;
-        mInternalCallback = new InternalCallback();
         mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
         mPendingOperations = new ArrayDeque<>();
         mBiometricService = biometricService;
@@ -356,6 +231,7 @@
 
     /**
      * 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
@@ -364,16 +240,14 @@
     public BiometricScheduler(@NonNull String tag,
             @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-        this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
-                CoexCoordinator.getInstance());
+        this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+                IBiometricService.Stub.asInterface(
+                        ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+                LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance());
     }
 
-    /**
-     * @return A reference to the internal callback that should be invoked whenever the scheduler
-     *         needs to (e.g. client started, client finished).
-     */
-    @NonNull protected InternalCallback getInternalCallback() {
+    @VisibleForTesting
+    public ClientMonitorCallback getInternalCallback() {
         return mInternalCallback;
     }
 
@@ -392,72 +266,46 @@
         }
 
         mCurrentOperation = mPendingOperations.poll();
-        final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;
         Slog.d(getTag(), "[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.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+        if (mCurrentOperation.isMarkedCanceling()) {
             Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
-            if (!(currentClient instanceof Interruptable)) {
-                throw new IllegalStateException("Mis-implemented client or scheduler, "
-                        + "trying to cancel non-interruptable operation: " + mCurrentOperation);
-            }
-
-            final Interruptable interruptable = (Interruptable) currentClient;
-            interruptable.cancelWithoutStarting(getInternalCallback());
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
             // Now we wait for the client to send its FinishCallback, which kicks off the next
             // operation.
             return;
         }
 
-        if (mGestureAvailabilityDispatcher != null
-                && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
+        if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) {
             mGestureAvailabilityDispatcher.markSensorActive(
-                    mCurrentOperation.mClientMonitor.getSensorId(),
-                    true /* active */);
+                    mCurrentOperation.getSensorId(), true /* active */);
         }
 
         // Not all operations start immediately. BiometricPrompt waits for its operation
         // to arrive at the head of the queue, before pinging it to start.
-        final boolean shouldStartNow = currentClient.getCookie() == 0;
-        if (shouldStartNow) {
-            if (mCurrentOperation.isUnstartableHalOperation()) {
-                final HalClientMonitor<?> halClientMonitor =
-                        (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
+        final int cookie = mCurrentOperation.isReadyToStart();
+        if (cookie == 0) {
+            if (!mCurrentOperation.start(mInternalCallback)) {
                 // Note down current length of queue
                 final int pendingOperationsLength = mPendingOperations.size();
-                final Operation lastOperation = mPendingOperations.peekLast();
+                final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast();
                 Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
                         + ". Last pending operation: " + lastOperation);
 
-                // For current operations, 1) unableToStart, which notifies the caller-side, then
-                // 2) notify operation's callback, to notify applicable system service that the
-                // operation failed.
-                halClientMonitor.unableToStart();
-                if (mCurrentOperation.mClientCallback != null) {
-                    mCurrentOperation.mClientCallback.onClientFinished(
-                            mCurrentOperation.mClientMonitor, false /* success */);
-                }
-
                 // Then for each operation currently in the pending queue at the time of this
                 // failure, do the same as above. Otherwise, it's possible that something like
                 // setActiveUser fails, but then authenticate (for the wrong user) is invoked.
                 for (int i = 0; i < pendingOperationsLength; i++) {
-                    final Operation operation = mPendingOperations.pollFirst();
-                    if (operation == null) {
+                    final BiometricSchedulerOperation operation = mPendingOperations.pollFirst();
+                    if (operation != null) {
+                        Slog.w(getTag(), "[Aborting Operation] " + operation);
+                        operation.abort();
+                    } else {
                         Slog.e(getTag(), "Null operation, index: " + i
                                 + ", expected length: " + pendingOperationsLength);
-                        break;
                     }
-                    if (operation.isHalOperation()) {
-                        ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
-                    }
-                    if (operation.mClientCallback != null) {
-                        operation.mClientCallback.onClientFinished(operation.mClientMonitor,
-                                false /* success */);
-                    }
-                    Slog.w(getTag(), "[Aborted Operation] " + operation);
                 }
 
                 // It's possible that during cleanup a new set of operations came in. We can try to
@@ -465,25 +313,20 @@
                 // actually be multiple operations (i.e. updateActiveUser + authenticate).
                 mCurrentOperation = null;
                 startNextOperationIfIdle();
-            } else {
-                Slog.d(getTag(), "[Starting] " + mCurrentOperation);
-                currentClient.start(getInternalCallback());
-                mCurrentOperation.mState = Operation.STATE_STARTED;
             }
         } else {
             try {
-                mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+                mBiometricService.onReadyForAuthentication(cookie);
             } catch (RemoteException e) {
                 Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
             }
             Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
-            mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
         }
     }
 
     /**
      * Starts the {@link #mCurrentOperation} if
-     * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+     * 1) its state is {@link BiometricSchedulerOperation#STATE_WAITING_FOR_COOKIE} and
      * 2) its cookie matches this cookie
      *
      * This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
@@ -499,45 +342,13 @@
             Slog.e(getTag(), "Current operation is null");
             return;
         }
-        if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) {
-            if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
-                Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
-                        + mCurrentOperation);
-                // This should trigger the internal onClientFinished callback, which clears the
-                // operation and starts the next one.
-                final ErrorConsumer errorConsumer =
-                        (ErrorConsumer) mCurrentOperation.mClientMonitor;
-                errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
-                        0 /* vendorCode */);
-                return;
-            } else {
-                Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
-                        + ", expected STATE_WAITING_FOR_COOKIE");
-                return;
-            }
-        }
-        if (mCurrentOperation.mClientMonitor.getCookie() != cookie) {
-            Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
-                    + ", received: " + cookie);
-            return;
-        }
 
-        if (mCurrentOperation.isUnstartableHalOperation()) {
+        if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) {
+            Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation);
+        } else {
             Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation);
-            // This is BiometricPrompt trying to auth but something's wrong with the HAL.
-            final HalClientMonitor<?> halClientMonitor =
-                    (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
-            halClientMonitor.unableToStart();
-            if (mCurrentOperation.mClientCallback != null) {
-                mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor,
-                        false /* success */);
-            }
             mCurrentOperation = null;
             startNextOperationIfIdle();
-        } else {
-            Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
-            mCurrentOperation.mState = Operation.STATE_STARTED;
-            mCurrentOperation.mClientMonitor.start(getInternalCallback());
         }
     }
 
@@ -557,22 +368,19 @@
      * @param clientCallback optional callback, invoked when the client state changes.
      */
     public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor,
-            @Nullable BaseClientMonitor.Callback clientCallback) {
+            @Nullable ClientMonitorCallback clientCallback) {
         // If the incoming operation should interrupt preceding clients, mark any interruptable
         // pending clients as canceling. Once they reach the head of the queue, the scheduler will
         // send ERROR_CANCELED and skip the operation.
         if (clientMonitor.interruptsPrecedingClients()) {
-            for (Operation operation : mPendingOperations) {
-                if (operation.mClientMonitor instanceof Interruptable
-                        && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
-                    Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
-                            + operation.mClientMonitor);
-                    operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+            for (BiometricSchedulerOperation operation : mPendingOperations) {
+                if (operation.markCanceling()) {
+                    Slog.d(getTag(), "New client, marking pending op as canceling: " + operation);
                 }
             }
         }
 
-        mPendingOperations.add(new Operation(clientMonitor, clientCallback));
+        mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback));
         Slog.d(getTag(), "[Added] " + clientMonitor
                 + ", new queue size: " + mPendingOperations.size());
 
@@ -580,67 +388,34 @@
         // cancellable, start the cancellation process.
         if (clientMonitor.interruptsPrecedingClients()
                 && mCurrentOperation != null
-                && mCurrentOperation.mClientMonitor instanceof Interruptable
-                && mCurrentOperation.mState == Operation.STATE_STARTED) {
+                && mCurrentOperation.isInterruptable()
+                && mCurrentOperation.isStarted()) {
             Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
-            cancelInternal(mCurrentOperation);
-        }
-
-        startNextOperationIfIdle();
-    }
-
-    private void cancelInternal(Operation operation) {
-        if (operation != mCurrentOperation) {
-            Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
-            return;
-        }
-        if (!(operation.mClientMonitor instanceof Interruptable)) {
-            Slog.w(getTag(), "Operation not interruptable: " + operation);
-            return;
-        }
-        if (operation.mState == Operation.STATE_STARTED_CANCELING) {
-            Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
-            return;
-        }
-        if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
-            Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
-            // We can set it to null immediately, since the HAL was never notified to start.
-            if (mCurrentOperation != null) {
-                mCurrentOperation.mClientMonitor.destroy();
-            }
-            mCurrentOperation = null;
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
+        } else {
             startNextOperationIfIdle();
-            return;
         }
-        Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor);
-        final Interruptable interruptable = (Interruptable) operation.mClientMonitor;
-        interruptable.cancel();
-        operation.mState = Operation.STATE_STARTED_CANCELING;
-
-        // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
-        // forcibly finish this client.
-        mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
-                CancellationWatchdog.DELAY_MS);
     }
 
     /**
      * Requests to cancel enrollment.
      * @param token from the caller, should match the token passed in when requesting enrollment
      */
-    public void cancelEnrollment(IBinder token) {
-        if (mCurrentOperation == null) {
-            Slog.e(getTag(), "Unable to cancel enrollment, null operation");
-            return;
-        }
-        final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient;
-        final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
-        if (!isEnrolling || !tokenMatches) {
-            Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
-                    + " tokenMatches: " + tokenMatches);
-            return;
-        }
+    public void cancelEnrollment(IBinder token, long requestId) {
+        Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId);
 
-        cancelInternal(mCurrentOperation);
+        if (mCurrentOperation != null
+                && canCancelEnrollOperation(mCurrentOperation, token, requestId)) {
+            Slog.d(getTag(), "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);
+                    operation.markCanceling();
+                }
+            }
+        }
     }
 
     /**
@@ -649,62 +424,42 @@
      * @param requestId the id returned when requesting authentication
      */
     public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
-        Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
-                + " current: " + mCurrentOperation
-                + " stack size: " + mPendingOperations.size());
+        Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId);
 
         if (mCurrentOperation != null
                 && canCancelAuthOperation(mCurrentOperation, token, requestId)) {
-            Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
-            cancelInternal(mCurrentOperation);
+            Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation);
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
         } else {
-            // Look through the current queue for all authentication clients for the specified
-            // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
-            // all of them, instead of just the first one, since the API surface currently doesn't
-            // allow us to distinguish between multiple authentication requests from the same
-            // process. However, this generally does not happen anyway, and would be a class of
-            // bugs on its own.
-            for (Operation operation : mPendingOperations) {
+            for (BiometricSchedulerOperation operation : mPendingOperations) {
                 if (canCancelAuthOperation(operation, token, requestId)) {
-                    Slog.d(getTag(), "Marking " + operation
-                            + " as STATE_WAITING_IN_QUEUE_CANCELING");
-                    operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+                    Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation);
+                    operation.markCanceling();
                 }
             }
         }
     }
 
-    private static boolean canCancelAuthOperation(Operation operation, IBinder token,
-            long requestId) {
+    private static boolean canCancelEnrollOperation(BiometricSchedulerOperation operation,
+            IBinder token, long requestId) {
+        return operation.isEnrollOperation()
+                && operation.isMatchingToken(token)
+                && operation.isMatchingRequestId(requestId);
+    }
+
+    private static boolean canCancelAuthOperation(BiometricSchedulerOperation operation,
+            IBinder token, long requestId) {
         // TODO: restrict callers that can cancel without requestId (negative value)?
-        return isAuthenticationOrDetectionOperation(operation)
-                && operation.mClientMonitor.getToken() == token
-                && isMatchingRequestId(operation, requestId);
-    }
-
-    // By default, monitors are not associated with a request id to retain the original
-    // behavior (i.e. if no requestId is explicitly set then assume it matches)
-    private static boolean isMatchingRequestId(Operation operation, long requestId) {
-        return !operation.mClientMonitor.hasRequestId()
-                || operation.mClientMonitor.getRequestId() == requestId;
-    }
-
-    private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
-        final boolean isAuthentication =
-                operation.mClientMonitor instanceof AuthenticationConsumer;
-        final boolean isDetection =
-                operation.mClientMonitor instanceof DetectionConsumer;
-        return isAuthentication || isDetection;
+        return operation.isAuthenticationOrDetectionOperation()
+                && operation.isMatchingToken(token)
+                && operation.isMatchingRequestId(requestId);
     }
 
     /**
      * @return the current operation
      */
     public BaseClientMonitor getCurrentClient() {
-        if (mCurrentOperation == null) {
-            return null;
-        }
-        return mCurrentOperation.mClientMonitor;
+        return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null;
     }
 
     public int getCurrentPendingCount() {
@@ -719,7 +474,7 @@
                 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
         final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
         final List<String> pendingOperations = new ArrayList<>();
-        for (Operation operation : mPendingOperations) {
+        for (BiometricSchedulerOperation operation : mPendingOperations) {
             pendingOperations.add(operation.toString());
         }
 
@@ -735,7 +490,7 @@
         pw.println("Type: " + mSensorType);
         pw.println("Current operation: " + mCurrentOperation);
         pw.println("Pending operations: " + mPendingOperations.size());
-        for (Operation operation : mPendingOperations) {
+        for (BiometricSchedulerOperation operation : mPendingOperations) {
             pw.println("Pending operation: " + operation);
         }
         for (CrashState crashState : mCrashStates) {
@@ -746,7 +501,7 @@
     public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
         final ProtoOutputStream proto = new ProtoOutputStream();
         proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
-                ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
+                ? mCurrentOperation.getProtoEnum() : BiometricsProto.CM_NONE);
         proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
 
         if (!mRecentOperations.isEmpty()) {
@@ -771,6 +526,7 @@
      * HAL dies.
      */
     public void reset() {
+        Slog.d(getTag(), "Resetting scheduler");
         mPendingOperations.clear();
         mCurrentOperation = null;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
new file mode 100644
index 0000000..812ca8a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains all the necessary information for a HAL operation.
+ */
+public class BiometricSchedulerOperation {
+    protected static final String TAG = "BiometricSchedulerOperation";
+
+    /**
+     * The operation is added to the list of pending operations and waiting for its turn.
+     */
+    protected static final int STATE_WAITING_IN_QUEUE = 0;
+
+    /**
+     * The operation is added to the list of pending operations, but a subsequent operation
+     * has been added. This state only applies to {@link Interruptable} operations. When this
+     * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+     */
+    protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+    /**
+     * The operation has reached the front of the queue and has started.
+     */
+    protected static final int STATE_STARTED = 2;
+
+    /**
+     * The operation was started, but is now canceling. Operations should wait for the HAL to
+     * acknowledge that the operation was canceled, at which point it finishes.
+     */
+    protected static final int STATE_STARTED_CANCELING = 3;
+
+    /**
+     * The operation has reached the head of the queue but is waiting for BiometricService
+     * to acknowledge and start the operation.
+     */
+    protected static final int STATE_WAITING_FOR_COOKIE = 4;
+
+    /**
+     * The {@link ClientMonitorCallback} has been invoked and the client is finished.
+     */
+    protected static final int STATE_FINISHED = 5;
+
+    @IntDef({STATE_WAITING_IN_QUEUE,
+            STATE_WAITING_IN_QUEUE_CANCELING,
+            STATE_STARTED,
+            STATE_STARTED_CANCELING,
+            STATE_WAITING_FOR_COOKIE,
+            STATE_FINISHED})
+    @Retention(RetentionPolicy.SOURCE)
+    protected @interface OperationState {}
+
+    private static final int CANCEL_WATCHDOG_DELAY_MS = 3000;
+
+    @NonNull
+    private final BaseClientMonitor mClientMonitor;
+    @Nullable
+    private final ClientMonitorCallback mClientCallback;
+    @OperationState
+    private int mState;
+    @VisibleForTesting
+    @NonNull
+    final Runnable mCancelWatchdog;
+
+    BiometricSchedulerOperation(
+            @NonNull BaseClientMonitor clientMonitor,
+            @Nullable ClientMonitorCallback callback
+    ) {
+        this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
+    }
+
+    protected BiometricSchedulerOperation(
+            @NonNull BaseClientMonitor clientMonitor,
+            @Nullable ClientMonitorCallback callback,
+            @OperationState int state
+    ) {
+        mClientMonitor = clientMonitor;
+        mClientCallback = callback;
+        mState = state;
+        mCancelWatchdog = () -> {
+            if (!isFinished()) {
+                Slog.e(TAG, "[Watchdog Triggered]: " + this);
+                getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+            }
+        };
+    }
+
+    /**
+     * Zero if this operation is ready to start or has already started. A non-zero cookie
+     * is returned if the operation has not started and is waiting on
+     * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}.
+     *
+     * @return cookie or 0 if ready/started
+     */
+    public int isReadyToStart() {
+        if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) {
+            final int cookie = mClientMonitor.getCookie();
+            if (cookie != 0) {
+                mState = STATE_WAITING_FOR_COOKIE;
+            }
+            return cookie;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Start this operation without waiting for a cookie
+     * (i.e. {@link #isReadyToStart() returns zero}
+     *
+     * @param callback lifecycle callback
+     * @return if this operation started
+     */
+    public boolean start(@NonNull ClientMonitorCallback callback) {
+        checkInState("start",
+                STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_WAITING_IN_QUEUE_CANCELING);
+
+        if (mClientMonitor.getCookie() != 0) {
+            throw new IllegalStateException("operation requires cookie");
+        }
+
+        return doStart(callback);
+    }
+
+    /**
+     * Start this operation after receiving the given cookie.
+     *
+     * @param callback lifecycle callback
+     * @param cookie   cookie indicting the operation should begin
+     * @return if this operation started
+     */
+    public boolean startWithCookie(@NonNull ClientMonitorCallback callback, int cookie) {
+        checkInState("start",
+                STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_WAITING_IN_QUEUE_CANCELING);
+
+        if (mClientMonitor.getCookie() != cookie) {
+            Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie);
+            return false;
+        }
+
+        return doStart(callback);
+    }
+
+    private boolean doStart(@NonNull ClientMonitorCallback callback) {
+        final ClientMonitorCallback cb = getWrappedCallback(callback);
+
+        if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
+            Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
+
+            cb.onClientFinished(mClientMonitor, true /* success */);
+            if (mClientMonitor instanceof ErrorConsumer) {
+                final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor;
+                errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                        0 /* vendorCode */);
+            } else {
+                Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer");
+            }
+
+            return false;
+        }
+
+        if (isUnstartableHalOperation()) {
+            Slog.v(TAG, "unable to start: " + this);
+            ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+            cb.onClientFinished(mClientMonitor, false /* success */);
+            return false;
+        }
+
+        mState = STATE_STARTED;
+        mClientMonitor.start(cb);
+
+        Slog.v(TAG, "started: " + this);
+        return true;
+    }
+
+    /**
+     * Abort a pending operation.
+     *
+     * This is similar to cancel but the operation must not have been started. It will
+     * immediately abort the operation and notify the client that it has finished unsuccessfully.
+     */
+    public void abort() {
+        checkInState("cannot abort a non-pending operation",
+                STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_WAITING_IN_QUEUE_CANCELING);
+
+        if (isHalOperation()) {
+            ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+        }
+        getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+
+        Slog.v(TAG, "Aborted: " + this);
+    }
+
+    /** Flags this operation as canceled, if possible, but does not cancel it until started. */
+    public boolean markCanceling() {
+        if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) {
+            mState = STATE_WAITING_IN_QUEUE_CANCELING;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Cancel the operation now.
+     *
+     * @param handler handler to use for the cancellation watchdog
+     * @param callback lifecycle callback (only used if this operation hasn't started, otherwise
+     *                 the callback used from {@link #start(ClientMonitorCallback)} is used)
+     */
+    public void cancel(@NonNull Handler handler, @NonNull ClientMonitorCallback callback) {
+        checkNotInState("cancel", STATE_FINISHED);
+
+        final int currentState = mState;
+        if (!isInterruptable()) {
+            Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this);
+            return;
+        }
+        if (currentState == STATE_STARTED_CANCELING) {
+            Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this);
+            return;
+        }
+
+        mState = STATE_STARTED_CANCELING;
+        if (currentState == STATE_WAITING_IN_QUEUE
+                || currentState == STATE_WAITING_IN_QUEUE_CANCELING
+                || currentState == STATE_WAITING_FOR_COOKIE) {
+            Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor);
+            ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback));
+        } else {
+            Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor);
+            ((Interruptable) mClientMonitor).cancel();
+        }
+
+        // forcibly finish this client if the HAL does not acknowledge within the timeout
+        handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS);
+    }
+
+    @NonNull
+    private ClientMonitorCallback getWrappedCallback() {
+        return getWrappedCallback(null);
+    }
+
+    @NonNull
+    private ClientMonitorCallback getWrappedCallback(
+            @Nullable ClientMonitorCallback callback) {
+        final ClientMonitorCallback destroyCallback = new ClientMonitorCallback() {
+            @Override
+            public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+                    boolean success) {
+                Slog.d(TAG, "[Finished / destroy]: " + clientMonitor);
+                mClientMonitor.destroy();
+                mState = STATE_FINISHED;
+            }
+        };
+        return new ClientMonitorCompositeCallback(destroyCallback, callback, mClientCallback);
+    }
+
+    /** {@link BaseClientMonitor#getSensorId()}. */
+    public int getSensorId() {
+        return mClientMonitor.getSensorId();
+    }
+
+    /** {@link BaseClientMonitor#getProtoEnum()}. */
+    public int getProtoEnum() {
+        return mClientMonitor.getProtoEnum();
+    }
+
+    /** {@link BaseClientMonitor#getTargetUserId()}. */
+    public int getTargetUserId() {
+        return mClientMonitor.getTargetUserId();
+    }
+
+    /** If the given clientMonitor is the same as the one in the constructor. */
+    public boolean isFor(@NonNull BaseClientMonitor clientMonitor) {
+        return mClientMonitor == clientMonitor;
+    }
+
+    /** If this operation is {@link Interruptable}. */
+    public boolean isInterruptable() {
+        return mClientMonitor instanceof Interruptable;
+    }
+
+    private boolean isHalOperation() {
+        return mClientMonitor instanceof HalClientMonitor<?>;
+    }
+
+    private boolean isUnstartableHalOperation() {
+        if (isHalOperation()) {
+            final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
+            if (client.getFreshDaemon() == null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** If this operation is an enrollment. */
+    public boolean isEnrollOperation() {
+        return mClientMonitor instanceof EnrollClient;
+    }
+
+    /** If this operation is authentication. */
+    public boolean isAuthenticateOperation() {
+        return mClientMonitor instanceof AuthenticationClient;
+    }
+
+    /** If this operation is authentication or detection. */
+    public boolean isAuthenticationOrDetectionOperation() {
+        final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer;
+        final boolean isDetection = mClientMonitor instanceof DetectionConsumer;
+        return isAuthentication || isDetection;
+    }
+
+    /** If this operation performs acquisition {@link AcquisitionClient}. */
+    public boolean isAcquisitionOperation() {
+        return mClientMonitor instanceof AcquisitionClient;
+    }
+
+    /**
+     * If this operation matches the original requestId.
+     *
+     * By default, monitors are not associated with a request id to retain the original
+     * behavior (i.e. if no requestId is explicitly set then assume it matches)
+     *
+     * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}.
+     */
+    public boolean isMatchingRequestId(long requestId) {
+        return !mClientMonitor.hasRequestId()
+                || mClientMonitor.getRequestId() == requestId;
+    }
+
+    /** If the token matches */
+    public boolean isMatchingToken(@Nullable IBinder token) {
+        return mClientMonitor.getToken() == token;
+    }
+
+    /** If this operation has started. */
+    public boolean isStarted() {
+        return mState == STATE_STARTED;
+    }
+
+    /** If this operation is cancelling but has not yet completed. */
+    public boolean isCanceling() {
+        return mState == STATE_STARTED_CANCELING;
+    }
+
+    /** If this operation has finished and completed its lifecycle. */
+    public boolean isFinished() {
+        return mState == STATE_FINISHED;
+    }
+
+    /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */
+    public boolean isMarkedCanceling() {
+        return mState == STATE_WAITING_IN_QUEUE_CANCELING;
+    }
+
+    /**
+     * The monitor passed to the constructor.
+     * @deprecated avoid using and move to encapsulate within the operation
+     */
+    @Deprecated
+    public BaseClientMonitor getClientMonitor() {
+        return mClientMonitor;
+    }
+
+    private void checkNotInState(String message, @OperationState int... states) {
+        for (int state : states) {
+            if (mState == state) {
+                throw new IllegalStateException(message + ": illegal state= " + state);
+            }
+        }
+    }
+
+    private void checkInState(String message, @OperationState int... states) {
+        for (int state : states) {
+            if (mState == state) {
+                return;
+            }
+        }
+        throw new IllegalStateException(message + ": illegal state= " + mState);
+    }
+
+    @Override
+    public String toString() {
+        return mClientMonitor + ", State: " + mState;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
new file mode 100644
index 0000000..8ea4ee9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface that ClientMonitor holders should use to receive callbacks.
+ */
+public interface ClientMonitorCallback {
+    /**
+     * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
+     * the queue and becomes the current operation).
+     *
+     * @param clientMonitor Reference of the ClientMonitor that is starting.
+     */
+    default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
+
+    /**
+     * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
+     * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
+     * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
+     * implementation.
+     *
+     * @param clientMonitor Reference of the ClientMonitor that finished.
+     * @param success       True if the operation completed successfully.
+     */
+    default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {}
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
new file mode 100644
index 0000000..b82f5fa
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
@@ -0,0 +1,53 @@
+/*
+ * 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.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Holder for wrapping multiple handlers into a single Callback. */
+public class ClientMonitorCompositeCallback implements ClientMonitorCallback {
+    @NonNull
+    private final List<ClientMonitorCallback> mCallbacks;
+
+    public ClientMonitorCompositeCallback(@NonNull ClientMonitorCallback... callbacks) {
+        mCallbacks = new ArrayList<>();
+
+        for (ClientMonitorCallback callback : callbacks) {
+            if (callback != null) {
+                mCallbacks.add(callback);
+            }
+        }
+    }
+
+    @Override
+    public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onClientStarted(clientMonitor);
+        }
+    }
+
+    @Override
+    public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+            boolean success) {
+        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+            mCallbacks.get(i).onClientFinished(clientMonitor, success);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 2826e0c..3b7adc1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -89,7 +89,8 @@
 
         if (remaining == 0) {
             mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
-            logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+            getLogger().logOnEnrolled(getTargetUserId(),
+                    System.currentTimeMillis() - mEnrollmentStartTimeMs,
                     true /* enrollSuccessful */);
             mCallback.onClientFinished(this, true /* success */);
         }
@@ -97,7 +98,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (hasReachedEnrollmentLimit()) {
@@ -116,7 +117,8 @@
      */
     @Override
     public void onError(int error, int vendorCode) {
-        logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+        getLogger().logOnEnrolled(getTargetUserId(),
+                System.currentTimeMillis() - mEnrollmentStartTimeMs,
                 false /* enrollSuccessful */);
         super.onError(error, vendorCode);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
index c2f909b..3060f30 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
@@ -23,7 +23,7 @@
 
     /**
      * Callers should typically check this after
-     * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)}
+     * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)}
      *
      * @return true if the user has gone from:
      *      1) none-enrolled --> enrolled
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 3d74f36..6fb6d08 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -47,7 +47,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 63cd412..c8830f8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -45,7 +45,7 @@
     /**
      * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
      * If such a problem is detected, the scheduler will not invoke
-     * {@link #start(Callback)}.
+     * {@link #start(ClientMonitorCallback)}.
      */
     public abstract void unableToStart();
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 579dfd6..0636893 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -23,7 +23,6 @@
 import android.os.IBinder;
 import android.util.Slog;
 
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.BiometricsProto;
 
 import java.util.ArrayList;
@@ -65,7 +64,7 @@
     private final boolean mHasEnrollmentsBeforeStarting;
     private BaseClientMonitor mCurrentTask;
 
-    private final Callback mEnumerateCallback = new Callback() {
+    private final ClientMonitorCallback mEnumerateCallback = new ClientMonitorCallback() {
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
@@ -91,7 +90,7 @@
         }
     };
 
-    private final Callback mRemoveCallback = new Callback() {
+    private final ClientMonitorCallback mRemoveCallback = new ClientMonitorCallback() {
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
@@ -128,10 +127,9 @@
         mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
                 template.mIdentifier.getBiometricId(), template.mUserId,
                 getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
-        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                mStatsModality,
-                BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
-                -1 /* sensorId */);
+
+        getLogger().logUnknownEnrollmentInHal();
+
         mCurrentTask.start(mRemoveCallback);
     }
 
@@ -141,7 +139,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         // Start enumeration. Removal will start if necessary, when enumeration is completed.
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 7f6903a..05ea19a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -23,7 +23,6 @@
 import android.os.IBinder;
 import android.util.Slog;
 
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.BiometricsProto;
 
 import java.util.ArrayList;
@@ -73,7 +72,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         // The biometric template ids will be removed when we get confirmation from the HAL
@@ -116,10 +115,8 @@
                     + identifier.getBiometricId() + " " + identifier.getName());
             mUtils.removeBiometricForUser(getContext(),
                     getTargetUserId(), identifier.getBiometricId());
-            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                    mStatsModality,
-                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
-                    -1 /* sensorId */);
+
+            getLogger().logUnknownEnrollmentInFramework();
         }
         mEnrolledList.clear();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index fab98b6..4f645ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -29,10 +29,15 @@
 
     /**
      * Notifies the client that it needs to finish before
-     * {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
+     * {@link BaseClientMonitor#start(ClientMonitorCallback)} was invoked. This usually happens
      * if the client is still waiting in the pending queue and got notified that a subsequent
      * operation is preempting it.
+     *
+     * This method must invoke
+     * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)} on the
+     * given callback (with success).
+     *
      * @param callback invoked when the operation is completed.
      */
-    void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
+    void cancelWithoutStarting(@NonNull ClientMonitorCallback callback);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index cede4a7..ee6bb0f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -62,7 +62,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index 5ba1b00..b2661a2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -84,7 +84,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), true /* inProgress */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
index f4997d4..92218b1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
@@ -25,7 +25,11 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * Allows clients (such as keyguard) to register for notifications on when biometric lockout
@@ -37,7 +41,8 @@
     private static final String TAG = "LockoutResetTracker";
 
     private final Context mContext;
-    private final ArrayList<ClientCallback> mClientCallbacks;
+    @VisibleForTesting
+    final List<ClientCallback> mClientCallbacks = new ArrayList<>();
 
     private static class ClientCallback {
         private static final long WAKELOCK_TIMEOUT_MS = 2000;
@@ -81,7 +86,6 @@
 
     public LockoutResetDispatcher(Context context) {
         mContext = context;
-        mClientCallbacks = new ArrayList<>();
     }
 
     public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) {
@@ -106,11 +110,13 @@
     @Override
     public void binderDied(IBinder who) {
         Slog.e(TAG, "Callback binder died: " + who);
-        for (ClientCallback callback : mClientCallbacks) {
+        final Iterator<ClientCallback> iterator = mClientCallbacks.iterator();
+        while (iterator.hasNext()) {
+            final ClientCallback callback = iterator.next();
             if (callback.mCallback.asBinder().equals(who)) {
                 Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName);
                 callback.releaseWakelock();
-                mClientCallbacks.remove(callback);
+                iterator.remove();
             }
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
deleted file mode 100644
index b4c82f2..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.util.Slog;
-
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.Utils;
-
-/**
- * Abstract class that adds logging functionality to the ClientMonitor classes.
- */
-public abstract class LoggableMonitor {
-
-    public static final String TAG = "Biometrics/LoggableMonitor";
-    public static final boolean DEBUG = false;
-
-    final int mStatsModality;
-    private final int mStatsAction;
-    private final int mStatsClient;
-    @NonNull private final SensorManager mSensorManager;
-    private long mFirstAcquireTimeMs;
-    private boolean mLightSensorEnabled = false;
-    private boolean mShouldLogMetrics = true;
-
-    /**
-     * Probe for loggable attributes that can be continuously monitored, such as ambient light.
-     *
-     * Disable probes when the sensors are in states that are not interesting for monitoring
-     * purposes to save power.
-     */
-    protected interface Probe {
-        /** Ensure the probe is actively sampling for new data. */
-        void enable();
-        /** Stop sampling data. */
-        void disable();
-    }
-
-    /**
-     * Client monitor callback that exposes a probe.
-     *
-     * Disables the probe when the operation completes.
-     */
-    protected static class CallbackWithProbe<T extends Probe>
-            implements BaseClientMonitor.Callback {
-        private final boolean mStartWithClient;
-        private final T mProbe;
-
-        public CallbackWithProbe(@NonNull T probe, boolean startWithClient) {
-            mProbe = probe;
-            mStartWithClient = startWithClient;
-        }
-
-        @Override
-        public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-            if (mStartWithClient) {
-                mProbe.enable();
-            }
-        }
-
-        @Override
-        public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
-            mProbe.disable();
-        }
-
-        @NonNull
-        public T getProbe() {
-            return mProbe;
-        }
-    }
-
-    private class ALSProbe implements Probe {
-        @Override
-        public void enable() {
-            setLightSensorLoggingEnabled(getAmbientLightSensor(mSensorManager));
-        }
-
-        @Override
-        public void disable() {
-            setLightSensorLoggingEnabled(null);
-        }
-    }
-
-    // report only the most recent value
-    // consider com.android.server.display.utils.AmbientFilter or similar if need arises
-    private volatile float mLastAmbientLux = 0;
-
-    private final SensorEventListener mLightSensorListener = new SensorEventListener() {
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            mLastAmbientLux = event.values[0];
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            // Not used.
-        }
-    };
-
-    /**
-     * @param context system_server context
-     * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
-     * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
-     * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
-     */
-    public LoggableMonitor(@NonNull Context context, int statsModality, int statsAction,
-            int statsClient) {
-        mStatsModality = statsModality;
-        mStatsAction = statsAction;
-        mStatsClient = statsClient;
-        mSensorManager = context.getSystemService(SensorManager.class);
-    }
-
-    /**
-     * Only valid for AuthenticationClient.
-     * @return true if the client is authenticating for a crypto operation.
-     */
-    protected boolean isCryptoOperation() {
-        return false;
-    }
-
-    protected void setShouldLog(boolean shouldLog) {
-        mShouldLogMetrics = shouldLog;
-    }
-
-    public int getStatsClient() {
-        return mStatsClient;
-    }
-
-    private boolean shouldSkipLogging() {
-        boolean shouldSkipLogging = (mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
-                || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN);
-
-        if (mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN) {
-            Slog.w(TAG, "Unknown field detected: MODALITY_UNKNOWN, will not report metric");
-        }
-
-        if (mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN) {
-            Slog.w(TAG, "Unknown field detected: ACTION_UNKNOWN, will not report metric");
-        }
-
-        if (mStatsClient == BiometricsProtoEnums.CLIENT_UNKNOWN) {
-            Slog.w(TAG, "Unknown field detected: CLIENT_UNKNOWN");
-        }
-
-        return shouldSkipLogging;
-    }
-
-    protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
-            int targetUserId) {
-        if (!mShouldLogMetrics) {
-            return;
-        }
-
-        final boolean isFace = mStatsModality == BiometricsProtoEnums.MODALITY_FACE;
-        final boolean isFingerprint = mStatsModality == BiometricsProtoEnums.MODALITY_FINGERPRINT;
-        if (isFace || isFingerprint) {
-            if ((isFingerprint && acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_START)
-                    || (isFace && acquiredInfo == FaceManager.FACE_ACQUIRED_START)) {
-                mFirstAcquireTimeMs = System.currentTimeMillis();
-            }
-        } else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
-            if (mFirstAcquireTimeMs == 0) {
-                mFirstAcquireTimeMs = System.currentTimeMillis();
-            }
-        }
-        if (DEBUG) {
-            Slog.v(TAG, "Acquired! Modality: " + mStatsModality
-                    + ", User: " + targetUserId
-                    + ", IsCrypto: " + isCryptoOperation()
-                    + ", Action: " + mStatsAction
-                    + ", Client: " + mStatsClient
-                    + ", AcquiredInfo: " + acquiredInfo
-                    + ", VendorCode: " + vendorCode);
-        }
-
-        if (shouldSkipLogging()) {
-            return;
-        }
-
-        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
-                mStatsModality,
-                targetUserId,
-                isCryptoOperation(),
-                mStatsAction,
-                mStatsClient,
-                acquiredInfo,
-                vendorCode,
-                Utils.isDebugEnabled(context, targetUserId),
-                -1 /* sensorId */);
-    }
-
-    protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) {
-        if (!mShouldLogMetrics) {
-            return;
-        }
-
-        final long latency = mFirstAcquireTimeMs != 0
-                ? (System.currentTimeMillis() - mFirstAcquireTimeMs) : -1;
-
-        if (DEBUG) {
-            Slog.v(TAG, "Error! Modality: " + mStatsModality
-                    + ", User: " + targetUserId
-                    + ", IsCrypto: " + isCryptoOperation()
-                    + ", Action: " + mStatsAction
-                    + ", Client: " + mStatsClient
-                    + ", Error: " + error
-                    + ", VendorCode: " + vendorCode
-                    + ", Latency: " + latency);
-        } else {
-            Slog.v(TAG, "Error latency: " + latency);
-        }
-
-        if (shouldSkipLogging()) {
-            return;
-        }
-
-        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
-                mStatsModality,
-                targetUserId,
-                isCryptoOperation(),
-                mStatsAction,
-                mStatsClient,
-                error,
-                vendorCode,
-                Utils.isDebugEnabled(context, targetUserId),
-                sanitizeLatency(latency),
-                -1 /* sensorId */);
-    }
-
-    protected final void logOnAuthenticated(Context context, boolean authenticated,
-            boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) {
-        if (!mShouldLogMetrics) {
-            return;
-        }
-
-        int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
-        if (!authenticated) {
-            authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
-        } else {
-            // Authenticated
-            if (isBiometricPrompt && requireConfirmation) {
-                authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
-            } else {
-                authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
-            }
-        }
-
-        // Only valid if we have a first acquired time, otherwise set to -1
-        final long latency = mFirstAcquireTimeMs != 0
-                ? (System.currentTimeMillis() - mFirstAcquireTimeMs)
-                : -1;
-
-        if (DEBUG) {
-            Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
-                    + ", User: " + targetUserId
-                    + ", IsCrypto: " + isCryptoOperation()
-                    + ", Client: " + mStatsClient
-                    + ", RequireConfirmation: " + requireConfirmation
-                    + ", State: " + authState
-                    + ", Latency: " + latency
-                    + ", Lux: " + mLastAmbientLux);
-        } else {
-            Slog.v(TAG, "Authentication latency: " + latency);
-        }
-
-        if (shouldSkipLogging()) {
-            return;
-        }
-
-        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
-                mStatsModality,
-                targetUserId,
-                isCryptoOperation(),
-                mStatsClient,
-                requireConfirmation,
-                authState,
-                sanitizeLatency(latency),
-                Utils.isDebugEnabled(context, targetUserId),
-                -1 /* sensorId */,
-                mLastAmbientLux /* ambientLightLux */);
-    }
-
-    protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
-        if (!mShouldLogMetrics) {
-            return;
-        }
-
-        if (DEBUG) {
-            Slog.v(TAG, "Enrolled! Modality: " + mStatsModality
-                    + ", User: " + targetUserId
-                    + ", Client: " + mStatsClient
-                    + ", Latency: " + latency
-                    + ", Lux: " + mLastAmbientLux
-                    + ", Success: " + enrollSuccessful);
-        } else {
-            Slog.v(TAG, "Enroll latency: " + latency);
-        }
-
-        if (shouldSkipLogging()) {
-            return;
-        }
-
-        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
-                mStatsModality,
-                targetUserId,
-                sanitizeLatency(latency),
-                enrollSuccessful,
-                -1, /* sensorId */
-                mLastAmbientLux /* ambientLightLux */);
-    }
-
-    private long sanitizeLatency(long latency) {
-        if (latency < 0) {
-            Slog.w(TAG, "found a negative latency : " + latency);
-            return -1;
-        }
-        return latency;
-    }
-
-    /**
-     * Get a callback to start/stop ALS capture when client runs.
-     *
-     * If the probe should not run for the entire operation, do not set startWithClient and
-     * start/stop the problem when needed.
-     *
-     * @param startWithClient if probe should start automatically when the operation starts.
-     */
-    @NonNull
-    protected CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
-        return new CallbackWithProbe<>(new ALSProbe(), startWithClient);
-    }
-
-    /** The sensor to use for ALS logging. */
-    @Nullable
-    protected Sensor getAmbientLightSensor(@NonNull SensorManager sensorManager) {
-        return mShouldLogMetrics ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null;
-    }
-
-    private void setLightSensorLoggingEnabled(@Nullable Sensor lightSensor) {
-        if (DEBUG) {
-            Slog.v(TAG, "capturing ambient light using: "
-                    + (lightSensor != null ? lightSensor : "[disabled]"));
-        }
-
-        if (lightSensor != null) {
-            if (!mLightSensorEnabled) {
-                mLightSensorEnabled = true;
-                mLastAmbientLux = 0;
-                mSensorManager.registerListener(mLightSensorListener, lightSensor,
-                        SensorManager.SENSOR_DELAY_NORMAL);
-            }
-        } else {
-            mLightSensorEnabled = false;
-            mLastAmbientLux = 0;
-            mSensorManager.unregisterListener(mLightSensorListener);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 2a6677e..e79819b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -17,7 +17,6 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
@@ -59,7 +58,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         // The biometric template ids will be removed when we get confirmation from the HAL
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 1edf5af..21a6ddf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -38,7 +38,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
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 b056bf8..4f90020 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -16,10 +16,14 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -52,7 +56,7 @@
     @NonNull private final UserSwitchCallback mUserSwitchCallback;
     @Nullable private StopUserClient<?> mStopUserClient;
 
-    private class ClientFinishedCallback implements BaseClientMonitor.Callback {
+    private class ClientFinishedCallback implements ClientMonitorCallback {
         private final BaseClientMonitor mOwner;
 
         ClientFinishedCallback(BaseClientMonitor owner) {
@@ -68,9 +72,8 @@
                     return;
                 }
 
-                Slog.d(getTag(), "[Client finished] "
-                        + clientMonitor + ", success: " + success);
-                if (mCurrentOperation != null && mCurrentOperation.mClientMonitor == mOwner) {
+                Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
+                if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
                     mCurrentOperation = null;
                     startNextOperationIfIdle();
                 } else {
@@ -83,26 +86,30 @@
     }
 
     @VisibleForTesting
-    UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+    public UserAwareBiometricScheduler(@NonNull String tag,
+            @NonNull Handler handler,
+            @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull IBiometricService biometricService,
             @NonNull CurrentUserRetriever currentUserRetriever,
             @NonNull UserSwitchCallback userSwitchCallback,
             @NonNull CoexCoordinator coexCoordinator) {
-        super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
+        super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
                 LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
 
         mCurrentUserRetriever = currentUserRetriever;
         mUserSwitchCallback = userSwitchCallback;
     }
 
-    public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+    public UserAwareBiometricScheduler(@NonNull String tag,
+            @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull CurrentUserRetriever currentUserRetriever,
             @NonNull UserSwitchCallback userSwitchCallback) {
-        this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
-                userSwitchCallback, CoexCoordinator.getInstance());
+        this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+                IBiometricService.Stub.asInterface(
+                        ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+                currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance());
     }
 
     @Override
@@ -122,7 +129,7 @@
         }
 
         final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
-        final int nextUserId = mPendingOperations.getFirst().mClientMonitor.getTargetUserId();
+        final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
 
         if (nextUserId == currentUserId) {
             super.startNextOperationIfIdle();
@@ -133,8 +140,8 @@
                     new ClientFinishedCallback(startClient);
 
             Slog.d(getTag(), "[Starting User] " + startClient);
-            mCurrentOperation = new Operation(
-                    startClient, finishedCallback, Operation.STATE_STARTED);
+            mCurrentOperation = new BiometricSchedulerOperation(
+                    startClient, finishedCallback, STATE_STARTED);
             startClient.start(finishedCallback);
         } else {
             if (mStopUserClient != null) {
@@ -147,8 +154,8 @@
 
                 Slog.d(getTag(), "[Stopping User] current: " + currentUserId
                         + ", next: " + nextUserId + ". " + mStopUserClient);
-                mCurrentOperation = new Operation(
-                        mStopUserClient, finishedCallback, Operation.STATE_STARTED);
+                mCurrentOperation = new BiometricSchedulerOperation(
+                        mStopUserClient, finishedCallback, STATE_STARTED);
                 mStopUserClient.start(finishedCallback);
             }
         }
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 675ee545..039b08e 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
@@ -213,7 +213,7 @@
         }
 
         @Override // Binder call
-        public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
+        public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -221,23 +221,24 @@
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for enroll");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+            return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
                     receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
         }
 
         @Override // Binder call
-        public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
+        public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 final int[] disabledFeatures) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
             // TODO(b/145027036): Implement this.
+            return -1;
         }
 
         @Override // Binder call
-        public void cancelEnrollment(final IBinder token) {
+        public void cancelEnrollment(final IBinder token, long requestId) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -246,7 +247,7 @@
                 return;
             }
 
-            provider.second.cancelEnrollment(provider.first, token);
+            provider.second.cancelEnrollment(provider.first, token, requestId);
         }
 
         @Override // Binder call
@@ -624,7 +625,7 @@
         private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
             for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
                 mServiceProviders.add(
-                        new Face10(getContext(), hidlSensor, mLockoutResetDispatcher));
+                        Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
             }
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index e099ba3..1e9b72b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -29,7 +29,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
 
@@ -94,12 +94,12 @@
     void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull String opPackageName, long challenge);
 
-    void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+    long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
             int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
             @NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
             boolean debugConsent);
 
-    void cancelEnrollment(int sensorId, @NonNull IBinder token);
+    void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
 
     long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
@@ -137,7 +137,7 @@
     void startPreparedClient(int sensorId, int cookie);
 
     void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback);
+            @Nullable ClientMonitorCallback callback);
 
     void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
             boolean clearSchedulerBuffer);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 66b942b..8998269 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -35,6 +35,7 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.util.HashSet;
@@ -221,7 +222,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         Slog.d(TAG, "cleanupInternalState: " + userId);
-        mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 4131ae1..dc21a04f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -39,7 +39,9 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutConsumer;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -97,15 +99,16 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         mState = STATE_STARTED;
     }
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
+                getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
     @Override
@@ -241,7 +244,8 @@
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
         // Lockout metrics are logged as an error code.
         final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
-        logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+        getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+                isCryptoOperation(), getTargetUserId());
 
         try {
             getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -256,7 +260,8 @@
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
         // Lockout metrics are logged as an error code.
         final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
-        logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+        getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+                isCryptoOperation(), getTargetUserId());
 
         try {
             getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 2158dfe..72a20db07 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -30,6 +30,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.DetectionConsumer;
 
@@ -58,7 +59,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index a806277..5c57dbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -41,7 +41,9 @@
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.face.FaceService;
 import com.android.server.biometrics.sensors.face.FaceUtils;
@@ -67,8 +69,8 @@
     private final int mMaxTemplatesPerUser;
     private final boolean mDebugConsent;
 
-    private final BaseClientMonitor.Callback mPreviewHandleDeleterCallback =
-            new BaseClientMonitor.Callback() {
+    private final ClientMonitorCallback mPreviewHandleDeleterCallback =
+            new ClientMonitorCallback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 }
@@ -82,13 +84,14 @@
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
+            @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
             @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
             @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
             boolean debugConsent) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
                 false /* shouldVibrate */);
+        setRequestId(requestId);
         mEnrollIgnoreList = getContext().getResources()
                 .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
         mEnrollIgnoreListVendor = getContext().getResources()
@@ -100,7 +103,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         BiometricNotificationUtils.cancelReEnrollNotification(getContext());
@@ -108,9 +111,9 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(mPreviewHandleDeleterCallback,
-                createALSCallback(true /* startWithClient */), callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(mPreviewHandleDeleterCallback,
+                getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index af826c2..584b58c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.Map;
@@ -48,7 +49,7 @@
         // Nothing to do here
     }
 
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index 315ede8b..acf5720 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -29,6 +29,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -60,7 +61,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 4bae775..9d7a552 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
@@ -49,6 +49,7 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.InvalidationRequesterClient;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -217,7 +218,7 @@
     }
 
     private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
-            BaseClientMonitor.Callback callback) {
+            ClientMonitorCallback callback) {
         if (!mSensors.contains(sensorId)) {
             throw new IllegalStateException("Unable to schedule client: " + client
                     + " for sensor: " + sensorId);
@@ -327,20 +328,21 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
             @NonNull String opPackageName, @NonNull int[] disabledFeatures,
             @Nullable Surface previewSurface, boolean debugConsent) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             final int maxTemplatesPerUser = mSensors.get(
                     sensorId).getSensorProperties().maxEnrollmentsPerUser;
             final FaceEnrollClient client = new FaceEnrollClient(mContext,
                     mSensors.get(sensorId).getLazySession(), token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
-                    opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
+                    opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
                     ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
                     debugConsent);
-            scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
+            scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
@@ -351,11 +353,13 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() ->
+                mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
     }
 
     @Override
@@ -508,7 +512,7 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             final List<Face> enrolledList = getEnrolledFaces(sensorId, userId);
             final FaceInternalCleanupClient client =
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 1e1b532..fd44c5c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -27,6 +27,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutCache;
@@ -64,7 +65,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 4515d04..ee6982a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -28,6 +28,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -65,7 +66,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
index 2b5f495..4a3da0d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StartUserClient;
 
 public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
@@ -43,7 +44,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 06328e3..88b9235 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
@@ -24,6 +24,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StopUserClient;
 
 public class FaceStopUserClient extends StopUserClient<ISession> {
@@ -36,7 +37,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 525e508..15d6a89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors.face.aidl;
 
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.face.EnrollmentStageConfig;
 import android.hardware.biometrics.face.Error;
 import android.hardware.biometrics.face.IFace;
@@ -188,6 +189,24 @@
                 Slog.w(TAG, "close");
                 cb.onSessionClosed();
             }
+
+            @Override
+            public ICancellationSignal authenticateWithContext(
+                    long operationId, OperationContext context) {
+                return authenticate(operationId);
+            }
+
+            @Override
+            public ICancellationSignal enrollWithContext(
+                    HardwareAuthToken hat, byte enrollmentType, byte[] features,
+                    NativeHandle previewSurface, OperationContext context) {
+                return enroll(hat, enrollmentType, features, previewSurface);
+            }
+
+            @Override
+            public ICancellationSignal detectInteractionWithContext(OperationContext context) {
+                return detectInteraction();
+            }
         };
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index b45578b..e7483b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -32,6 +32,7 @@
 
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.util.ArrayList;
@@ -197,7 +198,7 @@
     public void cleanupInternalState(int userId) {
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
-        mFace10.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mFace10.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
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 f4dcbbb..9a52db1 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
@@ -60,6 +60,7 @@
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnumerateConsumer;
 import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -333,12 +334,13 @@
     Face10(@NonNull Context context,
             @NonNull FaceSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull Handler handler,
             @NonNull BiometricScheduler scheduler) {
         mSensorProperties = sensorProps;
         mContext = context;
         mSensorId = sensorProps.sensorId;
         mScheduler = scheduler;
-        mHandler = new Handler(Looper.getMainLooper());
+        mHandler = handler;
         mUsageStats = new UsageStats(context);
         mAuthenticatorIds = new HashMap<>();
         mLazyDaemon = Face10.this::getDaemon;
@@ -357,9 +359,11 @@
         }
     }
 
-    public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
+    public static Face10 newInstance(@NonNull Context context,
+            @NonNull FaceSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
-        this(context, sensorProps, lockoutResetDispatcher,
+        final Handler handler = new Handler(Looper.getMainLooper());
+        return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
                 new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
                         null /* gestureAvailabilityTracker */));
     }
@@ -531,7 +535,7 @@
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
                     opPackageName, mSensorId, sSystemClock.millis());
             mGeneratedChallengeCache = client;
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                     if (client != clientMonitor) {
@@ -559,7 +563,7 @@
 
             final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
                     mLazyDaemon, token, userId, opPackageName, mSensorId);
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
@@ -573,10 +577,11 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
             @NonNull String opPackageName, @NonNull int[] disabledFeatures,
             @Nullable Surface previewSurface, boolean debugConsent) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -584,10 +589,10 @@
 
             final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
-                    opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+                    opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
                     ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
 
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
@@ -598,13 +603,12 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> {
-            mScheduler.cancelEnrollment(token);
-        });
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
     }
 
     @Override
@@ -739,7 +743,7 @@
             final int faceId = faces.get(0).getBiometricId();
             final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
                     token, listener, userId, opPackageName, mSensorId, feature, faceId);
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(
                         @NonNull BaseClientMonitor clientMonitor, boolean success) {
@@ -757,7 +761,7 @@
     }
 
     private void scheduleInternalCleanup(int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -771,7 +775,7 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         scheduleInternalCleanup(userId, callback);
     }
 
@@ -887,12 +891,14 @@
         final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
                 mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
                 hasEnrolled, mAuthenticatorIds);
-        mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+        mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
             @Override
             public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                     boolean success) {
                 if (success) {
                     mCurrentUserId = targetUserId;
+                } else {
+                    Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
                 }
             }
         });
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 7548d28..1e0e799 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -34,7 +34,9 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.face.UsageStats;
 
@@ -87,15 +89,16 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         mState = STATE_STARTED;
     }
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
+                getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 80828cced..8068e14 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -33,7 +33,9 @@
 import com.android.internal.R;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 
 import java.util.ArrayList;
@@ -53,12 +55,13 @@
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
             @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
             @Nullable Surface previewSurface, int sensorId) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
                 false /* shouldVibrate */);
+        setRequestId(requestId);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
         mEnrollIgnoreList = getContext().getResources()
                 .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
@@ -68,8 +71,9 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
+                getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index f418104..e29a192 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.GenerateChallengeClient;
 
@@ -39,7 +40,7 @@
 
     private static final String TAG = "FaceGenerateChallengeClient";
     static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
-    private static final Callback EMPTY_CALLBACK = new Callback() {
+    private static final ClientMonitorCallback EMPTY_CALLBACK = new ClientMonitorCallback() {
     };
 
     private final long mCreatedAt;
@@ -94,7 +95,7 @@
     }
 
     private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
-            @NonNull Callback ownerCallback) {
+            @NonNull ClientMonitorCallback ownerCallback) {
         Preconditions.checkState(mChallengeResult != null, "result not available");
         try {
             receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 7821601..0a9d96d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
@@ -66,7 +67,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 9d977d6..ee01c43 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.ArrayList;
@@ -57,7 +58,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index cc3d8f0..ee28f7b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -26,6 +26,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
@@ -71,7 +72,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 5343d0d..8ee8ce5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.io.File;
@@ -49,7 +50,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 3e70ee5..6366e19 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -249,7 +249,7 @@
         }
 
         @Override // Binder call
-        public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
+        public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
                 final int userId, final IFingerprintServiceReceiver receiver,
                 final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
@@ -257,15 +257,15 @@
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for enroll");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+            return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
                     receiver, opPackageName, enrollReason);
         }
 
         @Override // Binder call
-        public void cancelEnrollment(final IBinder token) {
+        public void cancelEnrollment(final IBinder token, long requestId) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -274,7 +274,7 @@
                 return;
             }
 
-            provider.second.cancelEnrollment(provider.first, token);
+            provider.second.cancelEnrollment(provider.first, token, requestId);
         }
 
         @SuppressWarnings("deprecation")
@@ -818,7 +818,7 @@
                             mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                 } else {
                     fingerprint21 = Fingerprint21.newInstance(getContext(),
-                            mFingerprintStateCallback, hidlSensor,
+                            mFingerprintStateCallback, hidlSensor, mHandler,
                             mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                 }
                 mServiceProviders.add(fingerprint21);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
index be0e6ed..04fd534 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
@@ -31,6 +31,7 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.EnrollmentModifier;
 
@@ -39,7 +40,7 @@
 /**
  * A callback for receiving notifications about changes in fingerprint state.
  */
-public class FingerprintStateCallback implements BaseClientMonitor.Callback {
+public class FingerprintStateCallback implements ClientMonitorCallback {
 
     @NonNull private final CopyOnWriteArrayList<IFingerprintStateListener>
             mFingerprintStateListeners = new CopyOnWriteArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 1772f81..0bdc4eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -30,7 +30,7 @@
 import android.os.IBinder;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
 
@@ -88,11 +88,11 @@
     /**
      * Schedules fingerprint enrollment.
      */
-    void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+    long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
             int userId, @NonNull IFingerprintServiceReceiver receiver,
             @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
 
-    void cancelEnrollment(int sensorId, @NonNull IBinder token);
+    void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
 
     long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
@@ -121,7 +121,7 @@
             @NonNull String opPackageName);
 
     void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback);
+            @Nullable ClientMonitorCallback callback);
 
     boolean isHardwareDetected(int sensorId);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 2b50b96..b29fbb6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -32,6 +32,7 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 
@@ -204,7 +205,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         Slog.d(TAG, "cleanupInternalState: " + userId);
-        mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index e4d5fba..f3d0121 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -33,9 +33,13 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutConsumer;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -80,11 +84,11 @@
         mLockoutCache = lockoutCache;
         mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
         mSensorProps = sensorProps;
-        mALSProbeCallback = createALSCallback(false /* startWithClient */);
+        mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (mSensorProps.isAnyUdfpsType()) {
@@ -97,8 +101,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(mALSProbeCallback, callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
     }
 
     @Override
@@ -233,7 +237,8 @@
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
         // Lockout metrics are logged as an error code.
         final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
-        logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+        getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+                isCryptoOperation(), getTargetUserId());
 
         try {
             getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -251,7 +256,8 @@
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
         // Lockout metrics are logged as an error code.
         final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
-        logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+        getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+                isCryptoOperation(), getTargetUserId());
 
         try {
             getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index ac3ce89..1f0482d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -30,6 +30,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.DetectionConsumer;
 import com.android.server.biometrics.sensors.SensorOverlays;
@@ -61,7 +62,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index ccb34aa..169c3eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -37,7 +37,9 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.SensorOverlays;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -57,7 +59,7 @@
     private boolean mIsPointerDown;
 
     FingerprintEnrollClient(@NonNull Context context,
-            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@@ -69,20 +71,22 @@
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
                 !sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+        setRequestId(requestId);
         mSensorProps = sensorProps;
         mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
         mMaxTemplatesPerUser = maxTemplatesPerUser;
 
         mEnrollReason = enrollReason;
         if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
-            setShouldLog(false);
+            getLogger().disableMetrics();
         }
     }
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
+                getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index ed2345e..52bd234 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.Map;
@@ -48,7 +49,7 @@
         // Nothing to do here
     }
 
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 734b173..efc9304 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
@@ -55,7 +55,9 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.InvalidationRequesterClient;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.PerformanceTracker;
@@ -248,7 +250,7 @@
     }
 
     private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
-            BaseClientMonitor.Callback callback) {
+            ClientMonitorCallback callback) {
         if (!mSensors.contains(sensorId)) {
             throw new IllegalStateException("Unable to schedule client: " + client
                     + " for sensor: " + sensorId);
@@ -347,20 +349,21 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
             @FingerprintManager.EnrollReason int enrollReason) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
                     .maxEnrollmentsPerUser;
             final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
-                    mSensors.get(sensorId).getLazySession(), token,
+                    mSensors.get(sensorId).getLazySession(), token, id,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
                     opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
                     mSensors.get(sensorId).getSensorProperties(),
                     mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
-            scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
+            scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
 
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -378,11 +381,13 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() ->
+                mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
     }
 
     @Override
@@ -481,7 +486,7 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
             final FingerprintInternalCleanupClient client =
@@ -490,7 +495,7 @@
                             mContext.getOpPackageName(), sensorId, enrolledList,
                             FingerprintUtils.getInstance(sensorId),
                             mSensors.get(sensorId).getAuthenticatorIds());
-            scheduleForSensor(sensorId, client, new BaseClientMonitor.CompositeCallback(callback,
+            scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback,
                     mFingerprintStateCallback));
         });
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 878ef46..ee8d170 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -27,6 +27,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutCache;
@@ -64,7 +65,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
index ee81620..9f11df6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StartUserClient;
 
 public class FingerprintStartUserClient extends StartUserClient<IFingerprint, ISession> {
@@ -44,7 +45,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 7055d65..9d38145 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
@@ -24,6 +24,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StopUserClient;
 
 public class FingerprintStopUserClient extends StopUserClient<ISession> {
@@ -36,7 +37,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index e771923..1eb153c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -17,10 +17,12 @@
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.fingerprint.Error;
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.RemoteException;
@@ -182,6 +184,34 @@
             public void onUiReady() {
                 Slog.w(TAG, "onUiReady");
             }
+
+            @Override
+            public ICancellationSignal authenticateWithContext(
+                    long operationId, OperationContext context) {
+                return authenticate(operationId);
+            }
+
+            @Override
+            public ICancellationSignal enrollWithContext(
+                    HardwareAuthToken hat, OperationContext context) {
+                return enroll(hat);
+            }
+
+            @Override
+            public ICancellationSignal detectInteractionWithContext(OperationContext context) {
+                return detectInteraction();
+            }
+
+            @Override
+            public void onPointerDownWithContext(PointerContext context) {
+                onPointerDown(
+                        context.pointerId, context.x, context.y, context.minor, context.major);
+            }
+
+            @Override
+            public void onPointerUpWithContext(PointerContext context) {
+                onPointerUp(context.pointerId);
+            }
         };
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 79c6b1b3..033855f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -31,6 +31,7 @@
 
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 
@@ -201,7 +202,7 @@
     public void cleanupInternalState(int userId)  {
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
-        mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
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 5f2f4cf..f160dff 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
@@ -42,7 +42,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IHwBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -63,7 +62,9 @@
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnumerateConsumer;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -320,7 +321,8 @@
     Fingerprint21(@NonNull Context context,
             @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
-            @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
+            @NonNull BiometricScheduler scheduler,
+            @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull HalResultController controller) {
         mContext = context;
@@ -356,16 +358,15 @@
     public static Fingerprint21 newInstance(@NonNull Context context,
             @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
+            @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-        final Handler handler = new Handler(Looper.getMainLooper());
         final BiometricScheduler scheduler =
                 new BiometricScheduler(TAG,
                         BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
                         gestureAvailabilityDispatcher);
         final HalResultController controller = new HalResultController(sensorProps.sensorId,
-                context, handler,
-                scheduler);
+                context, handler, scheduler);
         return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
                 lockoutResetDispatcher, controller);
     }
@@ -491,19 +492,25 @@
                 !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
         final FingerprintUpdateActiveUserClient client =
                 new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
-                        mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
-                        hasEnrolled, mAuthenticatorIds, force);
-        mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+                        mContext.getOpPackageName(), mSensorProperties.sensorId,
+                        this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
+        mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
             @Override
             public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                     boolean success) {
                 if (success) {
                     mCurrentUserId = targetUserId;
+                } else {
+                    Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
                 }
             }
         });
     }
 
+    private int getCurrentUser() {
+        return mCurrentUserId;
+    }
+
     @Override
     public boolean containsSensor(int sensorId) {
         return mSensorProperties.sensorId == sensorId;
@@ -558,19 +565,21 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
             @FingerprintManager.EnrollReason int enrollReason) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
             final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
-                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
-                    hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
-                    ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
-                    mSidefpsController, enrollReason);
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+                    mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+                    userId, hardwareAuthToken, opPackageName,
+                    FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+                    mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+                    enrollReason);
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                     mFingerprintStateCallback.onClientStarted(clientMonitor);
@@ -588,13 +597,12 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> {
-            mScheduler.cancelEnrollment(token);
-        });
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
     }
 
     @Override
@@ -693,7 +701,7 @@
     }
 
     private void scheduleInternalCleanup(int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -709,8 +717,8 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
-        scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback,
+            @Nullable ClientMonitorCallback callback) {
+        scheduleInternalCleanup(userId, new ClientMonitorCompositeCallback(callback,
                 mFingerprintStateCallback));
     }
 
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 dd68b4d..1694bd9 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
@@ -26,7 +26,6 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.FingerprintStateListener;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Handler;
 import android.os.IBinder;
@@ -135,43 +134,16 @@
     @NonNull private final RestartAuthRunnable mRestartAuthRunnable;
 
     private static class TestableBiometricScheduler extends BiometricScheduler {
-        @NonNull private final TestableInternalCallback mInternalCallback;
         @NonNull private Fingerprint21UdfpsMock mFingerprint21;
 
-        TestableBiometricScheduler(@NonNull String tag,
+        TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler,
                 @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-            super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
-                    gestureAvailabilityDispatcher);
-            mInternalCallback = new TestableInternalCallback();
-        }
-
-        class TestableInternalCallback extends InternalCallback {
-            @Override
-            public void onClientStarted(BaseClientMonitor clientMonitor) {
-                super.onClientStarted(clientMonitor);
-                Slog.d(TAG, "Client started: " + clientMonitor);
-                mFingerprint21.setDebugMessage("Started: " + clientMonitor);
-            }
-
-            @Override
-            public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
-                super.onClientFinished(clientMonitor, success);
-                Slog.d(TAG, "Client finished: " + clientMonitor);
-                mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
-            }
+            super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
         }
 
         void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
             mFingerprint21 = fingerprint21;
         }
-
-        /**
-         * Expose the internal finish callback so it can be used for testing
-         */
-        @Override
-        @NonNull protected InternalCallback getInternalCallback() {
-            return mInternalCallback;
-        }
     }
 
     /**
@@ -280,7 +252,7 @@
 
         final Handler handler = new Handler(Looper.getMainLooper());
         final TestableBiometricScheduler scheduler =
-                new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
+                new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
         final MockHalResultController controller =
                 new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
         return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
@@ -392,7 +364,7 @@
             final ClientMonitorCallbackConverter listener = client.getListener();
             final String opPackageName = client.getOwnerString();
             final boolean restricted = authClient.isRestricted();
-            final int statsClient = client.getStatsClient();
+            final int statsClient = client.getLogger().getStatsClient();
             final boolean isKeyguard = authClient.isKeyguard();
 
             // Don't actually send cancel() to the HAL, since successful auth already finishes
@@ -442,7 +414,8 @@
     }
 
     @Override
-    public void onTrustChanged(boolean enabled, int userId, int flags) {
+    public void onTrustChanged(boolean enabled, int userId, int flags,
+            List<String> trustGrantedMessages) {
         mUserHasTrust.put(userId, enabled);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 3058e25..87d47c1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -32,9 +32,13 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.SensorOverlays;
 import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -80,11 +84,11 @@
         mLockoutFrameworkImpl = lockoutTracker;
         mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
         mSensorProps = sensorProps;
-        mALSProbeCallback = createALSCallback(false /* startWithClient */);
+        mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (mSensorProps.isAnyUdfpsType()) {
@@ -97,8 +101,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(mALSProbeCallback, callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index b854fb3..9137212 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -32,6 +32,7 @@
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.SensorOverlays;
@@ -82,7 +83,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
@@ -127,8 +128,8 @@
     @Override
     public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
             ArrayList<Byte> hardwareAuthToken) {
-        logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
-                getTargetUserId(), false /* isBiometricPrompt */);
+        getLogger().logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
+                isCryptoOperation(), getTargetUserId(), false /* isBiometricPrompt */);
 
         // Do not distinguish between success/failures.
         vibrateSuccess();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1ebf44c..82b046d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -33,7 +33,9 @@
 
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.SensorOverlays;
 import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -55,7 +57,7 @@
 
     FingerprintEnrollClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -64,18 +66,20 @@
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
                 true /* shouldVibrate */);
+        setRequestId(requestId);
         mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
 
         mEnrollReason = enrollReason;
         if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
-            setShouldLog(false);
+            getLogger().disableMetrics();
         }
     }
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
+                getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
index a39f4f8..ed28e3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
@@ -22,6 +22,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 
 /**
  * Clears lockout, which is handled in the framework (and not the HAL) for the
@@ -40,7 +41,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
                 getTargetUserId());
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 fd38bdd..d317984 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
@@ -27,10 +27,12 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+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;
 
 /**
  * Sets the HAL's current active user, and updates the framework's authenticatorId cache.
@@ -40,7 +42,7 @@
     private static final String TAG = "FingerprintUpdateActiveUserClient";
     private static final String FP_DATA_DIR = "fpdata";
 
-    private final int mCurrentUserId;
+    private final Supplier<Integer> mCurrentUserId;
     private final boolean mForceUpdateAuthenticatorId;
     private final boolean mHasEnrolledBiometrics;
     private final Map<Integer, Long> mAuthenticatorIds;
@@ -48,8 +50,9 @@
 
     FingerprintUpdateActiveUserClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
-            @NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics,
-            @NonNull Map<Integer, Long> authenticatorIds, boolean forceUpdateAuthenticatorId) {
+            @NonNull String owner, int sensorId, Supplier<Integer> currentUserId,
+            boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
+            boolean forceUpdateAuthenticatorId) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
                 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
                 BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
@@ -60,10 +63,10 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
-        if (mCurrentUserId == getTargetUserId() && !mForceUpdateAuthenticatorId) {
+        if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
             Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
             callback.onClientFinished(this, true /* success */);
             return;
@@ -109,8 +112,10 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath());
-            mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+            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) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
index 8c93891..6654c0c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
@@ -189,7 +189,8 @@
                 removed.add(id);
             }
         }
-        if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()) {
+        if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()
+                && !chunk.isPurge()) {
             return null;
         }
         mComplete = chunk.isComplete();
@@ -239,9 +240,10 @@
         }
 
         // Determine number of chunks we need to send.
-        int numChunks = 0;
+        int numChunks = purge ? 1 : 0;
         if (modified != null) {
-            numChunks = roundUpFraction(modified.size(), maxNumModifiedPerChunk);
+            numChunks = Math.max(numChunks,
+                    roundUpFraction(modified.size(), maxNumModifiedPerChunk));
         }
         if (removed != null) {
             numChunks = Math.max(numChunks, roundUpFraction(removed.size(), maxNumRemovedPerChunk));
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 3120dc5..1e00ea9 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -36,6 +36,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.hardware.CameraSessionStats;
 import android.hardware.CameraStreamStats;
 import android.hardware.ICameraService;
@@ -46,6 +47,8 @@
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.hardware.display.DisplayManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
 import android.media.AudioManager;
 import android.nfc.INfcAdapter;
 import android.os.Binder;
@@ -303,6 +306,9 @@
 
         @Override
         public void onFixedRotationFinished(int displayId) { }
+
+        @Override
+        public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearArea) { }
     }
 
 
@@ -335,6 +341,16 @@
                         switchUserLocked(mLastUser);
                     }
                     break;
+                case UsbManager.ACTION_USB_DEVICE_ATTACHED:
+                case UsbManager.ACTION_USB_DEVICE_DETACHED:
+                    synchronized (mLock) {
+                        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                        if (device != null) {
+                            notifyUsbDeviceHotplugLocked(device,
+                                    action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED));
+                        }
+                    }
+                    break;
                 default:
                     break; // do nothing
             }
@@ -645,6 +661,8 @@
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
         mContext.registerReceiver(mIntentReceiver, filter);
 
         publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
@@ -788,6 +806,7 @@
                     streamProtos[i].histogramType = streamStats.getHistogramType();
                     streamProtos[i].histogramBins = streamStats.getHistogramBins();
                     streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
+                    streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
 
                     if (CameraServiceProxy.DEBUG) {
                         String histogramTypeName =
@@ -807,7 +826,8 @@
                                 + ", histogramBins "
                                 + Arrays.toString(streamProtos[i].histogramBins)
                                 + ", histogramCounts "
-                                + Arrays.toString(streamProtos[i].histogramCounts));
+                                + Arrays.toString(streamProtos[i].histogramCounts)
+                                + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile);
                     }
                 }
             }
@@ -961,6 +981,32 @@
         return true;
     }
 
+    private boolean notifyUsbDeviceHotplugLocked(@NonNull UsbDevice device, boolean attached) {
+        // Only handle external USB camera devices
+        if (device.getHasVideoCapture()) {
+            // Forward the usb hotplug event to the native camera service running in the
+            // cameraserver
+            // process.
+            ICameraService cameraService = getCameraServiceRawLocked();
+            if (cameraService == null) {
+                Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
+                return false;
+            }
+
+            try {
+                int eventType = attached ? ICameraService.EVENT_USB_DEVICE_ATTACHED
+                        : ICameraService.EVENT_USB_DEVICE_DETACHED;
+                mCameraServiceRaw.notifySystemEvent(eventType, new int[]{device.getDeviceId()});
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
+                // Not much we can do if camera service is dead.
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
     private void updateActivityCount(CameraSessionStats cameraState) {
         String cameraId = cameraState.getCameraId();
         int newCameraState = cameraState.getNewCameraState();
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index e120343..5b76695 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -81,6 +81,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiThread;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -127,6 +128,7 @@
     private final IUriGrantsManager mUgm;
     private final UriGrantsManagerInternal mUgmInternal;
     private final WindowManagerInternal mWm;
+    private final VirtualDeviceManagerInternal mVdm;
     private final IUserManager mUm;
     private final PackageManager mPm;
     private final AppOpsManager mAppOps;
@@ -158,6 +160,8 @@
         mUgm = UriGrantsManager.getService();
         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mWm = LocalServices.getService(WindowManagerInternal.class);
+        // Can be null; not all products have CDM + VirtualDeviceManager
+        mVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
         mPm = getContext().getPackageManager();
         mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -614,9 +618,6 @@
         mEmulatorClipboardMonitor.accept(clip);
 
         final int userId = UserHandle.getUserId(uid);
-        if (clip != null) {
-            startClassificationLocked(clip, userId);
-        }
 
         // Update this user
         setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -672,6 +673,17 @@
     @GuardedBy("mLock")
     private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
             int uid, @Nullable String sourcePackage) {
+        final int userId = UserHandle.getUserId(uid);
+        if (clip != null) {
+            startClassificationLocked(clip, userId);
+        }
+
+        setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
+    }
+
+    @GuardedBy("mLock")
+    private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+            @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
         revokeUris(clipboard);
         clipboard.activePermissionOwners.clear();
         if (clip == null && clipboard.primaryClip == null) {
@@ -965,6 +977,13 @@
         // First, verify package ownership to ensure use below is safe.
         mAppOps.checkPackage(uid, callingPackage);
 
+        // Nothing in a virtual session is permitted to touch clipboard contents
+        if (mVdm != null && mVdm.isAppRunningOnAnyVirtualDevice(uid)) {
+            Slog.w(TAG, "Clipboard access denied to " + uid + "/" + callingPackage
+                    + " within a virtual device session");
+            return false;
+        }
+
         // Shell can access the clipboard for testing purposes.
         if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
                     callingPackage) == PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
deleted file mode 100644
index 1220391..0000000
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.communal;
-
-import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
-import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_BY_DEFAULT;
-import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-
-import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
-import android.app.communal.ICommunalManager;
-import android.app.communal.ICommunalModeListener;
-import android.app.compat.CompatChanges;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.LaunchAfterAuthenticationActivity;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.wm.ActivityInterceptorCallback;
-import com.android.server.wm.ActivityTaskManagerInternal;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * System service for handling Communal Mode state.
- */
-public final class CommunalManagerService extends SystemService {
-    private static final String TAG = CommunalManagerService.class.getSimpleName();
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final String DELIMITER = ",";
-    private final Context mContext;
-    private final ActivityTaskManagerInternal mAtmInternal;
-    private final KeyguardManager mKeyguardManager;
-    private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false);
-    private final BinderService mBinderService;
-    private final PackageReceiver mPackageReceiver;
-    private final PackageManager mPackageManager;
-    private final DreamManagerInternal mDreamManagerInternal;
-    private final RemoteCallbackList<ICommunalModeListener> mListeners =
-            new RemoteCallbackList<>();
-
-    private final ActivityInterceptorCallback mActivityInterceptorCallback =
-            new ActivityInterceptorCallback() {
-                @Nullable
-                @Override
-                public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
-                    if (!shouldIntercept(info.aInfo)) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Activity allowed, not intercepting: "
-                                    + info.aInfo.getComponentName());
-                        }
-                        return null;
-                    }
-
-                    final IIntentSender target = mAtmInternal.getIntentSender(
-                            INTENT_SENDER_ACTIVITY,
-                            info.callingPackage,
-                            info.callingFeatureId,
-                            info.callingUid,
-                            info.userId,
-                            /* token= */null,
-                            /* resultWho= */ null,
-                            /* requestCode= */ 0,
-                            new Intent[]{info.intent},
-                            new String[]{info.resolvedType},
-                            PendingIntent.FLAG_IMMUTABLE,
-                            /* bOptions= */ null);
-
-                    return new ActivityInterceptResult(
-                            LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
-                                    new IntentSender(target)),
-                            info.checkedOptions);
-
-                }
-            };
-
-    public CommunalManagerService(Context context) {
-        super(context);
-        mContext = context;
-        mPackageManager = mContext.getPackageManager();
-        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
-        mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
-        mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
-        mBinderService = new BinderService();
-        mPackageReceiver = new PackageReceiver(mContext);
-    }
-
-    @VisibleForTesting
-    BinderService getBinderServiceInstance() {
-        return mBinderService;
-    }
-
-    @Override
-    public void onStart() {
-        publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
-    }
-
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase != SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) return;
-        mAtmInternal.registerActivityStartInterceptor(
-                COMMUNAL_MODE_ORDERED_ID,
-                mActivityInterceptorCallback);
-        mPackageReceiver.register();
-        removeUninstalledPackagesFromSettings();
-    }
-
-    @Override
-    public void finalize() {
-        mPackageReceiver.unregister();
-    }
-
-    private Set<String> getUserEnabledApps() {
-        final String encodedApps = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.COMMUNAL_MODE_PACKAGES,
-                UserHandle.USER_SYSTEM);
-
-        return TextUtils.isEmpty(encodedApps)
-                ? Collections.emptySet()
-                : new HashSet<>(Arrays.asList(encodedApps.split(DELIMITER)));
-    }
-
-    private void removeUninstalledPackagesFromSettings() {
-        for (String packageName : getUserEnabledApps()) {
-            if (!isPackageInstalled(packageName, mPackageManager)) {
-                removePackageFromSettings(packageName);
-            }
-        }
-    }
-
-    private void removePackageFromSettings(String packageName) {
-        Set<String> enabledPackages = getUserEnabledApps();
-        if (enabledPackages.remove(packageName)) {
-            Settings.Secure.putStringForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.COMMUNAL_MODE_PACKAGES,
-                    String.join(DELIMITER, enabledPackages),
-                    UserHandle.USER_SYSTEM);
-        }
-    }
-
-    @VisibleForTesting
-    static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
-        if (packageManager == null) return false;
-        try {
-            return packageManager.getPackageInfo(packageName, 0) != null;
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    private boolean isAppAllowed(ApplicationInfo appInfo) {
-        if (isActiveDream(appInfo) || isChangeEnabled(ALLOW_COMMUNAL_MODE_BY_DEFAULT, appInfo)) {
-            return true;
-        }
-
-        if (!isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, appInfo)) {
-            if (DEBUG) Slog.d(TAG, "App is not allowlisted: " + appInfo.packageName);
-            return false;
-        }
-
-        if (!getUserEnabledApps().contains(appInfo.packageName)) {
-            if (DEBUG) Slog.d(TAG, "App does not have user consent: " + appInfo.packageName);
-            return false;
-        }
-
-        return true;
-    }
-
-    private boolean isActiveDream(ApplicationInfo appInfo) {
-        final ComponentName activeDream = mDreamManagerInternal.getActiveDreamComponent(
-                /* doze= */ false);
-        final ComponentName activeDoze = mDreamManagerInternal.getActiveDreamComponent(
-                /* doze= */ true);
-        return isFromPackage(activeDream, appInfo) || isFromPackage(activeDoze, appInfo);
-    }
-
-    private static boolean isFromPackage(ComponentName componentName, ApplicationInfo appInfo) {
-        if (componentName == null) return false;
-        return TextUtils.equals(appInfo.packageName, componentName.getPackageName());
-    }
-
-    private static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
-        return CompatChanges.isChangeEnabled(changeId, appInfo.packageName, UserHandle.SYSTEM);
-    }
-
-    private boolean shouldIntercept(ActivityInfo activityInfo) {
-        if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
-        ApplicationInfo appInfo = activityInfo.applicationInfo;
-        // Dreams are allowed to show, and don't require the showWhenLocked attribute.
-        if (isActiveDream(appInfo)) return false;
-
-        // If the activity doesn't have showWhenLocked enabled, disallow the activity.
-        final boolean showWhenLocked =
-                (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
-        if (!showWhenLocked) {
-            if (DEBUG) {
-                Slog.d(TAG, "Activity does not contain showWhenLocked attribute: "
-                        + activityInfo.getComponentName());
-            }
-            return true;
-        }
-
-        return !isAppAllowed(appInfo);
-    }
-
-    private void dispatchCommunalMode(boolean isShowing) {
-        synchronized (mListeners) {
-            int i = mListeners.beginBroadcast();
-            while (i > 0) {
-                i--;
-                try {
-                    mListeners.getBroadcastItem(i).onCommunalModeChanged(isShowing);
-                } catch (RemoteException e) {
-                    // Handled by the RemoteCallbackList.
-                }
-            }
-            mListeners.finishBroadcast();
-        }
-    }
-
-    private void enforceReadPermission() {
-        mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE,
-                Manifest.permission.READ_COMMUNAL_STATE
-                        + "permission required to read communal state.");
-    }
-
-    private final class BinderService extends ICommunalManager.Stub {
-        /**
-         * Sets whether or not we are in communal mode.
-         */
-        @RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
-        @Override
-        public void setCommunalViewShowing(boolean isShowing) {
-            mContext.enforceCallingPermission(Manifest.permission.WRITE_COMMUNAL_STATE,
-                    Manifest.permission.WRITE_COMMUNAL_STATE
-                            + "permission required to modify communal state.");
-            if (mCommunalViewIsShowing.get() == isShowing) {
-                return;
-            }
-            mCommunalViewIsShowing.set(isShowing);
-            dispatchCommunalMode(isShowing);
-        }
-
-        /**
-         * Checks whether or not we are in communal mode.
-         */
-        @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
-        @Override
-        public boolean isCommunalMode() {
-            enforceReadPermission();
-            return mCommunalViewIsShowing.get();
-        }
-
-        /**
-         * Adds a callback to execute when communal state changes.
-         */
-        @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
-        public void addCommunalModeListener(ICommunalModeListener listener) {
-            enforceReadPermission();
-            synchronized (mListeners) {
-                mListeners.register(listener);
-            }
-        }
-
-        /**
-         * Removes an added callback that execute when communal state changes.
-         */
-        @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
-        public void removeCommunalModeListener(ICommunalModeListener listener) {
-            enforceReadPermission();
-            synchronized (mListeners) {
-                mListeners.unregister(listener);
-            }
-        }
-    }
-
-    /**
-     * A {@link BroadcastReceiver} that listens on package removed events and updates any stored
-     * package state in Settings.
-     */
-    private final class PackageReceiver extends BroadcastReceiver {
-        private final Context mContext;
-        private final IntentFilter mIntentFilter;
-
-        private PackageReceiver(Context context) {
-            mContext = context;
-            mIntentFilter = new IntentFilter();
-            mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
-            mIntentFilter.addDataScheme("package");
-        }
-
-        private void register() {
-            mContext.registerReceiverAsUser(
-                    this,
-                    UserHandle.SYSTEM,
-                    mIntentFilter,
-                    /* broadcastPermission= */null,
-                    /* scheduler= */ null,
-                    Context.RECEIVER_EXPORTED_UNAUDITED);
-        }
-
-        private void unregister() {
-            mContext.unregisterReceiver(this);
-        }
-
-        @Override
-        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
-            final Uri data = intent.getData();
-            if (data == null) {
-                Slog.w(TAG, "Failed to get package name in package receiver");
-                return;
-            }
-            final String packageName = data.getSchemeSpecificPart();
-            final String action = intent.getAction();
-            if (ACTION_PACKAGE_REMOVED.equals(action)) {
-                removePackageFromSettings(packageName);
-            } else {
-                Slog.w(TAG, "Unsupported action in package receiver: " + action);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/communal/OWNERS b/services/core/java/com/android/server/communal/OWNERS
deleted file mode 100644
index b02883d..0000000
--- a/services/core/java/com/android/server/communal/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
[email protected]
[email protected]
[email protected]
[email protected]
\ No newline at end of file
diff --git a/services/core/java/com/android/server/communal/TEST_MAPPING b/services/core/java/com/android/server/communal/TEST_MAPPING
deleted file mode 100644
index 026e9bb..0000000
--- a/services/core/java/com/android/server/communal/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.communal"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 39fa3f2..135276e 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -50,6 +50,12 @@
     public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
 
     /**
+     * Returns the flags that should be added to any virtual displays created on this virtual
+     * device.
+     */
+    public abstract int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice);
+
+    /**
      * Returns true if the given {@code uid} is the owner of any virtual devices that are
      * currently active.
      */
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index 880dbf6..fe002ce 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -25,6 +25,7 @@
 import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
 import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
 
+import static java.util.Collections.emptyMap;
 import static java.util.Collections.emptySet;
 
 import android.annotation.NonNull;
@@ -157,17 +158,17 @@
         Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove =
                 new ArrayMap<>();
         for (String packageName : packageNames) {
-            Long versionCode = getVersionCodeOrNull(packageName);
-            if (versionCode == null) {
-                // Package isn't installed yet.
-                continue;
-            }
-
             Set<Long> changeIdsToSkip = packageToChangeIdsToSkip.getOrDefault(packageName,
                     emptySet());
-            Map<Long, PackageOverride> overridesToAdd = mOverridesParser.parsePackageOverrides(
-                    properties.getString(packageName, /* defaultValue= */ ""), packageName,
-                    versionCode, changeIdsToSkip);
+
+            Map<Long, PackageOverride> overridesToAdd = emptyMap();
+            Long versionCode = getVersionCodeOrNull(packageName);
+            if (versionCode != null) {
+                // Only if package installed add overrides, otherwise just remove.
+                overridesToAdd = mOverridesParser.parsePackageOverrides(
+                        properties.getString(packageName, /* defaultValue= */ ""), packageName,
+                        versionCode, changeIdsToSkip);
+            }
             if (!overridesToAdd.isEmpty()) {
                 packageNameToOverridesToAdd.put(packageName,
                         new CompatibilityOverrideConfig(overridesToAdd));
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 72e900b..fce6737 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -68,7 +68,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsManagerInternal;
 
 import java.time.Clock;
 import java.time.ZoneId;
@@ -200,6 +199,7 @@
         private final NetworkTemplate mNetworkTemplate;
         private final UsageCallback mUsageCallback;
         private NetworkCapabilities mNetworkCapabilities;
+        private final NetworkStatsManager mStatsManager;
 
         public MultipathTracker(Network network, NetworkCapabilities nc) {
             this.network = network;
@@ -239,6 +239,13 @@
                     updateMultipathBudget();
                 }
             };
+            mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+            // Query stats from NetworkStatsService will trigger a poll by default.
+            // But since MultipathPolicyTracker listens NPMS events that triggered by
+            // stats updated event, and will query stats
+            // after the event. A polling -> updated -> query -> polling loop will be introduced
+            // if polls on open. Hence, set flag to false to prevent a polling loop.
+            mStatsManager.setPollOnOpen(false);
 
             updateMultipathBudget();
         }
@@ -262,8 +269,9 @@
 
         private long getNetworkTotalBytes(long start, long end) {
             try {
-                return LocalServices.getService(NetworkStatsManagerInternal.class)
-                        .getNetworkTotalBytes(mNetworkTemplate, start, end);
+                final android.app.usage.NetworkStats.Bucket ret =
+                        mStatsManager.querySummaryForDevice(mNetworkTemplate, start, end);
+                return ret.getRxBytes() + ret.getTxBytes();
             } catch (RuntimeException e) {
                 Log.w(TAG, "Failed to get data usage: " + e);
                 return -1;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6a716cb..066c263 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1223,8 +1223,11 @@
             for (RouteInfo route : mConfig.routes) {
                 lp.addRoute(route);
                 InetAddress address = route.getDestination().getAddress();
-                allowIPv4 |= address instanceof Inet4Address;
-                allowIPv6 |= address instanceof Inet6Address;
+
+                if (route.getType() == RouteInfo.RTN_UNICAST) {
+                    allowIPv4 |= address instanceof Inet4Address;
+                    allowIPv6 |= address instanceof Inet6Address;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 7fe24ff..78d55b9 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -43,14 +43,14 @@
  */
 public final class DeviceState {
     /**
-     * Flag that indicates sticky requests should be cancelled when this device state becomes the
+     * Flag that indicates override requests should be cancelled when this device state becomes the
      * base device state.
      */
-    public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0;
+    public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
 
     /** @hide */
     @IntDef(prefix = {"FLAG_"}, flag = true, value = {
-            FLAG_CANCEL_STICKY_REQUESTS,
+            FLAG_CANCEL_OVERRIDE_REQUESTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeviceStateFlags {}
@@ -114,4 +114,10 @@
     public int hashCode() {
         return Objects.hash(mIdentifier, mName, mFlags);
     }
+
+    /** Checks if a specific flag is set
+     */
+    public boolean hasFlag(int flagToCheckFor) {
+        return (mFlags & flagToCheckFor) == flagToCheckFor;
+    }
 }
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 792feea..709af91 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -20,6 +20,7 @@
 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
 
+import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
@@ -273,14 +274,14 @@
         synchronized (mLock) {
             final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
 
-            // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS
+            // Whether or not at least one device state has the flag FLAG_CANCEL_OVERRIDE_REQUESTS
             // set. If set to true, the OverrideRequestController will be configured to allow sticky
             // requests.
             boolean hasTerminalDeviceState = false;
             mDeviceStates.clear();
             for (int i = 0; i < supportedDeviceStates.length; i++) {
                 DeviceState state = supportedDeviceStates[i];
-                if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
+                if (state.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
                     hasTerminalDeviceState = true;
                 }
                 mDeviceStates.put(state.getIdentifier(), state);
@@ -345,8 +346,8 @@
             }
             mBaseState = Optional.of(baseState);
 
-            if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
-                mOverrideRequestController.cancelStickyRequests();
+            if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
+                mOverrideRequestController.cancelOverrideRequests();
             }
             mOverrideRequestController.handleBaseStateChanged();
             updatePendingStateLocked();
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 05c9eb2..36cb416 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -153,6 +153,16 @@
     }
 
     /**
+     * Cancels all override requests, this could be due to the device being put
+     * into a hardware state that declares the flag "FLAG_CANCEL_OVERRIDE_REQUESTS"
+     */
+    void cancelOverrideRequests() {
+        mTmpRequestsToCancel.clear();
+        mTmpRequestsToCancel.addAll(mRequests);
+        cancelRequestsLocked(mTmpRequestsToCancel);
+    }
+
+    /**
      * Returns {@code true} if this controller is current managing a request with the specified
      * {@code token}, {@code false} otherwise.
      */
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index c04032f..f4c36c6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -54,6 +54,10 @@
 
     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
 
+    public static final int AUTO_BRIGHTNESS_ENABLED = 1;
+    public static final int AUTO_BRIGHTNESS_DISABLED = 2;
+    public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3;
+
     // 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
     // non-zero, which in turn ensures that the total weight is non-zero.
@@ -70,13 +74,6 @@
     private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
     private static final int MSG_RUN_UPDATE = 6;
 
-    // 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;
-
-    // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
-    private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
-
     // Callbacks for requesting updates to the display's power state
     private final Callbacks mCallbacks;
 
@@ -121,8 +118,10 @@
     // and only then decide whether to change brightness.
     private final boolean mResetAmbientLuxAfterWarmUpConfig;
 
-    // Period of time in which to consider light samples in milliseconds.
-    private final int mAmbientLightHorizon;
+    // Period of time in which to consider light samples for a short/long-term estimate of ambient
+    // light in milliseconds.
+    private final int mAmbientLightHorizonLong;
+    private final int mAmbientLightHorizonShort;
 
     // The intercept used for the weighting calculation. This is used in order to keep all possible
     // weighting values positive.
@@ -214,7 +213,9 @@
     private IActivityTaskManager mActivityTaskManager;
     private PackageManager mPackageManager;
     private Context mContext;
+    private int mState = AUTO_BRIGHTNESS_DISABLED;
 
+    private Clock mClock;
     private final Injector mInjector;
 
     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
@@ -226,14 +227,16 @@
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, Context context,
             HighBrightnessModeController hbmController,
-            BrightnessMappingStrategy idleModeBrightnessMapper) {
+            BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
+            int ambientLightHorizonLong) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor,
                 interactiveModeBrightnessMapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
                 ambientBrightnessThresholds, screenBrightnessThresholds, context,
-                hbmController, idleModeBrightnessMapper
+                hbmController, idleModeBrightnessMapper, ambientLightHorizonShort,
+                ambientLightHorizonLong
         );
     }
 
@@ -247,8 +250,10 @@
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, Context context,
             HighBrightnessModeController hbmController,
-            BrightnessMappingStrategy idleModeBrightnessMapper) {
+            BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
+            int ambientLightHorizonLong) {
         mInjector = injector;
+        mClock = injector.createClock();
         mContext = context;
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
@@ -263,15 +268,16 @@
         mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
         mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
-        mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
-        mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
+        mAmbientLightHorizonLong = ambientLightHorizonLong;
+        mAmbientLightHorizonShort = ambientLightHorizonShort;
+        mWeightingIntercept = ambientLightHorizonLong;
         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
         mScreenBrightnessThresholds = screenBrightnessThresholds;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
         mHandler = new AutomaticBrightnessHandler(looper);
         mAmbientLightRingBuffer =
-            new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
+            new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock);
 
         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
             mLightSensor = lightSensor;
@@ -331,10 +337,11 @@
         return mCurrentBrightnessMapper.getAutoBrightnessAdjustment();
     }
 
-    public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
+    public void configure(int state, @Nullable BrightnessConfiguration configuration,
             float brightness, boolean userChangedBrightness, float adjustment,
             boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
-        mHbmController.setAutoBrightnessEnabled(enable);
+        mState = state;
+        mHbmController.setAutoBrightnessEnabled(mState);
         // While dozing, the application processor may be suspended which will prevent us from
         // receiving new information from the light sensor. On some devices, we may be able to
         // switch to a wake-up light sensor instead but for now we will simply disable the sensor
@@ -346,6 +353,7 @@
         if (userChangedAutoBrightnessAdjustment) {
             changed |= setAutoBrightnessAdjustment(adjustment);
         }
+        final boolean enable = mState == AUTO_BRIGHTNESS_ENABLED;
         if (userChangedBrightness && enable) {
             // Update the brightness curve with the new user control point. It's critical this
             // happens after we update the autobrightness adjustment since it may reset it.
@@ -390,6 +398,11 @@
         mHandler.sendEmptyMessage(MSG_RUN_UPDATE);
     }
 
+    @VisibleForTesting
+    float getAmbientLux() {
+        return mAmbientLux;
+    }
+
     private boolean setDisplayPolicy(int policy) {
         if (mDisplayPolicy == policy) {
             return false;
@@ -459,6 +472,7 @@
     public void dump(PrintWriter pw) {
         pw.println();
         pw.println("Automatic Brightness Controller Configuration:");
+        pw.println("  mState=" + configStateToString(mState));
         pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
         pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
         pw.println("  mDozeScaleFactor=" + mDozeScaleFactor);
@@ -468,7 +482,8 @@
         pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
         pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
         pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
-        pw.println("  mAmbientLightHorizon=" + mAmbientLightHorizon);
+        pw.println("  mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
+        pw.println("  mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
         pw.println("  mWeightingIntercept=" + mWeightingIntercept);
 
         pw.println();
@@ -520,11 +535,24 @@
         mScreenBrightnessThresholds.dump(pw);
     }
 
+    private String configStateToString(int state) {
+        switch (state) {
+        case AUTO_BRIGHTNESS_ENABLED:
+            return "AUTO_BRIGHTNESS_ENABLED";
+        case AUTO_BRIGHTNESS_DISABLED:
+            return "AUTO_BRIGHTNESS_DISABLED";
+        case AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE:
+            return "AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE";
+        default:
+            return String.valueOf(state);
+        }
+    }
+
     private boolean setLightSensorEnabled(boolean enable) {
         if (enable) {
             if (!mLightSensorEnabled) {
                 mLightSensorEnabled = true;
-                mLightSensorEnableTime = SystemClock.uptimeMillis();
+                mLightSensorEnableTime = mClock.uptimeMillis();
                 mCurrentLightSensorRate = mInitialLightSensorRate;
                 registerForegroundAppUpdater();
                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
@@ -559,7 +587,7 @@
 
     private void applyLightSensorMeasurement(long time, float lux) {
         mRecentLightSamples++;
-        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
+        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
         mAmbientLightRingBuffer.push(time, lux);
 
         // Remember this sample value.
@@ -700,8 +728,8 @@
     }
 
     private void updateAmbientLux() {
-        long time = SystemClock.uptimeMillis();
-        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
+        long time = mClock.uptimeMillis();
+        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
         updateAmbientLux(time);
     }
 
@@ -721,7 +749,7 @@
                         timeWhenSensorWarmedUp);
                 return;
             }
-            setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
+            setAmbientLux(calculateAmbientLux(time, mAmbientLightHorizonShort));
             mAmbientLuxValid = true;
             if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
@@ -741,8 +769,8 @@
         // proposed ambient light value since the slow value might be sufficiently far enough away
         // from the fast value to cause a recalculation while its actually just converging on
         // the fast value still.
-        float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
-        float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
+        float slowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
+        float fastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
 
         if ((slowAmbientLux >= mAmbientBrighteningThreshold
                 && fastAmbientLux >= mAmbientBrighteningThreshold
@@ -1023,7 +1051,7 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             if (mLightSensorEnabled) {
-                final long time = SystemClock.uptimeMillis();
+                final long time = mClock.uptimeMillis();
                 final float lux = event.values[0];
                 handleLightSensorEvent(time, lux);
             }
@@ -1049,6 +1077,15 @@
         void updateBrightness();
     }
 
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /**
+         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+         */
+        long uptimeMillis();
+    }
+
     /**
      * A ring buffer of ambient light measurements sorted by time.
      *
@@ -1068,14 +1105,16 @@
         private int mStart;
         private int mEnd;
         private int mCount;
+        Clock mClock;
 
-        public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+        public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon, Clock clock) {
             if (lightSensorRate <= 0) {
                 throw new IllegalArgumentException("lightSensorRate must be above 0");
             }
             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
             mRingLux = new float[mCapacity];
             mRingTime = new long[mCapacity];
+            mClock = clock;
         }
 
         public float getLux(int index) {
@@ -1160,7 +1199,7 @@
             StringBuilder buf = new StringBuilder();
             buf.append('[');
             for (int i = 0; i < mCount; i++) {
-                final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
+                final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
                 if (i != 0) {
                     buf.append(", ");
                 }
@@ -1189,5 +1228,9 @@
         public Handler getBackgroundThreadHandler() {
             return BackgroundThread.getHandler();
         }
+
+        Clock createClock() {
+            return SystemClock::uptimeMillis;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index def9685..d0ce9ef 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -37,6 +37,8 @@
  * </p>
  */
 abstract class DisplayDevice {
+    private static final Display.Mode EMPTY_DISPLAY_MODE = new Display.Mode.Builder().build();
+
     private final DisplayAdapter mDisplayAdapter;
     private final IBinder mDisplayToken;
     private final String mUniqueId;
@@ -213,6 +215,13 @@
     public void setUserPreferredDisplayModeLocked(Display.Mode mode) { }
 
     /**
+     * Returns the user preferred display mode.
+     */
+    public Display.Mode getUserPreferredDisplayModeLocked() {
+        return EMPTY_DISPLAY_MODE;
+    }
+
+    /**
      * Sets the requested color mode.
      */
     public void setRequestedColorModeLocked(int colorMode) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a9e1647..3df2422 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -52,6 +52,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -86,6 +87,13 @@
 
     private 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;
+
+    // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
+    private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
+
     private final Context mContext;
 
     // The details of the ambient light sensor associated with this display.
@@ -120,6 +128,8 @@
     private float mBrightnessRampFastIncrease = Float.NaN;
     private float mBrightnessRampSlowDecrease = Float.NaN;
     private float mBrightnessRampSlowIncrease = Float.NaN;
+    private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
+    private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
     private Spline mBrightnessToBacklightSpline;
     private Spline mBacklightToBrightnessSpline;
     private Spline mBacklightToNitsSpline;
@@ -346,6 +356,14 @@
         return mBrightnessRampSlowIncrease;
     }
 
+    public int getAmbientHorizonLong() {
+        return mAmbientHorizonLong;
+    }
+
+    public int getAmbientHorizonShort() {
+        return mAmbientHorizonShort;
+    }
+
     SensorData getAmbientLightSensor() {
         return mAmbientLightSensor;
     }
@@ -405,6 +423,8 @@
                 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
                 + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
                 + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+                + ", mAmbientHorizonLong=" + mAmbientHorizonLong
+                + ", mAmbientHorizonShort=" + mAmbientHorizonShort
                 + ", mAmbientLightSensor=" + mAmbientLightSensor
                 + ", mProximitySensor=" + mProximitySensor
                 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -461,6 +481,7 @@
                 loadBrightnessRamps(config);
                 loadAmbientLightSensorFromDdc(config);
                 loadProxSensorFromDdc(config);
+                loadAmbientHorizonFromDdc(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
@@ -869,6 +890,17 @@
         }
     }
 
+    private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
+        final BigInteger configLongHorizon = config.getAmbientLightHorizonLong();
+        if (configLongHorizon != null) {
+            mAmbientHorizonLong = configLongHorizon.intValue();
+        }
+        final BigInteger configShortHorizon = config.getAmbientLightHorizonShort();
+        if (configShortHorizon != null) {
+            mAmbientHorizonShort = configShortHorizon.intValue();
+        }
+    }
+
     static class SensorData {
         public String type;
         public String name;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index fd4cd8e..35e3db78 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -358,6 +358,12 @@
     public float brightnessMaximum;
     public float brightnessDefault;
 
+    /**
+     * Install orientation of display panel relative to its natural orientation.
+     */
+    @Surface.Rotation
+    public int installOrientation = Surface.ROTATION_0;
+
     public void setAssumedDensityForExternalDisplay(int width, int height) {
         densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
         // Technically, these values should be smaller than the apparent density
@@ -417,7 +423,8 @@
                 || !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
                 || !BrightnessSynchronizer.floatEquals(brightnessDefault,
                 other.brightnessDefault)
-                || !Objects.equals(roundedCorners, other.roundedCorners)) {
+                || !Objects.equals(roundedCorners, other.roundedCorners)
+                || installOrientation != other.installOrientation) {
             diff |= DIFF_OTHER;
         }
         return diff;
@@ -461,6 +468,7 @@
         brightnessMaximum = other.brightnessMaximum;
         brightnessDefault = other.brightnessDefault;
         roundedCorners = other.roundedCorners;
+        installOrientation = other.installOrientation;
     }
 
     // For debugging purposes
@@ -508,6 +516,7 @@
             sb.append(", roundedCorners ").append(roundedCorners);
         }
         sb.append(flagsToString(flags));
+        sb.append(", installOrientation ").append(installOrientation);
         sb.append("}");
         return sb.toString();
     }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4f2b14..3feffc6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -873,11 +873,11 @@
 
     private void updateUserPreferredDisplayModeSettingsLocked() {
         final float refreshRate = Settings.Global.getFloat(mContext.getContentResolver(),
-                Settings.Global.USER_PREFERRED_REFRESH_RATE, 0.0f);
+                Settings.Global.USER_PREFERRED_REFRESH_RATE, Display.INVALID_DISPLAY_REFRESH_RATE);
         final int height = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, -1);
+                Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, Display.INVALID_DISPLAY_HEIGHT);
         final int width = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, -1);
+                Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, Display.INVALID_DISPLAY_WIDTH);
         Display.Mode mode = new Display.Mode(width, height, refreshRate);
         mUserPreferredMode = isResolutionAndRefreshRateValid(mode) ? mode : null;
     }
@@ -1250,6 +1250,14 @@
         }
         final Surface surface = virtualDisplayConfig.getSurface();
         int flags = virtualDisplayConfig.getFlags();
+        if (virtualDevice != null) {
+            final VirtualDeviceManagerInternal vdm =
+                    getLocalService(VirtualDeviceManagerInternal.class);
+            if (!vdm.isValidVirtualDevice(virtualDevice)) {
+                throw new SecurityException("Invalid virtual device");
+            }
+            flags |= vdm.getBaseVirtualDisplayFlags(virtualDevice);
+        }
 
         if (surface != null && surface.isSingleBuffered()) {
             throw new IllegalArgumentException("Surface can't be single-buffered");
@@ -1282,14 +1290,6 @@
             }
         }
 
-        if (virtualDevice != null) {
-            final VirtualDeviceManagerInternal vdm =
-                    getLocalService(VirtualDeviceManagerInternal.class);
-            if (!vdm.isValidVirtualDevice(virtualDevice)) {
-                throw new SecurityException("Invalid virtual device");
-            }
-        }
-
         if (callingUid != Process.SYSTEM_UID
                 && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
             if (!canProjectVideo(projection)) {
@@ -1560,8 +1560,11 @@
         }
         if (mUserPreferredMode != null) {
             device.setUserPreferredDisplayModeLocked(mUserPreferredMode);
+        } else {
+            configurePreferredDisplayModeLocked(display);
         }
         addDisplayPowerControllerLocked(display);
+
         mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
 
         final float brightnessDefault = display.getDisplayInfoLocked().brightnessDefault;
@@ -1688,6 +1691,24 @@
         }
     }
 
+    private void configurePreferredDisplayModeLocked(LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        final Point userPreferredResolution =
+                mPersistentDataStore.getUserPreferredResolution(device);
+        final float refreshRate = mPersistentDataStore.getUserPreferredRefreshRate(device);
+        if (userPreferredResolution == null && Float.isNaN(refreshRate)) {
+            return;
+        }
+        Display.Mode.Builder modeBuilder = new Display.Mode.Builder();
+        if (userPreferredResolution != null) {
+            modeBuilder.setResolution(userPreferredResolution.x, userPreferredResolution.y);
+        }
+        if (!Float.isNaN(refreshRate)) {
+            modeBuilder.setRefreshRate(refreshRate);
+        }
+        device.setUserPreferredDisplayModeLocked(modeBuilder.build());
+    }
+
     // If we've never recorded stable device stats for this device before and they aren't
     // explicitly configured, go ahead and record the stable device stats now based on the status
     // of the default display at first boot.
@@ -1731,36 +1752,79 @@
         return mWideColorSpace.getId();
     }
 
-    void setUserPreferredDisplayModeInternal(Display.Mode mode) {
+    void setUserPreferredDisplayModeInternal(int displayId, Display.Mode mode) {
         synchronized (mSyncRoot) {
-            if (Objects.equals(mUserPreferredMode, mode)) {
+            if (Objects.equals(mUserPreferredMode, mode) && displayId == Display.INVALID_DISPLAY) {
                 return;
             }
 
-            if (mode != null && !isResolutionAndRefreshRateValid(mode)) {
+            if (mode != null && !isResolutionAndRefreshRateValid(mode)
+                    && displayId == Display.INVALID_DISPLAY) {
                 throw new IllegalArgumentException("width, height and refresh rate of mode should "
-                        + "be greater than 0");
+                        + "be greater than 0 when setting the global user preferred display mode.");
             }
-            mUserPreferredMode = mode;
 
-            final int resolutionHeight = mode == null ? -1 : mode.getPhysicalHeight();
-            final int resolutionWidth = mode == null ? -1 : mode.getPhysicalWidth();
-            final float refreshRate = mode == null ? 0.0f : mode.getRefreshRate();
-            Settings.Global.putFloat(mContext.getContentResolver(),
-                    Settings.Global.USER_PREFERRED_REFRESH_RATE, refreshRate);
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, resolutionHeight);
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
-            mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
-                device.setUserPreferredDisplayModeLocked(mode);
-            });
+            final int resolutionHeight = mode == null ? Display.INVALID_DISPLAY_HEIGHT
+                    : mode.getPhysicalHeight();
+            final int resolutionWidth = mode == null ? Display.INVALID_DISPLAY_WIDTH
+                    : mode.getPhysicalWidth();
+            final float refreshRate = mode == null ? Display.INVALID_DISPLAY_REFRESH_RATE
+                    : mode.getRefreshRate();
+
+            storeModeInPersistentDataStoreLocked(
+                    displayId, resolutionWidth, resolutionHeight, refreshRate);
+            if (displayId != Display.INVALID_DISPLAY) {
+                setUserPreferredModeForDisplayLocked(displayId, mode);
+            } else {
+                mUserPreferredMode = mode;
+                storeModeInGlobalSettingsLocked(
+                        resolutionWidth, resolutionHeight, refreshRate, mode);
+            }
         }
     }
 
-    private Display.Mode getUserPreferredDisplayModeInternal() {
+    private void storeModeInPersistentDataStoreLocked(int displayId, int resolutionWidth,
+            int resolutionHeight, float refreshRate) {
+        DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId);
+        if (displayDevice == null) {
+            return;
+        }
+        mPersistentDataStore.setUserPreferredResolution(
+                displayDevice, resolutionWidth, resolutionHeight);
+        mPersistentDataStore.setUserPreferredRefreshRate(displayDevice, refreshRate);
+    }
+
+    private void setUserPreferredModeForDisplayLocked(int displayId, Display.Mode mode) {
+        DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId);
+        if (displayDevice == null) {
+            return;
+        }
+        displayDevice.setUserPreferredDisplayModeLocked(mode);
+    }
+
+    private void storeModeInGlobalSettingsLocked(
+            int resolutionWidth, int resolutionHeight, float refreshRate, Display.Mode mode) {
+        Settings.Global.putFloat(mContext.getContentResolver(),
+                Settings.Global.USER_PREFERRED_REFRESH_RATE, refreshRate);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, resolutionHeight);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
+        mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
+            device.setUserPreferredDisplayModeLocked(mode);
+        });
+    }
+
+    Display.Mode getUserPreferredDisplayModeInternal(int displayId) {
         synchronized (mSyncRoot) {
-            return mUserPreferredMode;
+            if (displayId == Display.INVALID_DISPLAY) {
+                return mUserPreferredMode;
+            }
+            DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId);
+            if (displayDevice == null) {
+                return null;
+            }
+            return displayDevice.getUserPreferredDisplayModeLocked();
         }
     }
 
@@ -1781,6 +1845,14 @@
         return mDisplayModeDirector.getModeSwitchingType();
     }
 
+    private boolean getDisplayDecorationSupportInternal(int displayId) {
+        final IBinder displayToken = getDisplayToken(displayId);
+        if (null == displayToken) {
+            return false;
+        }
+        return SurfaceControl.getDisplayDecorationSupport(displayToken);
+    }
+
     private void setBrightnessConfigurationForDisplayInternal(
             @Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId,
             String packageName) {
@@ -2365,7 +2437,7 @@
             pw.println("  mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
 
             if (mUserPreferredMode != null) {
-                pw.println(mUserPreferredMode.toString());
+                pw.println(mUserPreferredMode);
             }
 
             pw.println();
@@ -3371,23 +3443,23 @@
         }
 
         @Override // Binder call
-        public void setUserPreferredDisplayMode(Display.Mode mode) {
+        public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE,
                     "Permission required to set the user preferred display mode.");
             final long token = Binder.clearCallingIdentity();
             try {
-                setUserPreferredDisplayModeInternal(mode);
+                setUserPreferredDisplayModeInternal(displayId, mode);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override // Binder call
-        public Display.Mode getUserPreferredDisplayMode() {
+        public Display.Mode getUserPreferredDisplayMode(int displayId) {
             final long token = Binder.clearCallingIdentity();
             try {
-                return getUserPreferredDisplayModeInternal();
+                return getUserPreferredDisplayModeInternal(displayId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -3441,6 +3513,16 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override // Binder call
+        public boolean getDisplayDecorationSupport(int displayId) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getDisplayDecorationSupportInternal(displayId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     private static boolean isValidBrightness(float brightness) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9a7ddcb..a9a1f08 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -113,13 +113,18 @@
         pw.println("  constrain-launcher-metrics [true|false]");
         pw.println("    Sets if Display#getRealSize and getRealMetrics should be constrained for ");
         pw.println("    Launcher.");
-        pw.println("  set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE");
+        pw.println("  set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE "
+                + "DISPLAY_ID (optional)");
         pw.println("    Sets the user preferred display mode which has fields WIDTH, HEIGHT and "
-                + "REFRESH-RATE");
-        pw.println("  clear-user-preferred-display-mode");
-        pw.println("    Clears the user preferred display mode");
-        pw.println("  get-user-preferred-display-mode");
-        pw.println("    Returns the user preferred display mode or null id no mode is set by user");
+                + "REFRESH-RATE. If DISPLAY_ID is passed, the mode change is applied to display"
+                + "with id = DISPLAY_ID, else mode change is applied globally.");
+        pw.println("  clear-user-preferred-display-mode DISPLAY_ID (optional)");
+        pw.println("    Clears the user preferred display mode. If DISPLAY_ID is passed, the mode"
+                + " is cleared for  display with id = DISPLAY_ID, else mode is cleared globally.");
+        pw.println("  get-user-preferred-display-mode DISPLAY_ID (optional)");
+        pw.println("    Returns the user preferred display mode or null if no mode is set by user."
+                + "If DISPLAY_ID is passed, the mode for display with id = DISPLAY_ID is "
+                + "returned, else global display mode is returned.");
         pw.println("  set-match-content-frame-rate-pref PREFERENCE");
         pw.println("    Sets the match content frame rate preference as PREFERENCE ");
         pw.println("  get-match-content-frame-rate-pref");
@@ -235,28 +240,54 @@
             getErrPrintWriter().println("Error: invalid format of width, height or refresh rate");
             return 1;
         }
-        if (width < 0 || height < 0 || refreshRate <= 0.0f) {
-            getErrPrintWriter().println("Error: invalid value of width, height or refresh rate");
+        if ((width < 0 || height < 0) && refreshRate <= 0.0f) {
+            getErrPrintWriter().println("Error: invalid value of resolution (width, height)"
+                    + " and refresh rate");
             return 1;
         }
 
-        final Context context = mService.getContext();
-        final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        dm.setUserPreferredDisplayMode(new Display.Mode(width, height, refreshRate));
+        final String displayIdText = getNextArg();
+        int displayId = Display.INVALID_DISPLAY;
+        if (displayIdText != null) {
+            try {
+                displayId = Integer.parseInt(displayIdText);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: invalid format of display ID");
+                return 1;
+            }
+        }
+        mService.setUserPreferredDisplayModeInternal(
+                displayId, new Display.Mode(width, height, refreshRate));
         return 0;
     }
 
     private int clearUserPreferredDisplayMode() {
-        final Context context = mService.getContext();
-        final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        dm.clearUserPreferredDisplayMode();
+        final String displayIdText = getNextArg();
+        int displayId = Display.INVALID_DISPLAY;
+        if (displayIdText != null) {
+            try {
+                displayId = Integer.parseInt(displayIdText);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: invalid format of display ID");
+                return 1;
+            }
+        }
+        mService.setUserPreferredDisplayModeInternal(displayId, null);
         return 0;
     }
 
     private int getUserPreferredDisplayMode() {
-        final Context context = mService.getContext();
-        final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        final Display.Mode mode =  dm.getUserPreferredDisplayMode();
+        final String displayIdText = getNextArg();
+        int displayId = Display.INVALID_DISPLAY;
+        if (displayIdText != null) {
+            try {
+                displayId = Integer.parseInt(displayIdText);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: invalid format of display ID");
+                return 1;
+            }
+        }
+        final Display.Mode mode =  mService.getUserPreferredDisplayModeInternal(displayId);
         if (mode == null) {
             getOutPrintWriter().println("User preferred display mode: null");
             return 0;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2dfaa8b..c6d3829 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -60,6 +60,7 @@
 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.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -125,6 +126,7 @@
     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_STATSD_HBM_BRIGHTNESS = 12;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -134,6 +136,8 @@
     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;
 
@@ -354,6 +358,9 @@
     private float mBrightnessRampRateSlowDecrease;
     private float mBrightnessRampRateSlowIncrease;
 
+    // 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;
@@ -464,6 +471,7 @@
         TAG = "DisplayPowerController[" + mDisplayId + "]";
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+        mDisplayStatsId = mUniqueDisplayId.hashCode();
         mHandler = new DisplayControllerHandler(handler.getLooper());
 
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
@@ -761,6 +769,7 @@
             if (mDisplayDevice != device) {
                 mDisplayDevice = device;
                 mUniqueDisplayId = uniqueId;
+                mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
                 loadFromDisplayDeviceConfig(token, info);
                 updatePowerState();
@@ -814,7 +823,7 @@
         loadNitsRange(mContext.getResources());
         setUpAutoBrightness(mContext.getResources(), mHandler);
         reloadReduceBrightColours();
-        mHbmController.resetHbmData(info.width, info.height, token,
+        mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData());
     }
 
@@ -946,7 +955,9 @@
                     lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
                     darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
                     ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
-                    mHbmController, mIdleModeBrightnessMapper);
+                    mHbmController, mIdleModeBrightnessMapper,
+                    mDisplayDeviceConfig.getAmbientHorizonShort(),
+                    mDisplayDeviceConfig.getAmbientHorizonLong());
         } else {
             mUseSoftwareAutoBrightnessConfig = false;
         }
@@ -1002,7 +1013,15 @@
         }
     };
 
-    private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState;
+    private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
+        @Override
+        public void onAnimationEnd() {
+            sendUpdatePowerState();
+
+            final float brightness = mPowerState.getScreenBrightness();
+            reportStats(brightness);
+        }
+    };
 
     /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
     private void cleanupHandlerThreadAfterStop() {
@@ -1177,6 +1196,13 @@
                     && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                     && Float.isNaN(brightnessState)
                     && mAutomaticBrightnessController != null;
+        final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
+                    && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+        final int autoBrightnessState = autoBrightnessEnabled
+                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
+                : autoBrightnessDisabledDueToDisplayOff
+                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+                : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
 
@@ -1227,7 +1253,7 @@
         // Configure auto-brightness.
         if (mAutomaticBrightnessController != null) {
             hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
-            mAutomaticBrightnessController.configure(autoBrightnessEnabled,
+            mAutomaticBrightnessController.configure(autoBrightnessState,
                     mBrightnessConfiguration,
                     mLastUserSetScreenBrightness,
                     userSetBrightnessChanged, autoBrightnessAdjustment,
@@ -1600,11 +1626,13 @@
         final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
         final IBinder displayToken =
                 mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
+        final String displayUniqueId =
+                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
         final DisplayDeviceConfig.HighBrightnessModeData hbmData =
                 ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
+                displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
                 () -> {
                     sendUpdatePowerStateLocked();
                     mHandler.post(mOnBrightnessChangeRunnable);
@@ -2435,6 +2463,39 @@
         }
     }
 
+    private void reportStats(float brightness) {
+        float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
+        synchronized(mCachedBrightnessInfo) {
+            hbmTransitionPoint = mCachedBrightnessInfo.highBrightnessTransitionPoint;
+        }
+
+        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.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS);
+            }
+        }
+    }
+
+    private final void logHbmBrightnessStats(float brightness, int displayStatsId) {
+        synchronized (mHandler) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
+        }
+    }
+
     private final class DisplayControllerHandler extends Handler {
         public DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -2499,6 +2560,10 @@
                     final int justActivated = msg.arg2;
                     handleRbcChanged(strengthChanged == 1, justActivated == 1);
                     break;
+
+                case MSG_STATSD_HBM_BRIGHTNESS:
+                    logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 1e1cfeb..b3be894b 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,6 +37,7 @@
 import android.view.SurfaceControlHdrLayerInfoListener;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
 import com.android.server.display.DisplayManagerService.Clock;
 
@@ -80,6 +81,7 @@
     private boolean mIsInAllowedAmbientRange = false;
     private boolean mIsTimeAvailable = false;
     private boolean mIsAutoBrightnessEnabled = false;
+    private boolean mIsAutoBrightnessOffByState = false;
     private float mBrightness;
     private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
     private boolean mIsHdrLayerPresent = false;
@@ -88,6 +90,8 @@
     private int mWidth;
     private int mHeight;
     private float mAmbientLux;
+    private int mDisplayStatsId;
+    private int mHbmStatsState = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
 
     /**
      * If HBM is currently running, this is the start time for the current HBM session.
@@ -102,15 +106,15 @@
     private LinkedList<HbmEvent> mEvents = new LinkedList<>();
 
     HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
-            float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
-            Runnable hbmChangeCallback, Context context) {
-        this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax,
-                hbmData, hbmChangeCallback, context);
+            String displayUniqueId, float brightnessMin, float brightnessMax,
+            HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) {
+        this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
+            brightnessMax, hbmData, hbmChangeCallback, context);
     }
 
     @VisibleForTesting
     HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
-            IBinder displayToken, float brightnessMin, float brightnessMax,
+            IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
             HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
             Context context) {
         mInjector = injector;
@@ -126,10 +130,13 @@
         mRecalcRunnable = this::recalculateTimeAllowance;
         mHdrListener = new HdrListener();
 
-        resetHbmData(width, height, displayToken, hbmData);
+        resetHbmData(width, height, displayToken, displayUniqueId, hbmData);
     }
 
-    void setAutoBrightnessEnabled(boolean isEnabled) {
+    void setAutoBrightnessEnabled(int state) {
+        final boolean isEnabled = state == AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+        mIsAutoBrightnessOffByState =
+                state == AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
         if (!deviceSupportsHbm() || isEnabled == mIsAutoBrightnessEnabled) {
             return;
         }
@@ -231,10 +238,12 @@
         mSettingsObserver.stopObserving();
     }
 
-    void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData) {
+    void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
+            HighBrightnessModeData hbmData) {
         mWidth = width;
         mHeight = height;
         mHbmData = hbmData;
+        mDisplayStatsId = displayUniqueId.hashCode();
 
         unregisterHdrListener();
         mSkinThermalStatusObserver.stopObserving();
@@ -270,11 +279,13 @@
         pw.println("  mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode)
                 + (mHbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
                 ? "(" + getHdrBrightnessValue() + ")" : ""));
+        pw.println("  mHbmStatsState=" + hbmStatsStateToString(mHbmStatsState));
         pw.println("  mHbmData=" + mHbmData);
         pw.println("  mAmbientLux=" + mAmbientLux
                 + (mIsAutoBrightnessEnabled ? "" : " (old/invalid)"));
         pw.println("  mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange);
         pw.println("  mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled);
+        pw.println("  mIsAutoBrightnessOffByState=" + mIsAutoBrightnessOffByState);
         pw.println("  mIsHdrLayerPresent=" + mIsHdrLayerPresent);
         pw.println("  mBrightnessMin=" + mBrightnessMin);
         pw.println("  mBrightnessMax=" + mBrightnessMax);
@@ -435,12 +446,71 @@
 
     private void updateHbmMode() {
         int newHbmMode = calculateHighBrightnessMode();
+        updateHbmStats(mHbmMode, newHbmMode);
         if (mHbmMode != newHbmMode) {
             mHbmMode = newHbmMode;
             mHbmChangeCallback.run();
         }
     }
 
+    private void updateHbmStats(int mode, int newMode) {
+        int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
+        if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+                && getHdrBrightnessValue() > mHbmData.transitionPoint) {
+            state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR;
+        } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+            state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT;
+        }
+        if (state == mHbmStatsState) {
+            return;
+        }
+        mHbmStatsState = state;
+
+        int reason =
+                FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN;
+        boolean oldHbmSv = (mode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+        boolean newHbmSv = (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+        if (oldHbmSv && !newHbmSv) {
+            // If more than one conditions are flipped and turn off HBM sunlight
+            // visibility, only one condition will be reported to make it simple.
+            if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF;
+            } else if (!mIsAutoBrightnessEnabled) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_AUTOBRIGHTNESS_OFF;
+            } else if (!mIsInAllowedAmbientRange) {
+                reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP;
+            } else if (!mIsTimeAvailable) {
+                reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT;
+            } else if (!mIsThermalStatusWithinLimit) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT;
+            } else if (mIsHdrLayerPresent) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING;
+            } else if (mIsBlockedByLowPowerMode) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON;
+            }
+        }
+
+        mInjector.reportHbmStateChange(mDisplayStatsId, state, reason);
+    }
+
+    private String hbmStatsStateToString(int hbmStatsState) {
+        switch (hbmStatsState) {
+        case FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF:
+            return "HBM_OFF";
+        case FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR:
+            return "HBM_ON_HDR";
+        case FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT:
+            return "HBM_ON_SUNLIGHT";
+        default:
+            return String.valueOf(hbmStatsState);
+        }
+    }
+
     private int calculateHighBrightnessMode() {
         if (!deviceSupportsHbm()) {
             return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
@@ -642,5 +712,10 @@
             return IThermalService.Stub.asInterface(
                     ServiceManager.getService(Context.THERMAL_SERVICE));
         }
+
+        public void reportHbmStateChange(int display, int state, int reason) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED, display, state, reason);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 300f59e..3a9ef0a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -641,6 +641,7 @@
 
                 mInfo.roundedCorners = RoundedCorners.fromResources(
                         res, mInfo.uniqueId, mInfo.width, mInfo.height);
+                mInfo.installOrientation = mStaticDisplayInfo.installOrientation;
 
                 if (mStaticDisplayInfo.isInternal) {
                     mInfo.type = Display.TYPE_INTERNAL;
@@ -847,6 +848,10 @@
         public void setUserPreferredDisplayModeLocked(Display.Mode mode) {
             final int oldModeId = getPreferredModeId();
             mUserPreferredMode = mode;
+            if (mode != null && (mode.isRefreshRateSet() ^ mode.isResolutionSet())) {
+                mUserPreferredMode = findMode(mode.getPhysicalWidth(),
+                        mode.getPhysicalHeight(), mode.getRefreshRate());
+            }
             mUserPreferredModeId = findUserPreferredModeIdLocked(mode);
 
             if (oldModeId != getPreferredModeId()) {
@@ -855,6 +860,11 @@
         }
 
         @Override
+        public Display.Mode getUserPreferredDisplayModeLocked() {
+            return mUserPreferredMode;
+        }
+
+        @Override
         public void setRequestedColorModeLocked(int colorMode) {
             requestColorModeLocked(colorMode);
         }
@@ -1062,6 +1072,18 @@
             return matchingModeId;
         }
 
+       // Returns a mode with resolution (width, height) and/or refreshRate. If any one of the
+       // resolution or refresh-rate is valid, a mode having the valid parameters is returned.
+        private Display.Mode findMode(int width, int height, float refreshRate) {
+            for (int i = 0; i < mSupportedModes.size(); i++) {
+                Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode;
+                if (supportedMode.matchesIfValid(width, height, refreshRate)) {
+                    return supportedMode;
+                }
+            }
+            return null;
+        }
+
         private int findUserPreferredModeIdLocked(Display.Mode userPreferredMode) {
             if (userPreferredMode != null) {
                 for (int i = 0; i < mSupportedModes.size(); i++) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 4d1367a3..e3ecf49 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -429,6 +429,7 @@
             mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
             mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
             mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
+            mBaseDisplayInfo.installOrientation = deviceInfo.installOrientation;
             mPrimaryDisplayDeviceInfo = deviceInfo;
             mInfo.set(null);
         }
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 7719dfe..93c73be 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -397,12 +397,16 @@
                 // We already told the displays to turn off, now we need to wake the device as
                 // we transition to this new state. We do it here so that the waking happens
                 // between the transition from one layout to another.
-                mPowerManager.wakeUp(SystemClock.uptimeMillis(),
-                        PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
+                mHandler.post(() -> {
+                    mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+                            PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
+                });
             } else if (sleepDevice) {
                 // Send the device to sleep when required.
-                mPowerManager.goToSleep(SystemClock.uptimeMillis(),
-                        PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, 0);
+                mHandler.post(() -> {
+                    mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+                            PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, 0);
+                });
             }
         }
 
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 4b0d43b..2eba080 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -291,6 +291,54 @@
         return false;
     }
 
+    public boolean setUserPreferredRefreshRate(DisplayDevice displayDevice, float refreshRate) {
+        final String displayDeviceUniqueId = displayDevice.getUniqueId();
+        if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+            return false;
+        }
+        DisplayState state = getDisplayState(displayDevice.getUniqueId(), true);
+        if (state.setRefreshRate(refreshRate)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public float getUserPreferredRefreshRate(DisplayDevice device) {
+        if (device == null || !device.hasStableUniqueId()) {
+            return Float.NaN;
+        }
+        final DisplayState state = getDisplayState(device.getUniqueId(), false);
+        if (state == null) {
+            return Float.NaN;
+        }
+        return state.getRefreshRate();
+    }
+
+    public boolean setUserPreferredResolution(DisplayDevice displayDevice, int width, int height) {
+        final String displayDeviceUniqueId = displayDevice.getUniqueId();
+        if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+            return false;
+        }
+        DisplayState state = getDisplayState(displayDevice.getUniqueId(), true);
+        if (state.setResolution(width, height)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public Point getUserPreferredResolution(DisplayDevice displayDevice) {
+        if (displayDevice == null || !displayDevice.hasStableUniqueId()) {
+            return null;
+        }
+        final DisplayState state = getDisplayState(displayDevice.getUniqueId(), false);
+        if (state == null) {
+            return null;
+        }
+        return state.getResolution();
+    }
+
     public Point getStableDisplaySize() {
         loadIfNeeded();
         return mStableDeviceValues.getDisplaySize();
@@ -536,6 +584,9 @@
     private static final class DisplayState {
         private int mColorMode;
         private float mBrightness;
+        private int mWidth;
+        private int mHeight;
+        private float mRefreshRate;
 
         // Brightness configuration by user
         private BrightnessConfigurations mDisplayBrightnessConfigurations =
@@ -576,6 +627,31 @@
             return mDisplayBrightnessConfigurations.mConfigurations.get(userSerial);
         }
 
+        public boolean setResolution(int width, int height) {
+            if (width == mWidth && height == mHeight) {
+                return false;
+            }
+            mWidth = width;
+            mHeight = height;
+            return true;
+        }
+
+        public Point getResolution() {
+            return new Point(mWidth, mHeight);
+        }
+
+        public boolean setRefreshRate(float refreshRate) {
+            if (refreshRate == mRefreshRate) {
+                return false;
+            }
+            mRefreshRate = refreshRate;
+            return true;
+        }
+
+        public float getRefreshRate() {
+            return mRefreshRate;
+        }
+
         public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index d2baaf22..d632ee3 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -147,7 +147,8 @@
                         getContext(), getHandler(), mWifiDisplayListener);
 
                 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                        new IntentFilter(ACTION_DISCONNECT), null, mHandler);
+                        new IntentFilter(ACTION_DISCONNECT), null, mHandler,
+                        Context.RECEIVER_NOT_EXPORTED);
             }
         });
     }
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 58308d8..751f2db 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -81,7 +81,7 @@
     public static final int ADDR_BROADCAST = 15;
 
     /** Logical address used to indicate it is not initialized or invalid. */
-    public static final int ADDR_INVALID = -1;
+    public static final int ADDR_INVALID = HdmiDeviceInfo.ADDR_INVALID;
 
     /** Logical address used to indicate the source comes from internal device. */
     public static final int ADDR_INTERNAL = HdmiDeviceInfo.ADDR_INTERNAL;
@@ -199,6 +199,7 @@
     static final int MESSAGE_SET_SYSTEM_AUDIO_MODE = 0x72;
     static final int MESSAGE_REPORT_AUDIO_STATUS = 0x7A;
     static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D;
+    static final int MESSAGE_SET_AUDIO_VOLUME_LEVEL = 0x73;
     static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 0x7E;
     static final int MESSAGE_ROUTING_CHANGE = 0x80;
     static final int MESSAGE_ROUTING_INFORMATION = 0x81;
@@ -243,7 +244,7 @@
     static final int MESSAGE_CDC_MESSAGE = 0xF8;
     static final int MESSAGE_ABORT = 0xFF;
 
-    static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+    static final int VENDOR_ID_UNKNOWN = HdmiDeviceInfo.VENDOR_ID_UNKNOWN;
 
     static final int TRUE = 1;
     static final int FALSE = 0;
@@ -389,6 +390,17 @@
 
     static final int UNKNOWN_VOLUME = -1;
 
+    // This constant is used in two operands in the CEC spec.
+    //
+    // CEC 1.4: [Audio Volume Status] (part of [Audio Status]) - operand for <Report Audio Status>
+    // Indicates that the current audio volume status is unknown.
+    //
+    // CEC 2.1a: [Audio Volume Level] - operand for <Set Audio Volume Level>
+    // Part of the Absolute Volume Control feature. Indicates that no change shall be made to the
+    // volume level of the recipient. This allows <Set Audio Volume Level> to be sent to determine
+    // whether the recipient supports Absolute Volume Control.
+    static final int AUDIO_VOLUME_STATUS_UNKNOWN = 0x7F;
+
     // States of property PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON
     // to decide if turn on the system audio control when power on the device
     @IntDef({
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 8980de1..e827866 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -77,7 +77,7 @@
 
         private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
         private int mPortId = Constants.INVALID_PORT_ID;
-        private int mVendorId = Constants.UNKNOWN_VENDOR_ID;
+        private int mVendorId = Constants.VENDOR_ID_UNKNOWN;
         private int mPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
         private String mDisplayName = "";
         private int mDeviceType = HdmiDeviceInfo.DEVICE_INACTIVE;
@@ -87,8 +87,15 @@
         }
 
         private HdmiDeviceInfo toHdmiDeviceInfo() {
-            return new HdmiDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType,
-                    mVendorId, mDisplayName, mPowerStatus);
+            return  HdmiDeviceInfo.cecDeviceBuilder()
+                    .setLogicalAddress(mLogicalAddress)
+                    .setPhysicalAddress(mPhysicalAddress)
+                    .setPortId(mPortId)
+                    .setVendorId(mVendorId)
+                    .setDeviceType(mDeviceType)
+                    .setDisplayName(mDisplayName)
+                    .setDevicePowerStatus(mPowerStatus)
+                    .build();
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java
new file mode 100644
index 0000000..4542565
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Sends <Give Features> to a target device. This action succeeds if the device responds with
+ * <Report Features> within {@link HdmiConfig.TIMEOUT_MS}.
+ *
+ * This action does not update the CEC network directly; an incoming <Report Features> message
+ * should be handled separately by {@link HdmiCecNetwork}.
+ */
+public class GiveFeaturesAction extends HdmiCecFeatureAction {
+    private static final String TAG = "GiveFeaturesAction";
+
+    private static final int STATE_WAITING_FOR_REPORT_FEATURES = 1;
+
+    private final int mTargetAddress;
+
+    public GiveFeaturesAction(HdmiCecLocalDevice source, int targetAddress,
+            IHdmiControlCallback callback) {
+        super(source, callback);
+
+        mTargetAddress = targetAddress;
+    }
+
+    boolean start() {
+        sendCommand(HdmiCecMessageBuilder.buildGiveFeatures(getSourceAddress(), mTargetAddress),
+                result -> {
+                    if (result == SendMessageResult.SUCCESS) {
+                        mState = STATE_WAITING_FOR_REPORT_FEATURES;
+                        addTimer(STATE_WAITING_FOR_REPORT_FEATURES, HdmiConfig.TIMEOUT_MS);
+                    } else {
+                        finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+                    }
+                });
+        return true;
+    }
+
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_REPORT_FEATURES) {
+            return false;
+        }
+        if (cmd instanceof ReportFeaturesMessage) {
+            return handleReportFeatures((ReportFeaturesMessage) cmd);
+        }
+        return false;
+    }
+
+    private boolean handleReportFeatures(ReportFeaturesMessage cmd) {
+        if (cmd.getSource() == mTargetAddress) {
+            // No need to update the network, since it should already have processed this message.
+            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+            return true;
+        }
+        return false;
+    }
+
+    void handleTimerEvent(int state) {
+        finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index cc86430..1a568c3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -47,6 +47,7 @@
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
@@ -694,7 +695,19 @@
     @ServiceThreadOnly
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
         assertRunOnServiceThread();
-        HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+
+        if (body.length == 0) {
+            Slog.e(TAG, "Message with empty body received.");
+            return;
+        }
+
+        HdmiCecMessage command = HdmiCecMessage.build(srcAddress, dstAddress, body[0],
+                Arrays.copyOfRange(body, 1, body.length));
+
+        if (command.getValidationResult() != HdmiCecMessageValidator.OK) {
+            Slog.e(TAG, "Invalid message received: " + command);
+        }
+
         HdmiLogger.debug("[R]:" + command);
         addCecMessageToHistory(true /* isReceived */, command);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3aa2f42..c674ffe 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.CallSuper;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -634,7 +635,34 @@
 
     protected abstract List<Integer> getRcFeatures();
 
-    protected abstract List<Integer> getDeviceFeatures();
+    /**
+     * Computes the set of supported device features. To update local state with changes in
+     * the set of supported device features, use {@link #getDeviceFeatures} instead.
+     */
+    protected DeviceFeatures computeDeviceFeatures() {
+        return DeviceFeatures.NO_FEATURES_SUPPORTED;
+    }
+
+    /**
+     * Computes the set of supported device features, and updates local state to match.
+     */
+    private void updateDeviceFeatures() {
+        synchronized (mLock) {
+            setDeviceInfo(getDeviceInfo().toBuilder()
+                    .setDeviceFeatures(computeDeviceFeatures())
+                    .build());
+        }
+    }
+
+    /**
+     * Computes and returns the set of supported device features. Updates local state to match.
+     */
+    protected final DeviceFeatures getDeviceFeatures() {
+        updateDeviceFeatures();
+        synchronized (mLock) {
+            return getDeviceInfo().getDeviceFeatures();
+        }
+    }
 
     @Constants.HandleMessageResult
     protected int handleGiveFeatures(HdmiCecMessage message) {
@@ -655,11 +683,17 @@
 
         int rcProfile = getRcProfile();
         List<Integer> rcFeatures = getRcFeatures();
-        List<Integer> deviceFeatures = getDeviceFeatures();
+        DeviceFeatures deviceFeatures = getDeviceFeatures();
+
+
+        int logicalAddress;
+        synchronized (mLock) {
+            logicalAddress = mDeviceInfo.getLogicalAddress();
+        }
 
         mService.sendCecCommand(
-                HdmiCecMessageBuilder.buildReportFeatures(
-                        mDeviceInfo.getLogicalAddress(),
+                ReportFeaturesMessage.build(
+                        logicalAddress,
                         mService.getCecVersion(),
                         localDeviceTypes,
                         rcProfile,
@@ -922,6 +956,7 @@
     final void handleAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
         mPreferredAddress = logicalAddress;
+        updateDeviceFeatures();
         if (mService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
             reportFeatures();
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 7e71589..2ef3ebf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+
 import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
 import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
 import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
@@ -22,6 +25,7 @@
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -177,14 +181,12 @@
     }
 
     @Override
-    protected List<Integer> getDeviceFeatures() {
-        List<Integer> deviceFeatures = new ArrayList<>();
+    protected DeviceFeatures computeDeviceFeatures() {
+        boolean arcSupport = SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true);
 
-        if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
-            deviceFeatures.add(Constants.DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX);
-        }
-
-        return deviceFeatures;
+        return DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                .setArcRxSupport(arcSupport ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED)
+                .build();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 4f55249..90b4f76 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -28,8 +28,6 @@
 import com.android.server.hdmi.Constants.LocalActivePort;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
-import com.google.android.collect.Lists;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -358,11 +356,6 @@
         return features;
     }
 
-    @Override
-    protected List<Integer> getDeviceFeatures() {
-        return Lists.newArrayList();
-    }
-
     // Active source claiming needs to be handled in Service
     // since service can decide who will be the active source when the device supports
     // multiple device types in this method.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c2ed24a..afcd3dd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,6 +16,8 @@
 
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
 import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_CEC_DISABLE;
 import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION;
 import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE;
@@ -31,6 +33,7 @@
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
 
 import android.annotation.Nullable;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -346,7 +349,7 @@
                 if (info == null) {
                     // No CEC/MHL device is present at the port. Attempt to switch to
                     // the hardware port itself for non-CEC devices that may be connected.
-                    info = new HdmiDeviceInfo(path, getActivePortId());
+                    info = HdmiDeviceInfo.hardwarePort(path, getActivePortId());
                 }
             }
             mService.invokeInputChangeListener(info);
@@ -1548,9 +1551,7 @@
     }
 
     @Override
-    protected List<Integer> getDeviceFeatures() {
-        List<Integer> deviceFeatures = new ArrayList<>();
-
+    protected DeviceFeatures computeDeviceFeatures() {
         boolean hasArcPort = false;
         List<HdmiPortInfo> ports = mService.getPortInfo();
         for (HdmiPortInfo port : ports) {
@@ -1559,11 +1560,11 @@
                 break;
             }
         }
-        if (hasArcPort) {
-            deviceFeatures.add(Constants.DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX);
-        }
-        deviceFeatures.add(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN);
-        return deviceFeatures;
+
+        return DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_SUPPORTED)
+                .setArcTxSupport(hasArcPort ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED)
+                .build();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index e3292a3..290cae50 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -16,6 +16,8 @@
 
 package com.android.server.hdmi;
 
+import static com.android.server.hdmi.HdmiCecMessageValidator.ValidationResult;
+
 import android.annotation.Nullable;
 
 import com.android.server.hdmi.Constants.FeatureOpcode;
@@ -26,11 +28,13 @@
 import java.util.Objects;
 
 /**
- * A class to encapsulate HDMI-CEC message used for the devices connected via
- * HDMI cable to communicate with one another. A message is defined by its
- * source and destination address, command (or opcode), and optional parameters.
+ * Encapsulates the data that defines an HDMI-CEC message: source and destination address,
+ * command (or opcode), and optional parameters. Also stores the result of validating the message.
+ *
+ * Subclasses of this class represent specific messages that have been validated, and expose their
+ * parsed parameters.
  */
-public final class HdmiCecMessage {
+public class HdmiCecMessage {
     public static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
 
     private final int mSource;
@@ -39,24 +43,60 @@
     private final int mOpcode;
     private final byte[] mParams;
 
+    private final int mValidationResult;
+
     /**
-     * Constructor.
+     * Constructor that allows the caller to provide the validation result.
+     * Must only be called by subclasses; other callers should use {@link #build}.
      */
-    public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+    protected HdmiCecMessage(int source, int destination, int opcode, byte[] params,
+            @ValidationResult int validationResult) {
         mSource = source;
         mDestination = destination;
         mOpcode = opcode & 0xFF;
         mParams = Arrays.copyOf(params, params.length);
+        mValidationResult = validationResult;
+    }
+
+    private HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+        this(source, destination, opcode, params,
+                HdmiCecMessageValidator.validate(source, destination, opcode & 0xFF, params));
+    }
+
+    /**
+     * Constructs and validates a message. The result of validation will be accessible via
+     * {@link #getValidationResult}.
+     *
+     * Intended for parsing incoming messages, as it takes raw bytes as message parameters.
+     *
+     * If the opcode has its own subclass of this one, this method will instead validate and build
+     * the message using the logic in that class. If successful, it will return a validated
+     * instance of that class that exposes parsed parameters.
+     */
+    static HdmiCecMessage build(int source, int destination, int opcode, byte[] params) {
+        switch (opcode & 0xFF) {
+            case Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL:
+                return SetAudioVolumeLevelMessage.build(source, destination, params);
+            case Constants.MESSAGE_REPORT_FEATURES:
+                return ReportFeaturesMessage.build(source, destination, params);
+            default:
+                return new HdmiCecMessage(source, destination, opcode & 0xFF, params);
+        }
+    }
+
+    static HdmiCecMessage build(int source, int destination, int opcode) {
+        return new HdmiCecMessage(source, destination, opcode, EMPTY_PARAM);
     }
 
     @Override
     public boolean equals(@Nullable Object message) {
         if (message instanceof HdmiCecMessage) {
             HdmiCecMessage that = (HdmiCecMessage) message;
-            return this.mSource == that.getSource() &&
-                this.mDestination == that.getDestination() &&
-                this.mOpcode == that.getOpcode() &&
-                Arrays.equals(this.mParams, that.getParams());
+            return this.mSource == that.getSource()
+                    && this.mDestination == that.getDestination()
+                    && this.mOpcode == that.getOpcode()
+                    && Arrays.equals(this.mParams, that.getParams())
+                    && this.mValidationResult == that.getValidationResult();
         }
         return false;
     }
@@ -111,6 +151,13 @@
         return mParams;
     }
 
+    /**
+     * Returns the validation result of the message.
+     */
+    public int getValidationResult() {
+        return mValidationResult;
+    }
+
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();
@@ -129,9 +176,30 @@
                 }
             }
         }
+        if (mValidationResult != HdmiCecMessageValidator.OK) {
+            s.append(String.format(" <Validation error: %s>",
+                    validationResultToString(mValidationResult)));
+        }
         return s.toString();
     }
 
+    private static String validationResultToString(@ValidationResult int validationResult) {
+        switch (validationResult) {
+            case HdmiCecMessageValidator.OK:
+                return "ok";
+            case HdmiCecMessageValidator.ERROR_SOURCE:
+                return "invalid source";
+            case HdmiCecMessageValidator.ERROR_DESTINATION:
+                return "invalid destination";
+            case HdmiCecMessageValidator.ERROR_PARAMETER:
+                return "invalid parameters";
+            case HdmiCecMessageValidator.ERROR_PARAMETER_SHORT:
+                return "short parameters";
+            default:
+                return "unknown error";
+        }
+    }
+
     private static String opcodeToString(@FeatureOpcode int opcode) {
         switch (opcode) {
             case Constants.MESSAGE_FEATURE_ABORT:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 1c8f21f..adcff4c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -16,17 +16,15 @@
 
 package com.android.server.hdmi;
 
-import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiDeviceInfo;
-
 import com.android.server.hdmi.Constants.AudioCodec;
 
 import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-import java.util.List;
 
 /**
  * A helper class to build {@link HdmiCecMessage} from various cec commands.
+ *
+ * If a message type has its own specific subclass of {@link HdmiCecMessage},
+ * its static factory method is instead declared in that subclass.
  */
 public class HdmiCecMessageBuilder {
     private static final int OSD_NAME_MAX_LENGTH = 14;
@@ -34,20 +32,6 @@
     private HdmiCecMessageBuilder() {}
 
     /**
-     * Build {@link HdmiCecMessage} from raw data.
-     *
-     * @param src source address of command
-     * @param dest destination address of command
-     * @param body body of message. It includes opcode.
-     * @return newly created {@link HdmiCecMessage}
-     */
-    static HdmiCecMessage of(int src, int dest, byte[] body) {
-        byte opcode = body[0];
-        byte params[] = Arrays.copyOfRange(body, 1, body.length);
-        return new HdmiCecMessage(src, dest, opcode, params);
-    }
-
-    /**
      * Build &lt;Feature Abort&gt; command. &lt;Feature Abort&gt; consists of
      * 1 byte original opcode and 1 byte reason fields with basic fields.
      *
@@ -63,7 +47,7 @@
                 (byte) (originalOpcode & 0xFF),
                 (byte) (reason & 0xFF),
         };
-        return buildCommand(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
     }
 
     /**
@@ -74,7 +58,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGivePhysicalAddress(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS);
     }
 
     /**
@@ -85,7 +69,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveOsdNameCommand(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_OSD_NAME);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_OSD_NAME);
     }
 
     /**
@@ -96,7 +80,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID);
     }
 
     /**
@@ -121,7 +105,7 @@
                 (byte) (normalized.charAt(2) & 0xFF),
         };
         // <Set Menu Language> is broadcast message.
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_SET_MENU_LANGUAGE, params);
     }
 
@@ -141,7 +125,7 @@
         } catch (UnsupportedEncodingException e) {
             return null;
         }
-        return buildCommand(src, dest, Constants.MESSAGE_SET_OSD_NAME, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_OSD_NAME, params);
     }
 
     /**
@@ -164,7 +148,7 @@
                 (byte) (deviceType & 0xFF)
         };
         // <Report Physical Address> is broadcast message.
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, params);
     }
 
@@ -185,7 +169,7 @@
                 (byte) (vendorId & 0xFF)
         };
         // <Device Vendor Id> is broadcast message.
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_DEVICE_VENDOR_ID, params);
     }
 
@@ -202,7 +186,7 @@
         byte[] params = new byte[] {
                 (byte) (version & 0xFF)
         };
-        return buildCommand(src, dest, Constants.MESSAGE_CEC_VERSION, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CEC_VERSION, params);
     }
 
     /**
@@ -213,7 +197,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRequestArcInitiation(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION);
     }
 
     /**
@@ -224,7 +208,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildInitiateArc(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_INITIATE_ARC);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_INITIATE_ARC);
     }
 
     /**
@@ -235,7 +219,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildTerminateArc(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_TERMINATE_ARC);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_TERMINATE_ARC);
     }
 
     /**
@@ -246,7 +230,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRequestArcTermination(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION);
     }
 
     /**
@@ -257,7 +241,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildReportArcInitiated(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED);
     }
 
     /**
@@ -268,7 +252,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildReportArcTerminated(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
     }
 
 
@@ -286,7 +270,8 @@
         for (int i = 0; i < params.length ; i++){
             params[i] = (byte) (audioFormats[i] & 0xff);
         }
-        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
+        return HdmiCecMessage.build(
+                src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
     }
 
 
@@ -298,7 +283,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildTextViewOn(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_TEXT_VIEW_ON);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_TEXT_VIEW_ON);
     }
 
     /**
@@ -308,7 +293,8 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRequestActiveSource(int src) {
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
+        return HdmiCecMessage.build(
+                src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
     }
 
     /**
@@ -319,7 +305,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildActiveSource(int src, int physicalAddress) {
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ACTIVE_SOURCE,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ACTIVE_SOURCE,
                 physicalAddressToParam(physicalAddress));
     }
 
@@ -331,7 +317,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildInactiveSource(int src, int physicalAddress) {
-        return buildCommand(src, Constants.ADDR_TV,
+        return HdmiCecMessage.build(src, Constants.ADDR_TV,
                 Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressToParam(physicalAddress));
     }
 
@@ -345,7 +331,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetStreamPath(int src, int streamPath) {
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_SET_STREAM_PATH, physicalAddressToParam(streamPath));
     }
 
@@ -364,7 +350,7 @@
             (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
             (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
         };
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ROUTING_CHANGE,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ROUTING_CHANGE,
                 param);
     }
 
@@ -378,7 +364,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRoutingInformation(int src, int physicalAddress) {
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
             Constants.MESSAGE_ROUTING_INFORMATION, physicalAddressToParam(physicalAddress));
     }
 
@@ -390,7 +376,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveDevicePowerStatus(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS);
     }
 
     /**
@@ -405,7 +391,7 @@
         byte[] param = new byte[] {
                 (byte) (powerStatus & 0xFF)
         };
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
     }
 
     /**
@@ -420,7 +406,7 @@
         byte[] param = new byte[] {
                 (byte) (menuStatus & 0xFF)
         };
-        return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_MENU_STATUS, param);
     }
 
     /**
@@ -435,10 +421,10 @@
     static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
             boolean enableSystemAudio) {
         if (enableSystemAudio) {
-            return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+            return HdmiCecMessage.build(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
                     physicalAddressToParam(avrPhysicalAddress));
         } else {
-            return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
+            return HdmiCecMessage.build(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
         }
     }
 
@@ -479,7 +465,8 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildReportShortAudioDescriptor(int src, int des, byte[] sadBytes) {
-        return buildCommand(src, des, Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR, sadBytes);
+        return HdmiCecMessage.build(
+                src, des, Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR, sadBytes);
     }
 
     /**
@@ -490,7 +477,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
     }
 
     /**
@@ -505,7 +492,7 @@
     static HdmiCecMessage buildReportAudioStatus(int src, int dest, int volume, boolean mute) {
         byte status = (byte) ((byte) (mute ? 1 << 7 : 0) | ((byte) volume & 0x7F));
         byte[] params = new byte[] { status };
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
     }
 
     /**
@@ -529,7 +516,8 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) {
-        return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
+        return HdmiCecMessage.build(
+                src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
     }
 
     /**
@@ -540,7 +528,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildUserControlReleased(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
     }
 
     /**
@@ -551,7 +539,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
     }
 
     /**
@@ -562,7 +550,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     public static HdmiCecMessage buildStandby(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_STANDBY);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_STANDBY);
     }
 
     /**
@@ -574,7 +562,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
     }
 
     /**
@@ -593,7 +581,7 @@
         params[1] = (byte) ((vendorId >> 8) & 0xFF);
         params[2] = (byte) (vendorId & 0xFF);
         System.arraycopy(operands, 0, params, 3, operands.length);
-        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
     }
 
     /**
@@ -605,7 +593,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRecordOn(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_RECORD_ON, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_RECORD_ON, params);
     }
 
     /**
@@ -616,7 +604,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRecordOff(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_RECORD_OFF);
     }
 
     /**
@@ -628,7 +616,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetDigitalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
     }
 
     /**
@@ -640,7 +628,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetAnalogueTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
     }
 
     /**
@@ -652,7 +640,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetExternalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
     }
 
     /**
@@ -664,7 +652,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildClearDigitalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
     }
 
     /**
@@ -676,7 +664,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildClearAnalogueTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
     }
 
     /**
@@ -688,73 +676,16 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildClearExternalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
     }
 
     static HdmiCecMessage buildGiveFeatures(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES);
-    }
-
-    static HdmiCecMessage buildReportFeatures(int src,
-            @HdmiControlManager.HdmiCecVersion int cecVersion,
-            List<Integer> allDeviceTypes, @Constants.RcProfile int rcProfile,
-            List<Integer> rcFeatures,
-            List<Integer> deviceFeatures) {
-        byte cecVersionByte = (byte) (cecVersion & 0xFF);
-        byte deviceTypes = 0;
-        for (Integer deviceType : allDeviceTypes) {
-            deviceTypes |= 1 << hdmiDeviceInfoDeviceTypeToShiftValue(deviceType);
-        }
-
-        byte rcProfileByte = 0;
-        rcProfileByte |= rcProfile << 6;
-        if (rcProfile == Constants.RC_PROFILE_SOURCE) {
-            for (@Constants.RcProfileSource Integer rcFeature : rcFeatures) {
-                rcProfileByte |= 1 << rcFeature;
-            }
-        } else {
-            @Constants.RcProfileTv byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF);
-            rcProfileByte |= rcProfileTv;
-        }
-
-        byte deviceFeaturesByte = 0;
-        for (@Constants.DeviceFeature Integer deviceFeature : deviceFeatures) {
-            deviceFeaturesByte |= 1 << deviceFeature;
-        }
-
-        byte[] params = {cecVersionByte, deviceTypes, rcProfileByte, deviceFeaturesByte};
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REPORT_FEATURES,
-                params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_FEATURES);
     }
 
     /***** Please ADD new buildXXX() methods above. ******/
 
     /**
-     * Build a {@link HdmiCecMessage} without extra parameter.
-     *
-     * @param src source address of command
-     * @param dest destination address of command
-     * @param opcode opcode for a message
-     * @return newly created {@link HdmiCecMessage}
-     */
-    private static HdmiCecMessage buildCommand(int src, int dest, int opcode) {
-        return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM);
-    }
-
-    /**
-     * Build a {@link HdmiCecMessage} with given values.
-     *
-     * @param src source address of command
-     * @param dest destination address of command
-     * @param opcode opcode for a message
-     * @param params extra parameters for command
-     * @return newly created {@link HdmiCecMessage}
-     */
-    private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) {
-        return new HdmiCecMessage(src, dest, opcode, params);
-    }
-
-    /**
      * Build a {@link HdmiCecMessage} with a boolean param and other given values.
      *
      * @param src source address of command
@@ -768,7 +699,7 @@
         byte[] params = new byte[]{
             param ? (byte) 0b1 : 0b0
         };
-        return buildCommand(src, des, opcode, params);
+        return HdmiCecMessage.build(src, des, opcode, params);
     }
 
     private static byte[] physicalAddressToParam(int physicalAddress) {
@@ -777,24 +708,4 @@
                 (byte) (physicalAddress & 0xFF)
         };
     }
-
-    @Constants.DeviceType
-    private static int hdmiDeviceInfoDeviceTypeToShiftValue(int deviceType) {
-        switch (deviceType) {
-            case HdmiDeviceInfo.DEVICE_TV:
-                return Constants.ALL_DEVICE_TYPES_TV;
-            case HdmiDeviceInfo.DEVICE_RECORDER:
-                return Constants.ALL_DEVICE_TYPES_RECORDER;
-            case HdmiDeviceInfo.DEVICE_TUNER:
-                return Constants.ALL_DEVICE_TYPES_TUNER;
-            case HdmiDeviceInfo.DEVICE_PLAYBACK:
-                return Constants.ALL_DEVICE_TYPES_PLAYBACK;
-            case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
-                return Constants.ALL_DEVICE_TYPES_AUDIO_SYSTEM;
-            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
-                return Constants.ALL_DEVICE_TYPES_SWITCH;
-            default:
-                throw new IllegalArgumentException("Unhandled device type: " + deviceType);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 8a727c6..220a438 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -16,23 +16,34 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.IntDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.SparseArray;
 
 /**
- * A helper class to validates {@link HdmiCecMessage}.
+ * A helper class to validate {@link HdmiCecMessage}.
+ *
+ * If a message type has its own specific subclass of {@link HdmiCecMessage},
+ * validation is performed in that subclass instead.
  */
 public class HdmiCecMessageValidator {
     private static final String TAG = "HdmiCecMessageValidator";
 
+    @IntDef({
+            OK,
+            ERROR_SOURCE,
+            ERROR_DESTINATION,
+            ERROR_PARAMETER,
+            ERROR_PARAMETER_SHORT,
+    })
+    public @interface ValidationResult {};
+
     static final int OK = 0;
     static final int ERROR_SOURCE = 1;
     static final int ERROR_DESTINATION = 2;
     static final int ERROR_PARAMETER = 3;
     static final int ERROR_PARAMETER_SHORT = 4;
 
-    private final HdmiControlService mService;
-
     interface ParameterValidator {
         /**
          * @return errorCode errorCode can be {@link #OK}, {@link #ERROR_PARAMETER} or
@@ -42,13 +53,13 @@
     }
 
     // Only the direct addressing is allowed.
-    private static final int DEST_DIRECT = 1 << 0;
+    public static final int DEST_DIRECT = 1 << 0;
     // Only the broadcast addressing is allowed.
-    private static final int DEST_BROADCAST = 1 << 1;
+    public static final int DEST_BROADCAST = 1 << 1;
     // Both the direct and the broadcast addressing are allowed.
-    private static final int DEST_ALL = DEST_DIRECT | DEST_BROADCAST;
+    public static final int DEST_ALL = DEST_DIRECT | DEST_BROADCAST;
     // True if the messages from address 15 (unregistered) are allowed.
-    private static final int SRC_UNREGISTERED = 1 << 2;
+    public static final int SRC_UNREGISTERED = 1 << 2;
 
     private static class ValidationInfo {
         public final ParameterValidator parameterValidator;
@@ -60,11 +71,11 @@
         }
     }
 
-    final SparseArray<ValidationInfo> mValidationInfo = new SparseArray<>();
+    private HdmiCecMessageValidator() {}
 
-    public HdmiCecMessageValidator(HdmiControlService service) {
-        mService = service;
+    private static final SparseArray<ValidationInfo> sValidationInfo = new SparseArray<>();
 
+    static {
         // Messages related to the physical address.
         PhysicalAddressValidator physicalAddressValidator = new PhysicalAddressValidator();
         addValidationInfo(Constants.MESSAGE_ACTIVE_SOURCE,
@@ -234,8 +245,6 @@
         // Messages for Feature Discovery.
         addValidationInfo(Constants.MESSAGE_GIVE_FEATURES, noneValidator,
                 DEST_DIRECT | SRC_UNREGISTERED);
-        addValidationInfo(Constants.MESSAGE_REPORT_FEATURES, new VariableLengthValidator(4, 14),
-                DEST_BROADCAST);
 
         // Messages for Dynamic Auto Lipsync
         addValidationInfo(Constants.MESSAGE_REQUEST_CURRENT_LATENCY, physicalAddressValidator,
@@ -250,59 +259,62 @@
                 DEST_BROADCAST | SRC_UNREGISTERED);
     }
 
-    private void addValidationInfo(int opcode, ParameterValidator validator, int addrType) {
-        mValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
+    private static void addValidationInfo(int opcode, ParameterValidator validator, int addrType) {
+        sValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
     }
 
-    int isValid(HdmiCecMessage message, boolean isMessageReceived) {
-        int opcode = message.getOpcode();
-        ValidationInfo info = mValidationInfo.get(opcode);
+    /**
+     * Validates all parameters of a HDMI-CEC message using static information stored in this class.
+     */
+    @ValidationResult
+    static int validate(int source, int destination, int opcode, byte[] params) {
+        ValidationInfo info = sValidationInfo.get(opcode);
+
         if (info == null) {
-            HdmiLogger.warning("No validation information for the message: " + message);
+            HdmiLogger.warning("No validation information for the opcode: " + opcode);
             return OK;
         }
 
+        int addressValidationResult = validateAddress(source, destination, info.addressType);
+        if (addressValidationResult != OK) {
+            return addressValidationResult;
+        }
+
+        // Validate parameters
+        int errorCode = info.parameterValidator.isValid(params);
+        if (errorCode != OK) {
+            return errorCode;
+        }
+
+        return OK;
+    }
+
+    /**
+     * Validates the source and destination addresses of a HDMI-CEC message according to input
+     * address type. Allows address validation logic to be expressed concisely without depending
+     * on static information in this class.
+     * @param source Source address to validate
+     * @param destination Destination address to validate
+     * @param addressType Rules for validating the addresses - e.g. {@link #DEST_BROADCAST}
+     */
+    @ValidationResult
+    static int validateAddress(int source, int destination, int addressType) {
         // Check the source field.
-        if (message.getSource() == Constants.ADDR_UNREGISTERED &&
-                (info.addressType & SRC_UNREGISTERED) == 0) {
-            HdmiLogger.warning("Unexpected source: " + message);
+        if (source == Constants.ADDR_UNREGISTERED
+                && (addressType & SRC_UNREGISTERED) == 0) {
             return ERROR_SOURCE;
         }
 
-        if (isMessageReceived) {
-            // Check if the source's logical address and local device's logical
-            // address are the same.
-            for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
-                synchronized (device.mLock) {
-                    if (message.getSource() == device.getDeviceInfo().getLogicalAddress()
-                            && message.getSource() != Constants.ADDR_UNREGISTERED) {
-                        HdmiLogger.warning(
-                                "Unexpected source: message sent from device itself, " + message);
-                        return ERROR_SOURCE;
-                    }
-                }
-            }
-        }
-
         // Check the destination field.
-        if (message.getDestination() == Constants.ADDR_BROADCAST) {
-            if ((info.addressType & DEST_BROADCAST) == 0) {
-                HdmiLogger.warning("Unexpected broadcast message: " + message);
+        if (destination == Constants.ADDR_BROADCAST) {
+            if ((addressType & DEST_BROADCAST) == 0) {
                 return ERROR_DESTINATION;
             }
         } else {  // Direct addressing.
-            if ((info.addressType & DEST_DIRECT) == 0) {
-                HdmiLogger.warning("Unexpected direct message: " + message);
+            if ((addressType & DEST_DIRECT) == 0) {
                 return ERROR_DESTINATION;
             }
         }
-
-        // Check the parameter type.
-        int errorCode = info.parameterValidator.isValid(message.getParams());
-        if (errorCode != OK) {
-            HdmiLogger.warning("Unexpected parameters: " + message);
-            return errorCode;
-        }
         return OK;
     }
 
@@ -336,7 +348,7 @@
         }
     }
 
-    private boolean isValidPhysicalAddress(byte[] params, int offset) {
+    private static boolean isValidPhysicalAddress(byte[] params, int offset) {
         int physicalAddress = HdmiUtils.twoBytesToInt(params, offset);
         while (physicalAddress != 0) {
             int maskedAddress = physicalAddress & 0xF000;
@@ -345,19 +357,6 @@
                 return false;
             }
         }
-
-        if (!mService.isTvDevice()) {
-            // If the device is not TV, we can't convert path to port-id, so stop here.
-            return true;
-        }
-        int path = HdmiUtils.twoBytesToInt(params, offset);
-        if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == mService.getPhysicalAddress()) {
-            return true;
-        }
-        int portId = mService.pathToPortId(path);
-        if (portId == Constants.INVALID_PORT_ID) {
-            return false;
-        }
         return true;
     }
 
@@ -380,7 +379,7 @@
         return success ? OK : ERROR_PARAMETER;
     }
 
-    private boolean isWithinRange(int value, int min, int max) {
+    private static boolean isWithinRange(int value, int min, int max) {
         value = value & 0xFF;
         return (value >= min && value <= max);
     }
@@ -392,7 +391,7 @@
      * @param value Display Control
      * @return true if the Display Control is valid
      */
-    private boolean isValidDisplayControl(int value) {
+    private static boolean isValidDisplayControl(int value) {
         value = value & 0xFF;
         return (value == 0x00 || value == 0x40 || value == 0x80 || value == 0xC0);
     }
@@ -407,7 +406,7 @@
      * @param maxLength Maximum length of string to be evaluated
      * @return true if the given type is valid
      */
-    private boolean isValidAsciiString(byte[] params, int offset, int maxLength) {
+    private static boolean isValidAsciiString(byte[] params, int offset, int maxLength) {
         for (int i = offset; i < params.length && i < maxLength; i++) {
             if (!isWithinRange(params[i], 0x20, 0x7E)) {
                 return false;
@@ -423,7 +422,7 @@
      * @param value day of month
      * @return true if the day of month is valid
      */
-    private boolean isValidDayOfMonth(int value) {
+    private static boolean isValidDayOfMonth(int value) {
         return isWithinRange(value, 1, 31);
     }
 
@@ -434,7 +433,7 @@
      * @param value month of year
      * @return true if the month of year is valid
      */
-    private boolean isValidMonthOfYear(int value) {
+    private static boolean isValidMonthOfYear(int value) {
         return isWithinRange(value, 1, 12);
     }
 
@@ -445,7 +444,7 @@
      * @param value hour
      * @return true if the hour is valid
      */
-    private boolean isValidHour(int value) {
+    private static boolean isValidHour(int value) {
         return isWithinRange(value, 0, 23);
     }
 
@@ -456,7 +455,7 @@
      * @param value minute
      * @return true if the minute is valid
      */
-    private boolean isValidMinute(int value) {
+    private static boolean isValidMinute(int value) {
         return isWithinRange(value, 0, 59);
     }
 
@@ -467,7 +466,7 @@
      * @param value duration hours
      * @return true if the duration hours is valid
      */
-    private boolean isValidDurationHours(int value) {
+    private static boolean isValidDurationHours(int value) {
         return isWithinRange(value, 0, 99);
     }
 
@@ -478,7 +477,7 @@
      * @param value recording sequence
      * @return true if the given recording sequence is valid
      */
-    private boolean isValidRecordingSequence(int value) {
+    private static boolean isValidRecordingSequence(int value) {
         value = value & 0xFF;
         // Validate bit 7 is set to zero
         if ((value & 0x80) != 0x00) {
@@ -496,7 +495,7 @@
      * @param value analogue broadcast type
      * @return true if the analogue broadcast type is valid
      */
-    private boolean isValidAnalogueBroadcastType(int value) {
+    private static boolean isValidAnalogueBroadcastType(int value) {
         return isWithinRange(value, 0x00, 0x02);
     }
 
@@ -508,7 +507,7 @@
      * @param value analogue frequency
      * @return true if the analogue frequency is valid
      */
-    private boolean isValidAnalogueFrequency(int value) {
+    private static boolean isValidAnalogueFrequency(int value) {
         value = value & 0xFFFF;
         return (value != 0x000 && value != 0xFFFF);
     }
@@ -520,7 +519,7 @@
      * @param value broadcast system
      * @return true if the broadcast system is valid
      */
-    private boolean isValidBroadcastSystem(int value) {
+    private static boolean isValidBroadcastSystem(int value) {
         return isWithinRange(value, 0, 31);
     }
 
@@ -531,7 +530,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is ARIB type
      */
-    private boolean isAribDbs(int value) {
+    private static boolean isAribDbs(int value) {
         return (value == 0x00 || isWithinRange(value, 0x08, 0x0A));
     }
 
@@ -542,7 +541,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is ATSC type
      */
-    private boolean isAtscDbs(int value) {
+    private static boolean isAtscDbs(int value) {
         return (value == 0x01 || isWithinRange(value, 0x10, 0x12));
     }
 
@@ -553,7 +552,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is DVB type
      */
-    private boolean isDvbDbs(int value) {
+    private static boolean isDvbDbs(int value) {
         return (value == 0x02 || isWithinRange(value, 0x18, 0x1B));
     }
 
@@ -565,7 +564,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is valid
      */
-    private boolean isValidDigitalBroadcastSystem(int value) {
+    private static boolean isValidDigitalBroadcastSystem(int value) {
         return (isAribDbs(value) || isAtscDbs(value) || isDvbDbs(value));
     }
 
@@ -578,7 +577,7 @@
      * @param offset start offset of Channel Identifier
      * @return true if the Channel Identifier is valid
      */
-    private boolean isValidChannelIdentifier(byte[] params, int offset) {
+    private static boolean isValidChannelIdentifier(byte[] params, int offset) {
         // First 6 bits contain Channel Number Format
         int channelNumberFormat = params[offset] & 0xFC;
         if (channelNumberFormat == 0x04) {
@@ -600,7 +599,7 @@
      * @param offset start offset of Digital Service Identification
      * @return true if the Digital Service Identification is valid
      */
-    private boolean isValidDigitalServiceIdentification(byte[] params, int offset) {
+    private static boolean isValidDigitalServiceIdentification(byte[] params, int offset) {
         // MSB contains Service Identification Method
         int serviceIdentificationMethod = params[offset] & 0x80;
         // Last 7 bits contains Digital Broadcast System
@@ -634,7 +633,7 @@
      * @param value External Plug
      * @return true if the External Plug is valid
      */
-    private boolean isValidExternalPlug(int value) {
+    private static boolean isValidExternalPlug(int value) {
         return isWithinRange(value, 1, 255);
     }
 
@@ -645,7 +644,7 @@
      * @param value External Source Specifier
      * @return true if the External Source is valid
      */
-    private boolean isValidExternalSource(byte[] params, int offset) {
+    private static boolean isValidExternalSource(byte[] params, int offset) {
         int externalSourceSpecifier = params[offset];
         offset = offset + 1;
         if (externalSourceSpecifier == 0x04) {
@@ -661,15 +660,15 @@
         return false;
     }
 
-    private boolean isValidProgrammedInfo(int programedInfo) {
+    private static boolean isValidProgrammedInfo(int programedInfo) {
         return (isWithinRange(programedInfo, 0x00, 0x0B));
     }
 
-    private boolean isValidNotProgrammedErrorInfo(int nonProgramedErrorInfo) {
+    private static boolean isValidNotProgrammedErrorInfo(int nonProgramedErrorInfo) {
         return (isWithinRange(nonProgramedErrorInfo, 0x00, 0x0E));
     }
 
-    private boolean isValidTimerStatusData(byte[] params, int offset) {
+    private static boolean isValidTimerStatusData(byte[] params, int offset) {
         int programedIndicator = params[offset] & 0x10;
         boolean durationAvailable = false;
         if (programedIndicator == 0x10) {
@@ -708,7 +707,7 @@
      * @param value Play mode
      * @return true if the Play mode is valid
      */
-    private boolean isValidPlayMode(int value) {
+    private static boolean isValidPlayMode(int value) {
         return (isWithinRange(value, 0x05, 0x07)
                 || isWithinRange(value, 0x09, 0x0B)
                 || isWithinRange(value, 0x15, 0x17)
@@ -725,7 +724,7 @@
      * @param value UI Broadcast type
      * @return true if the UI Broadcast type is valid
      */
-    private boolean isValidUiBroadcastType(int value) {
+    private static boolean isValidUiBroadcastType(int value) {
         return ((value == 0x00)
                 || (value == 0x01)
                 || (value == 0x10)
@@ -749,7 +748,7 @@
      * @param value UI Sound Presenation Control
      * @return true if the UI Sound Presenation Control is valid
      */
-    private boolean isValidUiSoundPresenationControl(int value) {
+    private static boolean isValidUiSoundPresenationControl(int value) {
         value = value & 0xFF;
         return ((value == 0x20)
                 || (value == 0x30)
@@ -768,7 +767,7 @@
      * @param params Tuner device info
      * @return true if the Tuner device info is valid
      */
-    private boolean isValidTunerDeviceInfo(byte[] params) {
+    private static boolean isValidTunerDeviceInfo(byte[] params) {
         int tunerDisplayInfo = params[0] & 0x7F;
         if (tunerDisplayInfo == 0x00) {
             // Displaying digital tuner
@@ -789,7 +788,7 @@
         return false;
     }
 
-    private class PhysicalAddressValidator implements ParameterValidator {
+    private static class PhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 2) {
@@ -799,7 +798,7 @@
         }
     }
 
-    private class SystemAudioModeRequestValidator extends PhysicalAddressValidator {
+    private static class SystemAudioModeRequestValidator extends PhysicalAddressValidator {
         @Override
         public int isValid(byte[] params) {
             // TV can send <System Audio Mode Request> with no parameters to terminate system audio.
@@ -810,7 +809,7 @@
         }
     }
 
-    private class ReportPhysicalAddressValidator implements ParameterValidator {
+    private static class ReportPhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 3) {
@@ -820,7 +819,7 @@
         }
     }
 
-    private class RoutingChangeValidator implements ParameterValidator {
+    private static class RoutingChangeValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 4) {
@@ -836,7 +835,7 @@
      * A valid parameter should lie within the range description of Record Status Info defined in
      * CEC 1.4 Specification : Operand Descriptions (Section 17)
      */
-    private class RecordStatusInfoValidator implements ParameterValidator {
+    private static class RecordStatusInfoValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -855,7 +854,7 @@
      * A valid parameter should lie within the range description of ASCII defined in CEC 1.4
      * Specification : Operand Descriptions (Section 17)
      */
-    private class AsciiValidator implements ParameterValidator {
+    private static class AsciiValidator implements ParameterValidator {
         private final int mMinLength;
         private final int mMaxLength;
 
@@ -885,7 +884,7 @@
      * A valid parameter should lie within the range description of ASCII defined in CEC 1.4
      * Specification : Operand Descriptions (Section 17)
      */
-    private class OsdStringValidator implements ParameterValidator {
+    private static class OsdStringValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             // If the length is longer than expected, we assume it's OK since the parameter can be
@@ -902,7 +901,7 @@
     }
 
     /** Check if the given parameters are one byte parameters and within range. */
-    private class OneByteRangeValidator implements ParameterValidator {
+    private static class OneByteRangeValidator implements ParameterValidator {
         private final int mMinValue, mMaxValue;
 
         OneByteRangeValidator(int minValue, int maxValue) {
@@ -924,7 +923,7 @@
      * adhere to message description of Analogue Timer defined in CEC 1.4 Specification : Message
      * Descriptions for Timer Programming Feature (CEC Table 12)
      */
-    private class AnalogueTimerValidator implements ParameterValidator {
+    private static class AnalogueTimerValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 11) {
@@ -950,7 +949,7 @@
      * to message description of Digital Timer defined in CEC 1.4 Specification : Message
      * Descriptions for Timer Programming Feature (CEC Table 12)
      */
-    private class DigitalTimerValidator implements ParameterValidator {
+    private static class DigitalTimerValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 11) {
@@ -974,7 +973,7 @@
      * adhere to message description of External Timer defined in CEC 1.4 Specification : Message
      * Descriptions for Timer Programming Feature (CEC Table 12)
      */
-    private class ExternalTimerValidator implements ParameterValidator {
+    private static class ExternalTimerValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 9) {
@@ -997,7 +996,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions
      * (Section 17)
      */
-    private class TimerClearedStatusValidator implements ParameterValidator {
+    private static class TimerClearedStatusValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1011,7 +1010,7 @@
      * Check if the given timer status data parameter is valid. A valid parameter should lie within
      * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
      */
-    private class TimerStatusValidator implements ParameterValidator {
+    private static class TimerStatusValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1025,7 +1024,7 @@
      * Check if the given play mode parameter is valid. A valid parameter should lie within the
      * range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
      */
-    private class PlayModeValidator implements ParameterValidator {
+    private static class PlayModeValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1040,7 +1039,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions
      * (Section 17)
      */
-    private class SelectAnalogueServiceValidator implements ParameterValidator {
+    private static class SelectAnalogueServiceValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 4) {
@@ -1057,7 +1056,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions
      * (Section 17)
      */
-    private class SelectDigitalServiceValidator implements ParameterValidator {
+    private static class SelectDigitalServiceValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 4) {
@@ -1072,7 +1071,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions (Section
      * 17)
      */
-    private class TunerDeviceStatusValidator implements ParameterValidator {
+    private static class TunerDeviceStatusValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1083,7 +1082,7 @@
     }
 
     /** Check if the given user control press parameter is valid. */
-    private class UserControlPressedValidator implements ParameterValidator {
+    private static class UserControlPressedValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 225785a..6497174 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -57,7 +57,8 @@
  * This class should not take any active action in sending CEC messages.
  *
  * Note that the information cached in this class is not guaranteed to be up-to-date, especially OSD
- * names, power states can be outdated.
+ * names, power states can be outdated. For local devices, more up-to-date information can be
+ * accessed through {@link HdmiCecLocalDevice#getDeviceInfo()}.
  */
 @VisibleForTesting
 public class HdmiCecNetwork {
@@ -390,7 +391,7 @@
             return;
         }
 
-        updateCecDevice(HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus));
+        updateCecDevice(info.toBuilder().setDevicePowerStatus(newPowerStatus).build());
     }
 
     /**
@@ -427,7 +428,8 @@
         for (HdmiPortInfo info : cecPortInfo) {
             portIdMap.put(info.getAddress(), info.getId());
             portInfoMap.put(info.getId(), info);
-            portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
+            portDeviceMap.put(info.getId(),
+                    HdmiDeviceInfo.hardwarePort(info.getAddress(), info.getId()));
         }
         mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
         mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
@@ -496,13 +498,19 @@
         // Add device by logical address if it's not already known
         int sourceAddress = message.getSource();
         if (getCecDeviceInfo(sourceAddress) == null) {
-            HdmiDeviceInfo newDevice = new HdmiDeviceInfo(sourceAddress,
-                    HdmiDeviceInfo.PATH_INVALID, HdmiDeviceInfo.PORT_INVALID,
-                    HdmiDeviceInfo.DEVICE_RESERVED, Constants.UNKNOWN_VENDOR_ID,
-                    HdmiUtils.getDefaultDeviceName(sourceAddress));
+            HdmiDeviceInfo newDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                    .setLogicalAddress(sourceAddress)
+                    .setDisplayName(HdmiUtils.getDefaultDeviceName(sourceAddress))
+                    .build();
             addCecDevice(newDevice);
         }
 
+        // If a message type has its own class, all valid messages of that type
+        // will be represented by an instance of that class.
+        if (message instanceof ReportFeaturesMessage) {
+            handleReportFeatures((ReportFeaturesMessage) message);
+        }
+
         switch (message.getOpcode()) {
             case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
                 handleReportPhysicalAddress(message);
@@ -519,18 +527,20 @@
             case Constants.MESSAGE_CEC_VERSION:
                 handleCecVersion(message);
                 break;
-            case Constants.MESSAGE_REPORT_FEATURES:
-                handleReportFeatures(message);
-                break;
         }
     }
 
     @ServiceThreadOnly
-    private void handleReportFeatures(HdmiCecMessage message) {
+    private void handleReportFeatures(ReportFeaturesMessage message) {
         assertRunOnServiceThread();
 
-        int version = Byte.toUnsignedInt(message.getParams()[0]);
-        updateDeviceCecVersion(message.getSource(), version);
+        HdmiDeviceInfo currentDeviceInfo = getCecDeviceInfo(message.getSource());
+        HdmiDeviceInfo newDeviceInfo = currentDeviceInfo.toBuilder()
+                .setCecVersion(message.getCecVersion())
+                .updateDeviceFeatures(message.getDeviceFeatures())
+                .build();
+
+        updateCecDevice(newDeviceInfo);
     }
 
     @ServiceThreadOnly
@@ -554,11 +564,11 @@
         if (deviceInfo == null) {
             Slog.i(TAG, "Unknown source device info for <Report Physical Address> " + message);
         } else {
-            HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                    physicalAddress,
-                    physicalAddressToPortId(physicalAddress), type, deviceInfo.getVendorId(),
-                    deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus(),
-                    deviceInfo.getCecVersion());
+            HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                    .setPhysicalAddress(physicalAddress)
+                    .setPortId(physicalAddressToPortId(physicalAddress))
+                    .setDeviceType(type)
+                    .build();
             updateCecDevice(updatedDeviceInfo);
         }
     }
@@ -588,11 +598,9 @@
             return;
         }
 
-        HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(), deviceInfo.getDeviceType(),
-                deviceInfo.getVendorId(),
-                deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus(),
-                hdmiCecVersion);
+        HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                .setCecVersion(hdmiCecVersion)
+                .build();
         updateCecDevice(updatedDeviceInfo);
     }
 
@@ -623,10 +631,11 @@
         Slog.d(TAG, "Updating device OSD name from "
                 + deviceInfo.getDisplayName()
                 + " to " + osdName);
-        updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
-                deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName,
-                deviceInfo.getDevicePowerStatus(), deviceInfo.getCecVersion()));
+
+        HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                .setDisplayName(osdName)
+                .build();
+        updateCecDevice(updatedDeviceInfo);
     }
 
     @ServiceThreadOnly
@@ -639,11 +648,9 @@
         if (deviceInfo == null) {
             Slog.i(TAG, "Unknown source device info for <Device Vendor ID> " + message);
         } else {
-            HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                    deviceInfo.getPhysicalAddress(),
-                    deviceInfo.getPortId(), deviceInfo.getDeviceType(), vendorId,
-                    deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus(),
-                    deviceInfo.getCecVersion());
+            HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                    .setVendorId(vendorId)
+                    .build();
             updateCecDevice(updatedDeviceInfo);
         }
     }
@@ -723,10 +730,7 @@
 
     /**
      * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
-     *
-     *
-     *
-     * qq   * the given routing path. CEC devices use routing path for its physical address to
+     * the given routing path. CEC devices use routing path for its physical address to
      * describe the hierarchy of the devices in the network.
      *
      * @param path routing path or physical address
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 6dd9aa0..8391e0b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -372,8 +372,6 @@
     @Nullable
     private HdmiCecController mCecController;
 
-    private HdmiCecMessageValidator mMessageValidator;
-
     private HdmiCecPowerStatusController mPowerStatusController;
 
     @ServiceThreadOnly
@@ -606,9 +604,6 @@
         mMhlDevices = Collections.emptyList();
 
         mHdmiCecNetwork.initPortInfo();
-        if (mMessageValidator == null) {
-            mMessageValidator = new HdmiCecMessageValidator(this);
-        }
         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                 new HdmiCecConfig.SettingChangeListener() {
                     @Override
@@ -1086,11 +1081,6 @@
     }
 
     @VisibleForTesting
-    void setMessageValidator(HdmiCecMessageValidator messageValidator) {
-        mMessageValidator = messageValidator;
-    }
-
-    @VisibleForTesting
     void setCecMessageBuffer(CecMessageBuffer cecMessageBuffer) {
         this.mCecMessageBuffer = cecMessageBuffer;
     }
@@ -1182,7 +1172,8 @@
     @ServiceThreadOnly
     void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
         assertRunOnServiceThread();
-        if (mMessageValidator.isValid(command, false) == HdmiCecMessageValidator.OK) {
+        if (command.getValidationResult() == HdmiCecMessageValidator.OK
+                && verifyPhysicalAddresses(command)) {
             mCecController.sendCommand(command, callback);
         } else {
             HdmiLogger.error("Invalid message type:" + command);
@@ -1210,20 +1201,99 @@
         mCecController.maySendFeatureAbortCommand(command, reason);
     }
 
+    /**
+     * Returns whether all of the physical addresses in a message could exist in this CEC network.
+     */
+    boolean verifyPhysicalAddresses(HdmiCecMessage message) {
+        byte[] params = message.getParams();
+        switch (message.getOpcode()) {
+            case Constants.MESSAGE_ROUTING_CHANGE:
+                return verifyPhysicalAddress(params, 0)
+                        && verifyPhysicalAddress(params, 2);
+            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
+                return params.length == 0 || verifyPhysicalAddress(params, 0);
+            case Constants.MESSAGE_ACTIVE_SOURCE:
+            case Constants.MESSAGE_INACTIVE_SOURCE:
+            case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+            case Constants.MESSAGE_ROUTING_INFORMATION:
+            case Constants.MESSAGE_SET_STREAM_PATH:
+                return verifyPhysicalAddress(params, 0);
+            case Constants.MESSAGE_CLEAR_EXTERNAL_TIMER:
+            case Constants.MESSAGE_SET_EXTERNAL_TIMER:
+                return verifyExternalSourcePhysicalAddress(params, 7);
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Returns whether a given physical address could exist in this CEC network.
+     * For a TV, the physical address must either be the address of the TV itself,
+     * or the address of a device connected to one of its ports (possibly indirectly).
+     */
+    private boolean verifyPhysicalAddress(byte[] params, int offset) {
+        if (!isTvDevice()) {
+            // If the device is not TV, we can't convert path to port-id, so stop here.
+            return true;
+        }
+        int path = HdmiUtils.twoBytesToInt(params, offset);
+        if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == getPhysicalAddress()) {
+            return true;
+        }
+        int portId = pathToPortId(path);
+        if (portId == Constants.INVALID_PORT_ID) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether the physical address of an external source could exist in this network.
+     */
+    private boolean verifyExternalSourcePhysicalAddress(byte[] params, int offset) {
+        int externalSourceSpecifier = params[offset];
+        offset = offset + 1;
+        if (externalSourceSpecifier == 0x05) {
+            if (params.length - offset >= 2) {
+                return verifyPhysicalAddress(params, offset);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether the source address of a message is a local logical address.
+     */
+    private boolean sourceAddressIsLocal(HdmiCecMessage message) {
+        for (HdmiCecLocalDevice device : getAllLocalDevices()) {
+            synchronized (device.mLock) {
+                if (message.getSource() == device.getDeviceInfo().getLogicalAddress()
+                        && message.getSource() != Constants.ADDR_UNREGISTERED) {
+                    HdmiLogger.warning(
+                            "Unexpected source: message sent from device itself, " + message);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     @ServiceThreadOnly
     @VisibleForTesting
     @Constants.HandleMessageResult
     protected int handleCecCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        int errorCode = mMessageValidator.isValid(message, true);
-        if (errorCode != HdmiCecMessageValidator.OK) {
-            // We'll not response on the messages with the invalid source or destination
-            // or with parameter length shorter than specified in the standard.
-            if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) {
-                return Constants.ABORT_INVALID_OPERAND;
-            }
+
+        @HdmiCecMessageValidator.ValidationResult
+        int validationResult = message.getValidationResult();
+        if (validationResult == HdmiCecMessageValidator.ERROR_PARAMETER
+                || !verifyPhysicalAddresses(message)) {
+            return Constants.ABORT_INVALID_OPERAND;
+        } else if (validationResult != HdmiCecMessageValidator.OK
+                || sourceAddressIsLocal(message)) {
             return Constants.HANDLED;
         }
+
         getHdmiCecNetwork().handleCecMessage(message);
 
         @Constants.HandleMessageResult int handleMessageResult =
@@ -1409,9 +1479,16 @@
     private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus,
             int cecVersion) {
         String displayName = readStringSetting(Global.DEVICE_NAME, Build.MODEL);
-        return new HdmiDeviceInfo(logicalAddress,
-                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
-                getVendorId(), displayName, powerStatus, cecVersion);
+        return HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(logicalAddress)
+                .setPhysicalAddress(getPhysicalAddress())
+                .setPortId(pathToPortId(getPhysicalAddress()))
+                .setDeviceType(deviceType)
+                .setVendorId(getVendorId())
+                .setDisplayName(displayName)
+                .setDevicePowerStatus(powerStatus)
+                .setCecVersion(cecVersion)
+                .build();
     }
 
     // Set the display name in HdmiDeviceInfo of the current devices to content provided by
@@ -1422,10 +1499,9 @@
             if (deviceInfo.getDisplayName().equals(newDisplayName)) {
                 continue;
             }
-            device.setDeviceInfo(new HdmiDeviceInfo(
-                    deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress(),
-                    deviceInfo.getPortId(), deviceInfo.getDeviceType(), deviceInfo.getVendorId(),
-                    newDisplayName, deviceInfo.getDevicePowerStatus(), deviceInfo.getCecVersion()));
+            synchronized (device.mLock) {
+                device.setDeviceInfo(deviceInfo.toBuilder().setDisplayName(newDisplayName).build());
+            }
             sendCecCommand(
                     HdmiCecMessageBuilder.buildSetOsdNameCommand(
                             deviceInfo.getLogicalAddress(), Constants.ADDR_TV, newDisplayName));
@@ -2629,7 +2705,7 @@
                 return activeSourceInfo;
             }
 
-            return new HdmiDeviceInfo(activeSource.physicalAddress,
+            return HdmiDeviceInfo.hardwarePort(activeSource.physicalAddress,
                     pathToPortId(activeSource.physicalAddress));
         }
 
@@ -2637,7 +2713,7 @@
             int activePath = tv().getActivePath();
             if (activePath != HdmiDeviceInfo.PATH_INVALID) {
                 HdmiDeviceInfo info = mHdmiCecNetwork.getSafeDeviceInfoByPath(activePath);
-                return (info != null) ? info : new HdmiDeviceInfo(activePath,
+                return (info != null) ? info : HdmiDeviceInfo.hardwarePort(activePath,
                         tv().getActivePortId());
             }
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
index 06ecb5a..43469b5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
+++ b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
@@ -8,7 +8,7 @@
  */
 final class HdmiMhlLocalDeviceStub {
 
-    private static final HdmiDeviceInfo INFO = new HdmiDeviceInfo(
+    private static final HdmiDeviceInfo INFO = HdmiDeviceInfo.mhlDevice(
             Constants.INVALID_PHYSICAL_ADDRESS, Constants.INVALID_PORT_ID, -1, -1);
     private final HdmiControlService mService;
     private final int mPortId;
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 03e5de8..ba19cf0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -391,15 +391,6 @@
     }
 
     /**
-     * Clone {@link HdmiDeviceInfo} with new power status.
-     */
-    static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) {
-        return new HdmiDeviceInfo(info.getLogicalAddress(),
-                info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
-                info.getVendorId(), info.getDisplayName(), newPowerStatus, info.getCecVersion());
-    }
-
-    /**
      * Dump a {@link SparseArray} to the print writer.
      *
      * <p>The dump is formatted:
@@ -470,7 +461,7 @@
     }
 
     /**
-     * Method to parse target physical address to the port number on the current device.
+     * Method to build target physical address to the port number on the current device.
      *
      * <p>This check assumes target address is valid.
      *
@@ -562,7 +553,28 @@
         for (int i = 0; i < params.length; i++) {
             params[i] = (byte) Integer.parseInt(parts[i + 2], 16);
         }
-        return new HdmiCecMessage(src, dest, opcode, params);
+        return HdmiCecMessage.build(src, dest, opcode, params);
+    }
+
+    /**
+     * Some operands in the CEC spec consist of a variable number of bytes, where each byte except
+     * the last one has bit 7 set to 1.
+     * Given the index of a byte in such an operand, this method returns the index of the last byte
+     * in the operand, or -1 if the input is invalid (e.g. operand not terminated properly).
+     * @param params Byte array representing a CEC message's parameters
+     * @param offset Index of a byte in the operand to find the end of
+     */
+    public static int getEndOfSequence(byte[] params, int offset) {
+        if (offset < 0) {
+            return -1;
+        }
+        while (offset < params.length && ((params[offset] >> 7) & 1) == 1) {
+            offset++;
+        }
+        if (offset >= params.length) {
+            return -1;
+        }
+        return offset;
     }
 
     public static class ShortAudioDescriptorXmlParser {
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index a307ea3..c4b98c2 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -68,7 +68,7 @@
         mDeviceLogicalAddress = deviceLogicalAddress;
         mDevicePhysicalAddress = devicePhysicalAddress;
         mDeviceType = deviceType;
-        mVendorId = Constants.UNKNOWN_VENDOR_ID;
+        mVendorId = Constants.VENDOR_ID_UNKNOWN;
     }
 
     @Override
@@ -174,10 +174,14 @@
         if (mDisplayName == null) {
             mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress);
         }
-        HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(
-                mDeviceLogicalAddress, mDevicePhysicalAddress,
-                tv().getPortId(mDevicePhysicalAddress),
-                mDeviceType, mVendorId, mDisplayName);
+        HdmiDeviceInfo deviceInfo = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mDeviceLogicalAddress)
+                .setPhysicalAddress(mDevicePhysicalAddress)
+                .setPortId(tv().getPortId(mDevicePhysicalAddress))
+                .setDeviceType(mDeviceType)
+                .setVendorId(mVendorId)
+                .setDisplayName(mDisplayName)
+                .build();
         localDevice().mService.getHdmiCecNetwork().addCecDevice(deviceInfo);
 
         // Consume CEC messages we already got for this newly found device.
diff --git a/services/core/java/com/android/server/hdmi/ReportFeaturesMessage.java b/services/core/java/com/android/server/hdmi/ReportFeaturesMessage.java
new file mode 100644
index 0000000..80bfb96
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ReportFeaturesMessage.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ValidationResult;
+
+import android.annotation.NonNull;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Represents a validated <Report Features> message with parsed parameters.
+ *
+ * Only parses the [CEC Version] and [Device Features] operands.
+ * [All Device Types] and [RC Profile] are not parsed, but can be specified in construction.
+ */
+public class ReportFeaturesMessage extends HdmiCecMessage {
+
+    @HdmiControlManager.HdmiCecVersion
+    private final int mCecVersion;
+
+    @NonNull
+    private final DeviceFeatures mDeviceFeatures;
+
+    private ReportFeaturesMessage(int source, int destination, byte[] params, int cecVersion,
+            DeviceFeatures deviceFeatures) {
+        super(source, destination, Constants.MESSAGE_REPORT_FEATURES, params, OK);
+        mCecVersion = cecVersion;
+        mDeviceFeatures = deviceFeatures;
+    }
+
+    /**
+     * Static factory method. Intended for constructing outgoing or test messages, as it uses
+     * structured types instead of raw bytes to construct the parameters.
+     */
+    public static HdmiCecMessage build(
+            int source,
+            @HdmiControlManager.HdmiCecVersion int cecVersion,
+            List<Integer> allDeviceTypes,
+            @Constants.RcProfile int rcProfile,
+            List<Integer> rcFeatures,
+            DeviceFeatures deviceFeatures) {
+
+        byte cecVersionByte = (byte) (cecVersion & 0xFF);
+        byte deviceTypes = 0;
+        for (Integer deviceType : allDeviceTypes) {
+            deviceTypes |= (byte) (1 << hdmiDeviceInfoDeviceTypeToShiftValue(deviceType));
+        }
+
+        byte rcProfileByte = 0;
+        rcProfileByte |= (byte) (rcProfile << 6);
+        if (rcProfile == Constants.RC_PROFILE_SOURCE) {
+            for (@Constants.RcProfileSource Integer rcFeature : rcFeatures) {
+                rcProfileByte |= (byte) (1 << rcFeature);
+            }
+        } else {
+            @Constants.RcProfileTv byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF);
+            rcProfileByte |= rcProfileTv;
+        }
+
+        byte[] fixedOperands = {cecVersionByte, deviceTypes, rcProfileByte};
+        byte[] deviceFeaturesBytes = deviceFeatures.toOperand();
+
+        // Concatenate fixed length operands and [Device Features]
+        byte[] params = Arrays.copyOf(fixedOperands,
+                fixedOperands.length + deviceFeaturesBytes.length);
+        System.arraycopy(deviceFeaturesBytes, 0, params,
+                fixedOperands.length, deviceFeaturesBytes.length);
+
+        @ValidationResult
+        int addressValidationResult = validateAddress(source, Constants.ADDR_BROADCAST);
+        if (addressValidationResult != OK) {
+            return new HdmiCecMessage(source, Constants.ADDR_BROADCAST,
+                    Constants.MESSAGE_REPORT_FEATURES, params, addressValidationResult);
+        } else {
+            return new ReportFeaturesMessage(source, Constants.ADDR_BROADCAST, params,
+                    cecVersion, deviceFeatures);
+        }
+    }
+
+    @Constants.DeviceType
+    private static int hdmiDeviceInfoDeviceTypeToShiftValue(int deviceType) {
+        switch (deviceType) {
+            case HdmiDeviceInfo.DEVICE_TV:
+                return Constants.ALL_DEVICE_TYPES_TV;
+            case HdmiDeviceInfo.DEVICE_RECORDER:
+                return Constants.ALL_DEVICE_TYPES_RECORDER;
+            case HdmiDeviceInfo.DEVICE_TUNER:
+                return Constants.ALL_DEVICE_TYPES_TUNER;
+            case HdmiDeviceInfo.DEVICE_PLAYBACK:
+                return Constants.ALL_DEVICE_TYPES_PLAYBACK;
+            case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
+                return Constants.ALL_DEVICE_TYPES_AUDIO_SYSTEM;
+            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
+                return Constants.ALL_DEVICE_TYPES_SWITCH;
+            default:
+                throw new IllegalArgumentException("Unhandled device type: " + deviceType);
+        }
+    }
+
+    /**
+     * Must only be called from {@link HdmiCecMessage#build}.
+     *
+     * Parses and validates CEC message data as a <Report Features> message. Intended for
+     * constructing a representation of an incoming message, as it takes raw bytes for parameters.
+     *
+     * If successful, returns an instance of {@link ReportFeaturesMessage}.
+     * If unsuccessful, returns an {@link HdmiCecMessage} with the reason for validation failure
+     * accessible through {@link HdmiCecMessage#getValidationResult}.
+     */
+    static HdmiCecMessage build(int source, int destination, byte[] params) {
+        // Helper function for building a message that failed validation
+        Function<Integer, HdmiCecMessage> invalidMessage =
+                validationResult -> new HdmiCecMessage(source, destination,
+                        Constants.MESSAGE_REPORT_FEATURES, params, validationResult);
+
+        @ValidationResult int addressValidationResult = validateAddress(source, destination);
+        if (addressValidationResult != OK) {
+            return invalidMessage.apply(addressValidationResult);
+        }
+
+        if (params.length < 4) {
+            return invalidMessage.apply(ERROR_PARAMETER_SHORT);
+        }
+
+        int cecVersion = Byte.toUnsignedInt(params[0]);
+
+        int rcProfileEnd = HdmiUtils.getEndOfSequence(params, 2);
+        if (rcProfileEnd == -1) {
+            return invalidMessage.apply(ERROR_PARAMETER_SHORT);
+        }
+        int deviceFeaturesEnd = HdmiUtils.getEndOfSequence(
+                params, rcProfileEnd + 1);
+        if (deviceFeaturesEnd == -1) {
+            return invalidMessage.apply(ERROR_PARAMETER_SHORT);
+        }
+        int deviceFeaturesStart = HdmiUtils.getEndOfSequence(params, 2) + 1;
+        byte[] deviceFeaturesBytes = Arrays.copyOfRange(params, deviceFeaturesStart, params.length);
+        DeviceFeatures deviceFeatures = DeviceFeatures.fromOperand(deviceFeaturesBytes);
+
+        return new ReportFeaturesMessage(source, destination, params, cecVersion, deviceFeatures);
+    }
+
+    /**
+     * Validates the source and destination addresses for a <Report Features> message.
+     */
+    public static int validateAddress(int source, int destination) {
+        return HdmiCecMessageValidator.validateAddress(source, destination,
+                HdmiCecMessageValidator.DEST_BROADCAST);
+    }
+
+    /**
+     * Returns the contents of the [CEC Version] operand: the version number of the CEC
+     * specification which was used to design the device.
+     */
+    @HdmiControlManager.HdmiCecVersion
+    public int getCecVersion() {
+        return mCecVersion;
+    }
+
+    /**
+     * Returns the contents of the [Device Features] operand: the set of features supported by
+     * the device.
+     */
+    public DeviceFeatures getDeviceFeatures() {
+        return mDeviceFeatures;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java
new file mode 100644
index 0000000..5154669
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+
+import static com.android.server.hdmi.Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL;
+
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Determines whether a target device supports the <Set Audio Volume Level> message.
+ *
+ * Sends the device <Set Audio Volume Level>[0x7F]. The value 0x7F is defined by the spec such that
+ * setting the volume to this level results in no change to the current volume level.
+ *
+ * The target device supports <Set Audio Volume Level> only if it does not respond with
+ * <Feature Abort> within {@link HdmiConfig.TIMEOUT_MS} milliseconds.
+ */
+public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction {
+    private static final String TAG = "SetAudioVolumeLevelDiscoveryAction";
+
+    private static final int STATE_WAITING_FOR_FEATURE_ABORT = 1;
+
+    private final int mTargetAddress;
+
+    public SetAudioVolumeLevelDiscoveryAction(HdmiCecLocalDevice source,
+            int targetAddress, IHdmiControlCallback callback) {
+        super(source, callback);
+
+        mTargetAddress = targetAddress;
+    }
+
+    boolean start() {
+        sendCommand(SetAudioVolumeLevelMessage.build(
+                getSourceAddress(), mTargetAddress, Constants.AUDIO_VOLUME_STATUS_UNKNOWN),
+                result -> {
+                    if (result == SendMessageResult.SUCCESS) {
+                        // Message sent successfully; wait for <Feature Abort> in response
+                        mState = STATE_WAITING_FOR_FEATURE_ABORT;
+                        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+                    } else {
+                        finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+                    }
+                });
+        return true;
+    }
+
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_FEATURE_ABORT) {
+            return false;
+        }
+        switch (cmd.getOpcode()) {
+            case Constants.MESSAGE_FEATURE_ABORT:
+                return handleFeatureAbort(cmd);
+            default:
+                return false;
+        }
+    }
+
+    private boolean handleFeatureAbort(HdmiCecMessage cmd) {
+        int originalOpcode = cmd.getParams()[0] & 0xFF;
+        if (originalOpcode == MESSAGE_SET_AUDIO_VOLUME_LEVEL && cmd.getSource() == mTargetAddress) {
+            if (updateAvcSupport(FEATURE_NOT_SUPPORTED)) {
+                finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+            } else {
+                finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    void handleTimerEvent(int state) {
+        if (updateAvcSupport(FEATURE_SUPPORTED)) {
+            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+        } else {
+            finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
+        }
+    }
+
+    /**
+     * Updates the System Audio device's support for <Set Audio Volume Level> in the
+     * {@link HdmiCecNetwork}. Can fail if the System Audio device is not in our
+     * {@link HdmiCecNetwork}.
+     *
+     * @return Whether support was successfully updated in the network.
+     */
+    private boolean updateAvcSupport(
+            @DeviceFeatures.FeatureSupportStatus int setAudioVolumeLevelSupport) {
+        HdmiCecNetwork network = source().mService.getHdmiCecNetwork();
+        HdmiDeviceInfo currentDeviceInfo = network.getCecDeviceInfo(mTargetAddress);
+
+        if (currentDeviceInfo == null) {
+            return false;
+        } else {
+            network.updateCecDevice(
+                    currentDeviceInfo.toBuilder()
+                            .setDeviceFeatures(currentDeviceInfo.getDeviceFeatures().toBuilder()
+                                    .setSetAudioVolumeLevelSupport(setAudioVolumeLevelSupport)
+                                    .build())
+                            .build()
+            );
+            return true;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelMessage.java b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelMessage.java
new file mode 100644
index 0000000..2ec0e7f
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelMessage.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ValidationResult;
+
+/**
+ * Represents a validated <Set Audio Volume Level> message with parsed parameters.
+ */
+public class SetAudioVolumeLevelMessage extends HdmiCecMessage {
+    private final int mAudioVolumeLevel;
+
+    private SetAudioVolumeLevelMessage(int source, int destination, byte[] params,
+            int audioVolumeLevel) {
+        super(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, params, OK);
+        mAudioVolumeLevel = audioVolumeLevel;
+    }
+
+    /**
+     * Static factory method. Intended for constructing outgoing or test messages, as it uses
+     * structured types instead of raw bytes to construct the parameters.
+     *
+     * @param source Initiator address. Cannot be {@link Constants#ADDR_UNREGISTERED}
+     * @param destination Destination address. Cannot be {@link Constants#ADDR_BROADCAST}
+     * @param audioVolumeLevel [Audio Volume Level]. Either 0x7F (representing no volume change)
+     *                         or between 0 and 100 inclusive (representing volume percentage).
+     */
+    public static HdmiCecMessage build(int source, int destination, int audioVolumeLevel) {
+        byte[] params = { (byte) (audioVolumeLevel & 0xFF) };
+
+        @ValidationResult
+        int addressValidationResult = validateAddress(source, destination);
+        if (addressValidationResult == OK) {
+            return new SetAudioVolumeLevelMessage(source, destination, params, audioVolumeLevel);
+        } else {
+            return new HdmiCecMessage(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                    params, addressValidationResult);
+        }
+    }
+
+    /**
+     * Must only be called from {@link HdmiCecMessage#build}.
+     *
+     * Parses and validates CEC message data as a <SetAudioVolumeLevel> message. Intended for
+     * constructing a representation of an incoming message, as it takes raw bytes for
+     * parameters.
+     *
+     * If successful, returns an instance of {@link SetAudioVolumeLevelMessage}.
+     * If unsuccessful, returns an {@link HdmiCecMessage} with the reason for validation failure
+     * accessible through {@link HdmiCecMessage#getValidationResult}.
+     */
+    public static HdmiCecMessage build(int source, int destination, byte[] params) {
+        if (params.length == 0) {
+            return new HdmiCecMessage(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                    params, ERROR_PARAMETER_SHORT);
+        }
+
+        int audioVolumeLevel = params[0];
+
+        @ValidationResult
+        int addressValidationResult = validateAddress(source, destination);
+        if (addressValidationResult == OK) {
+            return new SetAudioVolumeLevelMessage(source, destination, params, audioVolumeLevel);
+        } else {
+            return new HdmiCecMessage(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                    params, addressValidationResult);
+        }
+    }
+
+    /**
+     * Validates the source and destination addresses for a <Set Audio Volume Level> message.
+     */
+    public static int validateAddress(int source, int destination) {
+        return HdmiCecMessageValidator.validateAddress(source, destination,
+                HdmiCecMessageValidator.DEST_DIRECT);
+    }
+
+    /**
+     * Returns the contents of the [Audio Volume Level] operand:
+     * either 0x7F, indicating no change to the current volume level,
+     * or a percentage between 0 and 100 (inclusive).
+     */
+    public int getAudioVolumeLevel() {
+        return mAudioVolumeLevel;
+    }
+}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 1461675..9e00f95 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -509,7 +509,7 @@
      *
      * <pre><code>
      * resolver.registerContentObserver(Settings.Global.getUriFor(
-     *     Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
+     *     Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
      *     UserHandle.USER_ALL);
      * </code></pre>
      *
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 261aa32..2dd7a10 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.input;
 
+import static android.view.KeyEvent.KEYCODE_UNKNOWN;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -38,6 +40,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayViewport;
@@ -269,6 +272,10 @@
     private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>();
     @GuardedBy("mAssociationLock")
     private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
+    private final Object mPointerDisplayIdLock = new Object();
+    // Forces the MouseCursorController to target a specific display id.
+    @GuardedBy("mPointerDisplayIdLock")
+    private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
 
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
@@ -284,6 +291,8 @@
             int deviceId, int sourceMask, int sw);
     private static native boolean nativeHasKeys(long ptr,
             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
+    private static native int nativeGetKeyCodeForKeyLocation(long ptr, int deviceId,
+            int locationKeyCode);
     private static native InputChannel nativeCreateInputChannel(long ptr, String name);
     private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId,
             boolean isGestureMonitor, String name, int pid);
@@ -341,6 +350,9 @@
     private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId);
     private static native void nativeNotifyPortAssociationsChanged(long ptr);
     private static native void nativeChangeUniqueIdAssociation(long ptr);
+    private static native void nativeNotifyPointerDisplayIdChanged(long ptr);
+    private static native void nativeSetDisplayEligibilityForPointerCapture(long ptr, int displayId,
+            boolean enabled);
     private static native void nativeSetMotionClassifierEnabled(long ptr, boolean enabled);
     private static native InputSensorInfo[] nativeGetSensorList(long ptr, int deviceId);
     private static native boolean nativeFlushSensor(long ptr, int deviceId, int sensorType);
@@ -658,6 +670,22 @@
     }
 
     /**
+     * Returns the keyCode generated by the specified location on a US keyboard layout.
+     * This takes into consideration the currently active keyboard layout.
+     *
+     * @param deviceId The input device id.
+     * @param locationKeyCode The location of a key on a US keyboard layout.
+     * @return The KeyCode this physical key location produces.
+     */
+    @Override // Binder call
+    public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
+        if (locationKeyCode <= KEYCODE_UNKNOWN || locationKeyCode > KeyEvent.getMaxKeyCode()) {
+            return KEYCODE_UNKNOWN;
+        }
+        return nativeGetKeyCodeForKeyLocation(mPtr, deviceId, locationKeyCode);
+    }
+
+    /**
      * Transfer the current touch gesture to the provided window.
      *
      * @param destChannelToken The token of the window or input channel that should receive the
@@ -1902,6 +1930,18 @@
         return result;
     }
 
+    private void setVirtualMousePointerDisplayId(int displayId) {
+        synchronized (mPointerDisplayIdLock) {
+            mOverriddenPointerDisplayId = displayId;
+        }
+        // TODO(b/215597605): trigger MousePositionTracker update
+        nativeNotifyPointerDisplayIdChanged(mPtr);
+    }
+
+    private void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
+        nativeSetDisplayEligibilityForPointerCapture(mPtr, displayId, isEligible);
+    }
+
     private static class VibrationInfo {
         private final long[] mPattern;
         private final int[] mAmplitudes;
@@ -2575,6 +2615,7 @@
         synchronized (mInputFilterLock) { }
         synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
         synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
+        synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ }
         nativeMonitor(mPtr);
     }
 
@@ -2965,6 +3006,12 @@
 
     // Native callback.
     private int getPointerDisplayId() {
+        synchronized (mPointerDisplayIdLock) {
+            // Prefer the override to all other displays.
+            if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
+                return mOverriddenPointerDisplayId;
+            }
+        }
         return mWindowManagerCallbacks.getPointerDisplayId();
     }
 
@@ -3109,6 +3156,9 @@
 
         int getPointerDisplayId();
 
+        /** Gets the x and y coordinates of the cursor's current position. */
+        PointF getCursorPosition();
+
         /**
          * Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
          * occurred on a window that did not have focus.
@@ -3427,6 +3477,21 @@
         }
 
         @Override
+        public void setVirtualMousePointerDisplayId(int pointerDisplayId) {
+            InputManagerService.this.setVirtualMousePointerDisplayId(pointerDisplayId);
+        }
+
+        @Override
+        public PointF getCursorPosition() {
+            return mWindowManagerCallbacks.getCursorPosition();
+        }
+
+        @Override
+        public void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
+            InputManagerService.this.setDisplayEligibilityForPointerCapture(displayId, isEligible);
+        }
+
+        @Override
         public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
             registerLidSwitchCallbackInternal(callbacks);
         }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
new file mode 100644
index 0000000..c86ebd2
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Slog;
+import android.view.InputChannel;
+import android.view.MotionEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethod;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
+
+import java.util.List;
+
+/**
+ * A wrapper class to invoke IPCs defined in {@link IInputMethod}.
+ */
+final class IInputMethodInvoker {
+    private static final String TAG = InputMethodManagerService.TAG;
+    private static final boolean DEBUG = InputMethodManagerService.DEBUG;
+
+    @AnyThread
+    @Nullable
+    static IInputMethodInvoker create(@Nullable IInputMethod inputMethod) {
+        if (inputMethod == null) {
+            return null;
+        }
+        if (!Binder.isProxy(inputMethod)) {
+            // IInputMethodInvoker must be used only within the system_server and InputMethodService
+            // must not be running in the system_server.  Therefore, "inputMethod" must be a Proxy.
+            throw new UnsupportedOperationException(inputMethod + " must have been a BinderProxy.");
+        }
+        return new IInputMethodInvoker(inputMethod);
+    }
+
+    /**
+     * A simplified version of {@link android.os.Debug#getCaller()}.
+     *
+     * @return method name of the caller.
+     */
+    @AnyThread
+    private static String getCallerMethodName() {
+        final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+        if (callStack.length <= 4) {
+            return "<bottom of call stack>";
+        }
+        return callStack[4].getMethodName();
+    }
+
+    @AnyThread
+    private static void logRemoteException(@NonNull RemoteException e) {
+        if (DEBUG || !(e instanceof DeadObjectException)) {
+            Slog.w(TAG, "IPC failed at IInputMethodInvoker#" + getCallerMethodName(), e);
+        }
+    }
+
+    @AnyThread
+    static int getBinderIdentityHashCode(@Nullable IInputMethodInvoker obj) {
+        if (obj == null) {
+            return 0;
+        }
+
+        return System.identityHashCode(obj.mTarget);
+    }
+
+    @NonNull
+    private final IInputMethod mTarget;
+
+    private IInputMethodInvoker(@NonNull IInputMethod target) {
+        mTarget = target;
+    }
+
+    @AnyThread
+    @NonNull
+    IBinder asBinder() {
+        return mTarget.asBinder();
+    }
+
+    @AnyThread
+    void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
+            int configChanges, boolean stylusHwSupported) {
+        try {
+            mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo,
+            IInlineSuggestionsRequestCallback cb) {
+        try {
+            mTarget.onCreateInlineSuggestionsRequest(requestInfo, cb);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void bindInput(InputBinding binding) {
+        try {
+            mTarget.bindInput(binding);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void unbindInput() {
+        try {
+            mTarget.unbindInput();
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
+            boolean restarting) {
+        try {
+            mTarget.startInput(startInputToken, inputContext, attribute, restarting);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void createSession(InputChannel channel, IInputSessionCallback callback) {
+        try {
+            mTarget.createSession(channel, callback);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void setSessionEnabled(IInputMethodSession session, boolean enabled) {
+        try {
+            mTarget.setSessionEnabled(session, enabled);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    // TODO(b/192412909): Convert this back to void method
+    @AnyThread
+    boolean showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
+        try {
+            mTarget.showSoftInput(showInputToken, flags, resultReceiver);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+            return false;
+        }
+        return true;
+    }
+
+    // TODO(b/192412909): Convert this back to void method
+    @AnyThread
+    boolean hideSoftInput(IBinder hideInputToken, int flags, ResultReceiver resultReceiver) {
+        try {
+            mTarget.hideSoftInput(hideInputToken, flags, resultReceiver);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+            return false;
+        }
+        return true;
+    }
+
+    @AnyThread
+    void changeInputMethodSubtype(InputMethodSubtype subtype) {
+        try {
+            mTarget.changeInputMethodSubtype(subtype);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void canStartStylusHandwriting(int requestId) {
+        try {
+            mTarget.canStartStylusHandwriting(requestId);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+
+    @AnyThread
+    void startStylusHandwriting(InputChannel channel, List<MotionEvent> events) {
+        try {
+            mTarget.startStylusHandwriting(channel, events);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 220d790..2230dcd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -18,8 +18,6 @@
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
-import static com.android.server.inputmethod.InputMethodManagerService.MSG_INITIALIZE_IME;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -77,7 +75,7 @@
     @GuardedBy("ImfLock.class") @Nullable private String mCurId;
     @GuardedBy("ImfLock.class") @Nullable private String mSelectedMethodId;
     @GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent;
-    @GuardedBy("ImfLock.class") @Nullable private IInputMethod mCurMethod;
+    @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
     @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
     @GuardedBy("ImfLock.class") private IBinder mCurToken;
     @GuardedBy("ImfLock.class") private int mCurSeq;
@@ -243,7 +241,7 @@
      */
     @GuardedBy("ImfLock.class")
     @Nullable
-    IInputMethod getCurMethod() {
+    IInputMethodInvoker getCurMethod() {
         return mCurMethod;
     }
 
@@ -264,6 +262,13 @@
     }
 
     /**
+     * Returns {@code true} if current IME supports Stylus Handwriting.
+     */
+    boolean supportsStylusHandwriting() {
+        return mSupportsStylusHw;
+    }
+
+    /**
      * Used to bring IME service up to visible adjustment while it is being shown.
      */
     @GuardedBy("ImfLock.class")
@@ -293,7 +298,7 @@
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected");
             synchronized (ImfLock.class) {
                 if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
-                    mCurMethod = IInputMethod.Stub.asInterface(service);
+                    mCurMethod = IInputMethodInvoker.create(IInputMethod.Stub.asInterface(service));
                     updateCurrentMethodUid();
                     if (mCurToken == null) {
                         Slog.w(TAG, "Service connected without a token!");
@@ -302,15 +307,14 @@
                         return;
                     }
                     if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
-                    // Dispatch display id for InputMethodService to update context display.
-                    mService.executeOrSendMessage(mCurMethod,
-                            mService.mCaller.obtainMessageIOO(MSG_INITIALIZE_IME,
-                                    mMethodMap.get(mSelectedMethodId).getConfigChanges(),
-                                    mCurMethod, mCurToken));
+                    final InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
+                    mSupportsStylusHw = info.supportsStylusHandwriting();
+                    mService.initializeImeLocked(mCurMethod, mCurToken, info.getConfigChanges(),
+                            mSupportsStylusHw);
                     mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
                     mService.reRequestCurrentClientSessionLocked();
                 }
-                mSupportsStylusHw = mMethodMap.get(mSelectedMethodId).supportsStylusHandwriting();
+
                 if (mSupportsStylusHw) {
                     // TODO init Handwriting spy.
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c87dc89..0d41a37 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -97,7 +97,6 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IInterface;
 import android.os.LocaleList;
 import android.os.Message;
 import android.os.Parcel;
@@ -168,7 +167,6 @@
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInlineSuggestionsResponseCallback;
 import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethod;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
@@ -219,37 +217,25 @@
         int FAILURE = -1;
     }
 
-    static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
-    static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
-    static final int MSG_SHOW_IM_CONFIG = 3;
+    private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
 
-    static final int MSG_UNBIND_INPUT = 1000;
-    static final int MSG_BIND_INPUT = 1010;
-    static final int MSG_SHOW_SOFT_INPUT = 1020;
-    static final int MSG_HIDE_SOFT_INPUT = 1030;
-    static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
-    static final int MSG_INITIALIZE_IME = 1040;
-    static final int MSG_CREATE_SESSION = 1050;
-    static final int MSG_REMOVE_IME_SURFACE = 1060;
-    static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
-    static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
+    private static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
+    private static final int MSG_REMOVE_IME_SURFACE = 1060;
+    private static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
+    private static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
 
-    static final int MSG_START_INPUT = 2000;
+    private static final int MSG_UNBIND_CLIENT = 3000;
+    private static final int MSG_BIND_CLIENT = 3010;
+    private static final int MSG_SET_ACTIVE = 3020;
+    private static final int MSG_SET_INTERACTIVE = 3030;
+    private static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
 
-    static final int MSG_UNBIND_CLIENT = 3000;
-    static final int MSG_BIND_CLIENT = 3010;
-    static final int MSG_SET_ACTIVE = 3020;
-    static final int MSG_SET_INTERACTIVE = 3030;
-    static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
+    private static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
 
-    static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
+    private static final int MSG_SYSTEM_UNLOCK_USER = 5000;
+    private static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
 
-    static final int MSG_SYSTEM_UNLOCK_USER = 5000;
-    static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
-
-    static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
-
-    static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
+    private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
 
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
@@ -277,14 +263,14 @@
 
     final Context mContext;
     final Resources mRes;
-    final Handler mHandler;
+    private final Handler mHandler;
     final InputMethodSettings mSettings;
     final SettingsObserver mSettingsObserver;
     final IWindowManager mIWindowManager;
     final WindowManagerInternal mWindowManagerInternal;
     final PackageManagerInternal mPackageManagerInternal;
     final InputManagerInternal mInputManagerInternal;
-    final HandlerCaller mCaller;
+    private final HandlerCaller mCaller;
     final boolean mHasFeature;
     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
             new ArrayMap<>();
@@ -315,6 +301,12 @@
     private int mMethodMapUpdateCount = 0;
 
     /**
+     * Tracks requestIds for Stylus handwriting mode.
+     */
+    @GuardedBy("ImfLock.class")
+    private int mHwRequestId = 0;
+
+    /**
      * The display id for which the latest startInput was called.
      */
     @GuardedBy("ImfLock.class")
@@ -336,7 +328,7 @@
 
     static class SessionState {
         final ClientState client;
-        final IInputMethod method;
+        final IInputMethodInvoker method;
 
         IInputMethodSession session;
         InputChannel channel;
@@ -345,14 +337,14 @@
         public String toString() {
             return "SessionState{uid " + client.uid + " pid " + client.pid
                     + " method " + Integer.toHexString(
-                            System.identityHashCode(method))
+                            IInputMethodInvoker.getBinderIdentityHashCode(method))
                     + " session " + Integer.toHexString(
                             System.identityHashCode(session))
                     + " channel " + channel
                     + "}";
         }
 
-        SessionState(ClientState _client, IInputMethod _method,
+        SessionState(ClientState _client, IInputMethodInvoker _method,
                 IInputMethodSession _session, InputChannel _channel) {
             client = _client;
             method = _method;
@@ -620,7 +612,7 @@
      */
     @GuardedBy("ImfLock.class")
     @Nullable
-    private IInputMethod getCurMethodLocked() {
+    private IInputMethodInvoker getCurMethodLocked() {
         return mBindingController.getCurMethod();
     }
 
@@ -647,9 +639,9 @@
     boolean mBoundToMethod;
 
     /**
-     * Currently enabled session.  Only touched by service thread, not
-     * protected by a lock.
+     * Currently enabled session.
      */
+    @GuardedBy("ImfLock.class")
     SessionState mEnabledSession;
 
     /**
@@ -698,11 +690,11 @@
             new CopyOnWriteArrayList<>();
 
     /**
-     * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
-     * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
-     * will not affect those tasks that are already posted.
+     * Internal state snapshot when
+     * {@link com.android.internal.view.IInputMethod#startInput(IBinder, IInputContext, EditorInfo,
+     * boolean)} is about to be called.
      *
-     * <p>Posting {@link #MSG_START_INPUT} message basically means that
+     * <p>Calling that IPC endpoint basically means that
      * {@link InputMethodService#doStartInput(InputConnection, EditorInfo, boolean)} will be called
      * back in the current IME process shortly, which will also affect what the current IME starts
      * receiving from {@link InputMethodService#getCurrentInputConnection()}. In other words, this
@@ -778,7 +770,7 @@
             final int mFocusedWindowSoftInputMode;
             @SoftInputShowHideReason
             final int mReason;
-            // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT.
+            // The timing of handling showCurrentInputLocked() or hideCurrentInputLocked().
             final long mTimestamp;
             final long mWallTime;
             final boolean mInFullscreenMode;
@@ -1457,31 +1449,6 @@
         }
     }
 
-    private static final class MethodCallback extends IInputSessionCallback.Stub {
-        private final InputMethodManagerService mParentIMMS;
-        private final IInputMethod mMethod;
-        private final InputChannel mChannel;
-
-        MethodCallback(InputMethodManagerService imms, IInputMethod method,
-                InputChannel channel) {
-            mParentIMMS = imms;
-            mMethod = method;
-            mChannel = channel;
-        }
-
-        @Override
-        public void sessionCreated(IInputMethodSession session) {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated");
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mParentIMMS.onSessionCreated(mMethod, session, mChannel);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
     private static final class UserSwitchHandlerTask implements Runnable {
         final InputMethodManagerService mService;
 
@@ -1593,7 +1560,7 @@
             mHandler.removeCallbacks(mUserSwitchHandlerTask);
         }
         // Hide soft input before user switch task since switch task may block main handler a while
-        // and delayed the MSG_HIDE_SOFT_INPUT.
+        // and delayed the hideCurrentInputLocked().
         hideCurrentInputLocked(
                 mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
         final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
@@ -1799,10 +1766,10 @@
                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
                         com.android.internal.R.bool.show_ongoing_ime_switcher);
                 if (mShowOngoingImeSwitcherForPhones) {
-                    final InputMethodMenuController.HardKeyboardListener hardKeyboardListener =
-                            mMenuController.getHardKeyboardListener();
-                    mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
-                            hardKeyboardListener);
+                    mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(available -> {
+                        mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
+                                available ? 1 : 0, 0 /* unused */).sendToTarget();
+                    });
                 }
 
                 mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
@@ -1983,15 +1950,14 @@
             InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
         final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
         try {
-            IInputMethod curMethod = getCurMethodLocked();
+            IInputMethodInvoker curMethod = getCurMethodLocked();
             if (userId == mSettings.getCurrentUserId() && imi != null
                     && imi.isInlineSuggestionsEnabled() && curMethod != null) {
-                executeOrSendMessage(curMethod,
-                        mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, curMethod,
-                                requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
-                                        imi.getPackageName(), mCurTokenDisplayId,
-                                        getCurTokenLocked(),
-                                        this)));
+                final IInlineSuggestionsRequestCallback callbackImpl =
+                        new InlineSuggestionsRequestCallbackDecorator(callback,
+                                imi.getPackageName(), mCurTokenDisplayId, getCurTokenLocked(),
+                                this);
+                curMethod.onCreateInlineSuggestionsRequest(requestInfo, callbackImpl);
             } else {
                 callback.onInlineSuggestionsUnsupported();
             }
@@ -2219,10 +2185,9 @@
                             mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
                     if (mBoundToMethod) {
                         mBoundToMethod = false;
-                        IInputMethod curMethod = getCurMethodLocked();
+                        IInputMethodInvoker curMethod = getCurMethodLocked();
                         if (curMethod != null) {
-                            executeOrSendMessage(curMethod, mCaller.obtainMessageO(
-                                    MSG_UNBIND_INPUT, curMethod));
+                            curMethod.unbindInput();
                         }
                     }
                     mCurClient = null;
@@ -2234,8 +2199,14 @@
         }
     }
 
-    void executeOrSendMessage(IInterface target, Message msg) {
+    private void executeOrSendMessage(IInputMethodClient target, Message msg) {
          if (target.asBinder() instanceof Binder) {
+             // This is supposed to be emulating the one-way semantics when the IME client is
+             // system_server itself, which has not been explicitly prohibited so far while we have
+             // never ever officially supported such a use case...
+             // We probably should create a simple wrapper of IInputMethodClient as the first step
+             // to get rid of executeOrSendMessage() then should prohibit system_server to be the
+             // IME client for long term.
              mCaller.sendMessage(msg);
          } else {
              handleMessage(msg);
@@ -2250,10 +2221,9 @@
                     + mCurClient.client.asBinder());
             if (mBoundToMethod) {
                 mBoundToMethod = false;
-                IInputMethod curMethod = getCurMethodLocked();
+                IInputMethodInvoker curMethod = getCurMethodLocked();
                 if (curMethod != null) {
-                    executeOrSendMessage(curMethod, mCaller.obtainMessageO(
-                            MSG_UNBIND_INPUT, curMethod));
+                    curMethod.unbindInput();
                 }
             }
 
@@ -2302,16 +2272,15 @@
     @NonNull
     InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
         if (!mBoundToMethod) {
-            IInputMethod curMethod = getCurMethodLocked();
-            executeOrSendMessage(curMethod, mCaller.obtainMessageOO(
-                    MSG_BIND_INPUT, curMethod, mCurClient.binding));
+            getCurMethodLocked().bindInput(mCurClient.binding);
             mBoundToMethod = true;
         }
 
+        final boolean restarting = !initial;
         final Binder startInputToken = new Binder();
         final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(),
                 getCurTokenLocked(),
-                mCurTokenDisplayId, getCurIdLocked(), startInputReason, !initial,
+                mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
                 UserHandle.getUserId(mCurClient.uid), mCurClient.selfReportedDisplayId,
                 mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
                 getSequenceNumberLocked());
@@ -2330,9 +2299,9 @@
         }
 
         final SessionState session = mCurClient.curSession;
-        executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
-                MSG_START_INPUT, 0 /* unused */, initial ? 0 : 1 /* restarting */,
-                startInputToken, session, mCurInputContext, mCurAttribute));
+        setEnabledSessionLocked(session);
+        session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting);
+
         if (mShowRequested) {
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2348,11 +2317,20 @@
                 curId, getSequenceNumberLocked(), suppressesSpellChecker);
     }
 
+    /**
+     * Called by {@link #startInputOrWindowGainedFocusInternalLocked} to bind/unbind/attach the
+     * selected InputMethod to the given focused IME client.
+     *
+     * Note that this should be called after validating if the IME client has IME focus.
+     *
+     * @see WindowManagerInternal#hasInputMethodClientFocus(IBinder, int, int, int)
+     */
     @GuardedBy("ImfLock.class")
     @NonNull
-    InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
-            @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
-            @StartInputReason int startInputReason, int unverifiedTargetSdkVersion) {
+    private InputBindResult startInputUncheckedLocked(@NonNull ClientState cs,
+            IInputContext inputContext, @NonNull EditorInfo attribute,
+            @StartInputFlags int startInputFlags, @StartInputReason int startInputReason,
+            int unverifiedTargetSdkVersion) {
         // If no method is currently selected, do nothing.
         String selectedMethodId = getSelectedMethodIdLocked();
         if (selectedMethodId == null) {
@@ -2374,10 +2352,6 @@
             return InputBindResult.INVALID_PACKAGE_NAME;
         }
 
-        if (!mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid)) {
-            // Wait, the client no longer has access to the display.
-            return InputBindResult.INVALID_DISPLAY_ID;
-        }
         // Compute the final shown display ID with validated cs.selfReportedDisplayId for this
         // session & other conditions.
         mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
@@ -2519,40 +2493,58 @@
         }
     }
 
+    @GuardedBy("ImfLock.class")
+    void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token,
+            @android.content.pm.ActivityInfo.Config int configChanges, boolean supportStylusHw) {
+        if (DEBUG) {
+            Slog.v(TAG, "Sending attach of token: " + token + " for display: "
+                    + mCurTokenDisplayId);
+        }
+        inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
+                configChanges, supportStylusHw);
+    }
+
     @AnyThread
     void scheduleNotifyImeUidToAudioService(int uid) {
         mCaller.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE);
         mCaller.obtainMessageI(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid).sendToTarget();
     }
 
-    void onSessionCreated(IInputMethod method, IInputMethodSession session,
+    @BinderThread
+    void onSessionCreated(IInputMethodInvoker method, IInputMethodSession session,
             InputChannel channel) {
-        synchronized (ImfLock.class) {
-            if (mUserSwitchHandlerTask != null) {
-                // We have a pending user-switching task so it's better to just ignore this session.
-                channel.dispose();
-                return;
-            }
-            IInputMethod curMethod = getCurMethodLocked();
-            if (curMethod != null && method != null
-                    && curMethod.asBinder() == method.asBinder()) {
-                if (mCurClient != null) {
-                    clearClientSessionLocked(mCurClient);
-                    mCurClient.curSession = new SessionState(mCurClient,
-                            method, session, channel);
-                    InputBindResult res = attachNewInputLocked(
-                            StartInputReason.SESSION_CREATED_BY_IME, true);
-                    if (res.method != null) {
-                        executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
-                                MSG_BIND_CLIENT, mCurClient.client, res));
-                    }
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onSessionCreated");
+        try {
+            synchronized (ImfLock.class) {
+                if (mUserSwitchHandlerTask != null) {
+                    // We have a pending user-switching task so it's better to just ignore this
+                    // session.
+                    channel.dispose();
                     return;
                 }
+                IInputMethodInvoker curMethod = getCurMethodLocked();
+                if (curMethod != null && method != null
+                        && curMethod.asBinder() == method.asBinder()) {
+                    if (mCurClient != null) {
+                        clearClientSessionLocked(mCurClient);
+                        mCurClient.curSession = new SessionState(mCurClient,
+                                method, session, channel);
+                        InputBindResult res = attachNewInputLocked(
+                                StartInputReason.SESSION_CREATED_BY_IME, true);
+                        if (res.method != null) {
+                            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
+                                    MSG_BIND_CLIENT, mCurClient.client, res));
+                        }
+                        return;
+                    }
+                }
             }
-        }
 
-        // Session abandoned.  Close its associated input channel.
-        channel.dispose();
+            // Session abandoned.  Close its associated input channel.
+            channel.dispose();
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
     }
 
     @GuardedBy("ImfLock.class")
@@ -2584,12 +2576,38 @@
     void requestClientSessionLocked(ClientState cs) {
         if (!cs.sessionRequested) {
             if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
-            InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
+            final InputChannel serverChannel;
+            final InputChannel clientChannel;
+            {
+                final InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
+                serverChannel = channels[0];
+                clientChannel = channels[1];
+            }
+
             cs.sessionRequested = true;
-            IInputMethod curMethod = getCurMethodLocked();
-            executeOrSendMessage(curMethod, mCaller.obtainMessageOOO(
-                    MSG_CREATE_SESSION, curMethod, channels[1],
-                    new MethodCallback(this, curMethod, channels[0])));
+
+            final IInputMethodInvoker curMethod = getCurMethodLocked();
+            final IInputSessionCallback.Stub callback = new IInputSessionCallback.Stub() {
+                @Override
+                public void sessionCreated(IInputMethodSession session) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        onSessionCreated(curMethod, session, serverChannel);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
+            };
+
+            try {
+                curMethod.createSession(clientChannel, callback);
+            } finally {
+                // Dispose the channel because the remote proxy will get its own copy when
+                // unparceled.
+                if (clientChannel != null) {
+                    clientChannel.dispose();
+                }
+            }
         }
     }
 
@@ -2970,14 +2988,10 @@
             }
             if (newSubtype != oldSubtype) {
                 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
-                IInputMethod curMethod = getCurMethodLocked();
+                IInputMethodInvoker curMethod = getCurMethodLocked();
                 if (curMethod != null) {
-                    try {
-                        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
-                        curMethod.changeInputMethodSubtype(newSubtype);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to call changeInputMethodSubtype");
-                    }
+                    updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+                    curMethod.changeInputMethodSubtype(newSubtype);
                 }
             }
             return;
@@ -3019,21 +3033,8 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                if (mCurClient == null || client == null
-                        || mCurClient.client.asBinder() != client.asBinder()) {
-                    // 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());
-                    if (cs == null) {
-                        throw new IllegalArgumentException(
-                                "unknown client " + client.asBinder());
-                    }
-                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
-                            cs.selfReportedDisplayId)) {
-                        Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
-                        return false;
-                    }
+                if (!canInteractWithImeLocked(uid, client, "showSoftInput")) {
+                    return false;
                 }
                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
                 return showCurrentInputLocked(windowToken, flags, resultReceiver, reason);
@@ -3044,6 +3045,40 @@
         }
     }
 
+    @Override
+    public void startStylusHandwriting(IInputMethodClient client) {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.startStylusHandwriting");
+        ImeTracing.getInstance().triggerManagerServiceDump(
+                "InputMethodManagerService#startStylusHandwriting");
+        int uid = Binder.getCallingUid();
+        synchronized (ImfLock.class) {
+            if (!calledFromValidUserLocked()) {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                return;
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (!canInteractWithImeLocked(uid, client, "startStylusHandwriting")) {
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                    return;
+                }
+                if (!mBindingController.supportsStylusHandwriting()) {
+                    Slog.w(TAG, "Stylus HW unsupported by IME. Ignoring startStylusHandwriting()");
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                    return;
+                }
+                if (DEBUG) Slog.v(TAG, "Client requesting Stylus Handwriting to be started");
+                final IInputMethodInvoker curMethod = getCurMethodLocked();
+                if (curMethod != null) {
+                    curMethod.canStartStylusHandwriting(++mHwRequestId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            }
+        }
+    }
+
     @BinderThread
     @Override
     public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
@@ -3086,18 +3121,24 @@
         }
 
         mBindingController.setCurrentMethodVisible();
-        if (getCurMethodLocked() != null) {
+        final IInputMethodInvoker curMethod = getCurMethodLocked();
+        if (curMethod != null) {
             // create a placeholder token for IMS so that IMS cannot inject windows into client app.
             Binder showInputToken = new Binder();
             mShowRequestWindowMap.put(showInputToken, windowToken);
-            IInputMethod curMethod = getCurMethodLocked();
-            executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
-                    getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
-                    showInputToken));
+            final int showFlags = getImeShowFlagsLocked();
+            if (DEBUG) {
+                Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
+                        + ", " + showFlags + ", " + resultReceiver + ") for reason: "
+                        + InputMethodDebug.softInputDisplayReasonToString(reason));
+            }
+            // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
+            if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
+                onShowHideSoftInputRequested(true /* show */, windowToken, reason);
+            }
             mInputShown = true;
             return true;
         }
-
         return false;
     }
 
@@ -3121,14 +3162,11 @@
                     // be made before input is started in it.
                     final ClientState cs = mClients.get(client.asBinder());
                     if (cs == null) {
-                        throw new IllegalArgumentException(
-                                "unknown client " + client.asBinder());
+                        throw new IllegalArgumentException("unknown client " + client.asBinder());
                     }
-                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
-                            cs.selfReportedDisplayId)) {
+                    if (!isImeClientFocused(windowToken, cs)) {
                         if (DEBUG) {
-                            Slog.w(TAG,
-                                    "Ignoring hideSoftInput of uid " + uid + ": " + client);
+                            Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
                         }
                         return false;
                     }
@@ -3165,7 +3203,7 @@
         // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
         // IMMS#InputShown indicates that the software keyboard is shown.
         // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
-        IInputMethod curMethod = getCurMethodLocked();
+        IInputMethodInvoker curMethod = getCurMethodLocked();
         final boolean shouldHideSoftInput = (curMethod != null) && (mInputShown
                 || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
         boolean res;
@@ -3176,8 +3214,15 @@
             // delivered to the IME process as an IPC.  Hence the inconsistency between
             // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
             // the final state.
-            executeOrSendMessage(curMethod, mCaller.obtainMessageIOOO(MSG_HIDE_SOFT_INPUT,
-                    reason, curMethod, resultReceiver, hideInputToken));
+            if (DEBUG) {
+                Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken
+                        + ", " + resultReceiver + ") for reason: "
+                        + InputMethodDebug.softInputDisplayReasonToString(reason));
+            }
+            // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
+            if (curMethod.hideSoftInput(hideInputToken, 0 /* flags */, resultReceiver)) {
+                onShowHideSoftInputRequested(false /* show */, windowToken, reason);
+            }
             res = true;
         } else {
             res = false;
@@ -3190,6 +3235,12 @@
         return res;
     }
 
+    private boolean isImeClientFocused(IBinder windowToken, ClientState cs) {
+        final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
+                windowToken, cs.uid, cs.pid, cs.selfReportedDisplayId);
+        return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
+    }
+
     @NonNull
     @Override
     public InputBindResult startInputOrWindowGainedFocus(
@@ -3283,31 +3334,30 @@
                     + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
         }
 
-        final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-
         final ClientState cs = mClients.get(client.asBinder());
         if (cs == null) {
             throw new IllegalArgumentException("unknown client " + client.asBinder());
         }
-        if (cs.selfReportedDisplayId != windowDisplayId) {
-            Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch."
-                    + " from client:" + cs.selfReportedDisplayId
-                    + " from window:" + windowDisplayId);
-            return InputBindResult.DISPLAY_ID_MISMATCH;
-        }
 
-        if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
-                cs.selfReportedDisplayId)) {
-            // Check with the window manager to make sure this client actually
-            // has a window with focus.  If not, reject.  This is thread safe
-            // because if the focus changes some time before or after, the
-            // next client receiving focus that has any interest in input will
-            // be calling through here after that change happens.
-            if (DEBUG) {
-                Slog.w(TAG, "Focus gain on non-focused client " + cs.client
-                        + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
-            }
-            return InputBindResult.NOT_IME_TARGET_WINDOW;
+        final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
+                windowToken, cs.uid, cs.pid, cs.selfReportedDisplayId);
+        switch (imeClientFocus) {
+            case WindowManagerInternal.ImeClientFocusResult.DISPLAY_ID_MISMATCH:
+                Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch.");
+                return InputBindResult.DISPLAY_ID_MISMATCH;
+            case WindowManagerInternal.ImeClientFocusResult.NOT_IME_TARGET_WINDOW:
+                // Check with the window manager to make sure this client actually
+                // has a window with focus.  If not, reject.  This is thread safe
+                // because if the focus changes some time before or after, the
+                // next client receiving focus that has any interest in input will
+                // be calling through here after that change happens.
+                if (DEBUG) {
+                    Slog.w(TAG, "Focus gain on non-focused client " + cs.client
+                            + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
+                }
+                return InputBindResult.NOT_IME_TARGET_WINDOW;
+            case WindowManagerInternal.ImeClientFocusResult.INVALID_DISPLAY_ID:
+                return InputBindResult.INVALID_DISPLAY_ID;
         }
 
         if (mUserSwitchHandlerTask != null) {
@@ -3523,6 +3573,26 @@
         return res;
     }
 
+    @GuardedBy("ImfLock.class")
+    private boolean canInteractWithImeLocked(
+            int uid, IInputMethodClient client, String methodName) {
+        if (mCurClient == null || client == null
+                || mCurClient.client.asBinder() != client.asBinder()) {
+            // 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());
+            if (cs == null) {
+                throw new IllegalArgumentException("unknown client " + client.asBinder());
+            }
+            if (!isImeClientFocused(mCurFocusedWindow, cs)) {
+                Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
+                return false;
+            }
+        }
+        return true;
+    }
+
     private boolean shouldRestoreImeVisibility(IBinder windowToken,
             @SoftInputModeFlags int softInputMode) {
         switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) {
@@ -3632,8 +3702,7 @@
             if (!calledFromValidUserLocked()) {
                 return;
             }
-            executeOrSendMessage(getCurMethodLocked(), mCaller.obtainMessageO(
-                    MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
+            showInputMethodAndSubtypeEnabler(inputMethodId);
         }
     }
 
@@ -4057,7 +4126,7 @@
         }
     }
 
-    /** Called right after {@link IInputMethod#showSoftInput}. */
+    /** Called right after {@link com.android.internal.view.IInputMethod#showSoftInput}. */
     @GuardedBy("ImfLock.class")
     private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
             @SoftInputShowHideReason int reason) {
@@ -4108,22 +4177,17 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
-    void setEnabledSessionInHandlerThread(SessionState session) {
+    @GuardedBy("ImfLock.class")
+    void setEnabledSessionLocked(SessionState session) {
         if (mEnabledSession != session) {
             if (mEnabledSession != null && mEnabledSession.session != null) {
-                try {
-                    if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
-                    mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
-                } catch (RemoteException e) {
-                }
+                if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
+                mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
             }
             mEnabledSession = session;
             if (mEnabledSession != null && mEnabledSession.session != null) {
-                try {
-                    if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
-                    mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
-                } catch (RemoteException e) {
-                }
+                if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
+                mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
             }
         }
     }
@@ -4156,69 +4220,8 @@
                 mMenuController.showInputMethodMenu(showAuxSubtypes, displayId);
                 return true;
 
-            case MSG_SHOW_IM_SUBTYPE_ENABLER:
-                showInputMethodAndSubtypeEnabler((String)msg.obj);
-                return true;
-
-            case MSG_SHOW_IM_CONFIG:
-                showConfigureInputMethods();
-                return true;
-
             // ---------------------------------------------------------
 
-            case MSG_UNBIND_INPUT:
-                try {
-                    ((IInputMethod)msg.obj).unbindInput();
-                } catch (RemoteException e) {
-                    // There is nothing interesting about the method dying.
-                }
-                return true;
-            case MSG_BIND_INPUT:
-                args = (SomeArgs)msg.obj;
-                try {
-                    ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
-            case MSG_SHOW_SOFT_INPUT:
-                args = (SomeArgs) msg.obj;
-                try {
-                    final @SoftInputShowHideReason int reason = msg.arg2;
-                    if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
-                            + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
-                            + InputMethodDebug.softInputDisplayReasonToString(reason));
-                    final IBinder token = (IBinder) args.arg3;
-                    ((IInputMethod) args.arg1).showSoftInput(
-                            token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
-                    final IBinder requestToken;
-                    synchronized (ImfLock.class) {
-                        requestToken = mShowRequestWindowMap.get(token);
-                        onShowHideSoftInputRequested(true /* show */, requestToken, reason);
-                    }
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
-            case MSG_HIDE_SOFT_INPUT:
-                args = (SomeArgs) msg.obj;
-                try {
-                    final @SoftInputShowHideReason int reason = msg.arg1;
-                    if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
-                            + args.arg3 + ", " + args.arg2 + ") for reason: "
-                            + InputMethodDebug.softInputDisplayReasonToString(reason));
-                    final IBinder token = (IBinder) args.arg3;
-                    ((IInputMethod)args.arg1).hideSoftInput(
-                            token, 0 /* flags */, (ResultReceiver) args.arg2);
-                    final IBinder requestToken;
-                    synchronized (ImfLock.class) {
-                        requestToken = mHideRequestWindowMap.get(token);
-                        onShowHideSoftInputRequested(false /* show */, requestToken, reason);
-                    }
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
             case MSG_HIDE_CURRENT_INPUT_METHOD:
                 synchronized (ImfLock.class) {
                     final @SoftInputShowHideReason int reason = (int) msg.obj;
@@ -4226,39 +4229,6 @@
 
                 }
                 return true;
-            case MSG_INITIALIZE_IME:
-                args = (SomeArgs)msg.obj;
-                try {
-                    if (DEBUG) {
-                        synchronized (ImfLock.class) {
-                            Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
-                                    + mCurTokenDisplayId);
-                        }
-                    }
-                    final IBinder token = (IBinder) args.arg2;
-                    ((IInputMethod) args.arg1).initializeInternal(token,
-                            new InputMethodPrivilegedOperationsImpl(this, token), msg.arg1);
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
-            case MSG_CREATE_SESSION: {
-                args = (SomeArgs)msg.obj;
-                IInputMethod method = (IInputMethod)args.arg1;
-                InputChannel channel = (InputChannel)args.arg2;
-                try {
-                    method.createSession(channel, (IInputSessionCallback)args.arg3);
-                } catch (RemoteException e) {
-                } finally {
-                    // Dispose the channel if the input method is not local to this process
-                    // because the remote proxy will get its own copy when unparceled.
-                    if (channel != null && Binder.isProxy(method)) {
-                        channel.dispose();
-                    }
-                }
-                args.recycle();
-                return true;
-            }
             case MSG_REMOVE_IME_SURFACE: {
                 synchronized (ImfLock.class) {
                     try {
@@ -4290,25 +4260,6 @@
             }
             // ---------------------------------------------------------
 
-            case MSG_START_INPUT: {
-                final boolean restarting = msg.arg2 != 0;
-                args = (SomeArgs) msg.obj;
-                final IBinder startInputToken = (IBinder) args.arg1;
-                final SessionState session = (SessionState) args.arg2;
-                final IInputContext inputContext = (IInputContext) args.arg3;
-                final EditorInfo editorInfo = (EditorInfo) args.arg4;
-                try {
-                    setEnabledSessionInHandlerThread(session);
-                    session.method.startInput(startInputToken, inputContext, editorInfo,
-                            restarting);
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
-            }
-
-            // ---------------------------------------------------------
-
             case MSG_UNBIND_CLIENT:
                 try {
                     ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
@@ -4366,9 +4317,7 @@
 
             // --------------------------------------------------------------
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
-                final InputMethodMenuController.HardKeyboardListener hardKeyboardListener =
-                        mMenuController.getHardKeyboardListener();
-                hardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
+                mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
                 return true;
             case MSG_SYSTEM_UNLOCK_USER: {
                 final int userId = msg.arg1;
@@ -4384,23 +4333,6 @@
             }
 
             // ---------------------------------------------------------------
-            case MSG_INLINE_SUGGESTIONS_REQUEST: {
-                args = (SomeArgs) msg.obj;
-                final InlineSuggestionsRequestInfo requestInfo =
-                        (InlineSuggestionsRequestInfo) args.arg2;
-                final IInlineSuggestionsRequestCallback callback =
-                        (IInlineSuggestionsRequestCallback) args.arg3;
-                try {
-                    ((IInputMethod) args.arg1).onCreateInlineSuggestionsRequest(requestInfo,
-                            callback);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
-                }
-                args.recycle();
-                return true;
-            }
-
-            // ---------------------------------------------------------------
             case MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE: {
                 if (mAudioManagerInternal == null) {
                     mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
@@ -4414,6 +4346,19 @@
         return false;
     }
 
+    @BinderThread
+    private void onStylusHandwritingReady(int requestId) {
+        synchronized (ImfLock.class) {
+            if (mHwRequestId != requestId) {
+                // obsolete request
+                return;
+            }
+
+            // TODO: replace null  with actual Channel, MotionEvents
+            getCurMethodLocked().startStylusHandwriting(null, null);
+        }
+    }
+
     private void handleSetInteractive(final boolean interactive) {
         synchronized (ImfLock.class) {
             mIsInteractive = interactive;
@@ -4671,14 +4616,6 @@
         mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
     }
 
-    private void showConfigureInputMethods() {
-        Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
-                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
-    }
-
     // ----------------------------------------------------------------------
 
     /**
@@ -5151,7 +5088,7 @@
     @BinderThread
     private void dumpAsStringNoCheck(FileDescriptor fd, PrintWriter pw, String[] args,
             boolean isCritical) {
-        IInputMethod method;
+        IInputMethodInvoker method;
         ClientState client;
         ClientState focusedWindowClient;
 
@@ -5958,5 +5895,11 @@
         public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible) {
             mImms.applyImeVisibility(mToken, windowToken, setVisible);
         }
+
+        @BinderThread
+        @Override
+        public void onStylusHandwritingReady(int requestId) {
+            mImms.onStylusHandwritingReady(requestId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 132be7d..fcb1be0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
-import static com.android.server.inputmethod.InputMethodManagerService.MSG_HARD_KEYBOARD_SWITCH_CHANGED;
 import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
 
 import android.app.ActivityThread;
@@ -28,7 +27,6 @@
 import android.content.DialogInterface;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -62,10 +60,8 @@
     private final InputMethodManagerService mService;
     private final InputMethodUtils.InputMethodSettings mSettings;
     private final InputMethodSubtypeSwitchingController mSwitchingController;
-    private final Handler mHandler;
     private final ArrayMap<String, InputMethodInfo> mMethodMap;
     private final KeyguardManager mKeyguardManager;
-    private final HardKeyboardListener mHardKeyboardListener;
     private final WindowManagerInternal mWindowManagerInternal;
 
     private Context mSettingsContext;
@@ -83,10 +79,8 @@
         mService = service;
         mSettings = mService.mSettings;
         mSwitchingController = mService.mSwitchingController;
-        mHandler = mService.mHandler;
         mMethodMap = mService.mMethodMap;
         mKeyguardManager = mService.mKeyguardManager;
-        mHardKeyboardListener = new HardKeyboardListener();
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
     }
 
@@ -271,10 +265,6 @@
         }
     }
 
-    HardKeyboardListener getHardKeyboardListener() {
-        return mHardKeyboardListener;
-    }
-
     AlertDialog getSwitchingDialogLocked() {
         return mSwitchingDialog;
     }
@@ -290,24 +280,16 @@
         return mSwitchingDialog.isShowing();
     }
 
-    class HardKeyboardListener implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
-        @Override
-        public void onHardKeyboardStatusChange(boolean available) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
-                    available ? 1 : 0));
+    void handleHardKeyboardStatusChange(boolean available) {
+        if (DEBUG) {
+            Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
         }
-
-        public void handleHardKeyboardStatusChange(boolean available) {
-            if (DEBUG) {
-                Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
-            }
-            synchronized (ImfLock.class) {
-                if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
-                        && mSwitchingDialog.isShowing()) {
-                    mSwitchingDialogTitleView.findViewById(
-                            com.android.internal.R.id.hard_keyboard_section).setVisibility(
-                            available ? View.VISIBLE : View.GONE);
-                }
+        synchronized (ImfLock.class) {
+            if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
+                    && mSwitchingDialog.isShowing()) {
+                mSwitchingDialogTitleView.findViewById(
+                        com.android.internal.R.id.hard_keyboard_section).setVisibility(
+                        available ? View.VISIBLE : View.GONE);
             }
         }
     }
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 09780f3..6676987 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -45,7 +45,7 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
-import android.content.pm.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.net.Uri;
diff --git a/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java
new file mode 100644
index 0000000..282e3c1
--- /dev/null
+++ b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static android.os.Process.INVALID_UID;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Holds data used to report the ApplicationLocalesChanged atom.
+ */
+public final class AppLocaleChangedAtomRecord {
+    final int mCallingUid;
+    int mTargetUid = INVALID_UID;
+    String mNewLocales = "";
+    String mPrevLocales = "";
+    int mStatus = FrameworkStatsLog
+            .APPLICATION_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED;
+
+    AppLocaleChangedAtomRecord(int callingUid) {
+        this.mCallingUid = callingUid;
+    }
+
+    void setNewLocales(String newLocales) {
+        this.mNewLocales = newLocales;
+    }
+
+    void setTargetUid(int targetUid) {
+        this.mTargetUid = targetUid;
+    }
+
+    void setPrevLocales(String prevLocales) {
+        this.mPrevLocales = prevLocales;
+    }
+
+    void setStatus(int status) {
+        this.mStatus = status;
+    }
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index b2bd47b..134fb96 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -21,7 +21,6 @@
 import static com.android.server.locales.LocaleManagerService.DEBUG;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
 import android.content.BroadcastReceiver;
@@ -34,7 +33,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.os.BestClock;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.HandlerThread;
 import android.os.LocaleList;
 import android.os.Process;
@@ -42,7 +40,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedXmlPullParser;
@@ -53,17 +50,12 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.XmlUtils;
 
-import libcore.io.IoUtils;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
@@ -71,8 +63,6 @@
 import java.time.Duration;
 import java.time.ZoneOffset;
 import java.util.HashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Helper class for managing backup and restore of app-specific locales.
@@ -87,33 +77,28 @@
     private static final String ATTR_LOCALES = "locales";
     private static final String ATTR_CREATION_TIME_MILLIS = "creationTimeMillis";
 
-    private static final String STAGE_FILE_NAME = "staged_locales";
     private static final String SYSTEM_BACKUP_PACKAGE_KEY = "android";
-
-    private static final Pattern STAGE_FILE_NAME_PATTERN = Pattern.compile(
-            TextUtils.formatSimple("(^%s_)(\\d+)(\\.xml$)", STAGE_FILE_NAME));
-    private static final int USER_ID_GROUP_INDEX_IN_PATTERN = 2;
-    private static final Duration STAGE_FILE_RETENTION_PERIOD = Duration.ofDays(3);
+    // Stage data would be deleted on reboot since it's stored in memory. So it's retained until
+    // retention period OR next reboot, whichever happens earlier.
+    private static final Duration STAGE_DATA_RETENTION_PERIOD = Duration.ofDays(3);
 
     private final LocaleManagerService mLocaleManagerService;
     private final PackageManagerInternal mPackageManagerInternal;
-    private final File mStagedLocalesDir;
     private final Clock mClock;
     private final Context mContext;
     private final Object mStagedDataLock = new Object();
 
     // Staged data map keyed by user-id to handle multi-user scenario / work profiles. We are using
     // SparseArray because it is more memory-efficient than a HashMap.
-    private final SparseArray<StagedData> mStagedData = new SparseArray<>();
+    private final SparseArray<StagedData> mStagedData;
 
     private final PackageMonitor mPackageMonitor;
     private final BroadcastReceiver mUserMonitor;
 
     LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
             PackageManagerInternal pmInternal) {
-        this(localeManagerService.mContext, localeManagerService, pmInternal,
-                new File(Environment.getDataSystemCeDirectory(),
-                        "app_locales"), getDefaultClock());
+        this(localeManagerService.mContext, localeManagerService, pmInternal, getDefaultClock(),
+                new SparseArray<>());
     }
 
     private static @NonNull Clock getDefaultClock() {
@@ -123,14 +108,12 @@
 
     @VisibleForTesting LocaleManagerBackupHelper(Context context,
             LocaleManagerService localeManagerService,
-            PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
+            PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) {
         mContext = context;
         mLocaleManagerService = localeManagerService;
         mPackageManagerInternal = pmInternal;
         mClock = clock;
-        mStagedLocalesDir = stagedLocalesDir;
-
-        loadAllStageFiles();
+        mStagedData = stagedData;
 
         HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -158,67 +141,6 @@
     }
 
     /**
-     * Loads the staged data into memory by reading all the files in the staged directory.
-     *
-     * <p><b>Note:</b> We don't ned to hold the lock here because this is only called in the
-     * constructor (before any broadcast receivers are registered).
-     */
-    private void loadAllStageFiles() {
-        File[] files = mStagedLocalesDir.listFiles();
-        if (files == null) {
-            return;
-        }
-        for (File file : files) {
-            String fileName = file.getName();
-            Matcher matcher = STAGE_FILE_NAME_PATTERN.matcher(fileName);
-            if (!matcher.matches()) {
-                file.delete();
-                Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
-                        "Unrecognized file"));
-                continue;
-            }
-            try {
-                final int userId = Integer.parseInt(matcher.group(USER_ID_GROUP_INDEX_IN_PATTERN));
-                StagedData stagedData = readStageFile(file);
-                if (stagedData != null) {
-                    mStagedData.put(userId, stagedData);
-                } else {
-                    file.delete();
-                    Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
-                            "Could not read file"));
-                }
-            } catch (NumberFormatException e) {
-                file.delete();
-                Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
-                        "Could not parse user id from file name"));
-            }
-        }
-    }
-
-    /**
-     * Loads the stage file from the disk and parses it into a list of app backups.
-     */
-    private @Nullable StagedData readStageFile(@NonNull File file) {
-        InputStream stagedDataInputStream = null;
-        AtomicFile stageFile = new AtomicFile(file);
-        try {
-            stagedDataInputStream = stageFile.openRead();
-            final TypedXmlPullParser parser = Xml.newFastPullParser();
-            parser.setInput(stagedDataInputStream, StandardCharsets.UTF_8.name());
-
-            XmlUtils.beginDocument(parser, LOCALES_XML_TAG);
-            long creationTimeMillis = parser.getAttributeLong(/* namespace= */ null,
-                    ATTR_CREATION_TIME_MILLIS);
-            return new StagedData(creationTimeMillis, readFromXml(parser));
-        } catch (IOException | XmlPullParserException e) {
-            Slog.e(TAG, "Could not parse stage file ", e);
-        } finally {
-            IoUtils.closeQuietly(stagedDataInputStream);
-        }
-        return null;
-    }
-
-    /**
      * @see LocaleManagerInternal#getBackupPayload(int userId)
      */
     public byte[] getBackupPayload(int userId) {
@@ -261,9 +183,7 @@
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         try {
-            // Passing arbitrary value for creationTimeMillis since it is ignored when forStage
-            // is false.
-            writeToXml(out, pkgStates, /* forStage= */ false, /* creationTimeMillis= */ -1);
+            writeToXml(out, pkgStates);
         } catch (IOException e) {
             Slog.e(TAG, "Could not write to xml for backup ", e);
             return null;
@@ -284,7 +204,7 @@
             int userId = mStagedData.keyAt(i);
             StagedData stagedData = mStagedData.get(userId);
             if (stagedData.mCreationTimeMillis
-                    < mClock.millis() - STAGE_FILE_RETENTION_PERIOD.toMillis()) {
+                    < mClock.millis() - STAGE_DATA_RETENTION_PERIOD.toMillis()) {
                 deleteStagedDataLocked(userId);
             }
         }
@@ -305,7 +225,7 @@
 
         final ByteArrayInputStream inputStream = new ByteArrayInputStream(payload);
 
-        HashMap<String, String> pkgStates = new HashMap<>();
+        HashMap<String, String> pkgStates;
         try {
             // Parse the input blob into a list of BackupPackageState.
             final TypedXmlPullParser parser = Xml.newFastPullParser();
@@ -315,6 +235,7 @@
             pkgStates = readFromXml(parser);
         } catch (IOException | XmlPullParserException e) {
             Slog.e(TAG, "Could not parse payload ", e);
+            return;
         }
 
         // We need a lock here to prevent race conditions when accessing the stage file.
@@ -323,7 +244,7 @@
         // performed simultaneously.
         synchronized (mStagedDataLock) {
             // Backups for apps which are yet to be installed.
-            mStagedData.put(userId, new StagedData(mClock.millis(), new HashMap<>()));
+            StagedData stagedData = new StagedData(mClock.millis(), new HashMap<>());
 
             for (String pkgName : pkgStates.keySet()) {
                 String languageTags = pkgStates.get(pkgName);
@@ -333,7 +254,7 @@
                     checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
                 } else {
                     // Stage the data if the app isn't installed.
-                    mStagedData.get(userId).mPackageStates.put(pkgName, languageTags);
+                    stagedData.mPackageStates.put(pkgName, languageTags);
                     if (DEBUG) {
                         Slog.d(TAG, "Add locales=" + languageTags
                                 + " package=" + pkgName + " for lazy restore.");
@@ -341,7 +262,9 @@
                 }
             }
 
-            writeStageFileLocked(userId);
+            if (!stagedData.mPackageStates.isEmpty()) {
+                mStagedData.put(userId, stagedData);
+            }
         }
     }
 
@@ -396,55 +319,10 @@
         }
     }
 
-    /**
-     * Converts the list of app backups into xml and writes it onto the disk.
-     */
-    private void writeStageFileLocked(int userId) {
-        StagedData stagedData = mStagedData.get(userId);
-        if (stagedData.mPackageStates.isEmpty()) {
-            deleteStagedDataLocked(userId);
-            return;
-        }
-
-        final FileOutputStream stagedDataOutputStream;
-        AtomicFile stageFile = new AtomicFile(
-                new File(mStagedLocalesDir,
-                        TextUtils.formatSimple("%s_%d.xml", STAGE_FILE_NAME, userId)));
-        try {
-            stagedDataOutputStream = stageFile.startWrite();
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to save stage file");
-            return;
-        }
-
-        try {
-            writeToXml(stagedDataOutputStream, stagedData.mPackageStates,  /* forStage= */ true,
-                    stagedData.mCreationTimeMillis);
-            stageFile.finishWrite(stagedDataOutputStream);
-            if (DEBUG) {
-                Slog.d(TAG, "Stage file written.");
-            }
-        } catch (IOException e) {
-            Slog.e(TAG, "Could not write stage file", e);
-            stageFile.failWrite(stagedDataOutputStream);
-        }
-    }
-
     private void deleteStagedDataLocked(@UserIdInt int userId) {
-        AtomicFile stageFile = getStageFileIfExistsLocked(userId);
-        if (stageFile != null) {
-            stageFile.delete();
-        }
         mStagedData.remove(userId);
     }
 
-    private @Nullable AtomicFile getStageFileIfExistsLocked(@UserIdInt int userId) {
-        final File stageFile = new File(mStagedLocalesDir,
-                TextUtils.formatSimple("%s_%d.xml", STAGE_FILE_NAME, userId));
-        return stageFile.isFile() ? new AtomicFile(stageFile)
-                : null;
-    }
-
     /**
      * Parses the backup data from the serialized xml input stream.
      */
@@ -468,15 +346,8 @@
 
     /**
      * Converts the list of app backup data into a serialized xml stream.
-     *
-     * @param forStage Flag to indicate whether this method is called for the purpose of
-     * staging the data. Note that if this is false, {@code creationTimeMillis} is ignored because
-     * we only need it for the stage data.
-     * @param creationTimeMillis The timestamp when the stage data was created. This is required
-     * to determine when to delete the stage data.
      */
-    private static void writeToXml(OutputStream stream,
-            @NonNull HashMap<String, String> pkgStates, boolean forStage, long creationTimeMillis)
+    private static void writeToXml(OutputStream stream, @NonNull HashMap<String, String> pkgStates)
             throws IOException {
         if (pkgStates.isEmpty()) {
             // No need to write anything at all if pkgStates is empty.
@@ -488,11 +359,6 @@
         out.startDocument(/* encoding= */ null, /* standalone= */ true);
         out.startTag(/* namespace= */ null, LOCALES_XML_TAG);
 
-        if (forStage) {
-            out.attribute(/* namespace= */ null, ATTR_CREATION_TIME_MILLIS,
-                    Long.toString(creationTimeMillis));
-        }
-
         for (String pkg : pkgStates.keySet()) {
             out.startTag(/* namespace= */ null, PACKAGE_XML_TAG);
             out.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, pkg);
@@ -504,7 +370,7 @@
         out.endDocument();
     }
 
-    private static class StagedData {
+    static class StagedData {
         final long mCreationTimeMillis;
         final HashMap<String, String> mPackageStates;
 
@@ -517,7 +383,7 @@
     /**
      * Broadcast listener to capture user removed event.
      *
-     * <p>The stage file is deleted when a user is removed.
+     * <p>The stage data is deleted when a user is removed.
      */
     private final class UserMonitor extends BroadcastReceiver {
         @Override
@@ -546,6 +412,8 @@
         public void onPackageAdded(String packageName, int uid) {
             try {
                 synchronized (mStagedDataLock) {
+                    cleanStagedDataForOldEntriesLocked();
+
                     int userId = UserHandle.getUserId(uid);
                     if (mStagedData.contains(userId)) {
                         // Perform lazy restore only if the staged data exists.
@@ -589,7 +457,7 @@
         // Check if the package is installed indeed
         if (!isPackageInstalledForUser(packageName, userId)) {
             Slog.e(TAG, packageName + " not installed for user " + userId
-                    + ". Could not restore locales from stage file");
+                    + ". Could not restore locales from stage data");
             return;
         }
 
@@ -603,8 +471,11 @@
 
                 // Remove the restored entry from the staged data list.
                 stagedData.mPackageStates.remove(pkgName);
-                // Update the file on the disk.
-                writeStageFileLocked(userId);
+
+                // Remove the stage data entry for user if there are no more packages to restore.
+                if (stagedData.mPackageStates.isEmpty()) {
+                    mStagedData.remove(userId);
+                }
 
                 // No need to loop further after restoring locales because the staged data will
                 // contain at most one entry for the newly added package.
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 6aabdb5..d459f8d 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -148,40 +149,52 @@
      */
     public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId,
             @NonNull LocaleList locales) throws RemoteException, IllegalArgumentException {
-        requireNonNull(appPackageName);
-        requireNonNull(locales);
-
-        //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
-        userId = mActivityManagerInternal.handleIncomingUser(
-                Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
-                "setApplicationLocales", appPackageName);
-
-        // This function handles two types of set operations:
-        // 1.) A normal, non-privileged app setting its own locale.
-        // 2.) A privileged system service setting locales of another package.
-        // The least privileged case is a normal app performing a set, so check that first and
-        // set locales if the package name is owned by the app. Next, check if the caller has the
-        // necessary permission and set locales.
-        boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId);
-        if (!isCallerOwner) {
-            enforceChangeConfigurationPermission();
-        }
-
-        final long token = Binder.clearCallingIdentity();
+        AppLocaleChangedAtomRecord atomRecordForMetrics = new
+                AppLocaleChangedAtomRecord(Binder.getCallingUid());
         try {
-            setApplicationLocalesUnchecked(appPackageName, userId, locales);
+            requireNonNull(appPackageName);
+            requireNonNull(locales);
+            atomRecordForMetrics.setNewLocales(locales.toLanguageTags());
+            //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
+            userId = mActivityManagerInternal.handleIncomingUser(
+                    Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                    false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
+                    "setApplicationLocales", appPackageName);
+
+            // This function handles two types of set operations:
+            // 1.) A normal, non-privileged app setting its own locale.
+            // 2.) A privileged system service setting locales of another package.
+            // The least privileged case is a normal app performing a set, so check that first and
+            // set locales if the package name is owned by the app. Next, check if the caller has
+            // the necessary permission and set locales.
+            boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId,
+                    atomRecordForMetrics);
+            if (!isCallerOwner) {
+                enforceChangeConfigurationPermission(atomRecordForMetrics);
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setApplicationLocalesUnchecked(appPackageName, userId, locales,
+                        atomRecordForMetrics);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            logMetric(atomRecordForMetrics);
         }
     }
 
     private void setApplicationLocalesUnchecked(@NonNull String appPackageName,
-            @UserIdInt int userId, @NonNull LocaleList locales) {
+            @UserIdInt int userId, @NonNull LocaleList locales,
+            @NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
         if (DEBUG) {
             Slog.d(TAG, "setApplicationLocales: setting locales for package " + appPackageName
                     + " and user " + userId);
         }
+
+        atomRecordForMetrics.setPrevLocales(getApplicationLocalesUnchecked(appPackageName, userId)
+                .toLanguageTags());
         final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
                 mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
                         userId);
@@ -194,6 +207,11 @@
             notifyRegisteredReceivers(appPackageName, userId, locales);
 
             mBackupHelper.notifyBackupManager();
+            atomRecordForMetrics.setStatus(
+                    FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_COMMITTED);
+        } else {
+            atomRecordForMetrics.setStatus(FrameworkStatsLog
+                    .APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_UNCOMMITTED);
         }
     }
 
@@ -221,18 +239,13 @@
      */
     private void notifyInstallerOfAppWhoseLocaleChanged(String appPackageName, int userId,
             LocaleList locales) {
-        try {
-            String installingPackageName = mContext.getPackageManager()
-                    .getInstallSourceInfo(appPackageName).getInstallingPackageName();
-            if (installingPackageName != null) {
-                Intent intent = createBaseIntent(Intent.ACTION_APPLICATION_LOCALE_CHANGED,
-                        appPackageName, locales);
-                //Set package name to ensure that only installer of the app receives this intent.
-                intent.setPackage(installingPackageName);
-                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.w(TAG, "Package not found " + appPackageName);
+        String installingPackageName = getInstallingPackageName(appPackageName);
+        if (installingPackageName != null) {
+            Intent intent = createBaseIntent(Intent.ACTION_APPLICATION_LOCALE_CHANGED,
+                    appPackageName, locales);
+            //Set package name to ensure that only installer of the app receives this intent.
+            intent.setPackage(installingPackageName);
+            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
         }
     }
 
@@ -259,26 +272,48 @@
     }
 
     /**
+     * Same as {@link LocaleManagerService#isPackageOwnedByCaller(String, int,
+     * AppLocaleChangedAtomRecord)}, but for methods that do not log locale atom.
+     */
+    private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
+        return isPackageOwnedByCaller(appPackageName, userId, /* atomRecordForMetrics= */null);
+    }
+
+    /**
      * Checks if the package is owned by the calling app or not for the given user id.
      *
      * @throws IllegalArgumentException if package not found for given userid
      */
-    private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
-        final int uid = mPackageManagerInternal
-                .getPackageUid(appPackageName, /* flags */ 0, userId);
+    private boolean isPackageOwnedByCaller(String appPackageName, int userId,
+            @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics) {
+        final int uid = getPackageUid(appPackageName, userId);
         if (uid < 0) {
             Slog.w(TAG, "Unknown package " + appPackageName + " for user " + userId);
+            if (atomRecordForMetrics != null) {
+                atomRecordForMetrics.setStatus(FrameworkStatsLog
+                        .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE);
+            }
             throw new IllegalArgumentException("Unknown package: " + appPackageName
                     + " for user " + userId);
         }
+        if (atomRecordForMetrics != null) {
+            atomRecordForMetrics.setTargetUid(uid);
+        }
         //Once valid package found, ignore the userId part for validating package ownership
         //as apps with INTERACT_ACROSS_USERS permission could be changing locale for different user.
         return UserHandle.isSameApp(Binder.getCallingUid(), uid);
     }
 
-    private void enforceChangeConfigurationPermission() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
+    private void enforceChangeConfigurationPermission(@NonNull AppLocaleChangedAtomRecord
+            atomRecordForMetrics) {
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
+        } catch (SecurityException e) {
+            atomRecordForMetrics.setStatus(FrameworkStatsLog
+                    .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_PERMISSION_ABSENT);
+            throw e;
+        }
     }
 
     /**
@@ -295,13 +330,17 @@
                 false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
                 "getApplicationLocales", appPackageName);
 
-        // This function handles two types of query operations:
+        // This function handles three types of query operations:
         // 1.) A normal, non-privileged app querying its own locale.
-        // 2.) A privileged system service querying locales of another package.
+        // 2.) The installer of the given app querying locales of a package installed
+        // by said installer.
+        // 3.) A privileged system service querying locales of another package.
         // The least privileged case is a normal app performing a query, so check that first and
-        // get locales if the package name is owned by the app. Next, check if the caller has the
-        // necessary permission and get locales.
-        if (!isPackageOwnedByCaller(appPackageName, userId)) {
+        // get locales if the package name is owned by the app. Next check if the calling app
+        // is the installer of the given app and get locales. If neither conditions matched,
+        // check if the caller has the necessary permission and fetch locales.
+        if (!isPackageOwnedByCaller(appPackageName, userId)
+                && !isCallerInstaller(appPackageName, userId)) {
             enforceReadAppSpecificLocalesPermission();
         }
         final long token = Binder.clearCallingIdentity();
@@ -312,6 +351,7 @@
         }
     }
 
+    @NonNull
     private LocaleList getApplicationLocalesUnchecked(@NonNull String appPackageName,
             @UserIdInt int userId) {
         if (DEBUG) {
@@ -332,12 +372,41 @@
         return locales != null ? locales : LocaleList.getEmptyLocaleList();
     }
 
+    /**
+     * Checks if the calling app is the installer of the app whose locale changed.
+     */
+    private boolean isCallerInstaller(String appPackageName, int userId) {
+        String installingPackageName = getInstallingPackageName(appPackageName);
+        if (installingPackageName != null) {
+            // Get the uid of installer-on-record to compare with the calling uid.
+            int installerUid = getPackageUid(installingPackageName, userId);
+            return installerUid >= 0 && UserHandle.isSameApp(Binder.getCallingUid(), installerUid);
+        }
+        return false;
+    }
+
     private void enforceReadAppSpecificLocalesPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.READ_APP_SPECIFIC_LOCALES,
                 "getApplicationLocales");
     }
 
+    private int getPackageUid(String appPackageName, int userId) {
+        return mPackageManagerInternal
+                .getPackageUid(appPackageName, /* flags */ 0, userId);
+    }
+
+    @Nullable
+    private String getInstallingPackageName(String packageName) {
+        try {
+            return mContext.getPackageManager()
+                    .getInstallSourceInfo(packageName).getInstallingPackageName();
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Package not found " + packageName);
+        }
+        return null;
+    }
+
     /**
      * Dumps useful info related to service.
      */
@@ -345,4 +414,13 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         // TODO(b/201766221): Implement when there is state.
     }
+
+    private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
+        FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
+                atomRecordForMetrics.mCallingUid,
+                atomRecordForMetrics.mTargetUid,
+                atomRecordForMetrics.mNewLocales,
+                atomRecordForMetrics.mPrevLocales,
+                atomRecordForMetrics.mStatus);
+    }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index e40d86a..9fb1d8e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -896,8 +896,8 @@
         info.packageName = mPackage;
         info.attributionTag = mAttributionTag;
         info.type = (mUid == Process.SYSTEM_UID)
-             ? HostEndpointInfo.Type.TYPE_FRAMEWORK
-             : HostEndpointInfo.Type.TYPE_APP;
+             ? HostEndpointInfo.Type.FRAMEWORK
+             : HostEndpointInfo.Type.APP;
         mContextHubProxy.onHostEndpointConnected(info);
     }
 
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 19f7c4d..c199bb3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -152,6 +152,14 @@
 
             @Override
                 /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+                ContextHubStatsLog.write(
+                        ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED,
+                        nanoAppBinary.getNanoAppId(),
+                        nanoAppBinary.getNanoAppVersion(),
+                        ContextHubStatsLog
+                            .CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_LOAD,
+                        toStatsTransactionResult(result));
+
                 if (result == ContextHubTransaction.RESULT_SUCCESS) {
                     // NOTE: The legacy JNI code used to do a query right after a load success
                     // to synchronize the service cache. Instead store the binary that was
@@ -200,6 +208,13 @@
 
             @Override
                 /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+                ContextHubStatsLog.write(
+                        ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED, nanoAppId,
+                        0 /* nanoappVersion */,
+                        ContextHubStatsLog
+                            .CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_UNLOAD,
+                        toStatsTransactionResult(result));
+
                 if (result == ContextHubTransaction.RESULT_SUCCESS) {
                     mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
                 }
@@ -477,6 +492,30 @@
         }
     }
 
+    private int toStatsTransactionResult(@ContextHubTransaction.Result int result) {
+        switch (result) {
+            case ContextHubTransaction.RESULT_SUCCESS:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_SUCCESS;
+            case ContextHubTransaction.RESULT_FAILED_BAD_PARAMS:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_BAD_PARAMS;
+            case ContextHubTransaction.RESULT_FAILED_UNINITIALIZED:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_UNINITIALIZED;
+            case ContextHubTransaction.RESULT_FAILED_BUSY:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_BUSY;
+            case ContextHubTransaction.RESULT_FAILED_AT_HUB:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_AT_HUB;
+            case ContextHubTransaction.RESULT_FAILED_TIMEOUT:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_TIMEOUT;
+            case ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_SERVICE_INTERNAL_FAILURE;
+            case ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE:
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_HAL_UNAVAILABLE;
+            case ContextHubTransaction.RESULT_FAILED_UNKNOWN:
+            default: /* fall through */
+                return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_UNKNOWN;
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(100);
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index cc5aaf4..9b0b782 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -35,6 +35,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.util.Log;
 import android.util.Pair;
 
@@ -435,7 +436,7 @@
         public void onHostEndpointConnected(HostEndpointInfo info) {
             try {
                 mHub.onHostEndpointConnected(info);
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(TAG, "RemoteException in onHostEndpointConnected");
             }
         }
@@ -444,7 +445,7 @@
         public void onHostEndpointDisconnected(short hostEndpointId) {
             try {
                 mHub.onHostEndpointDisconnected((char) hostEndpointId);
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(TAG, "RemoteException in onHostEndpointDisconnected");
             }
         }
@@ -453,8 +454,13 @@
         public int sendMessageToContextHub(
                 short hostEndpointId, int contextHubId, NanoAppMessage message)
                 throws RemoteException {
-            return toTransactionResult(mHub.sendMessageToHub(contextHubId,
-                    ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message)));
+            try {
+                mHub.sendMessageToHub(contextHubId,
+                        ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message));
+                return ContextHubTransaction.RESULT_SUCCESS;
+            } catch (RemoteException | ServiceSpecificException e) {
+                return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            }
         }
 
         @ContextHubTransaction.Result
@@ -462,49 +468,71 @@
                 int transactionId) throws RemoteException {
             android.hardware.contexthub.NanoappBinary aidlNanoAppBinary =
                     ContextHubServiceUtil.createAidlNanoAppBinary(binary);
-            return toTransactionResult(
-                    mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId));
+            try {
+                mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId);
+                return ContextHubTransaction.RESULT_SUCCESS;
+            } catch (RemoteException | ServiceSpecificException e) {
+                return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            }
         }
 
         @ContextHubTransaction.Result
         public int unloadNanoapp(int contextHubId, long nanoappId, int transactionId)
                 throws RemoteException {
-            return toTransactionResult(mHub.unloadNanoapp(contextHubId, nanoappId, transactionId));
+            try {
+                mHub.unloadNanoapp(contextHubId, nanoappId, transactionId);
+                return ContextHubTransaction.RESULT_SUCCESS;
+            } catch (RemoteException | ServiceSpecificException e) {
+                return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            }
         }
 
         @ContextHubTransaction.Result
         public int enableNanoapp(int contextHubId, long nanoappId, int transactionId)
                 throws RemoteException {
-            return toTransactionResult(mHub.enableNanoapp(contextHubId, nanoappId, transactionId));
+            try {
+                mHub.enableNanoapp(contextHubId, nanoappId, transactionId);
+                return ContextHubTransaction.RESULT_SUCCESS;
+            } catch (RemoteException | ServiceSpecificException e) {
+                return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            }
         }
 
         @ContextHubTransaction.Result
         public int disableNanoapp(int contextHubId, long nanoappId, int transactionId)
                 throws RemoteException {
-            return toTransactionResult(mHub.disableNanoapp(contextHubId, nanoappId, transactionId));
+            try {
+                mHub.disableNanoapp(contextHubId, nanoappId, transactionId);
+                return ContextHubTransaction.RESULT_SUCCESS;
+            } catch (RemoteException | ServiceSpecificException e) {
+                return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            }
         }
 
         @ContextHubTransaction.Result
         public int queryNanoapps(int contextHubId) throws RemoteException {
-            return toTransactionResult(mHub.queryNanoapps(contextHubId));
+            try {
+                mHub.queryNanoapps(contextHubId);
+                return ContextHubTransaction.RESULT_SUCCESS;
+            } catch (RemoteException | ServiceSpecificException e) {
+                return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            }
         }
 
         public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
             mAidlCallbackMap.put(contextHubId, new ContextHubAidlCallback(contextHubId, callback));
-            mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
-        }
-
-        @ContextHubTransaction.Result
-        private int toTransactionResult(boolean success) {
-            return success ? ContextHubTransaction.RESULT_SUCCESS
-                    : ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+            try {
+                mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Exception while registering callback: " + e.getMessage());
+            }
         }
 
         private void onSettingChanged(byte setting, boolean enabled) {
             try {
                 mHub.onSettingChanged(setting, enabled);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException while sending setting update");
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Exception while sending setting update: " + e.getMessage());
             }
         }
     }
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index a52c9ce..5093f5d 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -131,8 +131,8 @@
             return mPermitted;
         }
 
-        boolean onLocationPermissionsChanged(String packageName) {
-            if (getIdentity().getPackageName().equals(packageName)) {
+        boolean onLocationPermissionsChanged(@Nullable String packageName) {
+            if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
                 return onLocationPermissionsChanged();
             }
 
@@ -242,7 +242,7 @@
             mLocationPermissionsListener =
             new LocationPermissionsHelper.LocationPermissionsListener() {
                 @Override
-                public void onLocationPermissionsChanged(String packageName) {
+                public void onLocationPermissionsChanged(@Nullable String packageName) {
                     GeofenceManager.this.onLocationPermissionsChanged(packageName);
                 }
 
@@ -494,7 +494,7 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
-    void onLocationPermissionsChanged(String packageName) {
+    void onLocationPermissionsChanged(@Nullable String packageName) {
         updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 5e6ae68..a540476 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -119,8 +119,8 @@
          */
         protected void onGnssListenerUnregister() {}
 
-        boolean onLocationPermissionsChanged(String packageName) {
-            if (getIdentity().getPackageName().equals(packageName)) {
+        boolean onLocationPermissionsChanged(@Nullable String packageName) {
+            if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
                 return onLocationPermissionsChanged();
             }
 
@@ -197,7 +197,7 @@
             mLocationPermissionsListener =
             new LocationPermissionsHelper.LocationPermissionsListener() {
                 @Override
-                public void onLocationPermissionsChanged(String packageName) {
+                public void onLocationPermissionsChanged(@Nullable String packageName) {
                     GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
                 }
 
@@ -390,7 +390,7 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
-    private void onLocationPermissionsChanged(String packageName) {
+    private void onLocationPermissionsChanged(@Nullable String packageName) {
         updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
     }
 
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 f114184..c02411e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -16,14 +16,15 @@
 
 package com.android.server.location.gnss;
 
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-
 import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.location.provider.ProviderProperties.ACCURACY_FINE;
 import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
 import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_LTE_CELLID;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_NR_CELLID;
 import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
 import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_IMSI;
 import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_MSISDN;
@@ -84,9 +85,18 @@
 import android.os.WorkSource.WorkChain;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoNr;
+import android.telephony.CellInfoWcdma;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -110,6 +120,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -1136,7 +1147,7 @@
         if (DEBUG) {
             Log.d(TAG, "startBatching " + mFixInterval + " " + batchLengthMs);
         }
-        if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), true)) {
+        if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), 0, true)) {
             mBatchingStarted = true;
 
             if (batchSize < getBatchSize()) {
@@ -1386,29 +1397,127 @@
         postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
     }
 
+
+    private static int getCellType(CellInfo ci) {
+        if (ci instanceof CellInfoGsm) {
+            return CellInfo.TYPE_GSM;
+        } else if (ci instanceof CellInfoWcdma) {
+            return CellInfo.TYPE_WCDMA;
+        } else if (ci instanceof CellInfoLte) {
+            return CellInfo.TYPE_LTE;
+        } else if (ci instanceof CellInfoNr) {
+            return CellInfo.TYPE_NR;
+        }
+        return CellInfo.TYPE_UNKNOWN;
+    }
+
+    /**
+     * Extract the CID/CI for GSM/WCDMA/LTE/NR
+     *
+     * @return the cell ID or -1 if invalid
+     */
+    private static long getCidFromCellIdentity(CellIdentity id) {
+        if (id == null) return -1;
+        long cid = -1;
+        switch(id.getType()) {
+            case CellInfo.TYPE_GSM: cid = ((CellIdentityGsm) id).getCid(); break;
+            case CellInfo.TYPE_WCDMA: cid = ((CellIdentityWcdma) id).getCid(); break;
+            case CellInfo.TYPE_LTE: cid = ((CellIdentityLte) id).getCi(); break;
+            case CellInfo.TYPE_NR: cid = ((CellIdentityNr) id).getNci(); break;
+            default: break;
+        }
+        // If the CID is unreported
+        if (cid == (id.getType() == CellInfo.TYPE_NR
+                ? CellInfo.UNAVAILABLE_LONG : CellInfo.UNAVAILABLE)) {
+            cid = -1;
+        }
+
+        return cid;
+    }
+
+    private void setRefLocation(int type, CellIdentity ci) {
+        String mcc_str = ci.getMccString();
+        String mnc_str = ci.getMncString();
+        int mcc = mcc_str != null ? Integer.parseInt(mcc_str) : CellInfo.UNAVAILABLE;
+        int mnc = mnc_str != null ? Integer.parseInt(mnc_str) : CellInfo.UNAVAILABLE;
+        int lac = CellInfo.UNAVAILABLE;
+        int tac = CellInfo.UNAVAILABLE;
+        int pcid = CellInfo.UNAVAILABLE;
+        int arfcn = CellInfo.UNAVAILABLE;
+        long cid = CellInfo.UNAVAILABLE_LONG;
+
+        switch (type) {
+            case AGPS_REF_LOCATION_TYPE_GSM_CELLID:
+                CellIdentityGsm cig = (CellIdentityGsm) ci;
+                cid = cig.getCid();
+                lac = cig.getLac();
+                break;
+            case AGPS_REF_LOCATION_TYPE_UMTS_CELLID:
+                CellIdentityWcdma ciw = (CellIdentityWcdma) ci;
+                cid = ciw.getCid();
+                lac = ciw.getLac();
+                break;
+            case AGPS_REF_LOCATION_TYPE_LTE_CELLID:
+                CellIdentityLte cil = (CellIdentityLte) ci;
+                cid = cil.getCi();
+                tac = cil.getTac();
+                pcid = cil.getPci();
+                break;
+            case AGPS_REF_LOCATION_TYPE_NR_CELLID:
+                CellIdentityNr cin = (CellIdentityNr) ci;
+                cid = cin.getNci();
+                tac = cin.getTac();
+                pcid = cin.getPci();
+                arfcn = cin.getNrarfcn();
+                break;
+            default:
+        }
+
+        mGnssNative.setAgpsReferenceLocationCellId(
+                type, mcc, mnc, lac, cid, tac, pcid, arfcn);
+    }
+
     private void requestRefLocation() {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
+
         final int phoneType = phone.getPhoneType();
         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
-            GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
-            if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
-                    && (phone.getNetworkOperator().length() > 3)) {
-                int type;
-                int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0, 3));
-                int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
-                int networkType = phone.getNetworkType();
-                if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
-                        || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
-                        || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
-                        || networkType == TelephonyManager.NETWORK_TYPE_HSPA
-                        || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
-                    type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
-                } else {
-                    type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+
+            List<CellInfo> cil = phone.getAllCellInfo();
+            if (cil != null) {
+                HashMap<Integer, CellIdentity> cellIdentityMap = new HashMap<>();
+                cil.sort(Comparator.comparingInt(
+                        (CellInfo ci) -> ci.getCellSignalStrength().getAsuLevel()).reversed());
+
+                for (CellInfo ci : cil) {
+                    int status = ci.getCellConnectionStatus();
+                    if (status == CellInfo.CONNECTION_PRIMARY_SERVING
+                            || status == CellInfo.CONNECTION_SECONDARY_SERVING) {
+                        CellIdentity c = ci.getCellIdentity();
+                        int t = getCellType(ci);
+                        if (getCidFromCellIdentity(c) != -1
+                                && !cellIdentityMap.containsKey(t)) {
+                            cellIdentityMap.put(t, c);
+                        }
+                    }
                 }
-                mGnssNative.setAgpsReferenceLocationCellId(type, mcc, mnc, gsm_cell.getLac(),
-                        gsm_cell.getCid());
+
+                if (cellIdentityMap.containsKey(CellInfo.TYPE_GSM)) {
+                    setRefLocation(AGPS_REF_LOCATION_TYPE_GSM_CELLID,
+                            cellIdentityMap.get(CellInfo.TYPE_GSM));
+                } else if (cellIdentityMap.containsKey(CellInfo.TYPE_WCDMA)) {
+                    setRefLocation(AGPS_REF_LOCATION_TYPE_UMTS_CELLID,
+                            cellIdentityMap.get(CellInfo.TYPE_WCDMA));
+                } else if (cellIdentityMap.containsKey(CellInfo.TYPE_LTE)) {
+                    setRefLocation(AGPS_REF_LOCATION_TYPE_LTE_CELLID,
+                            cellIdentityMap.get(CellInfo.TYPE_LTE));
+                } else if (cellIdentityMap.containsKey(CellInfo.TYPE_NR)) {
+                    setRefLocation(AGPS_REF_LOCATION_TYPE_NR_CELLID,
+                            cellIdentityMap.get(CellInfo.TYPE_NR));
+                } else {
+                    Log.e(TAG, "No available serving cell information.");
+                }
             } else {
                 Log.e(TAG, "Error getting cell location info.");
             }
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index cc5dcf30..e072bf7 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -124,9 +124,12 @@
     // IMPORTANT - must match OEM definitions, this isn't part of a hal for some reason
     public static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
     public static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
+    public static final int AGPS_REF_LOCATION_TYPE_LTE_CELLID = 4;
+    public static final int AGPS_REF_LOCATION_TYPE_NR_CELLID = 8;
 
     @IntDef(prefix = "AGPS_REF_LOCATION_TYPE_", value = {AGPS_REF_LOCATION_TYPE_GSM_CELLID,
-            AGPS_REF_LOCATION_TYPE_UMTS_CELLID})
+            AGPS_REF_LOCATION_TYPE_UMTS_CELLID, AGPS_REF_LOCATION_TYPE_LTE_CELLID,
+            AGPS_REF_LOCATION_TYPE_NR_CELLID})
     @Retention(RetentionPolicy.SOURCE)
     public @interface AgpsReferenceLocationType {}
 
@@ -623,8 +626,38 @@
     public void injectLocation(Location location) {
         Preconditions.checkState(mRegistered);
         if (location.hasAccuracy()) {
-            mGnssHal.injectLocation(location.getLatitude(), location.getLongitude(),
-                    location.getAccuracy());
+
+            int gnssLocationFlags = GNSS_LOCATION_HAS_LAT_LONG
+                    | (location.hasAltitude() ? GNSS_LOCATION_HAS_ALTITUDE : 0)
+                    | (location.hasSpeed() ? GNSS_LOCATION_HAS_SPEED : 0)
+                    | (location.hasBearing() ? GNSS_LOCATION_HAS_BEARING : 0)
+                    | (location.hasAccuracy() ? GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY : 0)
+                    | (location.hasVerticalAccuracy() ? GNSS_LOCATION_HAS_VERTICAL_ACCURACY : 0)
+                    | (location.hasSpeedAccuracy() ? GNSS_LOCATION_HAS_SPEED_ACCURACY : 0)
+                    | (location.hasBearingAccuracy() ? GNSS_LOCATION_HAS_BEARING_ACCURACY : 0);
+
+            double latitudeDegrees = location.getLatitude();
+            double longitudeDegrees = location.getLongitude();
+            double altitudeMeters = location.getAltitude();
+            float speedMetersPerSec = location.getSpeed();
+            float bearingDegrees = location.getBearing();
+            float horizontalAccuracyMeters = location.getAccuracy();
+            float verticalAccuracyMeters = location.getVerticalAccuracyMeters();
+            float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond();
+            float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
+            long timestamp = location.getTime();
+
+            int elapsedRealtimeFlags = GNSS_REALTIME_HAS_TIMESTAMP_NS
+                    | (location.hasElapsedRealtimeUncertaintyNanos()
+                    ? GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS : 0);
+            long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
+            double elapsedRealtimeUncertaintyNanos = location.getElapsedRealtimeUncertaintyNanos();
+
+            mGnssHal.injectLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                    altitudeMeters, speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+                    verticalAccuracyMeters, speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+                    timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+                    elapsedRealtimeUncertaintyNanos);
         }
     }
 
@@ -784,9 +817,10 @@
     /**
      * Start batching.
      */
-    public boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+    public boolean startBatch(long periodNanos, float minUpdateDistanceMeters,
+            boolean wakeOnFifoFull) {
         Preconditions.checkState(mRegistered);
-        return mGnssHal.startBatch(periodNanos, wakeOnFifoFull);
+        return mGnssHal.startBatch(periodNanos, minUpdateDistanceMeters, wakeOnFifoFull);
     }
 
     /**
@@ -900,9 +934,9 @@
      * Sets AGPS reference cell id location.
      */
     public void setAgpsReferenceLocationCellId(@AgpsReferenceLocationType int type, int mcc,
-            int mnc, int lac, int cid) {
+            int mnc, int lac, long cid, int tac, int pcid, int arfcn) {
         Preconditions.checkState(mRegistered);
-        mGnssHal.setAgpsReferenceLocationCellId(type, mcc, mnc, lac, cid);
+        mGnssHal.setAgpsReferenceLocationCellId(type, mcc, mnc, lac, cid, tac, pcid, arfcn);
     }
 
     /**
@@ -1263,8 +1297,15 @@
             return native_read_nmea(buffer, bufferSize);
         }
 
-        protected void injectLocation(double latitude, double longitude, float accuracy) {
-            native_inject_location(latitude, longitude, accuracy);
+        protected void injectLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
+                double longitude, double altitude, float speed, float bearing,
+                float horizontalAccuracy, float verticalAccuracy, float speedAccuracy,
+                float bearingAccuracy, long timestamp, @GnssRealtimeFlags int elapsedRealtimeFlags,
+                long elapsedRealtimeNanos, double elapsedRealtimeUncertaintyNanos) {
+            native_inject_location(gnssLocationFlags, latitude, longitude, altitude, speed,
+                    bearing, horizontalAccuracy, verticalAccuracy, speedAccuracy, bearingAccuracy,
+                    timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+                    elapsedRealtimeUncertaintyNanos);
         }
 
         protected void injectBestLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
@@ -1340,8 +1381,9 @@
             native_cleanup_batching();
         }
 
-        protected boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
-            return native_start_batch(periodNanos, wakeOnFifoFull);
+        protected boolean startBatch(long periodNanos, float minUpdateDistanceMeters,
+                boolean wakeOnFifoFull) {
+            return native_start_batch(periodNanos, minUpdateDistanceMeters, wakeOnFifoFull);
         }
 
         protected void flushBatch() {
@@ -1396,8 +1438,8 @@
         }
 
         protected void setAgpsReferenceLocationCellId(@AgpsReferenceLocationType int type, int mcc,
-                int mnc, int lac, int cid) {
-            native_agps_set_ref_location_cellid(type, mcc, mnc, lac, cid);
+                int mnc, int lac, long cid, int tac, int pcid, int arfcn) {
+            native_agps_set_ref_location_cellid(type, mcc, mnc, lac, cid, tac, pcid, arfcn);
         }
 
         protected boolean isPsdsSupported() {
@@ -1438,8 +1480,13 @@
 
     // location injection APIs
 
-    private static native void native_inject_location(double latitude, double longitude,
-            float accuracy);
+    private static native void native_inject_location(
+            int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees,
+            double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
+            float horizontalAccuracyMeters, float verticalAccuracyMeters,
+            float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
+            long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos,
+            double elapsedRealtimeUncertaintyNanos);
 
 
     private static native void native_inject_best_location(
@@ -1494,7 +1541,8 @@
 
     private static native void native_cleanup_batching();
 
-    private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
+    private static native boolean native_start_batch(long periodNanos,
+            float minUpdateDistanceMeters, boolean wakeOnFifoFull);
 
     private static native void native_flush_batch();
 
@@ -1533,7 +1581,7 @@
     private static native void native_agps_set_id(int type, String setid);
 
     private static native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
-            int lac, int cid);
+            int lac, long cid, int tac, int pcid, int arfcn);
 
     // PSDS APIs
 
diff --git a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
index 2df2101..557ecda 100644
--- a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
 
+import android.annotation.Nullable;
 import android.location.util.identity.CallerIdentity;
 
 import com.android.server.location.LocationPermissions;
@@ -36,9 +37,10 @@
     public interface LocationPermissionsListener {
 
         /**
-         * Called when something has changed about location permissions for the given package.
+         * Called when something has changed about location permissions for the given package. A
+         * null package indicates this affects every package.
          */
-        void onLocationPermissionsChanged(String packageName);
+        void onLocationPermissionsChanged(@Nullable String packageName);
 
         /**
          * Called when something has changed about location permissions for the given uid.
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 1ba32ac..d42e2c6 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -509,8 +509,8 @@
         }
 
         @GuardedBy("mLock")
-        final boolean onLocationPermissionsChanged(String packageName) {
-            if (getIdentity().getPackageName().equals(packageName)) {
+        final boolean onLocationPermissionsChanged(@Nullable String packageName) {
+            if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
                 return onLocationPermissionsChanged();
             }
 
@@ -915,30 +915,19 @@
                 return null;
             }
 
+            // acquire a wakelock for non-passive requests
+            boolean useWakeLock =
+                    getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL;
+
             // deliver location
             return new ListenerOperation<LocationTransport>() {
 
-                private boolean mUseWakeLock;
-
                 @Override
                 public void onPreExecute() {
-                    mUseWakeLock = false;
-
-                    // don't acquire a wakelock for passive requests or for mock locations
-                    if (getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL) {
-                        final int size = locationResult.size();
-                        for (int i = 0; i < size; ++i) {
-                            if (!locationResult.get(i).isMock()) {
-                                mUseWakeLock = true;
-                                break;
-                            }
-                        }
-                    }
-
                     // update last delivered location
                     setLastDeliveredLocation(locationResult.getLastLocation());
 
-                    if (mUseWakeLock) {
+                    if (useWakeLock) {
                         mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
                     }
                 }
@@ -955,14 +944,14 @@
                     }
 
                     listener.deliverOnLocationChanged(deliverLocationResult,
-                            mUseWakeLock ? mWakeLockReleaser : null);
+                            useWakeLock ? mWakeLockReleaser : null);
                     EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
                             getIdentity());
                 }
 
                 @Override
                 public void onPostExecute(boolean success) {
-                    if (!success && mUseWakeLock) {
+                    if (!success && useWakeLock) {
                         mWakeLock.release();
                     }
 
@@ -1355,7 +1344,7 @@
     private final LocationPermissionsListener mLocationPermissionsListener =
             new LocationPermissionsListener() {
                 @Override
-                public void onLocationPermissionsChanged(String packageName) {
+                public void onLocationPermissionsChanged(@Nullable String packageName) {
                     LocationProviderManager.this.onLocationPermissionsChanged(packageName);
                 }
 
@@ -2361,7 +2350,7 @@
         }
     }
 
-    private void onLocationPermissionsChanged(String packageName) {
+    private void onLocationPermissionsChanged(@Nullable String packageName) {
         synchronized (mLock) {
             updateRegistrations(
                     registration -> registration.onLocationPermissionsChanged(packageName));
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index fab11a1..8f05130 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -20,6 +20,9 @@
 import static android.Manifest.permission.MANAGE_BIOMETRIC;
 import static android.Manifest.permission.READ_CONTACTS;
 import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.USER_ALL;
@@ -39,7 +42,10 @@
 import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
+import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
+import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_WEAK;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -114,6 +120,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -123,6 +130,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.LockscreenCredential;
@@ -135,6 +144,7 @@
 import com.android.server.locksettings.LockSettingsStorage.PersistentData;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.locksettings.SyntheticPasswordManager.TokenType;
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -636,12 +646,9 @@
 
     private void showEncryptionNotificationForProfile(UserHandle user) {
         Resources r = mContext.getResources();
-        CharSequence title = r.getText(
-                com.android.internal.R.string.profile_encrypted_title);
-        CharSequence message = r.getText(
-                com.android.internal.R.string.profile_encrypted_message);
-        CharSequence detail = r.getText(
-                com.android.internal.R.string.profile_encrypted_detail);
+        CharSequence title = getEncryptionNotificationTitle();
+        CharSequence message = getEncryptionNotificationMessage();
+        CharSequence detail = getEncryptionNotificationDetail();
 
         final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
         final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
@@ -657,6 +664,24 @@
         showEncryptionNotification(user, title, message, detail, intent);
     }
 
+    private String getEncryptionNotificationTitle() {
+        return mInjector.getDevicePolicyManager().getString(
+                PROFILE_ENCRYPTED_TITLE,
+                () -> mContext.getString(R.string.profile_encrypted_title));
+    }
+
+    private String getEncryptionNotificationDetail() {
+        return mInjector.getDevicePolicyManager().getString(
+                PROFILE_ENCRYPTED_DETAIL,
+                () -> mContext.getString(R.string.profile_encrypted_detail));
+    }
+
+    private String getEncryptionNotificationMessage() {
+        return mInjector.getDevicePolicyManager().getString(
+                PROFILE_ENCRYPTED_MESSAGE,
+                () -> mContext.getString(R.string.profile_encrypted_message));
+    }
+
     private void showEncryptionNotification(UserHandle user, CharSequence title,
             CharSequence message, CharSequence detail, PendingIntent intent) {
         if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
@@ -1123,6 +1148,16 @@
         return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
     }
 
+    private void checkManageWeakEscrowTokenMethodUsage() {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN,
+                "Requires MANAGE_WEAK_ESCROW_TOKEN permission.");
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            throw new IllegalArgumentException(
+                    "Weak escrow token are only for automotive devices.");
+        }
+    }
+
     @Override
     public boolean hasSecureLockScreen() {
         return mHasSecureLockScreen;
@@ -1911,6 +1946,97 @@
         });
     }
 
+    /** Register the given WeakEscrowTokenRemovedListener. */
+    @Override
+    public boolean registerWeakEscrowTokenRemovedListener(
+            @NonNull IWeakEscrowTokenRemovedListener listener) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mSpManager.registerWeakEscrowTokenRemovedListener(listener);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /** Unregister the given WeakEscrowTokenRemovedListener. */
+    @Override
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            @NonNull IWeakEscrowTokenRemovedListener listener) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mSpManager.unregisterWeakEscrowTokenRemovedListener(listener);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public long addWeakEscrowToken(byte[] token, int userId,
+            @NonNull IWeakEscrowTokenActivatedListener listener) {
+        checkManageWeakEscrowTokenMethodUsage();
+        Objects.requireNonNull(listener, "Listener can not be null.");
+        EscrowTokenStateChangeCallback internalListener = (handle, userId1) -> {
+            try {
+                listener.onWeakEscrowTokenActivated(handle, userId1);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception while notifying weak escrow token has been activated", e);
+            }
+        };
+        final long restoreToken = Binder.clearCallingIdentity();
+        try {
+            return addEscrowToken(token, TOKEN_TYPE_WEAK, userId, internalListener);
+        } finally {
+            Binder.restoreCallingIdentity(restoreToken);
+        }
+    }
+
+    @Override
+    public boolean removeWeakEscrowToken(long handle, int userId) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return removeEscrowToken(handle, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public boolean isWeakEscrowTokenActive(long handle, int userId) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return isEscrowTokenActive(handle, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public boolean isWeakEscrowTokenValid(long handle, byte[] token, int userId) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long restoreToken = Binder.clearCallingIdentity();
+        try {
+            synchronized (mSpManager) {
+                if (!mSpManager.hasEscrowData(userId)) {
+                    Slog.w(TAG, "Escrow token is disabled on the current user");
+                    return false;
+                }
+                AuthenticationResult authResult = mSpManager.unwrapWeakTokenBasedSyntheticPassword(
+                        getGateKeeperService(), handle, token, userId);
+                if (authResult.authToken == null) {
+                    Slog.w(TAG, "Invalid escrow token supplied");
+                    return false;
+                }
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(restoreToken);
+        }
+    }
+
     @VisibleForTesting /** Note: this method is overridden in unit tests */
     protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
         if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
@@ -3029,6 +3155,7 @@
     private long setLockCredentialWithAuthTokenLocked(LockscreenCredential credential,
             AuthenticationToken auth, int userId) {
         if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
+        final int savedCredentialType = getCredentialTypeInternal(userId);
         long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
                 credential, auth, userId);
         final Map<Integer, LockscreenCredential> profilePasswords;
@@ -3075,6 +3202,9 @@
 
         setUserPasswordMetrics(credential, userId);
         mManagedProfilePasswordCache.removePassword(userId);
+        if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
+            mSpManager.destroyAllWeakTokenBasedSyntheticPasswords(userId);
+        }
 
         if (profilePasswords != null) {
             for (Map.Entry<Integer, LockscreenCredential> entry : profilePasswords.entrySet()) {
@@ -3242,8 +3372,9 @@
         }
     }
 
-    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback) {
-        if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId);
+    private long addEscrowToken(@NonNull byte[] token, @TokenType int type, int userId,
+            @NonNull EscrowTokenStateChangeCallback callback) {
+        if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId + ", type=" + type);
         synchronized (mSpManager) {
             // Migrate to synthetic password based credentials if the user has no password,
             // the token can then be activated immediately.
@@ -3264,7 +3395,8 @@
                     throw new SecurityException("Escrow token is disabled on the current user");
                 }
             }
-            long handle = mSpManager.createTokenBasedSyntheticPassword(token, userId, callback);
+            long handle = mSpManager.createTokenBasedSyntheticPassword(token, type, userId,
+                    callback);
             if (auth != null) {
                 mSpManager.activateTokenBasedSyntheticPassword(handle, auth, userId);
             }
@@ -3345,8 +3477,8 @@
     private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential,
             long tokenHandle, byte[] token, int userId) {
         final AuthenticationResult result;
-        result = mSpManager.unwrapTokenBasedSyntheticPassword(
-                getGateKeeperService(), tokenHandle, token, userId);
+        result = mSpManager.unwrapTokenBasedSyntheticPassword(getGateKeeperService(), tokenHandle,
+                token, userId);
         if (result.authToken == null) {
             Slog.w(TAG, "Invalid escrow token supplied");
             return false;
@@ -3369,7 +3501,8 @@
         AuthenticationResult authResult;
         synchronized (mSpManager) {
             if (!mSpManager.hasEscrowData(userId)) {
-                throw new SecurityException("Escrow token is disabled on the current user");
+                Slog.w(TAG, "Escrow token is disabled on the current user");
+                return false;
             }
             authResult = mSpManager.unwrapTokenBasedSyntheticPassword(getGateKeeperService(),
                     tokenHandle, token, userId);
@@ -3643,7 +3776,8 @@
         @Override
         public long addEscrowToken(byte[] token, int userId,
                 EscrowTokenStateChangeCallback callback) {
-            return LockSettingsService.this.addEscrowToken(token, userId, callback);
+            return LockSettingsService.this.addEscrowToken(token, TOKEN_TYPE_STRONG, userId,
+                    callback);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 601a572..2da4431 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.PasswordMetrics;
@@ -28,6 +29,7 @@
 import android.hardware.weaver.V1_0.WeaverReadResponse;
 import android.hardware.weaver.V1_0.WeaverReadStatus;
 import android.hardware.weaver.V1_0.WeaverStatus;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.security.GateKeeper;
@@ -41,6 +43,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
@@ -48,6 +51,8 @@
 
 import libcore.util.HexEncoding;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -111,7 +116,8 @@
     private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
     private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
-    private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
+    private static final byte SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED = 1;
+    private static final byte SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED = 2;
 
     // 256-bit synthetic password
     private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
@@ -366,10 +372,47 @@
         }
     }
 
+    static class SyntheticPasswordBlob {
+        byte mVersion;
+        byte mType;
+        byte[] mContent;
+
+        public static SyntheticPasswordBlob create(byte version, byte type, byte[] content) {
+            SyntheticPasswordBlob result = new SyntheticPasswordBlob();
+            result.mVersion = version;
+            result.mType = type;
+            result.mContent = content;
+            return result;
+        }
+
+        public static SyntheticPasswordBlob fromBytes(byte[] data) {
+            SyntheticPasswordBlob result = new SyntheticPasswordBlob();
+            result.mVersion = data[0];
+            result.mType = data[1];
+            result.mContent = Arrays.copyOfRange(data, 2, data.length);
+            return result;
+        }
+
+        public byte[] toByte() {
+            byte[] blob = new byte[mContent.length + 1 + 1];
+            blob[0] = mVersion;
+            blob[1] = mType;
+            System.arraycopy(mContent, 0, blob, 2, mContent.length);
+            return blob;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TOKEN_TYPE_STRONG, TOKEN_TYPE_WEAK})
+    @interface TokenType {}
+    static final int TOKEN_TYPE_STRONG = 0;
+    static final int TOKEN_TYPE_WEAK = 1;
+
     static class TokenData {
         byte[] secdiscardableOnDisk;
         byte[] weaverSecret;
         byte[] aggregatedSecret;
+        @TokenType int mType;
         EscrowTokenStateChangeCallback mCallback;
     }
 
@@ -381,6 +424,9 @@
 
     private final UserManager mUserManager;
 
+    private final RemoteCallbackList<IWeakEscrowTokenRemovedListener> mListeners =
+            new RemoteCallbackList<>();
+
     public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
             UserManager userManager, PasswordSlotManager passwordSlotManager) {
         mContext = context;
@@ -880,13 +926,34 @@
      * Create a token based Synthetic password for the given user.
      * @return the handle of the token
      */
-    public long createTokenBasedSyntheticPassword(byte[] token, int userId,
+    public long createStrongTokenBasedSyntheticPassword(byte[] token, int userId,
+            @Nullable EscrowTokenStateChangeCallback changeCallback) {
+        return createTokenBasedSyntheticPassword(token, TOKEN_TYPE_STRONG, userId,
+                changeCallback);
+    }
+
+    /**
+     * Create a weak token based Synthetic password for the given user.
+     * @return the handle of the weak token
+     */
+    public long createWeakTokenBasedSyntheticPassword(byte[] token, int userId,
+            @Nullable EscrowTokenStateChangeCallback changeCallback) {
+        return createTokenBasedSyntheticPassword(token, TOKEN_TYPE_WEAK, userId,
+                changeCallback);
+    }
+
+    /**
+     * Create a token based Synthetic password of the given type for the given user.
+     * @return the handle of the token
+     */
+    public long createTokenBasedSyntheticPassword(byte[] token, @TokenType int type, int userId,
             @Nullable EscrowTokenStateChangeCallback changeCallback) {
         long handle = generateHandle();
         if (!tokenMap.containsKey(userId)) {
             tokenMap.put(userId, new ArrayMap<>());
         }
         TokenData tokenData = new TokenData();
+        tokenData.mType = type;
         final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
         if (isWeaverAvailable()) {
             tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
@@ -910,6 +977,7 @@
         return new ArraySet<>(tokenMap.get(userId).keySet());
     }
 
+    /** Remove the given pending token. */
     public boolean removePendingToken(long handle, int userId) {
         if (!tokenMap.containsKey(userId)) {
             return false;
@@ -941,7 +1009,7 @@
             mPasswordSlotManager.markSlotInUse(slot);
         }
         saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
-        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
+        createSyntheticPasswordBlob(handle, getTokenBasedBlobType(tokenData.mType), authToken,
                 tokenData.aggregatedSecret, 0L, userId);
         tokenMap.get(userId).remove(handle);
         if (tokenData.mCallback != null) {
@@ -953,26 +1021,23 @@
     private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
             byte[] applicationId, long sid, int userId) {
         final byte[] secret;
-        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
+        if (type == SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED
+                || type == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
             secret = authToken.getEscrowSecret();
         } else {
             secret = authToken.getSyntheticPassword();
         }
         byte[] content = createSPBlob(getKeyName(handle), secret, applicationId, sid);
-        byte[] blob = new byte[content.length + 1 + 1];
         /*
          * We can upgrade from v1 to v2 because that's just a change in the way that
          * the SP is stored. However, we can't upgrade to v3 because that is a change
          * in the way that passwords are derived from the SP.
          */
-        if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
-            blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
-        } else {
-            blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
-        }
-        blob[1] = type;
-        System.arraycopy(content, 0, blob, 2, content.length);
-        saveState(SP_BLOB_NAME, blob, handle, userId);
+        byte version = authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3
+                ? SYNTHETIC_PASSWORD_VERSION_V3 : SYNTHETIC_PASSWORD_VERSION_V2;
+
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob.create(version, type, content);
+        saveState(SP_BLOB_NAME, blob.toByte(), handle, userId);
     }
 
     /**
@@ -1089,6 +1154,36 @@
      */
     public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
             IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob
+                .fromBytes(loadState(SP_BLOB_NAME, handle, userId));
+        return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle,
+                blob.mType, token, userId);
+    }
+
+    /**
+     * Decrypt a synthetic password by supplying an strong escrow token and corresponding token
+     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
+     * verification to referesh the SID & Auth token maintained by the system.
+     */
+    public @NonNull AuthenticationResult unwrapStrongTokenBasedSyntheticPassword(
+            IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
+        return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle,
+                SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED, token, userId);
+    }
+
+    /**
+     * Decrypt a synthetic password by supplying a weak escrow token and corresponding token
+     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
+     * verification to referesh the SID & Auth token maintained by the system.
+     */
+    public @NonNull AuthenticationResult unwrapWeakTokenBasedSyntheticPassword(
+            IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
+        return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle,
+                SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED, token, userId);
+    }
+
+    private  @NonNull AuthenticationResult unwrapTokenBasedSyntheticPasswordInternal(
+            IGateKeeperService gatekeeper, long handle, byte type, byte[] token, int userId) {
         AuthenticationResult result = new AuthenticationResult();
         byte[] secdiscardable = loadSecdiscardable(handle, userId);
         int slotId = loadWeaverSlot(handle, userId);
@@ -1109,8 +1204,7 @@
                     PERSONALISATION_WEAVER_TOKEN, secdiscardable);
         }
         byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
-        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
-                applicationId, 0L, userId);
+        result.authToken = unwrapSyntheticPasswordBlob(handle, type, applicationId, 0L, userId);
         if (result.authToken != null) {
             result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
             if (result.gkResponse == null) {
@@ -1126,33 +1220,33 @@
 
     private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
             byte[] applicationId, long sid, int userId) {
-        byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
-        if (blob == null) {
+        byte[] data = loadState(SP_BLOB_NAME, handle, userId);
+        if (data == null) {
             return null;
         }
-        final byte version = blob[0];
-        if (version != SYNTHETIC_PASSWORD_VERSION_V3
-                && version != SYNTHETIC_PASSWORD_VERSION_V2
-                && version != SYNTHETIC_PASSWORD_VERSION_V1) {
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(data);
+        if (blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V3
+                && blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V2
+                && blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V1) {
             throw new IllegalArgumentException("Unknown blob version");
         }
-        if (blob[1] != type) {
+        if (blob.mType != type) {
             throw new IllegalArgumentException("Invalid blob type");
         }
         final byte[] secret;
-        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
-            secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle),
-                    Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+        if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) {
+            secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle), blob.mContent,
+                    applicationId);
         } else {
-            secret = decryptSPBlob(getKeyName(handle),
-                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+            secret = decryptSPBlob(getKeyName(handle), blob.mContent, applicationId);
         }
         if (secret == null) {
             Slog.e(TAG, "Fail to decrypt SP for user " + userId);
             return null;
         }
-        AuthenticationToken result = new AuthenticationToken(version);
-        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
+        AuthenticationToken result = new AuthenticationToken(blob.mVersion);
+        if (type == SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED
+                || type == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
             if (!loadEscrowData(result, userId)) {
                 Slog.e(TAG, "User is not escrowable: " + userId);
                 return null;
@@ -1161,7 +1255,7 @@
         } else {
             result.recreateDirectly(secret);
         }
-        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
+        if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) {
             Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
             createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
         }
@@ -1233,9 +1327,28 @@
         return hasState(SP_BLOB_NAME, handle, userId);
     }
 
+    /** Destroy the escrow token with the given handle for the given user. */
     public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME, handle,
+                userId));
         destroySyntheticPassword(handle, userId);
         destroyState(SECDISCARDABLE_NAME, handle, userId);
+        if (blob.mType == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
+            notifyWeakEscrowTokenRemovedListeners(handle, userId);
+        }
+    }
+
+    /** Destroy all weak escrow tokens for the given user. */
+    public void destroyAllWeakTokenBasedSyntheticPasswords(int userId) {
+        List<Long> handles = mStorage.listSyntheticPasswordHandlesForUser(SECDISCARDABLE_NAME,
+                userId);
+        for (long handle: handles) {
+            SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME,
+                    handle, userId));
+            if (blob.mType == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
+                destroyTokenBasedSyntheticPassword(handle, userId);
+            }
+        }
     }
 
     public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
@@ -1285,6 +1398,16 @@
         return loadState(SECDISCARDABLE_NAME, handle, userId);
     }
 
+    private byte getTokenBasedBlobType(@TokenType int type) {
+        switch (type) {
+            case TOKEN_TYPE_WEAK:
+                return SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED;
+            case TOKEN_TYPE_STRONG:
+            default:
+                return SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED;
+        }
+    }
+
     /**
      * Retrieves the saved password metrics associated with a SP handle. Only meaningful to be
      * called on the handle of a password-based synthetic password. A valid AuthenticationToken for
@@ -1439,4 +1562,33 @@
         }
         return success;
     }
+
+    /** Register the given IWeakEscrowTokenRemovedListener. */
+    public boolean registerWeakEscrowTokenRemovedListener(
+            IWeakEscrowTokenRemovedListener listener) {
+        return mListeners.register(listener);
+    }
+
+    /** Unregister the given IWeakEscrowTokenRemovedListener. */
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            IWeakEscrowTokenRemovedListener listener) {
+        return mListeners.unregister(listener);
+    }
+
+    private void notifyWeakEscrowTokenRemovedListeners(long handle, int userId) {
+        int i = mListeners.beginBroadcast();
+        try {
+            while (i > 0) {
+                i--;
+                try {
+                    mListeners.getBroadcastItem(i).onWeakEscrowTokenRemoved(handle, userId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Exception while notifying WeakEscrowTokenRemovedListener.",
+                            e);
+                }
+            }
+        } finally {
+            mListeners.finishBroadcast();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index ffc1aed4..91de9e5 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -344,10 +344,19 @@
     }
 
     private void addActiveRoute(BluetoothRouteInfo btRoute) {
+        if (btRoute == null) {
+            if (DEBUG) {
+                Log.d(TAG, " btRoute is null");
+            }
+            return;
+        }
         if (DEBUG) {
             Log.d(TAG, "Adding active route: " + btRoute.route);
         }
-        if (btRoute == null || mActiveRoutes.contains(btRoute)) {
+        if (mActiveRoutes.contains(btRoute)) {
+            if (DEBUG) {
+                Log.d(TAG, " btRoute is already added.");
+            }
             return;
         }
         setRouteConnectionState(btRoute, STATE_CONNECTED);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 4822d6a..96391ac 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -52,7 +52,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
-import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
 
@@ -643,22 +642,24 @@
     }
 
     private void pushQueueUpdate() {
-        ParceledListSlice<QueueItem> parcelableQueue;
+        ArrayList<QueueItem> toSend;
         synchronized (mLock) {
             if (mDestroyed) {
                 return;
             }
-            if (mQueue == null) {
-                parcelableQueue = null;
-            } else {
-                parcelableQueue = new ParceledListSlice<>(mQueue);
-                // Limit the size of initial Parcel to prevent binder buffer overflow
-                // as onQueueChanged is an async binder call.
-                parcelableQueue.setInlineCountLimit(1);
+            toSend = new ArrayList<>();
+            if (mQueue != null) {
+                toSend.ensureCapacity(mQueue.size());
+                toSend.addAll(mQueue);
             }
         }
         Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
         for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+            ParceledListSlice<QueueItem> parcelableQueue = new ParceledListSlice<>(toSend);
+            // Limit the size of initial Parcel to prevent binder buffer overflow
+            // as onQueueChanged is an async binder call.
+            parcelableQueue.setInlineCountLimit(1);
+
             try {
                 holder.mCallback.onQueueChanged(parcelableQueue);
             } catch (DeadObjectException e) {
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 2e2d812..8097f4e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -1,8 +1,6 @@
+# Bug component: 137631
 [email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
 [email protected]
[email protected]
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/services/core/java/com/android/server/net/DelayedDiskWrite.java
deleted file mode 100644
index 8f09eb7..0000000
--- a/services/core/java/com/android/server/net/DelayedDiskWrite.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.DataOutputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class DelayedDiskWrite {
-    private HandlerThread mDiskWriteHandlerThread;
-    private Handler mDiskWriteHandler;
-    /* Tracks multiple writes on the same thread */
-    private int mWriteSequence = 0;
-    private final String TAG = "DelayedDiskWrite";
-
-    public interface Writer {
-        public void onWriteCalled(DataOutputStream out) throws IOException;
-    }
-
-    public void write(final String filePath, final Writer w) {
-        write(filePath, w, true);
-    }
-
-    public void write(final String filePath, final Writer w, final boolean open) {
-        if (TextUtils.isEmpty(filePath)) {
-            throw new IllegalArgumentException("empty file path");
-        }
-
-        /* Do a delayed write to disk on a separate handler thread */
-        synchronized (this) {
-            if (++mWriteSequence == 1) {
-                mDiskWriteHandlerThread = new HandlerThread("DelayedDiskWriteThread");
-                mDiskWriteHandlerThread.start();
-                mDiskWriteHandler = new Handler(mDiskWriteHandlerThread.getLooper());
-            }
-        }
-
-        mDiskWriteHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                doWrite(filePath, w, open);
-            }
-        });
-    }
-
-    private void doWrite(String filePath, Writer w, boolean open) {
-        DataOutputStream out = null;
-        try {
-            if (open) {
-                out = new DataOutputStream(new BufferedOutputStream(
-                        new FileOutputStream(filePath)));
-            }
-            w.onWriteCalled(out);
-        } catch (IOException e) {
-            loge("Error writing data file " + filePath);
-        } finally {
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (Exception e) {}
-            }
-
-            // Quit if no more writes sent
-            synchronized (this) {
-                if (--mWriteSequence == 0) {
-                    mDiskWriteHandler.getLooper().quit();
-                    mDiskWriteHandler = null;
-                    mDiskWriteHandlerThread = null;
-                }
-            }
-        }
-    }
-
-    private void loge(String s) {
-        Log.e(TAG, s);
-    }
-}
-
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 03a63b9..8ef42ff 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,11 +16,8 @@
 
 package com.android.server.net;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Network;
-import android.net.NetworkTemplate;
-import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.telephony.SubscriptionPlan;
 
@@ -56,11 +53,6 @@
      */
     public abstract SubscriptionPlan getSubscriptionPlan(Network network);
 
-    /**
-     * Return the active {@link SubscriptionPlan} for the given template.
-     */
-    public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
-
     public static final int QUOTA_TYPE_JOBS = 1;
     public static final int QUOTA_TYPE_MULTIPATH = 2;
 
@@ -99,13 +91,4 @@
      */
     public abstract void setMeteredRestrictedPackagesAsync(
             Set<String> packageNames, int userId);
-
-    /**
-     *  Notifies that the specified {@link NetworkStatsProvider} has reached its quota
-     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
-     *  {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
-     *
-     * @param tag the human readable identifier of the custom network stats provider.
-     */
-    public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag);
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 65c5b88..e555c13 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,7 +63,6 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkIdentity.OEM_NONE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -130,7 +129,7 @@
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -151,6 +150,8 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -181,7 +182,6 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.TrafficStats;
@@ -441,7 +441,7 @@
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
-    private NetworkStatsManagerInternal mNetworkStats;
+    private NetworkStatsManager mNetworkStats;
     private final INetworkManagementService mNetworkManager;
     private UsageStatsManagerInternal mUsageStats;
     private AppStandbyInternal mAppStandby;
@@ -453,6 +453,8 @@
     private ConnectivityManager mConnManager;
     private PowerManagerInternal mPowerManagerInternal;
     private PowerWhitelistManager mPowerWhitelistManager;
+    @NonNull
+    private final Dependencies mDeps;
 
     /** Current cached value of the current Battery Saver mode's setting for restrict background. */
     @GuardedBy("mUidRulesFirstLock")
@@ -704,7 +706,7 @@
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement) {
         this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
-                getDefaultClock(), getDefaultSystemDir(), false);
+                getDefaultClock(), getDefaultSystemDir(), false, new Dependencies(context));
     }
 
     private static @NonNull File getDefaultSystemDir() {
@@ -716,9 +718,59 @@
                 Clock.systemUTC());
     }
 
+    static class Dependencies {
+        final Context mContext;
+        final NetworkStatsManager mNetworkStatsManager;
+        Dependencies(Context context) {
+            mContext = context;
+            mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+            // Query stats from NetworkStatsService will trigger a poll by default.
+            // But since NPMS listens stats updated event, and will query stats
+            // after the event. A polling -> updated -> query -> polling loop will be introduced
+            // if polls on open. Hence, while NPMS manages it's poll requests explicitly, set
+            // flag to false to prevent a polling loop.
+            mNetworkStatsManager.setPollOnOpen(false);
+        }
+
+        long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
+            try {
+                final NetworkStats.Bucket ret = mNetworkStatsManager
+                        .querySummaryForDevice(template, start, end);
+                return ret.getRxBytes() + ret.getTxBytes();
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failed to read network stats: " + e);
+                return 0;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_NETWORK);
+            }
+        }
+
+        @NonNull
+        List<NetworkStats.Bucket> getNetworkUidBytes(
+                @NonNull NetworkTemplate template, long start, long end) {
+            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
+            final List<NetworkStats.Bucket> buckets = new ArrayList<>();
+            try {
+                final NetworkStats stats = mNetworkStatsManager.querySummary(template, start, end);
+                while (stats.hasNextBucket()) {
+                    final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+                    stats.getNextBucket(bucket);
+                    buckets.add(bucket);
+                }
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failed to read network stats: " + e);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_NETWORK);
+            }
+            return buckets;
+        }
+    }
+
+    @VisibleForTesting
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
-            File systemDir, boolean suppressDefaultPolicy) {
+            File systemDir, boolean suppressDefaultPolicy, Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing context");
         mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
         mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
@@ -739,10 +791,12 @@
         mUidEventHandler = new Handler(mUidEventThread.getLooper(), mUidEventHandlerCallback);
 
         mSuppressDefaultPolicy = suppressDefaultPolicy;
+        mDeps = Objects.requireNonNull(deps, "missing Dependencies");
 
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"), "net-policy");
 
         mAppOps = context.getSystemService(AppOpsManager.class);
+        mNetworkStats = context.getSystemService(NetworkStatsManager.class);
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
         // Expose private service for system components to use.
         LocalServices.addService(NetworkPolicyManagerInternal.class,
@@ -842,7 +896,6 @@
 
             mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
             mAppStandby = LocalServices.getService(AppStandbyInternal.class);
-            mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
@@ -1161,21 +1214,34 @@
     };
 
     /**
-     * Receiver that watches for {@link INetworkStatsService} updates, which we
+     * Receiver that watches for {@link NetworkStatsManager} updates, which we
      * use to check against {@link NetworkPolicy#warningBytes}.
      */
-    final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() {
+    private final NetworkStatsBroadcastReceiver mStatsReceiver =
+            new NetworkStatsBroadcastReceiver();
+    private class NetworkStatsBroadcastReceiver extends BroadcastReceiver {
+        private boolean mIsAnyIntentReceived = false;
         @Override
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified
             // READ_NETWORK_USAGE_HISTORY permission above.
 
+            mIsAnyIntentReceived = true;
+
             synchronized (mNetworkPoliciesSecondLock) {
                 updateNetworkRulesNL();
                 updateNetworkEnabledNL();
                 updateNotificationsNL();
             }
         }
+
+        /**
+         * Return whether any {@code ACTION_NETWORK_STATS_UPDATED} intent is received.
+         * Used to determine if NetworkStatsService is ready.
+         */
+        public boolean isAnyIntentReceived() {
+            return mIsAnyIntentReceived;
+        }
     };
 
     /**
@@ -1385,15 +1451,17 @@
         long maxBytes = 0;
         int maxUid = 0;
 
-        final NetworkStats stats = getNetworkUidBytes(template, start, end);
-        NetworkStats.Entry entry = null;
-        for (int i = 0; i < stats.size(); i++) {
-            entry = stats.getValues(i, entry);
-            final long bytes = entry.rxBytes + entry.txBytes;
+        // Skip if not ready. NetworkStatsService will block public API calls until it is
+        // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+        if (!mStatsReceiver.isAnyIntentReceived()) return null;
+
+        final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
+        for (final NetworkStats.Bucket entry : stats) {
+            final long bytes = entry.getRxBytes() + entry.getTxBytes();
             totalBytes += bytes;
             if (bytes > maxBytes) {
                 maxBytes = bytes;
-                maxUid = entry.uid;
+                maxUid = entry.getUid();
             }
         }
 
@@ -1429,13 +1497,11 @@
         for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
             final int subId = mSubIdToSubscriberId.keyAt(i);
             final String subscriberId = mSubIdToSubscriberId.valueAt(i);
-            final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
-                    true, OEM_NONE);
-            /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a
-             * placeholder value here. The probeIdent is matched against a NetworkTemplate which
-             * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the
-             * template to match probeIdent without regard to OEM managed status. */
+            final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+                    .setType(TYPE_MOBILE)
+                    .setSubscriberId(subscriberId)
+                    .setMetered(true)
+                    .setDefaultNetwork(true).build();
             if (template.matches(probeIdent)) {
                 return subId;
             }
@@ -1668,9 +1734,11 @@
 
         // find and update the carrier NetworkPolicy for this subscriber id
         boolean policyUpdated = false;
-        final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
-                OEM_NONE);
+        final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+                .setType(TYPE_MOBILE)
+                .setSubscriberId(subscriberId)
+                .setMetered(true)
+                .setDefaultNetwork(true).build();
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
@@ -1898,10 +1966,11 @@
                 for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
                     final int subId = mSubIdToSubscriberId.keyAt(i);
                     final String subscriberId = mSubIdToSubscriberId.valueAt(i);
-
-                    final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                            TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
-                            true, OEM_NONE);
+                    final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+                            .setType(TYPE_MOBILE)
+                            .setSubscriberId(subscriberId)
+                            .setMetered(true)
+                            .setDefaultNetwork(true).build();
                     // Template is matched when subscriber id matches.
                     if (template.matches(probeIdent)) {
                         matchingSubIds.add(subId);
@@ -2005,11 +2074,9 @@
         for (final NetworkStateSnapshot snapshot : snapshots) {
             mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
 
-            // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype
-            // in the object created here is never used and its value doesn't matter, so use
-            // NETWORK_TYPE_UNKNOWN.
-            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
-                    true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+            // Policies matched by NPMS only match by subscriber ID or by network ID.
+            final NetworkIdentity ident = new NetworkIdentity.Builder()
+                    .setNetworkStateSnapshot(snapshot).setDefaultNetwork(true).build();
             identified.put(snapshot, ident);
         }
 
@@ -2206,9 +2273,11 @@
     @GuardedBy("mNetworkPoliciesSecondLock")
     private boolean ensureActiveCarrierPolicyAL(int subId, String subscriberId) {
         // Poke around to see if we already have a policy
-        final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
-                OEM_NONE);
+        final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+                .setType(TYPE_MOBILE)
+                .setSubscriberId(subscriberId)
+                .setMetered(true)
+                .setDefaultNetwork(true).build();
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
@@ -2618,7 +2687,7 @@
         final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
         for (int i = 0; i < configs.size(); ++i) {
             final WifiConfiguration config = configs.get(i);
-            for (String key : config.getAllPersistableNetworkKeys()) {
+            for (String key : config.getAllNetworkKeys()) {
                 final Boolean metered = wifiNetworkKeys.get(key);
                 if (metered != null) {
                     Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
@@ -3363,6 +3432,35 @@
         return result;
     }
 
+    /**
+     * Get subscription plan for the given networkTemplate.
+     *
+     * @param template the networkTemplate to get the subscription plan for.
+     */
+    @Override
+    public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+        synchronized (mNetworkPoliciesSecondLock) {
+            final int subId = findRelevantSubIdNL(template);
+            return getPrimarySubscriptionPlanLocked(subId);
+        }
+    }
+
+    /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+     * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     */
+    @Override
+    public void notifyStatsProviderWarningOrLimitReached() {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+        // This API may be called before the system is ready.
+        synchronized (mNetworkPoliciesSecondLock) {
+            if (!mSystemReady) return;
+        }
+        mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -4980,7 +5078,8 @@
                     // make sure stats are recorded frequently enough; we aim
                     // for 2MB threshold for 2GB/month rules.
                     final long persistThreshold = lowestRule / 1000;
-                    mNetworkStats.advisePersistThreshold(persistThreshold);
+                    // TODO: Sync internal naming with the API surface.
+                    mNetworkStats.setDefaultGlobalAlert(persistThreshold);
                     return true;
                 }
                 case MSG_UPDATE_INTERFACE_QUOTAS: {
@@ -5349,25 +5448,10 @@
 
     @Deprecated
     private long getTotalBytes(NetworkTemplate template, long start, long end) {
-        return getNetworkTotalBytes(template, start, end);
-    }
-
-    private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-        try {
-            return mNetworkStats.getNetworkTotalBytes(template, start, end);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failed to read network stats: " + e);
-            return 0;
-        }
-    }
-
-    private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-        try {
-            return mNetworkStats.getNetworkUidBytes(template, start, end);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failed to read network stats: " + e);
-            return new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        }
+        // Skip if not ready. NetworkStatsService will block public API calls until it is
+        // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+        if (!mStatsReceiver.isAnyIntentReceived()) return 0;
+        return mDeps.getNetworkTotalBytes(template, start, end);
     }
 
     private boolean isBandwidthControlEnabled() {
@@ -5583,14 +5667,6 @@
         }
 
         @Override
-        public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
-            synchronized (mNetworkPoliciesSecondLock) {
-                final int subId = findRelevantSubIdNL(template);
-                return getPrimarySubscriptionPlanLocked(subId);
-            }
-        }
-
-        @Override
         public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
             final long quotaBytes;
             synchronized (mNetworkPoliciesSecondLock) {
@@ -5632,12 +5708,6 @@
             mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
                     userId, 0, packageNames).sendToTarget();
         }
-
-        @Override
-        public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
-            Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
-            mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
-        }
     }
 
     private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index 471c9b9..4b70e2e 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -94,7 +94,8 @@
     @Override
     public void onConnected() {
         if (DEBUG) Slog.d(TAG, "onConnected");
-        mContext.registerReceiver(mReceiver, new IntentFilter(ACTION));
+        mContext.registerReceiver(mReceiver, new IntentFilter(ACTION),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mConnected = true;
     }
 
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 4be4f0a..4fe7a27 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -306,7 +306,8 @@
             filter.addAction(Intent.ACTION_TIME_CHANGED);
             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
             filter.addAction(ACTION_EVALUATE);
-            registerReceiver(mReceiver, filter);
+            registerReceiver(mReceiver, filter,
+                    Context.RECEIVER_EXPORTED_UNAUDITED);
         } else {
             unregisterReceiver(mReceiver);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 8a33299..a9b2570 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -100,7 +100,8 @@
 
         IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION);
         deletionFilter.addDataScheme(SCHEME_DELETION);
-        mContext.registerReceiver(mFileCleanupReceiver, deletionFilter);
+        mContext.registerReceiver(mFileCleanupReceiver, deletionFilter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     public void init() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 0528b95..c548e7e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -36,4 +36,10 @@
     void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId);
 
     void onConversationRemoved(String pkg, int uid, Set<String> shortcuts);
+
+    /** Get the number of notification channels for a given package */
+    int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
+
+    /** Does the specified package/uid have permission to post notifications? */
+    boolean areNotificationsEnabledForPackage(String pkg, int uid);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2068632..86b385b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -133,6 +133,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerInternal.ServiceNotificationPolicy;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -296,6 +297,7 @@
 import com.android.server.notification.toast.ToastRecord;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.quota.MultiRateLimiter;
@@ -480,6 +482,7 @@
     private IPackageManager mPackageManager;
     private PackageManager mPackageManagerClient;
     private PackageManagerInternal mPackageManagerInternal;
+    private PermissionPolicyInternal mPermissionPolicyInternal;
     AudioManager mAudioManager;
     AudioManagerInternal mAudioManagerInternal;
     // Can be null for wear
@@ -595,6 +598,7 @@
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
     private boolean mLockScreenAllowSecureNotifications = true;
+    boolean mAllowFgsDismissal = false;
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
@@ -1137,8 +1141,9 @@
                     id = r.getSbn().getId();
                 }
             }
+            int mustNotHaveFlags = FLAG_ONGOING_EVENT;
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
-                    FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
+                    mustNotHaveFlags,
                     true, userId, REASON_CANCEL, nv.rank, nv.count,null);
             nv.recycle();
         }
@@ -2104,6 +2109,7 @@
         mPackageManager = packageManager;
         mPackageManagerClient = packageManagerClient;
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
         mAppOps = appOps;
         mAppOpsService = iAppOps;
         try {
@@ -2328,7 +2334,8 @@
 
         IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
         timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
-        getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
+        getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
         getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
@@ -2411,7 +2418,7 @@
         publishLocalService(NotificationManagerInternal.class, mInternalService);
     }
 
-    private void registerDeviceConfigChange() {
+    void registerDeviceConfigChange() {
         mDeviceConfigChangedListener = properties -> {
             if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
                 return;
@@ -2440,9 +2447,19 @@
                     } else if ("false".equals(value)) {
                         mAssistants.disallowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
                     }
+                } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
+                    String value = properties.getString(name, null);
+                    if ("true".equals(value)) {
+                        mAllowFgsDismissal = true;
+                    } else if ("false".equals(value)) {
+                        mAllowFgsDismissal = false;
+                    }
                 }
             }
         };
+        mAllowFgsDismissal = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false);
         DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 new HandlerExecutor(mHandler),
@@ -2640,16 +2657,20 @@
     }
 
     private void sendAppBlockStateChangedBroadcast(String pkg, int uid, boolean blocked) {
-        try {
-            getContext().sendBroadcastAsUser(
-                    new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
-                            .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
-                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                            .setPackage(pkg),
-                    UserHandle.of(UserHandle.getUserId(uid)), null);
-        } catch (SecurityException e) {
-            Slog.w(TAG, "Can't notify app about app block change", e);
-        }
+        // From Android T, revoking the notification permission will cause the app to be killed.
+        // delay this broadcast so it doesn't race with that process death
+        mHandler.postDelayed(() -> {
+            try {
+                getContext().sendBroadcastAsUser(
+                        new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
+                                .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
+                                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                                .setPackage(pkg),
+                        UserHandle.of(UserHandle.getUserId(uid)), null);
+            } catch (SecurityException e) {
+                Slog.w(TAG, "Can't notify app about app block change", e);
+            }
+        }, 500);
     }
 
     @Override
@@ -2754,7 +2775,7 @@
         }
     }
 
-    private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
+    void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
             boolean fromApp, boolean fromListener) {
         Objects.requireNonNull(group);
         Objects.requireNonNull(pkg);
@@ -3077,6 +3098,13 @@
                 if (mPreferencesHelper.setValidMessageSent(
                         r.getSbn().getPackageName(), r.getUid())) {
                     handleSavePolicyFile();
+                } else if (r.getNotification().getBubbleMetadata() != null) {
+                    // If bubble metadata is present it is valid (if invalid it's removed
+                    // via BubbleExtractor).
+                    if (mPreferencesHelper.setValidBubbleSent(
+                            r.getSbn().getPackageName(), r.getUid())) {
+                        handleSavePolicyFile();
+                    }
                 }
             } else {
                 if (mPreferencesHelper.setInvalidMessageSent(
@@ -3343,8 +3371,7 @@
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
 
-            // Calling from user space, don't allow the canceling of actively
-            // running foreground services.
+            // Don't allow the app to cancel active FGS notifications
             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                     pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
                     REASON_APP_CANCEL_ALL, null);
@@ -3581,6 +3608,12 @@
         }
 
         @Override
+        public boolean hasSentValidBubble(String pkg, int uid) {
+            checkCallerIsSystem();
+            return mPreferencesHelper.hasSentValidBubble(pkg, uid);
+        }
+
+        @Override
         public void setNotificationDelegate(String callingPkg, String delegate) {
             checkCallerIsSameApp(callingPkg);
             final int callingUid = Binder.getCallingUid();
@@ -3663,9 +3696,19 @@
 
         private void createNotificationChannelsImpl(String pkg, int uid,
                 ParceledListSlice channelsList) {
+            createNotificationChannelsImpl(pkg, uid, channelsList,
+                    ActivityTaskManager.INVALID_TASK_ID);
+        }
+
+        private void createNotificationChannelsImpl(String pkg, int uid,
+                ParceledListSlice channelsList, int startingTaskId) {
             List<NotificationChannel> channels = channelsList.getList();
             final int channelsSize = channels.size();
+            ParceledListSlice<NotificationChannel> oldChannels =
+                    mPreferencesHelper.getNotificationChannels(pkg, uid, true);
+            final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty();
             boolean needsPolicyFileChange = false;
+            boolean hasRequestedNotificationPermission = false;
             for (int i = 0; i < channelsSize; i++) {
                 final NotificationChannel channel = channels.get(i);
                 Objects.requireNonNull(channel, "channel in list is null");
@@ -3679,6 +3722,19 @@
                             mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(),
                                     false),
                             NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
+                    boolean hasChannel = hadChannel || hasRequestedNotificationPermission;
+                    if (!hasChannel) {
+                        ParceledListSlice<NotificationChannel> currChannels =
+                                mPreferencesHelper.getNotificationChannels(pkg, uid, true);
+                        hasChannel = currChannels != null && !currChannels.getList().isEmpty();
+                    }
+                    if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
+                            && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
+                        hasRequestedNotificationPermission = true;
+                        mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg,
+                                UserHandle.getUserId(uid), startingTaskId,
+                                mPermissionPolicyInternal));
+                    }
                 }
             }
             if (needsPolicyFileChange) {
@@ -3687,10 +3743,29 @@
         }
 
         @Override
-        public void createNotificationChannels(String pkg,
-                ParceledListSlice channelsList) {
+        public void createNotificationChannels(String pkg, ParceledListSlice channelsList) {
             checkCallerIsSystemOrSameApp(pkg);
-            createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
+            int taskId = ActivityTaskManager.INVALID_TASK_ID;
+            try {
+                int uid = mPackageManager.getPackageUid(pkg, 0,
+                        UserHandle.getUserId(Binder.getCallingUid()));
+                List<ActivityManager.AppTask> tasks = mAtm.getAppTasks(pkg, uid);
+                for (int i = 0; i < tasks.size(); i++) {
+                    ActivityManager.RecentTaskInfo task = tasks.get(i).getTaskInfo();
+                    if (mPermissionPolicyInternal == null) {
+                        mPermissionPolicyInternal =
+                                LocalServices.getService(PermissionPolicyInternal.class);
+                    }
+                    if (mPermissionPolicyInternal != null
+                            && mPermissionPolicyInternal.canShowPermissionPromptForTask(task)) {
+                        taskId = task.taskId;
+                        break;
+                    }
+                }
+            } catch (RemoteException e) {
+                // Do nothing
+            }
+            createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList, taskId);
         }
 
         @Override
@@ -3811,7 +3886,8 @@
 
             final int callingUid = Binder.getCallingUid();
             NotificationChannelGroup groupToDelete =
-                    mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
+                    mPreferencesHelper.getNotificationChannelGroupWithChannels(
+                            pkg, callingUid, groupId, false);
             if (groupToDelete != null) {
                 // Preflight for allowability
                 final int userId = UserHandle.getUserId(callingUid);
@@ -3873,8 +3949,8 @@
         public int getNumNotificationChannelsForPackage(String pkg, int uid,
                 boolean includeDeleted) {
             enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
-            return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted)
-                    .getList().size();
+            return NotificationManagerService.this
+                    .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted);
         }
 
         @Override
@@ -4414,8 +4490,9 @@
         @GuardedBy("mNotificationLock")
         private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
                 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
-            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
-                    FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
+            int mustNotHaveFlags = FLAG_ONGOING_EVENT;
+            cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
+                    mustNotHaveFlags,
                     true,
                     userId, REASON_LISTENER_CANCEL, info);
         }
@@ -6132,15 +6209,31 @@
                 return;
             }
             StatusBarNotification sbn = r.getSbn();
-            // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
+            // NoMan adds flags FLAG_ONGOING_EVENT when it sees
             // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
             // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
             // initially *and* force remove FLAG_FOREGROUND_SERVICE.
-            sbn.getNotification().flags =
-                    (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
+            sbn.getNotification().flags = (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
+        }
+
+        @Override
+        public int getNumNotificationChannelsForPackage(String pkg, int uid,
+                boolean includeDeleted) {
+            return NotificationManagerService.this
+                    .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted);
+        }
+
+        @Override
+        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
+            return areNotificationsEnabledForPackageInt(pkg, uid);
         }
     };
 
+    int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
+        return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList()
+                .size();
+    }
+
     void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
             String tag, int id, int userId) {
         userId = ActivityManager.handleIncomingUser(callingPid,
@@ -6598,31 +6691,33 @@
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
             final int callingUid = Binder.getCallingUid();
-            if (mNotificationsByKey.get(r.getSbn().getKey()) == null
-                    && isCallerInstantApp(callingUid, userId)) {
-                // Ephemeral apps have some special constraints for notifications.
-                // They are not allowed to create new notifications however they are allowed to
-                // update notifications created by the system (e.g. a foreground service
-                // notification).
-                throw new SecurityException("Instant app " + pkg
-                        + " cannot create notifications");
-            }
+            synchronized (mNotificationLock) {
+                if (mNotificationsByKey.get(r.getSbn().getKey()) == null
+                        && isCallerInstantApp(callingUid, userId)) {
+                    // Ephemeral apps have some special constraints for notifications.
+                    // They are not allowed to create new notifications however they are allowed to
+                    // update notifications created by the system (e.g. a foreground service
+                    // notification).
+                    throw new SecurityException("Instant app " + pkg
+                            + " cannot create notifications");
+                }
 
-            // rate limit updates that aren't completed progress notifications
-            if (mNotificationsByKey.get(r.getSbn().getKey()) != null
-                    && !r.getNotification().hasCompletedProgress()
-                    && !isAutogroup) {
+                // rate limit updates that aren't completed progress notifications
+                if (mNotificationsByKey.get(r.getSbn().getKey()) != null
+                        && !r.getNotification().hasCompletedProgress()
+                        && !isAutogroup) {
 
-                final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
-                if (appEnqueueRate > mMaxPackageEnqueueRate) {
-                    mUsageStats.registerOverRateQuota(pkg);
-                    final long now = SystemClock.elapsedRealtime();
-                    if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
-                        Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
-                                + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
-                        mLastOverRateLogTime = now;
+                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
+                        mUsageStats.registerOverRateQuota(pkg);
+                        final long now = SystemClock.elapsedRealtime();
+                        if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+                            Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+                                    + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
+                            mLastOverRateLogTime = now;
+                        }
+                        return false;
                     }
-                    return false;
                 }
             }
 
@@ -6910,7 +7005,6 @@
                                 r.getKey(), true /* notifSuppressed */, isBubbleSuppressed);
                         return;
                     }
-
                     if ((r.getNotification().flags & mMustHaveFlags) != mMustHaveFlags) {
                         return;
                     }
@@ -6918,19 +7012,25 @@
                         return;
                     }
 
-                    // Bubbled children get to stick around if the summary was manually cancelled
-                    // (user removed) from systemui.
-                    FlagChecker childrenFlagChecker = null;
-                    if (mReason == REASON_CANCEL
-                            || mReason == REASON_CLICK
-                            || mReason == REASON_CANCEL_ALL) {
-                        childrenFlagChecker = (flags) -> {
-                            if ((flags & FLAG_BUBBLE) != 0) {
+                    FlagChecker childrenFlagChecker = (flags) -> {
+                            if (mReason == REASON_CANCEL
+                                    || mReason == REASON_CLICK
+                                    || mReason == REASON_CANCEL_ALL) {
+                                // Bubbled children get to stick around if the summary was manually
+                                // cancelled (user removed) from systemui.
+                                if ((flags & FLAG_BUBBLE) != 0) {
+                                    return false;
+                                }
+                            } else if (mReason == REASON_APP_CANCEL) {
+                                if ((flags & FLAG_FOREGROUND_SERVICE) != 0) {
+                                    return false;
+                                }
+                            }
+                            if ((flags & mMustNotHaveFlags) != 0) {
                                 return false;
                             }
                             return true;
                         };
-                    }
 
                     // Cancel the notification.
                     boolean wasPosted = removeFromNotificationListsLocked(r);
@@ -6957,6 +7057,44 @@
         }
     }
 
+    protected static class ShowNotificationPermissionPromptRunnable implements Runnable {
+        private final String mPkgName;
+        private final int mUserId;
+        private final int mTaskId;
+        private final PermissionPolicyInternal mPpi;
+
+        ShowNotificationPermissionPromptRunnable(String pkg, int user, int task,
+                PermissionPolicyInternal pPi) {
+            mPkgName = pkg;
+            mUserId = user;
+            mTaskId = task;
+            mPpi = pPi;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof ShowNotificationPermissionPromptRunnable)) {
+                return false;
+            }
+
+            ShowNotificationPermissionPromptRunnable other =
+                    (ShowNotificationPermissionPromptRunnable) o;
+
+            return Objects.equals(mPkgName, other.mPkgName) && mUserId == other.mUserId
+                    && mTaskId == other.mTaskId;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPkgName, mUserId, mTaskId);
+        }
+
+        @Override
+        public void run() {
+            mPpi.showNotificationPromptIfNeeded(mPkgName, mUserId, mTaskId);
+        }
+    }
+
     protected class EnqueueNotificationRunnable implements Runnable {
         private final NotificationRecord r;
         private final int userId;
@@ -7141,8 +7279,10 @@
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
                     if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
-                        notification.flags |= FLAG_ONGOING_EVENT
-                                | FLAG_NO_CLEAR;
+                        notification.flags |= FLAG_NO_CLEAR;
+                        if (!mAllowFgsDismissal) {
+                            notification.flags |= FLAG_ONGOING_EVENT;
+                        }
                     }
 
                     mRankingHelper.extractSignals(r);
@@ -7417,13 +7557,20 @@
             mSummaryByGroupKey.put(group, r);
         }
 
+        FlagChecker childrenFlagChecker = (flags) -> {
+            if ((flags & FLAG_FOREGROUND_SERVICE) != 0) {
+                return false;
+            }
+            return true;
+        };
+
         // Clear out group children of the old notification if the update
         // causes the group summary to go away. This happens when the old
         // notification was a summary and the new one isn't, or when the old
         // notification was a summary and its group key changed.
         if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
             cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
-                    null, REASON_APP_CANCEL);
+                    childrenFlagChecker, REASON_APP_CANCEL);
         }
     }
 
@@ -9042,7 +9189,6 @@
             final StatusBarNotification childSbn = childR.getSbn();
             if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
                     childR.getGroupKey().equals(parentNotification.getGroupKey())
-                    && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
                     && (flagChecker == null || flagChecker.apply(childR.getFlags()))
                     && (!childR.getChannel().isImportantConversation()
                             || reason != REASON_CANCEL)) {
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 24008d0..0cbdbc18 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.permission.PermissionManager.PERMISSION_GRANTED;
@@ -159,13 +160,21 @@
     }
 
     /**
+     * @see setNotificationPermission(String, int, boolean, boolean, boolean)
+     */
+    public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
+            boolean userSet) {
+        setNotificationPermission(packageName, userId, grant, userSet, false);
+    }
+
+    /**
      * Grants or revokes the notification permission for a given package/user. UserSet should
      * only be true if this method is being called to migrate existing user choice, because it
      * can prevent the user from seeing the in app permission dialog. Must not be called
      * with a lock held.
      */
     public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
-            boolean userSet) {
+            boolean userSet, boolean reviewRequired) {
         assertFlag();
         final long callingId = Binder.clearCallingIdentity();
         try {
@@ -177,7 +186,12 @@
             }
             if (userSet) {
                 mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION,
-                        FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, userId);
+                        FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED,
+                        FLAG_PERMISSION_USER_SET, true, userId);
+            } else if (reviewRequired) {
+                mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION,
+                        FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true,
+                        userId);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Could not reach system server", e);
@@ -186,15 +200,17 @@
         }
     }
 
+    /**
+     * Set the notification permission state upon phone version upgrade from S- to T+, or upon
+     * restoring a pre-T backup on a T+ device
+     */
     public void setNotificationPermission(PackagePermission pkgPerm) {
         assertFlag();
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            setNotificationPermission(
-                    pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet);
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
+        if (pkgPerm == null || pkgPerm.packageName == null) {
+            return;
         }
+        setNotificationPermission(pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted,
+                pkgPerm.userSet, !pkgPerm.userSet);
     }
 
     public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 258ae8c..05f000c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -101,6 +101,8 @@
 
     @VisibleForTesting
     static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
+    @VisibleForTesting
+    static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
 
     private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
     private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
@@ -254,6 +256,7 @@
                                 }
                             }
                             boolean skipWarningLogged = false;
+                            boolean skipGroupWarningLogged = false;
                             boolean hasSAWPermission = false;
                             if (upgradeForBubbles && uid != UNKNOWN_UID) {
                                 hasSAWPermission = mAppOps.noteOpNoThrow(
@@ -303,6 +306,14 @@
                                 String tagName = parser.getName();
                                 // Channel groups
                                 if (TAG_GROUP.equals(tagName)) {
+                                    if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) {
+                                        if (!skipGroupWarningLogged) {
+                                            Slog.w(TAG, "Skipping further groups for " + r.pkg
+                                                    + "; app has too many");
+                                            skipGroupWarningLogged = true;
+                                        }
+                                        continue;
+                                    }
                                     String id = parser.getAttributeValue(null, ATT_ID);
                                     CharSequence groupName = parser.getAttributeValue(null,
                                             ATT_NAME);
@@ -808,6 +819,23 @@
         }
     }
 
+    /** Sets whether this package has sent a notification with valid bubble metadata. */
+    public boolean setValidBubbleSent(String packageName, int uid) {
+        synchronized (mPackagePreferences) {
+            PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+            boolean valueChanged = !r.hasSentValidBubble;
+            r.hasSentValidBubble = true;
+            return valueChanged;
+        }
+    }
+
+    boolean hasSentValidBubble(String packageName, int uid) {
+        synchronized (mPackagePreferences) {
+            PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+            return r.hasSentValidBubble;
+        }
+    }
+
     @Override
     public boolean isGroupBlocked(String packageName, int uid, String groupId) {
         if (groupId == null) {
@@ -848,6 +876,12 @@
             if (r == null) {
                 throw new IllegalArgumentException("Invalid package");
             }
+            if (fromTargetApp) {
+                group.setBlocked(false);
+                if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) {
+                    throw new IllegalStateException("Limit exceed; cannot create more groups");
+                }
+            }
             final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
             if (oldGroup != null) {
                 group.setChannels(oldGroup.getChannels());
@@ -2813,8 +2847,9 @@
 
         boolean hasSentInvalidMessage = false;
         boolean hasSentValidMessage = false;
-        // notE: only valid while hasSentMessage is false and hasSentInvalidMessage is true
+        // note: only valid while hasSentMessage is false and hasSentInvalidMessage is true
         boolean userDemotedMsgApp = false;
+        boolean hasSentValidBubble = false;
 
         Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 92cdce7..737353d 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -261,7 +261,8 @@
             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
             filter.addAction(ACTION_EVALUATE);
             filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
-            registerReceiver(mReceiver, filter);
+            registerReceiver(mReceiver, filter,
+                    Context.RECEIVER_EXPORTED_UNAUDITED);
         } else {
             unregisterReceiver(mReceiver);
         }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 4500bbc..7f265df 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -116,7 +116,8 @@
         mContext = context;
         IntentFilter filter = new IntentFilter(REPOST_ACTION);
         filter.addDataScheme(REPOST_SCHEME);
-        mContext.registerReceiver(mBroadcastReceiver, filter);
+        mContext.registerReceiver(mBroadcastReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mAm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mCallback = callback;
         mUserProfiles = userProfiles;
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index be5f219..54dd113 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.media.AudioAttributes;
 import android.os.Process;
 import android.os.VibrationAttributes;
@@ -39,18 +40,16 @@
 
     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
     private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
-    private static final int CHIRP_LEVEL_DURATION_MILLIS = 100;
-    private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100;
-    private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50;
 
     private final Vibrator mVibrator;
     private final long[] mDefaultPattern;
     private final long[] mFallbackPattern;
+    @Nullable private final float[] mDefaultPwlePattern;
+    @Nullable private final float[] mFallbackPwlePattern;
 
     public VibratorHelper(Context context) {
         mVibrator = context.getSystemService(Vibrator.class);
-        mDefaultPattern = getLongArray(
-                context.getResources(),
+        mDefaultPattern = getLongArray(context.getResources(),
                 com.android.internal.R.array.config_defaultNotificationVibePattern,
                 VIBRATE_PATTERN_MAXLEN,
                 DEFAULT_VIBRATE_PATTERN);
@@ -58,6 +57,10 @@
                 R.array.config_notificationFallbackVibePattern,
                 VIBRATE_PATTERN_MAXLEN,
                 DEFAULT_VIBRATE_PATTERN);
+        mDefaultPwlePattern = getFloatArray(context.getResources(),
+                com.android.internal.R.array.config_defaultNotificationVibeWaveform);
+        mFallbackPwlePattern = getFloatArray(context.getResources(),
+                com.android.internal.R.array.config_notificationFallbackVibeWaveform);
     }
 
     /**
@@ -83,6 +86,52 @@
     }
 
     /**
+     * Safely create a {@link VibrationEffect} from given waveform description.
+     *
+     * <p>The waveform is described by a sequence of values for target amplitude, frequency and
+     * duration, that are forwarded to
+     * {@link VibrationEffect.WaveformBuilder#addRamp(float, float, int)}.
+     *
+     * <p>This method returns {@code null} if the pattern is also {@code null} or invalid.
+     *
+     * @param values The list of values describing the waveform as a sequence of target amplitude,
+     *               frequency and duration.
+     * @param insistent {@code true} if the vibration should loop until it is cancelled.
+     */
+    @Nullable
+    public static VibrationEffect createPwleWaveformVibration(@Nullable float[] values,
+            boolean insistent) {
+        try {
+            if (values == null) {
+                return null;
+            }
+
+            int length = values.length;
+            // The waveform is described by triples (amplitude, frequency, duration)
+            if ((length == 0) || (length % 3 != 0)) {
+                return null;
+            }
+
+            VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform();
+            for (int i = 0; i < length; i += 3) {
+                waveformBuilder.addRamp(
+                        /* amplitude= */ values[i],
+                        /* frequencyHz= */ values[i + 1],
+                        /* duration= */ (int) values[i + 2]);
+            }
+
+            if (insistent) {
+                return waveformBuilder.build(/* repeat= */ 0);
+            }
+            return waveformBuilder.build();
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Error creating vibration PWLE waveform with pattern: "
+                    + Arrays.toString(values));
+        }
+        return null;
+    }
+
+    /**
      * Vibrate the device with given {@code effect}.
      *
      * <p>We need to vibrate as "android" so we can breakthrough DND.
@@ -106,7 +155,10 @@
      */
     public VibrationEffect createFallbackVibration(boolean insistent) {
         if (mVibrator.hasFrequencyControl()) {
-            return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent);
+            VibrationEffect effect = createPwleWaveformVibration(mFallbackPwlePattern, insistent);
+            if (effect != null) {
+                return effect;
+            }
         }
         return createWaveformVibration(mFallbackPattern, insistent);
     }
@@ -118,29 +170,29 @@
      */
     public VibrationEffect createDefaultVibration(boolean insistent) {
         if (mVibrator.hasFrequencyControl()) {
-            return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent);
+            VibrationEffect effect = createPwleWaveformVibration(mDefaultPwlePattern, insistent);
+            if (effect != null) {
+                return effect;
+            }
         }
         return createWaveformVibration(mDefaultPattern, insistent);
     }
 
-    private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
-        VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
-                .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
-                .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
-                .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
-                .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
-
-        if (insistent) {
-            return waveformBuilder
-                    .addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS)
-                    .build(/* repeat= */ 0);
+    @Nullable
+    private static float[] getFloatArray(Resources resources, int resId) {
+        TypedArray array = resources.obtainTypedArray(resId);
+        try {
+            float[] values = new float[array.length()];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = array.getFloat(i, Float.NaN);
+                if (Float.isNaN(values[i])) {
+                    return null;
+                }
+            }
+            return values;
+        } finally {
+            array.recycle();
         }
-
-        VibrationEffect singleBeat = waveformBuilder.build();
-        return VibrationEffect.startComposition()
-                .addEffect(singleBeat)
-                .addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS)
-                .compose();
     }
 
     private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index c9e564a..8e944b7 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -37,13 +37,14 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * To prevent idmap2d from continuously running, the idmap daemon will terminate after 10
- * seconds without a transaction.
+ * To prevent idmap2d from continuously running, the idmap daemon will terminate after 10 seconds
+ * without a transaction.
  **/
 class IdmapDaemon {
     // The amount of time in milliseconds to wait after a transaction to the idmap service is made
@@ -67,11 +68,14 @@
      * to the service is open.
      **/
     private class Connection implements AutoCloseable {
+        @Nullable
+        private final IIdmap2 mIdmap2;
         private boolean mOpened = true;
 
-        private Connection() {
+        private Connection(IIdmap2 idmap2) {
             synchronized (mIdmapToken) {
                 mOpenedCount.incrementAndGet();
+                mIdmap2 = idmap2;
             }
         }
 
@@ -102,6 +106,11 @@
                 }, mIdmapToken, SERVICE_TIMEOUT_MS);
             }
         }
+
+        @Nullable
+        public IIdmap2 getIdmap2() {
+            return mIdmap2;
+        }
     }
 
     static IdmapDaemon getInstance() {
@@ -115,14 +124,29 @@
             @Nullable String overlayName, int policies, boolean enforce, int userId)
             throws TimeoutException, RemoteException {
         try (Connection c = connect()) {
-            return mService.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
+            final IIdmap2 idmap2 = c.getIdmap2();
+            if (idmap2 == null) {
+                Slog.w(TAG, "idmap2d service is not ready for createIdmap(\"" + targetPath
+                        + "\", \"" + overlayPath + "\", \"" + overlayName + "\", " + policies + ", "
+                        + enforce + ", " + userId + ")");
+                return null;
+            }
+
+            return idmap2.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
                     policies, enforce, userId);
         }
     }
 
     boolean removeIdmap(String overlayPath, int userId) throws TimeoutException, RemoteException {
         try (Connection c = connect()) {
-            return mService.removeIdmap(overlayPath, userId);
+            final IIdmap2 idmap2 = c.getIdmap2();
+            if (idmap2 == null) {
+                Slog.w(TAG, "idmap2d service is not ready for removeIdmap(\"" + overlayPath
+                        + "\", " + userId + ")");
+                return false;
+            }
+
+            return idmap2.removeIdmap(overlayPath, userId);
         }
     }
 
@@ -130,14 +154,29 @@
             @Nullable String overlayName, int policies, boolean enforce, int userId)
             throws Exception {
         try (Connection c = connect()) {
-            return mService.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
+            final IIdmap2 idmap2 = c.getIdmap2();
+            if (idmap2 == null) {
+                Slog.w(TAG, "idmap2d service is not ready for verifyIdmap(\"" + targetPath
+                        + "\", \"" + overlayPath + "\", \"" + overlayName + "\", " + policies + ", "
+                        + enforce + ", " + userId + ")");
+                return false;
+            }
+
+            return idmap2.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
                     policies, enforce, userId);
         }
     }
 
     boolean idmapExists(String overlayPath, int userId) {
         try (Connection c = connect()) {
-            return new File(mService.getIdmapPath(overlayPath, userId)).isFile();
+            final IIdmap2 idmap2 = c.getIdmap2();
+            if (idmap2 == null) {
+                Slog.w(TAG, "idmap2d service is not ready for idmapExists(\"" + overlayPath
+                        + "\", " + userId + ")");
+                return false;
+            }
+
+            return new File(idmap2.getIdmapPath(overlayPath, userId)).isFile();
         } catch (Exception e) {
             Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e);
             return false;
@@ -146,7 +185,13 @@
 
     FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
         try (Connection c = connect()) {
-            return mService.createFabricatedOverlay(overlay);
+            final IIdmap2 idmap2 = c.getIdmap2();
+            if (idmap2 == null) {
+                Slog.w(TAG, "idmap2d service is not ready for createFabricatedOverlay()");
+                return null;
+            }
+
+            return idmap2.createFabricatedOverlay(overlay);
         } catch (Exception e) {
             Slog.wtf(TAG, "failed to fabricate overlay " + overlay, e);
             return null;
@@ -155,7 +200,14 @@
 
     boolean deleteFabricatedOverlay(@NonNull String path) {
         try (Connection c = connect()) {
-            return mService.deleteFabricatedOverlay(path);
+            final IIdmap2 idmap2 = c.getIdmap2();
+            if (idmap2 == null) {
+                Slog.w(TAG, "idmap2d service is not ready for deleteFabricatedOverlay(\"" + path
+                        + "\")");
+                return false;
+            }
+
+            return idmap2.deleteFabricatedOverlay(path);
         } catch (Exception e) {
             Slog.wtf(TAG, "failed to delete fabricated overlay '" + path + "'", e);
             return false;
@@ -164,10 +216,18 @@
 
     synchronized List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
         final ArrayList<FabricatedOverlayInfo> allInfos = new ArrayList<>();
-        try (Connection c = connect()) {
-            mService.acquireFabricatedOverlayIterator();
+        Connection c = null;
+        try {
+            c = connect();
+            final IIdmap2 service = c.getIdmap2();
+            if (service == null) {
+                Slog.w(TAG, "idmap2d service is not ready for getFabricatedOverlayInfos()");
+                return Collections.emptyList();
+            }
+
+            service.acquireFabricatedOverlayIterator();
             List<FabricatedOverlayInfo> infos;
-            while (!(infos = mService.nextFabricatedOverlayInfos()).isEmpty()) {
+            while (!(infos = service.nextFabricatedOverlayInfos()).isEmpty()) {
                 allInfos.addAll(infos);
             }
             return allInfos;
@@ -175,17 +235,26 @@
             Slog.wtf(TAG, "failed to get all fabricated overlays", e);
         } finally {
             try {
-                mService.releaseFabricatedOverlayIterator();
+                if (c.getIdmap2() != null) {
+                    c.getIdmap2().releaseFabricatedOverlayIterator();
+                }
             } catch (RemoteException e) {
                 // ignore
             }
+            c.close();
         }
         return allInfos;
     }
 
     String dumpIdmap(@NonNull String overlayPath) {
         try (Connection c = connect()) {
-            String dump = mService.dumpIdmap(overlayPath);
+            final IIdmap2 service = c.getIdmap2();
+            if (service == null) {
+                final String dumpText = "idmap2d service is not ready for dumpIdmap()";
+                Slog.w(TAG, dumpText);
+                return dumpText;
+            }
+            String dump = service.dumpIdmap(overlayPath);
             return TextUtils.nullIfEmpty(dump);
         } catch (Exception e) {
             Slog.wtf(TAG, "failed to dump idmap", e);
@@ -193,8 +262,16 @@
         }
     }
 
+    @Nullable
     private IBinder getIdmapService() throws TimeoutException, RemoteException {
-        SystemService.start(IDMAP_DAEMON);
+        try {
+            SystemService.start(IDMAP_DAEMON);
+        } catch (RuntimeException e) {
+            if (e.getMessage().contains("failed to set system property")) {
+                Slog.w(TAG, "Failed to enable idmap2 daemon", e);
+                return null;
+            }
+        }
 
         final long endMillis = SystemClock.elapsedRealtime() + SERVICE_CONNECT_TIMEOUT_MS;
         while (SystemClock.elapsedRealtime() <= endMillis) {
@@ -226,17 +303,23 @@
         }
     }
 
+    @NonNull
     private Connection connect() throws TimeoutException, RemoteException {
         synchronized (mIdmapToken) {
             FgThread.getHandler().removeCallbacksAndMessages(mIdmapToken);
             if (mService != null) {
                 // Not enough time has passed to stop the idmap service. Reuse the existing
                 // interface.
-                return new Connection();
+                return new Connection(mService);
             }
 
-            mService = IIdmap2.Stub.asInterface(getIdmapService());
-            return new Connection();
+            IBinder binder = getIdmapService();
+            if (binder == null) {
+                return new Connection(null);
+            }
+
+            mService = IIdmap2.Stub.asInterface(binder);
+            return new Connection(mService);
         }
     }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 047a701..38781fa 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -33,7 +33,7 @@
 import android.content.om.OverlayIdentifier;
 import android.content.om.OverlayInfo;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.os.FabricatedOverlayInfo;
 import android.os.FabricatedOverlayInternal;
 import android.text.TextUtils;
@@ -492,7 +492,7 @@
     Set<PackageAndUser> registerFabricatedOverlay(
             @NonNull final FabricatedOverlayInternal overlay)
             throws OperationFailedException {
-        if (ParsingPackageUtils.validateName(overlay.overlayName,
+        if (FrameworkParsingPackageUtils.validateName(overlay.overlayName,
                 false /* requireSeparator */, true /* requireFilename */) != null) {
             throw new OperationFailedException(
                     "overlay name can only consist of alphanumeric characters, '_', and '.'");
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 0556748..2e9ad50 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,9 +32,9 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Binder;
@@ -45,6 +45,7 @@
 import android.sysprop.ApexProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.PrintWriterPrinter;
 import android.util.Singleton;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -799,7 +800,7 @@
                 throw new RuntimeException(re);
             } catch (Exception e) {
                 throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                        PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
                         "apexd verification failed : " + e.getMessage());
             }
         }
@@ -826,7 +827,7 @@
                 throw new RuntimeException(re);
             } catch (Exception e) {
                 throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                        PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
                         "Failed to mark apexd session as ready : " + e.getMessage());
             }
         }
@@ -1164,6 +1165,10 @@
                 ipw.println("Path: " + pi.applicationInfo.sourceDir);
                 ipw.println("IsActive: " + isActive(pi));
                 ipw.println("IsFactory: " + isFactory(pi));
+                ipw.println("ApplicationInfo: ");
+                ipw.increaseIndent();
+                pi.applicationInfo.dump(new PrintWriterPrinter(ipw), "");
+                ipw.decreaseIndent();
                 ipw.decreaseIndent();
             }
             ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index bedb8b9..4b999e9 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.PackageManager;
-import android.content.pm.SELinuxUtil;
 import android.content.pm.UserInfo;
 import android.os.CreateAppDataArgs;
 import android.os.Environment;
@@ -35,6 +34,9 @@
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
+import android.security.AndroidKeyStoreMaintenance;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
@@ -46,6 +48,7 @@
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.SELinuxUtil;
 
 import dalvik.system.VMRuntime;
 
@@ -101,6 +104,12 @@
             mPm.mSettings.writeKernelMappingLPr(ps);
         }
 
+        // TODO(b/211761016): should we still create the profile dirs?
+        if (!shouldHaveAppStorage(pkg)) {
+            Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName());
+            return;
+        }
+
         Installer.Batch batch = new Installer.Batch();
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
         StorageManagerInternal smInternal = mInjector.getLocalService(
@@ -150,8 +159,7 @@
      * <ul>
      * <li>If previousAppId < 0, app data will be migrated to the new app ID
      * <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated
-     * <li>If previousAppId > 0, it will migrate all data owned by previousAppId
-     *     to the new app ID
+     * <li>If previousAppId > 0, app data owned by previousAppId will be migrated to the new app ID
      * </ul>
      */
     private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
@@ -161,6 +169,10 @@
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return CompletableFuture.completedFuture(null);
         }
+        if (!shouldHaveAppStorage(pkg)) {
+            Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName());
+            return CompletableFuture.completedFuture(null);
+        }
         return prepareAppDataLeaf(batch, pkg, previousAppId, userId, flags);
     }
 
@@ -381,7 +393,7 @@
             for (File file : files) {
                 final String packageName = file.getName();
                 try {
-                    assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+                    assertPackageStorageValid(volumeUuid, packageName, userId);
                 } catch (PackageManagerException e) {
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
                     try {
@@ -398,7 +410,7 @@
             for (File file : files) {
                 final String packageName = file.getName();
                 try {
-                    assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+                    assertPackageStorageValid(volumeUuid, packageName, userId);
                 } catch (PackageManagerException e) {
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
                     try {
@@ -446,7 +458,11 @@
         return result;
     }
 
-    private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
+    /**
+     * Asserts that storage path is valid by checking that {@code packageName} is present,
+     * installed for the given {@code userId} and can have app data.
+     */
+    private void assertPackageStorageValid(String volumeUuid, String packageName, int userId)
             throws PackageManagerException {
         synchronized (mPm.mLock) {
             // Normalize package name to handle renamed packages
@@ -462,6 +478,9 @@
             } else if (!ps.getInstalled(userId)) {
                 throw new PackageManagerException(
                         "Package " + packageName + " not installed for user " + userId);
+            } else if (ps.getPkg() != null && !shouldHaveAppStorage(ps.getPkg())) {
+                throw new PackageManagerException(
+                        "Package " + packageName + " shouldn't have storage");
             }
         }
     }
@@ -528,6 +547,22 @@
         return prepareAppDataFuture;
     }
 
+    public void migrateKeyStoreData(int previousAppId, int appId) {
+        for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) {
+            int srcUid = UserHandle.getUid(userId, previousAppId);
+            int destUid = UserHandle.getUid(userId, appId);
+            final KeyDescriptor[] keys = AndroidKeyStoreMaintenance.listEntries(Domain.APP, srcUid);
+            if (keys == null) continue;
+            for (final KeyDescriptor key : keys) {
+                KeyDescriptor dest = new KeyDescriptor();
+                dest.domain = Domain.APP;
+                dest.nspace = destUid;
+                dest.alias = key.alias;
+                AndroidKeyStoreMaintenance.migrateKeyNamespace(key, dest);
+            }
+        }
+    }
+
     void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
         if (pkg == null) {
             return;
@@ -603,4 +638,27 @@
             Slog.w(TAG, String.valueOf(e));
         }
     }
+
+    /**
+     * Returns {@code true} if app's internal storage should be created for this {@code pkg}.
+     */
+    private boolean shouldHaveAppStorage(AndroidPackage pkg) {
+        PackageManager.Property noAppDataProp =
+                pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+        return noAppDataProp == null || !noAppDataProp.getBoolean();
+    }
+
+    /**
+     * Remove entries from the keystore daemon. Will only remove if the {@code appId} is valid.
+     */
+    public void clearKeystoreData(int userId, int appId) {
+        if (appId < 0) {
+            return;
+        }
+
+        for (int realUserId : mPm.resolveUserIds(userId)) {
+            AndroidKeyStoreMaintenance.clearNamespace(
+                    Domain.APP, UserHandle.getUid(realUserId, appId));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 6f54625..b916de3 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -33,11 +33,11 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
 import android.os.Binder;
 import android.os.Process;
 import android.os.Trace;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 27db2f9..31df0a5 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -37,6 +37,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -146,6 +147,11 @@
     @GuardedBy("mLock")
     @Status private int mLastExecutionStatus = STATUS_OK;
 
+    @GuardedBy("mLock")
+    private long mLastExecutionStartTimeMs;
+    @GuardedBy("mLock")
+    private long mLastExecutionDurationMs;
+
     // Keeps packages cancelled from PDO for last session. This is for debugging.
     @GuardedBy("mLock")
     private final ArraySet<String> mLastCancelledPackages = new ArraySet<String>();
@@ -218,9 +224,15 @@
             writer.print("mDexOptCancellingThread:");
             writer.println(mDexOptCancellingThread);
             writer.print("mFinishedPostBootUpdate:");
-            writer.print(mFinishedPostBootUpdate);
-            writer.print(",mLastExecutionStatus:");
+            writer.println(mFinishedPostBootUpdate);
+            writer.print("mLastExecutionStatus:");
             writer.println(mLastExecutionStatus);
+            writer.print("mLastExecutionStartTimeMs:");
+            writer.println(mLastExecutionStartTimeMs);
+            writer.print("mLastExecutionDurationMs:");
+            writer.println(mLastExecutionDurationMs);
+            writer.print("now:");
+            writer.println(SystemClock.elapsedRealtime());
             writer.print("mLastCancelledPackages:");
             writer.println(String.join(",", mLastCancelledPackages));
             writer.print("mFailedPackageNamesPrimary:");
@@ -514,12 +526,17 @@
     /** Returns true if completed */
     private boolean runIdleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
             boolean isPostBootUpdate) {
+        synchronized (mLock) {
+            mLastExecutionStartTimeMs = SystemClock.elapsedRealtime();
+            mLastExecutionDurationMs = -1;
+        }
         long lowStorageThreshold = getLowStorageThreshold();
         int status = idleOptimizePackages(pm, pkgs, lowStorageThreshold,
                 isPostBootUpdate);
         logStatus(status);
         synchronized (mLock) {
             mLastExecutionStatus = status;
+            mLastExecutionDurationMs = SystemClock.elapsedRealtime() - mLastExecutionStartTimeMs;
         }
 
         return status == STATUS_OK;
@@ -879,10 +896,10 @@
         synchronized (mLock) {
             if (!mFinishedPostBootUpdate) {
                 mFinishedPostBootUpdate = true;
-                JobScheduler js = mInjector.getJobScheduler();
-                js.cancel(JOB_POST_BOOT_UPDATE);
             }
         }
+        // Safe to do this outside lock.
+        mInjector.getJobScheduler().cancel(JOB_POST_BOOT_UPDATE);
     }
 
     private void notifyPinService(ArraySet<String> updatedPackages) {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 6ec3405..cd4244b 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -36,14 +36,14 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedProviderImpl;
-import android.content.pm.parsing.component.ParsedService;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.component.ParsedService;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index dd66130..fcf4a02 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -49,8 +49,6 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -309,10 +307,6 @@
     @Nullable
     String getRenamedPackage(@NonNull String packageName);
 
-    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
-    @NonNull
-    WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries();
-
     /**
      * @return set of packages to notify
      */
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2f4f271..69c475a 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.INSTALL_PACKAGES;
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
+import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_DEFAULT;
 import static android.content.Intent.CATEGORY_HOME;
@@ -92,14 +93,6 @@
 import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VersionedPackage;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.pkg.PackageUserStateUtils;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
@@ -142,6 +135,14 @@
 import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserState;
 import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationUtils;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -350,7 +351,6 @@
     private final CompilerStats mCompilerStats;
     private final BackgroundDexOptService mBackgroundDexOptService;
     private final PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
-    private final ProtectedPackages mProtectedPackages;
 
     // PackageManagerService attributes that are primitives are referenced through the
     // pms object directly.  Primitives are the only attributes so referenced.
@@ -402,7 +402,6 @@
         mCompilerStats = args.service.mCompilerStats;
         mBackgroundDexOptService = args.service.mBackgroundDexOptService;
         mExternalSourcesPolicy = args.service.mExternalSourcesPolicy;
-        mProtectedPackages = args.service.mProtectedPackages;
 
         // Used to reference PMS attributes that are primitives and which are not
         // updated under control of the PMS lock.
@@ -3490,9 +3489,8 @@
         return mSettings.getRenamedPackageLPr(packageName);
     }
 
-    @NonNull
-    @Override
-    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+    private WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+            getSharedLibraries() {
         return mSharedLibraries.getAll();
     }
 
@@ -4620,8 +4618,24 @@
             }
         }
         if (!checkedGrants) {
-            enforceCrossUserPermission(callingUid, userId, false, false, "resolveContentProvider");
+            boolean enforceCrossUser = true;
+
+            if (isAuthorityRedirectedForCloneProfile(name)) {
+                final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+
+                UserInfo userInfo = umInternal.getUserInfo(UserHandle.getUserId(callingUid));
+                if (userInfo != null && userInfo.isCloneProfile()
+                        && userInfo.profileGroupId == userId) {
+                    enforceCrossUser = false;
+                }
+            }
+
+            if (enforceCrossUser) {
+                enforceCrossUserPermission(callingUid, userId, false, false,
+                        "resolveContentProvider");
+            }
         }
+
         if (providerInfo == null) {
             return null;
         }
@@ -4788,7 +4802,7 @@
     @Override
     public List<PackageStateInternal> findSharedNonSystemLibraries(
             @NonNull PackageStateInternal pkgSetting) {
-        List<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+        List<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
         if (!deps.isEmpty()) {
             List<PackageStateInternal> retValue = new ArrayList<>();
             for (SharedLibraryInfo info : deps) {
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index bd730e9..529aca3 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -46,8 +46,6 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -269,14 +267,6 @@
 
     @NonNull
     @Override
-    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
-        synchronized (mLock) {
-            return super.getSharedLibraries();
-        }
-    }
-
-    @NonNull
-    @Override
     public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
         synchronized (mLock) {
             return super.getNotifyPackagesForReplacedReceived(packages);
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index e6ff836..52309ce 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -47,8 +47,6 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -703,14 +701,6 @@
 
     @NonNull
     @Override
-    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
-        try (ThreadComputer current = snapshot()) {
-            return current.mComputer.getSharedLibraries();
-        }
-    }
-
-    @NonNull
-    @Override
     public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getNotifyPackagesForReplacedReceived(packages);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 62db886..b307984 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.MANAGE_APP_OPS_MODES;
+import static android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES;
 import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
 import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED;
@@ -154,17 +155,15 @@
             // must have the required permission and the users must be in the same profile group
             // in order to launch any of its own activities.
             if (callerUserId != userId) {
-                final int permissionFlag = PermissionChecker.checkPermissionForPreflight(
-                        mContext,
-                        INTERACT_ACROSS_PROFILES,
-                        callingPid,
-                        callingUid,
-                        callingPackage);
-                if (permissionFlag != PermissionChecker.PERMISSION_GRANTED
-                        || !isSameProfileGroup(callerUserId, userId)) {
-                    throw new SecurityException("Attempt to launch activity without required "
-                            + INTERACT_ACROSS_PROFILES
-                            + " permission or target user is not in the same profile group.");
+                if (!hasInteractAcrossProfilesPermission(callingPackage, callingUid, callingPid)
+                        && !isPermissionGranted(START_CROSS_PROFILE_ACTIVITIES, callingUid)) {
+                    throw new SecurityException("Attempt to launch activity without one of the"
+                            + " required " + INTERACT_ACROSS_PROFILES + " or "
+                            + START_CROSS_PROFILE_ACTIVITIES + " permissions.");
+                }
+                if (!isSameProfileGroup(callerUserId, userId)) {
+                    throw new SecurityException("Attempt to launch activity when target user is"
+                            + " not in the same profile group.");
                 }
             }
             launchIntent.setComponent(component);
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 9a80a4e..48689a8 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -485,8 +485,7 @@
                 mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
                         FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
             }
-            PackageManagerService.removeKeystoreDataIfNeeded(mUserManagerInternal, nextUserId,
-                    ps.getAppId());
+            mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
             preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
                     nextUserId);
             mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index eac38af..dcad3ec 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -379,7 +379,7 @@
         // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
         // and the first package that uses the library will dexopt it. The
         // others will see that the compiled code for the library is up to date.
-        Collection<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+        Collection<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
         final String[] instructionSets = getAppDexInstructionSets(
                 AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
                 AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index dfa6c66..9efe81a 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -32,7 +32,6 @@
 import static com.android.server.pm.PackageManagerService.TAG;
 
 import android.annotation.Nullable;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -47,6 +46,7 @@
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedArrayMap;
 
 import java.io.File;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 0f96e83..80699ac 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -91,7 +91,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.backup.IBackupManager;
 import android.content.ContentResolver;
@@ -113,11 +112,6 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
 import android.content.pm.dex.DexMetadataHelper;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.net.Uri;
@@ -163,6 +157,11 @@
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
@@ -195,38 +194,32 @@
     private final AppDataHelper mAppDataHelper;
     private final BroadcastHelper mBroadcastHelper;
     private final RemovePackageHelper mRemovePackageHelper;
-    private final StorageManager mStorageManager;
-    private final RollbackManagerInternal mRollbackManager;
     private final IncrementalManager mIncrementalManager;
     private final ApexManager mApexManager;
     private final DexManager mDexManager;
     private final ArtManagerService mArtManagerService;
-    private final AppOpsManager mAppOpsManager;
     private final Context mContext;
     private final PackageDexOptimizer mPackageDexOptimizer;
     private final PackageAbiHelper mPackageAbiHelper;
     private final ViewCompiler mViewCompiler;
-    private final IBackupManager mIBackupManager;
     private final SharedLibrariesImpl mSharedLibraries;
+    private final PackageManagerServiceInjector mInjector;
 
     // TODO(b/198166813): remove PMS dependency
     InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
         mPm = pm;
+        mInjector = pm.mInjector;
         mAppDataHelper = appDataHelper;
         mBroadcastHelper = new BroadcastHelper(pm.mInjector);
         mRemovePackageHelper = new RemovePackageHelper(pm);
-        mStorageManager = pm.mInjector.getSystemService(StorageManager.class);
-        mRollbackManager = pm.mInjector.getLocalService(RollbackManagerInternal.class);
         mIncrementalManager = pm.mInjector.getIncrementalManager();
         mApexManager = pm.mInjector.getApexManager();
         mDexManager = pm.mInjector.getDexManager();
         mArtManagerService = pm.mInjector.getArtManagerService();
-        mAppOpsManager = pm.mInjector.getSystemService(AppOpsManager.class);
         mContext = pm.mInjector.getContext();
         mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
         mPackageAbiHelper = pm.mInjector.getAbiHelper();
         mViewCompiler = pm.mInjector.getViewCompiler();
-        mIBackupManager = pm.mInjector.getIBackupManager();
         mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
     }
 
@@ -693,7 +686,8 @@
      * Returns whether the restore successfully completed.
      */
     private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
-        if (mIBackupManager != null) {
+        IBackupManager iBackupManager = mInjector.getIBackupManager();
+        if (iBackupManager != null) {
             // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
             // in the BackupManager. USER_ALL is used in compatibility tests.
             if (userId == UserHandle.USER_ALL) {
@@ -704,8 +698,8 @@
             }
             Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
             try {
-                if (mIBackupManager.isUserReadyForBackup(userId)) {
-                    mIBackupManager.restoreAtInstallForUser(
+                if (iBackupManager.isUserReadyForBackup(userId)) {
+                    iBackupManager.restoreAtInstallForUser(
                             userId, res.mPkg.getPackageName(), token);
                 } else {
                     Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
@@ -756,7 +750,9 @@
 
         if (ps != null && doSnapshotOrRestore) {
             final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
-            mRollbackManager.snapshotAndRestoreUserData(packageName,
+            final RollbackManagerInternal rollbackManager =
+                    mInjector.getLocalService(RollbackManagerInternal.class);
+            rollbackManager.snapshotAndRestoreUserData(packageName,
                     UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token);
             return true;
         }
@@ -894,8 +890,6 @@
         final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
         final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
         final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
-        final Map<String, PackageSetting> lastStaticSharedLibSettings =
-                new ArrayMap<>(requests.size());
         final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
         boolean success = false;
         try {
@@ -942,39 +936,35 @@
                     if (result.needsNewAppId()) {
                         request.mInstallResult.mRemovedInfo.mAppIdChanging = true;
                     }
+                    if (!checkNoAppStorageIsConsistent(
+                            result.mRequest.mOldPkg, result.mPkgSetting.getPkg())) {
+                        // TODO: INSTALL_FAILED_UPDATE_INCOMPATIBLE is about incomptabible
+                        //  signatures. Is there a better error code?
+                        request.mInstallResult.setError(
+                                INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                "Update attempted to change value of "
+                                        + PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+                        return;
+                    }
                     createdAppId.put(packageName, optimisticallyRegisterAppId(result));
                     versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
                             mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
-                    if (result.mStaticSharedLibraryInfo != null
-                            || result.mSdkSharedLibraryInfo != null) {
-                        final PackageSetting sharedLibLatestVersionSetting =
-                                mSharedLibraries.getSharedLibLatestVersionSetting(result);
-                        if (sharedLibLatestVersionSetting != null) {
-                            lastStaticSharedLibSettings.put(
-                                    result.mPkgSetting.getPkg().getPackageName(),
-                                    sharedLibLatestVersionSetting);
-                        }
-                    }
                 } catch (PackageManagerException e) {
                     request.mInstallResult.setError("Scanning Failed.", e);
                     return;
                 }
             }
-            ReconcileRequest
-                    reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
-                    installResults,
-                    prepareResults,
-                    mSharedLibraries.getAll(),
-                    Collections.unmodifiableMap(mPm.mPackages), versionInfos,
-                    lastStaticSharedLibSettings);
+            ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
+                    installResults, prepareResults,
+                    Collections.unmodifiableMap(mPm.mPackages), versionInfos);
             CommitRequest commitRequest = null;
             synchronized (mPm.mLock) {
                 Map<String, ReconciledPackage> reconciledPackages;
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                     reconciledPackages = ReconcilePackageUtils.reconcilePackages(
-                            reconcileRequest, mPm.mSettings.getKeySetManagerService(),
-                            mPm.mInjector);
+                            reconcileRequest, mSharedLibraries,
+                            mPm.mSettings.getKeySetManagerService());
                 } catch (ReconcileFailure e) {
                     for (InstallRequest request : requests) {
                         request.mInstallResult.setError("Reconciliation failed...", e);
@@ -1040,6 +1030,22 @@
     }
 
     @GuardedBy("mPm.mInstallLock")
+    private boolean checkNoAppStorageIsConsistent(AndroidPackage oldPkg, AndroidPackage newPkg) {
+        if (oldPkg == null) {
+            // New install, nothing to check against.
+            return true;
+        }
+        final PackageManager.Property curProp =
+                oldPkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+        final PackageManager.Property newProp =
+                newPkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
+        if (curProp == null || !curProp.getBoolean()) {
+            return newProp == null || !newProp.getBoolean();
+        }
+        return newProp != null && newProp.getBoolean();
+    }
+
+    @GuardedBy("mPm.mInstallLock")
     private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
             throws PrepareFailure {
         final int installFlags = args.mInstallFlags;
@@ -1201,21 +1207,36 @@
             }
 
             PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
-            if (ps != null) {
-                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
+            PackageSetting signatureCheckPs = ps;
 
-                // Static shared libs have same package with different versions where
-                // we internally use a synthetic package name to allow multiple versions
-                // of the same package, therefore we need to compare signatures against
-                // the package setting for the latest library version.
-                PackageSetting signatureCheckPs = ps;
-                if (parsedPackage.isStaticSharedLibrary()) {
-                    SharedLibraryInfo libraryInfo = mSharedLibraries.getLatestSharedLibraVersionLPr(
-                            parsedPackage);
-                    if (libraryInfo != null) {
-                        signatureCheckPs = mPm.mSettings.getPackageLPr(
-                                libraryInfo.getPackageName());
-                    }
+            // SDK libs can have other major versions with different package names.
+            if (signatureCheckPs == null && parsedPackage.isSdkLibrary()) {
+                WatchedLongSparseArray<SharedLibraryInfo> libraryInfos =
+                        mSharedLibraries.getSharedLibraryInfos(
+                                parsedPackage.getSdkLibName());
+                if (libraryInfos != null && libraryInfos.size() > 0) {
+                    // Any existing version would do.
+                    SharedLibraryInfo libraryInfo = libraryInfos.valueAt(0);
+                    signatureCheckPs = mPm.mSettings.getPackageLPr(libraryInfo.getPackageName());
+                }
+            }
+
+            // Static shared libs have same package with different versions where
+            // we internally use a synthetic package name to allow multiple versions
+            // of the same package, therefore we need to compare signatures against
+            // the package setting for the latest library version.
+            if (parsedPackage.isStaticSharedLibrary()) {
+                SharedLibraryInfo libraryInfo =
+                        mSharedLibraries.getLatestStaticSharedLibraVersionLPr(parsedPackage);
+                if (libraryInfo != null) {
+                    signatureCheckPs = mPm.mSettings.getPackageLPr(libraryInfo.getPackageName());
+                }
+            }
+
+            if (signatureCheckPs != null) {
+                if (DEBUG_INSTALL) {
+                    Slog.d(TAG,
+                            "Existing package for signature checking: " + signatureCheckPs);
                 }
 
                 // Quick validity check that we're signed correctly if updating;
@@ -1250,6 +1271,10 @@
                         throw new PrepareFailure(e.error, e.getMessage());
                     }
                 }
+            }
+
+            if (ps != null) {
+                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
 
                 if (ps.getPkg() != null) {
                     systemApp = ps.getPkg().isSystem();
@@ -1959,14 +1984,15 @@
                             reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
                     if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
                             == 0) {
-                        if (ps1.getOldCodePaths() == null) {
-                            ps1.setOldCodePaths(new ArraySet<>());
+                        Set<String> oldCodePaths = ps1.getOldCodePaths();
+                        if (oldCodePaths == null) {
+                            oldCodePaths = new ArraySet<>();
                         }
-                        Collections.addAll(ps1.getOldCodePaths(), oldPackage.getBaseApkPath());
+                        Collections.addAll(oldCodePaths, oldPackage.getBaseApkPath());
                         if (oldPackage.getSplitCodePaths() != null) {
-                            Collections.addAll(ps1.getOldCodePaths(),
-                                    oldPackage.getSplitCodePaths());
+                            Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
                         }
+                        ps1.setOldCodePaths(oldCodePaths);
                     } else {
                         ps1.setOldCodePaths(null);
                     }
@@ -2219,6 +2245,17 @@
             if (reconciledPkg.mScanResult.needsNewAppId()) {
                 // Only set previousAppId if the app is migrating out of shared UID
                 previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
+
+                if (pkg.shouldInheritKeyStoreKeys()) {
+                    // Migrate keystore data
+                    mAppDataHelper.migrateKeyStoreData(
+                            previousAppId, reconciledPkg.mPkgSetting.getAppId());
+                }
+
+                if (reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId == previousAppId) {
+                    // If the previous app ID is removed, clear the keys
+                    mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, previousAppId);
+                }
             }
             mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
             if (reconciledPkg.mPrepareResult.mClearCodeCache) {
@@ -2777,8 +2814,10 @@
                 // Send broadcast package appeared if external for all users
                 if (res.mPkg.isExternalStorage()) {
                     if (!update) {
+                        final StorageManager storageManager =
+                                mInjector.getSystemService(StorageManager.class);
                         VolumeInfo volume =
-                                mStorageManager.findVolumeByUuid(
+                                storageManager.findVolumeByUuid(
                                         StorageManager.convert(
                                                 res.mPkg.getVolumeUuid()).toString());
                         int packageExternalStorageType =
@@ -3559,22 +3598,17 @@
                 boolean appIdCreated = false;
                 try {
                     final String pkgName = scanResult.mPkgSetting.getPackageName();
+                    final ReconcileRequest reconcileRequest = new ReconcileRequest(
+                            Collections.singletonMap(pkgName, scanResult),
+                            mPm.mPackages,
+                            Collections.singletonMap(pkgName,
+                                    mPm.getSettingsVersionForPackage(parsedPackage)));
                     final Map<String, ReconciledPackage> reconcileResult =
-                            ReconcilePackageUtils.reconcilePackages(
-                                    new ReconcileRequest(
-                                            Collections.singletonMap(pkgName, scanResult),
-                                            mSharedLibraries.getAll(),
-                                            mPm.mPackages,
-                                            Collections.singletonMap(
-                                                    pkgName,
-                                                    mPm.getSettingsVersionForPackage(
-                                                            parsedPackage)),
-                                            Collections.singletonMap(pkgName, mSharedLibraries
-                                                    .getSharedLibLatestVersionSetting(scanResult))),
-                                    mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+                            ReconcilePackageUtils.reconcilePackages(reconcileRequest,
+                                    mSharedLibraries, mPm.mSettings.getKeySetManagerService());
                     appIdCreated = optimisticallyRegisterAppId(scanResult);
-                    commitReconciledScanResultLocked(
-                            reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+                    commitReconciledScanResultLocked(reconcileResult.get(pkgName),
+                            mPm.mUserManager.getUserIds());
                 } catch (PackageManagerException e) {
                     if (appIdCreated) {
                         cleanUpAppIdCreation(scanResult);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 1e1d169..db346da 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -17,11 +17,11 @@
 package com.android.server.pm;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
 
 import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
 
 import android.annotation.NonNull;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Base64;
@@ -340,9 +340,10 @@
         if (p == null || p.getKeySetData() == null) {
             return null;
         }
-        Long keySetId = p.getKeySetData().getAliases().get(alias);
+        final ArrayMap<String, Long> aliases = p.getKeySetData().getAliases();
+        Long keySetId = aliases.get(alias);
         if (keySetId == null) {
-            throw new IllegalArgumentException("Unknown KeySet alias: " + alias);
+            throw new IllegalArgumentException("Unknown KeySet alias: " + alias + ", aliases = " + aliases);
         }
         return mKeySets.get(keySetId);
     }
@@ -811,7 +812,7 @@
         long identifier = parser.getAttributeLong(null, "identifier");
         int refCount = 0;
         byte[] publicKey = parser.getAttributeBytesBase64(null, "value", null);
-        PublicKey pub = parsePublicKey(publicKey);
+        PublicKey pub = FrameworkParsingPackageUtils.parsePublicKey(publicKey);
         if (pub != null) {
             PublicKeyHandle pkh = new PublicKeyHandle(identifier, refCount, pub);
             mPublicKeys.put(identifier, pkh);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 0777cde..ca87685 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -88,6 +88,7 @@
 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.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -103,6 +104,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.ExecutionException;
 
 /**
  * Service that manages requests and callbacks for launchers that support
@@ -728,9 +730,16 @@
                 return null;
             }
 
-            final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
-                    getCallingUserId(), callingPackage, packageName, shortcutId,
-                    user.getIdentifier(), injectBinderCallingPid(), injectBinderCallingUid());
+            final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
+            Intent[] intents;
+            mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(),
+                    callingPackage, packageName, shortcutId, user.getIdentifier(),
+                    injectBinderCallingPid(), injectBinderCallingUid(), ret);
+            try {
+                intents = ret.get();
+            } catch (InterruptedException | ExecutionException e) {
+                return null;
+            }
             if (intents == null || intents.length == 0) {
                 return null;
             }
@@ -901,6 +910,40 @@
         }
 
         @Override
+        public void getShortcutsAsync(@NonNull final String callingPackage,
+                @NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser,
+                @NonNull final AndroidFuture<List<ShortcutInfo>> cb) {
+            ensureShortcutPermission(callingPackage);
+            if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) {
+                cb.complete(Collections.EMPTY_LIST);
+                return;
+            }
+
+            final long changedSince = query.getChangedSince();
+            final String packageName = query.getPackage();
+            final List<String> shortcutIds = query.getShortcutIds();
+            final List<LocusId> locusIds = query.getLocusIds();
+            final ComponentName componentName = query.getActivity();
+            final int flags = query.getQueryFlags();
+            if (shortcutIds != null && packageName == null) {
+                throw new IllegalArgumentException(
+                        "To query by shortcut ID, package name must also be set");
+            }
+            if (locusIds != null && packageName == null) {
+                throw new IllegalArgumentException(
+                        "To query by locus ID, package name must also be set");
+            }
+            if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+                ensureStrictAccessShortcutsPermission(callingPackage);
+            }
+
+            mShortcutServiceInternal.getShortcutsAsync(getCallingUserId(),
+                    callingPackage, changedSince, packageName, shortcutIds, locusIds,
+                    componentName, flags, targetUser.getIdentifier(),
+                    injectBinderCallingPid(), injectBinderCallingUid(), cb);
+        }
+
+        @Override
         public void registerShortcutChangeCallback(@NonNull final String callingPackage,
                 @NonNull final ShortcutQueryWrapper query,
                 @NonNull final IShortcutChangeCallback callback) {
@@ -991,8 +1034,14 @@
                 return null;
             }
 
-            return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
-                    callingPackage, packageName, id, targetUserId);
+            final AndroidFuture<ParcelFileDescriptor> ret = new AndroidFuture<>();
+            mShortcutServiceInternal.getShortcutIconFdAsync(getCallingUserId(),
+                    callingPackage, packageName, id, targetUserId, ret);
+            try {
+                return ret.get();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         @Override
@@ -1003,8 +1052,14 @@
                 return null;
             }
 
-            return mShortcutServiceInternal.getShortcutIconUri(getCallingUserId(), callingPackage,
-                    packageName, shortcutId, userId);
+            final AndroidFuture<String> ret = new AndroidFuture<>();
+            mShortcutServiceInternal.getShortcutIconUriAsync(getCallingUserId(), callingPackage,
+                    packageName, shortcutId, userId, ret);
+            try {
+                return ret.get();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         @Override
@@ -1037,9 +1092,16 @@
                 ensureShortcutPermission(callerUid, callerPid, callingPackage);
             }
 
-            final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
-                    callingUserId, callingPackage, packageName, shortcutId, targetUserId,
-                    callerPid, callerUid);
+            final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
+            Intent[] intents;
+            mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(), callingPackage,
+                    packageName, shortcutId, targetUserId,
+                    injectBinderCallingPid(), injectBinderCallingUid(), ret);
+            try {
+                intents = ret.get();
+            } catch (InterruptedException | ExecutionException e) {
+                return false;
+            }
             if (intents == null || intents.length == 0) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 1bdc9f3..c219f80 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -40,7 +40,7 @@
 per-file KeySetManagerService.java = [email protected], [email protected]
 per-file PackageKeySetData.java = [email protected], [email protected]
 per-file PackageSignatures.java = [email protected], [email protected]
-per-file SELinuxMMAC* = [email protected], [email protected], [email protected], [email protected]
+per-file SELinuxMMAC* = [email protected], [email protected], [email protected]
 
 # shortcuts
 per-file LauncherAppsService.java = [email protected], [email protected], [email protected], [email protected], [email protected]
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cfcf199..ccc375f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -29,6 +31,7 @@
 import android.app.NotificationManager;
 import android.app.PackageDeleteObserver;
 import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -70,6 +73,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -121,7 +125,7 @@
 public class PackageInstallerService extends IPackageInstaller.Stub implements
         PackageSessionProvider {
     private static final String TAG = "PackageInstaller";
-    private static final boolean LOGD = false;
+    private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
@@ -135,7 +139,7 @@
     /** Automatically destroy sessions older than this */
     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
     /** Automatically destroy staged sessions that have not changed state in this time */
-    private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS;
+    private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 21 * DateUtils.DAY_IN_MILLIS;
     /** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */
     private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024;
     /** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */
@@ -330,7 +334,7 @@
                 StagingManager.StagedSession stagedSession = session.mStagedSession;
                 if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
                         && getSession(stagedSession.getParentSessionId()) == null) {
-                    stagedSession.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                    stagedSession.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
                             "An orphan staged session " + stagedSession.sessionId() + " is found, "
                                 + "parent " + stagedSession.getParentSessionId() + " is missing");
                     continue;
@@ -510,20 +514,27 @@
                 valid = true;
             }
             if (!valid) {
+                Slog.w(TAG, "Remove old session: " + session.sessionId);
                 // Remove expired sessions as well as child sessions if any
-                mSessions.remove(session.sessionId);
-                // Since this is early during boot we don't send
-                // any observer events about the session, but we
-                // keep details around for dumpsys.
-                addHistoricalSessionLocked(session);
-                for (PackageInstallerSession child : session.getChildSessions()) {
-                    mSessions.remove(child.sessionId);
-                    addHistoricalSessionLocked(child);
-                }
+                removeActiveSession(session);
             }
         }
     }
 
+    /**
+     * Moves a session (including the child sessions) from mSessions to mHistoricalSessions.
+     * This should only be called on a root session.
+     */
+    @GuardedBy("mSessions")
+    private void removeActiveSession(PackageInstallerSession session) {
+        mSessions.remove(session.sessionId);
+        addHistoricalSessionLocked(session);
+        for (PackageInstallerSession child : session.getChildSessions()) {
+            mSessions.remove(child.sessionId);
+            addHistoricalSessionLocked(child);
+        }
+    }
+
     @GuardedBy("mSessions")
     private void addHistoricalSessionLocked(PackageInstallerSession session) {
         CharArrayWriter writer = new CharArrayWriter();
@@ -841,7 +852,7 @@
                 mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
                 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
                 null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
-                false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
+                false, false, false, SessionInfo.SESSION_NO_ERROR, "");
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
@@ -850,6 +861,9 @@
         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
 
         mSettingsWriteRequest.schedule();
+        if (LOGD) {
+            Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged);
+        }
         return sessionId;
     }
 
@@ -1301,7 +1315,7 @@
             mPackageName = packageName;
             if (showNotification) {
                 mNotification = buildSuccessNotification(mContext,
-                        mContext.getResources().getString(R.string.package_deleted_device_owner),
+                        getDeviceOwnerDeletedPackageMsg(),
                         packageName,
                         userId);
             } else {
@@ -1309,6 +1323,12 @@
             }
         }
 
+        private String getDeviceOwnerDeletedPackageMsg() {
+            DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+            return dpm.getString(PACKAGE_DELETED_BY_DO,
+                    () -> mContext.getString(R.string.package_updated_device_owner));
+        }
+
         @Override
         public void onUserActionRequired(Intent intent) {
             if (mTarget == null) {
@@ -1649,10 +1669,18 @@
                         mStagingManager.abortSession(session.mStagedSession);
                     }
                     synchronized (mSessions) {
-                        if (!session.isStaged() || !success) {
-                            mSessions.remove(session.sessionId);
+                        // Child sessions will be removed along with its parent as a whole
+                        if (!session.hasParentSessionId()) {
+                            // Retain policy:
+                            // 1. Don't keep non-staged sessions
+                            // 2. Don't keep explicitly abandoned sessions
+                            // 3. Don't keep sessions that fail validation (isCommitted() is false)
+                            boolean shouldRemove = !session.isStaged() || session.isDestroyed()
+                                    || !session.isCommitted();
+                            if (shouldRemove) {
+                                removeActiveSession(session);
+                            }
                         }
-                        addHistoricalSessionLocked(session);
 
                         final File appIconFile = buildAppIconFile(session.sessionId);
                         if (appIconFile.exists()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f45e54b..d9ade96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,6 +17,8 @@
 package com.android.server.pm;
 
 import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
 import static android.content.pm.DataLoaderType.INCREMENTAL;
 import static android.content.pm.DataLoaderType.STREAMING;
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
@@ -27,6 +29,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -55,6 +58,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
@@ -90,7 +94,6 @@
 import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.graphics.Bitmap;
@@ -155,6 +158,7 @@
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
@@ -460,7 +464,7 @@
     @GuardedBy("mLock")
     private boolean mSessionFailed;
     @GuardedBy("mLock")
-    private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+    private int mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
     @GuardedBy("mLock")
     private String mSessionErrorMessage;
 
@@ -1733,61 +1737,22 @@
 
     @WorkerThread
     private void handleStreamValidateAndCommit() {
-        PackageManagerException unrecoverableFailure = null;
-        // This will track whether the session and any children were validated and are ready to
-        // progress to the next phase of install
-        boolean allSessionsReady = false;
         try {
-            allSessionsReady = streamValidateAndCommit();
+            // This will track whether the session and any children were validated and are ready to
+            // progress to the next phase of install
+            boolean allSessionsReady = true;
+            for (PackageInstallerSession child : getChildSessions()) {
+                allSessionsReady &= child.streamValidateAndCommit();
+            }
+            if (allSessionsReady && streamValidateAndCommit()) {
+                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
+            }
         } catch (PackageManagerException e) {
-            unrecoverableFailure = e;
+            destroy();
+            String msg = ExceptionUtils.getCompleteMessage(e);
+            dispatchSessionFinished(e.error, msg, null);
+            maybeFinishChildSessions(e.error, msg);
         }
-
-        if (isMultiPackage()) {
-            final List<PackageInstallerSession> childSessions;
-            synchronized (mLock) {
-                childSessions = getChildSessionsLocked();
-            }
-            int childCount = childSessions.size();
-
-            // This will contain all child sessions that do not encounter an unrecoverable failure
-            ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
-
-            for (int i = childCount - 1; i >= 0; --i) {
-                // commit all children, regardless if any of them fail; we'll throw/return
-                // as appropriate once all children have been processed
-                try {
-                    PackageInstallerSession session = childSessions.get(i);
-                    allSessionsReady &= session.streamValidateAndCommit();
-                    nonFailingSessions.add(session);
-                } catch (PackageManagerException e) {
-                    allSessionsReady = false;
-                    if (unrecoverableFailure == null) {
-                        unrecoverableFailure = e;
-                    }
-                }
-            }
-            // If we encountered any unrecoverable failures, destroy all other sessions including
-            // the parent
-            if (unrecoverableFailure != null) {
-                // {@link #streamValidateAndCommit()} calls
-                // {@link #onSessionValidationFailure(PackageManagerException)}, but we don't
-                // expect it to ever do so for parent sessions. Call that on this parent to clean
-                // it up and notify listeners of the error.
-                onSessionValidationFailure(unrecoverableFailure);
-                // fail other child sessions that did not already fail
-                for (int i = nonFailingSessions.size() - 1; i >= 0; --i) {
-                    PackageInstallerSession session = nonFailingSessions.get(i);
-                    session.onSessionValidationFailure(unrecoverableFailure);
-                }
-            }
-        }
-
-        if (!allSessionsReady) {
-            return;
-        }
-
-        mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
     }
 
     private final class FileSystemConnector extends
@@ -2025,11 +1990,11 @@
             }
             return true;
         } catch (PackageManagerException e) {
-            throw onSessionValidationFailure(e);
+            throw e;
         } catch (Throwable e) {
             // Convert all exceptions into package manager exceptions as only those are handled
             // in the code above.
-            throw onSessionValidationFailure(new PackageManagerException(e));
+            throw new PackageManagerException(e);
         }
     }
 
@@ -2092,15 +2057,15 @@
         if (isStaged()) {
             // This will clean up the session when it reaches the terminal state
             mStagedSession.setSessionFailed(
-                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
+                    SessionInfo.SESSION_VERIFICATION_FAILED, msgWithErrorCode);
             mStagedSession.notifyEndPreRebootVerification();
         } else {
             // Session is sealed and committed but could not be verified, we need to destroy it.
             destroy();
-            // Dispatch message to remove session from PackageInstallerService.
-            dispatchSessionFinished(error, msg, null);
-            maybeFinishChildSessions(error, msg);
         }
+        // Dispatch message to remove session from PackageInstallerService.
+        dispatchSessionFinished(error, msg, null);
+        maybeFinishChildSessions(error, msg);
     }
 
     private void onSessionInstallationFailure(int error, String detailedMessage) {
@@ -2301,7 +2266,7 @@
         if (params.isStaged) {
             // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
             //  though ideally, we just need to send session committed broadcast.
-            dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
+            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
 
             mStagedSession.verifySession();
         } else {
@@ -2547,8 +2512,11 @@
         if (isStaged()) {
             mSessionProvider.getSessionVerifier().verifyStaged(mStagedSession, (error, msg) -> {
                 mStagedSession.notifyEndPreRebootVerification();
-                if (error == SessionInfo.STAGED_SESSION_NO_ERROR) {
+                if (error == SessionInfo.SESSION_NO_ERROR) {
                     mStagingManager.commitSession(mStagedSession);
+                } else {
+                    dispatchSessionFinished(INSTALL_FAILED_VERIFICATION_FAILURE, msg, null);
+                    maybeFinishChildSessions(INSTALL_FAILED_VERIFICATION_FAILURE, msg);
                 }
             });
             return;
@@ -2578,7 +2546,7 @@
         // Do not try to install staged apex session. Parent session will have at least one apk
         // session.
         if (!isMultiPackage() && isApexSession() && params.isStaged) {
-            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
+            dispatchSessionFinished(INSTALL_SUCCEEDED,
                     "Apex package should have been installed by apexd", null);
             return null;
         }
@@ -2592,14 +2560,12 @@
             @Override
             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                     Bundle extras) {
-                if (isStaged()) {
-                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
-                } else {
+                if (!isStaged()) {
                     // We've reached point of no return; call into PMS to install the stage.
                     // Regardless of success or failure we always destroy session.
                     destroyInternal();
-                    dispatchSessionFinished(returnCode, msg, extras);
                 }
+                dispatchSessionFinished(returnCode, msg, extras);
             }
         };
 
@@ -4168,7 +4134,7 @@
             mSessionReady = true;
             mSessionApplied = false;
             mSessionFailed = false;
-            mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+            mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
             mSessionErrorMessage = "";
         }
         mCallback.onSessionChanged(this);
@@ -4196,7 +4162,7 @@
             mSessionReady = false;
             mSessionApplied = true;
             mSessionFailed = false;
-            mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+            mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
             mSessionErrorMessage = "";
             Slog.d(TAG, "Marking session " + sessionId + " as applied");
         }
@@ -4373,9 +4339,7 @@
         if (INSTALL_SUCCEEDED == returnCode && showNotification) {
             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
             Notification notification = PackageInstallerService.buildSuccessNotification(context,
-                    context.getResources()
-                            .getString(update ? R.string.package_updated_device_owner :
-                                    R.string.package_installed_device_owner),
+                    getDeviceOwnerInstalledPackageMsg(context, update),
                     basePackageName,
                     userId);
             if (notification != null) {
@@ -4407,6 +4371,15 @@
         }
     }
 
+    private static String getDeviceOwnerInstalledPackageMsg(Context context, boolean update) {
+        DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        return update
+                ? dpm.getString(PACKAGE_UPDATED_BY_DO,
+                    () -> context.getString(R.string.package_updated_device_owner))
+                : dpm.getString(PACKAGE_INSTALLED_BY_DO,
+                    () -> context.getString(R.string.package_installed_device_owner));
+    }
+
     /**
      * This method doesn't change internal states and is safe to call outside the lock.
      */
@@ -4705,7 +4678,7 @@
         final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false);
         final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false);
         final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE,
-                SessionInfo.STAGED_SESSION_NO_ERROR);
+                SessionInfo.SESSION_NO_ERROR);
         final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE);
 
         if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 248944e..e00f4f5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -130,9 +130,6 @@
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.IArtManager;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -170,7 +167,6 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
-import android.security.KeyStore;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -237,11 +233,11 @@
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.SuspendParams;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
 import com.android.server.pm.pkg.mutate.PackageStateWrite;
-import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -292,7 +288,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Predicate;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -955,6 +950,7 @@
     final @Nullable String mRetailDemoPackage;
     final @Nullable String mOverlayConfigSignaturePackage;
     final @Nullable String mRecentsPackage;
+    final @Nullable String mAmbientContextDetectionPackage;
 
     @GuardedBy("mLock")
     private final PackageUsage mPackageUsage = new PackageUsage();
@@ -971,6 +967,7 @@
     private final PreferredActivityHelper mPreferredActivityHelper;
     private final ResolveIntentHelper mResolveIntentHelper;
     private final DexOptHelper mDexOptHelper;
+    private final SuspendPackageHelper mSuspendPackageHelper;
 
     /**
      * Invalidate the package info cache, which includes updating the cached computer.
@@ -1672,6 +1669,7 @@
         mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
         mRetailDemoPackage = testParams.retailDemoPackage;
         mRecentsPackage = testParams.recentsPackage;
+        mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
         mConfiguratorPackage = testParams.configuratorPackage;
         mAppPredictionServicePackage = testParams.appPredictionServicePackage;
         mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -1706,6 +1704,7 @@
         mPreferredActivityHelper = testParams.preferredActivityHelper;
         mResolveIntentHelper = testParams.resolveIntentHelper;
         mDexOptHelper = testParams.dexOptHelper;
+        mSuspendPackageHelper = testParams.suspendPackageHelper;
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
 
         invalidatePackageInfoCache();
@@ -1852,6 +1851,8 @@
         mPreferredActivityHelper = new PreferredActivityHelper(this);
         mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
         mDexOptHelper = new DexOptHelper(this);
+        mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
+                mProtectedPackages);
 
         synchronized (mLock) {
             // Create the computer as soon as the state objects have been installed.  The
@@ -1996,6 +1997,7 @@
             mRetailDemoPackage = getRetailDemoPackageName();
             mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName();
             mRecentsPackage = getRecentsPackageName();
+            mAmbientContextDetectionPackage = getAmbientContextDetectionPackageName();
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -2683,7 +2685,7 @@
         return mComputer.getPackageUid(packageName, flags, userId);
     }
 
-    private int getPackageUidInternal(String packageName,
+    int getPackageUidInternal(String packageName,
             @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
         return mComputer.getPackageUidInternal(packageName, flags, userId, callingUid);
     }
@@ -4295,51 +4297,6 @@
         info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
     }
 
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
-    void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
-        final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
-        final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
-        final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
-        final int[] userIds = new int[] {userId};
-        // Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if
-        // allow lists are the same.
-        for (int i = 0; i < pkgList.length; i++) {
-            final String pkgName = pkgList[i];
-            final int uid = uidList[i];
-            SparseArray<int[]> allowList = mAppsFilter.getVisibilityAllowList(
-                    getPackageStateInternal(pkgName, Process.SYSTEM_UID),
-                    userIds, getPackageStates());
-            if (allowList == null) {
-                allowList = new SparseArray<>(0);
-            }
-            boolean merged = false;
-            for (int j = 0; j < allowListsToSend.size(); j++) {
-                if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) {
-                    pkgsToSend.get(j).add(pkgName);
-                    uidsToSend.get(j).add(uid);
-                    merged = true;
-                    break;
-                }
-            }
-            if (!merged) {
-                pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName)));
-                uidsToSend.add(IntArray.wrap(new int[] {uid}));
-                allowListsToSend.add(allowList);
-            }
-        }
-
-        for (int i = 0; i < pkgsToSend.size(); i++) {
-            final Bundle extras = new Bundle(3);
-            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
-                    pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()]));
-            extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
-            final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
-                    ? null : allowListsToSend.get(i);
-            sendPackageBroadcast(intent, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null,
-                    null, userIds, null, allowList, null);
-        }
-    }
-
     /**
      * Returns true if application is not found or there was an error. Otherwise it returns
      * the hidden state of the package for the given user.
@@ -4382,7 +4339,8 @@
                     + userId);
         }
         Objects.requireNonNull(packageNames, "packageNames cannot be null");
-        if (restrictionFlags != 0 && !isSuspendAllowedForUser(userId)) {
+        if (restrictionFlags != 0
+                && !mSuspendPackageHelper.isSuspendAllowedForUser(userId, callingUid)) {
             Slog.w(TAG, "Cannot restrict packages due to restrictions on user " + userId);
             return packageNames;
         }
@@ -4390,8 +4348,9 @@
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        final boolean[] canRestrict = (restrictionFlags != 0) ? canSuspendPackageForUserInternal(
-                packageNames, userId) : null;
+        final boolean[] canRestrict = (restrictionFlags != 0)
+                ? mSuspendPackageHelper.canSuspendPackageForUser(packageNames, userId, callingUid)
+                : null;
 
         for (int i = 0; i < packageNames.length; i++) {
             final String packageName = packageNames[i];
@@ -4470,84 +4429,8 @@
         final int callingUid = Binder.getCallingUid();
         enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
                 "setPackagesSuspendedAsUser");
-
-        if (ArrayUtils.isEmpty(packageNames)) {
-            return packageNames;
-        }
-        if (suspended && !isSuspendAllowedForUser(userId)) {
-            Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
-            return packageNames;
-        }
-
-        final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
-        final IntArray changedUids = new IntArray(packageNames.length);
-        final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
-        final IntArray modifiedUids = new IntArray(packageNames.length);
-        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
-                userId) : null;
-
-        for (int i = 0; i < packageNames.length; i++) {
-            final String packageName = packageNames[i];
-            if (callingPackage.equals(packageName)) {
-                Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
-                        + (suspended ? "" : "un") + "suspend itself. Ignoring");
-                unactionedPackages.add(packageName);
-                continue;
-            }
-            final PackageSetting pkgSetting;
-            synchronized (mLock) {
-                pkgSetting = mSettings.getPackageLPr(packageName);
-                if (pkgSetting == null
-                        || shouldFilterApplication(pkgSetting, callingUid, userId)) {
-                    Slog.w(TAG, "Could not find package setting for package: " + packageName
-                            + ". Skipping suspending/un-suspending.");
-                    unactionedPackages.add(packageName);
-                    continue;
-                }
-            }
-            if (canSuspend != null && !canSuspend[i]) {
-                unactionedPackages.add(packageName);
-                continue;
-            }
-            final boolean packageUnsuspended;
-            final boolean packageModified;
-            synchronized (mLock) {
-                if (suspended) {
-                    packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
-                            dialogInfo, appExtras, launcherExtras, userId);
-                } else {
-                    packageModified = pkgSetting.removeSuspension(callingPackage, userId);
-                }
-                packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
-            }
-            if (suspended || packageUnsuspended) {
-                changedPackagesList.add(packageName);
-                changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
-            }
-            if (packageModified) {
-                modifiedPackagesList.add(packageName);
-                modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
-            }
-        }
-
-        if (!changedPackagesList.isEmpty()) {
-            final String[] changedPackages = changedPackagesList.toArray(new String[0]);
-            sendPackagesSuspendedForUser(
-                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
-                              : Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    changedPackages, changedUids.toArray(), userId);
-            sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
-            synchronized (mLock) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
-        }
-        // Send the suspension changed broadcast to ensure suspension state is not stale.
-        if (!modifiedPackagesList.isEmpty()) {
-            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
-                    modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
-        }
-        return unactionedPackages.toArray(new String[0]);
+        return mSuspendPackageHelper.setPackagesSuspended(packageNames, suspended, appExtras,
+                launcherExtras, dialogInfo, callingPackage, userId, callingUid);
     }
 
     @Override
@@ -4557,56 +4440,8 @@
             throw new SecurityException("Calling package " + packageName
                     + " does not belong to calling uid " + callingUid);
         }
-        return getSuspendedPackageAppExtrasInternal(packageName, userId);
-    }
-
-    private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) {
-        final PackageStateInternal ps = getPackageStateInternal(packageName);
-        if (ps == null) {
-            return null;
-        }
-        final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
-        final Bundle allExtras = new Bundle();
-        if (pus.isSuspended()) {
-            for (int i = 0; i < pus.getSuspendParams().size(); i++) {
-                final SuspendParams params = pus.getSuspendParams().valueAt(i);
-                if (params != null && params.getAppExtras() != null) {
-                    allExtras.putAll(params.getAppExtras());
-                }
-            }
-        }
-        return (allExtras.size() > 0) ? allExtras : null;
-    }
-
-    private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
-            int userId) {
-        final String action = suspended
-                ? Intent.ACTION_MY_PACKAGE_SUSPENDED
-                : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
-        mHandler.post(() -> {
-            final IActivityManager am = ActivityManager.getService();
-            if (am == null) {
-                Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
-                        + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
-                return;
-            }
-            final int[] targetUserIds = new int[] {userId};
-            for (String packageName : affectedPackages) {
-                final Bundle appExtras = suspended
-                        ? getSuspendedPackageAppExtrasInternal(packageName, userId)
-                        : null;
-                final Bundle intentExtras;
-                if (appExtras != null) {
-                    intentExtras = new Bundle(1);
-                    intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
-                } else {
-                    intentExtras = null;
-                }
-                mHandler.post(() -> mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
-                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
-                        targetUserIds, false, null, null));
-            }
-        });
+        return mSuspendPackageHelper.getSuspendedPackageAppExtras(
+                packageName, userId, callingUid);
     }
 
     @Override
@@ -4619,50 +4454,14 @@
         synchronized (mLock) {
             allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
         }
-        removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId);
+        mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
+                allPackages, suspendingPackage::equals, userId);
     }
 
     private boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
         return mComputer.isSuspendingAnyPackages(suspendingPackage, userId);
     }
 
-    /**
-     * Removes any suspensions on given packages that were added by packages that pass the given
-     * predicate.
-     *
-     * <p> Caller must flush package restrictions if it cares about immediate data consistency.
-     *
-     * @param packagesToChange The packages on which the suspension are to be removed.
-     * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
-     *                                   suspensions will be removed.
-     * @param userId The user for which the changes are taking place.
-     */
-    private void removeSuspensionsBySuspendingPackage(String[] packagesToChange,
-            Predicate<String> suspendingPackagePredicate, int userId) {
-        final List<String> unsuspendedPackages = new ArrayList<>();
-        final IntArray unsuspendedUids = new IntArray();
-        synchronized (mLock) {
-            for (String packageName : packagesToChange) {
-                final PackageSetting ps = mSettings.getPackageLPr(packageName);
-                if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
-                    ps.removeSuspension(suspendingPackagePredicate, userId);
-                    if (!ps.getUserStateOrDefault(userId).isSuspended()) {
-                        unsuspendedPackages.add(ps.getPackageName());
-                        unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
-                    }
-                }
-            }
-            scheduleWritePackageRestrictionsLocked(userId);
-        }
-        if (!unsuspendedPackages.isEmpty()) {
-            final String[] packageArray = unsuspendedPackages.toArray(
-                    new String[unsuspendedPackages.size()]);
-            sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
-            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    packageArray, unsuspendedUids.toArray(), userId);
-        }
-    }
-
     void removeAllDistractingPackageRestrictions(int userId) {
         final String[] allPackages = mComputer.getAllAvailablePackageNames();
         removeDistractingPackageRestrictions(allPackages, userId);
@@ -4699,24 +4498,6 @@
         }
     }
 
-    private boolean isCallerDeviceOrProfileOwner(int userId) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid == Process.SYSTEM_UID) {
-            return true;
-        }
-        final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
-        if (ownerPackage != null) {
-            return callingUid == getPackageUidInternal(ownerPackage, 0, userId, callingUid);
-        }
-        return false;
-    }
-
-    private boolean isSuspendAllowedForUser(int userId) {
-        return isCallerDeviceOrProfileOwner(userId)
-                || (!mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
-                && !mUserManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
-    }
-
     @Override
     public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
         Objects.requireNonNull(packageNames, "packageNames cannot be null");
@@ -4727,125 +4508,8 @@
             throw new SecurityException("Calling uid " + callingUid
                     + " cannot query getUnsuspendablePackagesForUser for user " + userId);
         }
-        if (!isSuspendAllowedForUser(userId)) {
-            Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
-            return packageNames;
-        }
-        final ArraySet<String> unactionablePackages = new ArraySet<>();
-        final boolean[] canSuspend = canSuspendPackageForUserInternal(packageNames, userId);
-        for (int i = 0; i < packageNames.length; i++) {
-            if (!canSuspend[i]) {
-                unactionablePackages.add(packageNames[i]);
-                continue;
-            }
-            synchronized (mLock) {
-                final PackageSetting ps = mSettings.getPackageLPr(packageNames[i]);
-                if (ps == null || shouldFilterApplication(ps, callingUid, userId)) {
-                    Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
-                    unactionablePackages.add(packageNames[i]);
-                }
-            }
-        }
-        return unactionablePackages.toArray(new String[unactionablePackages.size()]);
-    }
-
-    /**
-     * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
-     * be suspended or not.
-     *
-     * @param packageNames  The package names to check suspendability for.
-     * @param userId The user to check in
-     * @return An array containing results of the checks
-     */
-    @NonNull
-    private boolean[] canSuspendPackageForUserInternal(@NonNull String[] packageNames, int userId) {
-        final boolean[] canSuspend = new boolean[packageNames.length];
-        final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
-            final String dialerPackageName = mDefaultAppProvider.getDefaultDialer(userId);
-            for (int i = 0; i < packageNames.length; i++) {
-                canSuspend[i] = false;
-                final String packageName = packageNames[i];
-
-                if (isPackageDeviceAdmin(packageName, userId)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": has an active device admin");
-                    continue;
-                }
-                if (packageName.equals(activeLauncherPackageName)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": contains the active launcher");
-                    continue;
-                }
-                if (packageName.equals(mRequiredInstallerPackage)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": required for package installation");
-                    continue;
-                }
-                if (packageName.equals(mRequiredUninstallerPackage)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": required for package uninstallation");
-                    continue;
-                }
-                if (packageName.equals(mRequiredVerifierPackage)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": required for package verification");
-                    continue;
-                }
-                if (packageName.equals(dialerPackageName)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": is the default dialer");
-                    continue;
-                }
-                if (packageName.equals(mRequiredPermissionControllerPackage)) {
-                    Slog.w(TAG, "Cannot suspend package \"" + packageName
-                            + "\": required for permissions management");
-                    continue;
-                }
-                synchronized (mLock) {
-                    if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
-                        Slog.w(TAG, "Cannot suspend package \"" + packageName
-                                + "\": protected package");
-                        continue;
-                    }
-                    if (!isCallerOwner && mSettings.getBlockUninstallLPr(userId, packageName)) {
-                        Slog.w(TAG, "Cannot suspend package \"" + packageName
-                                + "\": blocked by admin");
-                        continue;
-                    }
-
-                    AndroidPackage pkg = mPackages.get(packageName);
-                    if (pkg != null) {
-                        // Cannot suspend SDK libs as they are controlled by SDK manager.
-                        if (pkg.isSdkLibrary()) {
-                            Slog.w(TAG, "Cannot suspend package: " + packageName
-                                    + " providing SDK library: "
-                                    + pkg.getSdkLibName());
-                            continue;
-                        }
-                        // Cannot suspend static shared libs as they are considered
-                        // a part of the using app (emulating static linking). Also
-                        // static libs are installed always on internal storage.
-                        if (pkg.isStaticSharedLibrary()) {
-                            Slog.w(TAG, "Cannot suspend package: " + packageName
-                                    + " providing static shared library: "
-                                    + pkg.getStaticSharedLibName());
-                            continue;
-                        }
-                    }
-                }
-                if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
-                    Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
-                    continue;
-                }
-                canSuspend[i] = true;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-        return canSuspend;
+        return mSuspendPackageHelper.getUnsuspendablePackagesForUser(
+                packageNames, userId, callingUid);
     }
 
     @Override
@@ -5446,7 +5110,7 @@
                 FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
 
         final int appId = UserHandle.getAppId(pkg.getUid());
-        removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId);
+        mAppDataHelper.clearKeystoreData(userId, appId);
 
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
         StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
@@ -5519,30 +5183,6 @@
         }
     }
 
-    /**
-     * Remove entries from the keystore daemon. Will only remove it if the
-     * {@code appId} is valid.
-     */
-    static void removeKeystoreDataIfNeeded(UserManagerInternal um, @UserIdInt int userId,
-            @AppIdInt int appId) {
-        if (appId < 0) {
-            return;
-        }
-
-        final KeyStore keyStore = KeyStore.getInstance();
-        if (keyStore != null) {
-            if (userId == UserHandle.USER_ALL) {
-                for (final int individual : um.getUserIds()) {
-                    keyStore.clearUid(UserHandle.getUid(individual, appId));
-                }
-            } else {
-                keyStore.clearUid(UserHandle.getUid(userId, appId));
-            }
-        } else {
-            Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId);
-        }
-    }
-
     @Override
     public void deleteApplicationCacheFiles(final String packageName,
             final IPackageDataObserver observer) {
@@ -5981,6 +5621,11 @@
         return mPmInternal.getSetupWizardPackageName();
     }
 
+    public @Nullable String getAmbientContextDetectionPackageName() {
+        return ensureSystemPackageName(getPackageFromComponentString(
+                        R.string.config_defaultAmbientContextDetectionService));
+    }
+
     public String getIncidentReportApproverPackageName() {
         return ensureSystemPackageName(mContext.getString(
                 R.string.config_incidentReportApproverPackage));
@@ -7421,41 +7066,27 @@
 
         @Override
         public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
-            final PackageStateInternal packageState = getPackageStateInternal(packageName);
-            if (packageState == null) {
-                return null;
-            }
-            Bundle allExtras = new Bundle();
-            PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
-            if (userState.isSuspended()) {
-                for (int i = 0; i < userState.getSuspendParams().size(); i++) {
-                    final SuspendParams params = userState.getSuspendParams().valueAt(i);
-                    if (params != null && params.getLauncherExtras() != null) {
-                        allExtras.putAll(params.getLauncherExtras());
-                    }
-                }
-            }
-            return (allExtras.size() > 0) ? allExtras : null;
+            return mSuspendPackageHelper.getSuspendedPackageLauncherExtras(
+                    packageName, userId, Binder.getCallingUid());
         }
 
         @Override
         public boolean isPackageSuspended(String packageName, int userId) {
-            final PackageStateInternal packageState = getPackageStateInternal(packageName);
-            return packageState != null && packageState.getUserStateOrDefault(userId)
-                    .isSuspended();
+            return mSuspendPackageHelper.isPackageSuspended(
+                    packageName, userId, Binder.getCallingUid());
         }
 
         @Override
         public void removeAllNonSystemPackageSuspensions(int userId) {
             final String[] allPackages = mComputer.getAllAvailablePackageNames();
-            PackageManagerService.this.removeSuspensionsBySuspendingPackage(allPackages,
+            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(allPackages,
                     (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
                     userId);
         }
 
         @Override
         public void removeNonSystemPackageSuspensions(String packageName, int userId) {
-            PackageManagerService.this.removeSuspensionsBySuspendingPackage(
+            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
                     new String[]{packageName},
                     (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
                     userId);
@@ -7481,46 +7112,15 @@
 
         @Override
         public String getSuspendingPackage(String suspendedPackage, int userId) {
-            final PackageStateInternal packageState = getPackageStateInternal(suspendedPackage);
-            if (packageState == null) {
-                return  null;
-            }
-
-            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
-            if (!userState.isSuspended()) {
-                return null;
-            }
-
-            String suspendingPackage = null;
-            for (int i = 0; i < userState.getSuspendParams().size(); i++) {
-                suspendingPackage = userState.getSuspendParams().keyAt(i);
-                if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
-                    return suspendingPackage;
-                }
-            }
-            return suspendingPackage;
+            return mSuspendPackageHelper.getSuspendingPackage(
+                    suspendedPackage, userId, Binder.getCallingUid());
         }
 
         @Override
         public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
                 String suspendingPackage, int userId) {
-            final PackageStateInternal packageState = getPackageStateInternal(suspendedPackage);
-            if (packageState == null) {
-                return  null;
-            }
-
-            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
-            if (!userState.isSuspended()) {
-                return null;
-            }
-
-            final ArrayMap<String, SuspendParams> suspendParamsMap = userState.getSuspendParams();
-            if (suspendParamsMap == null) {
-                return null;
-            }
-
-            final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
-            return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
+            return mSuspendPackageHelper.getSuspendedDialogInfo(
+                    suspendedPackage, suspendingPackage, userId, Binder.getCallingUid());
         }
 
         @Override
@@ -9084,6 +8684,8 @@
                 return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) };
             case PackageManagerInternal.PACKAGE_INSTALLER:
                 return mComputer.filterOnlySystemPackages(mRequiredInstallerPackage);
+            case PackageManagerInternal.PACKAGE_UNINSTALLER:
+                return mComputer.filterOnlySystemPackages(mRequiredUninstallerPackage);
             case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
                 return mComputer.filterOnlySystemPackages(mSetupWizardPackage);
             case PackageManagerInternal.PACKAGE_SYSTEM:
@@ -9099,6 +8701,8 @@
                 return mComputer.filterOnlySystemPackages(mConfiguratorPackage);
             case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
                 return mComputer.filterOnlySystemPackages(mIncidentReportApproverPackage);
+            case PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION:
+                return mComputer.filterOnlySystemPackages(mAmbientContextDetectionPackage);
             case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
                 return mComputer.filterOnlySystemPackages(mAppPredictionServicePackage);
             case PackageManagerInternal.PACKAGE_COMPANION:
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index a1acc38..db60686 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -91,6 +91,7 @@
     public ViewCompiler viewCompiler;
     public @Nullable String retailDemoPackage;
     public @Nullable String recentsPackage;
+    public @Nullable String ambientContextDetectionPackage;
     public ComponentName resolveComponentName;
     public ArrayMap<String, AndroidPackage> packages;
     public boolean enableFreeCacheV2;
@@ -111,4 +112,5 @@
     public PreferredActivityHelper preferredActivityHelper;
     public ResolveIntentHelper resolveIntentHelper;
     public DexOptHelper dexOptHelper;
+    public SuspendPackageHelper suspendPackageHelper;
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 898f673..e03cf0a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -51,7 +51,6 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Binder;
@@ -92,6 +91,7 @@
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import dalvik.system.VMRuntime;
@@ -560,8 +560,8 @@
 
             if (!match) {
                 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                        "Package " + packageName +
-                        " signatures do not match previously installed version; ignoring!");
+                        "Existing package " + packageName
+                                + " signatures do not match newer version; ignoring!");
             }
         }
         // Check for shared user signatures
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 0564e85..fd2256f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -230,6 +230,8 @@
                     return runDexoptJob();
                 case "cancel-bg-dexopt-job":
                     return cancelBgDexOptJob();
+                case "delete-dexopt":
+                    return runDeleteDexOpt();
                 case "dump-profiles":
                     return runDumpProfiles();
                 case "snapshot-profile":
@@ -1917,6 +1919,24 @@
         return 0;
     }
 
+    private int runDeleteDexOpt() throws RemoteException {
+        PrintWriter pw = getOutPrintWriter();
+        String packageName = getNextArg();
+        if (TextUtils.isEmpty(packageName)) {
+            pw.println("Error: no package name");
+            return 1;
+        }
+        long freedBytes = LocalServices.getService(
+                PackageManagerInternal.class).deleteOatArtifactsOfPackage(packageName);
+        if (freedBytes < 0) {
+            pw.println("Error: delete failed");
+            return 1;
+        }
+        pw.println("Success: freed " + freedBytes + " bytes");
+        Slog.i(TAG, "delete-dexopt " + packageName + " ,freed " + freedBytes + " bytes");
+        return 0;
+    }
+
     private int runDumpProfiles() throws RemoteException {
         String packageName = getNextArg();
         mInterface.dumpProfiles(packageName);
@@ -2745,7 +2765,7 @@
         IUserManager um = IUserManager.Stub.asInterface(
                 ServiceManager.getService(Context.USER_SERVICE));
         if (setEphemeralIfInUse) {
-            return removeUserOrSetEphemeral(um, userId);
+            return removeUserWhenPossible(um, userId);
         } else {
             final boolean success = wait ? removeUserAndWait(um, userId) : removeUser(um, userId);
             if (success) {
@@ -2808,15 +2828,15 @@
         }
     }
 
-    private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId)
+    private int removeUserWhenPossible(IUserManager um, @UserIdInt int userId)
             throws RemoteException {
         Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use.");
-        int result = um.removeUserOrSetEphemeral(userId, /* evenWhenDisallowed= */ false);
+        int result = um.removeUserWhenPossible(userId, /* overrideDevicePolicy= */ false);
         switch (result) {
             case UserManager.REMOVE_RESULT_REMOVED:
                 getOutPrintWriter().printf("Success: user %d removed\n", userId);
                 return 0;
-            case UserManager.REMOVE_RESULT_SET_EPHEMERAL:
+            case UserManager.REMOVE_RESULT_DEFERRED:
                 getOutPrintWriter().printf("Success: user %d set as ephemeral\n", userId);
                 return 0;
             case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED:
@@ -4000,6 +4020,9 @@
         pw.println("  force-dex-opt PACKAGE");
         pw.println("    Force immediate execution of dex opt for the given PACKAGE.");
         pw.println("");
+        pw.println("  delete-dexopt PACKAGE");
+        pw.println("    Delete dex optimization results for the given PACKAGE.");
+        pw.println("");
         pw.println("  bg-dexopt-job");
         pw.println("    Execute the background optimizations immediately.");
         pw.println("    Note that the command only runs the background optimizer logic. It may");
diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java
index ee9ed3b..2055537 100644
--- a/services/core/java/com/android/server/pm/PackageProperty.java
+++ b/services/core/java/com/android/server/pm/PackageProperty.java
@@ -27,7 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageManager.PropertyLocation;
-import android.content.pm.parsing.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedComponent;
 import android.os.Binder;
 import android.os.UserHandle;
 import android.util.ArrayMap;
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index a532fe3..9bfb7d1 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -28,7 +28,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.rollback.RollbackInfo;
@@ -202,7 +202,7 @@
     }
 
     private void onVerificationSuccess(StagingManager.StagedSession session, Callback callback) {
-        callback.onResult(SessionInfo.STAGED_SESSION_NO_ERROR, null);
+        callback.onResult(SessionInfo.SESSION_NO_ERROR, null);
     }
 
     private void onVerificationFailure(StagingManager.StagedSession session, Callback callback,
@@ -298,7 +298,7 @@
             // Failed to get hold of StorageManager
             Slog.e(TAG, "Failed to get hold of StorageManager", e);
             throw new PackageManagerException(
-                    SessionInfo.STAGED_SESSION_UNKNOWN,
+                    SessionInfo.SESSION_UNKNOWN_ERROR,
                     "Failed to get hold of StorageManager");
         }
         // Proactively mark session as ready before calling apexd. Although this call order
@@ -336,7 +336,7 @@
         final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
                 input.reset(), apexPath, minSignatureScheme);
         if (newResult.isError()) {
-            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+            throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
                     "Failed to parse APEX package " + apexPath + " : "
                             + newResult.getException(), newResult.getException());
         }
@@ -355,7 +355,7 @@
                 input.reset(), existingApexPkg.applicationInfo.sourceDir,
                 SigningDetails.SignatureSchemeVersion.JAR);
         if (existingResult.isError()) {
-            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+            throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
                     "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
                             + " : " + existingResult.getException(), existingResult.getException());
         }
@@ -369,7 +369,7 @@
             return;
         }
 
-        throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+        throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
                 "APK-container signature of APEX package " + packageName + " with version "
                         + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
                         + " compatible with the one currently installed on device");
@@ -412,11 +412,11 @@
                 packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
                 if (packageInfo == null) {
                     throw new PackageManagerException(
-                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                            SessionInfo.SESSION_VERIFICATION_FAILED,
                             "Unable to generate package info: " + apexInfo.modulePath);
                 }
             } catch (PackageManagerException e) {
-                throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
                         "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
             }
             result.add(packageInfo);
@@ -438,7 +438,7 @@
             }
         }
         throw new PackageManagerException(
-                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                SessionInfo.SESSION_VERIFICATION_FAILED,
                 "Could not find rollback id for commit session: " + sessionId);
     }
 
@@ -546,7 +546,7 @@
         try {
             checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint());
         } catch (RemoteException e) {
-            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+            throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
                     "Can't query fs-checkpoint status : " + e);
         }
     }
@@ -562,7 +562,7 @@
         }
         if (!supportsCheckpoint && activeSessions > 1) {
             throw new PackageManagerException(
-                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                    SessionInfo.SESSION_VERIFICATION_FAILED,
                     "Cannot stage multiple sessions without checkpoint support");
         }
     }
@@ -593,13 +593,13 @@
                     // will be deleted.
                 }
                 stagedSession.setSessionFailed(
-                        SessionInfo.STAGED_SESSION_CONFLICT,
+                        SessionInfo.SESSION_CONFLICT,
                         "Session was failed by rollback session: " + session.sessionId());
                 Slog.i(TAG, "Session " + stagedSession.sessionId() + " is marked failed due to "
                         + "rollback session: " + session.sessionId());
             } else if (!isRollback(session) && isRollback(stagedSession)) {
                 throw new PackageManagerException(
-                        SessionInfo.STAGED_SESSION_CONFLICT,
+                        SessionInfo.SESSION_CONFLICT,
                         "Session was failed by rollback session: " + stagedSession.sessionId());
 
             }
@@ -622,7 +622,7 @@
         final String packageName = child.getPackageName();
         if (packageName == null) {
             throw new PackageManagerException(
-                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                    SessionInfo.SESSION_VERIFICATION_FAILED,
                     "Cannot stage session " + child.sessionId() + " with package name null");
         }
         for (StagingManager.StagedSession stagedSession : mStagedSessions) {
@@ -634,14 +634,14 @@
                 if (stagedSession.getCommittedMillis() < parent.getCommittedMillis()) {
                     // Fail the session committed later when there are overlapping packages
                     throw new PackageManagerException(
-                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                            SessionInfo.SESSION_VERIFICATION_FAILED,
                             "Package: " + packageName + " in session: "
                                     + child.sessionId()
                                     + " has been staged already by session: "
                                     + stagedSession.sessionId());
                 } else {
                     stagedSession.setSessionFailed(
-                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                            SessionInfo.SESSION_VERIFICATION_FAILED,
                             "Package: " + packageName + " in session: "
                                     + stagedSession.sessionId()
                                     + " has been staged already by session: "
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5a25004..f3d88ed 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -28,7 +28,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -42,8 +42,8 @@
 
 final class ReconcilePackageUtils {
     public static Map<String, ReconciledPackage> reconcilePackages(
-            final ReconcileRequest request, KeySetManagerService ksms,
-            PackageManagerServiceInjector injector)
+            final ReconcileRequest request, SharedLibrariesImpl sharedLibraries,
+            KeySetManagerService ksms)
             throws ReconcileFailure {
         final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
 
@@ -67,11 +67,10 @@
 
             // in the first pass, we'll build up the set of incoming shared libraries
             final List<SharedLibraryInfo> allowedSharedLibInfos =
-                    SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
-                            request.mSharedLibrarySource);
+                    sharedLibraries.getAllowedSharedLibInfos(scanResult);
             if (allowedSharedLibInfos != null) {
                 for (SharedLibraryInfo info : allowedSharedLibInfos) {
-                    if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+                    if (!SharedLibraryUtils.addSharedLibraryToPackageVersionMap(
                             incomingSharedLibraries, info)) {
                         throw new ReconcileFailure("Shared Library " + info.getName()
                                 + " is being installed twice in this set!");
@@ -113,7 +112,8 @@
 
             final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
             final PackageSetting lastStaticSharedLibSetting =
-                    request.mLastStaticSharedLibSettings.get(installPackageName);
+                    scanResult.mStaticSharedLibraryInfo == null ? null
+                            : sharedLibraries.getStaticSharedLibLatestVersionSetting(scanResult);
             final PackageSetting signatureCheckPs =
                     (prepareResult != null && lastStaticSharedLibSetting != null)
                             ? lastStaticSharedLibSetting
@@ -264,11 +264,9 @@
             }
             try {
                 result.get(installPackageName).mCollectedSharedLibraryInfos =
-                        SharedLibraryHelper.collectSharedLibraryInfos(
-                                scanResult.mRequest.mParsedPackage,
-                                combinedPackages, request.mSharedLibrarySource,
-                                incomingSharedLibraries, injector.getCompatibility());
-
+                        sharedLibraries.collectSharedLibraryInfos(
+                                scanResult.mRequest.mParsedPackage, combinedPackages,
+                                incomingSharedLibraries);
             } catch (PackageManagerException e) {
                 throw new ReconcileFailure(e.error, e.getMessage());
             }
diff --git a/services/core/java/com/android/server/pm/ReconcileRequest.java b/services/core/java/com/android/server/pm/ReconcileRequest.java
index 3188138..9e4e986 100644
--- a/services/core/java/com/android/server/pm/ReconcileRequest.java
+++ b/services/core/java/com/android/server/pm/ReconcileRequest.java
@@ -16,10 +16,7 @@
 
 package com.android.server.pm;
 
-import android.content.pm.SharedLibraryInfo;
-
 import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.utils.WatchedLongSparseArray;
 
 import java.util.Collections;
 import java.util.Map;
@@ -37,38 +34,29 @@
     public final Map<String, ScanResult> mScannedPackages;
 
     public final Map<String, AndroidPackage> mAllPackages;
-    public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibrarySource;
     public final Map<String, InstallArgs> mInstallArgs;
     public final Map<String, PackageInstalledInfo> mInstallResults;
     public final Map<String, PrepareResult> mPreparedPackages;
     public final Map<String, Settings.VersionInfo> mVersionInfos;
-    public final Map<String, PackageSetting> mLastStaticSharedLibSettings;
 
     ReconcileRequest(Map<String, ScanResult> scannedPackages,
             Map<String, InstallArgs> installArgs,
             Map<String, PackageInstalledInfo> installResults,
             Map<String, PrepareResult> preparedPackages,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
             Map<String, AndroidPackage> allPackages,
-            Map<String, Settings.VersionInfo> versionInfos,
-            Map<String, PackageSetting> lastStaticSharedLibSettings) {
+            Map<String, Settings.VersionInfo> versionInfos) {
         mScannedPackages = scannedPackages;
         mInstallArgs = installArgs;
         mInstallResults = installResults;
         mPreparedPackages = preparedPackages;
-        mSharedLibrarySource = sharedLibrarySource;
         mAllPackages = allPackages;
         mVersionInfos = versionInfos;
-        mLastStaticSharedLibSettings = lastStaticSharedLibSettings;
     }
 
     ReconcileRequest(Map<String, ScanResult> scannedPackages,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
             Map<String, AndroidPackage> allPackages,
-            Map<String, Settings.VersionInfo> versionInfos,
-            Map<String, PackageSetting> lastStaticSharedLibSettings) {
+            Map<String, Settings.VersionInfo> versionInfos) {
         this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
-                Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
-                lastStaticSharedLibSettings);
+                Collections.emptyMap(), allPackages, versionInfos);
     }
 }
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index d60d019..7e898cb 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -29,7 +29,6 @@
 
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
-import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.os.UserHandle;
 import android.os.incremental.IncrementalManager;
 import android.util.Log;
@@ -43,6 +42,7 @@
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
 
 import java.io.File;
 import java.util.Collections;
@@ -330,8 +330,7 @@
         if (removedAppId != -1) {
             // A user ID was deleted here. Go through all users and remove it
             // from KeyStore.
-            mPm.removeKeystoreDataIfNeeded(
-                    mUserManagerInternal, UserHandle.USER_ALL, removedAppId);
+            mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, removedAppId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 6d2ec0d..79ab563 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -52,13 +52,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Build;
@@ -85,6 +78,13 @@
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import dalvik.system.VMRuntime;
 
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
index 482b79c..34abdb1 100644
--- a/services/core/java/com/android/server/pm/ScanRequest.java
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -18,12 +18,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 /** A package to be scanned */
 @VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 21df5a9..45837717 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -50,13 +50,13 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.PackageUserStateUtils;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -4863,11 +4863,11 @@
             pw.print(userState.isInstantApp());
             pw.print(" virtual=");
             pw.println(userState.isVirtualPreload());
-            pw.print(" installReason=");
+            pw.print("      installReason=");
             pw.println(userState.getInstallReason());
 
             final PackageUserStateInternal pus = ps.readUserState(user.id);
-            pw.print(" firstInstallTime=");
+            pw.print("      firstInstallTime=");
             date.setTime(pus.getFirstInstallTime());
             pw.println(sdf.format(date));
 
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index f38ae77..2227a78 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -16,19 +16,27 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.pm.PackageManagerService.TAG;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.VersionedPackage;
+import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.service.pm.PackageServiceDumpProto;
 import android.util.ArraySet;
+import android.util.PackageUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -36,8 +44,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
 import com.android.server.utils.Watchable;
@@ -47,10 +57,13 @@
 import com.android.server.utils.WatchedLongSparseArray;
 import com.android.server.utils.Watcher;
 
+import libcore.util.HexEncoding;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -62,6 +75,24 @@
  * Current known shared libraries on the device.
  */
 public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable {
+    private static final boolean DEBUG_SHARED_LIBRARIES = false;
+
+    /**
+     * Apps targeting Android S and above need to declare dependencies to the public native
+     * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+     * in its {@code AndroidManifest.xml}.
+     *
+     * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+     * the package manager rejects to install the app. The dependency can be specified as optional
+     * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+     * dependency doesn't stop the installation.
+     * <p>Once installed, an app is provided with only the native shared libraries that are
+     * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+     * in the app manifest will fail even if it actually exists on the device.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
 
     // TODO(b/200588896): remove PMS dependency
     private final PackageManagerService mPm;
@@ -353,7 +384,7 @@
      * @return The latest version of shared library info.
      */
     @GuardedBy("mPm.mLock")
-    @Nullable SharedLibraryInfo getLatestSharedLibraVersionLPr(@NonNull AndroidPackage pkg) {
+    @Nullable SharedLibraryInfo getLatestStaticSharedLibraVersionLPr(@NonNull AndroidPackage pkg) {
         WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
                 pkg.getStaticSharedLibName());
         if (versionedLib == null) {
@@ -381,11 +412,11 @@
      * @return The package setting that represents the latest version of shared library info.
      */
     @Nullable
-    PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
+    PackageSetting getStaticSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
         PackageSetting sharedLibPackage = null;
         synchronized (mPm.mLock) {
             final SharedLibraryInfo latestSharedLibraVersionLPr =
-                    getLatestSharedLibraVersionLPr(scanResult.mRequest.mParsedPackage);
+                    getLatestStaticSharedLibraVersionLPr(scanResult.mRequest.mParsedPackage);
             if (latestSharedLibraVersionLPr != null) {
                 sharedLibPackage = mPm.mSettings.getPackageLPr(
                         latestSharedLibraVersionLPr.getPackageName());
@@ -493,10 +524,8 @@
             @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
             @NonNull Map<String, AndroidPackage> availablePackages)
             throws PackageManagerException {
-        final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
-                SharedLibraryHelper.collectSharedLibraryInfos(
-                        pkgSetting.getPkg(), availablePackages, mSharedLibraries,
-                        null /* newLibraries */, mInjector.getCompatibility());
+        final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
+                pkgSetting.getPkg(), availablePackages, null /* newLibraries */);
         executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting,
                 sharedLibraryInfos, mPm.mUserManager.getUserIds());
     }
@@ -735,6 +764,234 @@
     }
 
     /**
+     * Compare the newly scanned package with current system state to see which of its declared
+     * shared libraries should be allowed to be added to the system.
+     */
+    List<SharedLibraryInfo> getAllowedSharedLibInfos(ScanResult scanResult) {
+        // Let's used the parsed package as scanResult.pkgSetting may be null
+        final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+        if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
+                && scanResult.mDynamicSharedLibraryInfos == null) {
+            return null;
+        }
+
+        // Any app can add new SDKs and static shared libraries.
+        if (scanResult.mSdkSharedLibraryInfo != null) {
+            return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
+        }
+        if (scanResult.mStaticSharedLibraryInfo != null) {
+            return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
+        }
+        final boolean hasDynamicLibraries = parsedPackage.isSystem()
+                && scanResult.mDynamicSharedLibraryInfos != null;
+        if (!hasDynamicLibraries) {
+            return null;
+        }
+        final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
+                .isUpdatedSystemApp();
+        // We may not yet have disabled the updated package yet, so be sure to grab the
+        // current setting if that's the case.
+        final PackageSetting updatedSystemPs = isUpdatedSystemApp
+                ? scanResult.mRequest.mDisabledPkgSetting == null
+                ? scanResult.mRequest.mOldPkgSetting
+                : scanResult.mRequest.mDisabledPkgSetting
+                : null;
+        if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
+                || updatedSystemPs.getPkg().getLibraryNames() == null)) {
+            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                    + " declares libraries that are not declared on the system image; skipping");
+            return null;
+        }
+        final ArrayList<SharedLibraryInfo> infos =
+                new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
+        for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
+            final String name = info.getName();
+            if (isUpdatedSystemApp) {
+                // New library entries can only be added through the
+                // system image.  This is important to get rid of a lot
+                // of nasty edge cases: for example if we allowed a non-
+                // system update of the app to add a library, then uninstalling
+                // the update would make the library go away, and assumptions
+                // we made such as through app install filtering would now
+                // have allowed apps on the device which aren't compatible
+                // with it.  Better to just have the restriction here, be
+                // conservative, and create many fewer cases that can negatively
+                // impact the user experience.
+                if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
+                    Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                            + " declares library " + name
+                            + " that is not declared on system image; skipping");
+                    continue;
+                }
+            }
+            synchronized (mPm.mLock) {
+                if (getSharedLibraryInfo(name, SharedLibraryInfo.VERSION_UNDEFINED) != null) {
+                    Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
+                            + name + " that already exists; skipping");
+                    continue;
+                }
+            }
+            infos.add(info);
+        }
+        return infos;
+    }
+
+    /**
+     * Collects shared library infos that are being used by the given package.
+     *
+     * @param pkg The package using shared libraries.
+     * @param availablePackages The available packages which are installed and being installed,
+     * @param newLibraries Shared libraries defined by packages which are being installed.
+     * @return A list of shared library infos
+     */
+    ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(@Nullable AndroidPackage pkg,
+            @NonNull Map<String, AndroidPackage> availablePackages,
+            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+            throws PackageManagerException {
+        if (pkg == null) {
+            return null;
+        }
+        final PlatformCompat platformCompat = mInjector.getCompatibility();
+        // The collection used here must maintain the order of addition (so
+        // that libraries are searched in the correct order) and must have no
+        // duplicates.
+        ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
+        if (!pkg.getUsesLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
+                    pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
+                    availablePackages, newLibraries);
+        }
+        if (!pkg.getUsesStaticLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
+                    pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
+                    pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
+                    usesLibraryInfos, availablePackages, newLibraries);
+        }
+        if (!pkg.getUsesOptionalLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
+                    pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
+                    usesLibraryInfos, availablePackages, newLibraries);
+        }
+        if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
+                pkg.getPackageName(), pkg.getTargetSdkVersion())) {
+            if (!pkg.getUsesNativeLibraries().isEmpty()) {
+                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+                        null, pkg.getPackageName(), "native shared", true,
+                        pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+                        newLibraries);
+            }
+            if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+                        null, null, pkg.getPackageName(), "native shared", false,
+                        pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+                        newLibraries);
+            }
+        }
+        if (!pkg.getUsesSdkLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
+                    pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
+                    pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
+                    availablePackages, newLibraries);
+        }
+        return usesLibraryInfos;
+    }
+
+    private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
+            @NonNull List<String> requestedLibraries,
+            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
+            @NonNull String packageName, @NonNull String libraryType, boolean required,
+            int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+            @NonNull final Map<String, AndroidPackage> availablePackages,
+            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+            throws PackageManagerException {
+        final int libCount = requestedLibraries.size();
+        for (int i = 0; i < libCount; i++) {
+            final String libName = requestedLibraries.get(i);
+            final long libVersion = requiredVersions != null ? requiredVersions[i]
+                    : SharedLibraryInfo.VERSION_UNDEFINED;
+            final SharedLibraryInfo libraryInfo;
+            synchronized (mPm.mLock) {
+                libraryInfo = SharedLibraryUtils.getSharedLibraryInfo(
+                        libName, libVersion, mSharedLibraries, newLibraries);
+            }
+            if (libraryInfo == null) {
+                if (required) {
+                    throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                            "Package " + packageName + " requires unavailable " + libraryType
+                                    + " library " + libName + "; failing!");
+                } else if (DEBUG_SHARED_LIBRARIES) {
+                    Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
+                            + " library " + libName + "; ignoring!");
+                }
+            } else {
+                if (requiredVersions != null && requiredCertDigests != null) {
+                    if (libraryInfo.getLongVersion() != requiredVersions[i]) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                "Package " + packageName + " requires unavailable " + libraryType
+                                        + " library " + libName + " version "
+                                        + libraryInfo.getLongVersion() + "; failing!");
+                    }
+                    AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
+                    SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
+                    if (libPkg == null) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                "Package " + packageName + " requires unavailable " + libraryType
+                                        + " library; failing!");
+                    }
+                    final String[] expectedCertDigests = requiredCertDigests[i];
+                    if (expectedCertDigests.length > 1) {
+                        // For apps targeting O MR1 we require explicit enumeration of all certs.
+                        final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
+                                ? PackageUtils.computeSignaturesSha256Digests(
+                                libPkg.getSignatures())
+                                : PackageUtils.computeSignaturesSha256Digests(
+                                        new Signature[]{libPkg.getSignatures()[0]});
+
+                        // Take a shortcut if sizes don't match. Note that if an app doesn't
+                        // target O we don't parse the "additional-certificate" tags similarly
+                        // how we only consider all certs only for apps targeting O (see above).
+                        // Therefore, the size check is safe to make.
+                        if (expectedCertDigests.length != libCertDigests.length) {
+                            throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                    "Package " + packageName + " requires differently signed "
+                                            + libraryType + " library; failing!");
+                        }
+
+                        // Use a predictable order as signature order may vary
+                        Arrays.sort(libCertDigests);
+                        Arrays.sort(expectedCertDigests);
+
+                        final int certCount = libCertDigests.length;
+                        for (int j = 0; j < certCount; j++) {
+                            if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
+                                throw new PackageManagerException(
+                                        INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                        "Package " + packageName + " requires differently signed "
+                                                + libraryType + " library; failing!");
+                            }
+                        }
+                    } else {
+                        // lib signing cert could have rotated beyond the one expected, check to see
+                        // if the new one has been blessed by the old
+                        byte[] digestBytes = HexEncoding.decode(
+                                expectedCertDigests[0], false /* allowSingleChar */);
+                        if (!libPkg.hasSha256Certificate(digestBytes)) {
+                            throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                    "Package " + packageName + " requires differently signed "
+                                            + libraryType + " library; failing!");
+                        }
+                    }
+                }
+                if (outUsedLibraries == null) {
+                    outUsedLibraries = new ArrayList<>();
+                }
+                outUsedLibraries.add(libraryInfo);
+            }
+        }
+        return outUsedLibraries;
+    }
+
+    /**
      * Dump all shared libraries.
      */
     @GuardedBy("mPm.mLock")
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
deleted file mode 100644
index dd8fad0..0000000
--- a/services/core/java/com/android/server/pm/SharedLibraryHelper.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-
-import static com.android.server.pm.PackageManagerService.TAG;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.os.Build;
-import android.util.PackageUtils;
-import android.util.Slog;
-
-import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedLongSparseArray;
-
-import libcore.util.HexEncoding;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-final class SharedLibraryHelper {
-    private static final boolean DEBUG_SHARED_LIBRARIES = false;
-
-    /**
-     * Apps targeting Android S and above need to declare dependencies to the public native
-     * shared libraries that are defined by the device maker using {@code uses-native-library} tag
-     * in its {@code AndroidManifest.xml}.
-     *
-     * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
-     * the package manager rejects to install the app. The dependency can be specified as optional
-     * using {@code android:required} attribute in the tag, in which case failing to satisfy the
-     * dependency doesn't stop the installation.
-     * <p>Once installed, an app is provided with only the native shared libraries that are
-     * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
-     * in the app manifest will fail even if it actually exists on the device.
-     */
-    @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
-    private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
-
-    /**
-     * Compare the newly scanned package with current system state to see which of its declared
-     * shared libraries should be allowed to be added to the system.
-     */
-    public static List<SharedLibraryInfo> getAllowedSharedLibInfos(
-            ScanResult scanResult,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
-        // Let's used the parsed package as scanResult.pkgSetting may be null
-        final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
-        if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
-                && scanResult.mDynamicSharedLibraryInfos == null) {
-            return null;
-        }
-
-        // Any app can add new SDKs and static shared libraries.
-        if (scanResult.mSdkSharedLibraryInfo != null) {
-            return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
-        }
-        if (scanResult.mStaticSharedLibraryInfo != null) {
-            return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
-        }
-        final boolean hasDynamicLibraries = parsedPackage.isSystem()
-                && scanResult.mDynamicSharedLibraryInfos != null;
-        if (!hasDynamicLibraries) {
-            return null;
-        }
-        final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
-                .isUpdatedSystemApp();
-        // We may not yet have disabled the updated package yet, so be sure to grab the
-        // current setting if that's the case.
-        final PackageSetting updatedSystemPs = isUpdatedSystemApp
-                ? scanResult.mRequest.mDisabledPkgSetting == null
-                ? scanResult.mRequest.mOldPkgSetting
-                : scanResult.mRequest.mDisabledPkgSetting
-                : null;
-        if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
-                || updatedSystemPs.getPkg().getLibraryNames() == null)) {
-            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
-                    + " declares libraries that are not declared on the system image; skipping");
-            return null;
-        }
-        final ArrayList<SharedLibraryInfo> infos =
-                new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
-        for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
-            final String name = info.getName();
-            if (isUpdatedSystemApp) {
-                // New library entries can only be added through the
-                // system image.  This is important to get rid of a lot
-                // of nasty edge cases: for example if we allowed a non-
-                // system update of the app to add a library, then uninstalling
-                // the update would make the library go away, and assumptions
-                // we made such as through app install filtering would now
-                // have allowed apps on the device which aren't compatible
-                // with it.  Better to just have the restriction here, be
-                // conservative, and create many fewer cases that can negatively
-                // impact the user experience.
-                if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
-                    Slog.w(TAG, "Package " + parsedPackage.getPackageName()
-                            + " declares library " + name
-                            + " that is not declared on system image; skipping");
-                    continue;
-                }
-            }
-            if (sharedLibExists(
-                    name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
-                Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
-                        + name + " that already exists; skipping");
-                continue;
-            }
-            infos.add(info);
-        }
-        return infos;
-    }
-
-    public static boolean sharedLibExists(final String name, final long version,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
-        WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
-        return versionedLib != null && versionedLib.indexOfKey(version) >= 0;
-    }
-
-    /**
-     * Returns false if the adding shared library already exists in the map and so could not be
-     * added.
-     */
-    public static boolean addSharedLibraryToPackageVersionMap(
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
-            SharedLibraryInfo library) {
-        final String name = library.getName();
-        if (target.containsKey(name)) {
-            if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
-                // We've already added this non-version-specific library to the map.
-                return false;
-            } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
-                // We've already added this version of a version-specific library to the map.
-                return false;
-            }
-        } else {
-            target.put(name, new WatchedLongSparseArray<>());
-        }
-        target.get(name).put(library.getLongVersion(), library);
-        return true;
-    }
-
-    public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
-            Map<String, AndroidPackage> availablePackages,
-            @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
-            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
-            PlatformCompat platformCompat) throws PackageManagerException {
-        if (pkg == null) {
-            return null;
-        }
-        // The collection used here must maintain the order of addition (so
-        // that libraries are searched in the correct order) and must have no
-        // duplicates.
-        ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
-        if (!pkg.getUsesLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
-                    pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
-                    availablePackages, existingLibraries, newLibraries);
-        }
-        if (!pkg.getUsesStaticLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
-                    pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
-                    pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
-                    usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
-        }
-        if (!pkg.getUsesOptionalLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
-                    pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
-                    usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
-        }
-        if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
-                pkg.getPackageName(), pkg.getTargetSdkVersion())) {
-            if (!pkg.getUsesNativeLibraries().isEmpty()) {
-                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
-                        null, pkg.getPackageName(), "native shared", true,
-                        pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
-                        existingLibraries, newLibraries);
-            }
-            if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
-                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
-                        null, null, pkg.getPackageName(), "native shared", false,
-                        pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
-                        existingLibraries, newLibraries);
-            }
-        }
-        if (!pkg.getUsesSdkLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
-                    pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
-                    pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
-                    availablePackages, existingLibraries, newLibraries);
-        }
-        return usesLibraryInfos;
-    }
-
-    public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
-            @NonNull List<String> requestedLibraries,
-            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
-            @NonNull String packageName, @NonNull String libraryType, boolean required,
-            int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
-            @NonNull final Map<String, AndroidPackage> availablePackages,
-            @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
-            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
-            throws PackageManagerException {
-        final int libCount = requestedLibraries.size();
-        for (int i = 0; i < libCount; i++) {
-            final String libName = requestedLibraries.get(i);
-            final long libVersion = requiredVersions != null ? requiredVersions[i]
-                    : SharedLibraryInfo.VERSION_UNDEFINED;
-            final SharedLibraryInfo libraryInfo =
-                    getSharedLibraryInfo(libName, libVersion, existingLibraries, newLibraries);
-            if (libraryInfo == null) {
-                if (required) {
-                    throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                            "Package " + packageName + " requires unavailable " + libraryType
-                                    + " library " + libName + "; failing!");
-                } else if (DEBUG_SHARED_LIBRARIES) {
-                    Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
-                            + " library " + libName + "; ignoring!");
-                }
-            } else {
-                if (requiredVersions != null && requiredCertDigests != null) {
-                    if (libraryInfo.getLongVersion() != requiredVersions[i]) {
-                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                "Package " + packageName + " requires unavailable " + libraryType
-                                        + " library " + libName + " version "
-                                        + libraryInfo.getLongVersion() + "; failing!");
-                    }
-                    AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
-                    SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
-                    if (libPkg == null) {
-                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                "Package " + packageName + " requires unavailable " + libraryType
-                                        + " library; failing!");
-                    }
-                    final String[] expectedCertDigests = requiredCertDigests[i];
-                    if (expectedCertDigests.length > 1) {
-                        // For apps targeting O MR1 we require explicit enumeration of all certs.
-                        final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
-                                ? PackageUtils.computeSignaturesSha256Digests(
-                                libPkg.getSignatures())
-                                : PackageUtils.computeSignaturesSha256Digests(
-                                        new Signature[]{libPkg.getSignatures()[0]});
-
-                        // Take a shortcut if sizes don't match. Note that if an app doesn't
-                        // target O we don't parse the "additional-certificate" tags similarly
-                        // how we only consider all certs only for apps targeting O (see above).
-                        // Therefore, the size check is safe to make.
-                        if (expectedCertDigests.length != libCertDigests.length) {
-                            throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                    "Package " + packageName + " requires differently signed "
-                                            + libraryType + " library; failing!");
-                        }
-
-                        // Use a predictable order as signature order may vary
-                        Arrays.sort(libCertDigests);
-                        Arrays.sort(expectedCertDigests);
-
-                        final int certCount = libCertDigests.length;
-                        for (int j = 0; j < certCount; j++) {
-                            if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
-                                throw new PackageManagerException(
-                                        INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                        "Package " + packageName + " requires differently signed "
-                                                + libraryType + " library; failing!");
-                            }
-                        }
-                    } else {
-                        // lib signing cert could have rotated beyond the one expected, check to see
-                        // if the new one has been blessed by the old
-                        byte[] digestBytes = HexEncoding.decode(
-                                expectedCertDigests[0], false /* allowSingleChar */);
-                        if (!libPkg.hasSha256Certificate(digestBytes)) {
-                            throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                    "Package " + packageName + " requires differently signed "
-                                            + libraryType + " library; failing!");
-                        }
-                    }
-                }
-                if (outUsedLibraries == null) {
-                    outUsedLibraries = new ArrayList<>();
-                }
-                outUsedLibraries.add(libraryInfo);
-            }
-        }
-        return outUsedLibraries;
-    }
-
-    @Nullable
-    public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
-            @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
-        if (newLibraries != null) {
-            final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
-            SharedLibraryInfo info = null;
-            if (versionedLib != null) {
-                info = versionedLib.get(version);
-            }
-            if (info != null) {
-                return info;
-            }
-        }
-        final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
-        if (versionedLib == null) {
-            return null;
-        }
-        return versionedLib.get(version);
-    }
-
-    public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
-        if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
-            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
-            Set<String> collectedNames = new HashSet<>();
-            for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
-                findSharedLibrariesRecursive(info, retValue, collectedNames);
-            }
-            return retValue;
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
-            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
-        if (!collectedNames.contains(info.getName())) {
-            collectedNames.add(info.getName());
-            collected.add(info);
-
-            if (info.getDependencies() != null) {
-                for (SharedLibraryInfo dep : info.getDependencies()) {
-                    findSharedLibrariesRecursive(dep, collected, collectedNames);
-                }
-            }
-        }
-    }
-
-}
diff --git a/services/core/java/com/android/server/pm/SharedLibraryUtils.java b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
new file mode 100644
index 0000000..274870d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class SharedLibraryUtils {
+
+    /**
+     * Returns false if the adding shared library already exists in the map and so could not be
+     * added.
+     */
+    public static boolean addSharedLibraryToPackageVersionMap(
+            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
+            SharedLibraryInfo library) {
+        final String name = library.getName();
+        if (target.containsKey(name)) {
+            if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
+                // We've already added this non-version-specific library to the map.
+                return false;
+            } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
+                // We've already added this version of a version-specific library to the map.
+                return false;
+            }
+        } else {
+            target.put(name, new WatchedLongSparseArray<>());
+        }
+        target.get(name).put(library.getLongVersion(), library);
+        return true;
+    }
+
+    @Nullable
+    public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
+            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
+        if (newLibraries != null) {
+            final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+            SharedLibraryInfo info = null;
+            if (versionedLib != null) {
+                info = versionedLib.get(version);
+            }
+            if (info != null) {
+                return info;
+            }
+        }
+        final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
+        if (versionedLib == null) {
+            return null;
+        }
+        return versionedLib.get(version);
+    }
+
+    public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
+        if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
+            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
+            Set<String> collectedNames = new HashSet<>();
+            for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
+                findSharedLibrariesRecursive(info, retValue, collectedNames);
+            }
+            return retValue;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
+            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
+        if (!collectedNames.contains(info.getName())) {
+            collectedNames.add(info.getName());
+            collected.add(info);
+
+            if (info.getDependencies() != null) {
+                for (SharedLibraryInfo dep : info.getDependencies()) {
+                    findSharedLibrariesRecursive(dep, collected, collectedNames);
+                }
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 9df0edb2..bc48461 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProcessImpl;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProcessImpl;
 import android.service.pm.PackageServiceDumpProto;
 import android.util.ArrayMap;
 import android.util.ArraySet;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 3d10b6f..15e64df 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -22,6 +22,7 @@
 import android.app.appsearch.AppSearchManager;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.GetByDocumentIdRequest;
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.PutDocumentsRequest;
 import android.app.appsearch.RemoveByDocumentIdRequest;
@@ -145,8 +146,10 @@
     private static final String ATTR_PERSON_IS_IMPORTANT = "is-important";
 
     private static final String NAME_CATEGORIES = "categories";
+    private static final String NAME_CAPABILITY = "capability";
 
     private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
+    private static final String TAG_MAP_XMLUTILS = "map";
     private static final String ATTR_NAME_XMLUTILS = "name";
 
     private static final String KEY_DYNAMIC = "dynamic";
@@ -820,42 +823,6 @@
                 getPinnedByAnyLauncher, si));
     }
 
-    /**
-     * Find all shortcuts that has id matching {@code ids}.
-     */
-    public void findAllByIds(@NonNull final List<ShortcutInfo> result,
-            @NonNull final Collection<String> ids, @Nullable final Predicate<ShortcutInfo> filter,
-            final int cloneFlag) {
-        findAllByIds(result, ids, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
-    }
-
-    /**
-     * Find all shortcuts that has id matching {@code ids}.
-     *
-     * This will also provide a "view" for each launcher -- a non-dynamic shortcut that's not pinned
-     * by the calling launcher will not be included in the result, and also "isPinned" will be
-     * adjusted for the caller too.
-     */
-    public void findAllByIds(@NonNull List<ShortcutInfo> result,
-            @NonNull final Collection<String> ids, @Nullable final Predicate<ShortcutInfo> query,
-            int cloneFlag, @Nullable String callingLauncher, int launcherUserId,
-            boolean getPinnedByAnyLauncher) {
-        if (getPackageInfo().isShadow()) {
-            // Restored and the app not installed yet, so don't return any.
-            return;
-        }
-        final ShortcutService s = mShortcutUser.mService;
-
-        // Set of pinned shortcuts by the calling launcher.
-        final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
-                : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
-                        .getPinnedShortcutIds(getPackageName(), getPackageUserId());
-        for (ShortcutInfo si : mShortcuts.values()) {
-            filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
-                    getPinnedByAnyLauncher, si);
-        }
-    }
-
     private void filter(@NonNull final List<ShortcutInfo> result,
             @Nullable final Predicate<ShortcutInfo> query, final int cloneFlag,
             @Nullable final String callingLauncher,
@@ -1864,6 +1831,12 @@
             }
 
             ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+
+            final Map<String, Map<String, List<String>>> capabilityBindings =
+                    si.getCapabilityBindings();
+            if (capabilityBindings != null && !capabilityBindings.isEmpty()) {
+                XmlUtils.writeMapXml(si.getCapabilityBindings(), NAME_CAPABILITY, out);
+            }
         }
 
         out.endTag(null, TAG_SHORTCUT);
@@ -1996,6 +1969,7 @@
         int backupVersionCode;
         ArraySet<String> categories = null;
         ArrayList<Person> persons = new ArrayList<>();
+        Map<String, Map<String, List<String>>> capabilityBindings = null;
 
         id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
         activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -2064,6 +2038,13 @@
                         }
                     }
                     continue;
+                case TAG_MAP_XMLUTILS:
+                    if (NAME_CAPABILITY.equals(ShortcutService.parseStringAttribute(parser,
+                            ATTR_NAME_XMLUTILS))) {
+                        capabilityBindings = (Map<String, Map<String, List<String>>>)
+                                XmlUtils.readValueXml(parser, new String[1]);
+                    }
+                    continue;
             }
             throw ShortcutService.throwForInvalidTag(depth, tag);
         }
@@ -2099,7 +2080,7 @@
                 rank, extras, lastChangedTimestamp, flags,
                 iconResId, iconResName, bitmapPath, iconUri,
                 disabledReason, persons.toArray(new Person[persons.size()]), locusId,
-                splashScreenThemeResName);
+                splashScreenThemeResName, capabilityBindings);
     }
 
     private static Intent parseIntent(TypedXmlPullParser parser)
@@ -2411,6 +2392,25 @@
                 })));
     }
 
+    void getShortcutByIdsAsync(@NonNull final Set<String> ids,
+            @NonNull final Consumer<List<ShortcutInfo>> cb) {
+        if (!isAppSearchEnabled()) {
+            cb.accept(Collections.emptyList());
+            return;
+        }
+        runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+            session.getByDocumentId(new GetByDocumentIdRequest.Builder(getPackageName())
+                    .addIds(ids).build(), mShortcutUser.mExecutor, result -> {
+                    final List<ShortcutInfo> ret = result.getSuccesses().values()
+                            .stream().map(doc ->
+                                    new AppSearchShortcutInfo(doc)
+                                            .toShortcutInfo(mShortcutUser.getUserId()))
+                            .collect(Collectors.toList());
+                    cb.accept(ret);
+                });
+        }));
+    }
+
     private void removeShortcutAsync(@NonNull final String... id) {
         Objects.requireNonNull(id);
         removeShortcutAsync(Arrays.asList(id));
@@ -2444,9 +2444,8 @@
         }
         if (ShortcutService.DEBUG_REBOOT) {
             Slog.d(TAG, "Saving shortcuts async for user=" + mShortcutUser.getUserId()
-                    + " pkg=" + getPackageName() + " ids=["
-                    + shortcuts.stream().map(ShortcutInfo::getId)
-                    .collect(Collectors.joining(",")) + "]");
+                    + " pkg=" + getPackageName() + " ids=" + shortcuts.stream()
+                    .map(ShortcutInfo::getId).collect(Collectors.joining(",", "[", "]")));
         }
         runAsSystem(() -> fromAppSearch().thenAccept(session -> {
             if (shortcuts.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index b86c50b..63f1f2d 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -459,7 +459,8 @@
                 disabledReason,
                 null /* persons */,
                 null /* locusId */,
-                splashScreenThemeResName);
+                splashScreenThemeResName,
+                null /* capabilityBindings */);
     }
 
     private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a482f9a..057f8de 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -153,6 +153,7 @@
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * TODO:
@@ -2957,13 +2958,8 @@
 
             final Predicate<ShortcutInfo> filter = getFilterFromQuery(ids, locusIds, changedSince,
                     componentName, queryFlags, getPinnedByAnyLauncher);
-            if (ids != null && !ids.isEmpty()) {
-                p.findAllByIds(ret, ids, filter, cloneFlag, callingPackage, launcherUserId,
+            p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
                         getPinnedByAnyLauncher);
-            } else {
-                p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
-                        getPinnedByAnyLauncher);
-            }
         }
 
         private Predicate<ShortcutInfo> getFilterFromQuery(@Nullable ArraySet<String> ids,
@@ -3010,6 +3006,51 @@
         }
 
         @Override
+        public void getShortcutsAsync(int launcherUserId,
+                @NonNull String callingPackage, long changedSince,
+                @Nullable String packageName, @Nullable List<String> shortcutIds,
+                @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
+                int queryFlags, int userId, int callingPid, int callingUid,
+                @NonNull AndroidFuture<List<ShortcutInfo>> cb) {
+            final List<ShortcutInfo> ret = getShortcuts(launcherUserId, callingPackage,
+                    changedSince, packageName, shortcutIds, locusIds, componentName, queryFlags,
+                    userId, callingPid, callingUid);
+            if (shortcutIds == null || packageName == null || ret.size() >= shortcutIds.size()) {
+                // skip persistence layer if not querying by id in a specific package or all
+                // shortcuts have already been found.
+                cb.complete(ret);
+                return;
+            }
+            final ShortcutPackage p;
+            synchronized (mLock) {
+                p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
+            }
+            if (p == null) {
+                cb.complete(ret);
+                return; // Bail-out directly if package doesn't exist.
+            }
+            // fetch remaining shortcuts from persistence layer
+            final ArraySet<String> ids = new ArraySet<>(shortcutIds);
+            // remove the ids that are already fetched
+            ret.stream().map(ShortcutInfo::getId).collect(Collectors.toList()).forEach(ids::remove);
+
+            int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
+            if ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0) {
+                flags = ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+            } else if ((queryFlags & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+                flags &= ~ShortcutInfo.CLONE_REMOVE_PERSON;
+            }
+            final int cloneFlag = flags;
+
+            p.getShortcutByIdsAsync(ids, shortcuts -> {
+                if (shortcuts != null) {
+                    shortcuts.stream().map(si -> si.clone(cloneFlag)).forEach(ret::add);
+                }
+                cb.complete(ret);
+            });
+        }
+
+        @Override
         public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
             Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -3047,12 +3088,32 @@
             }
 
             final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
-            p.findAllByIds(list, Collections.singletonList(shortcutId),
-                    (ShortcutInfo si) -> shortcutId.equals(si.getId()),
+            p.findAll(list, (ShortcutInfo si) -> shortcutId.equals(si.getId()),
                     /* clone flags=*/ 0, callingPackage, launcherUserId, getPinnedByAnyLauncher);
             return list.size() == 0 ? null : list.get(0);
         }
 
+        private void getShortcutInfoAsync(
+                int launcherUserId, @NonNull String packageName, @NonNull String shortcutId,
+                int userId, @NonNull Consumer<ShortcutInfo> cb) {
+            Preconditions.checkStringNotEmpty(packageName, "packageName");
+            Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+
+            throwIfUserLockedL(userId);
+            throwIfUserLockedL(launcherUserId);
+
+            final ShortcutPackage p;
+            synchronized (mLock) {
+                p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
+            }
+            if (p == null) {
+                cb.accept(null);
+                return;
+            }
+            p.getShortcutByIdsAsync(Collections.singleton(shortcutId), shortcuts ->
+                    cb.accept(shortcuts == null || shortcuts.isEmpty() ? null : shortcuts.get(0)));
+        }
+
         @Override
         public void pinShortcuts(int launcherUserId,
                 @NonNull String callingPackage, @NonNull String packageName,
@@ -3237,6 +3298,48 @@
         }
 
         @Override
+        public void createShortcutIntentsAsync(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull String shortcutId, int userId, int callingPid,
+                int callingUid, @NonNull AndroidFuture<Intent[]> cb) {
+            // Calling permission must be checked by LauncherAppsImpl.
+            Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
+            Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
+
+            // Check in memory shortcut first
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
+                throwIfUserLockedL(launcherUserId);
+
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave();
+
+                final boolean getPinnedByAnyLauncher =
+                        canSeeAnyPinnedShortcut(callingPackage, launcherUserId,
+                                callingPid, callingUid);
+
+                // Make sure the shortcut is actually visible to the launcher.
+                final ShortcutInfo si = getShortcutInfoLocked(
+                        launcherUserId, callingPackage, packageName, shortcutId, userId,
+                        getPinnedByAnyLauncher);
+                if (si != null) {
+                    if (!si.isEnabled() || !(si.isAlive() || getPinnedByAnyLauncher)) {
+                        Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
+                        cb.complete(null);
+                        return;
+                    }
+                    cb.complete(si.getIntents());
+                    return;
+                }
+            }
+
+            // Otherwise check persisted shortcuts
+            getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
+                cb.complete(si == null ? null : si.getIntents());
+            });
+        }
+
+        @Override
         public void addListener(@NonNull ShortcutChangeListener listener) {
             synchronized (mLock) {
                 mListeners.add(Objects.requireNonNull(listener));
@@ -3326,23 +3429,68 @@
                 }
 
                 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
-                if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
+                if (shortcutInfo == null) {
                     return null;
                 }
-                final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
-                if (path == null) {
-                    Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
-                    return null;
+                return getShortcutIconParcelFileDescriptor(shortcutInfo);
+            }
+        }
+
+        @Override
+        public void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
+                @NonNull String packageName, @NonNull String shortcutId, int userId,
+                @NonNull AndroidFuture<ParcelFileDescriptor> cb) {
+            Objects.requireNonNull(callingPackage, "callingPackage");
+            Objects.requireNonNull(packageName, "packageName");
+            Objects.requireNonNull(shortcutId, "shortcutId");
+
+            // Checks shortcuts in memory first
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
+                throwIfUserLockedL(launcherUserId);
+
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave();
+
+                final ShortcutPackage p = getUserShortcutsLocked(userId)
+                        .getPackageShortcutsIfExists(packageName);
+                if (p == null) {
+                    cb.complete(null);
+                    return;
                 }
-                try {
-                    return ParcelFileDescriptor.open(
-                            new File(path),
-                            ParcelFileDescriptor.MODE_READ_ONLY);
-                } catch (FileNotFoundException e) {
-                    Slog.e(TAG, "Icon file not found: " + path);
-                    return null;
+
+                final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+                if (shortcutInfo != null) {
+                    cb.complete(getShortcutIconParcelFileDescriptor(shortcutInfo));
+                    return;
                 }
             }
+
+            // Otherwise check persisted shortcuts
+            getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
+                cb.complete(getShortcutIconParcelFileDescriptor(si));
+            });
+        }
+
+        @Nullable
+        private ParcelFileDescriptor getShortcutIconParcelFileDescriptor(
+                @Nullable final ShortcutInfo shortcutInfo) {
+            if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
+                return null;
+            }
+            final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
+            if (path == null) {
+                Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
+                return null;
+            }
+            try {
+                return ParcelFileDescriptor.open(
+                        new File(path),
+                        ParcelFileDescriptor.MODE_READ_ONLY);
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "Icon file not found: " + path);
+                return null;
+            }
         }
 
         @Override
@@ -3366,34 +3514,82 @@
                 }
 
                 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
-                if (shortcutInfo == null || !shortcutInfo.hasIconUri()) {
+                if (shortcutInfo == null) {
                     return null;
                 }
-                String uri = shortcutInfo.getIconUri();
-                if (uri == null) {
-                    Slog.w(TAG, "null uri detected in getShortcutIconUri()");
-                    return null;
+                return getShortcutIconUriInternal(launcherUserId, launcherPackage,
+                        packageName, shortcutInfo, userId);
+            }
+        }
+
+        @Override
+        public void getShortcutIconUriAsync(int launcherUserId, @NonNull String launcherPackage,
+                @NonNull String packageName, @NonNull String shortcutId, int userId,
+                @NonNull AndroidFuture<String> cb) {
+            Objects.requireNonNull(launcherPackage, "launcherPackage");
+            Objects.requireNonNull(packageName, "packageName");
+            Objects.requireNonNull(shortcutId, "shortcutId");
+
+            // Checks shortcuts in memory first
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
+                throwIfUserLockedL(launcherUserId);
+
+                getLauncherShortcutsLocked(launcherPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave();
+
+                final ShortcutPackage p = getUserShortcutsLocked(userId)
+                        .getPackageShortcutsIfExists(packageName);
+                if (p == null) {
+                    cb.complete(null);
+                    return;
                 }
 
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    int packageUid = mPackageManagerInternal.getPackageUid(packageName,
-                            PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
-                    // Grant read uri permission to the caller on behalf of the shortcut owner. All
-                    // granted permissions are revoked when the default launcher changes, or when
-                    // device is rebooted.
-                    mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
-                            launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                            userId, launcherUserId);
-                } catch (Exception e) {
-                    Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri,
-                            e);
-                    uri = null;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+                final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+                if (shortcutInfo != null) {
+                    cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
+                            packageName, shortcutInfo, userId));
+                    return;
                 }
-                return uri;
             }
+
+            // Otherwise check persisted shortcuts
+            getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
+                cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
+                        packageName, si, userId));
+            });
+        }
+
+        private String getShortcutIconUriInternal(int launcherUserId,
+                @NonNull String launcherPackage, @NonNull String packageName,
+                @NonNull ShortcutInfo shortcutInfo, int userId) {
+            if (!shortcutInfo.hasIconUri()) {
+                return null;
+            }
+            String uri = shortcutInfo.getIconUri();
+            if (uri == null) {
+                Slog.w(TAG, "null uri detected in getShortcutIconUri()");
+                return null;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                int packageUid = mPackageManagerInternal.getPackageUid(packageName,
+                        PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
+                // Grant read uri permission to the caller on behalf of the shortcut owner. All
+                // granted permissions are revoked when the default launcher changes, or when
+                // device is rebooted.
+                mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
+                        launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                        userId, launcherUserId);
+            } catch (Exception e) {
+                Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri,
+                        e);
+                uri = null;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return uri;
         }
 
         @Override
@@ -5154,7 +5350,7 @@
         }
 
         List<ShortcutInfo> result = new ArrayList<>();
-        ps.findAllByIds(result, resultIds, (ShortcutInfo si) -> resultIds.contains(si.getId()),
+        ps.findAll(result, (ShortcutInfo si) -> resultIds.contains(si.getId()),
                 ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
         return result;
     }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8a6ef6b..29de555 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -284,7 +284,7 @@
             String packageName = apexSession.getPackageName();
             String errorMsg = mApexManager.getApkInApexInstallError(packageName);
             if (errorMsg != null) {
-                throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                throw new PackageManagerException(SessionInfo.SESSION_ACTIVATION_FAILED,
                         "Failed to install apk-in-apex of " + packageName + " : " + errorMsg);
             }
         }
@@ -397,7 +397,7 @@
                 revertMsg += " Reason for revert: " + reasonForRevert;
             }
             Slog.d(TAG, revertMsg);
-            session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
+            session.setSessionFailed(SessionInfo.SESSION_UNKNOWN_ERROR, revertMsg);
             return;
         }
 
@@ -484,7 +484,7 @@
             for (String apkInApex : mApexManager.getApksInApex(packageName)) {
                 if (!apkNames.add(apkInApex)) {
                     throw new PackageManagerException(
-                            SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                            SessionInfo.SESSION_ACTIVATION_FAILED,
                             "Package: " + packageName + " in session: "
                                     + apexSession.sessionId() + " has duplicate apk-in-apex: "
                                     + apkInApex, null);
@@ -511,7 +511,7 @@
             Slog.e(TAG, "Failure to install APK staged session "
                     + session.sessionId() + " [" + errorMessage + "]");
             throw new PackageManagerException(
-                    SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+                    SessionInfo.SESSION_ACTIVATION_FAILED, errorMessage);
         }
     }
 
@@ -665,7 +665,7 @@
             // is upgrading. Fail all the sessions and exit early.
             for (int i = 0; i < sessions.size(); i++) {
                 StagedSession session = sessions.get(i);
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
                         "Build fingerprint has changed");
             }
             return;
@@ -705,7 +705,7 @@
             final ApexSessionInfo apexSession = apexSessions.get(session.sessionId());
             if (apexSession == null || apexSession.isUnknown) {
                 hasFailedApexSession = true;
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "apexd did "
+                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "apexd did "
                         + "not know anything about a staged session supposed to be activated");
                 continue;
             } else if (isApexSessionFailed(apexSession)) {
@@ -721,7 +721,7 @@
                     errorMsg += " Error: " + apexSession.errorMessage;
                 }
                 Slog.d(TAG, errorMsg);
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
+                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, errorMsg);
                 continue;
             } else if (apexSession.isActivated || apexSession.isSuccess) {
                 hasAppliedApexSession = true;
@@ -730,13 +730,13 @@
                 // Apexd did not apply the session for some unknown reason. There is no guarantee
                 // that apexd will install it next time. Safer to proactively mark it as failed.
                 hasFailedApexSession = true;
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
                         "Staged session " + session.sessionId() + " at boot didn't activate nor "
                         + "fail. Marking it as failed anyway.");
             } else {
                 Slog.w(TAG, "Apex session " + session.sessionId() + " is in impossible state");
                 hasFailedApexSession = true;
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
                         "Impossible state");
             }
         }
@@ -756,7 +756,7 @@
                     // Session has been already failed in the loop above.
                     continue;
                 }
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
                         "Another apex session failed");
             }
             return;
@@ -772,7 +772,7 @@
             } catch (Exception e) {
                 Slog.e(TAG, "Staged install failed due to unhandled exception", e);
                 onInstallationFailure(session, new PackageManagerException(
-                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        SessionInfo.SESSION_ACTIVATION_FAILED,
                         "Staged install failed due to unhandled exception: " + e),
                         supportsCheckpoint, needsCheckpoint);
             }
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 1433abd..bb7e55a 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -32,7 +32,7 @@
 import android.content.pm.PackagePartitions;
 import android.content.pm.UserInfo;
 import android.content.pm.VersionedPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.UserHandle;
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
new file mode 100644
index 0000000..f466ca7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+import static android.content.pm.PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER;
+import static android.content.pm.PackageManagerInternal.PACKAGE_UNINSTALLER;
+import static android.content.pm.PackageManagerInternal.PACKAGE_VERIFIER;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal.KnownPackage;
+import android.content.pm.SuspendDialogInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+public final class SuspendPackageHelper {
+    // TODO(b/198166813): remove PMS dependency
+    private final PackageManagerService mPm;
+    private final PackageManagerServiceInjector mInjector;
+
+    private final BroadcastHelper mBroadcastHelper;
+    private final ProtectedPackages mProtectedPackages;
+
+    /**
+     * Constructor for {@link PackageManagerService}.
+     */
+    SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
+            BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages) {
+        mPm = pm;
+        mInjector = injector;
+        mBroadcastHelper = broadcastHelper;
+        mProtectedPackages = protectedPackages;
+    }
+
+    /**
+     * Updates the package to the suspended or unsuspended state.
+     *
+     * @param packageNames The names of the packages to set the suspended status.
+     * @param suspended {@code true} to suspend packages, or {@code false} to unsuspend packages.
+     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+     *                  which will be shared with the apps being suspended. Ignored if
+     *                  {@code suspended} is false.
+     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+     *                       provide which will be shared with the launcher. Ignored if
+     *                       {@code suspended} is false.
+     * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
+     *                   should be shown to the user when they try to launch a suspended app.
+     *                   Ignored if {@code suspended} is false.
+     * @param callingPackage The caller's package name.
+     * @param userId The user where packages reside.
+     * @param callingUid The caller's uid.
+     * @return The names of failed packages.
+     */
+    @Nullable
+    String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+            @Nullable SuspendDialogInfo dialogInfo, @NonNull String callingPackage,
+            int userId, int callingUid) {
+        if (ArrayUtils.isEmpty(packageNames)) {
+            return packageNames;
+        }
+        if (suspended && !isSuspendAllowedForUser(userId, callingUid)) {
+            Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+            return packageNames;
+        }
+
+        final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray changedUids = new IntArray(packageNames.length);
+        final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray modifiedUids = new IntArray(packageNames.length);
+        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+        final boolean[] canSuspend =
+                suspended ? canSuspendPackageForUser(packageNames, userId, callingUid) : null;
+
+        for (int i = 0; i < packageNames.length; i++) {
+            final String packageName = packageNames[i];
+            if (callingPackage.equals(packageName)) {
+                Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+                        + (suspended ? "" : "un") + "suspend itself. Ignoring");
+                unactionedPackages.add(packageName);
+                continue;
+            }
+            final PackageSetting pkgSetting;
+            synchronized (mPm.mLock) {
+                pkgSetting = mPm.mSettings.getPackageLPr(packageName);
+                if (pkgSetting == null
+                        || mPm.shouldFilterApplication(pkgSetting, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageName
+                            + ". Skipping suspending/un-suspending.");
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+            }
+            if (canSuspend != null && !canSuspend[i]) {
+                unactionedPackages.add(packageName);
+                continue;
+            }
+            final boolean packageUnsuspended;
+            final boolean packageModified;
+            synchronized (mPm.mLock) {
+                if (suspended) {
+                    packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
+                            dialogInfo, appExtras, launcherExtras, userId);
+                } else {
+                    packageModified = pkgSetting.removeSuspension(callingPackage, userId);
+                }
+                packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
+            }
+            if (suspended || packageUnsuspended) {
+                changedPackagesList.add(packageName);
+                changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+            }
+            if (packageModified) {
+                modifiedPackagesList.add(packageName);
+                modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+            }
+        }
+
+        if (!changedPackagesList.isEmpty()) {
+            final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+            sendPackagesSuspendedForUser(
+                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+                            : Intent.ACTION_PACKAGES_UNSUSPENDED,
+                    changedPackages, changedUids.toArray(), userId);
+            sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
+            synchronized (mPm.mLock) {
+                mPm.scheduleWritePackageRestrictionsLocked(userId);
+            }
+        }
+        // Send the suspension changed broadcast to ensure suspension state is not stale.
+        if (!modifiedPackagesList.isEmpty()) {
+            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+                    modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+        }
+        return unactionedPackages.toArray(new String[0]);
+    }
+
+    /**
+     * Returns the names in the {@code packageNames} which can not be suspended by the caller.
+     *
+     * @param packageNames The names of packages to check.
+     * @param userId The user where packages reside.
+     * @param callingUid The caller's uid.
+     * @return The names of packages which are Unsuspendable.
+     */
+    @NonNull
+    String[] getUnsuspendablePackagesForUser(@NonNull String[] packageNames, int userId,
+            int callingUid) {
+        if (!isSuspendAllowedForUser(userId, callingUid)) {
+            Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+            return packageNames;
+        }
+        final ArraySet<String> unactionablePackages = new ArraySet<>();
+        final boolean[] canSuspend = canSuspendPackageForUser(packageNames, userId, callingUid);
+        for (int i = 0; i < packageNames.length; i++) {
+            if (!canSuspend[i]) {
+                unactionablePackages.add(packageNames[i]);
+                continue;
+            }
+            synchronized (mPm.mLock) {
+                final PackageSetting ps = mPm.mSettings.getPackageLPr(packageNames[i]);
+                if (ps == null || mPm.shouldFilterApplication(ps, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
+                    unactionablePackages.add(packageNames[i]);
+                }
+            }
+        }
+        return unactionablePackages.toArray(new String[unactionablePackages.size()]);
+    }
+
+    /**
+     * Returns the app extras of the given suspended package.
+     *
+     * @param packageName The suspended package name.
+     * @param userId The user where the package resides.
+     * @param callingUid The caller's uid.
+     * @return The app extras of the suspended package.
+     */
+    @Nullable
+    Bundle getSuspendedPackageAppExtras(@NonNull String packageName, int userId, int callingUid) {
+        final PackageStateInternal ps = mPm.getPackageStateInternal(packageName, callingUid);
+        if (ps == null) {
+            return null;
+        }
+        final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
+        final Bundle allExtras = new Bundle();
+        if (pus.isSuspended()) {
+            for (int i = 0; i < pus.getSuspendParams().size(); i++) {
+                final SuspendParams params = pus.getSuspendParams().valueAt(i);
+                if (params != null && params.getAppExtras() != null) {
+                    allExtras.putAll(params.getAppExtras());
+                }
+            }
+        }
+        return (allExtras.size() > 0) ? allExtras : null;
+    }
+
+    /**
+     * Removes any suspensions on given packages that were added by packages that pass the given
+     * predicate.
+     *
+     * <p> Caller must flush package restrictions if it cares about immediate data consistency.
+     *
+     * @param packagesToChange The packages on which the suspension are to be removed.
+     * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
+     *                                   suspensions will be removed.
+     * @param userId The user for which the changes are taking place.
+     */
+    void removeSuspensionsBySuspendingPackage(@NonNull String[] packagesToChange,
+            @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
+        final List<String> unsuspendedPackages = new ArrayList<>();
+        final IntArray unsuspendedUids = new IntArray();
+        synchronized (mPm.mLock) {
+            for (String packageName : packagesToChange) {
+                final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+                if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
+                    ps.removeSuspension(suspendingPackagePredicate, userId);
+                    if (!ps.getUserStateOrDefault(userId).isSuspended()) {
+                        unsuspendedPackages.add(ps.getPackageName());
+                        unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
+                    }
+                }
+            }
+            mPm.scheduleWritePackageRestrictionsLocked(userId);
+        }
+        if (!unsuspendedPackages.isEmpty()) {
+            final String[] packageArray = unsuspendedPackages.toArray(
+                    new String[unsuspendedPackages.size()]);
+            sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
+            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+                    packageArray, unsuspendedUids.toArray(), userId);
+        }
+    }
+
+    /**
+     * Returns the launcher extras for the given suspended package.
+     *
+     * @param packageName The name of the suspended package.
+     * @param userId The user where the package resides.
+     * @param callingUid The caller's uid.
+     * @return The launcher extras.
+     */
+    @Nullable
+    Bundle getSuspendedPackageLauncherExtras(@NonNull String packageName, int userId,
+            int callingUid) {
+        final PackageStateInternal packageState = mPm.getPackageStateInternal(
+                packageName, callingUid);
+        if (packageState == null) {
+            return null;
+        }
+        Bundle allExtras = new Bundle();
+        PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+        if (userState.isSuspended()) {
+            for (int i = 0; i < userState.getSuspendParams().size(); i++) {
+                final SuspendParams params = userState.getSuspendParams().valueAt(i);
+                if (params != null && params.getLauncherExtras() != null) {
+                    allExtras.putAll(params.getLauncherExtras());
+                }
+            }
+        }
+        return (allExtras.size() > 0) ? allExtras : null;
+    }
+
+    /**
+     * Return {@code true}, if the given package is suspended.
+     *
+     * @param packageName The name of package to check.
+     * @param userId The user where the package resides.
+     * @param callingUid The caller's uid.
+     * @return {@code true}, if the given package is suspended.
+     */
+    boolean isPackageSuspended(@NonNull String packageName, int userId, int callingUid) {
+        final PackageStateInternal packageState = mPm.getPackageStateInternal(
+                packageName, callingUid);
+        return packageState != null && packageState.getUserStateOrDefault(userId)
+                .isSuspended();
+    }
+
+    /**
+     * Given a suspended package, returns the name of package which invokes suspending to it.
+     *
+     * @param suspendedPackage The suspended package to check.
+     * @param userId The user where the package resides.
+     * @param callingUid The caller's uid.
+     * @return The name of suspending package.
+     */
+    @Nullable
+    String getSuspendingPackage(@NonNull String suspendedPackage, int userId, int callingUid) {
+        final PackageStateInternal packageState = mPm.getPackageStateInternal(
+                suspendedPackage, callingUid);
+        if (packageState == null) {
+            return  null;
+        }
+
+        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+        if (!userState.isSuspended()) {
+            return null;
+        }
+
+        String suspendingPackage = null;
+        for (int i = 0; i < userState.getSuspendParams().size(); i++) {
+            suspendingPackage = userState.getSuspendParams().keyAt(i);
+            if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+                return suspendingPackage;
+            }
+        }
+        return suspendingPackage;
+    }
+
+    /**
+     *  Returns the dialog info of the given suspended package.
+     *
+     * @param suspendedPackage The name of the suspended package.
+     * @param suspendingPackage The name of the suspending package.
+     * @param userId The user where the package resides.
+     * @param callingUid The caller's uid.
+     * @return The dialog info.
+     */
+    @Nullable
+    SuspendDialogInfo getSuspendedDialogInfo(@NonNull String suspendedPackage,
+            @NonNull String suspendingPackage, int userId, int callingUid) {
+        final PackageStateInternal packageState = mPm.getPackageStateInternal(
+                suspendedPackage, callingUid);
+        if (packageState == null) {
+            return  null;
+        }
+
+        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+        if (!userState.isSuspended()) {
+            return null;
+        }
+
+        final ArrayMap<String, SuspendParams> suspendParamsMap = userState.getSuspendParams();
+        if (suspendParamsMap == null) {
+            return null;
+        }
+
+        final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
+        return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
+    }
+
+    /**
+     * Return {@code true} if the user is allowed to suspend packages by the caller.
+     *
+     * @param userId The user id to check.
+     * @param callingUid The caller's uid.
+     * @return {@code true} if the user is allowed to suspend packages by the caller.
+     */
+    boolean isSuspendAllowedForUser(int userId, int callingUid) {
+        final UserManagerService userManager = mInjector.getUserManagerService();
+        return isCallerDeviceOrProfileOwner(userId, callingUid)
+                || (!userManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
+                && !userManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
+    }
+
+    /**
+     * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
+     * be suspended or not.
+     *
+     * @param packageNames  The package names to check suspendability for.
+     * @param userId The user to check in
+     * @param callingUid The caller's uid.
+     * @return An array containing results of the checks
+     */
+    @NonNull
+    boolean[] canSuspendPackageForUser(@NonNull String[] packageNames, int userId, int callingUid) {
+        final boolean[] canSuspend = new boolean[packageNames.length];
+        final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId, callingUid);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
+            final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId);
+            final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId);
+            final String requiredInstallerPackage = getKnownPackageName(PACKAGE_INSTALLER, userId);
+            final String requiredUninstallerPackage =
+                    getKnownPackageName(PACKAGE_UNINSTALLER, userId);
+            final String requiredVerifierPackage = getKnownPackageName(PACKAGE_VERIFIER, userId);
+            final String requiredPermissionControllerPackage =
+                    getKnownPackageName(PACKAGE_PERMISSION_CONTROLLER, userId);
+            for (int i = 0; i < packageNames.length; i++) {
+                canSuspend[i] = false;
+                final String packageName = packageNames[i];
+
+                if (mPm.isPackageDeviceAdmin(packageName, userId)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": has an active device admin");
+                    continue;
+                }
+                if (packageName.equals(activeLauncherPackageName)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": contains the active launcher");
+                    continue;
+                }
+                if (packageName.equals(requiredInstallerPackage)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": required for package installation");
+                    continue;
+                }
+                if (packageName.equals(requiredUninstallerPackage)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": required for package uninstallation");
+                    continue;
+                }
+                if (packageName.equals(requiredVerifierPackage)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": required for package verification");
+                    continue;
+                }
+                if (packageName.equals(dialerPackageName)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": is the default dialer");
+                    continue;
+                }
+                if (packageName.equals(requiredPermissionControllerPackage)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": required for permissions management");
+                    continue;
+                }
+                synchronized (mPm.mLock) {
+                    if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                        Slog.w(TAG, "Cannot suspend package \"" + packageName
+                                + "\": protected package");
+                        continue;
+                    }
+                    if (!isCallerOwner && mPm.mSettings.getBlockUninstallLPr(userId, packageName)) {
+                        Slog.w(TAG, "Cannot suspend package \"" + packageName
+                                + "\": blocked by admin");
+                        continue;
+                    }
+
+                    AndroidPackage pkg = mPm.mPackages.get(packageName);
+                    if (pkg != null) {
+                        // Cannot suspend SDK libs as they are controlled by SDK manager.
+                        if (pkg.isSdkLibrary()) {
+                            Slog.w(TAG, "Cannot suspend package: " + packageName
+                                    + " providing SDK library: "
+                                    + pkg.getSdkLibName());
+                            continue;
+                        }
+                        // Cannot suspend static shared libs as they are considered
+                        // a part of the using app (emulating static linking). Also
+                        // static libs are installed always on internal storage.
+                        if (pkg.isStaticSharedLibrary()) {
+                            Slog.w(TAG, "Cannot suspend package: " + packageName
+                                    + " providing static shared library: "
+                                    + pkg.getStaticSharedLibName());
+                            continue;
+                        }
+                    }
+                }
+                if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+                    Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
+                    continue;
+                }
+                canSuspend[i] = true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return canSuspend;
+    }
+
+    /**
+     * Send broadcast intents for packages suspension changes.
+     *
+     * @param intent The action name of the suspension intent.
+     * @param pkgList The names of packages which have suspension changes.
+     * @param uidList The uids of packages which have suspension changes.
+     * @param userId The user where packages reside.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
+            @NonNull int[] uidList, int userId) {
+        final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
+        final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
+        final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
+        final int[] userIds = new int[] {userId};
+        // Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if
+        // allow lists are the same.
+        for (int i = 0; i < pkgList.length; i++) {
+            final String pkgName = pkgList[i];
+            final int uid = uidList[i];
+            SparseArray<int[]> allowList = mInjector.getAppsFilter().getVisibilityAllowList(
+                    mPm.getPackageStateInternal(pkgName, SYSTEM_UID),
+                    userIds, mPm.getPackageStates());
+            if (allowList == null) {
+                allowList = new SparseArray<>(0);
+            }
+            boolean merged = false;
+            for (int j = 0; j < allowListsToSend.size(); j++) {
+                if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) {
+                    pkgsToSend.get(j).add(pkgName);
+                    uidsToSend.get(j).add(uid);
+                    merged = true;
+                    break;
+                }
+            }
+            if (!merged) {
+                pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName)));
+                uidsToSend.add(IntArray.wrap(new int[] {uid}));
+                allowListsToSend.add(allowList);
+            }
+        }
+
+        final Handler handler = mInjector.getHandler();
+        for (int i = 0; i < pkgsToSend.size(); i++) {
+            final Bundle extras = new Bundle(3);
+            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+                    pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()]));
+            extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
+            final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
+                    ? null : allowListsToSend.get(i);
+            handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
+                    extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
+                    null /* finishedReceiver */, userIds, null /* instantUserIds */,
+                    allowList, null /* bOptions */));
+        }
+    }
+
+    private String getKnownPackageName(@KnownPackage int knownPackage, int userId) {
+        final String[] knownPackages = mPm.getKnownPackageNamesInternal(knownPackage, userId);
+        return knownPackages.length > 0 ? knownPackages[0] : null;
+    }
+
+    private boolean isCallerDeviceOrProfileOwner(int userId, int callingUid) {
+        if (callingUid == SYSTEM_UID) {
+            return true;
+        }
+        final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
+        if (ownerPackage != null) {
+            return callingUid == mPm.getPackageUidInternal(
+                    ownerPackage, 0, userId, callingUid);
+        }
+        return false;
+    }
+
+    private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
+            int userId) {
+        final Handler handler = mInjector.getHandler();
+        final String action = suspended
+                ? Intent.ACTION_MY_PACKAGE_SUSPENDED
+                : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
+        handler.post(() -> {
+            final IActivityManager am = ActivityManager.getService();
+            if (am == null) {
+                Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+                        + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+                return;
+            }
+            final int[] targetUserIds = new int[] {userId};
+            for (String packageName : affectedPackages) {
+                final Bundle appExtras = suspended
+                        ? getSuspendedPackageAppExtras(packageName, userId, SYSTEM_UID)
+                        : null;
+                final Bundle intentExtras;
+                if (appExtras != null) {
+                    intentExtras = new Bundle(1);
+                    intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+                } else {
+                    intentExtras = null;
+                }
+                handler.post(() -> mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
+                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+                        targetUserIds, false, null, null));
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 4bcc2a3..f87063a 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -56,27 +56,6 @@
       ]
     },
     {
-      "name": "CtsAppSecurityHostTestCases",
-      "options": [
-        {
-          "include-filter": "android.appsecurity.cts.PrivilegedUpdateTests"
-        }
-      ]
-    },
-    {
-      "name": "CtsAppSecurityHostTestCases",
-      "file_patterns": [
-        "core/java/.*Install.*",
-        "services/core/.*Install.*",
-        "services/core/java/com/android/server/pm/.*"
-      ],
-      "options": [
-        {
-          "include-filter": "android.appsecurity.cts.SplitTests"
-        }
-      ]
-    },
-    {
       "name": "PackageManagerServiceHostTests",
       "options": [
         {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 167ad3b..d6e88f4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2487,20 +2487,25 @@
                 return false;
             }
 
-            // Limit the number of profiles that can be created
-            final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
-            if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
-                final int userTypeCount = getProfileIds(userId, userType, false).length;
-                final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
-                if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+            final int userTypeCount = getProfileIds(userId, userType, false).length;
+            final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
+            final int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+                    - profilesRemovedCount;
+
+            // Limit total number of users that can be created
+            if (usersCountAfterRemoving >= UserManager.getMaxSupportedUsers()) {
+                // Special case: Allow creating a managed profile anyway if there's only 1 user
+                // Otherwise, disallow.
+                if (!(isManagedProfile && usersCountAfterRemoving == 1)) {
                     return false;
                 }
-                // Allow creating a managed profile in the special case where there is only one user
-                if (isManagedProfile) {
-                    int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
-                            - profilesRemovedCount;
-                    return usersCountAfterRemoving == 1
-                            || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+            }
+
+            // Limit the number of profiles of this type that can be created.
+            final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
+            if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+                if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+                    return false;
                 }
             }
         }
@@ -3537,8 +3542,11 @@
                     Slog.wtf(LOG_TAG, "Seeing both legacy and current local restrictions in xml");
                 }
             } else if (legacyLocalRestrictions != null) {
-                mDevicePolicyLocalUserRestrictions.put(id,
-                        new RestrictionsSet(id, legacyLocalRestrictions));
+                RestrictionsSet legacyLocalRestrictionsSet =
+                        legacyLocalRestrictions.isEmpty()
+                                ? new RestrictionsSet()
+                                : new RestrictionsSet(id, legacyLocalRestrictions);
+                mDevicePolicyLocalUserRestrictions.put(id, legacyLocalRestrictionsSet);
             }
             if (globalRestrictions != null) {
                 mDevicePolicyGlobalUserRestrictions.updateRestrictions(id,
@@ -3667,9 +3675,18 @@
             @UserInfoFlag int flags, @UserIdInt int parentId,
             @Nullable String[] disallowedPackages)
             throws UserManager.CheckedUserOperationException {
-        String restriction = (UserManager.isUserTypeManagedProfile(userType))
-                ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
-                : UserManager.DISALLOW_ADD_USER;
+
+        // Checking user restriction before creating new user,
+        // default check is for DISALLOW_ADD_USER
+        // If new user is of type CLONE, check if creation of clone profile is allowed
+        // If new user is of type MANAGED, check if creation of managed profile is allowed
+        String restriction = UserManager.DISALLOW_ADD_USER;
+        if (UserManager.isUserTypeCloneProfile(userType)) {
+            restriction = UserManager.DISALLOW_ADD_CLONE_PROFILE;
+        } else if (UserManager.isUserTypeManagedProfile(userType)) {
+            restriction = UserManager.DISALLOW_ADD_MANAGED_PROFILE;
+        }
+
         enforceUserRestriction(restriction, UserHandle.getCallingUserId(),
                 "Cannot add user");
         return createUserInternalUnchecked(name, userType, flags, parentId,
@@ -3750,6 +3767,7 @@
         final boolean isGuest = UserManager.isUserTypeGuest(userType);
         final boolean isRestricted = UserManager.isUserTypeRestricted(userType);
         final boolean isDemo = UserManager.isUserTypeDemo(userType);
+        final boolean isManagedProfile = UserManager.isUserTypeManagedProfile(userType);
 
         final long ident = Binder.clearCallingIdentity();
         UserInfo userInfo;
@@ -3773,6 +3791,14 @@
                                     + ". Maximum number of that type already exists.",
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
+                if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
+                    // If the user limit has been reached, we cannot add a user (except guest/demo).
+                    // Note that managed profiles can bypass it in certain circumstances (taken
+                    // into account in the profile check below).
+                    throwCheckedUserOperationException(
+                            "Cannot add user. Maximum user limit is reached.",
+                            UserManager.USER_OPERATION_ERROR_MAX_USERS);
+                }
                 // TODO(b/142482943): Perhaps let the following code apply to restricted users too.
                 if (isProfile && !canAddMoreProfilesToUser(userType, parentId, false)) {
                     throwCheckedUserOperationException(
@@ -3780,13 +3806,6 @@
                                     + " for user " + parentId,
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
-                if (!isGuest && !isProfile && !isDemo && isUserLimitReached()) {
-                    // If we're not adding a guest/demo user or a profile and the 'user limit' has
-                    // been reached, cannot add a user.
-                    throwCheckedUserOperationException(
-                            "Cannot add user. Maximum user limit is reached.",
-                            UserManager.USER_OPERATION_ERROR_MAX_USERS);
-                }
                 // In legacy mode, restricted profile's parent can only be the owner user
                 if (isRestricted && !UserManager.isSplitSystemUser()
                         && (parentId != UserHandle.USER_SYSTEM)) {
@@ -4427,11 +4446,11 @@
     }
 
     @Override
-    public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
-            boolean evenWhenDisallowed) {
+    public @UserManager.RemoveResult int removeUserWhenPossible(@UserIdInt int userId,
+            boolean overrideDevicePolicy) {
         checkCreateUsersPermission("Only the system can remove users");
 
-        if (!evenWhenDisallowed) {
+        if (!overrideDevicePolicy) {
             final String restriction = getUserRemovalRestriction(userId);
             if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
                 Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
@@ -4478,7 +4497,7 @@
                 userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
                 writeUserLP(userData);
 
-                return UserManager.REMOVE_RESULT_SET_EPHEMERAL;
+                return UserManager.REMOVE_RESULT_DEFERRED;
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0f3b4bc..1fa9013 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -101,6 +101,7 @@
             UserManager.DISALLOW_FACTORY_RESET,
             UserManager.DISALLOW_ADD_USER,
             UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+            UserManager.DISALLOW_ADD_CLONE_PROFILE,
             UserManager.ENSURE_VERIFY_APPS,
             UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
             UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 01bf634..e28a6ea 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -31,7 +31,7 @@
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
 import android.content.pm.dex.PackageOptimizationInfo;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index 8afe62a..61aedd8 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -16,7 +16,7 @@
 
 package com.android.server.pm.dex;
 
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
 import android.os.Binder;
 import android.os.UserHandle;
 import android.util.Log;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 07cc3d0..0fa0dc3 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -34,19 +34,19 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingUtils;
-import android.content.pm.parsing.component.ComponentParseUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.PackageUserStateUtils;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f467a7f..08e2f7d 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -22,9 +22,9 @@
 import android.app.ActivityThread;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
diff --git a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
index 3a49216..564585b 100644
--- a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
@@ -18,10 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.parsing.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedComponent;
 import android.util.Pair;
 
-import com.android.server.pm.PackageSetting;
 import com.android.server.pm.pkg.PackageStateInternal;
 
 /**
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index bbf584d..dc3bf78 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -21,7 +21,7 @@
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
-import android.content.pm.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index bf7d897..b357ba0 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -17,7 +17,9 @@
 package com.android.server.pm.parsing.pkg;
 
 import android.annotation.NonNull;
-import android.content.pm.parsing.ParsingPackageRead;
+
+import com.android.internal.content.om.OverlayConfig;
+import com.android.server.pm.pkg.parsing.ParsingPackageRead;
 
 import com.android.server.pm.pkg.AndroidPackageApi;
 
@@ -31,8 +33,8 @@
  *
  * @hide
  */
-public interface AndroidPackage extends ParsingPackageRead, AndroidPackageApi {
-
+public interface AndroidPackage extends ParsingPackageRead, AndroidPackageApi,
+        OverlayConfig.PackageProvider.Package {
 
     /**
      * The package name as declared in the manifest, since the package can be renamed. For example,
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 8b2c3a12..7e59bd6 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -23,12 +23,12 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.DexMetadataHelper;
-import android.content.pm.parsing.ParsingPackageRead;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
+import com.android.server.pm.pkg.parsing.ParsingPackageRead;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.incremental.IncrementalManager;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 6846ac5..193e1a2 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -22,14 +22,8 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.SELinuxUtil;
+import com.android.server.pm.pkg.SELinuxUtil;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageImpl;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
 import android.content.res.TypedArray;
 import android.os.Environment;
 import android.os.Parcel;
@@ -41,6 +35,12 @@
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageImpl;
 
 import java.io.File;
 
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java b/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
index a13f297..6ddae9b 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
@@ -18,7 +18,7 @@
 
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.parsing.PkgWithoutStateAppInfo;
+import com.android.server.pm.pkg.parsing.PkgWithoutStateAppInfo;
 
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.pkg.PackageState;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java b/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
index e2efbe1..da7f1dc 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
@@ -17,7 +17,7 @@
 package com.android.server.pm.parsing.pkg;
 
 import android.content.pm.PackageInfo;
-import android.content.pm.parsing.PkgWithoutStatePackageInfo;
+import com.android.server.pm.pkg.parsing.PkgWithoutStatePackageInfo;
 
 import com.android.server.pm.PackageManagerService;
 
diff --git a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java b/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java
new file mode 100644
index 0000000..d962505
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.Manifest;
+import android.annotation.NonNull;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Implements compatibility support for permissions, and old applications
+ * will be automatically granted it.
+ *
+ * Compatibility permissions are permissions that are automatically granted to
+ * packages that target an SDK prior to when the permission was introduced.
+ * Sometimes the platform makes breaking behaviour changes and hides the legacy
+ * behaviour behind a permission. In these instances, we ensure applications
+ * targeting older platform versions are implicitly granted the correct set of
+ * permissions.
+ *
+ * @hide
+ */
+@DataClass(genGetters = true, genBuilder = false)
+public class CompatibilityPermissionInfo {
+
+    @NonNull
+    private final String mName;
+    private final int mSdkVersion;
+
+    /**
+     * List of new permissions that have been added since 1.0.
+     *
+     * NOTE: These must be declared in SDK version order, with permissions
+     * added to newer SDKs appearing before those added to older SDKs.
+     *
+     * @hide
+     */
+    public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
+            new CompatibilityPermissionInfo[]{
+                    new CompatibilityPermissionInfo(Manifest.permission.POST_NOTIFICATIONS,
+                            android.os.Build.VERSION_CODES.TIRAMISU),
+                    new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                            android.os.Build.VERSION_CODES.DONUT),
+                    new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
+                            android.os.Build.VERSION_CODES.DONUT)
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public CompatibilityPermissionInfo(
+            @NonNull String name,
+            int sdkVersion) {
+        this.mName = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mName);
+        this.mSdkVersion = sdkVersion;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return mName;
+    }
+
+    @DataClass.Generated.Member
+    public int getSdkVersion() {
+        return mSdkVersion;
+    }
+
+    @DataClass.Generated(
+            time = 1627674427184L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final  int mSdkVersion\npublic static final  android.content.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\[email protected](genGetters=true, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index b15e495..60d2fc1 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -217,6 +217,11 @@
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
     }
 
+    private static final Set<String> NOTIFICATION_PERMISSIONS = new ArraySet<>();
+    static {
+        NOTIFICATION_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS);
+    }
+
     private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
 
     private static final String ACTION_TRACK = "com.android.fitness.TRACK";
@@ -378,18 +383,43 @@
 
         grantPermissionsToSysComponentsAndPrivApps(pm, userId);
         grantDefaultSystemHandlerPermissions(pm, userId);
+        grantSignatureAppsNotificationPermissions(pm, userId);
         grantDefaultPermissionExceptions(pm, userId);
 
         // Apply delayed state
         pm.apply();
     }
 
+    private void grantSignatureAppsNotificationPermissions(PackageManagerWrapper pm, int userId) {
+        Log.i(TAG, "Granting Notification permissions to platform signature apps for user "
+                + userId);
+        List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
+                DEFAULT_PACKAGE_INFO_QUERY_FLAGS, UserHandle.USER_SYSTEM);
+        for (PackageInfo pkg : packages) {
+            if (pkg == null || !pkg.applicationInfo.isSystemApp()
+                    || !pkg.applicationInfo.isSignedWithPlatformKey()) {
+                continue;
+            }
+            grantRuntimePermissionsForSystemPackage(pm, userId, pkg, NOTIFICATION_PERMISSIONS);
+        }
+
+    }
+
     private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
             int userId, PackageInfo pkg) {
+        grantRuntimePermissionsForSystemPackage(pm, userId, pkg, null);
+    }
+
+    private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
+            int userId, PackageInfo pkg, Set<String> filterPermissions) {
+        if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+            return;
+        }
         Set<String> permissions = new ArraySet<>();
         for (String permission : pkg.requestedPermissions) {
             final PermissionInfo perm = pm.getPermissionInfo(permission);
-            if (perm == null) {
+            if (perm == null
+                    || (filterPermissions != null && !filterPermissions.contains(permission))) {
                 continue;
             }
             if (perm.isRuntime()) {
@@ -547,23 +577,31 @@
         String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
                 syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
 
+        // PermissionController
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                mContext.getPackageManager().getPermissionControllerPackageName(), userId,
+                NOTIFICATION_PERMISSIONS);
+
         // Installer
         grantSystemFixedPermissionsToSystemPackage(pm,
                 ArrayUtils.firstOrNull(getKnownPackages(
                         PackageManagerInternal.PACKAGE_INSTALLER, userId)),
-                userId, STORAGE_PERMISSIONS);
+                userId, STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Verifier
         final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
                 PackageManagerInternal.PACKAGE_VERIFIER, userId));
         grantSystemFixedPermissionsToSystemPackage(pm, verifier, userId, STORAGE_PERMISSIONS);
-        grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
+        grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS,
+                NOTIFICATION_PERMISSIONS);
 
         // SetupWizard
         final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
                 PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId));
         grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
                 CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+        grantSystemFixedPermissionsToSystemPackage(pm, setupWizardPackage, userId,
+                NOTIFICATION_PERMISSIONS);
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)
                 || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE,
                 0)) {
@@ -585,12 +623,12 @@
         // Media provider
         grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId,
-                STORAGE_PERMISSIONS);
+                STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Downloads provider
         grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultProviderAuthorityPackage("downloads", userId), userId,
-                STORAGE_PERMISSIONS);
+                STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Downloads UI
         grantSystemFixedPermissionsToSystemPackage(pm,
@@ -649,7 +687,7 @@
         // Cell Broadcast Receiver
         grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
-                userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
+                userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Carrier Provisioning Service
         grantPermissionsToSystemPackage(pm,
@@ -661,7 +699,7 @@
         grantPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackageForCategory(pm,
                         Intent.CATEGORY_APP_CALENDAR, userId),
-                userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS);
+                userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Calendar provider
         String calendarProvider =
@@ -762,7 +800,8 @@
                 grantPermissionsToSystemPackage(pm, packageName, userId,
                         CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
-                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
+                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
+                        NOTIFICATION_PERMISSIONS);
                 grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
                         ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
@@ -791,7 +830,7 @@
                 .addCategory(Intent.CATEGORY_LAUNCHER_APP);
         grantPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackage(pm, homeIntent, userId), userId,
-                ALWAYS_LOCATION_PERMISSIONS);
+                ALWAYS_LOCATION_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Watches
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
@@ -816,7 +855,7 @@
 
         // Print Spooler
         grantSystemFixedPermissionsToSystemPackage(pm, PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
-                userId, ALWAYS_LOCATION_PERMISSIONS);
+                userId, ALWAYS_LOCATION_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // EmergencyInfo
         grantSystemFixedPermissionsToSystemPackage(pm,
@@ -920,12 +959,13 @@
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
         if (isPhonePermFixed) {
             grantSystemFixedPermissionsToSystemPackage(pm, dialerPackage, userId,
-                    PHONE_PERMISSIONS);
+                    PHONE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
         } else {
             grantPermissionsToSystemPackage(pm, dialerPackage, userId, PHONE_PERMISSIONS);
         }
         grantPermissionsToSystemPackage(pm, dialerPackage, userId,
-                CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+                CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS,
+                NOTIFICATION_PERMISSIONS);
         boolean isAndroidAutomotive =
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
         if (isAndroidAutomotive) {
@@ -937,7 +977,8 @@
             String smsPackage, int userId) {
         grantPermissionsToSystemPackage(pm, smsPackage, userId,
                 PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
-                STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+                STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS,
+                NOTIFICATION_PERMISSIONS);
     }
 
     private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(PackageManagerWrapper pm,
@@ -1337,7 +1378,9 @@
                         pm.grantPermission(permission, pkg, user);
                     }
 
-                    pm.updatePermissionFlags(permission, pkg, newFlags, newFlags, user);
+                    // clear the REVIEW_REQUIRED flag, if set
+                    int flagMask = newFlags | PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+                    pm.updatePermissionFlags(permission, pkg, flagMask, newFlags, user);
                 }
 
                 // If a component gets a permission for being the default handler A
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 4bbe373..d455be7 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -162,7 +162,10 @@
      * The delay to wait before revoking on the event an app is terminated. Recommended to be long
      * enough so that apps don't lose permission on an immediate restart
      */
-    private static long getKilledDelayMillis() {
+    private long getKilledDelayMillis(boolean isSelfRevokedPermissionSession) {
+        if (isSelfRevokedPermissionSession) {
+            return 0;
+        }
         return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
                 PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS);
     }
@@ -175,6 +178,18 @@
         mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
     }
 
+    void setSelfRevokedPermissionSession(int uid) {
+        synchronized (mLock) {
+            PackageInactivityListener listener = mListeners.get(uid);
+            if (listener == null) {
+                Log.e(LOG_TAG, "Could not set session for uid " + uid
+                        + " as self-revoke session: session not found");
+                return;
+            }
+            listener.setSelfRevokedPermissionSession();
+        }
+    }
+
     /**
      * A class which watches a package for inactivity and notifies the permission controller when
      * the package becomes inactive
@@ -189,6 +204,7 @@
         private final int mImportanceToResetTimer;
         private final int mImportanceToKeepSessionAlive;
 
+        private boolean mIsSelfRevokedPermissionSession;
         private boolean mIsAlarmSet;
         private boolean mIsFinished;
 
@@ -255,7 +271,7 @@
                             }
                             onImportanceChanged(mUid, imp);
                         }
-                    }, mToken, getKilledDelayMillis());
+                    }, mToken, getKilledDelayMillis(mIsSelfRevokedPermissionSession));
                     return;
                 }
                 if (importance > mImportanceToResetTimer) {
@@ -291,6 +307,14 @@
         }
 
         /**
+         * Marks the session as a self-revoke session, which does not delay the revocation when
+         * the app is restarting.
+         */
+        public void setSelfRevokedPermissionSession() {
+            mIsSelfRevokedPermissionSession = true;
+        }
+
+        /**
          * Set the alarm which will callback when the package is inactive
          */
         @GuardedBy("mInnerLock")
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 041c4fe..d5456e3 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -23,7 +23,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermission;
 import android.os.Build;
 import android.os.UserHandle;
 import android.util.Log;
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 a3b6b82..317730a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -66,9 +66,12 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerService;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider;
 
@@ -384,7 +387,8 @@
     @Override
     public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
             long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) {
-        mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
                 "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
                         + " to register permissions as one time.");
         Objects.requireNonNull(packageName);
@@ -549,6 +553,27 @@
     }
 
     @Override
+    public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) {
+        mPermissionManagerServiceImpl.revokePostNotificationPermissionWithoutKillForTest(
+                packageName, userId);
+    }
+
+    @Override
+    public void revokeOwnPermissionsOnKill(@NonNull String packageName,
+            @NonNull List<String> permissions) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        AndroidFuture<Void> future = new AndroidFuture<>();
+        future.whenComplete((result, err) -> {
+            if (err == null) {
+                getOneTimePermissionUserManager(callingUserId)
+                        .setSelfRevokedPermissionSession(callingUid);
+            }
+        });
+        mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions, future);
+    }
+
+    @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int userId) {
         return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
@@ -678,8 +703,23 @@
 
         @Override
         public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId,
-                @NonNull PackageInstalledParams params, @UserIdInt int userId) {
-            mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, userId);
+                @NonNull PackageInstalledParams params, @UserIdInt int rawUserId) {
+            Objects.requireNonNull(pkg, "pkg");
+            Objects.requireNonNull(params, "params");
+            Preconditions.checkArgument(rawUserId >= UserHandle.USER_SYSTEM
+                    || rawUserId == UserHandle.USER_ALL, "userId");
+
+            mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, rawUserId);
+            final int[] userIds = rawUserId == UserHandle.USER_ALL ? getAllUserIds()
+                    : new int[] { rawUserId };
+            for (final int userId : userIds) {
+                final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
+                if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
+                        || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
+                    setAutoRevokeExemptedInternal(pkg,
+                            autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
+                }
+            }
         }
 
         @Override
@@ -780,6 +820,15 @@
     }
 
     /**
+     * Returns all relevant user ids.  This list include the current set of created user ids as well
+     * as pre-created user ids.
+     * @return user ids for created users and pre-created users
+     */
+    private int[] getAllUserIds() {
+        return UserManagerService.getInstance().getUserIdsIncludingPreCreated();
+    }
+
+    /**
      * Interface to intercept permission checks and optionally pass through to the original
      * implementation.
      */
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index c9fd122..9b3d6d6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -19,8 +19,6 @@
 import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED;
@@ -63,7 +61,6 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
-import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
@@ -76,11 +73,11 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedPermissionUtils;
-import android.content.pm.permission.CompatibilityPermissionInfo;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedPermissionUtils;
+
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
@@ -116,6 +113,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.RoSystemProperties;
@@ -170,6 +168,10 @@
     private static final String TAG = "PackageManager";
     private static final String LOG_TAG = PermissionManagerServiceImpl.class.getSimpleName();
 
+    private static final String SKIP_KILL_APP_REASON_NOTIFICATION_TEST = "skip permission revoke "
+            + "app kill for notification test";
+
+
     private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
 
     // For automotive products, CarService enforces allow-listing of the privileged permissions
@@ -198,14 +200,19 @@
     /** All nearby devices permissions */
     private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>();
 
-    // TODO: This is a placeholder. Replace with actual implementation
-    private static final List<String> NOTIFICATION_PERMISSIONS = new ArrayList<>();
-
     /**
-     * All permissions that should be granted with the REVOKE_WHEN_REQUESTED flag, if they are
-     * implicitly added to a package
+     * All notification permissions.
+     * Notification permission state is treated differently from other permissions. Notification
+     * permission get the REVIEW_REQUIRED flag set for S- apps, or for T+ apps on updating to T or
+     * restoring a pre-T backup. The permission and app op remain denied. The flag will be read by
+     * the notification system, and allow apps to send notifications, until cleared.
+     * The flag is cleared for S- apps by the system showing a permission request prompt, and the
+     * user clicking "allow" or "deny" in the dialog. For T+ apps, the flag is cleared upon the
+     * first activity launch.
+     *
+     * @see PermissionPolicyInternal#showNotificationPromptIfNeeded(String, int, int)
      */
-    private static final List<String> IMPLICIT_GRANTED_PERMISSIONS = new ArrayList<>();
+    private static final List<String> NOTIFICATION_PERMISSIONS = new ArrayList<>();
 
     /** If the permission of the value is granted, so is the key */
     private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
@@ -221,7 +228,7 @@
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE);
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT);
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
-        IMPLICIT_GRANTED_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS);
+        NOTIFICATION_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS);
     }
 
     /** Set of source package names for Privileged Permission Allowlist */
@@ -244,9 +251,6 @@
     /** Permission controller: User space permission management */
     private PermissionControllerManager mPermissionControllerManager;
 
-    /** App ops manager */
-    private final AppOpsManager mAppOpsManager;
-
     /**
      * Built-in permissions. Read from system configuration files. Mapping is from
      * UID to permission name.
@@ -321,11 +325,15 @@
             mPackageManagerInt.writeSettings(true);
         }
         @Override
-        public void onPermissionRevoked(int uid, int userId, String reason) {
+        public void onPermissionRevoked(int uid, int userId, String reason, boolean overrideKill) {
             mOnPermissionChangeListeners.onPermissionsChanged(uid);
 
             // Critical; after this call the application should never have the permission
             mPackageManagerInt.writeSettings(false);
+            if (overrideKill) {
+                return;
+            }
+
             final int appId = UserHandle.getAppId(uid);
             if (reason == null) {
                 mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
@@ -374,7 +382,6 @@
         mContext = context;
         mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
-        mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
         mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME);
         // PackageManager.hasSystemFeature() is not used here because PackageManagerService
@@ -1440,9 +1447,29 @@
                 reason, mDefaultPermissionCallback);
     }
 
+    @Override
+    public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) {
+        final int callingUid = Binder.getCallingUid();
+        final boolean overridePolicy =
+                checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
+                        == PackageManager.PERMISSION_GRANTED;
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, "");
+        revokeRuntimePermissionInternal(packageName, Manifest.permission.POST_NOTIFICATIONS,
+                overridePolicy, true, callingUid, userId,
+                SKIP_KILL_APP_REASON_NOTIFICATION_TEST, mDefaultPermissionCallback);
+    }
+
     private void revokeRuntimePermissionInternal(String packageName, String permName,
-            boolean overridePolicy, int callingUid, final int userId, String reason,
-            PermissionCallback callback) {
+            boolean overridePolicy, int callingUid, final int userId,
+            String reason, PermissionCallback callback) {
+        revokeRuntimePermissionInternal(packageName, permName, overridePolicy, false, callingUid,
+                userId, reason, callback);
+    }
+
+    private void revokeRuntimePermissionInternal(String packageName, String permName,
+            boolean overridePolicy, boolean overrideKill, int callingUid, final int userId,
+            String reason, PermissionCallback callback) {
         if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
                 && PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
             Log.i(TAG, "System is revoking " + packageName + " "
@@ -1554,7 +1581,7 @@
         if (callback != null) {
             if (isRuntimePermission) {
                 callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId,
-                        reason);
+                        reason, overrideKill);
             } else {
                 mDefaultPermissionCallback.onInstallPermissionRevoked();
             }
@@ -1565,6 +1592,27 @@
         }
     }
 
+    @Override
+    public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions,
+            AndroidFuture<Void> callback) {
+        final int callingUid = Binder.getCallingUid();
+        int callingUserId = UserHandle.getUserId(callingUid);
+        int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId);
+        if (targetPackageUid != callingUid) {
+            throw new SecurityException("uid " + callingUid
+                    + " cannot revoke permissions for package " + packageName + " with uid "
+                    + targetPackageUid);
+        }
+        for (String permName : permissions) {
+            if (!checkCallingOrSelfPermission(permName)) {
+                throw new SecurityException("uid " + callingUid + " cannot revoke permission "
+                        + permName + " because it does not hold that permission");
+            }
+        }
+        mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions,
+                callback);
+    }
+
     private boolean mayManageRolePermission(int uid) {
         final PackageManager packageManager = mContext.getPackageManager();
         final String[] packageNames = packageManager.getPackagesForUid(uid);
@@ -2594,9 +2642,12 @@
 
                     // Cache newImplicitPermissions before modifing permissionsState as for the
                     // shared uids the original and new state are the same object
+                    // TODO(205888750): remove the line for LEGACY_REVIEW once propagated through
+                    // droidfood
                     if (!origState.hasPermissionState(permName)
                             && (pkg.getImplicitPermissions().contains(permName)
-                            || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+                            || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))
+                            || NOTIFICATION_PERMISSIONS.contains(permName)) {
                         if (pkg.getImplicitPermissions().contains(permName)) {
                             // If permName is an implicit permission, try to auto-grant
                             newImplicitPermissions.add(permName);
@@ -2754,9 +2805,11 @@
                             }
 
                             // Remove review flag as it is not necessary anymore
-                            if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                                flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
-                                wasChanged = true;
+                            if (!NOTIFICATION_PERMISSIONS.contains(perm)) {
+                                if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                    flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                    wasChanged = true;
+                                }
                             }
 
                             if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0
@@ -3117,28 +3170,41 @@
                     inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps,
                             pkg);
                 }
-            } else if (IMPLICIT_GRANTED_PERMISSIONS.contains(newPerm)
-                    && !origPs.hasPermissionState(newPerm)) {
+            } else if (NOTIFICATION_PERMISSIONS.contains(newPerm)) {
+                //&& (origPs.getPermissionState(newPerm) == null) {
+                // TODO(b/205888750): add back line about origPs once propagated through droidfood
                 Permission bp = mRegistry.getPermission(newPerm);
                 if (bp == null) {
                     throw new IllegalStateException("Unknown new permission " + newPerm);
                 }
-                if ((ps.getPermissionState(newPerm).getFlags()
-                        & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                    // No need to grant if review is required
-                    continue;
+                // TODO(b/205888750): remove the line for REVOKE_WHEN_REQUESTED once propagated
+                //  through droidfood
+                if (!isUserSetOrPregrantedOrFixed(ps.getPermissionFlags(newPerm))) {
+                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                    ps.updatePermissionFlags(bp, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                                    | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+                            PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED);
+                    // TODO(b/205888750): remove if/else block once propagated through droidfood
+                    if (ps.isPermissionGranted(newPerm)
+                            && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
+                        ps.revokePermission(bp);
+                    } else if (!ps.isPermissionGranted(newPerm)
+                            && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+                        ps.grantPermission(bp);
+                    }
                 }
-                updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
-                ps.updatePermissionFlags(bp,
-                        FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
-                        FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
-                ps.grantPermission(bp);
             }
         }
 
         return updatedUserIds;
     }
 
+    private boolean isUserSetOrPregrantedOrFixed(int flags) {
+        return (flags & (FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED
+                | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED
+                | FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0;
+    }
+
     @NonNull
     @Override
     public List<SplitPermissionInfoParcelable> getSplitPermissions() {
@@ -4323,9 +4389,9 @@
                 updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false)
         );
 
-        mSystemReady = true;
-
         synchronized (mLock) {
+            mSystemReady = true;
+
             if (mPrivappPermissionsViolations != null) {
                 throw new IllegalStateException("Signature|privileged permissions not in "
                         + "privapp-permissions allowlist: " + mPrivappPermissionsViolations);
@@ -4905,15 +4971,6 @@
             addAllowlistedRestrictedPermissionsInternal(pkg,
                     params.getAllowlistedRestrictedPermissions(),
                     FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
-            final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
-            if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
-                    || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
-                // TODO: theianchen Bug: 182523293
-                // We should move this portion of code that's calling
-                // setAutoRevokeExemptedInternal() into the old PMS
-                setAutoRevokeExemptedInternal(pkg,
-                        autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
-            }
             grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId);
         }
     }
@@ -5182,25 +5239,6 @@
         onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds);
     }
 
-    private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted,
-            @UserIdInt int userId) {
-        final int packageUid = UserHandle.getUid(userId, pkg.getUid());
-        if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
-                packageUid, pkg.getPackageName()) != MODE_ALLOWED) {
-            // Allowlist user set - don't override
-            return false;
-        }
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid,
-                    pkg.getPackageName(), exempted ? MODE_IGNORED : MODE_ALLOWED);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return true;
-    }
-
     /**
      * Callbacks invoked when interesting actions have been taken on a permission.
      * <p>
@@ -5213,7 +5251,11 @@
         public void onPermissionChanged() {}
         public void onPermissionGranted(int uid, @UserIdInt int userId) {}
         public void onInstallPermissionGranted() {}
-        public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {}
+        public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {
+            onPermissionRevoked(uid, userId, reason, false);
+        }
+        public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason,
+                boolean overrideKill) {}
         public void onInstallPermissionRevoked() {}
         public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {}
         public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index d2018f2..91c558b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -27,6 +27,7 @@
 import android.permission.IOnPermissionsChangeListener;
 import android.permission.PermissionManagerInternal;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.FileDescriptor;
@@ -318,6 +319,37 @@
             String reason);
 
     /**
+     * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
+     * USED in CTS or local tests.
+     *
+     * @param packageName The package to be revoked
+     * @param userId The user for which to revoke
+     */
+    void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
+
+    /**
+     * Triggers the revocation of one or more permissions for a package, under the following
+     * conditions:
+     * <ul>
+     * <li>The package {@code packageName} must be under the same UID as the calling process
+     *   (typically, the target package is the calling package).
+     * <li>Each permission in {@code permissions} must be granted to the package
+     * {@code packageName}.
+     * <li>Each permission in {@code permissions} must be a runtime permission.
+     * </ul>
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. This revocation happens asynchronously and kills all processes running in the
+     * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     * @param callback Callback called when the revocation request has been completed.
+     */
+    void revokeOwnPermissionsOnKill(String packageName, List<String> permissions,
+            AndroidFuture<Void> callback);
+
+    /**
      * Get whether you should show UI with rationale for requesting a permission. You should do this
      * only if you do not have the permission and the context in which the permission is requested
      * does not clearly communicate to the user what would be the benefit from grating this
diff --git a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
index 0e3fda7..3a61704 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index 6c8e0b7..656c445 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -20,8 +20,8 @@
 import android.annotation.Nullable;
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+
 import android.util.SparseArray;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 03b1692..d47c5ec 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.content.pm.PackageManager;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.pkg.FrameworkPackageUserState;
 import android.os.UserHandle;
 
 import java.util.Map;
@@ -34,7 +33,7 @@
  */
 // TODO(b/173807334): Expose API
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface PackageUserState extends FrameworkPackageUserState {
+public interface PackageUserState {
 
     PackageUserState DEFAULT = PackageUserStateInternal.DEFAULT;
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
new file mode 100644
index 0000000..917c4af
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg;
+
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+
+import android.annotation.NonNull;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.os.Debug;
+import android.util.DebugUtils;
+import android.util.Slog;
+
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.parsing.ParsingPackageRead;
+
+/** @hide */
+public class PackageUserStateUtils {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "PackageUserStateUtils";
+
+    public static boolean isMatch(@NonNull PackageUserState state,
+            ComponentInfo componentInfo, long flags) {
+        return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
+                componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.directBootAware, componentInfo.name, flags);
+    }
+
+    public static boolean isMatch(@NonNull PackageUserState state, boolean isSystem,
+            boolean isPackageEnabled, ParsedMainComponent component, long flags) {
+        return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
+                component.isDirectBootAware(), component.getName(), flags);
+    }
+
+    /**
+     * Test if the given component is considered installed, 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>
+     */
+    public static boolean isMatch(@NonNull PackageUserState state, boolean isSystem,
+            boolean isPackageEnabled, boolean isComponentEnabled,
+            boolean isComponentDirectBootAware, String componentName, long flags) {
+        final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
+        if (!isAvailable(state, flags) && !(isSystem && matchUninstalled)) {
+            return reportIfDebug(false, flags);
+        }
+
+        if (!isEnabled(state, isPackageEnabled, isComponentEnabled, componentName, flags)) {
+            return reportIfDebug(false, flags);
+        }
+
+        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);
+    }
+
+    public static boolean isAvailable(@NonNull PackageUserState state, long flags) {
+        // True if it is installed for this user and it is not hidden. If it is hidden,
+        // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
+        final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
+        final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+        return matchAnyUser
+                || (state.isInstalled()
+                && (!state.isHidden() || matchUninstalled));
+    }
+
+    public 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;
+    }
+
+    public static boolean isEnabled(@NonNull PackageUserState state, ComponentInfo componentInfo,
+            long flags) {
+        return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.name, flags);
+    }
+
+    public static boolean isEnabled(@NonNull PackageUserState state, boolean isPackageEnabled,
+            ParsedMainComponent parsedComponent, long flags) {
+        return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
+                parsedComponent.getName(), flags);
+    }
+
+    /**
+     * Test if the given component is considered enabled.
+     */
+    public static boolean isEnabled(@NonNull PackageUserState state,
+            boolean isPackageEnabled, boolean isComponentEnabled, String componentName,
+            long flags) {
+        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+            return true;
+        }
+
+        // First check if the overall package is disabled; if the package is
+        // enabled then fall through to check specific component
+        switch (state.getEnabledState()) {
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+                return false;
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+                    return false;
+                }
+                // fallthrough
+            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+                if (!isPackageEnabled) {
+                    return false;
+                }
+                // fallthrough
+            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                break;
+        }
+
+        // Check if component has explicit state before falling through to
+        // the manifest default
+        if (state.isComponentEnabled(componentName)) {
+            return true;
+        } else if (state.isComponentDisabled(componentName)) {
+            return false;
+        }
+
+        return isComponentEnabled;
+    }
+
+    public static boolean isPackageEnabled(@NonNull PackageUserState state,
+            @NonNull ParsingPackageRead pkg) {
+        switch (state.getEnabledState()) {
+            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                return true;
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                return false;
+            default:
+            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+                return pkg.isEnabled();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java
new file mode 100644
index 0000000..6cbc1de
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg;
+
+/**
+ * Utility methods that need to be used in application space.
+ * @hide
+ */
+public final class SELinuxUtil {
+
+    /** Append to existing seinfo label for instant apps @hide */
+    private static final String INSTANT_APP_STR = ":ephemeralapp";
+
+    /** Append to existing seinfo when modifications are complete @hide */
+    public static final String COMPLETE_STR = ":complete";
+
+    /** @hide */
+    public static String getSeinfoUser(PackageUserState userState) {
+        if (userState.isInstantApp()) {
+           return INSTANT_APP_STR + COMPLETE_STR;
+        }
+        return COMPLETE_STR;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
new file mode 100644
index 0000000..1deb8d0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually
+ * be removed once all post-parsing mutation is moved to parsing.
+ *
+ * @hide
+ */
+public class ComponentMutateUtils {
+
+    public static void setMaxAspectRatio(@NonNull ParsedActivity activity, int resizeMode,
+            float maxAspectRatio) {
+        ((ParsedActivityImpl) activity).setMaxAspectRatio(resizeMode, maxAspectRatio);
+    }
+
+    public static void setMinAspectRatio(@NonNull ParsedActivity activity, int resizeMode,
+            float minAspectRatio) {
+        ((ParsedActivityImpl) activity).setMinAspectRatio(resizeMode, minAspectRatio);
+    }
+
+    public static void setSupportsSizeChanges(@NonNull ParsedActivity activity,
+            boolean supportsSizeChanges) {
+        ((ParsedActivityImpl) activity).setSupportsSizeChanges(supportsSizeChanges);
+    }
+
+    public static void setResizeMode(@NonNull ParsedActivity activity, int resizeMode) {
+        ((ParsedActivityImpl) activity).setResizeMode(resizeMode);
+    }
+
+    public static void setExactFlags(ParsedComponent component, int exactFlags) {
+        ((ParsedComponentImpl) component).setFlags(exactFlags);
+    }
+
+    public static void setEnabled(@NonNull ParsedMainComponent component, boolean enabled) {
+        ((ParsedMainComponentImpl) component).setEnabled(enabled);
+    }
+
+    public static void setPackageName(@NonNull ParsedComponent component,
+            @NonNull String packageName) {
+        ((ParsedComponentImpl) component).setPackageName(packageName);
+    }
+
+    public static void setDirectBootAware(@NonNull ParsedMainComponent component,
+            boolean directBootAware) {
+        ((ParsedMainComponentImpl) component).setDirectBootAware(directBootAware);
+    }
+
+    public static void setExported(@NonNull ParsedMainComponent component, boolean exported) {
+        ((ParsedMainComponentImpl) component).setExported(exported);
+    }
+
+    public static void setAuthority(@NonNull ParsedProvider provider, @Nullable String authority) {
+        ((ParsedProviderImpl) provider).setAuthority(authority);
+    }
+
+    public static void setSyncable(@NonNull ParsedProvider provider, boolean syncable) {
+        ((ParsedProviderImpl) provider).setSyncable(syncable);
+    }
+
+    public static void setProtectionLevel(@NonNull ParsedPermission permission,
+            int protectionLevel) {
+        ((ParsedPermissionImpl) permission).setProtectionLevel(protectionLevel);
+    }
+
+    public static void setParsedPermissionGroup(@NonNull ParsedPermission permission,
+            @NonNull ParsedPermissionGroup permissionGroup) {
+        ((ParsedPermissionImpl) permission).setParsedPermissionGroup(permissionGroup);
+    }
+
+    public static void setPriority(@NonNull ParsedPermissionGroup parsedPermissionGroup,
+            int priority) {
+        ((ParsedPermissionGroupImpl) parsedPermissionGroup).setPriority(priority);
+    }
+
+    public static void addStateFrom(@NonNull ParsedProcess oldProcess,
+            @NonNull ParsedProcess newProcess) {
+        ((ParsedProcessImpl) oldProcess).addStateFrom(newProcess);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
new file mode 100644
index 0000000..a8fb79a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * @hide
+ */
+public class ComponentParseUtils {
+
+    public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
+        return intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE)
+                || intentFilter.hasAction(Intent.ACTION_SEND)
+                || intentFilter.hasAction(Intent.ACTION_SENDTO)
+                || intentFilter.hasAction(Intent.ACTION_SEND_MULTIPLE);
+    }
+
+    static <Component extends ParsedComponentImpl> ParseResult<Component> parseAllMetaData(
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
+            Component component, ParseInput input) throws XmlPullParserException, IOException {
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            if ("meta-data".equals(parser.getName())) {
+                result = ParsedComponentUtils.addMetaData(component, pkg, res, parser, input);
+            } else {
+                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(component);
+    }
+
+    @NonNull
+    public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
+            CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
+        if ((flags & ParsingPackageUtils.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
+                procSeq)) {
+            return input.success(defProc != null ? defProc : pkg);
+        }
+        if (separateProcesses != null) {
+            for (int i = separateProcesses.length - 1; i >= 0; i--) {
+                String sp = separateProcesses[i];
+                if (sp.equals(pkg) || sp.equals(defProc) || sp.contentEquals(procSeq)) {
+                    return input.success(pkg);
+                }
+            }
+        }
+        if (procSeq == null || procSeq.length() <= 0) {
+            return input.success(defProc);
+        }
+
+        ParseResult<String> nameResult = ComponentParseUtils.buildCompoundName(pkg, procSeq,
+                "process", input);
+        return input.success(TextUtils.safeIntern(nameResult.getResult()));
+    }
+
+    @NonNull
+    public static ParseResult<String> buildTaskAffinityName(String pkg, String defProc,
+            CharSequence procSeq, ParseInput input) {
+        if (procSeq == null) {
+            return input.success(defProc);
+        }
+        if (procSeq.length() <= 0) {
+            return input.success(null);
+        }
+        return buildCompoundName(pkg, procSeq, "taskAffinity", input);
+    }
+
+    public static ParseResult<String> buildCompoundName(String pkg, CharSequence procSeq,
+            String type, ParseInput input) {
+        String proc = procSeq.toString();
+        char c = proc.charAt(0);
+        if (pkg != null && c == ':') {
+            if (proc.length() < 2) {
+                return input.error("Bad " + type + " name " + proc + " in package " + pkg
+                        + ": must be at least two characters");
+            }
+            String subName = proc.substring(1);
+            final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input,
+                    subName, false, false);
+            if (nameResult.isError()) {
+                return input.error("Invalid " + type + " name " + proc + " in package " + pkg
+                        + ": " + nameResult.getErrorMessage());
+            }
+            return input.success(pkg + proc);
+        }
+        if (!"system".equals(proc)) {
+            final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, proc,
+                    true, false);
+            if (nameResult.isError()) {
+                return input.error("Invalid " + type + " name " + proc + " in package " + pkg
+                        + ": " + nameResult.getErrorMessage());
+            }
+        }
+        return input.success(proc);
+    }
+
+    public static int flag(int flag, @AttrRes int attribute, TypedArray typedArray) {
+        return typedArray.getBoolean(attribute, false) ? flag : 0;
+    }
+
+    public static int flag(int flag, @AttrRes int attribute, boolean defaultValue,
+            TypedArray typedArray) {
+        return typedArray.getBoolean(attribute, defaultValue) ? flag : 0;
+    }
+
+    /**
+     * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
+     */
+    @Nullable
+    public static CharSequence getNonLocalizedLabel(
+            ParsedComponent component) {
+        return component.getNonLocalizedLabel();
+    }
+
+    /**
+     * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
+     * <p>
+     * This is a method of the utility class to discourage use.
+     */
+    public static int getIcon(ParsedComponent component) {
+        return component.getIcon();
+    }
+
+    public static boolean isMatch(PackageUserState state, boolean isSystem,
+            boolean isPackageEnabled, ParsedMainComponent component, long flags) {
+        return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled,
+                component.isEnabled(), component.isDirectBootAware(), component.getName(), flags);
+    }
+
+    public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled,
+            ParsedMainComponent parsedComponent, long flags) {
+        return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
+                parsedComponent.getName(), flags);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
new file mode 100644
index 0000000..6d978c4
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+
+/** @hide **/
+public interface ParsedActivity extends ParsedMainComponent {
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    @NonNull
+    static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
+            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
+        // Proxy method since ParsedActivityImpl is supposed to be package visibility
+        return ParsedActivityImpl.makeAppDetailsActivity(packageName, processName, uiOptions,
+                taskAffinity, hardwareAccelerated);
+    }
+
+    int getColorMode();
+
+    int getConfigChanges();
+
+    int getDocumentLaunchMode();
+
+    int getLaunchMode();
+
+    int getLockTaskLaunchMode();
+
+    int getMaxRecents();
+
+    float getMaxAspectRatio();
+
+    float getMinAspectRatio();
+
+    @Nullable
+    String getParentActivityName();
+
+    @Nullable
+    String getPermission();
+
+    int getPersistableMode();
+
+    int getPrivateFlags();
+
+    @Nullable
+    String getRequestedVrComponent();
+
+    int getRotationAnimation();
+
+    int getResizeMode();
+
+    int getScreenOrientation();
+
+    int getSoftInputMode();
+
+    @Nullable
+    String getTargetActivity();
+
+    @Nullable
+    String getTaskAffinity();
+
+    int getTheme();
+
+    int getUiOptions();
+
+    @Nullable
+    ActivityInfo.WindowLayout getWindowLayout();
+
+    boolean isSupportsSizeChanges();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
new file mode 100644
index 0000000..ff97c13
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity {
+
+    private int theme;
+    private int uiOptions;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetActivity;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String parentActivityName;
+    @Nullable
+    private String taskAffinity;
+    private int privateFlags;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+
+    private int launchMode;
+    private int documentLaunchMode;
+    private int maxRecents;
+    private int configChanges;
+    private int softInputMode;
+    private int persistableMode;
+    private int lockTaskLaunchMode;
+
+    private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+    private float maxAspectRatio = ParsingUtils.NOT_SET;
+    private float minAspectRatio = ParsingUtils.NOT_SET;
+
+    private boolean supportsSizeChanges;
+
+    @Nullable
+    private String requestedVrComponent;
+    private int rotationAnimation = -1;
+    private int colorMode;
+
+    @Nullable
+    private ActivityInfo.WindowLayout windowLayout;
+
+    public ParsedActivityImpl(ParsedActivityImpl other) {
+        super(other);
+        this.theme = other.theme;
+        this.uiOptions = other.uiOptions;
+        this.targetActivity = other.targetActivity;
+        this.parentActivityName = other.parentActivityName;
+        this.taskAffinity = other.taskAffinity;
+        this.privateFlags = other.privateFlags;
+        this.permission = other.permission;
+        this.launchMode = other.launchMode;
+        this.documentLaunchMode = other.documentLaunchMode;
+        this.maxRecents = other.maxRecents;
+        this.configChanges = other.configChanges;
+        this.softInputMode = other.softInputMode;
+        this.persistableMode = other.persistableMode;
+        this.lockTaskLaunchMode = other.lockTaskLaunchMode;
+        this.screenOrientation = other.screenOrientation;
+        this.resizeMode = other.resizeMode;
+        this.maxAspectRatio = other.maxAspectRatio;
+        this.minAspectRatio = other.minAspectRatio;
+        this.supportsSizeChanges = other.supportsSizeChanges;
+        this.requestedVrComponent = other.requestedVrComponent;
+        this.rotationAnimation = other.rotationAnimation;
+        this.colorMode = other.colorMode;
+        this.windowLayout = other.windowLayout;
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    @NonNull
+    static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
+            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
+        ParsedActivityImpl activity = new ParsedActivityImpl();
+        activity.setPackageName(packageName);
+        activity.theme = android.R.style.Theme_NoDisplay;
+        activity.setExported(true);
+        activity.setName(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+        activity.setProcessName(processName);
+        activity.uiOptions = uiOptions;
+        activity.taskAffinity = taskAffinity;
+        activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+        activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
+        activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
+        activity.configChanges = ParsedActivityUtils.getActivityConfigChanges(0, 0);
+        activity.softInputMode = 0;
+        activity.persistableMode = ActivityInfo.PERSIST_NEVER;
+        activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        activity.lockTaskLaunchMode = 0;
+        activity.setDirectBootAware(false);
+        activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        if (hardwareAccelerated) {
+            activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_HARDWARE_ACCELERATED);
+        }
+        return activity;
+    }
+
+    @NonNull
+    static ParsedActivityImpl makeAlias(String targetActivityName, ParsedActivity target) {
+        ParsedActivityImpl alias = new ParsedActivityImpl();
+        alias.setPackageName(target.getPackageName());
+        alias.setTargetActivity(targetActivityName);
+        alias.configChanges = target.getConfigChanges();
+        alias.setFlags(target.getFlags());
+        alias.privateFlags = target.getPrivateFlags();
+        alias.setIcon(target.getIcon());
+        alias.setLogo(target.getLogo());
+        alias.setBanner(target.getBanner());
+        alias.setLabelRes(target.getLabelRes());
+        alias.setNonLocalizedLabel(target.getNonLocalizedLabel());
+        alias.launchMode = target.getLaunchMode();
+        alias.lockTaskLaunchMode = target.getLockTaskLaunchMode();
+        alias.documentLaunchMode = target.getDocumentLaunchMode();
+        alias.setDescriptionRes(target.getDescriptionRes());
+        alias.screenOrientation = target.getScreenOrientation();
+        alias.taskAffinity = target.getTaskAffinity();
+        alias.theme = target.getTheme();
+        alias.softInputMode = target.getSoftInputMode();
+        alias.uiOptions = target.getUiOptions();
+        alias.parentActivityName = target.getParentActivityName();
+        alias.maxRecents = target.getMaxRecents();
+        alias.windowLayout = target.getWindowLayout();
+        alias.resizeMode = target.getResizeMode();
+        alias.maxAspectRatio = target.getMaxAspectRatio();
+        alias.minAspectRatio = target.getMinAspectRatio();
+        alias.supportsSizeChanges = target.isSupportsSizeChanges();
+        alias.requestedVrComponent = target.getRequestedVrComponent();
+        alias.setDirectBootAware(target.isDirectBootAware());
+        alias.setProcessName(target.getProcessName());
+        return alias;
+
+        // Not all attributes from the target ParsedActivity are copied to the alias.
+        // Careful when adding an attribute and determine whether or not it should be copied.
+//        alias.enabled = target.enabled;
+//        alias.exported = target.exported;
+//        alias.permission = target.permission;
+//        alias.splitName = target.splitName;
+//        alias.persistableMode = target.persistableMode;
+//        alias.rotationAnimation = target.rotationAnimation;
+//        alias.colorMode = target.colorMode;
+//        alias.intents.addAll(target.intents);
+//        alias.order = target.order;
+//        alias.metaData = target.metaData;
+    }
+
+    public ParsedActivityImpl setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
+        if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
+                || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+            // Resizeable activities can be put in any aspect ratio.
+            return this;
+        }
+
+        if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+            // Ignore any value lesser than 1.0.
+            return this;
+        }
+
+        this.maxAspectRatio = maxAspectRatio;
+        return this;
+    }
+
+    public ParsedActivityImpl setMinAspectRatio(int resizeMode, float minAspectRatio) {
+        if (resizeMode == RESIZE_MODE_RESIZEABLE
+                || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+            // Resizeable activities can be put in any aspect ratio.
+            return this;
+        }
+
+        if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+            // Ignore any value lesser than 1.0.
+            return this;
+        }
+
+        this.minAspectRatio = minAspectRatio;
+        return this;
+    }
+
+    public ParsedActivityImpl setTargetActivity(String targetActivity) {
+        this.targetActivity = TextUtils.safeIntern(targetActivity);
+        return this;
+    }
+
+    public ParsedActivityImpl setPermission(String permission) {
+        // Empty string must be converted to null
+        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Activity{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.theme);
+        dest.writeInt(this.uiOptions);
+        dest.writeString(this.targetActivity);
+        dest.writeString(this.parentActivityName);
+        dest.writeString(this.taskAffinity);
+        dest.writeInt(this.privateFlags);
+        sForInternedString.parcel(this.permission, dest, flags);
+        dest.writeInt(this.launchMode);
+        dest.writeInt(this.documentLaunchMode);
+        dest.writeInt(this.maxRecents);
+        dest.writeInt(this.configChanges);
+        dest.writeInt(this.softInputMode);
+        dest.writeInt(this.persistableMode);
+        dest.writeInt(this.lockTaskLaunchMode);
+        dest.writeInt(this.screenOrientation);
+        dest.writeInt(this.resizeMode);
+        dest.writeValue(this.maxAspectRatio);
+        dest.writeValue(this.minAspectRatio);
+        dest.writeBoolean(this.supportsSizeChanges);
+        dest.writeString(this.requestedVrComponent);
+        dest.writeInt(this.rotationAnimation);
+        dest.writeInt(this.colorMode);
+        dest.writeBundle(this.getMetaData());
+
+        if (windowLayout != null) {
+            dest.writeInt(1);
+            windowLayout.writeToParcel(dest);
+        } else {
+            dest.writeBoolean(false);
+        }
+    }
+
+    public ParsedActivityImpl() {
+    }
+
+    protected ParsedActivityImpl(Parcel in) {
+        super(in);
+        this.theme = in.readInt();
+        this.uiOptions = in.readInt();
+        this.targetActivity = in.readString();
+        this.parentActivityName = in.readString();
+        this.taskAffinity = in.readString();
+        this.privateFlags = in.readInt();
+        this.permission = sForInternedString.unparcel(in);
+        this.launchMode = in.readInt();
+        this.documentLaunchMode = in.readInt();
+        this.maxRecents = in.readInt();
+        this.configChanges = in.readInt();
+        this.softInputMode = in.readInt();
+        this.persistableMode = in.readInt();
+        this.lockTaskLaunchMode = in.readInt();
+        this.screenOrientation = in.readInt();
+        this.resizeMode = in.readInt();
+        this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.supportsSizeChanges = in.readBoolean();
+        this.requestedVrComponent = in.readString();
+        this.rotationAnimation = in.readInt();
+        this.colorMode = in.readInt();
+        this.setMetaData(in.readBundle());
+        if (in.readBoolean()) {
+            windowLayout = new ActivityInfo.WindowLayout(in);
+        }
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedActivityImpl> CREATOR =
+            new Parcelable.Creator<ParsedActivityImpl>() {
+        @Override
+        public ParsedActivityImpl createFromParcel(Parcel source) {
+            return new ParsedActivityImpl(source);
+        }
+
+        @Override
+        public ParsedActivityImpl[] newArray(int size) {
+            return new ParsedActivityImpl[size];
+        }
+    };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedActivityImpl(
+            int theme,
+            int uiOptions,
+            @Nullable String targetActivity,
+            @Nullable String parentActivityName,
+            @Nullable String taskAffinity,
+            int privateFlags,
+            @Nullable String permission,
+            int launchMode,
+            int documentLaunchMode,
+            int maxRecents,
+            int configChanges,
+            int softInputMode,
+            int persistableMode,
+            int lockTaskLaunchMode,
+            int screenOrientation,
+            int resizeMode,
+            float maxAspectRatio,
+            float minAspectRatio,
+            boolean supportsSizeChanges,
+            @Nullable String requestedVrComponent,
+            int rotationAnimation,
+            int colorMode,
+            @Nullable ActivityInfo.WindowLayout windowLayout) {
+        this.theme = theme;
+        this.uiOptions = uiOptions;
+        this.targetActivity = targetActivity;
+        this.parentActivityName = parentActivityName;
+        this.taskAffinity = taskAffinity;
+        this.privateFlags = privateFlags;
+        this.permission = permission;
+        this.launchMode = launchMode;
+        this.documentLaunchMode = documentLaunchMode;
+        this.maxRecents = maxRecents;
+        this.configChanges = configChanges;
+        this.softInputMode = softInputMode;
+        this.persistableMode = persistableMode;
+        this.lockTaskLaunchMode = lockTaskLaunchMode;
+        this.screenOrientation = screenOrientation;
+        this.resizeMode = resizeMode;
+        this.maxAspectRatio = maxAspectRatio;
+        this.minAspectRatio = minAspectRatio;
+        this.supportsSizeChanges = supportsSizeChanges;
+        this.requestedVrComponent = requestedVrComponent;
+        this.rotationAnimation = rotationAnimation;
+        this.colorMode = colorMode;
+        this.windowLayout = windowLayout;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getTheme() {
+        return theme;
+    }
+
+    @DataClass.Generated.Member
+    public int getUiOptions() {
+        return uiOptions;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTargetActivity() {
+        return targetActivity;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getParentActivityName() {
+        return parentActivityName;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTaskAffinity() {
+        return taskAffinity;
+    }
+
+    @DataClass.Generated.Member
+    public int getPrivateFlags() {
+        return privateFlags;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getPermission() {
+        return permission;
+    }
+
+    @DataClass.Generated.Member
+    public int getLaunchMode() {
+        return launchMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getDocumentLaunchMode() {
+        return documentLaunchMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getMaxRecents() {
+        return maxRecents;
+    }
+
+    @DataClass.Generated.Member
+    public int getConfigChanges() {
+        return configChanges;
+    }
+
+    @DataClass.Generated.Member
+    public int getSoftInputMode() {
+        return softInputMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getPersistableMode() {
+        return persistableMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getLockTaskLaunchMode() {
+        return lockTaskLaunchMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getScreenOrientation() {
+        return screenOrientation;
+    }
+
+    @DataClass.Generated.Member
+    public int getResizeMode() {
+        return resizeMode;
+    }
+
+    @DataClass.Generated.Member
+    public float getMaxAspectRatio() {
+        return maxAspectRatio;
+    }
+
+    @DataClass.Generated.Member
+    public float getMinAspectRatio() {
+        return minAspectRatio;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isSupportsSizeChanges() {
+        return supportsSizeChanges;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getRequestedVrComponent() {
+        return requestedVrComponent;
+    }
+
+    @DataClass.Generated.Member
+    public int getRotationAnimation() {
+        return rotationAnimation;
+    }
+
+    @DataClass.Generated.Member
+    public int getColorMode() {
+        return colorMode;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable ActivityInfo.WindowLayout getWindowLayout() {
+        return windowLayout;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setTheme( int value) {
+        theme = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setUiOptions( int value) {
+        uiOptions = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setParentActivityName(@NonNull String value) {
+        parentActivityName = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setTaskAffinity(@NonNull String value) {
+        taskAffinity = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setPrivateFlags( int value) {
+        privateFlags = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setLaunchMode( int value) {
+        launchMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setDocumentLaunchMode( int value) {
+        documentLaunchMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setMaxRecents( int value) {
+        maxRecents = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setConfigChanges( int value) {
+        configChanges = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setSoftInputMode( int value) {
+        softInputMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setPersistableMode( int value) {
+        persistableMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setLockTaskLaunchMode( int value) {
+        lockTaskLaunchMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setScreenOrientation( int value) {
+        screenOrientation = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setResizeMode( int value) {
+        resizeMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setMaxAspectRatio( float value) {
+        maxAspectRatio = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setMinAspectRatio( float value) {
+        minAspectRatio = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setSupportsSizeChanges( boolean value) {
+        supportsSizeChanges = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setRequestedVrComponent(@NonNull String value) {
+        requestedVrComponent = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setRotationAnimation( int value) {
+        rotationAnimation = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setColorMode( int value) {
+        colorMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setWindowLayout(@NonNull ActivityInfo.WindowLayout value) {
+        windowLayout = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1630600615936L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java",
+            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
new file mode 100644
index 0000000..db8815e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseInput.DeferredError;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class ParsedActivityUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    public static final boolean LOG_UNSAFE_BROADCASTS = false;
+
+    // Set of broadcast actions that are safe for manifest receivers
+    public static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
+    static {
+        SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
+    }
+
+    /**
+     * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
+     */
+    private static final int RECREATE_ON_CONFIG_CHANGES_MASK =
+            ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
+
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            boolean useRoundIcon, @Nullable String defaultSplitName, ParseInput input)
+            throws XmlPullParserException, IOException {
+        final String packageName = pkg.getPackageName();
+        final ParsedActivityImpl activity = new ParsedActivityImpl();
+
+        boolean receiver = "receiver".equals(parser.getName());
+        String tag = "<" + parser.getName() + ">";
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
+        try {
+            ParseResult<ParsedActivityImpl> result =
+                    ParsedMainComponentUtils.parseMainComponent(activity, tag, separateProcesses,
+                            pkg, sa, flags, useRoundIcon, defaultSplitName, input,
+                            R.styleable.AndroidManifestActivity_banner,
+                            R.styleable.AndroidManifestActivity_description,
+                            R.styleable.AndroidManifestActivity_directBootAware,
+                            R.styleable.AndroidManifestActivity_enabled,
+                            R.styleable.AndroidManifestActivity_icon,
+                            R.styleable.AndroidManifestActivity_label,
+                            R.styleable.AndroidManifestActivity_logo,
+                            R.styleable.AndroidManifestActivity_name,
+                            R.styleable.AndroidManifestActivity_process,
+                            R.styleable.AndroidManifestActivity_roundIcon,
+                            R.styleable.AndroidManifestActivity_splitName,
+                            R.styleable.AndroidManifestActivity_attributionTags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            if (receiver && pkg.isCantSaveState()) {
+                // A heavy-weight application can not have receivers in its main process
+                if (Objects.equals(activity.getProcessName(), packageName)) {
+                    return input.error("Heavy-weight applications can not have receivers "
+                            + "in main process");
+                }
+            }
+
+            // The following section has formatting off to make it easier to read the flags.
+            // Multi-lining them to fit within the column restriction makes it hard to tell what
+            // field is assigned where.
+            // @formatter:off
+            activity.setTheme(sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0))
+                    .setUiOptions(sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions()));
+
+            activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
+                                | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
+                                | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
+                                | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
+                                | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
+                                | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
+                                | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
+                                | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
+                                | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
+                                | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
+                                | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
+                                | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
+                                | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa)));
+
+            if (!receiver) {
+                activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
+                                        | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
+                                        | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
+                                        | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
+                                        | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
+                                        | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
+                                        | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
+                                        | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
+                                        | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
+                                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
+
+                activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
+                                        R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
+                                        | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND,
+                                        R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa)));
+
+                activity.setColorMode(sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT))
+                        .setDocumentLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE))
+                        .setLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE))
+                        .setLockTaskLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0))
+                        .setMaxRecents(sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic()))
+                        .setPersistableMode(sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY))
+                        .setRequestedVrComponent(sa.getString(R.styleable.AndroidManifestActivity_enableVrMode))
+                        .setRotationAnimation(sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED))
+                        .setSoftInputMode(sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0))
+                        .setConfigChanges(getActivityConfigChanges(
+                                sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+                                sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0))
+                        );
+
+                int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
+                int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
+                activity.setScreenOrientation(screenOrientation)
+                        .setResizeMode(resizeMode);
+
+                if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                        && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                        == TypedValue.TYPE_FLOAT) {
+                    activity.setMaxAspectRatio(resizeMode,
+                            sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
+                                    0 /*default*/));
+                }
+
+                if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+                        && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+                        == TypedValue.TYPE_FLOAT) {
+                    activity.setMinAspectRatio(resizeMode,
+                            sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+                                    0 /*default*/));
+                }
+            } else {
+                activity.setLaunchMode(ActivityInfo.LAUNCH_MULTIPLE)
+                        .setConfigChanges(0)
+                        .setFlags(activity.getFlags()|flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa));
+            }
+            // @formatter:on
+
+            String taskAffinity = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivity_taskAffinity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+
+            ParseResult<String> affinityNameResult = ComponentParseUtils.buildTaskAffinityName(
+                    packageName, pkg.getTaskAffinity(), taskAffinity, input);
+            if (affinityNameResult.isError()) {
+                return input.error(affinityNameResult);
+            }
+
+            activity.setTaskAffinity(affinityNameResult.getResult());
+
+            boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
+                pkg.setVisibleToInstantApps(true);
+            }
+
+            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
+                    false /*isAlias*/, visibleToEphemeral, input,
+                    R.styleable.AndroidManifestActivity_parentActivityName,
+                    R.styleable.AndroidManifestActivity_permission,
+                    R.styleable.AndroidManifestActivity_exported
+            );
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, boolean useRoundIcon, @Nullable String defaultSplitName,
+            @NonNull ParseInput input) throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivityAlias);
+        try {
+            String targetActivity = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivityAlias_targetActivity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+            if (targetActivity == null) {
+                return input.error("<activity-alias> does not specify android:targetActivity");
+            }
+
+            String packageName = pkg.getPackageName();
+            targetActivity = ParsingUtils.buildClassName(packageName, targetActivity);
+            if (targetActivity == null) {
+                return input.error("Empty class name in package " + packageName);
+            }
+
+            ParsedActivity target = null;
+
+            List<ParsedActivity> activities = pkg.getActivities();
+            final int activitiesSize = ArrayUtils.size(activities);
+            for (int i = 0; i < activitiesSize; i++) {
+                ParsedActivity t = activities.get(i);
+                if (targetActivity.equals(t.getName())) {
+                    target = t;
+                    break;
+                }
+            }
+
+            if (target == null) {
+                return input.error("<activity-alias> target activity " + targetActivity
+                        + " not found in manifest with activities = "
+                        + pkg.getActivities()
+                        + ", parsedActivities = " + activities);
+            }
+
+            ParsedActivityImpl activity = ParsedActivityImpl.makeAlias(targetActivity, target);
+            String tag = "<" + parser.getName() + ">";
+
+            ParseResult<ParsedActivityImpl> result = ParsedMainComponentUtils.parseMainComponent(
+                    activity, tag, null, pkg, sa, 0, useRoundIcon, defaultSplitName, input,
+                    R.styleable.AndroidManifestActivityAlias_banner,
+                    R.styleable.AndroidManifestActivityAlias_description,
+                    NOT_SET /*directBootAwareAttr*/,
+                    R.styleable.AndroidManifestActivityAlias_enabled,
+                    R.styleable.AndroidManifestActivityAlias_icon,
+                    R.styleable.AndroidManifestActivityAlias_label,
+                    R.styleable.AndroidManifestActivityAlias_logo,
+                    R.styleable.AndroidManifestActivityAlias_name,
+                    NOT_SET /*processAttr*/,
+                    R.styleable.AndroidManifestActivityAlias_roundIcon,
+                    NOT_SET /*splitNameAttr*/,
+                    R.styleable.AndroidManifestActivityAlias_attributionTags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            // TODO add visibleToInstantApps attribute to activity alias
+            final boolean visibleToEphemeral =
+                    ((activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
+
+            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, false /*isReceiver*/, true /*isAlias*/,
+                    visibleToEphemeral, input,
+                    R.styleable.AndroidManifestActivityAlias_parentActivityName,
+                    R.styleable.AndroidManifestActivityAlias_permission,
+                    R.styleable.AndroidManifestActivityAlias_exported);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    /**
+     * This method shares parsing logic between Activity/Receiver/alias instances, but requires
+     * passing in booleans for isReceiver/isAlias, since there's no indicator in the other
+     * parameters.
+     *
+     * They're used to filter the parsed tags and their behavior. This makes the method rather
+     * messy, but it's more maintainable than writing 3 separate methods for essentially the same
+     * type of logic.
+     */
+    @NonNull
+    private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivityImpl activity,
+            ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
+            TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
+            ParseInput input, int parentActivityNameAttr, int permissionAttr,
+            int exportedAttr) throws IOException, XmlPullParserException {
+        String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
+        if (parentActivityName != null) {
+            String packageName = pkg.getPackageName();
+            String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);
+            if (parentClassName == null) {
+                Log.e(TAG, "Activity " + activity.getName()
+                        + " specified invalid parentActivityName " + parentActivityName);
+            } else {
+                activity.setParentActivityName(parentClassName);
+            }
+        }
+
+        String permission = array.getNonConfigurationString(permissionAttr, 0);
+        if (isAlias) {
+            // An alias will override permissions to allow referencing an Activity through its alias
+            // without needing the original permission. If an alias needs the same permission,
+            // it must be re-declared.
+            activity.setPermission(permission);
+        } else {
+            activity.setPermission(permission != null ? permission : pkg.getPermission());
+        }
+
+        final boolean setExported = array.hasValue(exportedAttr);
+        if (setExported) {
+            activity.setExported(array.getBoolean(exportedAttr, false));
+        }
+
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            if (parser.getName().equals("intent-filter")) {
+                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+                        !isReceiver, visibleToEphemeral, resources, parser, input);
+                if (intentResult.isSuccess()) {
+                    ParsedIntentInfo intentInfo = intentResult.getResult();
+                    if (intentInfo != null) {
+                        IntentFilter intentFilter = intentInfo.getIntentFilter();
+                        activity.setOrder(Math.max(intentFilter.getOrder(), activity.getOrder()));
+                        activity.addIntent(intentInfo);
+                        if (LOG_UNSAFE_BROADCASTS && isReceiver
+                                && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+                            int actionCount = intentFilter.countActions();
+                            for (int i = 0; i < actionCount; i++) {
+                                final String action = intentFilter.getAction(i);
+                                if (action == null || !action.startsWith("android.")) {
+                                    continue;
+                                }
+
+                                if (!SAFE_BROADCASTS.contains(action)) {
+                                    Slog.w(TAG,
+                                            "Broadcast " + action + " may never be delivered to "
+                                                    + pkg.getPackageName() + " as requested at: "
+                                                    + parser.getPositionDescription());
+                                }
+                            }
+                        }
+                    }
+                }
+                result = intentResult;
+            } else if (parser.getName().equals("meta-data")) {
+                result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
+            } else if (parser.getName().equals("property")) {
+                result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
+            } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
+                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+                        true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
+                        resources, parser, input);
+                if (intentResult.isSuccess()) {
+                    ParsedIntentInfo intent = intentResult.getResult();
+                    if (intent != null) {
+                        pkg.addPreferredActivityFilter(activity.getClassName(), intent);
+                    }
+                }
+                result = intentResult;
+            } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
+                ParseResult<ActivityInfo.WindowLayout> layoutResult =
+                        parseActivityWindowLayout(resources, parser, input);
+                if (layoutResult.isSuccess()) {
+                    activity.setWindowLayout(layoutResult.getResult());
+                }
+                result = layoutResult;
+            } else {
+                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (!isAlias && activity.getLaunchMode() != LAUNCH_SINGLE_INSTANCE_PER_TASK
+                && activity.getMetaData() != null && activity.getMetaData().containsKey(
+                ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
+            final String launchMode = activity.getMetaData().getString(
+                    ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
+            if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
+                activity.setLaunchMode(LAUNCH_SINGLE_INSTANCE_PER_TASK);
+            }
+        }
+
+        if (!isAlias) {
+            // Default allow the activity to be displayed on a remote device unless it explicitly
+            // set to false.
+            boolean canDisplayOnRemoteDevices = array.getBoolean(
+                    R.styleable.AndroidManifestActivity_canDisplayOnRemoteDevices, true);
+            if (activity.getMetaData() != null && !activity.getMetaData().getBoolean(
+                    ParsingPackageUtils.METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES, true)) {
+                canDisplayOnRemoteDevices = false;
+            }
+            if (canDisplayOnRemoteDevices) {
+                activity.setFlags(activity.getFlags()
+                        | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES);
+            }
+        }
+
+        ParseResult<ActivityInfo.WindowLayout> layoutResult =
+                resolveActivityWindowLayout(activity, input);
+        if (layoutResult.isError()) {
+            return input.error(layoutResult);
+        }
+        activity.setWindowLayout(layoutResult.getResult());
+
+        if (!setExported) {
+            boolean hasIntentFilters = activity.getIntents().size() > 0;
+            if (hasIntentFilters) {
+                final ParseResult exportedCheckResult = input.deferError(
+                        activity.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
+                        + " and above) requires that an explicit value for android:exported be"
+                        + " defined when intent filters are present",
+                        DeferredError.MISSING_EXPORTED_FLAG);
+                if (exportedCheckResult.isError()) {
+                    return input.error(exportedCheckResult);
+                }
+            }
+            activity.setExported(hasIntentFilters);
+        }
+
+        return input.success(activity);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg,
+            ParsedActivityImpl activity, boolean allowImplicitEphemeralVisibility,
+            boolean visibleToEphemeral, Resources resources, XmlResourceParser parser,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity,
+                pkg, resources, parser, visibleToEphemeral, true /*allowGlobs*/,
+                true /*allowAutoVerify*/, allowImplicitEphemeralVisibility,
+                true /*failOnNoActions*/, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        ParsedIntentInfo intent = result.getResult();
+        if (intent != null) {
+            final IntentFilter intentFilter = intent.getIntentFilter();
+            if (intentFilter.isVisibleToInstantApp()) {
+                activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
+            }
+            if (intentFilter.isImplicitlyVisibleToInstantApp()) {
+                activity.setFlags(
+                        activity.getFlags() | ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP);
+            }
+        }
+
+        return input.success(intent);
+    }
+
+    private static int getActivityResizeMode(ParsingPackage pkg, TypedArray sa,
+            int screenOrientation) {
+        Boolean resizeableActivity = pkg.getResizeableActivity();
+
+        if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
+                || resizeableActivity != null) {
+            // Activity or app explicitly set if it is resizeable or not;
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+                    resizeableActivity != null && resizeableActivity)) {
+                return ActivityInfo.RESIZE_MODE_RESIZEABLE;
+            } else {
+                return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+            }
+        }
+
+        if (pkg.isResizeableActivityViaSdkVersion()) {
+            // The activity or app didn't explicitly set the resizing option, however we want to
+            // make it resize due to the sdk version it is targeting.
+            return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+        }
+
+        // resize preference isn't set and target sdk version doesn't support resizing apps by
+        // default. For the app to be resizeable if it isn't fixed orientation or immersive.
+        if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+        } else {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res,
+            AttributeSet attrs, ParseInput input) {
+        TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
+        try {
+            int width = -1;
+            float widthFraction = -1f;
+            int height = -1;
+            float heightFraction = -1f;
+            final int widthType = sw.getType(R.styleable.AndroidManifestLayout_defaultWidth);
+            if (widthType == TypedValue.TYPE_FRACTION) {
+                widthFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultWidth, 1, 1,
+                        -1);
+            } else if (widthType == TypedValue.TYPE_DIMENSION) {
+                width = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultWidth,
+                        -1);
+            }
+            final int heightType = sw.getType(R.styleable.AndroidManifestLayout_defaultHeight);
+            if (heightType == TypedValue.TYPE_FRACTION) {
+                heightFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultHeight, 1,
+                        1, -1);
+            } else if (heightType == TypedValue.TYPE_DIMENSION) {
+                height = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultHeight,
+                        -1);
+            }
+            int gravity = sw.getInt(R.styleable.AndroidManifestLayout_gravity, Gravity.CENTER);
+            int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
+            int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
+                    -1);
+            String windowLayoutAffinity =
+                    sw.getNonConfigurationString(
+                            R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0);
+            final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width,
+                    widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+                    windowLayoutAffinity);
+            return input.success(windowLayout);
+        } finally {
+            sw.recycle();
+        }
+    }
+
+    /**
+     * Resolves values in {@link ActivityInfo.WindowLayout}.
+     *
+     * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+     * Android R and some variants of pre-R.
+     */
+    private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
+            ParsedActivity activity, ParseInput input) {
+        // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+        if (activity.getMetaData() == null || !activity.getMetaData().containsKey(
+                ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+            return input.success(activity.getWindowLayout());
+        }
+
+        // Layout already specifies a value. We should just use that one.
+        if (activity.getWindowLayout() != null && activity.getWindowLayout().windowLayoutAffinity != null) {
+            return input.success(activity.getWindowLayout());
+        }
+
+        String windowLayoutAffinity = activity.getMetaData().getString(
+                ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+        ActivityInfo.WindowLayout layout = activity.getWindowLayout();
+        if (layout == null) {
+            layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
+                    -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
+                    -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity);
+        } else {
+            layout.windowLayoutAffinity = windowLayoutAffinity;
+        }
+        return input.success(layout);
+    }
+
+    /**
+     * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
+     * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
+     *                                AndroidManifest.xml.
+     * @hide
+     */
+    public static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) {
+        return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
new file mode 100644
index 0000000..7690818
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+/** @hide */
+public interface ParsedApexSystemService extends Parcelable {
+
+    @NonNull
+    String getName();
+
+    @Nullable
+    String getJarPath();
+
+    @Nullable
+    String getMinSdkVersion();
+
+    @Nullable
+    String getMaxSdkVersion();
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
new file mode 100644
index 0000000..8c4d8f0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+/** @hide **/
+@DataClass(genGetters = true, genAidl = false, genSetters = true, genParcelable = true)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedApexSystemServiceImpl implements ParsedApexSystemService {
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @NonNull
+    private String name;
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @Nullable
+    private String jarPath;
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @Nullable
+    private String minSdkVersion;
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @Nullable
+    private String maxSdkVersion;
+
+    public ParsedApexSystemServiceImpl() {
+    }
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedApexSystemServiceImpl(
+            @NonNull String name,
+            @Nullable String jarPath,
+            @Nullable String minSdkVersion,
+            @Nullable String maxSdkVersion) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.jarPath = jarPath;
+        this.minSdkVersion = minSdkVersion;
+        this.maxSdkVersion = maxSdkVersion;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getJarPath() {
+        return jarPath;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getMinSdkVersion() {
+        return minSdkVersion;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getMaxSdkVersion() {
+        return maxSdkVersion;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setJarPath(@NonNull String value) {
+        jarPath = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setMinSdkVersion(@NonNull String value) {
+        minSdkVersion = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setMaxSdkVersion(@NonNull String value) {
+        maxSdkVersion = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForName =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForName == null) {
+            sParcellingForName = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForJarPath =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForJarPath == null) {
+            sParcellingForJarPath = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForMinSdkVersion =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForMinSdkVersion == null) {
+            sParcellingForMinSdkVersion = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForMaxSdkVersion =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForMaxSdkVersion == null) {
+            sParcellingForMaxSdkVersion = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (jarPath != null) flg |= 0x2;
+        if (minSdkVersion != null) flg |= 0x4;
+        if (maxSdkVersion != null) flg |= 0x8;
+        dest.writeByte(flg);
+        sParcellingForName.parcel(name, dest, flags);
+        sParcellingForJarPath.parcel(jarPath, dest, flags);
+        sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags);
+        sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedApexSystemServiceImpl(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String _name = sParcellingForName.unparcel(in);
+        String _jarPath = sParcellingForJarPath.unparcel(in);
+        String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in);
+        String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in);
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.jarPath = _jarPath;
+        this.minSdkVersion = _minSdkVersion;
+        this.maxSdkVersion = _maxSdkVersion;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull android.os.Parcelable.Creator<ParsedApexSystemServiceImpl> CREATOR
+            = new android.os.Parcelable.Creator<ParsedApexSystemServiceImpl>() {
+        @Override
+        public ParsedApexSystemServiceImpl[] newArray(int size) {
+            return new ParsedApexSystemServiceImpl[size];
+        }
+
+        @Override
+        public ParsedApexSystemServiceImpl createFromParcel(@NonNull android.os.Parcel in) {
+            return new ParsedApexSystemServiceImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1638903241144L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService]\[email protected](genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
new file mode 100644
index 0000000..38a6f5a35
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.R;
+import android.annotation.NonNull;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedApexSystemServiceUtils {
+
+    @NonNull
+    public static ParseResult<ParsedApexSystemService> parseApexSystemService(
+            Resources res, XmlResourceParser parser, ParseInput input)
+            throws XmlPullParserException, IOException {
+        final ParsedApexSystemServiceImpl systemService =
+                new ParsedApexSystemServiceImpl();
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestApexSystemService);
+        try {
+            String className = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_name);
+            if (TextUtils.isEmpty(className)) {
+                return input.error("<apex-system-service> does not have name attribute");
+            }
+
+            String jarPath = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_path);
+            String minSdkVersion = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_minSdkVersion);
+            String maxSdkVersion = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_maxSdkVersion);
+
+            systemService.setName(className)
+                    .setMinSdkVersion(minSdkVersion)
+                    .setMaxSdkVersion(maxSdkVersion);
+            if (!TextUtils.isEmpty(jarPath)) {
+                systemService.setJarPath(jarPath);
+            }
+
+            return input.success(systemService);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
new file mode 100644
index 0000000..3b91f28
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+public interface ParsedAttribution extends Parcelable {
+
+    /**
+     * Maximum length of attribution tag
+     * @hide
+     */
+    int MAX_ATTRIBUTION_TAG_LEN = 50;
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @NonNull List<String> getInheritFrom();
+
+    /**
+     * User visible label for the attribution
+     */
+    @StringRes int getLabel();
+
+    /**
+     * Tag of the attribution
+     */
+    @NonNull String getTag();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
new file mode 100644
index 0000000..a4eb4f1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false, genSetters = true, genBuilder = false, genParcelable = true)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedAttributionImpl implements ParsedAttribution {
+
+    /** Maximum amount of attributions per package */
+    static final int MAX_NUM_ATTRIBUTIONS = 10000;
+
+    /** Tag of the attribution */
+    private @NonNull String tag;
+
+    /** User visible label fo the attribution */
+    private @StringRes int label;
+
+    /** Ids of previously declared attributions this attribution inherits from */
+    private @NonNull List<String> inheritFrom;
+
+    public ParsedAttributionImpl() {}
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ParsedAttributionImpl.
+     *
+     * @param tag
+     *   Tag of the attribution
+     * @param label
+     *   User visible label fo the attribution
+     * @param inheritFrom
+     *   Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public ParsedAttributionImpl(
+            @NonNull String tag,
+            @StringRes int label,
+            @NonNull List<String> inheritFrom) {
+        this.tag = tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Tag of the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getTag() {
+        return tag;
+    }
+
+    /**
+     * User visible label fo the attribution
+     */
+    @DataClass.Generated.Member
+    public @StringRes int getLabel() {
+        return label;
+    }
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getInheritFrom() {
+        return inheritFrom;
+    }
+
+    /**
+     * Tag of the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttributionImpl setTag(@NonNull String value) {
+        tag = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        return this;
+    }
+
+    /**
+     * User visible label fo the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttributionImpl setLabel(@StringRes int value) {
+        label = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        return this;
+    }
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttributionImpl setInheritFrom(@NonNull List<String> value) {
+        inheritFrom = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(tag);
+        dest.writeInt(label);
+        dest.writeStringList(inheritFrom);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedAttributionImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _tag = in.readString();
+        int _label = in.readInt();
+        List<String> _inheritFrom = new ArrayList<>();
+        in.readStringList(_inheritFrom);
+
+        this.tag = _tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = _label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = _inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedAttributionImpl> CREATOR
+            = new Parcelable.Creator<ParsedAttributionImpl>() {
+        @Override
+        public ParsedAttributionImpl[] newArray(int size) {
+            return new ParsedAttributionImpl[size];
+        }
+
+        @Override
+        public ParsedAttributionImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedAttributionImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627594502974L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java",
+            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution]\[email protected](genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
new file mode 100644
index 0000000..98e94c5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public class ParsedAttributionUtils {
+
+    @NonNull
+    public static ParseResult<ParsedAttribution> parseAttribution(Resources res,
+            XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        String attributionTag;
+        int label;
+        List<String> inheritFrom = null;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution);
+        if (sa == null) {
+            return input.error("<attribution> could not be parsed");
+        }
+
+        try {
+            attributionTag = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestAttribution_tag, 0);
+            if (attributionTag == null) {
+                return input.error("<attribution> does not specify android:tag");
+            }
+            if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) {
+                return input.error("android:tag is too long. Max length is "
+                        + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN);
+            }
+
+            label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0);
+            if (label == Resources.ID_NULL) {
+                return input.error("<attribution> does not specify android:label");
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("inherit-from")) {
+                sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestAttributionInheritFrom);
+                if (sa == null) {
+                    return input.error("<inherit-from> could not be parsed");
+                }
+
+                try {
+                    String inheritFromId = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestAttributionInheritFrom_tag, 0);
+
+                    if (inheritFrom == null) {
+                        inheritFrom = new ArrayList<>();
+                    }
+                    inheritFrom.add(inheritFromId);
+                } finally {
+                    sa.recycle();
+                }
+            } else {
+                return input.error("Bad element under <attribution>: " + tagName);
+            }
+        }
+
+        if (inheritFrom == null) {
+            inheritFrom = Collections.emptyList();
+        } else {
+            ((ArrayList) inheritFrom).trimToSize();
+        }
+
+        return input.success(new ParsedAttributionImpl(attributionTag, label, inheritFrom));
+    }
+
+    /**
+     * @return Is this set of attributions a valid combination for a single package?
+     */
+    public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
+        if (attributions == null) {
+            return true;
+        }
+
+        ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
+        ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
+
+        int numAttributions = attributions.size();
+        if (numAttributions > ParsedAttributionImpl.MAX_NUM_ATTRIBUTIONS) {
+            return false;
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            boolean wasAdded = attributionTags.add(attributions.get(attributionNum).getTag());
+            if (!wasAdded) {
+                // feature id is not unique
+                return false;
+            }
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            ParsedAttribution feature = attributions.get(attributionNum);
+
+            final List<String> inheritFromList = feature.getInheritFrom();
+            int numInheritFrom = inheritFromList.size();
+            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+                String inheritFrom = inheritFromList.get(inheritFromNum);
+
+                if (attributionTags.contains(inheritFrom)) {
+                    // Cannot inherit from a attribution that is still defined
+                    return false;
+                }
+
+                boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
+                if (!wasAdded) {
+                    // inheritFrom is not unique
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
new file mode 100644
index 0000000..1a8230d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PackageManager.Property;
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import java.util.List;
+import java.util.Map;
+
+/** @hide */
+public interface ParsedComponent extends Parcelable {
+
+    int getBanner();
+
+    @NonNull
+    ComponentName getComponentName();
+
+    int getDescriptionRes();
+
+    int getFlags();
+
+    int getIcon();
+
+    @NonNull
+    List<ParsedIntentInfo> getIntents();
+
+    int getLabelRes();
+
+    int getLogo();
+
+    @Nullable
+    Bundle getMetaData();
+
+    @NonNull
+    String getName();
+
+    @Nullable
+    CharSequence getNonLocalizedLabel();
+
+    @NonNull
+    String getPackageName();
+
+    @NonNull
+    Map<String, Property> getProperties();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
new file mode 100644
index 0000000..9125e8c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+
+import static java.util.Collections.emptyMap;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PackageManager.Property;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genConstructor = false, genBuilder = false)
[email protected]({"setComponentName", "setProperties", "setIntents"})
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public abstract class ParsedComponentImpl implements ParsedComponent {
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String name;
+    private int icon;
+    private int labelRes;
+    @Nullable
+    private CharSequence nonLocalizedLabel;
+    private int logo;
+    private int banner;
+    private int descriptionRes;
+
+    // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
+    private int flags;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String packageName;
+
+    @NonNull
+    @DataClass.PluralOf("intent")
+    private List<ParsedIntentInfo> intents = Collections.emptyList();
+
+    @Nullable
+    private ComponentName componentName;
+
+    @Nullable
+    private Bundle metaData;
+
+    @NonNull
+    private Map<String, Property> mProperties = emptyMap();
+
+    public ParsedComponentImpl() {
+
+    }
+
+    protected ParsedComponentImpl(ParsedComponent other) {
+        this.metaData = other.getMetaData();
+        this.name = other.getName();
+        this.icon = other.getIcon();
+        this.labelRes = other.getLabelRes();
+        this.nonLocalizedLabel = other.getNonLocalizedLabel();
+        this.logo = other.getLogo();
+        this.banner = other.getBanner();
+        this.descriptionRes = other.getDescriptionRes();
+        this.flags = other.getFlags();
+        this.packageName = other.getPackageName();
+        this.componentName = other.getComponentName();
+        this.intents = new ArrayList<>(other.getIntents());
+        this.mProperties = new ArrayMap<>();
+        this.mProperties.putAll(other.getProperties());
+    }
+
+    public void addIntent(ParsedIntentInfo intent) {
+        this.intents = CollectionUtils.add(this.intents, intent);
+    }
+
+    /** Add a property to the component */
+    public void addProperty(@NonNull Property property) {
+        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+    }
+
+    public ParsedComponentImpl setName(String name) {
+        this.name = TextUtils.safeIntern(name);
+        return this;
+    }
+
+    @CallSuper
+    public void setPackageName(@NonNull String packageName) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        //noinspection ConstantConditions
+        this.componentName = null;
+
+        // Note: this method does not edit name (which can point to a class), because this package
+        // name change is not changing the package in code, but the identifier used by the system.
+    }
+
+    @Override
+    @NonNull
+    public ComponentName getComponentName() {
+        if (componentName == null) {
+            componentName = new ComponentName(getPackageName(), getName());
+        }
+        return componentName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.name);
+        dest.writeInt(this.getIcon());
+        dest.writeInt(this.getLabelRes());
+        dest.writeCharSequence(this.getNonLocalizedLabel());
+        dest.writeInt(this.getLogo());
+        dest.writeInt(this.getBanner());
+        dest.writeInt(this.getDescriptionRes());
+        dest.writeInt(this.getFlags());
+        sForInternedString.parcel(this.packageName, dest, flags);
+        dest.writeTypedList(this.getIntents());
+        dest.writeBundle(this.metaData);
+        dest.writeMap(this.mProperties);
+    }
+
+    protected ParsedComponentImpl(Parcel in) {
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        //noinspection ConstantConditions
+        this.name = in.readString();
+        this.icon = in.readInt();
+        this.labelRes = in.readInt();
+        this.nonLocalizedLabel = in.readCharSequence();
+        this.logo = in.readInt();
+        this.banner = in.readInt();
+        this.descriptionRes = in.readInt();
+        this.flags = in.readInt();
+        //noinspection ConstantConditions
+        this.packageName = sForInternedString.unparcel(in);
+        this.intents = ParsingUtils.createTypedInterfaceList(in, ParsedIntentInfoImpl.CREATOR);
+        this.metaData = in.readBundle(boot);
+        this.mProperties = in.readHashMap(boot);
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public int getIcon() {
+        return icon;
+    }
+
+    @DataClass.Generated.Member
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    @DataClass.Generated.Member
+    public int getLogo() {
+        return logo;
+    }
+
+    @DataClass.Generated.Member
+    public int getBanner() {
+        return banner;
+    }
+
+    @DataClass.Generated.Member
+    public int getDescriptionRes() {
+        return descriptionRes;
+    }
+
+    @DataClass.Generated.Member
+    public int getFlags() {
+        return flags;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return packageName;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull List<ParsedIntentInfo> getIntents() {
+        return intents;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Bundle getMetaData() {
+        return metaData;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Map<String,Property> getProperties() {
+        return mProperties;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setIcon( int value) {
+        icon = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setLabelRes( int value) {
+        labelRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setNonLocalizedLabel(@NonNull CharSequence value) {
+        nonLocalizedLabel = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setLogo( int value) {
+        logo = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setBanner( int value) {
+        banner = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setDescriptionRes( int value) {
+        descriptionRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setFlags( int value) {
+        flags = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setMetaData(@NonNull Bundle value) {
+        metaData = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627680195484L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java",
+            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic  void addIntent(android.content.pm.parsing.component.ParsedIntentInfo)\npublic  void addProperty(android.content.pm.PackageManager.Property)\npublic  android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent]\[email protected](genGetters=true, genSetters=true, genConstructor=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
new file mode 100644
index 0000000..e208854
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.TypedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** @hide */
+class ParsedComponentUtils {
+
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    static <Component extends ParsedComponentImpl> ParseResult<Component> parseComponent(
+            Component component, String tag, ParsingPackage pkg, TypedArray array,
+            boolean useRoundIcon, ParseInput input, int bannerAttr, int descriptionAttr,
+            int iconAttr, int labelAttr, int logoAttr, int nameAttr, int roundIconAttr) {
+        String name = array.getNonConfigurationString(nameAttr, 0);
+        if (TextUtils.isEmpty(name)) {
+            return input.error(tag + " does not specify android:name");
+        }
+
+        String packageName = pkg.getPackageName();
+        String className = ParsingUtils.buildClassName(packageName, name);
+        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+            return input.error(tag + " invalid android:name");
+        }
+
+        //noinspection ConstantConditions; null check done above with isEmpty
+        component.setName(className)
+                .setPackageName(packageName);
+
+        int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
+        if (roundIconVal != 0) {
+            component.setIcon(roundIconVal)
+                    .setNonLocalizedLabel(null);
+        } else {
+            int iconVal = array.getResourceId(iconAttr, 0);
+            if (iconVal != 0) {
+                component.setIcon(iconVal);
+                component.setNonLocalizedLabel(null);
+            }
+        }
+
+        int logoVal = array.getResourceId(logoAttr, 0);
+        if (logoVal != 0) {
+            component.setLogo(logoVal);
+        }
+
+        int bannerVal = array.getResourceId(bannerAttr, 0);
+        if (bannerVal != 0) {
+            component.setBanner(bannerVal);
+        }
+
+        if (descriptionAttr != NOT_SET) {
+            component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
+        }
+
+        TypedValue v = array.peekValue(labelAttr);
+        if (v != null) {
+            component.setLabelRes(v.resourceId);
+            if (v.resourceId == 0) {
+                component.setNonLocalizedLabel(v.coerceToString());
+            }
+        }
+
+        return input.success(component);
+    }
+
+    static ParseResult<Bundle> addMetaData(ParsedComponentImpl component, ParsingPackage pkg,
+            Resources resources, XmlResourceParser parser, ParseInput input) {
+        ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
+                resources, parser, "<meta-data>", input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        final Property property = result.getResult();
+        if (property != null) {
+            component.setMetaData(property.toBundle(component.getMetaData()));
+        }
+        return input.success(component.getMetaData());
+    }
+
+    static ParseResult<Property> addProperty(ParsedComponentImpl component, ParsingPackage pkg,
+            Resources resources, XmlResourceParser parser, ParseInput input) {
+        ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
+                resources, parser, "<property>", input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        final Property property = result.getResult();
+        if (property != null) {
+            component.addProperty(property);
+        }
+        return input.success(property);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
new file mode 100644
index 0000000..a0eae8c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.Nullable;
+
+/** @hide */
+public interface ParsedInstrumentation extends ParsedComponent {
+
+    @Nullable
+    String getTargetPackage();
+
+    @Nullable
+    String getTargetProcesses();
+
+    boolean isFunctionalTest();
+
+    boolean isHandleProfiling();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
new file mode 100644
index 0000000..c8baa9e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedInstrumentationImpl extends ParsedComponentImpl implements
+        ParsedInstrumentation {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetPackage;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetProcesses;
+    private boolean handleProfiling;
+    private boolean functionalTest;
+
+    public ParsedInstrumentationImpl() {
+    }
+
+    public ParsedInstrumentationImpl setTargetPackage(@Nullable String targetPackage) {
+        this.targetPackage = TextUtils.safeIntern(targetPackage);
+        return this;
+    }
+
+    public ParsedInstrumentationImpl setTargetProcesses(@Nullable String targetProcesses) {
+        this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Instrumentation{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        sForInternedString.parcel(this.targetPackage, dest, flags);
+        sForInternedString.parcel(this.targetProcesses, dest, flags);
+        dest.writeBoolean(this.handleProfiling);
+        dest.writeBoolean(this.functionalTest);
+    }
+
+    protected ParsedInstrumentationImpl(Parcel in) {
+        super(in);
+        this.targetPackage = sForInternedString.unparcel(in);
+        this.targetProcesses = sForInternedString.unparcel(in);
+        this.handleProfiling = in.readByte() != 0;
+        this.functionalTest = in.readByte() != 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedInstrumentationImpl> CREATOR =
+            new Parcelable.Creator<ParsedInstrumentationImpl>() {
+                @Override
+                public ParsedInstrumentationImpl createFromParcel(Parcel source) {
+                    return new ParsedInstrumentationImpl(source);
+                }
+
+                @Override
+                public ParsedInstrumentationImpl[] newArray(int size) {
+                    return new ParsedInstrumentationImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedInstrumentationImpl(
+            @Nullable String targetPackage,
+            @Nullable String targetProcesses,
+            boolean handleProfiling,
+            boolean functionalTest) {
+        this.targetPackage = targetPackage;
+        this.targetProcesses = targetProcesses;
+        this.handleProfiling = handleProfiling;
+        this.functionalTest = functionalTest;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTargetPackage() {
+        return targetPackage;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTargetProcesses() {
+        return targetProcesses;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isHandleProfiling() {
+        return handleProfiling;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isFunctionalTest() {
+        return functionalTest;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedInstrumentationImpl setHandleProfiling( boolean value) {
+        handleProfiling = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedInstrumentationImpl setFunctionalTest( boolean value) {
+        functionalTest = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627595809880L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
new file mode 100644
index 0000000..51e1428
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+
+import android.annotation.NonNull;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedInstrumentationUtils {
+
+    @NonNull
+    public static ParseResult<ParsedInstrumentation> parseInstrumentation(ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, boolean useRoundIcon,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParsedInstrumentationImpl
+                instrumentation = new ParsedInstrumentationImpl();
+        String tag = "<" + parser.getName() + ">";
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
+        try {
+            ParseResult<ParsedInstrumentationImpl> result = ParsedComponentUtils.parseComponent(
+                    instrumentation, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestInstrumentation_banner,
+                    NOT_SET /*descriptionAttr*/,
+                    R.styleable.AndroidManifestInstrumentation_icon,
+                    R.styleable.AndroidManifestInstrumentation_label,
+                    R.styleable.AndroidManifestInstrumentation_logo,
+                    R.styleable.AndroidManifestInstrumentation_name,
+                    R.styleable.AndroidManifestInstrumentation_roundIcon);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            // @formatter:off
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage))
+                    .setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses))
+                    .setHandleProfiling(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false))
+                    .setFunctionalTest(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false));
+            // @formatter:on
+        } finally {
+            sa.recycle();
+        }
+
+        ParseResult<ParsedInstrumentationImpl> result =
+                ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, instrumentation, input);
+
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
new file mode 100644
index 0000000..57b486a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.os.Parcelable;
+
+/** @hide **/
+public interface ParsedIntentInfo extends Parcelable {
+
+    boolean isHasDefault();
+
+    int getLabelRes();
+
+    @Nullable CharSequence getNonLocalizedLabel();
+
+    int getIcon();
+
+    @NonNull IntentFilter getIntentFilter();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
new file mode 100644
index 0000000..1c816da
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+/**
+ * @hide
+ **/
+@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
+        genBuilder = false, genConstructor = false)
[email protected]({"setIntentFilter"})
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedIntentInfoImpl implements ParsedIntentInfo {
+
+    private boolean mHasDefault;
+    private int mLabelRes;
+    @Nullable
+    private CharSequence mNonLocalizedLabel;
+    private int mIcon;
+
+    @NonNull
+    private IntentFilter mIntentFilter = new IntentFilter();
+
+    public ParsedIntentInfoImpl() {}
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public boolean isHasDefault() {
+        return mHasDefault;
+    }
+
+    @DataClass.Generated.Member
+    public int getLabelRes() {
+        return mLabelRes;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getNonLocalizedLabel() {
+        return mNonLocalizedLabel;
+    }
+
+    @DataClass.Generated.Member
+    public int getIcon() {
+        return mIcon;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull IntentFilter getIntentFilter() {
+        return mIntentFilter;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setHasDefault( boolean value) {
+        mHasDefault = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setLabelRes( int value) {
+        mLabelRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setNonLocalizedLabel(@NonNull CharSequence value) {
+        mNonLocalizedLabel = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setIcon( int value) {
+        mIcon = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mHasDefault) flg |= 0x1;
+        if (mNonLocalizedLabel != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeInt(mLabelRes);
+        if (mNonLocalizedLabel != null) dest.writeCharSequence(mNonLocalizedLabel);
+        dest.writeInt(mIcon);
+        dest.writeTypedObject(mIntentFilter, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedIntentInfoImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean hasDefault = (flg & 0x1) != 0;
+        int labelRes = in.readInt();
+        CharSequence nonLocalizedLabel = (flg & 0x4) == 0 ? null : (CharSequence) in.readCharSequence();
+        int icon = in.readInt();
+        IntentFilter intentFilter = (IntentFilter) in.readTypedObject(IntentFilter.CREATOR);
+
+        this.mHasDefault = hasDefault;
+        this.mLabelRes = labelRes;
+        this.mNonLocalizedLabel = nonLocalizedLabel;
+        this.mIcon = icon;
+        this.mIntentFilter = intentFilter;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIntentFilter);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedIntentInfoImpl> CREATOR
+            = new Parcelable.Creator<ParsedIntentInfoImpl>() {
+        @Override
+        public ParsedIntentInfoImpl[] newArray(int size) {
+            return new ParsedIntentInfoImpl[size];
+        }
+
+        @Override
+        public ParsedIntentInfoImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedIntentInfoImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627691925408L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java",
+            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo]\[email protected](genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
new file mode 100644
index 0000000..1e6f630
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.IntentFilter;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.PatternMatcher;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+/** @hide */
+public class ParsedIntentInfoUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    public static final boolean DEBUG = false;
+
+    @NonNull
+    public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
+            boolean allowAutoVerify, ParseInput input)
+            throws XmlPullParserException, IOException {
+        ParsedIntentInfoImpl intentInfo = new ParsedIntentInfoImpl();
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
+        try {
+            intentFilter.setPriority(
+                    sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
+            intentFilter.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
+            if (v != null) {
+                intentInfo.setLabelRes(v.resourceId);
+                if (v.resourceId == 0) {
+                    intentInfo.setNonLocalizedLabel(v.coerceToString());
+                }
+            }
+
+            if (ParsingPackageUtils.sUseRoundIcon) {
+                intentInfo.setIcon(sa.getResourceId(
+                        R.styleable.AndroidManifestIntentFilter_roundIcon, 0));
+            }
+
+            if (intentInfo.getIcon() == 0) {
+                intentInfo.setIcon(
+                        sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0));
+            }
+
+            if (allowAutoVerify) {
+                intentFilter.setAutoVerify(sa.getBoolean(
+                        R.styleable.AndroidManifestIntentFilter_autoVerify,
+                        false));
+            }
+        } finally {
+            sa.recycle();
+        }
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String nodeName = parser.getName();
+            switch (nodeName) {
+                case "action": {
+                    String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
+                    if (value == null) {
+                        result = input.error("No value supplied for <android:name>");
+                    } else if (value.isEmpty()) {
+                        intentFilter.addAction(value);
+                        // Prior to R, this was not a failure
+                        result = input.deferError("No value supplied for <android:name>",
+                                ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
+                    } else {
+                        intentFilter.addAction(value);
+                        result = input.success(null);
+                    }
+                    break;
+                }
+                case "category": {
+                    String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
+                    if (value == null) {
+                        result = input.error("No value supplied for <android:name>");
+                    } else if (value.isEmpty()) {
+                        intentFilter.addCategory(value);
+                        // Prior to R, this was not a failure
+                        result = input.deferError("No value supplied for <android:name>",
+                                ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
+                    } else {
+                        intentFilter.addCategory(value);
+                        result = input.success(null);
+                    }
+                    break;
+                }
+                case "data":
+                    result = parseData(intentInfo, res, parser, allowGlobs, input);
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        intentInfo.setHasDefault(intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
+
+        if (DEBUG) {
+            final StringBuilder cats = new StringBuilder("Intent d=");
+            cats.append(intentInfo.isHasDefault());
+            cats.append(", cat=");
+
+            final Iterator<String> it = intentFilter.categoriesIterator();
+            if (it != null) {
+                while (it.hasNext()) {
+                    cats.append(' ');
+                    cats.append(it.next());
+                }
+            }
+            Slog.d(TAG, cats.toString());
+        }
+
+        return input.success(intentInfo);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
+            Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
+        TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
+        try {
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_mimeType, 0);
+            if (str != null) {
+                try {
+                    intentFilter.addDataType(str);
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    return input.error(e.toString());
+                }
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_mimeGroup, 0);
+            if (str != null) {
+                intentFilter.addMimeGroup(str);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_scheme, 0);
+            if (str != null) {
+                intentFilter.addDataScheme(str);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_ssp, 0);
+            if (str != null) {
+                intentFilter.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_LITERAL);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_sspPrefix, 0);
+            if (str != null) {
+                intentFilter.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_PREFIX);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_sspPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "sspPattern not allowed here; ssp must be literal");
+                }
+                intentFilter.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_SIMPLE_GLOB);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_sspAdvancedPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "sspAdvancedPattern not allowed here; ssp must be literal");
+                }
+                intentFilter.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_ADVANCED_GLOB);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_sspSuffix, 0);
+            if (str != null) {
+                intentFilter.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_SUFFIX);
+            }
+
+
+            String host = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_host, 0);
+            String port = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_port, 0);
+            if (host != null) {
+                intentFilter.addDataAuthority(host, port);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_path, 0);
+            if (str != null) {
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathPrefix, 0);
+            if (str != null) {
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "pathPattern not allowed here; path must be literal");
+                }
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "pathAdvancedPattern not allowed here; path must be literal");
+                }
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathSuffix, 0);
+            if (str != null) {
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_SUFFIX);
+            }
+
+
+            return input.success(null);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
new file mode 100644
index 0000000..8c1d6c8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.Nullable;
+
+/** @hide */
+public interface ParsedMainComponent extends ParsedComponent {
+
+    @Nullable
+    String[] getAttributionTags();
+
+    /**
+     * A main component's name is a class name. This makes code slightly more readable.
+     */
+    String getClassName();
+
+    boolean isDirectBootAware();
+
+    boolean isEnabled();
+
+    boolean isExported();
+
+    int getOrder();
+
+    @Nullable
+    String getProcessName();
+
+    @Nullable
+    String getSplitName();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
new file mode 100644
index 0000000..9b57f48
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedMainComponentImpl extends ParsedComponentImpl implements ParsedMainComponent {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String processName;
+    private boolean directBootAware;
+    private boolean enabled = true;
+    private boolean exported;
+    private int order;
+
+    @Nullable
+    private String splitName;
+    @Nullable
+    private String[] attributionTags;
+
+    public ParsedMainComponentImpl() {
+    }
+
+    public ParsedMainComponentImpl(ParsedMainComponent other) {
+        super(other);
+        this.processName = other.getProcessName();
+        this.directBootAware = other.isDirectBootAware();
+        this.enabled = other.isEnabled();
+        this.exported = other.isExported();
+        this.order = other.getOrder();
+        this.splitName = other.getSplitName();
+        this.attributionTags = other.getAttributionTags();
+    }
+
+    public ParsedMainComponentImpl setProcessName(String processName) {
+        this.processName = TextUtils.safeIntern(processName);
+        return this;
+    }
+
+    /**
+     * A main component's name is a class name. This makes code slightly more readable.
+     */
+    public String getClassName() {
+        return getName();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        sForInternedString.parcel(this.processName, dest, flags);
+        dest.writeBoolean(this.directBootAware);
+        dest.writeBoolean(this.enabled);
+        dest.writeBoolean(this.exported);
+        dest.writeInt(this.order);
+        dest.writeString(this.splitName);
+        dest.writeString8Array(this.attributionTags);
+    }
+
+    protected ParsedMainComponentImpl(Parcel in) {
+        super(in);
+        this.processName = sForInternedString.unparcel(in);
+        this.directBootAware = in.readBoolean();
+        this.enabled = in.readBoolean();
+        this.exported = in.readBoolean();
+        this.order = in.readInt();
+        this.splitName = in.readString();
+        this.attributionTags = in.createString8Array();
+    }
+
+    public static final Parcelable.Creator<ParsedMainComponentImpl> CREATOR =
+            new Parcelable.Creator<ParsedMainComponentImpl>() {
+                @Override
+                public ParsedMainComponentImpl createFromParcel(Parcel source) {
+                    return new ParsedMainComponentImpl(source);
+                }
+
+                @Override
+                public ParsedMainComponentImpl[] newArray(int size) {
+                    return new ParsedMainComponentImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedMainComponentImpl(
+            @Nullable String processName,
+            boolean directBootAware,
+            boolean enabled,
+            boolean exported,
+            int order,
+            @Nullable String splitName,
+            @Nullable String[] attributionTags) {
+        this.processName = processName;
+        this.directBootAware = directBootAware;
+        this.enabled = enabled;
+        this.exported = exported;
+        this.order = order;
+        this.splitName = splitName;
+        this.attributionTags = attributionTags;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getProcessName() {
+        return processName;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isDirectBootAware() {
+        return directBootAware;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isExported() {
+        return exported;
+    }
+
+    @DataClass.Generated.Member
+    public int getOrder() {
+        return order;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getSplitName() {
+        return splitName;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String[] getAttributionTags() {
+        return attributionTags;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setDirectBootAware( boolean value) {
+        directBootAware = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setEnabled( boolean value) {
+        enabled = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setExported( boolean value) {
+        exported = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setOrder( int value) {
+        order = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setSplitName(@android.annotation.NonNull String value) {
+        splitName = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setAttributionTags(@android.annotation.NonNull String... value) {
+        attributionTags = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627324857874L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
new file mode 100644
index 0000000..2a3e653
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+class ParsedMainComponentUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    static <Component extends ParsedMainComponentImpl> ParseResult<Component> parseMainComponent(
+            Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
+            TypedArray array, int flags, boolean useRoundIcon,  @Nullable String defaultSplitName,
+            @NonNull ParseInput input, int bannerAttr, int descriptionAttr, int directBootAwareAttr,
+            int enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
+            int processAttr, int roundIconAttr, int splitNameAttr, int attributionTagsAttr) {
+        ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
+                array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
+                logoAttr, nameAttr, roundIconAttr);
+        if (result.isError()) {
+            return result;
+        }
+
+        if (directBootAwareAttr != NOT_SET) {
+            component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
+            if (component.isDirectBootAware()) {
+                pkg.setPartiallyDirectBootAware(true);
+            }
+        }
+
+        if (enabledAttr != NOT_SET) {
+            component.setEnabled(array.getBoolean(enabledAttr, true));
+        }
+
+        if (processAttr != NOT_SET) {
+            CharSequence processName;
+            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                processName = array.getNonConfigurationString(processAttr,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                processName = array.getNonResourceString(processAttr);
+            }
+
+            // Backwards-compat, ignore error
+            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+                    pkg.getPackageName(), pkg.getProcessName(), processName, flags,
+                    separateProcesses, input);
+            if (processNameResult.isError()) {
+                return input.error(processNameResult);
+            }
+
+            component.setProcessName(processNameResult.getResult());
+        }
+
+        if (splitNameAttr != NOT_SET) {
+            component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
+        }
+
+        if (defaultSplitName != null && component.getSplitName() == null) {
+            component.setSplitName(defaultSplitName);
+        }
+
+        if (attributionTagsAttr != NOT_SET) {
+            final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
+            if (attributionTags != null) {
+                component.setAttributionTags(attributionTags.split("\\|"));
+            }
+        }
+
+        return input.success(component);
+    }
+
+    static ParseResult<ParsedIntentInfo> parseIntentFilter(
+            ParsedMainComponent mainComponent,
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser,
+            boolean visibleToEphemeral, boolean allowGlobs, boolean allowAutoVerify,
+            boolean allowImplicitEphemeralVisibility, boolean failOnNoActions,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParseResult<ParsedIntentInfo> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
+                mainComponent.getName(), pkg, resources, parser, allowGlobs,
+                allowAutoVerify, input);
+        if (intentResult.isError()) {
+            return input.error(intentResult);
+        }
+
+        ParsedIntentInfo intent = intentResult.getResult();
+        IntentFilter intentFilter = intent.getIntentFilter();
+        int actionCount = intentFilter.countActions();
+        if (actionCount == 0 && failOnNoActions) {
+            Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseApkPath() + " "
+                    + parser.getPositionDescription());
+            // Backward-compat, do not actually fail
+            return input.success(null);
+        }
+
+        int intentVisibility;
+        if (visibleToEphemeral) {
+            intentVisibility = IntentFilter.VISIBILITY_EXPLICIT;
+        } else if (allowImplicitEphemeralVisibility
+                && ComponentParseUtils.isImplicitlyExposedIntent(intent)){
+            intentVisibility = IntentFilter.VISIBILITY_IMPLICIT;
+        } else {
+            intentVisibility = IntentFilter.VISIBILITY_NONE;
+        }
+        intentFilter.setVisibilityToInstantApp(intentVisibility);
+
+        return input.success(intentResult.getResult());
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
new file mode 100644
index 0000000..4a6d2c3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.Nullable;
+
+import java.util.Set;
+
+/** @hide */
+public interface ParsedPermission extends ParsedComponent {
+
+    @Nullable
+    String getBackgroundPermission();
+
+    @Nullable
+    String getGroup();
+
+    @Nullable
+    Set<String> getKnownCerts();
+
+    @Nullable
+    ParsedPermissionGroup getParsedPermissionGroup();
+
+    int getProtectionLevel();
+
+    int getRequestRes();
+
+    boolean isTree();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
new file mode 100644
index 0000000..73b5ffa
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+/** @hide */
+public interface ParsedPermissionGroup extends ParsedComponent {
+
+    int getBackgroundRequestDetailResourceId();
+
+    int getBackgroundRequestResourceId();
+
+    int getPriority();
+
+    int getRequestDetailResourceId();
+
+    int getRequestRes();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
new file mode 100644
index 0000000..f47fb75
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+        genAidl = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements
+        ParsedPermissionGroup {
+
+    private int requestDetailResourceId;
+    private int backgroundRequestResourceId;
+    private int backgroundRequestDetailResourceId;
+    private int requestRes;
+    private int priority;
+
+    public String toString() {
+        return "PermissionGroup{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + getName() + "}";
+    }
+
+    public ParsedPermissionGroupImpl() {
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedPermissionGroupImpl(
+            int requestDetailResourceId,
+            int backgroundRequestResourceId,
+            int backgroundRequestDetailResourceId,
+            int requestRes,
+            int priority) {
+        this.requestDetailResourceId = requestDetailResourceId;
+        this.backgroundRequestResourceId = backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
+        this.requestRes = requestRes;
+        this.priority = priority;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getRequestDetailResourceId() {
+        return requestDetailResourceId;
+    }
+
+    @DataClass.Generated.Member
+    public int getBackgroundRequestResourceId() {
+        return backgroundRequestResourceId;
+    }
+
+    @DataClass.Generated.Member
+    public int getBackgroundRequestDetailResourceId() {
+        return backgroundRequestDetailResourceId;
+    }
+
+    @DataClass.Generated.Member
+    public int getRequestRes() {
+        return requestRes;
+    }
+
+    @DataClass.Generated.Member
+    public int getPriority() {
+        return priority;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestDetailResourceId( int value) {
+        requestDetailResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestResourceId( int value) {
+        backgroundRequestResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestDetailResourceId( int value) {
+        backgroundRequestDetailResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestRes( int value) {
+        requestRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setPriority( int value) {
+        priority = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        super.writeToParcel(dest, flags);
+
+        dest.writeInt(requestDetailResourceId);
+        dest.writeInt(backgroundRequestResourceId);
+        dest.writeInt(backgroundRequestDetailResourceId);
+        dest.writeInt(requestRes);
+        dest.writeInt(priority);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedPermissionGroupImpl(@android.annotation.NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        super(in);
+
+        int _requestDetailResourceId = in.readInt();
+        int _backgroundRequestResourceId = in.readInt();
+        int _backgroundRequestDetailResourceId = in.readInt();
+        int _requestRes = in.readInt();
+        int _priority = in.readInt();
+
+        this.requestDetailResourceId = _requestDetailResourceId;
+        this.backgroundRequestResourceId = _backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
+        this.requestRes = _requestRes;
+        this.priority = _priority;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull android.os.Parcelable.Creator<ParsedPermissionGroupImpl> CREATOR
+            = new android.os.Parcelable.Creator<ParsedPermissionGroupImpl>() {
+        @Override
+        public ParsedPermissionGroupImpl[] newArray(int size) {
+            return new ParsedPermissionGroupImpl[size];
+        }
+
+        @Override
+        public ParsedPermissionGroupImpl createFromParcel(@android.annotation.NonNull Parcel in) {
+            return new ParsedPermissionGroupImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627602253988L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java",
+            inputSignatures = "private  int requestDetailResourceId\nprivate  int backgroundRequestResourceId\nprivate  int backgroundRequestDetailResourceId\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\nclass ParsedPermissionGroupImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermissionGroup]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
new file mode 100644
index 0000000..98007ff
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+
+import java.util.Locale;
+import java.util.Set;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission {
+
+    private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+
+    @Nullable
+    private String backgroundPermission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String group;
+    private int requestRes;
+    private int protectionLevel;
+    private boolean tree;
+    @Nullable
+    private ParsedPermissionGroup parsedPermissionGroup;
+    @Nullable
+    private Set<String> knownCerts;
+
+    @VisibleForTesting
+    public ParsedPermissionImpl() {
+    }
+
+    public ParsedPermissionImpl(ParsedPermission other) {
+        super(other);
+        this.backgroundPermission = other.getBackgroundPermission();
+        this.group = other.getGroup();
+        this.requestRes = other.getRequestRes();
+        this.protectionLevel = other.getProtectionLevel();
+        this.tree = other.isTree();
+        this.parsedPermissionGroup = other.getParsedPermissionGroup();
+    }
+
+    public ParsedPermissionImpl setGroup(String group) {
+        this.group = TextUtils.safeIntern(group);
+        return this;
+    }
+
+    protected void setKnownCert(String knownCert) {
+        // Convert the provided digest to upper case for consistent Set membership
+        // checks when verifying the signing certificate digests of requesting apps.
+        this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+    }
+
+    protected void setKnownCerts(String[] knownCerts) {
+        this.knownCerts = new ArraySet<>();
+        for (String knownCert : knownCerts) {
+            this.knownCerts.add(knownCert.toUpperCase(Locale.US));
+        }
+    }
+
+    public String toString() {
+        return "Permission{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + getName() + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(this.backgroundPermission);
+        dest.writeString(this.group);
+        dest.writeInt(this.requestRes);
+        dest.writeInt(this.protectionLevel);
+        dest.writeBoolean(this.tree);
+        dest.writeParcelable(this.parsedPermissionGroup, flags);
+        sForStringSet.parcel(knownCerts, dest, flags);
+    }
+
+    protected ParsedPermissionImpl(Parcel in) {
+        super(in);
+        this.backgroundPermission = in.readString();
+        this.group = TextUtils.safeIntern(in.readString());
+        this.requestRes = in.readInt();
+        this.protectionLevel = in.readInt();
+        this.tree = in.readBoolean();
+        this.parsedPermissionGroup = in.readParcelable(ParsedPermissionGroup.class.getClassLoader(),
+                ParsedPermissionGroup.class);
+        this.knownCerts = sForStringSet.unparcel(in);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedPermissionImpl> CREATOR =
+            new Parcelable.Creator<ParsedPermissionImpl>() {
+                @Override
+                public ParsedPermissionImpl createFromParcel(Parcel source) {
+                    return new ParsedPermissionImpl(source);
+                }
+
+                @Override
+                public ParsedPermissionImpl[] newArray(int size) {
+                    return new ParsedPermissionImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedPermissionImpl(
+            @Nullable String backgroundPermission,
+            @Nullable String group,
+            int requestRes,
+            int protectionLevel,
+            boolean tree,
+            @Nullable ParsedPermissionGroup parsedPermissionGroup,
+            @Nullable Set<String> knownCerts) {
+        this.backgroundPermission = backgroundPermission;
+        this.group = group;
+        this.requestRes = requestRes;
+        this.protectionLevel = protectionLevel;
+        this.tree = tree;
+        this.parsedPermissionGroup = parsedPermissionGroup;
+        this.knownCerts = knownCerts;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getBackgroundPermission() {
+        return backgroundPermission;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getGroup() {
+        return group;
+    }
+
+    @DataClass.Generated.Member
+    public int getRequestRes() {
+        return requestRes;
+    }
+
+    @DataClass.Generated.Member
+    public int getProtectionLevel() {
+        return protectionLevel;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isTree() {
+        return tree;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable ParsedPermissionGroup getParsedPermissionGroup() {
+        return parsedPermissionGroup;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Set<String> getKnownCerts() {
+        return knownCerts;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setBackgroundPermission(@NonNull String value) {
+        backgroundPermission = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setRequestRes( int value) {
+        requestRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setProtectionLevel( int value) {
+        protectionLevel = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setTree( boolean value) {
+        tree = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setParsedPermissionGroup(@NonNull ParsedPermissionGroup value) {
+        parsedPermissionGroup = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setKnownCerts(@NonNull Set<String> value) {
+        knownCerts = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627598236506L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java",
+            inputSignatures = "private static  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
new file mode 100644
index 0000000..8562fdf
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+
+import android.annotation.NonNull;
+import android.content.pm.PermissionInfo;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+
+/** @hide */
+public class ParsedPermissionUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    @NonNull
+    public static ParseResult<ParsedPermission> parsePermission(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        String packageName = pkg.getPackageName();
+        ParsedPermissionImpl permission = new ParsedPermissionImpl();
+        String tag = "<" + parser.getName() + ">";
+        ParseResult<ParsedPermissionImpl> result;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
+        try {
+            result = ParsedComponentUtils.parseComponent(
+                    permission, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestPermission_banner,
+                    R.styleable.AndroidManifestPermission_description,
+                    R.styleable.AndroidManifestPermission_icon,
+                    R.styleable.AndroidManifestPermission_label,
+                    R.styleable.AndroidManifestPermission_logo,
+                    R.styleable.AndroidManifestPermission_name,
+                    R.styleable.AndroidManifestPermission_roundIcon);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            if (sa.hasValue(
+                    R.styleable.AndroidManifestPermission_backgroundPermission)) {
+                if ("android".equals(packageName)) {
+                    permission.setBackgroundPermission(sa.getNonResourceString(
+                            R.styleable.AndroidManifestPermission_backgroundPermission));
+                } else {
+                    Slog.w(TAG, packageName + " defines a background permission. Only the "
+                            + "'android' package can do that.");
+                }
+            }
+
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            permission.setGroup(sa.getNonResourceString(
+                    R.styleable.AndroidManifestPermission_permissionGroup))
+                    .setRequestRes(sa.getResourceId(
+                            R.styleable.AndroidManifestPermission_request, 0))
+                    .setProtectionLevel(sa.getInt(
+                            R.styleable.AndroidManifestPermission_protectionLevel,
+                            PermissionInfo.PROTECTION_NORMAL))
+                    .setFlags(sa.getInt(
+                            R.styleable.AndroidManifestPermission_permissionFlags, 0));
+
+            final int knownCertsResource = sa.getResourceId(
+                    R.styleable.AndroidManifestPermission_knownCerts, 0);
+            if (knownCertsResource != 0) {
+                // The knownCerts attribute supports both a string array resource as well as a
+                // string resource for the case where the permission should only be granted to a
+                // single known signer.
+                final String resourceType = res.getResourceTypeName(knownCertsResource);
+                if (resourceType.equals("array")) {
+                    final String[] knownCerts = res.getStringArray(knownCertsResource);
+                    if (knownCerts != null) {
+                        permission.setKnownCerts(knownCerts);
+                    }
+                } else {
+                    final String knownCert = res.getString(knownCertsResource);
+                    if (knownCert != null) {
+                        permission.setKnownCert(knownCert);
+                    }
+                }
+                if (permission.getKnownCerts() == null) {
+                    Slog.w(TAG, packageName + " defines a knownSigner permission but"
+                            + " the provided knownCerts resource is null");
+                }
+            } else {
+                // If the knownCerts resource ID is null check if the app specified a string
+                // value for the attribute representing a single trusted signer.
+                final String knownCert = sa.getString(
+                        R.styleable.AndroidManifestPermission_knownCerts);
+                if (knownCert != null) {
+                    permission.setKnownCert(knownCert);
+                }
+            }
+
+            // For now only platform runtime permissions can be restricted
+            if (!isRuntime(permission) || !"android".equals(permission.getPackageName())) {
+                permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_HARD_RESTRICTED);
+                permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_SOFT_RESTRICTED);
+            } else {
+                // The platform does not get to specify conflicting permissions
+                if ((permission.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+                        && (permission.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+                    throw new IllegalStateException("Permission cannot be both soft and hard"
+                            + " restricted: " + permission.getName());
+                }
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        permission.setProtectionLevel(
+                PermissionInfo.fixProtectionLevel(permission.getProtectionLevel()));
+
+        final int otherProtectionFlags = getProtectionFlags(permission)
+                & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
+                | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
+        if (otherProtectionFlags != 0
+                && getProtection(permission) != PermissionInfo.PROTECTION_SIGNATURE
+                && getProtection(permission) != PermissionInfo.PROTECTION_INTERNAL) {
+            return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
+                    + " non-runtimeOnly flag but is not based on signature or internal type");
+        }
+
+        result = ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
+    }
+
+    @NonNull
+    public static ParseResult<ParsedPermission> parsePermissionTree(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        ParsedPermissionImpl permission = new ParsedPermissionImpl();
+        String tag = "<" + parser.getName() + ">";
+        ParseResult<ParsedPermissionImpl> result;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
+        try {
+            result = ParsedComponentUtils.parseComponent(
+                    permission, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestPermissionTree_banner,
+                    NOT_SET /*descriptionAttr*/,
+                    R.styleable.AndroidManifestPermissionTree_icon,
+                    R.styleable.AndroidManifestPermissionTree_label,
+                    R.styleable.AndroidManifestPermissionTree_logo,
+                    R.styleable.AndroidManifestPermissionTree_name,
+                    R.styleable.AndroidManifestPermissionTree_roundIcon);
+            if (result.isError()) {
+                return input.error(result);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        int index = permission.getName().indexOf('.');
+        if (index > 0) {
+            index = permission.getName().indexOf('.', index + 1);
+        }
+        if (index < 0) {
+            return input.error("<permission-tree> name has less than three segments: "
+                    + permission.getName());
+        }
+
+        permission.setProtectionLevel(PermissionInfo.PROTECTION_NORMAL)
+                .setTree(true);
+
+        result = ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
+    }
+
+    @NonNull
+    public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        ParsedPermissionGroupImpl
+                permissionGroup = new ParsedPermissionGroupImpl();
+        String tag = "<" + parser.getName() + ">";
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
+        try {
+            ParseResult<ParsedPermissionGroupImpl> result = ParsedComponentUtils.parseComponent(
+                    permissionGroup, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestPermissionGroup_banner,
+                    R.styleable.AndroidManifestPermissionGroup_description,
+                    R.styleable.AndroidManifestPermissionGroup_icon,
+                    R.styleable.AndroidManifestPermissionGroup_label,
+                    R.styleable.AndroidManifestPermissionGroup_logo,
+                    R.styleable.AndroidManifestPermissionGroup_name,
+                    R.styleable.AndroidManifestPermissionGroup_roundIcon);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            // @formatter:off
+            permissionGroup.setRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0))
+                    .setBackgroundRequestResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0))
+                    .setBackgroundRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0))
+                    .setRequestRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0))
+                    .setPriority(sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0))
+                    .setFlags(sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0));
+            // @formatter:on
+        } finally {
+            sa.recycle();
+        }
+
+        ParseResult<ParsedPermissionGroupImpl> result = ComponentParseUtils.parseAllMetaData(pkg,
+                res, parser, tag, permissionGroup, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
+    }
+
+    public static boolean isRuntime(@NonNull ParsedPermission permission) {
+        return getProtection(permission) == PermissionInfo.PROTECTION_DANGEROUS;
+    }
+
+    public static boolean isAppOp(@NonNull ParsedPermission permission) {
+        return (permission.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+    }
+
+    @PermissionInfo.Protection
+    public static int getProtection(@NonNull ParsedPermission permission) {
+        return permission.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public static int getProtectionFlags(@NonNull ParsedPermission permission) {
+        return permission.getProtectionLevel() & ~PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public static int calculateFootprint(@NonNull ParsedPermission permission) {
+        int size = permission.getName().length();
+        CharSequence nonLocalizedLabel = permission.getNonLocalizedLabel();
+        if (nonLocalizedLabel != null) {
+            size += nonLocalizedLabel.length();
+        }
+        return size;
+    }
+
+    /**
+     * @return {@code true} if the package declares duplicate permissions with different
+     * protection levels.
+     */
+    public static boolean declareDuplicatePermission(@NonNull ParsingPackage pkg) {
+        final List<ParsedPermission> permissions = pkg.getPermissions();
+        final int size = permissions.size();
+        if (size > 0) {
+            final ArrayMap<String, ParsedPermission> checkDuplicatePerm = new ArrayMap<>(size);
+            for (int i = 0; i < size; i++) {
+                final ParsedPermission parsedPermission = permissions.get(i);
+                final String name = parsedPermission.getName();
+                final ParsedPermission perm = checkDuplicatePerm.get(name);
+                // Since a permission tree is also added as a permission with normal protection
+                // level, we need to skip if the parsedPermission is a permission tree.
+                if (perm != null && !(perm.isTree() || parsedPermission.isTree())
+                        && perm.getProtectionLevel() != parsedPermission.getProtectionLevel()) {
+                    return true;
+                }
+                checkDuplicatePerm.put(name, parsedPermission);
+            }
+        }
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
new file mode 100644
index 0000000..ff391ff
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Set;
+
+/** @hide */
+public interface ParsedProcess extends Parcelable {
+
+    @NonNull
+    Set<String> getDeniedPermissions();
+
+    @ApplicationInfo.GwpAsanMode
+    int getGwpAsanMode();
+
+    @ApplicationInfo.MemtagMode
+    int getMemtagMode();
+
+    @NonNull
+    String getName();
+
+    /**
+     * The app class names in this (potentially shared) process, from a package name to
+     * the application class name.
+     * It's a map, because in shared processes, different packages can have different application
+     * classes.
+     */
+    @NonNull
+    ArrayMap<String, String> getAppClassNamesByPackage();
+
+    @ApplicationInfo.NativeHeapZeroInitialized
+    int getNativeHeapZeroInitialized();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
new file mode 100644
index 0000000..96560c7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Set;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
+        genBuilder = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedProcessImpl implements ParsedProcess {
+
+    @NonNull
+    private String name;
+
+    /** @see ParsedProcess#getAppClassNamesByPackage() */
+    @NonNull
+    private ArrayMap<String, String> appClassNamesByPackage = ArrayMap.EMPTY;
+
+    @NonNull
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
+    private Set<String> deniedPermissions = emptySet();
+
+    @ApplicationInfo.GwpAsanMode
+    private int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+    @ApplicationInfo.MemtagMode
+    private int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+    @ApplicationInfo.NativeHeapZeroInitialized
+    private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
+
+    public ParsedProcessImpl() {
+    }
+
+    public ParsedProcessImpl(@NonNull ParsedProcess other) {
+        name = other.getName();
+        appClassNamesByPackage = (other.getAppClassNamesByPackage().size() == 0)
+                ? ArrayMap.EMPTY : new ArrayMap<>(other.getAppClassNamesByPackage());
+        deniedPermissions = new ArraySet<>(other.getDeniedPermissions());
+        gwpAsanMode = other.getGwpAsanMode();
+        memtagMode = other.getMemtagMode();
+        nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+    }
+
+    public void addStateFrom(@NonNull ParsedProcess other) {
+        deniedPermissions = CollectionUtils.addAll(deniedPermissions, other.getDeniedPermissions());
+        gwpAsanMode = other.getGwpAsanMode();
+        memtagMode = other.getMemtagMode();
+        nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+
+        final ArrayMap<String, String> oacn = other.getAppClassNamesByPackage();
+        for (int i = 0; i < oacn.size(); i++) {
+            appClassNamesByPackage.put(oacn.keyAt(i), oacn.valueAt(i));
+        }
+    }
+
+    /**
+     * Sets a custom application name used in this process for a given package.
+     */
+    public void putAppClassNameForPackage(String packageName, String className) {
+        if (appClassNamesByPackage.size() == 0) {
+            appClassNamesByPackage = new ArrayMap<>(4);
+        }
+        appClassNamesByPackage.put(packageName, className);
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ParsedProcessImpl.
+     *
+     */
+    @DataClass.Generated.Member
+    public ParsedProcessImpl(
+            @NonNull String name,
+            @NonNull ArrayMap<String,String> appClassNamesByPackage,
+            @NonNull Set<String> deniedPermissions,
+            @ApplicationInfo.GwpAsanMode int gwpAsanMode,
+            @ApplicationInfo.MemtagMode int memtagMode,
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.appClassNamesByPackage = appClassNamesByPackage;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, appClassNamesByPackage);
+        this.deniedPermissions = deniedPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        this.gwpAsanMode = gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        this.memtagMode = memtagMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    /**
+     * @see ParsedProcess#getAppClassNamesByPackage()
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArrayMap<String,String> getAppClassNamesByPackage() {
+        return appClassNamesByPackage;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Set<String> getDeniedPermissions() {
+        return deniedPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public @ApplicationInfo.GwpAsanMode int getGwpAsanMode() {
+        return gwpAsanMode;
+    }
+
+    @DataClass.Generated.Member
+    public @ApplicationInfo.MemtagMode int getMemtagMode() {
+        return memtagMode;
+    }
+
+    @DataClass.Generated.Member
+    public @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized() {
+        return nativeHeapZeroInitialized;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    /**
+     * @see ParsedProcess#getAppClassNamesByPackage()
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setAppClassNamesByPackage(@NonNull ArrayMap<String,String> value) {
+        appClassNamesByPackage = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, appClassNamesByPackage);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setDeniedPermissions(@NonNull Set<String> value) {
+        deniedPermissions = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
+        gwpAsanMode = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
+        memtagMode = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setNativeHeapZeroInitialized(@ApplicationInfo.NativeHeapZeroInitialized int value) {
+        nativeHeapZeroInitialized = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<Set<String>> sParcellingForDeniedPermissions =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedStringSet.class);
+    static {
+        if (sParcellingForDeniedPermissions == null) {
+            sParcellingForDeniedPermissions = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedStringSet());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(name);
+        dest.writeMap(appClassNamesByPackage);
+        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+        dest.writeInt(gwpAsanMode);
+        dest.writeInt(memtagMode);
+        dest.writeInt(nativeHeapZeroInitialized);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedProcessImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _name = in.readString();
+        ArrayMap<String,String> _appClassNamesByPackage = new ArrayMap();
+        in.readMap(_appClassNamesByPackage, String.class.getClassLoader());
+        Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+        int _gwpAsanMode = in.readInt();
+        int _memtagMode = in.readInt();
+        int _nativeHeapZeroInitialized = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.appClassNamesByPackage = _appClassNamesByPackage;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, appClassNamesByPackage);
+        this.deniedPermissions = _deniedPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        this.gwpAsanMode = _gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        this.memtagMode = _memtagMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedProcessImpl> CREATOR
+            = new Parcelable.Creator<ParsedProcessImpl>() {
+        @Override
+        public ParsedProcessImpl[] newArray(int size) {
+            return new ParsedProcessImpl[size];
+        }
+
+        @Override
+        public ParsedProcessImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedProcessImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1639076603310L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\[email protected](genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
new file mode 100644
index 0000000..d03f153
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/** @hide */
+public class ParsedProcessUtils {
+
+    @NonNull
+    private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
+            Resources res, XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
+        try {
+            String perm = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestDenyPermission_name, 0);
+            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+                perms = CollectionUtils.add(perms, perm);
+            }
+        } finally {
+            sa.recycle();
+        }
+        XmlUtils.skipCurrentTag(parser);
+        return input.success(perms);
+    }
+
+    @NonNull
+    private static ParseResult<Set<String>> parseAllowPermission(Set<String> perms, Resources res,
+            XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
+        try {
+            String perm = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestAllowPermission_name, 0);
+            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+                perms = CollectionUtils.remove(perms, perm);
+            }
+        } finally {
+            sa.recycle();
+        }
+        XmlUtils.skipCurrentTag(parser);
+        return input.success(perms);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParsedProcessImpl proc = new ParsedProcessImpl();
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
+        try {
+            if (perms != null) {
+                proc.setDeniedPermissions(new ArraySet<>(perms));
+            }
+
+            String processName = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProcess_process, 0);
+            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+                    pkg.getPackageName(), pkg.getPackageName(), processName, flags, separateProcesses,
+                    input);
+            if (processNameResult.isError()) {
+                return input.error(processNameResult);
+            }
+
+            String packageName = pkg.getPackageName();
+            String className = ParsingUtils.buildClassName(packageName,
+                    sa.getNonConfigurationString(R.styleable.AndroidManifestProcess_name, 0));
+
+            proc.setName(processNameResult.getResult());
+            proc.putAppClassNameForPackage(packageName, className);
+            proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
+            proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
+            if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
+                final boolean v = sa.getBoolean(
+                        R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
+                proc.setNativeHeapZeroInitialized(
+                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            ParseResult<?> result;
+
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "deny-permission":
+                    ParseResult<Set<String>> denyResult = parseDenyPermission(
+                            proc.getDeniedPermissions(), res, parser, input);
+                    result = denyResult;
+                    if (denyResult.isSuccess()) {
+                        proc.setDeniedPermissions(denyResult.getResult());
+                    }
+                    break;
+                case "allow-permission":
+                    ParseResult<Set<String>> allowResult = parseAllowPermission(
+                            proc.getDeniedPermissions(), res, parser, input);
+                    result = allowResult;
+                    if (allowResult.isSuccess()) {
+                        proc.setDeniedPermissions(allowResult.getResult());
+                    }
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<process>", pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(proc);
+    }
+
+    @NonNull
+    public static ParseResult<ArrayMap<String, ParsedProcess>> parseProcesses(
+            String[] separateProcesses, ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, int flags, ParseInput input)
+            throws IOException, XmlPullParserException {
+        Set<String> deniedPerms = null;
+        ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            ParseResult<?> result;
+
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "deny-permission":
+                    ParseResult<Set<String>> denyResult = parseDenyPermission(deniedPerms, res,
+                            parser, input);
+                    result = denyResult;
+                    if (denyResult.isSuccess()) {
+                        deniedPerms = denyResult.getResult();
+                    }
+                    break;
+                case "allow-permission":
+                    ParseResult<Set<String>> allowResult = parseAllowPermission(deniedPerms, res,
+                            parser, input);
+                    result = allowResult;
+                    if (allowResult.isSuccess()) {
+                        deniedPerms = allowResult.getResult();
+                    }
+                    break;
+                case "process":
+                    ParseResult<ParsedProcess> processResult = parseProcess(deniedPerms,
+                            separateProcesses, pkg, res, parser, flags, input);
+                    result = processResult;
+                    if (processResult.isSuccess()) {
+                        ParsedProcess process = processResult.getResult();
+                        if (processes.put(process.getName(), process) != null) {
+                            result = input.error(
+                                    "<process> specified existing name '" + process.getName() + "'");
+                        }
+                    }
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<processes>", pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+        }
+
+        return input.success(processes);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
new file mode 100644
index 0000000..8cc6fc7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.Nullable;
+import android.content.pm.PathPermission;
+import android.os.PatternMatcher;
+
+/** @hide **/
+public interface ParsedProvider extends ParsedMainComponent {
+
+    @Nullable
+    String getAuthority();
+
+    int getInitOrder();
+
+    boolean isMultiProcess();
+
+    @Nullable PathPermission[] getPathPermissions();
+
+    @Nullable String getReadPermission();
+
+    @Nullable PatternMatcher[] getUriPermissionPatterns();
+
+    @Nullable String getWritePermission();
+
+    boolean isForceUriPermissions();
+
+    boolean isGrantUriPermissions();
+
+    boolean isSyncable();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
new file mode 100644
index 0000000..e04fc86
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PathPermission;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+@DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedProviderImpl extends ParsedMainComponentImpl implements ParsedProvider {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String authority;
+    private boolean syncable;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String readPermission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String writePermission;
+    private boolean grantUriPermissions;
+    private boolean forceUriPermissions;
+    private boolean multiProcess;
+    private int initOrder;
+    @Nullable
+    private PatternMatcher[] uriPermissionPatterns;
+    @Nullable
+    private PathPermission[] pathPermissions;
+
+    public ParsedProviderImpl(ParsedProvider other) {
+        super(other);
+
+        this.authority = other.getAuthority();
+        this.syncable = other.isSyncable();
+        this.readPermission = other.getReadPermission();
+        this.writePermission = other.getWritePermission();
+        this.grantUriPermissions = other.isGrantUriPermissions();
+        this.forceUriPermissions = other.isForceUriPermissions();
+        this.multiProcess = other.isMultiProcess();
+        this.initOrder = other.getInitOrder();
+        this.uriPermissionPatterns = other.getUriPermissionPatterns();
+        this.pathPermissions = other.getPathPermissions();
+    }
+
+    public ParsedProviderImpl setReadPermission(String readPermission) {
+        // Empty string must be converted to null
+        this.readPermission = TextUtils.isEmpty(readPermission)
+                ? null : readPermission.intern();
+        return this;
+    }
+
+    public ParsedProviderImpl setWritePermission(String writePermission) {
+        // Empty string must be converted to null
+        this.writePermission = TextUtils.isEmpty(writePermission)
+                ? null : writePermission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Provider{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(this.authority);
+        dest.writeBoolean(this.syncable);
+        sForInternedString.parcel(this.readPermission, dest, flags);
+        sForInternedString.parcel(this.writePermission, dest, flags);
+        dest.writeBoolean(this.grantUriPermissions);
+        dest.writeBoolean(this.forceUriPermissions);
+        dest.writeBoolean(this.multiProcess);
+        dest.writeInt(this.initOrder);
+        dest.writeTypedArray(this.uriPermissionPatterns, flags);
+        dest.writeTypedArray(this.pathPermissions, flags);
+    }
+
+    public ParsedProviderImpl() {
+    }
+
+    protected ParsedProviderImpl(Parcel in) {
+        super(in);
+        this.authority = in.readString();
+        this.syncable = in.readBoolean();
+        this.readPermission = sForInternedString.unparcel(in);
+        this.writePermission = sForInternedString.unparcel(in);
+        this.grantUriPermissions = in.readBoolean();
+        this.forceUriPermissions = in.readBoolean();
+        this.multiProcess = in.readBoolean();
+        this.initOrder = in.readInt();
+        this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+        this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedProviderImpl> CREATOR =
+            new Parcelable.Creator<ParsedProviderImpl>() {
+                @Override
+                public ParsedProviderImpl createFromParcel(Parcel source) {
+                    return new ParsedProviderImpl(source);
+                }
+
+                @Override
+                public ParsedProviderImpl[] newArray(int size) {
+                    return new ParsedProviderImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedProviderImpl(
+            @Nullable String authority,
+            boolean syncable,
+            @Nullable String readPermission,
+            @Nullable String writePermission,
+            boolean grantUriPermissions,
+            boolean forceUriPermissions,
+            boolean multiProcess,
+            int initOrder,
+            @Nullable PatternMatcher[] uriPermissionPatterns,
+            @Nullable PathPermission[] pathPermissions) {
+        this.authority = authority;
+        this.syncable = syncable;
+        this.readPermission = readPermission;
+        this.writePermission = writePermission;
+        this.grantUriPermissions = grantUriPermissions;
+        this.forceUriPermissions = forceUriPermissions;
+        this.multiProcess = multiProcess;
+        this.initOrder = initOrder;
+        this.uriPermissionPatterns = uriPermissionPatterns;
+        this.pathPermissions = pathPermissions;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getAuthority() {
+        return authority;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isSyncable() {
+        return syncable;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getReadPermission() {
+        return readPermission;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getWritePermission() {
+        return writePermission;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isGrantUriPermissions() {
+        return grantUriPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isForceUriPermissions() {
+        return forceUriPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isMultiProcess() {
+        return multiProcess;
+    }
+
+    @DataClass.Generated.Member
+    public int getInitOrder() {
+        return initOrder;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable PatternMatcher[] getUriPermissionPatterns() {
+        return uriPermissionPatterns;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable PathPermission[] getPathPermissions() {
+        return pathPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setAuthority(@NonNull String value) {
+        authority = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setSyncable( boolean value) {
+        syncable = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setGrantUriPermissions( boolean value) {
+        grantUriPermissions = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setForceUriPermissions( boolean value) {
+        forceUriPermissions = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setMultiProcess( boolean value) {
+        multiProcess = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setInitOrder( int value) {
+        initOrder = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setUriPermissionPatterns(@NonNull PatternMatcher... value) {
+        uriPermissionPatterns = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setPathPermissions(@NonNull PathPermission... value) {
+        pathPermissions = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627590522169L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.Nullable android.os.PatternMatcher[] uriPermissionPatterns\nprivate @android.annotation.Nullable android.content.pm.PathPermission[] pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedProviderImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedProvider]\[email protected](genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
new file mode 100644
index 0000000..9d3129b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
+import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.os.PatternMatcher;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedProviderUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    @NonNull
+    public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
+            throws IOException, XmlPullParserException {
+        String authority;
+        boolean visibleToEphemeral;
+
+        final int targetSdkVersion = pkg.getTargetSdkVersion();
+        final String packageName = pkg.getPackageName();
+        final ParsedProviderImpl provider = new ParsedProviderImpl();
+        final String tag = parser.getName();
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
+        try {
+            ParseResult<ParsedProviderImpl> result =
+                    ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
+                            pkg, sa, flags, useRoundIcon, defaultSplitName, input,
+                            R.styleable.AndroidManifestProvider_banner,
+                            R.styleable.AndroidManifestProvider_description,
+                            R.styleable.AndroidManifestProvider_directBootAware,
+                            R.styleable.AndroidManifestProvider_enabled,
+                            R.styleable.AndroidManifestProvider_icon,
+                            R.styleable.AndroidManifestProvider_label,
+                            R.styleable.AndroidManifestProvider_logo,
+                            R.styleable.AndroidManifestProvider_name,
+                            R.styleable.AndroidManifestProvider_process,
+                            R.styleable.AndroidManifestProvider_roundIcon,
+                            R.styleable.AndroidManifestProvider_splitName,
+                            R.styleable.AndroidManifestProvider_attributionTags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);
+
+            // For compatibility, applications targeting API level 16 or lower
+            // should have their content providers exported by default, unless they
+            // specify otherwise.
+            provider.setSyncable(sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_syncable, false))
+                    .setExported(sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
+                            targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1));
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_permission, 0);
+            String readPermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_readPermission, 0);
+            if (readPermission == null) {
+                readPermission = permission;
+            }
+            if (readPermission == null) {
+                provider.setReadPermission(pkg.getPermission());
+            } else {
+                provider.setReadPermission(readPermission);
+            }
+            String writePermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_writePermission, 0);
+            if (writePermission == null) {
+                writePermission = permission;
+            }
+            if (writePermission == null) {
+                provider.setWritePermission(pkg.getPermission());
+            } else {
+                provider.setWritePermission(writePermission);
+            }
+
+            provider.setGrantUriPermissions(
+                    sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false))
+                    .setForceUriPermissions(
+                            sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions,
+                                    false))
+                    .setMultiProcess(
+                            sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false))
+                    .setInitOrder(sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0))
+                    .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER,
+                            R.styleable.AndroidManifestProvider_singleUser, sa));
+
+            visibleToEphemeral = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                provider.setFlags(provider.getFlags() | ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP);
+                pkg.setVisibleToInstantApps(true);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        if (pkg.isCantSaveState()) {
+            // A heavy-weight application can not have providers in its main process
+            if (Objects.equals(provider.getProcessName(), packageName)) {
+                return input.error("Heavy-weight applications can not have providers"
+                        + " in main process");
+            }
+        }
+
+        if (authority == null) {
+            return input.error("<provider> does not include authorities attribute");
+        }
+        if (authority.length() <= 0) {
+            return input.error("<provider> has empty authorities attribute");
+        }
+        provider.setAuthority(authority);
+
+        return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
+            Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
+            ParsedProviderImpl provider, ParseInput input)
+            throws XmlPullParserException, IOException {
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+            final ParseResult result;
+            switch (name) {
+                case "intent-filter":
+                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+                            .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
+                                    true /*allowGlobs*/, false /*allowAutoVerify*/,
+                                    false /*allowImplicitEphemeralVisibility*/,
+                                    false /*failOnNoActions*/, input);
+                    result = intentResult;
+                    if (intentResult.isSuccess()) {
+                        ParsedIntentInfo intent = intentResult.getResult();
+                        IntentFilter intentFilter = intent.getIntentFilter();
+                        provider.setOrder(Math.max(intentFilter.getOrder(), provider.getOrder()));
+                        provider.addIntent(intent);
+                    }
+                    break;
+                case "meta-data":
+                    result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
+                    break;
+                case "property":
+                    result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input);
+                    break;
+                case "grant-uri-permission": {
+                    result = parseGrantUriPermission(provider, pkg, res, parser, input);
+                    break;
+                }
+                case "path-permission": {
+                    result = parsePathPermission(provider, pkg, res, parser, input);
+                    break;
+                }
+                default:
+                    result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(provider);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProviderImpl provider,
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = resources.obtainAttributes(parser,
+                R.styleable.AndroidManifestGrantUriPermission);
+        try {
+            String name = parser.getName();
+            // Pattern has priority over pre/suffix over literal path
+            PatternMatcher pa = null;
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestGrantUriPermission_pathAdvancedPattern, 0);
+            if (str != null) {
+                pa = new PatternMatcher(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+            } else {
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                } else {
+                    str = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
+                    if (str != null) {
+                        pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+                    } else {
+                        str = sa.getNonConfigurationString(
+                                R.styleable.AndroidManifestGrantUriPermission_pathSuffix, 0);
+                        if (str != null) {
+                            pa = new PatternMatcher(str, PatternMatcher.PATTERN_SUFFIX);
+                        } else {
+                            str = sa.getNonConfigurationString(
+                                    R.styleable.AndroidManifestGrantUriPermission_path, 0);
+                            if (str != null) {
+                                pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (pa != null) {
+                if (provider.getUriPermissionPatterns() == null) {
+                    provider.setUriPermissionPatterns(new PatternMatcher[1]);
+                    provider.getUriPermissionPatterns()[0] = pa;
+                } else {
+                    final int N = provider.getUriPermissionPatterns().length;
+                    PatternMatcher[] newp = new PatternMatcher[N + 1];
+                    System.arraycopy(provider.getUriPermissionPatterns(), 0, newp, 0, N);
+                    newp[N] = pa;
+                    provider.setUriPermissionPatterns(newp);
+                }
+                provider.setGrantUriPermissions(true);
+            } else {
+                if (RIGID_PARSER) {
+                    return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
+                }
+
+                Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at "
+                        + pkg.getBaseApkPath() + " " + parser.getPositionDescription());
+            }
+
+            return input.success(provider);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProvider> parsePathPermission(ParsedProviderImpl provider,
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = resources.obtainAttributes(parser,
+                R.styleable.AndroidManifestPathPermission);
+        try {
+            String name = parser.getName();
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPathPermission_permission, 0);
+            String readPermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPathPermission_readPermission, 0);
+            if (readPermission == null) {
+                readPermission = permission;
+            }
+            String writePermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPathPermission_writePermission, 0);
+            if (writePermission == null) {
+                writePermission = permission;
+            }
+
+            boolean havePerm = false;
+            if (readPermission != null) {
+                readPermission = readPermission.intern();
+                havePerm = true;
+            }
+            if (writePermission != null) {
+                writePermission = writePermission.intern();
+                havePerm = true;
+            }
+
+            if (!havePerm) {
+                if (RIGID_PARSER) {
+                    return input.error(
+                            "No readPermission or writePermission for <path-permission>");
+                }
+                Slog.w(TAG, "No readPermission or writePermission for <path-permission>: "
+                        + name + " at " + pkg.getBaseApkPath() + " "
+                        + parser.getPositionDescription());
+                return input.success(provider);
+            }
+
+            // Advanced has priority over simply over prefix over literal
+            PathPermission pa = null;
+            String path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
+            if (path != null) {
+                pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission,
+                        writePermission);
+            } else {
+                path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathPattern, 0);
+                if (path != null) {
+                    pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB,
+                            readPermission, writePermission);
+                } else {
+                    path = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
+                    if (path != null) {
+                        pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission,
+                                writePermission);
+                    } else {
+                        path = sa.getNonConfigurationString(
+                                R.styleable.AndroidManifestPathPermission_pathSuffix, 0);
+                        if (path != null) {
+                            pa = new PathPermission(path, PatternMatcher.PATTERN_SUFFIX,
+                                    readPermission, writePermission);
+                        } else {
+                            path = sa.getNonConfigurationString(
+                                    R.styleable.AndroidManifestPathPermission_path, 0);
+                            if (path != null) {
+                                pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
+                                        readPermission, writePermission);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (pa != null) {
+                if (provider.getPathPermissions() == null) {
+                    provider.setPathPermissions(new PathPermission[1]);
+                    provider.getPathPermissions()[0] = pa;
+                } else {
+                    final int N = provider.getPathPermissions().length;
+                    PathPermission[] newp = new PathPermission[N + 1];
+                    System.arraycopy(provider.getPathPermissions(), 0, newp, 0, N);
+                    newp[N] = pa;
+                    provider.setPathPermissions(newp);
+                }
+            } else {
+                if (RIGID_PARSER) {
+                    return input.error(
+                            "No path, pathPrefix, or pathPattern for <path-permission>");
+                }
+
+                Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
+                        + name + " at " + pkg.getBaseApkPath()
+                        + " "
+                        + parser.getPositionDescription());
+            }
+
+            return input.success(provider);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
new file mode 100644
index 0000000..11696be
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.Nullable;
+
+/** @hide **/
+public interface ParsedService extends ParsedMainComponent {
+
+    int getForegroundServiceType();
+
+    @Nullable
+    String getPermission();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
new file mode 100644
index 0000000..0171c49
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+@DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedServiceImpl extends ParsedMainComponentImpl implements ParsedService {
+
+    private int foregroundServiceType;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+
+    public ParsedServiceImpl(ParsedServiceImpl other) {
+        super(other);
+        this.foregroundServiceType = other.foregroundServiceType;
+        this.permission = other.permission;
+    }
+
+    public ParsedMainComponent setPermission(String permission) {
+        // Empty string must be converted to null
+        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Service{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.foregroundServiceType);
+        sForInternedString.parcel(this.permission, dest, flags);
+    }
+
+    public ParsedServiceImpl() {
+    }
+
+    protected ParsedServiceImpl(Parcel in) {
+        super(in);
+        this.foregroundServiceType = in.readInt();
+        this.permission = sForInternedString.unparcel(in);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedServiceImpl> CREATOR =
+            new Parcelable.Creator<ParsedServiceImpl>() {
+                @Override
+                public ParsedServiceImpl createFromParcel(Parcel source) {
+                    return new ParsedServiceImpl(source);
+                }
+
+                @Override
+                public ParsedServiceImpl[] newArray(int size) {
+                    return new ParsedServiceImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedServiceImpl(
+            int foregroundServiceType,
+            @Nullable String permission) {
+        this.foregroundServiceType = foregroundServiceType;
+        this.permission = permission;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getForegroundServiceType() {
+        return foregroundServiceType;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getPermission() {
+        return permission;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedServiceImpl setForegroundServiceType( int value) {
+        foregroundServiceType = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627592563052L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java",
+            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService]\[email protected](genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
new file mode 100644
index 0000000..6fe9411
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ServiceInfo;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseInput.DeferredError;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedServiceUtils {
+
+    @NonNull
+    public static ParseResult<ParsedService> parseService(String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
+            throws XmlPullParserException, IOException {
+        boolean visibleToEphemeral;
+        boolean setExported;
+
+        final String packageName = pkg.getPackageName();
+        final ParsedServiceImpl service = new ParsedServiceImpl();
+        String tag = parser.getName();
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
+        try {
+            ParseResult<ParsedServiceImpl> result = ParsedMainComponentUtils.parseMainComponent(
+                    service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, defaultSplitName,
+                    input,
+                    R.styleable.AndroidManifestService_banner,
+                    R.styleable.AndroidManifestService_description,
+                    R.styleable.AndroidManifestService_directBootAware,
+                    R.styleable.AndroidManifestService_enabled,
+                    R.styleable.AndroidManifestService_icon,
+                    R.styleable.AndroidManifestService_label,
+                    R.styleable.AndroidManifestService_logo,
+                    R.styleable.AndroidManifestService_name,
+                    R.styleable.AndroidManifestService_process,
+                    R.styleable.AndroidManifestService_roundIcon,
+                    R.styleable.AndroidManifestService_splitName,
+                    R.styleable.AndroidManifestService_attributionTags
+            );
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
+            if (setExported) {
+                service.setExported(sa.getBoolean(R.styleable.AndroidManifestService_exported,
+                        false));
+            }
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestService_permission, 0);
+            service.setPermission(permission != null ? permission : pkg.getPermission());
+
+            service.setForegroundServiceType(sa.getInt(
+                    R.styleable.AndroidManifestService_foregroundServiceType,
+                    ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE))
+                    .setFlags(service.getFlags() | (flag(ServiceInfo.FLAG_STOP_WITH_TASK,
+                            R.styleable.AndroidManifestService_stopWithTask, sa)
+                            | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
+                            R.styleable.AndroidManifestService_isolatedProcess, sa)
+                            | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
+                            R.styleable.AndroidManifestService_externalService, sa)
+                            | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
+                            R.styleable.AndroidManifestService_useAppZygote, sa)
+                            | flag(ServiceInfo.FLAG_SINGLE_USER,
+                            R.styleable.AndroidManifestService_singleUser, sa)));
+
+            visibleToEphemeral = sa.getBoolean(
+                    R.styleable.AndroidManifestService_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
+                pkg.setVisibleToInstantApps(true);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        if (pkg.isCantSaveState()) {
+            // A heavy-weight application can not have services in its main process
+            // We can do direct compare because we intern all strings.
+            if (Objects.equals(service.getProcessName(), packageName)) {
+                return input.error("Heavy-weight applications can not have services "
+                        + "in main process");
+            }
+        }
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult parseResult;
+            switch (parser.getName()) {
+                case "intent-filter":
+                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+                            .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
+                                    true /*allowGlobs*/, false /*allowAutoVerify*/,
+                                    false /*allowImplicitEphemeralVisibility*/,
+                                    false /*failOnNoActions*/, input);
+                    parseResult = intentResult;
+                    if (intentResult.isSuccess()) {
+                        ParsedIntentInfo intent = intentResult.getResult();
+                        IntentFilter intentFilter = intent.getIntentFilter();
+                        service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder()));
+                        service.addIntent(intent);
+                    }
+                    break;
+                case "meta-data":
+                    parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
+                    break;
+                case "property":
+                    parseResult =
+                            ParsedComponentUtils.addProperty(service, pkg, res, parser, input);
+                    break;
+                default:
+                    parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
+                    break;
+            }
+
+            if (parseResult.isError()) {
+                return input.error(parseResult);
+            }
+        }
+
+        if (!setExported) {
+            boolean hasIntentFilters = service.getIntents().size() > 0;
+            if (hasIntentFilters) {
+                final ParseResult exportedCheckResult = input.deferError(
+                        service.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
+                        + " and above) requires that an explicit value for android:exported be"
+                        + " defined when intent filters are present",
+                        DeferredError.MISSING_EXPORTED_FLAG);
+                if (exportedCheckResult.isError()) {
+                    return input.error(exportedCheckResult);
+                }
+            }
+            service.setExported(hasIntentFilters);
+        }
+
+        return input.success(service);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
new file mode 100644
index 0000000..8e3401e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestUsesPermission
+ * &lt;uses-permission&gt;} tag parsed from the manifest.
+ *
+ * @hide
+ */
+public interface ParsedUsesPermission extends Parcelable {
+
+    /**
+     * Strong assertion by a developer that they will never use this permission to derive the
+     * physical location of the device, regardless of ACCESS_FINE_LOCATION and/or
+     * ACCESS_COARSE_LOCATION being granted.
+     */
+    int FLAG_NEVER_FOR_LOCATION = PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_NEVER_FOR_LOCATION
+    })
+    @interface UsesPermissionFlags {}
+
+    @NonNull
+    String getName();
+
+    @UsesPermissionFlags
+    int getUsesPermissionFlags();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
new file mode 100644
index 0000000..70d6f24
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestUsesPermission
+ * &lt;uses-permission&gt;} tag parsed from the manifest.
+ *
+ * @hide
+ */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+        genAidl = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedUsesPermissionImpl implements ParsedUsesPermission {
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @NonNull
+    private String name;
+
+    @ParsedUsesPermission.UsesPermissionFlags
+    private int usesPermissionFlags;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedUsesPermissionImpl(
+            @NonNull String name,
+            @ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.usesPermissionFlags = usesPermissionFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @ParsedUsesPermission.UsesPermissionFlags int getUsesPermissionFlags() {
+        return usesPermissionFlags;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedUsesPermissionImpl setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedUsesPermissionImpl setUsesPermissionFlags(@ParsedUsesPermission.UsesPermissionFlags int value) {
+        usesPermissionFlags = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForName =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForName == null) {
+            sParcellingForName = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        sParcellingForName.parcel(name, dest, flags);
+        dest.writeInt(usesPermissionFlags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedUsesPermissionImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _name = sParcellingForName.unparcel(in);
+        int _usesPermissionFlags = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.usesPermissionFlags = _usesPermissionFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedUsesPermissionImpl> CREATOR
+            = new Parcelable.Creator<ParsedUsesPermissionImpl>() {
+        @Override
+        public ParsedUsesPermissionImpl[] newArray(int size) {
+            return new ParsedUsesPermissionImpl[size];
+        }
+
+        @Override
+        public ParsedUsesPermissionImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedUsesPermissionImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627674645598L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission]\[email protected](genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
new file mode 100644
index 0000000..2d6c616
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -0,0 +1,972 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apex.ApexInfo;
+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.PermissionGroupInfo;
+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.content.pm.overlay.OverlayPaths;
+import android.os.Environment;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.SELinuxUtil;
+import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
+
+import libcore.util.EmptyArray;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @hide
+ **/
+public class PackageInfoWithoutStateUtils {
+
+    public static final String SYSTEM_DATA_PATH =
+            Environment.getDataDirectoryPath() + File.separator + "system";
+
+    @Nullable
+    public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state,
+            int userId) {
+        return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
+                state, userId, null);
+    }
+
+    @Nullable
+    public static PackageInfo generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags) {
+        return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
+                PackageUserState.DEFAULT, UserHandle.getCallingUserId(), apexInfo);
+    }
+
+    @Nullable
+    private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state,
+            int userId, @Nullable ApexInfo apexInfo) {
+        ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        if (applicationInfo == null) {
+            return null;
+        }
+        PackageInfo info = generateWithoutComponents(pkg, gids, flags, firstInstallTime,
+                lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
+
+        if (info == null) {
+            return null;
+        }
+
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int N = pkg.getActivities().size();
+            if (N > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final ParsedActivity a = pkg.getActivities().get(i);
+                    if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
+                            flags)) {
+                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+                                a.getName())) {
+                            continue;
+                        }
+                        res[num++] = generateActivityInfo(pkg, a, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                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 (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
+                            flags)) {
+                        res[num++] = generateActivityInfo(pkg, a, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                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 (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), s,
+                            flags)) {
+                        res[num++] = generateServiceInfo(pkg, s, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                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 (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), pr,
+                            flags)) {
+                        res[num++] = generateProviderInfo(pkg, pr, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                info.providers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+            int N = pkg.getInstrumentations().size();
+            if (N > 0) {
+                info.instrumentation = new InstrumentationInfo[N];
+                for (int i = 0; i < N; i++) {
+                    info.instrumentation[i] = generateInstrumentationInfo(
+                            pkg.getInstrumentations().get(i), pkg, flags, userId,
+                            true /* assignUserFields */);
+                }
+            }
+        }
+
+        return info;
+    }
+
+    @Nullable
+    public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state,
+            int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+
+        return generateWithoutComponentsUnchecked(pkg, gids, flags, firstInstallTime,
+                lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of system
+     * server.
+     * <p>
+     * Prefer {@link #generateWithoutComponents(ParsingPackageRead, int[], int, long, long, Set,
+     * PackageUserState, int, ApexInfo, ApplicationInfo)}.
+     */
+    @NonNull
+    public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state,
+            int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = pkg.getPackageName();
+        pi.splitNames = pkg.getSplitNames();
+        pi.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
+        pi.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
+        pi.baseRevisionCode = pkg.getBaseRevisionCode();
+        pi.splitRevisionCodes = pkg.getSplitRevisionCodes();
+        pi.versionName = pkg.getVersionName();
+        pi.sharedUserId = pkg.getSharedUserId();
+        pi.sharedUserLabel = pkg.getSharedUserLabel();
+        pi.applicationInfo = applicationInfo;
+        pi.installLocation = pkg.getInstallLocation();
+        if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            pi.requiredForAllUsers = pkg.isRequiredForAllUsers();
+        }
+        pi.restrictedAccountType = pkg.getRestrictedAccountType();
+        pi.requiredAccountType = pkg.getRequiredAccountType();
+        pi.overlayTarget = pkg.getOverlayTarget();
+        pi.targetOverlayableName = pkg.getOverlayTargetOverlayableName();
+        pi.overlayCategory = pkg.getOverlayCategory();
+        pi.overlayPriority = pkg.getOverlayPriority();
+        pi.mOverlayIsStatic = pkg.isOverlayIsStatic();
+        pi.compileSdkVersion = pkg.getCompileSdkVersion();
+        pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+        pi.firstInstallTime = firstInstallTime;
+        pi.lastUpdateTime = lastUpdateTime;
+        if ((flags & PackageManager.GET_GIDS) != 0) {
+            pi.gids = gids;
+        }
+        if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
+            int size = pkg.getConfigPreferences().size();
+            if (size > 0) {
+                pi.configPreferences = new ConfigurationInfo[size];
+                pkg.getConfigPreferences().toArray(pi.configPreferences);
+            }
+            size = pkg.getRequestedFeatures().size();
+            if (size > 0) {
+                pi.reqFeatures = new FeatureInfo[size];
+                pkg.getRequestedFeatures().toArray(pi.reqFeatures);
+            }
+            size = pkg.getFeatureGroups().size();
+            if (size > 0) {
+                pi.featureGroups = new FeatureGroupInfo[size];
+                pkg.getFeatureGroups().toArray(pi.featureGroups);
+            }
+        }
+        if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
+            int size = ArrayUtils.size(pkg.getPermissions());
+            if (size > 0) {
+                pi.permissions = new PermissionInfo[size];
+                for (int i = 0; i < size; i++) {
+                    pi.permissions[i] = generatePermissionInfo(pkg.getPermissions().get(i),
+                            flags);
+                }
+            }
+            final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
+            size = usesPermissions.size();
+            if (size > 0) {
+                pi.requestedPermissions = new String[size];
+                pi.requestedPermissionsFlags = new int[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedUsesPermission usesPermission = usesPermissions.get(i);
+                    pi.requestedPermissions[i] = usesPermission.getName();
+                    // The notion of required permissions is deprecated but for compatibility.
+                    pi.requestedPermissionsFlags[i] |=
+                            PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    if (grantedPermissions != null
+                            && grantedPermissions.contains(usesPermission.getName())) {
+                        pi.requestedPermissionsFlags[i] |=
+                                PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                    }
+                    if ((usesPermission.getUsesPermissionFlags()
+                            & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
+                        pi.requestedPermissionsFlags[i] |=
+                                PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
+                    }
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_ATTRIBUTIONS) != 0) {
+            int size = ArrayUtils.size(pkg.getAttributions());
+            if (size > 0) {
+                pi.attributions = new Attribution[size];
+                for (int i = 0; i < size; i++) {
+                    pi.attributions[i] = generateAttribution(pkg.getAttributions().get(i));
+                }
+            }
+            if (pkg.areAttributionsUserVisible()) {
+                pi.applicationInfo.privateFlagsExt
+                        |= ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+            } else {
+                pi.applicationInfo.privateFlagsExt
+                        &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+            }
+        } else {
+            pi.applicationInfo.privateFlagsExt
+                    &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+        }
+
+        if (apexInfo != null) {
+            File apexFile = new File(apexInfo.modulePath);
+
+            pi.applicationInfo.sourceDir = apexFile.getPath();
+            pi.applicationInfo.publicSourceDir = apexFile.getPath();
+            if (apexInfo.isFactory) {
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+            } else {
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+            }
+            if (apexInfo.isActive) {
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+            } else {
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+            }
+            pi.isApex = true;
+        }
+
+        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.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = signingDetails.getPastSigningCertificates()[0];
+            } else if (signingDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = signingDetails.getSignatures().length;
+                pi.signatures = new Signature[numberOfSigs];
+                System.arraycopy(signingDetails.getSignatures(), 0, pi.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
+                pi.signingInfo = new SigningInfo(signingDetails);
+            } else {
+                pi.signingInfo = null;
+            }
+        }
+
+        return pi;
+    }
+
+    @Nullable
+    public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
+            @PackageManager.ApplicationInfoFlagsBits long flags, PackageUserState state,
+            int userId) {
+        if (pkg == null) {
+            return null;
+        }
+
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+
+        return generateApplicationInfoUnchecked(pkg, flags, state, userId,
+                true /* assignUserFields */);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of system
+     * server.
+     * <p>
+     * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}.
+     *
+     * @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user
+     *                         specific fields. This can be skipped when building from a system
+     *                         server package, as there are cached strings which can be used rather
+     *                         than querying and concatenating the comparatively expensive {@link
+     *                         Environment#getDataDirectory(String)}}.
+     */
+    @NonNull
+    public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
+            @NonNull PackageUserState state, int userId, boolean assignUserFields) {
+        // Make shallow copy so we can store the metadata/libraries safely
+        ApplicationInfo ai = ((ParsingPackageHidden) pkg).toAppInfoWithoutState();
+
+        if (assignUserFields) {
+            assignUserFields(pkg, ai, userId);
+        }
+
+        updateApplicationInfo(ai, flags, state);
+
+        return ai;
+    }
+
+    private static void updateApplicationInfo(ApplicationInfo ai, long flags,
+            PackageUserState state) {
+        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();
+        }
+
+        ai.flags |= flag(state.isStopped(), ApplicationInfo.FLAG_STOPPED)
+                | flag(state.isInstalled(), ApplicationInfo.FLAG_INSTALLED)
+                | flag(state.isSuspended(), ApplicationInfo.FLAG_SUSPENDED);
+        ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
+                | flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
+                | flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
+
+        if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+            ai.enabled = true;
+        } else if (state.getEnabledState()
+                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+            ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+        } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                || state.getEnabledState()
+                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+            ai.enabled = false;
+        }
+        ai.enabledSetting = state.getEnabledState();
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+        }
+        ai.seInfoUser = SELinuxUtil.getSeinfoUser(state);
+        final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+        if (overlayPaths != null) {
+            ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+            ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+        }
+    }
+
+    @Nullable
+    public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
+            @NonNull PackageUserState state, int userId) {
+        if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
+            return null;
+        }
+        // This is used to return the ResolverActivity or instantAppInstallerActivity;
+        // we will just always make a copy.
+        ai = new ApplicationInfo(ai);
+        ai.initForUser(userId);
+        ai.icon = (ParsingPackageUtils.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes
+                : ai.iconRes;
+        updateApplicationInfo(ai, flags, state);
+        return ai;
+    }
+
+    @Nullable
+    public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull PackageUserState state, int userId) {
+        if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
+            return null;
+        }
+        // This is used to return the ResolverActivity or instantAppInstallerActivity;
+        // we will just always make a copy.
+        final ActivityInfo ai = new ActivityInfo(a);
+        ai.applicationInfo =
+                generateDelegateApplicationInfo(ai.applicationInfo, flags, state, userId);
+        return ai;
+    }
+
+    @Nullable
+    public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (a == null) return null;
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (applicationInfo == null) {
+            return null;
+        }
+
+        return generateActivityInfoUnchecked(a, flags, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of system
+     * server.
+     * <p>
+     * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, long,
+     * PackageUserState, ApplicationInfo, int)}.
+     */
+    @NonNull
+    public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull ApplicationInfo applicationInfo) {
+        // Make shallow copies so we can store the metadata safely
+        ActivityInfo ai = new ActivityInfo();
+        assignSharedFieldsForComponentInfo(ai, a);
+        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) {
+            ai.metaData = a.getMetaData();
+        }
+        ai.applicationInfo = applicationInfo;
+        return ai;
+    }
+
+    @Nullable
+    public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            int userId) {
+        return generateActivityInfo(pkg, a, flags, state, null, userId);
+    }
+
+    @Nullable
+    public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (s == null) return null;
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (applicationInfo == null) {
+            return null;
+        }
+
+        return generateServiceInfoUnchecked(s, flags,  applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of system
+     * server.
+     * <p>
+     * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, long, PackageUserState,
+     * ApplicationInfo, int)}.
+     */
+    @NonNull
+    public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull ApplicationInfo applicationInfo) {
+        // Make shallow copies so we can store the metadata safely
+        ServiceInfo si = new ServiceInfo();
+        assignSharedFieldsForComponentInfo(si, s);
+        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) {
+            si.metaData = s.getMetaData();
+        }
+        return si;
+    }
+
+    @Nullable
+    public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            int userId) {
+        return generateServiceInfo(pkg, s, flags, state, null, userId);
+    }
+
+    @Nullable
+    public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (p == null) return null;
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (applicationInfo == null) {
+            return null;
+        }
+
+        return generateProviderInfoUnchecked(p, flags, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of system
+     * server.
+     * <p>
+     * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, long,
+     * PackageUserState, ApplicationInfo, int)}.
+     */
+    @NonNull
+    public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull ApplicationInfo applicationInfo) {
+        // Make shallow copies so we can store the metadata safely
+        ProviderInfo pi = new ProviderInfo();
+        assignSharedFieldsForComponentInfo(pi, p);
+        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();
+        pi.pathPermissions = p.getPathPermissions();
+        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+            pi.uriPermissionPatterns = null;
+        }
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            pi.metaData = p.getMetaData();
+        }
+        pi.applicationInfo = applicationInfo;
+        return pi;
+    }
+
+    @Nullable
+    public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            int userId) {
+        return generateProviderInfo(pkg, p, flags, state, null, userId);
+    }
+
+    /**
+     * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead,
+     *                         long, PackageUserState, int, boolean)}
+     */
+    @Nullable
+    public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+            ParsingPackageRead pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId,
+            boolean assignUserFields) {
+        if (i == null) return null;
+
+        InstrumentationInfo ii = new InstrumentationInfo();
+        assignSharedFieldsForPackageItemInfo(ii, i);
+        ii.targetPackage = i.getTargetPackage();
+        ii.targetProcesses = i.getTargetProcesses();
+        ii.handleProfiling = i.isHandleProfiling();
+        ii.functionalTest = i.isFunctionalTest();
+
+        ii.sourceDir = pkg.getBaseApkPath();
+        ii.publicSourceDir = pkg.getBaseApkPath();
+        ii.splitNames = pkg.getSplitNames();
+        ii.splitSourceDirs = pkg.getSplitCodePaths();
+        ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
+        ii.splitDependencies = pkg.getSplitDependencies();
+
+        if (assignUserFields) {
+            assignUserFields(pkg, ii, userId);
+        }
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return ii;
+        }
+        ii.metaData = i.getMetaData();
+        return ii;
+    }
+
+    @Nullable
+    public static PermissionInfo generatePermissionInfo(ParsedPermission p,
+            @PackageManager.ComponentInfoFlagsBits long flags) {
+        if (p == null) return null;
+
+        PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
+
+        assignSharedFieldsForPackageItemInfo(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) {
+            return pi;
+        }
+        pi.metaData = p.getMetaData();
+        return pi;
+    }
+
+    @Nullable
+    public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
+            @PackageManager.ComponentInfoFlagsBits long flags) {
+        if (pg == null) return null;
+
+        PermissionGroupInfo pgi = new PermissionGroupInfo(
+                pg.getRequestDetailResourceId(),
+                pg.getBackgroundRequestResourceId(),
+                pg.getBackgroundRequestDetailResourceId()
+        );
+
+        assignSharedFieldsForPackageItemInfo(pgi, pg);
+        pgi.descriptionRes = pg.getDescriptionRes();
+        pgi.priority = pg.getPriority();
+        pgi.requestRes = pg.getRequestRes();
+        pgi.flags = pg.getFlags();
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return pgi;
+        }
+        pgi.metaData = pg.getMetaData();
+        return pgi;
+    }
+
+    @Nullable
+    public static Attribution generateAttribution(ParsedAttribution pa) {
+        if (pa == null) return null;
+        return new Attribution(pa.getTag(), pa.getLabel());
+    }
+
+    private static boolean checkUseInstalledOrHidden(long flags,
+            @NonNull PackageUserState state, @Nullable ApplicationInfo appInfo) {
+        // Returns false if the package is hidden system app until installed.
+        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+                && !state.isInstalled()
+                && appInfo != null && appInfo.hiddenUntilInstalled) {
+            return false;
+        }
+
+        // If available for the target user, or trying to match uninstalled packages and it's
+        // a system app.
+        return PackageUserStateUtils.isAvailable(state, flags)
+                || (appInfo != null && appInfo.isSystemApp()
+                && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+                || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
+    }
+
+    private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
+            @NonNull ParsedMainComponent mainComponent) {
+        assignSharedFieldsForPackageItemInfo(componentInfo, mainComponent);
+        componentInfo.descriptionRes = mainComponent.getDescriptionRes();
+        componentInfo.directBootAware = mainComponent.isDirectBootAware();
+        componentInfo.enabled = mainComponent.isEnabled();
+        componentInfo.splitName = mainComponent.getSplitName();
+        componentInfo.attributionTags = mainComponent.getAttributionTags();
+    }
+
+    private static void assignSharedFieldsForPackageItemInfo(
+            @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();
+    }
+
+    @CheckResult
+    private static int flag(boolean hasFlag, int flag) {
+        if (hasFlag) {
+            return flag;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * @see ApplicationInfo#flags
+     */
+    public static int appInfoFlags(ParsingPackageRead pkg) {
+        // @formatter:off
+        return flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE)
+                | flag(pkg.isBaseHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED)
+                | flag(pkg.isAllowBackup(), ApplicationInfo.FLAG_ALLOW_BACKUP)
+                | flag(pkg.isKillAfterRestore(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
+                | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+                | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY)
+                | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT)
+                | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE)
+                | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE)
+                | flag(pkg.isHasCode(), ApplicationInfo.FLAG_HAS_CODE)
+                | flag(pkg.isAllowTaskReparenting(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
+                | flag(pkg.isAllowClearUserData(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
+                | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP)
+                | flag(pkg.isUsesCleartextTraffic(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
+                | flag(pkg.isSupportsRtl(), ApplicationInfo.FLAG_SUPPORTS_RTL)
+                | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY)
+                | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH)
+                | flag(pkg.isExtractNativeLibs(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS)
+                | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME)
+                | flag(pkg.isSupportsSmallScreens(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS)
+                | flag(pkg.isSupportsNormalScreens(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS)
+                | flag(pkg.isSupportsLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS)
+                | flag(pkg.isSupportsExtraLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
+                | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
+                | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES);
+        // @formatter:on
+    }
+
+    /** @see ApplicationInfo#privateFlags */
+    public static int appInfoPrivateFlags(ParsingPackageRead pkg) {
+        // @formatter:off
+        int privateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY)
+                | flag(pkg.isOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY)
+                | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING)
+                | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS)
+                | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL)
+                | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND)
+                | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX)
+                | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
+                | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE)
+                | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE)
+                | flag(pkg.isAllowClearUserDataOnFailedRestore(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE)
+                | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE)
+                | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
+                | flag(pkg.isUsesNonSdkApi(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API)
+                | flag(pkg.isHasFragileUserData(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
+                | flag(pkg.isCantSaveState(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+                | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
+                | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING);
+        // @formatter:on
+
+        Boolean resizeableActivity = pkg.getResizeableActivity();
+        if (resizeableActivity != null) {
+            if (resizeableActivity) {
+                privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+            } else {
+                privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+            }
+        }
+
+        return privateFlags;
+    }
+
+    /** @see ApplicationInfo#privateFlagsExt */
+    public static int appInfoPrivateFlagsExt(ParsingPackageRead pkg) {
+        // @formatter:off
+        int privateFlagsExt =
+                flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE)
+                | flag(pkg.hasRequestForegroundServiceExemption(),
+                        ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION)
+                | flag(pkg.areAttributionsUserVisible(),
+                        ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE);
+        // @formatter:on
+        return privateFlagsExt;
+    }
+
+    private static boolean checkUseInstalled(ParsingPackageRead pkg,
+            PackageUserState state, @PackageManager.PackageInfoFlagsBits long flags) {
+        // If available for the target user
+        return PackageUserStateUtils.isAvailable(state, flags);
+    }
+
+    @NonNull
+    public static File getDataDir(ParsingPackageRead pkg, int userId) {
+        if ("android".equals(pkg.getPackageName())) {
+            return Environment.getDataSystemDirectory();
+        }
+
+        if (pkg.isDefaultToDeviceProtectedStorage()
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            return getDeviceProtectedDataDir(pkg, userId);
+        } else {
+            return getCredentialProtectedDataDir(pkg, userId);
+        }
+    }
+
+    @NonNull
+    public static File getDeviceProtectedDataDir(ParsingPackageRead pkg, int userId) {
+        return Environment.getDataUserDePackageDirectory(pkg.getVolumeUuid(), userId,
+                pkg.getPackageName());
+    }
+
+    @NonNull
+    public static File getCredentialProtectedDataDir(ParsingPackageRead pkg, int userId) {
+        return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId,
+                pkg.getPackageName());
+    }
+
+    private static void assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId) {
+        // This behavior is undefined for no-state ApplicationInfos when called by a public API,
+        // since the uid is never assigned by the system. It will always effectively be appId 0.
+        info.uid = UserHandle.getUid(userId, UserHandle.getAppId(info.uid));
+
+        String pkgName = pkg.getPackageName();
+        if ("android".equals(pkgName)) {
+            info.dataDir = SYSTEM_DATA_PATH;
+            return;
+        }
+
+        // For performance reasons, all these paths are built as strings
+        String baseDataDirPrefix =
+                Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator;
+        String userIdPkgSuffix = File.separator + userId + File.separator + pkgName;
+        info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE
+                + userIdPkgSuffix;
+        info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix;
+
+        if (pkg.isDefaultToDeviceProtectedStorage()
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            info.dataDir = info.deviceProtectedDataDir;
+        } else {
+            info.dataDir = info.credentialProtectedDataDir;
+        }
+    }
+
+    private static void assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info,
+            int userId) {
+        String pkgName = pkg.getPackageName();
+        if ("android".equals(pkgName)) {
+            info.dataDir = SYSTEM_DATA_PATH;
+            return;
+        }
+
+        // For performance reasons, all these paths are built as strings
+        String baseDataDirPrefix =
+                Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator;
+        String userIdPkgSuffix = File.separator + userId + File.separator + pkgName;
+        info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE
+                + userIdPkgSuffix;
+        info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix;
+
+        if (pkg.isDefaultToDeviceProtectedStorage()
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            info.dataDir = info.deviceProtectedDataDir;
+        } else {
+            info.dataDir = info.credentialProtectedDataDir;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
new file mode 100644
index 0000000..52d9b7a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.SigningDetails;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
+
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Methods used for mutation during direct package parsing.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedReturnValue")
+public interface ParsingPackage extends ParsingPackageRead {
+
+    ParsingPackage addActivity(ParsedActivity parsedActivity);
+
+    ParsingPackage addAdoptPermission(String adoptPermission);
+
+    ParsingPackage addApexSystemService(ParsedApexSystemService parsedApexSystemService);
+
+    ParsingPackage addConfigPreference(ConfigurationInfo configPreference);
+
+    ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup);
+
+    ParsingPackage addImplicitPermission(String permission);
+
+    ParsingPackage addInstrumentation(ParsedInstrumentation instrumentation);
+
+    ParsingPackage addKeySet(String keySetName, PublicKey publicKey);
+
+    ParsingPackage addLibraryName(String libraryName);
+
+    ParsingPackage addOriginalPackage(String originalPackage);
+
+    ParsingPackage addOverlayable(String overlayableName, String actorName);
+
+    ParsingPackage addPermission(ParsedPermission permission);
+
+    ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
+
+    ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo);
+
+    /** Add a property to the application scope */
+    ParsingPackage addProperty(Property property);
+
+    ParsingPackage addProtectedBroadcast(String protectedBroadcast);
+
+    ParsingPackage addProvider(ParsedProvider parsedProvider);
+
+    ParsingPackage addAttribution(ParsedAttribution attribution);
+
+    ParsingPackage addReceiver(ParsedActivity parsedReceiver);
+
+    ParsingPackage addReqFeature(FeatureInfo reqFeature);
+
+    ParsingPackage addUsesPermission(ParsedUsesPermission parsedUsesPermission);
+
+    ParsingPackage addService(ParsedService parsedService);
+
+    ParsingPackage addUsesLibrary(String libraryName);
+
+    ParsingPackage addUsesOptionalLibrary(String libraryName);
+
+    ParsingPackage addUsesNativeLibrary(String libraryName);
+
+    ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
+
+    ParsingPackage addUsesSdkLibrary(String libraryName, long versionMajor,
+            String[] certSha256Digests);
+
+    ParsingPackage addUsesStaticLibrary(String libraryName, long version,
+            String[] certSha256Digests);
+
+    ParsingPackage addQueriesIntent(Intent intent);
+
+    ParsingPackage addQueriesPackage(String packageName);
+
+    ParsingPackage addQueriesProvider(String authority);
+
+    /** Sets a process name -> {@link ParsedProcess} map coming from the <processes> tag. */
+    ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
+
+    ParsingPackage asSplit(
+            String[] splitNames,
+            String[] splitCodePaths,
+            int[] splitRevisionCodes,
+            @Nullable SparseArray<int[]> splitDependencies
+    );
+
+    ParsingPackage setMetaData(Bundle metaData);
+
+    ParsingPackage setForceQueryable(boolean forceQueryable);
+
+    ParsingPackage setMaxAspectRatio(float maxAspectRatio);
+
+    ParsingPackage setMinAspectRatio(float minAspectRatio);
+
+    ParsingPackage setPermission(String permission);
+
+    ParsingPackage setProcessName(String processName);
+
+    ParsingPackage setSharedUserId(String sharedUserId);
+
+    ParsingPackage setStaticSharedLibName(String staticSharedLibName);
+
+    ParsingPackage setTaskAffinity(String taskAffinity);
+
+    ParsingPackage setTargetSdkVersion(int targetSdkVersion);
+
+    ParsingPackage setUiOptions(int uiOptions);
+
+    ParsingPackage setBaseHardwareAccelerated(boolean baseHardwareAccelerated);
+
+    ParsingPackage setResizeableActivity(Boolean resizeable);
+
+    ParsingPackage setResizeableActivityViaSdkVersion(boolean resizeableViaSdkVersion);
+
+    ParsingPackage setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture);
+
+    ParsingPackage setAllowBackup(boolean allowBackup);
+
+    ParsingPackage setAllowClearUserData(boolean allowClearUserData);
+
+    ParsingPackage setAllowClearUserDataOnFailedRestore(boolean allowClearUserDataOnFailedRestore);
+
+    ParsingPackage setAllowTaskReparenting(boolean allowTaskReparenting);
+
+    ParsingPackage setOverlay(boolean isOverlay);
+
+    ParsingPackage setBackupInForeground(boolean backupInForeground);
+
+    ParsingPackage setCantSaveState(boolean cantSaveState);
+
+    ParsingPackage setDebuggable(boolean debuggable);
+
+    ParsingPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage);
+
+    ParsingPackage setDirectBootAware(boolean directBootAware);
+
+    ParsingPackage setExternalStorage(boolean externalStorage);
+
+    ParsingPackage setExtractNativeLibs(boolean extractNativeLibs);
+
+    ParsingPackage setFullBackupOnly(boolean fullBackupOnly);
+
+    ParsingPackage setHasCode(boolean hasCode);
+
+    ParsingPackage setHasFragileUserData(boolean hasFragileUserData);
+
+    ParsingPackage setGame(boolean isGame);
+
+    ParsingPackage setIsolatedSplitLoading(boolean isolatedSplitLoading);
+
+    ParsingPackage setKillAfterRestore(boolean killAfterRestore);
+
+    ParsingPackage setLargeHeap(boolean largeHeap);
+
+    ParsingPackage setMultiArch(boolean multiArch);
+
+    ParsingPackage setPartiallyDirectBootAware(boolean partiallyDirectBootAware);
+
+    ParsingPackage setPersistent(boolean persistent);
+
+    ParsingPackage setProfileableByShell(boolean profileableByShell);
+
+    ParsingPackage setProfileable(boolean profileable);
+
+    ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
+
+    ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+
+    ParsingPackage setAutoRevokePermissions(int autoRevokePermissions);
+
+    ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
+
+    ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
+
+    ParsingPackage setSdkLibName(String sdkLibName);
+
+    ParsingPackage setSdkLibVersionMajor(int sdkLibVersionMajor);
+
+    ParsingPackage setSdkLibrary(boolean sdkLibrary);
+
+    ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
+
+    ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary);
+
+    ParsingPackage setSupportsRtl(boolean supportsRtl);
+
+    ParsingPackage setTestOnly(boolean testOnly);
+
+    ParsingPackage setUseEmbeddedDex(boolean useEmbeddedDex);
+
+    ParsingPackage setUsesCleartextTraffic(boolean usesCleartextTraffic);
+
+    ParsingPackage setUsesNonSdkApi(boolean usesNonSdkApi);
+
+    ParsingPackage setVisibleToInstantApps(boolean visibleToInstantApps);
+
+    ParsingPackage setVmSafeMode(boolean vmSafeMode);
+
+    ParsingPackage removeUsesOptionalLibrary(String libraryName);
+
+    ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
+
+    ParsingPackage setAnyDensity(int anyDensity);
+
+    ParsingPackage setAppComponentFactory(String appComponentFactory);
+
+    ParsingPackage setBackupAgentName(String backupAgentName);
+
+    ParsingPackage setBanner(int banner);
+
+    ParsingPackage setCategory(int category);
+
+    ParsingPackage setClassLoaderName(String classLoaderName);
+
+    ParsingPackage setClassName(String className);
+
+    ParsingPackage setCompatibleWidthLimitDp(int compatibleWidthLimitDp);
+
+    ParsingPackage setDescriptionRes(int descriptionRes);
+
+    ParsingPackage setEnabled(boolean enabled);
+
+    ParsingPackage setGwpAsanMode(@ApplicationInfo.GwpAsanMode int gwpAsanMode);
+
+    ParsingPackage setMemtagMode(@ApplicationInfo.MemtagMode int memtagMode);
+
+    ParsingPackage setNativeHeapZeroInitialized(
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
+
+    ParsingPackage setRequestRawExternalStorageAccess(
+            @Nullable Boolean requestRawExternalStorageAccess);
+
+    ParsingPackage setCrossProfile(boolean crossProfile);
+
+    ParsingPackage setFullBackupContent(int fullBackupContent);
+
+    ParsingPackage setDataExtractionRules(int dataExtractionRules);
+
+    ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
+
+    ParsingPackage setIconRes(int iconRes);
+
+    ParsingPackage setInstallLocation(int installLocation);
+
+    /** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */
+    ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys);
+
+    ParsingPackage setLabelRes(int labelRes);
+
+    ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
+
+    ParsingPackage setLogo(int logo);
+
+    ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
+
+    ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions);
+
+    ParsingPackage setMinSdkVersion(int minSdkVersion);
+
+    ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
+
+    ParsingPackage setNonLocalizedLabel(CharSequence nonLocalizedLabel);
+
+    ParsingPackage setOverlayCategory(String overlayCategory);
+
+    ParsingPackage setOverlayIsStatic(boolean overlayIsStatic);
+
+    ParsingPackage setOverlayPriority(int overlayPriority);
+
+    ParsingPackage setOverlayTarget(String overlayTarget);
+
+    ParsingPackage setOverlayTargetOverlayableName(String overlayTargetOverlayableName);
+
+    ParsingPackage setRequiredAccountType(String requiredAccountType);
+
+    ParsingPackage setRequiredForAllUsers(boolean requiredForAllUsers);
+
+    ParsingPackage setRequiresSmallestWidthDp(int requiresSmallestWidthDp);
+
+    ParsingPackage setResizeable(int resizeable);
+
+    ParsingPackage setRestrictUpdateHash(byte[] restrictUpdateHash);
+
+    ParsingPackage setRestrictedAccountType(String restrictedAccountType);
+
+    ParsingPackage setRoundIconRes(int roundIconRes);
+
+    ParsingPackage setSharedUserLabel(int sharedUserLabel);
+
+    ParsingPackage setSigningDetails(SigningDetails signingDetails);
+
+    ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
+
+    ParsingPackage setStaticSharedLibVersion(long staticSharedLibVersion);
+
+    ParsingPackage setSupportsLargeScreens(int supportsLargeScreens);
+
+    ParsingPackage setSupportsNormalScreens(int supportsNormalScreens);
+
+    ParsingPackage setSupportsSmallScreens(int supportsSmallScreens);
+
+    ParsingPackage setSupportsExtraLargeScreens(int supportsExtraLargeScreens);
+
+    ParsingPackage setTargetSandboxVersion(int targetSandboxVersion);
+
+    ParsingPackage setTheme(int theme);
+
+    ParsingPackage setRequestForegroundServiceExemption(boolean requestForegroundServiceExemption);
+
+    ParsingPackage setUpgradeKeySets(@NonNull Set<String> upgradeKeySets);
+
+    ParsingPackage setUse32BitAbi(boolean use32BitAbi);
+
+    ParsingPackage setVolumeUuid(@Nullable String volumeUuid);
+
+    ParsingPackage setZygotePreloadName(String zygotePreloadName);
+
+    ParsingPackage sortActivities();
+
+    ParsingPackage sortReceivers();
+
+    ParsingPackage sortServices();
+
+    ParsingPackage setBaseRevisionCode(int baseRevisionCode);
+
+    ParsingPackage setVersionName(String versionName);
+
+    ParsingPackage setCompileSdkVersion(int compileSdkVersion);
+
+    ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName);
+
+    ParsingPackage setAttributionsAreUserVisible(boolean attributionsAreUserVisible);
+
+    ParsingPackage setResetEnabledSettingsOnAppDataCleared(
+            boolean resetEnabledSettingsOnAppDataCleared);
+
+    ParsingPackage setLocaleConfigRes(int localeConfigRes);
+
+    // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
+    //  for moving to the next step
+    @CallSuper
+    Object hideAsParsed();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java
new file mode 100644
index 0000000..66e01a6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+
+/**
+ * Methods that normal consumers should not have access to. This usually means the field is stateful
+ * or deprecated and should be access through a utility class or a system manager class.
+ * <p>
+ * This is a separate interface, not implemented by the base {@link ParsingPackageRead} because Java
+ * doesn't support non-public interface methods. The class must be cast to this interface.
+ *
+ * @hide
+ */
+interface ParsingPackageHidden {
+
+    /**
+     * @see PackageInfo#versionCode
+     * @see ApplicationInfo#versionCode
+     */
+    int getVersionCode();
+
+    /**
+     * @see PackageInfo#versionCodeMajor
+     */
+    int getVersionCodeMajor();
+
+    // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
+    ApplicationInfo toAppInfoWithoutState();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
new file mode 100644
index 0000000..1f21938
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -0,0 +1,2975 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.annotation.CallSuper;
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.SigningDetails;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArray;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringList;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl;
+import com.android.server.pm.pkg.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedAttributionImpl;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
+import com.android.server.pm.pkg.component.ParsedPermissionImpl;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.component.ParsedServiceImpl;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
+import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
+
+import java.security.PublicKey;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * The backing data for a package that was parsed from disk.
+ *
+ * The field nullability annotations here are for internal reference. For effective nullability,
+ * see the parent interfaces.
+ *
+ * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case
+ *
+ * @hide
+ */
+public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, Parcelable {
+
+    public static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+    public static ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
+            ForInternedString.class);
+    public static ForInternedStringArray sForInternedStringArray = Parcelling.Cache.getOrCreate(
+            ForInternedStringArray.class);
+    public static ForInternedStringList sForInternedStringList = Parcelling.Cache.getOrCreate(
+            ForInternedStringList.class);
+    public static ForInternedStringValueMap sForInternedStringValueMap =
+            Parcelling.Cache.getOrCreate(ForInternedStringValueMap.class);
+    public static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+    public static ForInternedStringSet sForInternedStringSet =
+            Parcelling.Cache.getOrCreate(ForInternedStringSet.class);
+    protected static ParsingUtils.StringPairListParceler sForIntentInfoPairs =
+            new ParsingUtils.StringPairListParceler();
+
+    private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
+            (first, second) -> Integer.compare(second.getOrder(), first.getOrder());
+
+    // These are objects because null represents not explicitly set
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsSmallScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsNormalScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsLargeScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsExtraLargeScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean resizeable;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean anyDensity;
+
+    protected int versionCode;
+    protected int versionCodeMajor;
+    private int baseRevisionCode;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String versionName;
+
+    private int compileSdkVersion;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String compileSdkVersionCodeName;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    protected String packageName;
+
+    @NonNull
+    protected String mBaseApkPath;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String restrictedAccountType;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String requiredAccountType;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String overlayTarget;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String overlayTargetOverlayableName;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String overlayCategory;
+    private int overlayPriority;
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringValueMap.class)
+    private Map<String, String> overlayables = emptyMap();
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String sdkLibName;
+    private int sdkLibVersionMajor;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String staticSharedLibName;
+    private long staticSharedLibVersion;
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> libraryNames = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesLibraries = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesOptionalLibraries = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesNativeLibraries = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesOptionalNativeLibraries = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> usesStaticLibraries = emptyList();
+    @Nullable
+    private long[] usesStaticLibrariesVersions;
+    @Nullable
+    private String[][] usesStaticLibrariesCertDigests;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> usesSdkLibraries = emptyList();
+    @Nullable
+    private long[] usesSdkLibrariesVersionsMajor;
+    @Nullable
+    private String[][] usesSdkLibrariesCertDigests;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String sharedUserId;
+
+    private int sharedUserLabel;
+    @NonNull
+    private List<ConfigurationInfo> configPreferences = emptyList();
+    @NonNull
+    private List<FeatureInfo> reqFeatures = emptyList();
+    @NonNull
+    private List<FeatureGroupInfo> featureGroups = emptyList();
+
+    @Nullable
+    private byte[] restrictUpdateHash;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> originalPackages = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> adoptPermissions = emptyList();
+    /**
+     * @deprecated consider migrating to {@link #getUsesPermissions} which has
+     *             more parsed details, such as flags
+     */
+    @NonNull
+    @Deprecated
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> requestedPermissions = emptyList();
+
+    @NonNull
+    private List<ParsedUsesPermission> usesPermissions = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> implicitPermissions = emptyList();
+
+    @NonNull
+    private Set<String> upgradeKeySets = emptySet();
+    @NonNull
+    private Map<String, ArraySet<PublicKey>> keySetMapping = emptyMap();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> protectedBroadcasts = emptyList();
+
+    @NonNull
+    protected List<ParsedActivity> activities = emptyList();
+
+    @NonNull
+    protected List<ParsedApexSystemService> apexSystemServices = emptyList();
+
+    @NonNull
+    protected List<ParsedActivity> receivers = emptyList();
+
+    @NonNull
+    protected List<ParsedService> services = emptyList();
+
+    @NonNull
+    protected List<ParsedProvider> providers = emptyList();
+
+    @NonNull
+    private List<ParsedAttribution> attributions = emptyList();
+
+    @NonNull
+    protected List<ParsedPermission> permissions = emptyList();
+
+    @NonNull
+    protected List<ParsedPermissionGroup> permissionGroups = emptyList();
+
+    @NonNull
+    protected List<ParsedInstrumentation> instrumentations = emptyList();
+
+    @NonNull
+//    @DataClass.ParcelWith(ParsingUtils.StringPairListParceler.class)
+    private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
+
+    /**
+     * Map from a process name to a {@link ParsedProcess}.
+     */
+    @NonNull
+    private Map<String, ParsedProcess> processes = emptyMap();
+
+    @Nullable
+    private Bundle metaData;
+
+    @NonNull
+    private Map<String, Property> mProperties = emptyMap();
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    protected String volumeUuid;
+    @Nullable
+    private SigningDetails signingDetails;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    protected String mPath;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<Intent> queriesIntents = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> queriesPackages = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringSet.class)
+    private Set<String> queriesProviders = emptySet();
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedStringArray.class)
+    private String[] splitClassLoaderNames;
+    @Nullable
+    protected String[] splitCodePaths;
+    @Nullable
+    private SparseArray<int[]> splitDependencies;
+    @Nullable
+    private int[] splitFlags;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedStringArray.class)
+    private String[] splitNames;
+    @Nullable
+    private int[] splitRevisionCodes;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String appComponentFactory;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String backupAgentName;
+    private int banner;
+    private int category = ApplicationInfo.CATEGORY_UNDEFINED;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String classLoaderName;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String className;
+    private int compatibleWidthLimitDp;
+    private int descriptionRes;
+
+    private int fullBackupContent;
+    private int dataExtractionRules;
+    private int iconRes;
+    private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION;
+    private int labelRes;
+    private int largestWidthLimitDp;
+    private int logo;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String manageSpaceActivityName;
+    private float maxAspectRatio;
+    private float minAspectRatio;
+    @Nullable
+    private SparseIntArray minExtensionVersions;
+    private int minSdkVersion = ParsingUtils.DEFAULT_MIN_SDK_VERSION;
+    private int networkSecurityConfigRes;
+    @Nullable
+    private CharSequence nonLocalizedLabel;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String processName;
+    private int requiresSmallestWidthDp;
+    private int roundIconRes;
+    private int targetSandboxVersion;
+    private int targetSdkVersion = ParsingUtils.DEFAULT_TARGET_SDK_VERSION;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String taskAffinity;
+    private int theme;
+
+    private int uiOptions;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String zygotePreloadName;
+
+    /**
+     * @see ParsingPackageRead#getResizeableActivity()
+     */
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean resizeableActivity;
+
+    private int autoRevokePermissions;
+
+    @ApplicationInfo.GwpAsanMode
+    private int gwpAsanMode;
+
+    @ApplicationInfo.MemtagMode
+    private int memtagMode;
+
+    @ApplicationInfo.NativeHeapZeroInitialized
+    private int nativeHeapZeroInitialized;
+
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean requestRawExternalStorageAccess;
+
+    // TODO(chiuwinson): Non-null
+    @Nullable
+    private ArraySet<String> mimeGroups;
+
+    // Usually there's code to set enabled to true during parsing, but it's possible to install
+    // an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
+    // and never assign this, so initialize this to true for those cases.
+    private long mBooleans = Booleans.ENABLED;
+
+    /**
+     * Flags used for a internal bitset. These flags should never be persisted or exposed outside
+     * of this class. It is expected that PackageCacher explicitly clears itself whenever the
+     * Parcelable implementation changes such that all these flags can be re-ordered or invalidated.
+     */
+    protected static class Booleans {
+        @LongDef({
+                EXTERNAL_STORAGE,
+                BASE_HARDWARE_ACCELERATED,
+                ALLOW_BACKUP,
+                KILL_AFTER_RESTORE,
+                RESTORE_ANY_VERSION,
+                FULL_BACKUP_ONLY,
+                PERSISTENT,
+                DEBUGGABLE,
+                VM_SAFE_MODE,
+                HAS_CODE,
+                ALLOW_TASK_REPARENTING,
+                ALLOW_CLEAR_USER_DATA,
+                LARGE_HEAP,
+                USES_CLEARTEXT_TRAFFIC,
+                SUPPORTS_RTL,
+                TEST_ONLY,
+                MULTI_ARCH,
+                EXTRACT_NATIVE_LIBS,
+                GAME,
+                STATIC_SHARED_LIBRARY,
+                OVERLAY,
+                ISOLATED_SPLIT_LOADING,
+                HAS_DOMAIN_URLS,
+                PROFILEABLE_BY_SHELL,
+                BACKUP_IN_FOREGROUND,
+                USE_EMBEDDED_DEX,
+                DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
+                DIRECT_BOOT_AWARE,
+                PARTIALLY_DIRECT_BOOT_AWARE,
+                RESIZEABLE_ACTIVITY_VIA_SDK_VERSION,
+                ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
+                ALLOW_AUDIO_PLAYBACK_CAPTURE,
+                REQUEST_LEGACY_EXTERNAL_STORAGE,
+                USES_NON_SDK_API,
+                HAS_FRAGILE_USER_DATA,
+                CANT_SAVE_STATE,
+                ALLOW_NATIVE_HEAP_POINTER_TAGGING,
+                PRESERVE_LEGACY_EXTERNAL_STORAGE,
+                REQUIRED_FOR_ALL_USERS,
+                OVERLAY_IS_STATIC,
+                USE_32_BIT_ABI,
+                VISIBLE_TO_INSTANT_APPS,
+                FORCE_QUERYABLE,
+                CROSS_PROFILE,
+                ENABLED,
+                DISALLOW_PROFILING,
+                REQUEST_FOREGROUND_SERVICE_EXEMPTION,
+                ATTRIBUTIONS_ARE_USER_VISIBLE,
+                RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED,
+                SDK_LIBRARY,
+                INHERIT_KEYSTORE_KEYS,
+        })
+        public @interface Values {}
+        private static final long EXTERNAL_STORAGE = 1L;
+        private static final long BASE_HARDWARE_ACCELERATED = 1L << 1;
+        private static final long ALLOW_BACKUP = 1L << 2;
+        private static final long KILL_AFTER_RESTORE = 1L << 3;
+        private static final long RESTORE_ANY_VERSION = 1L << 4;
+        private static final long FULL_BACKUP_ONLY = 1L << 5;
+        private static final long PERSISTENT = 1L << 6;
+        private static final long DEBUGGABLE = 1L << 7;
+        private static final long VM_SAFE_MODE = 1L << 8;
+        private static final long HAS_CODE = 1L << 9;
+        private static final long ALLOW_TASK_REPARENTING = 1L << 10;
+        private static final long ALLOW_CLEAR_USER_DATA = 1L << 11;
+        private static final long LARGE_HEAP = 1L << 12;
+        private static final long USES_CLEARTEXT_TRAFFIC = 1L << 13;
+        private static final long SUPPORTS_RTL = 1L << 14;
+        private static final long TEST_ONLY = 1L << 15;
+        private static final long MULTI_ARCH = 1L << 16;
+        private static final long EXTRACT_NATIVE_LIBS = 1L << 17;
+        private static final long GAME = 1L << 18;
+        private static final long STATIC_SHARED_LIBRARY = 1L << 19;
+        private static final long OVERLAY = 1L << 20;
+        private static final long ISOLATED_SPLIT_LOADING = 1L << 21;
+        private static final long HAS_DOMAIN_URLS = 1L << 22;
+        private static final long PROFILEABLE_BY_SHELL = 1L << 23;
+        private static final long BACKUP_IN_FOREGROUND = 1L << 24;
+        private static final long USE_EMBEDDED_DEX = 1L << 25;
+        private static final long DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1L << 26;
+        private static final long DIRECT_BOOT_AWARE = 1L << 27;
+        private static final long PARTIALLY_DIRECT_BOOT_AWARE = 1L << 28;
+        private static final long RESIZEABLE_ACTIVITY_VIA_SDK_VERSION = 1L << 29;
+        private static final long ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1L << 30;
+        private static final long ALLOW_AUDIO_PLAYBACK_CAPTURE = 1L << 31;
+        private static final long REQUEST_LEGACY_EXTERNAL_STORAGE = 1L << 32;
+        private static final long USES_NON_SDK_API = 1L << 33;
+        private static final long HAS_FRAGILE_USER_DATA = 1L << 34;
+        private static final long CANT_SAVE_STATE = 1L << 35;
+        private static final long ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1L << 36;
+        private static final long PRESERVE_LEGACY_EXTERNAL_STORAGE = 1L << 37;
+        private static final long REQUIRED_FOR_ALL_USERS = 1L << 38;
+        private static final long OVERLAY_IS_STATIC = 1L << 39;
+        private static final long USE_32_BIT_ABI = 1L << 40;
+        private static final long VISIBLE_TO_INSTANT_APPS = 1L << 41;
+        private static final long FORCE_QUERYABLE = 1L << 42;
+        private static final long CROSS_PROFILE = 1L << 43;
+        private static final long ENABLED = 1L << 44;
+        private static final long DISALLOW_PROFILING = 1L << 45;
+        private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46;
+        private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47;
+        private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
+        private static final long SDK_LIBRARY = 1L << 49;
+        private static final long INHERIT_KEYSTORE_KEYS = 1L << 50;
+    }
+
+    private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
+        if (value) {
+            mBooleans |= flag;
+        } else {
+            mBooleans &= ~flag;
+        }
+        return this;
+    }
+
+    private boolean getBoolean(@Booleans.Values long flag) {
+        return (mBooleans & flag) != 0;
+    }
+
+    // Derived fields
+    @NonNull
+    private UUID mStorageUuid;
+    private long mLongVersionCode;
+
+    private int mLocaleConfigRes;
+
+    @VisibleForTesting
+    public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
+            @NonNull String path, @Nullable TypedArray manifestArray) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        this.mBaseApkPath = baseApkPath;
+        this.mPath = path;
+
+        if (manifestArray != null) {
+            versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
+            versionCodeMajor = manifestArray.getInteger(
+                    R.styleable.AndroidManifest_versionCodeMajor, 0);
+            setBaseRevisionCode(
+                    manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode, 0));
+            setVersionName(manifestArray.getNonConfigurationString(
+                    R.styleable.AndroidManifest_versionName, 0));
+
+            setCompileSdkVersion(manifestArray.getInteger(
+                    R.styleable.AndroidManifest_compileSdkVersion, 0));
+            setCompileSdkVersionCodeName(manifestArray.getNonConfigurationString(
+                    R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
+
+            setIsolatedSplitLoading(manifestArray.getBoolean(
+                    R.styleable.AndroidManifest_isolatedSplits, false));
+
+        }
+    }
+
+    public boolean isSupportsSmallScreens() {
+        if (supportsSmallScreens == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return supportsSmallScreens;
+    }
+
+    public boolean isSupportsNormalScreens() {
+        return supportsNormalScreens == null || supportsNormalScreens;
+    }
+
+    public boolean isSupportsLargeScreens() {
+        if (supportsLargeScreens == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return supportsLargeScreens;
+    }
+
+    public boolean isSupportsExtraLargeScreens() {
+        if (supportsExtraLargeScreens == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.GINGERBREAD;
+        }
+
+        return supportsExtraLargeScreens;
+    }
+
+    public boolean isResizeable() {
+        if (resizeable == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return resizeable;
+    }
+
+    public boolean isAnyDensity() {
+        if (anyDensity == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return anyDensity;
+    }
+
+    @Override
+    public ParsingPackageImpl sortActivities() {
+        Collections.sort(this.activities, ORDER_COMPARATOR);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl sortReceivers() {
+        Collections.sort(this.receivers, ORDER_COMPARATOR);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl sortServices() {
+        Collections.sort(this.services, ORDER_COMPARATOR);
+        return this;
+    }
+
+    @CallSuper
+    @Override
+    public Object hideAsParsed() {
+        assignDerivedFields();
+        return this;
+    }
+
+    private void assignDerivedFields() {
+        mStorageUuid = StorageManager.convert(volumeUuid);
+        mLongVersionCode = PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
+    @Override
+    public ParsingPackageImpl addConfigPreference(ConfigurationInfo configPreference) {
+        this.configPreferences = CollectionUtils.add(this.configPreferences, configPreference);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addReqFeature(FeatureInfo reqFeature) {
+        this.reqFeatures = CollectionUtils.add(this.reqFeatures, reqFeature);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) {
+        this.featureGroups = CollectionUtils.add(this.featureGroups, featureGroup);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addProperty(@Nullable Property property) {
+        if (property == null) {
+            return this;
+        }
+        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) {
+        if (!this.protectedBroadcasts.contains(protectedBroadcast)) {
+            this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts,
+                    TextUtils.safeIntern(protectedBroadcast));
+        }
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addInstrumentation(ParsedInstrumentation instrumentation) {
+        this.instrumentations = CollectionUtils.add(this.instrumentations, instrumentation);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addOriginalPackage(String originalPackage) {
+        this.originalPackages = CollectionUtils.add(this.originalPackages, originalPackage);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage addOverlayable(String overlayableName, String actorName) {
+        this.overlayables = CollectionUtils.add(this.overlayables, overlayableName,
+                TextUtils.safeIntern(actorName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addAdoptPermission(String adoptPermission) {
+        this.adoptPermissions = CollectionUtils.add(this.adoptPermissions,
+                TextUtils.safeIntern(adoptPermission));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addPermission(ParsedPermission permission) {
+        this.permissions = CollectionUtils.add(this.permissions, permission);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) {
+        this.permissionGroups = CollectionUtils.add(this.permissionGroups, permissionGroup);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesPermission(ParsedUsesPermission permission) {
+        this.usesPermissions = CollectionUtils.add(this.usesPermissions, permission);
+
+        // Continue populating legacy data structures to avoid performance
+        // issues until all that code can be migrated
+        this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
+                permission.getName());
+
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addImplicitPermission(String permission) {
+        addUsesPermission(new ParsedUsesPermissionImpl(permission, 0 /*usesPermissionFlags*/));
+        this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
+                TextUtils.safeIntern(permission));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addKeySet(String keySetName, PublicKey publicKey) {
+        ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName);
+        if (publicKeys == null) {
+            publicKeys = new ArraySet<>();
+        }
+        publicKeys.add(publicKey);
+        keySetMapping = CollectionUtils.add(this.keySetMapping, keySetName, publicKeys);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addActivity(ParsedActivity parsedActivity) {
+        this.activities = CollectionUtils.add(this.activities, parsedActivity);
+        addMimeGroupsFromComponent(parsedActivity);
+        return this;
+    }
+
+    @Override
+    public final ParsingPackageImpl addApexSystemService(
+            ParsedApexSystemService parsedApexSystemService) {
+        this.apexSystemServices = CollectionUtils.add(
+                this.apexSystemServices, parsedApexSystemService);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
+        this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
+        addMimeGroupsFromComponent(parsedReceiver);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addService(ParsedService parsedService) {
+        this.services = CollectionUtils.add(this.services, parsedService);
+        addMimeGroupsFromComponent(parsedService);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addProvider(ParsedProvider parsedProvider) {
+        this.providers = CollectionUtils.add(this.providers, parsedProvider);
+        addMimeGroupsFromComponent(parsedProvider);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addAttribution(ParsedAttribution attribution) {
+        this.attributions = CollectionUtils.add(this.attributions, attribution);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addLibraryName(String libraryName) {
+        this.libraryNames = CollectionUtils.add(this.libraryNames,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesOptionalLibrary(String libraryName) {
+        this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesLibrary(String libraryName) {
+        this.usesLibraries = CollectionUtils.add(this.usesLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl removeUsesOptionalLibrary(String libraryName) {
+        this.usesOptionalLibraries = CollectionUtils.remove(this.usesOptionalLibraries,
+                libraryName);
+        return this;
+    }
+
+    @Override
+    public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
+        this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
+        this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
+        this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
+                libraryName);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesSdkLibrary(String libraryName, long versionMajor,
+            String[] certSha256Digests) {
+        this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries,
+                TextUtils.safeIntern(libraryName));
+        this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
+                this.usesSdkLibrariesVersionsMajor, versionMajor, true);
+        this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+                this.usesSdkLibrariesCertDigests, certSha256Digests, true);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesStaticLibrary(String libraryName, long version,
+            String[] certSha256Digests) {
+        this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
+                TextUtils.safeIntern(libraryName));
+        this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
+                version, true);
+        this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+                this.usesStaticLibrariesCertDigests, certSha256Digests, true);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addPreferredActivityFilter(String className,
+            ParsedIntentInfo intentInfo) {
+        this.preferredActivityFilters = CollectionUtils.add(this.preferredActivityFilters,
+                Pair.create(className, intentInfo));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addQueriesIntent(Intent intent) {
+        this.queriesIntents = CollectionUtils.add(this.queriesIntents, intent);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addQueriesPackage(String packageName) {
+        this.queriesPackages = CollectionUtils.add(this.queriesPackages,
+                TextUtils.safeIntern(packageName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addQueriesProvider(String authority) {
+        this.queriesProviders = CollectionUtils.add(this.queriesProviders, authority);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
+        if (supportsSmallScreens == 1) {
+            return this;
+        }
+
+        this.supportsSmallScreens = supportsSmallScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsNormalScreens(int supportsNormalScreens) {
+        if (supportsNormalScreens == 1) {
+            return this;
+        }
+
+        this.supportsNormalScreens = supportsNormalScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsLargeScreens(int supportsLargeScreens) {
+        if (supportsLargeScreens == 1) {
+            return this;
+        }
+
+        this.supportsLargeScreens = supportsLargeScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsExtraLargeScreens(int supportsExtraLargeScreens) {
+        if (supportsExtraLargeScreens == 1) {
+            return this;
+        }
+
+        this.supportsExtraLargeScreens = supportsExtraLargeScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setResizeable(int resizeable) {
+        if (resizeable == 1) {
+            return this;
+        }
+
+        this.resizeable = resizeable < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAnyDensity(int anyDensity) {
+        if (anyDensity == 1) {
+            return this;
+        }
+
+        this.anyDensity = anyDensity < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl asSplit(String[] splitNames, String[] splitCodePaths,
+            int[] splitRevisionCodes, SparseArray<int[]> splitDependencies) {
+        this.splitNames = splitNames;
+        this.splitCodePaths = splitCodePaths;
+        this.splitRevisionCodes = splitRevisionCodes;
+        this.splitDependencies = splitDependencies;
+
+        int count = splitNames.length;
+        this.splitFlags = new int[count];
+        this.splitClassLoaderNames = new String[count];
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) {
+        this.splitFlags[splitIndex] = splitHasCode
+                ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE
+                : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) {
+        this.splitClassLoaderNames[splitIndex] = classLoaderName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequiredAccountType(@Nullable String requiredAccountType) {
+        this.requiredAccountType = TextUtils.nullIfEmpty(requiredAccountType);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayTarget(@Nullable String overlayTarget) {
+        this.overlayTarget = TextUtils.safeIntern(overlayTarget);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setVolumeUuid(@Nullable String volumeUuid) {
+        this.volumeUuid = TextUtils.safeIntern(volumeUuid);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setStaticSharedLibName(String staticSharedLibName) {
+        this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSharedUserId(String sharedUserId) {
+        this.sharedUserId = TextUtils.safeIntern(sharedUserId);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) {
+        nonLocalizedLabel = value == null ? null : value.toString().trim();
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public String getProcessName() {
+        return processName != null ? processName : packageName;
+    }
+
+    @Override
+    public String toString() {
+        return "Package{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + packageName + "}";
+    }
+
+    @Deprecated
+    @Override
+    public ApplicationInfo toAppInfoWithoutState() {
+        ApplicationInfo appInfo = toAppInfoWithoutStateWithoutFlags();
+        appInfo.flags = PackageInfoWithoutStateUtils.appInfoFlags(this);
+        appInfo.privateFlags = PackageInfoWithoutStateUtils.appInfoPrivateFlags(this);
+        appInfo.privateFlagsExt = PackageInfoWithoutStateUtils.appInfoPrivateFlagsExt(this);
+        return appInfo;
+    }
+
+    public ApplicationInfo toAppInfoWithoutStateWithoutFlags() {
+        ApplicationInfo appInfo = new ApplicationInfo();
+
+        // Lines that are commented below are state related and should not be assigned here.
+        // They are left in as placeholders, since there is no good backwards compatible way to
+        // separate these.
+        appInfo.appComponentFactory = appComponentFactory;
+        appInfo.backupAgentName = backupAgentName;
+        appInfo.banner = banner;
+        appInfo.category = category;
+        appInfo.classLoaderName = classLoaderName;
+        appInfo.className = className;
+        appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
+        appInfo.compileSdkVersion = compileSdkVersion;
+        appInfo.compileSdkVersionCodename = compileSdkVersionCodeName;
+//        appInfo.credentialProtectedDataDir
+        appInfo.crossProfile = isCrossProfile();
+//        appInfo.dataDir
+        appInfo.descriptionRes = descriptionRes;
+//        appInfo.deviceProtectedDataDir
+        appInfo.enabled = getBoolean(Booleans.ENABLED);
+//        appInfo.enabledSetting
+        appInfo.fullBackupContent = fullBackupContent;
+        appInfo.dataExtractionRulesRes = dataExtractionRules;
+        // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
+//        appInfo.mHiddenApiPolicy
+//        appInfo.hiddenUntilInstalled
+        appInfo.icon =
+                (ParsingPackageUtils.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
+        appInfo.iconRes = iconRes;
+        appInfo.roundIconRes = roundIconRes;
+        appInfo.installLocation = installLocation;
+        appInfo.labelRes = labelRes;
+        appInfo.largestWidthLimitDp = largestWidthLimitDp;
+        appInfo.logo = logo;
+        appInfo.manageSpaceActivityName = manageSpaceActivityName;
+        appInfo.maxAspectRatio = maxAspectRatio;
+        appInfo.metaData = metaData;
+        appInfo.minAspectRatio = minAspectRatio;
+        appInfo.minSdkVersion = minSdkVersion;
+        appInfo.name = className;
+//        appInfo.nativeLibraryDir
+//        appInfo.nativeLibraryRootDir
+//        appInfo.nativeLibraryRootRequiresIsa
+        appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
+        appInfo.nonLocalizedLabel = nonLocalizedLabel;
+        appInfo.packageName = packageName;
+        appInfo.permission = permission;
+//        appInfo.primaryCpuAbi
+        appInfo.processName = getProcessName();
+        appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
+//        appInfo.resourceDirs
+//        appInfo.secondaryCpuAbi
+//        appInfo.secondaryNativeLibraryDir
+//        appInfo.seInfo
+//        appInfo.seInfoUser
+//        appInfo.sharedLibraryFiles
+//        appInfo.sharedLibraryInfos
+//        appInfo.showUserIcon
+        appInfo.splitClassLoaderNames = splitClassLoaderNames;
+        appInfo.splitDependencies = splitDependencies;
+        appInfo.splitNames = splitNames;
+        appInfo.storageUuid = mStorageUuid;
+        appInfo.targetSandboxVersion = targetSandboxVersion;
+        appInfo.targetSdkVersion = targetSdkVersion;
+        appInfo.taskAffinity = taskAffinity;
+        appInfo.theme = theme;
+//        appInfo.uid
+        appInfo.uiOptions = uiOptions;
+        appInfo.volumeUuid = volumeUuid;
+        appInfo.zygotePreloadName = zygotePreloadName;
+        appInfo.setGwpAsanMode(gwpAsanMode);
+        appInfo.setMemtagMode(memtagMode);
+        appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
+        appInfo.setRequestRawExternalStorageAccess(requestRawExternalStorageAccess);
+        appInfo.setBaseCodePath(mBaseApkPath);
+        appInfo.setBaseResourcePath(mBaseApkPath);
+        appInfo.setCodePath(mPath);
+        appInfo.setResourcePath(mPath);
+        appInfo.setSplitCodePaths(splitCodePaths);
+        appInfo.setSplitResourcePaths(splitCodePaths);
+        appInfo.setVersionCode(mLongVersionCode);
+        appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
+        appInfo.setLocaleConfigRes(mLocaleConfigRes);
+
+        return appInfo;
+    }
+
+    /**
+     * Create a map from a process name to the custom application class for this process,
+     * which comes from <processes><process android:name="xxx">.
+     *
+     * The original information is stored in {@link #processes}, but it's stored in
+     * a form of: [process name] -[1:N]-> [package name] -[1:N]-> [class name].
+     * We scan it and collect the process names and their app class names, only for this package.
+     *
+     * The resulting map only contains processes with a custom application class set.
+     */
+    @Nullable
+    private ArrayMap<String, String> buildAppClassNamesByProcess() {
+        if (ArrayUtils.size(processes) == 0) {
+            return null;
+        }
+        final ArrayMap<String, String> ret = new ArrayMap<>(4);
+        for (String processName : processes.keySet()) {
+            final ParsedProcess process = processes.get(processName);
+            final ArrayMap<String, String> appClassesByPackage =
+                    process.getAppClassNamesByPackage();
+
+            for (int i = 0; i < appClassesByPackage.size(); i++) {
+                final String packageName = appClassesByPackage.keyAt(i);
+
+                if (this.packageName.equals(packageName)) {
+                    final String appClassName = appClassesByPackage.valueAt(i);
+                    if (!TextUtils.isEmpty(appClassName)) {
+                        ret.put(processName, appClassName);
+                    }
+                }
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
+        sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
+        sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
+        sForBoolean.parcel(this.supportsExtraLargeScreens, dest, flags);
+        sForBoolean.parcel(this.resizeable, dest, flags);
+        sForBoolean.parcel(this.anyDensity, dest, flags);
+        dest.writeInt(this.versionCode);
+        dest.writeInt(this.versionCodeMajor);
+        dest.writeInt(this.baseRevisionCode);
+        sForInternedString.parcel(this.versionName, dest, flags);
+        dest.writeInt(this.compileSdkVersion);
+        dest.writeString(this.compileSdkVersionCodeName);
+        sForInternedString.parcel(this.packageName, dest, flags);
+        dest.writeString(this.mBaseApkPath);
+        dest.writeString(this.restrictedAccountType);
+        dest.writeString(this.requiredAccountType);
+        sForInternedString.parcel(this.overlayTarget, dest, flags);
+        dest.writeString(this.overlayTargetOverlayableName);
+        dest.writeString(this.overlayCategory);
+        dest.writeInt(this.overlayPriority);
+        sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
+        sForInternedString.parcel(this.sdkLibName, dest, flags);
+        dest.writeInt(this.sdkLibVersionMajor);
+        sForInternedString.parcel(this.staticSharedLibName, dest, flags);
+        dest.writeLong(this.staticSharedLibVersion);
+        sForInternedStringList.parcel(this.libraryNames, dest, flags);
+        sForInternedStringList.parcel(this.usesLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
+
+        sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
+        dest.writeLongArray(this.usesStaticLibrariesVersions);
+        if (this.usesStaticLibrariesCertDigests == null) {
+            dest.writeInt(-1);
+        } else {
+            dest.writeInt(this.usesStaticLibrariesCertDigests.length);
+            for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) {
+                dest.writeStringArray(this.usesStaticLibrariesCertDigests[index]);
+            }
+        }
+
+        sForInternedStringList.parcel(this.usesSdkLibraries, dest, flags);
+        dest.writeLongArray(this.usesSdkLibrariesVersionsMajor);
+        if (this.usesSdkLibrariesCertDigests == null) {
+            dest.writeInt(-1);
+        } else {
+            dest.writeInt(this.usesSdkLibrariesCertDigests.length);
+            for (int index = 0; index < this.usesSdkLibrariesCertDigests.length; index++) {
+                dest.writeStringArray(this.usesSdkLibrariesCertDigests[index]);
+            }
+        }
+
+        sForInternedString.parcel(this.sharedUserId, dest, flags);
+        dest.writeInt(this.sharedUserLabel);
+        dest.writeTypedList(this.configPreferences);
+        dest.writeTypedList(this.reqFeatures);
+        dest.writeTypedList(this.featureGroups);
+        dest.writeByteArray(this.restrictUpdateHash);
+        dest.writeStringList(this.originalPackages);
+        sForInternedStringList.parcel(this.adoptPermissions, dest, flags);
+        sForInternedStringList.parcel(this.requestedPermissions, dest, flags);
+        dest.writeTypedList(this.usesPermissions);
+        sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
+        sForStringSet.parcel(this.upgradeKeySets, dest, flags);
+        ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
+        sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
+        dest.writeTypedList(this.activities);
+        dest.writeTypedList(this.apexSystemServices);
+        dest.writeTypedList(this.receivers);
+        dest.writeTypedList(this.services);
+        dest.writeTypedList(this.providers);
+        dest.writeTypedList(this.attributions);
+        dest.writeTypedList(this.permissions);
+        dest.writeTypedList(this.permissionGroups);
+        dest.writeTypedList(this.instrumentations);
+        sForIntentInfoPairs.parcel(this.preferredActivityFilters, dest, flags);
+        dest.writeMap(this.processes);
+        dest.writeBundle(this.metaData);
+        sForInternedString.parcel(this.volumeUuid, dest, flags);
+        dest.writeParcelable(this.signingDetails, flags);
+        dest.writeString(this.mPath);
+        dest.writeTypedList(this.queriesIntents, flags);
+        sForInternedStringList.parcel(this.queriesPackages, dest, flags);
+        sForInternedStringSet.parcel(this.queriesProviders, dest, flags);
+        dest.writeString(this.appComponentFactory);
+        dest.writeString(this.backupAgentName);
+        dest.writeInt(this.banner);
+        dest.writeInt(this.category);
+        dest.writeString(this.classLoaderName);
+        dest.writeString(this.className);
+        dest.writeInt(this.compatibleWidthLimitDp);
+        dest.writeInt(this.descriptionRes);
+        dest.writeInt(this.fullBackupContent);
+        dest.writeInt(this.dataExtractionRules);
+        dest.writeInt(this.iconRes);
+        dest.writeInt(this.installLocation);
+        dest.writeInt(this.labelRes);
+        dest.writeInt(this.largestWidthLimitDp);
+        dest.writeInt(this.logo);
+        dest.writeString(this.manageSpaceActivityName);
+        dest.writeFloat(this.maxAspectRatio);
+        dest.writeFloat(this.minAspectRatio);
+        dest.writeInt(this.minSdkVersion);
+        dest.writeInt(this.networkSecurityConfigRes);
+        dest.writeCharSequence(this.nonLocalizedLabel);
+        dest.writeString(this.permission);
+        dest.writeString(this.processName);
+        dest.writeInt(this.requiresSmallestWidthDp);
+        dest.writeInt(this.roundIconRes);
+        dest.writeInt(this.targetSandboxVersion);
+        dest.writeInt(this.targetSdkVersion);
+        dest.writeString(this.taskAffinity);
+        dest.writeInt(this.theme);
+        dest.writeInt(this.uiOptions);
+        dest.writeString(this.zygotePreloadName);
+        dest.writeStringArray(this.splitClassLoaderNames);
+        dest.writeStringArray(this.splitCodePaths);
+        dest.writeSparseArray(this.splitDependencies);
+        dest.writeIntArray(this.splitFlags);
+        dest.writeStringArray(this.splitNames);
+        dest.writeIntArray(this.splitRevisionCodes);
+        sForBoolean.parcel(this.resizeableActivity, dest, flags);
+        dest.writeInt(this.autoRevokePermissions);
+        dest.writeArraySet(this.mimeGroups);
+        dest.writeInt(this.gwpAsanMode);
+        dest.writeSparseIntArray(this.minExtensionVersions);
+        dest.writeLong(this.mBooleans);
+        dest.writeMap(this.mProperties);
+        dest.writeInt(this.memtagMode);
+        dest.writeInt(this.nativeHeapZeroInitialized);
+        sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
+        dest.writeInt(this.mLocaleConfigRes);
+    }
+
+    public ParsingPackageImpl(Parcel in) {
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        this.supportsSmallScreens = sForBoolean.unparcel(in);
+        this.supportsNormalScreens = sForBoolean.unparcel(in);
+        this.supportsLargeScreens = sForBoolean.unparcel(in);
+        this.supportsExtraLargeScreens = sForBoolean.unparcel(in);
+        this.resizeable = sForBoolean.unparcel(in);
+        this.anyDensity = sForBoolean.unparcel(in);
+        this.versionCode = in.readInt();
+        this.versionCodeMajor = in.readInt();
+        this.baseRevisionCode = in.readInt();
+        this.versionName = sForInternedString.unparcel(in);
+        this.compileSdkVersion = in.readInt();
+        this.compileSdkVersionCodeName = in.readString();
+        this.packageName = sForInternedString.unparcel(in);
+        this.mBaseApkPath = in.readString();
+        this.restrictedAccountType = in.readString();
+        this.requiredAccountType = in.readString();
+        this.overlayTarget = sForInternedString.unparcel(in);
+        this.overlayTargetOverlayableName = in.readString();
+        this.overlayCategory = in.readString();
+        this.overlayPriority = in.readInt();
+        this.overlayables = sForInternedStringValueMap.unparcel(in);
+        this.sdkLibName = sForInternedString.unparcel(in);
+        this.sdkLibVersionMajor = in.readInt();
+        this.staticSharedLibName = sForInternedString.unparcel(in);
+        this.staticSharedLibVersion = in.readLong();
+        this.libraryNames = sForInternedStringList.unparcel(in);
+        this.usesLibraries = sForInternedStringList.unparcel(in);
+        this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+        this.usesNativeLibraries = sForInternedStringList.unparcel(in);
+        this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
+
+        this.usesStaticLibraries = sForInternedStringList.unparcel(in);
+        this.usesStaticLibrariesVersions = in.createLongArray();
+        {
+            int digestsSize = in.readInt();
+            if (digestsSize >= 0) {
+                this.usesStaticLibrariesCertDigests = new String[digestsSize][];
+                for (int index = 0; index < digestsSize; index++) {
+                    this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(
+                            in);
+                }
+            }
+        }
+
+        this.usesSdkLibraries = sForInternedStringList.unparcel(in);
+        this.usesSdkLibrariesVersionsMajor = in.createLongArray();
+        {
+            int digestsSize = in.readInt();
+            if (digestsSize >= 0) {
+                this.usesSdkLibrariesCertDigests = new String[digestsSize][];
+                for (int index = 0; index < digestsSize; index++) {
+                    this.usesSdkLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
+                }
+            }
+        }
+
+        this.sharedUserId = sForInternedString.unparcel(in);
+        this.sharedUserLabel = in.readInt();
+        this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);
+        this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);
+        this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR);
+        this.restrictUpdateHash = in.createByteArray();
+        this.originalPackages = in.createStringArrayList();
+        this.adoptPermissions = sForInternedStringList.unparcel(in);
+        this.requestedPermissions = sForInternedStringList.unparcel(in);
+        this.usesPermissions = ParsingUtils.createTypedInterfaceList(in,
+                ParsedUsesPermissionImpl.CREATOR);
+        this.implicitPermissions = sForInternedStringList.unparcel(in);
+        this.upgradeKeySets = sForStringSet.unparcel(in);
+        this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in);
+        this.protectedBroadcasts = sForInternedStringList.unparcel(in);
+
+        this.activities = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
+        this.apexSystemServices = ParsingUtils.createTypedInterfaceList(in,
+                ParsedApexSystemServiceImpl.CREATOR);
+        this.receivers = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
+        this.services = ParsingUtils.createTypedInterfaceList(in, ParsedServiceImpl.CREATOR);
+        this.providers = ParsingUtils.createTypedInterfaceList(in, ParsedProviderImpl.CREATOR);
+        this.attributions = ParsingUtils.createTypedInterfaceList(in,
+                ParsedAttributionImpl.CREATOR);
+        this.permissions = ParsingUtils.createTypedInterfaceList(in, ParsedPermissionImpl.CREATOR);
+        this.permissionGroups = ParsingUtils.createTypedInterfaceList(in,
+                ParsedPermissionGroupImpl.CREATOR);
+        this.instrumentations = ParsingUtils.createTypedInterfaceList(in,
+                ParsedInstrumentationImpl.CREATOR);
+        this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
+        this.processes = in.readHashMap(ParsedProcess.class.getClassLoader());
+        this.metaData = in.readBundle(boot);
+        this.volumeUuid = sForInternedString.unparcel(in);
+        this.signingDetails = in.readParcelable(boot);
+        this.mPath = in.readString();
+        this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
+        this.queriesPackages = sForInternedStringList.unparcel(in);
+        this.queriesProviders = sForInternedStringSet.unparcel(in);
+        this.appComponentFactory = in.readString();
+        this.backupAgentName = in.readString();
+        this.banner = in.readInt();
+        this.category = in.readInt();
+        this.classLoaderName = in.readString();
+        this.className = in.readString();
+        this.compatibleWidthLimitDp = in.readInt();
+        this.descriptionRes = in.readInt();
+        this.fullBackupContent = in.readInt();
+        this.dataExtractionRules = in.readInt();
+        this.iconRes = in.readInt();
+        this.installLocation = in.readInt();
+        this.labelRes = in.readInt();
+        this.largestWidthLimitDp = in.readInt();
+        this.logo = in.readInt();
+        this.manageSpaceActivityName = in.readString();
+        this.maxAspectRatio = in.readFloat();
+        this.minAspectRatio = in.readFloat();
+        this.minSdkVersion = in.readInt();
+        this.networkSecurityConfigRes = in.readInt();
+        this.nonLocalizedLabel = in.readCharSequence();
+        this.permission = in.readString();
+        this.processName = in.readString();
+        this.requiresSmallestWidthDp = in.readInt();
+        this.roundIconRes = in.readInt();
+        this.targetSandboxVersion = in.readInt();
+        this.targetSdkVersion = in.readInt();
+        this.taskAffinity = in.readString();
+        this.theme = in.readInt();
+        this.uiOptions = in.readInt();
+        this.zygotePreloadName = in.readString();
+        this.splitClassLoaderNames = in.createStringArray();
+        this.splitCodePaths = in.createStringArray();
+        this.splitDependencies = in.readSparseArray(boot);
+        this.splitFlags = in.createIntArray();
+        this.splitNames = in.createStringArray();
+        this.splitRevisionCodes = in.createIntArray();
+        this.resizeableActivity = sForBoolean.unparcel(in);
+
+        this.autoRevokePermissions = in.readInt();
+        this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
+        this.gwpAsanMode = in.readInt();
+        this.minExtensionVersions = in.readSparseIntArray();
+        this.mBooleans = in.readLong();
+        this.mProperties = in.readHashMap(boot);
+        this.memtagMode = in.readInt();
+        this.nativeHeapZeroInitialized = in.readInt();
+        this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
+        this.mLocaleConfigRes = in.readInt();
+        assignDerivedFields();
+    }
+
+    public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
+            new Parcelable.Creator<ParsingPackageImpl>() {
+                @Override
+                public ParsingPackageImpl createFromParcel(Parcel source) {
+                    return new ParsingPackageImpl(source);
+                }
+
+                @Override
+                public ParsingPackageImpl[] newArray(int size) {
+                    return new ParsingPackageImpl[size];
+                }
+            };
+
+    @Override
+    public int getVersionCode() {
+        return versionCode;
+    }
+
+    @Override
+    public int getVersionCodeMajor() {
+        return versionCodeMajor;
+    }
+
+    @Override
+    public long getLongVersionCode() {
+        return mLongVersionCode;
+    }
+
+    @Override
+    public int getBaseRevisionCode() {
+        return baseRevisionCode;
+    }
+
+    @Nullable
+    @Override
+    public String getVersionName() {
+        return versionName;
+    }
+
+    @Override
+    public int getCompileSdkVersion() {
+        return compileSdkVersion;
+    }
+
+    @Nullable
+    @Override
+    public String getCompileSdkVersionCodeName() {
+        return compileSdkVersionCodeName;
+    }
+
+    @NonNull
+    @Override
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @NonNull
+    @Override
+    public String getBaseApkPath() {
+        return mBaseApkPath;
+    }
+
+    @Override
+    public boolean isRequiredForAllUsers() {
+        return getBoolean(Booleans.REQUIRED_FOR_ALL_USERS);
+    }
+
+    @Nullable
+    @Override
+    public String getRestrictedAccountType() {
+        return restrictedAccountType;
+    }
+
+    @Nullable
+    @Override
+    public String getRequiredAccountType() {
+        return requiredAccountType;
+    }
+
+    @Nullable
+    @Override
+    public String getOverlayTarget() {
+        return overlayTarget;
+    }
+
+    @Nullable
+    @Override
+    public String getOverlayTargetOverlayableName() {
+        return overlayTargetOverlayableName;
+    }
+
+    @Nullable
+    @Override
+    public String getOverlayCategory() {
+        return overlayCategory;
+    }
+
+    @Override
+    public int getOverlayPriority() {
+        return overlayPriority;
+    }
+
+    @Override
+    public boolean isOverlayIsStatic() {
+        return getBoolean(Booleans.OVERLAY_IS_STATIC);
+    }
+
+    @NonNull
+    @Override
+    public Map<String,String> getOverlayables() {
+        return overlayables;
+    }
+
+    @Nullable
+    @Override
+    public String getSdkLibName() {
+        return sdkLibName;
+    }
+
+    @Override
+    public int getSdkLibVersionMajor() {
+        return sdkLibVersionMajor;
+    }
+
+    @Nullable
+    @Override
+    public String getStaticSharedLibName() {
+        return staticSharedLibName;
+    }
+
+    @Override
+    public long getStaticSharedLibVersion() {
+        return staticSharedLibVersion;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getLibraryNames() {
+        return libraryNames;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesLibraries() {
+        return usesLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesOptionalLibraries() {
+        return usesOptionalLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesNativeLibraries() {
+        return usesNativeLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesOptionalNativeLibraries() {
+        return usesOptionalNativeLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesStaticLibraries() {
+        return usesStaticLibraries;
+    }
+
+    @Nullable
+    @Override
+    public long[] getUsesStaticLibrariesVersions() {
+        return usesStaticLibrariesVersions;
+    }
+
+    @Nullable
+    @Override
+    public String[][] getUsesStaticLibrariesCertDigests() {
+        return usesStaticLibrariesCertDigests;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesSdkLibraries() { return usesSdkLibraries; }
+
+    @Nullable
+    @Override
+    public long[] getUsesSdkLibrariesVersionsMajor() { return usesSdkLibrariesVersionsMajor; }
+
+    @Nullable
+    @Override
+    public String[][] getUsesSdkLibrariesCertDigests() { return usesSdkLibrariesCertDigests; }
+
+    @Nullable
+    @Override
+    public String getSharedUserId() {
+        return sharedUserId;
+    }
+
+    @Override
+    public int getSharedUserLabel() {
+        return sharedUserLabel;
+    }
+
+    @NonNull
+    @Override
+    public List<ConfigurationInfo> getConfigPreferences() {
+        return configPreferences;
+    }
+
+    @NonNull
+    @Override
+    public List<FeatureInfo> getRequestedFeatures() {
+        return reqFeatures;
+    }
+
+    @NonNull
+    @Override
+    public List<FeatureGroupInfo> getFeatureGroups() {
+        return featureGroups;
+    }
+
+    @Nullable
+    @Override
+    public byte[] getRestrictUpdateHash() {
+        return restrictUpdateHash;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getOriginalPackages() {
+        return originalPackages;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getAdoptPermissions() {
+        return adoptPermissions;
+    }
+
+    /**
+     * @deprecated consider migrating to {@link #getUsesPermissions} which has
+     *             more parsed details, such as flags
+     */
+    @NonNull
+    @Override
+    @Deprecated
+    public List<String> getRequestedPermissions() {
+        return requestedPermissions;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedUsesPermission> getUsesPermissions() {
+        return usesPermissions;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getImplicitPermissions() {
+        return implicitPermissions;
+    }
+
+    @NonNull
+    @Override
+    public Map<String, Property> getProperties() {
+        return mProperties;
+    }
+
+    @NonNull
+    @Override
+    public Set<String> getUpgradeKeySets() {
+        return upgradeKeySets;
+    }
+
+    @NonNull
+    @Override
+    public Map<String,ArraySet<PublicKey>> getKeySetMapping() {
+        return keySetMapping;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getProtectedBroadcasts() {
+        return protectedBroadcasts;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedActivity> getActivities() {
+        return activities;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedApexSystemService> getApexSystemServices() {
+        return apexSystemServices;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedActivity> getReceivers() {
+        return receivers;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedService> getServices() {
+        return services;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedProvider> getProviders() {
+        return providers;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedAttribution> getAttributions() {
+        return attributions;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedPermission> getPermissions() {
+        return permissions;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedPermissionGroup> getPermissionGroups() {
+        return permissionGroups;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedInstrumentation> getInstrumentations() {
+        return instrumentations;
+    }
+
+    @NonNull
+    @Override
+    public List<Pair<String,ParsedIntentInfo>> getPreferredActivityFilters() {
+        return preferredActivityFilters;
+    }
+
+    @NonNull
+    @Override
+    public Map<String,ParsedProcess> getProcesses() {
+        return processes;
+    }
+
+    @Nullable
+    @Override
+    public Bundle getMetaData() {
+        return metaData;
+    }
+
+    private void addMimeGroupsFromComponent(ParsedComponent component) {
+        for (int i = component.getIntents().size() - 1; i >= 0; i--) {
+            IntentFilter filter = component.getIntents().get(i).getIntentFilter();
+            for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
+                mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
+            }
+        }
+    }
+
+    @Override
+    @Nullable
+    public Set<String> getMimeGroups() {
+        return mimeGroups;
+    }
+
+    @Nullable
+    @Override
+    public String getVolumeUuid() {
+        return volumeUuid;
+    }
+
+    @Nullable
+    @Override
+    public SigningDetails getSigningDetails() {
+        return signingDetails;
+    }
+
+    @NonNull
+    @Override
+    public String getPath() {
+        return mPath;
+    }
+
+    @Override
+    public boolean isUse32BitAbi() {
+        return getBoolean(Booleans.USE_32_BIT_ABI);
+    }
+
+    @Override
+    public boolean isVisibleToInstantApps() {
+        return getBoolean(Booleans.VISIBLE_TO_INSTANT_APPS);
+    }
+
+    @Override
+    public boolean isForceQueryable() {
+        return getBoolean(Booleans.FORCE_QUERYABLE);
+    }
+
+    @NonNull
+    @Override
+    public List<Intent> getQueriesIntents() {
+        return queriesIntents;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getQueriesPackages() {
+        return queriesPackages;
+    }
+
+    @NonNull
+    @Override
+    public Set<String> getQueriesProviders() {
+        return queriesProviders;
+    }
+
+    @Nullable
+    @Override
+    public String[] getSplitClassLoaderNames() {
+        return splitClassLoaderNames;
+    }
+
+    @Nullable
+    @Override
+    public String[] getSplitCodePaths() {
+        return splitCodePaths;
+    }
+
+    @Nullable
+    @Override
+    public SparseArray<int[]> getSplitDependencies() {
+        return splitDependencies;
+    }
+
+    @Nullable
+    @Override
+    public int[] getSplitFlags() {
+        return splitFlags;
+    }
+
+    @Nullable
+    @Override
+    public String[] getSplitNames() {
+        return splitNames;
+    }
+
+    @Nullable
+    @Override
+    public int[] getSplitRevisionCodes() {
+        return splitRevisionCodes;
+    }
+
+    @Nullable
+    @Override
+    public String getAppComponentFactory() {
+        return appComponentFactory;
+    }
+
+    @Nullable
+    @Override
+    public String getBackupAgentName() {
+        return backupAgentName;
+    }
+
+    @Override
+    public int getBanner() {
+        return banner;
+    }
+
+    @Override
+    public int getCategory() {
+        return category;
+    }
+
+    @Nullable
+    @Override
+    public String getClassLoaderName() {
+        return classLoaderName;
+    }
+
+    @Nullable
+    @Override
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    public int getCompatibleWidthLimitDp() {
+        return compatibleWidthLimitDp;
+    }
+
+    @Override
+    public int getDescriptionRes() {
+        return descriptionRes;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return getBoolean(Booleans.ENABLED);
+    }
+
+    @Override
+    public boolean isCrossProfile() {
+        return getBoolean(Booleans.CROSS_PROFILE);
+    }
+
+    @Override
+    public int getFullBackupContent() {
+        return fullBackupContent;
+    }
+
+    @Override
+    public int getDataExtractionRules() {
+        return dataExtractionRules;
+    }
+
+    @Override
+    public int getIconRes() {
+        return iconRes;
+    }
+
+    @Override
+    public int getInstallLocation() {
+        return installLocation;
+    }
+
+    @Override
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @Override
+    public int getLargestWidthLimitDp() {
+        return largestWidthLimitDp;
+    }
+
+    @Override
+    public int getLogo() {
+        return logo;
+    }
+
+    @Nullable
+    @Override
+    public String getManageSpaceActivityName() {
+        return manageSpaceActivityName;
+    }
+
+    @Override
+    public float getMaxAspectRatio() {
+        return maxAspectRatio;
+    }
+
+    @Override
+    public float getMinAspectRatio() {
+        return minAspectRatio;
+    }
+
+    @Nullable
+    @Override
+    public SparseIntArray getMinExtensionVersions() {
+        return minExtensionVersions;
+    }
+
+    @Override
+    public int getMinSdkVersion() {
+        return minSdkVersion;
+    }
+
+    @Override
+    public int getNetworkSecurityConfigRes() {
+        return networkSecurityConfigRes;
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    @Nullable
+    @Override
+    public String getPermission() {
+        return permission;
+    }
+
+    @Override
+    public int getRequiresSmallestWidthDp() {
+        return requiresSmallestWidthDp;
+    }
+
+    @Override
+    public int getRoundIconRes() {
+        return roundIconRes;
+    }
+
+    @Override
+    public int getTargetSdkVersion() {
+        return targetSdkVersion;
+    }
+
+    @Override
+    public int getTargetSandboxVersion() {
+        return targetSandboxVersion;
+    }
+
+    @Nullable
+    @Override
+    public String getTaskAffinity() {
+        return taskAffinity;
+    }
+
+    @Override
+    public int getTheme() {
+        return theme;
+    }
+
+    @Override
+    public int getUiOptions() {
+        return uiOptions;
+    }
+
+    @Nullable
+    @Override
+    public String getZygotePreloadName() {
+        return zygotePreloadName;
+    }
+
+    @Override
+    public boolean isExternalStorage() {
+        return getBoolean(Booleans.EXTERNAL_STORAGE);
+    }
+
+    @Override
+    public boolean isBaseHardwareAccelerated() {
+        return getBoolean(Booleans.BASE_HARDWARE_ACCELERATED);
+    }
+
+    @Override
+    public boolean isAllowBackup() {
+        return getBoolean(Booleans.ALLOW_BACKUP);
+    }
+
+    @Override
+    public boolean isKillAfterRestore() {
+        return getBoolean(Booleans.KILL_AFTER_RESTORE);
+    }
+
+    @Override
+    public boolean isRestoreAnyVersion() {
+        return getBoolean(Booleans.RESTORE_ANY_VERSION);
+    }
+
+    @Override
+    public boolean isFullBackupOnly() {
+        return getBoolean(Booleans.FULL_BACKUP_ONLY);
+    }
+
+    @Override
+    public boolean isPersistent() {
+        return getBoolean(Booleans.PERSISTENT);
+    }
+
+    @Override
+    public boolean isDebuggable() {
+        return getBoolean(Booleans.DEBUGGABLE);
+    }
+
+    @Override
+    public boolean isVmSafeMode() {
+        return getBoolean(Booleans.VM_SAFE_MODE);
+    }
+
+    @Override
+    public boolean isHasCode() {
+        return getBoolean(Booleans.HAS_CODE);
+    }
+
+    @Override
+    public boolean isAllowTaskReparenting() {
+        return getBoolean(Booleans.ALLOW_TASK_REPARENTING);
+    }
+
+    @Override
+    public boolean isAllowClearUserData() {
+        return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA);
+    }
+
+    @Override
+    public boolean isLargeHeap() {
+        return getBoolean(Booleans.LARGE_HEAP);
+    }
+
+    @Override
+    public boolean isUsesCleartextTraffic() {
+        return getBoolean(Booleans.USES_CLEARTEXT_TRAFFIC);
+    }
+
+    @Override
+    public boolean isSupportsRtl() {
+        return getBoolean(Booleans.SUPPORTS_RTL);
+    }
+
+    @Override
+    public boolean isTestOnly() {
+        return getBoolean(Booleans.TEST_ONLY);
+    }
+
+    @Override
+    public boolean isMultiArch() {
+        return getBoolean(Booleans.MULTI_ARCH);
+    }
+
+    @Override
+    public boolean isExtractNativeLibs() {
+        return getBoolean(Booleans.EXTRACT_NATIVE_LIBS);
+    }
+
+    @Override
+    public boolean isGame() {
+        return getBoolean(Booleans.GAME);
+    }
+
+    /**
+     * @see ParsingPackageRead#getResizeableActivity()
+     */
+    @Nullable
+    @Override
+    public Boolean getResizeableActivity() {
+        return resizeableActivity;
+    }
+
+    @Override
+    public boolean isStaticSharedLibrary() {
+        return getBoolean(Booleans.STATIC_SHARED_LIBRARY);
+    }
+
+    @Override
+    public boolean isSdkLibrary() {
+        return getBoolean(Booleans.SDK_LIBRARY);
+    }
+
+    @Override
+    public boolean isOverlay() {
+        return getBoolean(Booleans.OVERLAY);
+    }
+
+    @Override
+    public boolean isIsolatedSplitLoading() {
+        return getBoolean(Booleans.ISOLATED_SPLIT_LOADING);
+    }
+
+    @Override
+    public boolean isHasDomainUrls() {
+        return getBoolean(Booleans.HAS_DOMAIN_URLS);
+    }
+
+    @Override
+    public boolean isProfileableByShell() {
+        return isProfileable() && getBoolean(Booleans.PROFILEABLE_BY_SHELL);
+    }
+
+    @Override
+    public boolean isProfileable() {
+        return !getBoolean(Booleans.DISALLOW_PROFILING);
+    }
+
+    @Override
+    public boolean isBackupInForeground() {
+        return getBoolean(Booleans.BACKUP_IN_FOREGROUND);
+    }
+
+    @Override
+    public boolean isUseEmbeddedDex() {
+        return getBoolean(Booleans.USE_EMBEDDED_DEX);
+    }
+
+    @Override
+    public boolean isDefaultToDeviceProtectedStorage() {
+        return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE);
+    }
+
+    @Override
+    public boolean isDirectBootAware() {
+        return getBoolean(Booleans.DIRECT_BOOT_AWARE);
+    }
+
+    @ApplicationInfo.GwpAsanMode
+    @Override
+    public int getGwpAsanMode() {
+        return gwpAsanMode;
+    }
+
+    @ApplicationInfo.MemtagMode
+    @Override
+    public int getMemtagMode() {
+        return memtagMode;
+    }
+
+    @ApplicationInfo.NativeHeapZeroInitialized
+    @Override
+    public int getNativeHeapZeroInitialized() {
+        return nativeHeapZeroInitialized;
+    }
+
+    @Override
+    public int getLocaleConfigRes() {
+        return mLocaleConfigRes;
+    }
+
+    @Nullable
+    @Override
+    public Boolean hasRequestRawExternalStorageAccess() {
+        return requestRawExternalStorageAccess;
+    }
+
+    @Override
+    public boolean isPartiallyDirectBootAware() {
+        return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE);
+    }
+
+    @Override
+    public boolean isResizeableActivityViaSdkVersion() {
+        return getBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION);
+    }
+
+    @Override
+    public boolean isAllowClearUserDataOnFailedRestore() {
+        return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE);
+    }
+
+    @Override
+    public boolean isAllowAudioPlaybackCapture() {
+        return getBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE);
+    }
+
+    @Override
+    public boolean isRequestLegacyExternalStorage() {
+        return getBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE);
+    }
+
+    @Override
+    public boolean isUsesNonSdkApi() {
+        return getBoolean(Booleans.USES_NON_SDK_API);
+    }
+
+    @Override
+    public boolean isHasFragileUserData() {
+        return getBoolean(Booleans.HAS_FRAGILE_USER_DATA);
+    }
+
+    @Override
+    public boolean isCantSaveState() {
+        return getBoolean(Booleans.CANT_SAVE_STATE);
+    }
+
+    @Override
+    public boolean isAllowNativeHeapPointerTagging() {
+        return getBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING);
+    }
+
+    @Override
+    public int getAutoRevokePermissions() {
+        return autoRevokePermissions;
+    }
+
+    @Override
+    public boolean hasPreserveLegacyExternalStorage() {
+        return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE);
+    }
+
+    @Override
+    public boolean hasRequestForegroundServiceExemption() {
+        return getBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION);
+    }
+
+    @Override
+    public boolean areAttributionsUserVisible() {
+        return getBoolean(Booleans.ATTRIBUTIONS_ARE_USER_VISIBLE);
+    }
+
+    @Override
+    public boolean isResetEnabledSettingsOnAppDataCleared() {
+        return getBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED);
+    }
+
+    @Override
+    public boolean shouldInheritKeyStoreKeys() {
+        return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
+    }
+
+    @Override
+    public ParsingPackageImpl setBaseRevisionCode(int value) {
+        baseRevisionCode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCompileSdkVersion(int value) {
+        compileSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequiredForAllUsers(boolean value) {
+        return setBoolean(Booleans.REQUIRED_FOR_ALL_USERS, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayPriority(int value) {
+        overlayPriority = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayIsStatic(boolean value) {
+        return setBoolean(Booleans.OVERLAY_IS_STATIC, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setStaticSharedLibVersion(long value) {
+        staticSharedLibVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSharedUserLabel(int value) {
+        sharedUserLabel = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRestrictUpdateHash(@Nullable byte... value) {
+        restrictUpdateHash = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUpgradeKeySets(@NonNull Set<String> value) {
+        upgradeKeySets = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setProcesses(@NonNull Map<String,ParsedProcess> value) {
+        processes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMetaData(@Nullable Bundle value) {
+        metaData = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
+        signingDetails = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUse32BitAbi(boolean value) {
+        return setBoolean(Booleans.USE_32_BIT_ABI, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setVisibleToInstantApps(boolean value) {
+        return setBoolean(Booleans.VISIBLE_TO_INSTANT_APPS, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setForceQueryable(boolean value) {
+        return setBoolean(Booleans.FORCE_QUERYABLE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setBanner(int value) {
+        banner = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCategory(int value) {
+        category = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCompatibleWidthLimitDp(int value) {
+        compatibleWidthLimitDp = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setDescriptionRes(int value) {
+        descriptionRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setEnabled(boolean value) {
+        return setBoolean(Booleans.ENABLED, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setCrossProfile(boolean value) {
+        return setBoolean(Booleans.CROSS_PROFILE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setFullBackupContent(int value) {
+        fullBackupContent = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setDataExtractionRules(int value) {
+        dataExtractionRules = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setIconRes(int value) {
+        iconRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setInstallLocation(int value) {
+        installLocation = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setInheritKeyStoreKeys(boolean value) {
+        return setBoolean(Booleans.INHERIT_KEYSTORE_KEYS, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setLabelRes(int value) {
+        labelRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLargestWidthLimitDp(int value) {
+        largestWidthLimitDp = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLogo(int value) {
+        logo = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMaxAspectRatio(float value) {
+        maxAspectRatio = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMinAspectRatio(float value) {
+        minAspectRatio = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) {
+        minExtensionVersions = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMinSdkVersion(int value) {
+        minSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setNetworkSecurityConfigRes(int value) {
+        networkSecurityConfigRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequiresSmallestWidthDp(int value) {
+        requiresSmallestWidthDp = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRoundIconRes(int value) {
+        roundIconRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTargetSandboxVersion(int value) {
+        targetSandboxVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTargetSdkVersion(int value) {
+        targetSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTheme(int value) {
+        theme = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequestForegroundServiceExemption(boolean value) {
+        return setBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setUiOptions(int value) {
+        uiOptions = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setExternalStorage(boolean value) {
+        return setBoolean(Booleans.EXTERNAL_STORAGE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) {
+        return setBoolean(Booleans.BASE_HARDWARE_ACCELERATED, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowBackup(boolean value) {
+        return setBoolean(Booleans.ALLOW_BACKUP, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setKillAfterRestore(boolean value) {
+        return setBoolean(Booleans.KILL_AFTER_RESTORE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setRestoreAnyVersion(boolean value) {
+        return setBoolean(Booleans.RESTORE_ANY_VERSION, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setFullBackupOnly(boolean value) {
+        return setBoolean(Booleans.FULL_BACKUP_ONLY, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setPersistent(boolean value) {
+        return setBoolean(Booleans.PERSISTENT, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setDebuggable(boolean value) {
+        return setBoolean(Booleans.DEBUGGABLE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setVmSafeMode(boolean value) {
+        return setBoolean(Booleans.VM_SAFE_MODE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setHasCode(boolean value) {
+        return setBoolean(Booleans.HAS_CODE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowTaskReparenting(boolean value) {
+        return setBoolean(Booleans.ALLOW_TASK_REPARENTING, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowClearUserData(boolean value) {
+        return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setLargeHeap(boolean value) {
+        return setBoolean(Booleans.LARGE_HEAP, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setUsesCleartextTraffic(boolean value) {
+        return setBoolean(Booleans.USES_CLEARTEXT_TRAFFIC, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsRtl(boolean value) {
+        return setBoolean(Booleans.SUPPORTS_RTL, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setTestOnly(boolean value) {
+        return setBoolean(Booleans.TEST_ONLY, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setMultiArch(boolean value) {
+        return setBoolean(Booleans.MULTI_ARCH, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setExtractNativeLibs(boolean value) {
+        return setBoolean(Booleans.EXTRACT_NATIVE_LIBS, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setGame(boolean value) {
+        return setBoolean(Booleans.GAME, value);
+    }
+
+    /**
+     * @see ParsingPackageRead#getResizeableActivity()
+     */
+    @Override
+    public ParsingPackageImpl setResizeableActivity(@Nullable Boolean value) {
+        resizeableActivity = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSdkLibName(String sdkLibName) {
+        this.sdkLibName = TextUtils.safeIntern(sdkLibName);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSdkLibVersionMajor(int sdkLibVersionMajor) {
+        this.sdkLibVersionMajor = sdkLibVersionMajor;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSdkLibrary(boolean value) {
+        return setBoolean(Booleans.SDK_LIBRARY, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
+        return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlay(boolean value) {
+        return setBoolean(Booleans.OVERLAY, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setIsolatedSplitLoading(boolean value) {
+        return setBoolean(Booleans.ISOLATED_SPLIT_LOADING, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setHasDomainUrls(boolean value) {
+        return setBoolean(Booleans.HAS_DOMAIN_URLS, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setProfileableByShell(boolean value) {
+        return setBoolean(Booleans.PROFILEABLE_BY_SHELL, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setProfileable(boolean value) {
+        return setBoolean(Booleans.DISALLOW_PROFILING, !value);
+    }
+
+    @Override
+    public ParsingPackageImpl setBackupInForeground(boolean value) {
+        return setBoolean(Booleans.BACKUP_IN_FOREGROUND, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setUseEmbeddedDex(boolean value) {
+        return setBoolean(Booleans.USE_EMBEDDED_DEX, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) {
+        return setBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setDirectBootAware(boolean value) {
+        return setBoolean(Booleans.DIRECT_BOOT_AWARE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
+        gwpAsanMode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
+        memtagMode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setNativeHeapZeroInitialized(
+            @ApplicationInfo.NativeHeapZeroInitialized int value) {
+        nativeHeapZeroInitialized = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+        requestRawExternalStorageAccess = value;
+        return this;
+    }
+    @Override
+    public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
+        return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) {
+        return setBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) {
+        return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) {
+        return setBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) {
+        return setBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setUsesNonSdkApi(boolean value) {
+        return setBoolean(Booleans.USES_NON_SDK_API, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setHasFragileUserData(boolean value) {
+        return setBoolean(Booleans.HAS_FRAGILE_USER_DATA, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setCantSaveState(boolean value) {
+        return setBoolean(Booleans.CANT_SAVE_STATE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) {
+        return setBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setAutoRevokePermissions(int value) {
+        autoRevokePermissions = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) {
+        return setBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE, value);
+    }
+
+    @Override
+    public ParsingPackageImpl setVersionName(String versionName) {
+        this.versionName = versionName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName) {
+        this.compileSdkVersionCodeName = compileSdkVersionCodeName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setProcessName(String processName) {
+        this.processName = processName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRestrictedAccountType(@Nullable String restrictedAccountType) {
+        this.restrictedAccountType = restrictedAccountType;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayTargetOverlayableName(
+            @Nullable String overlayTargetOverlayableName) {
+        this.overlayTargetOverlayableName = overlayTargetOverlayableName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayCategory(@Nullable String overlayCategory) {
+        this.overlayCategory = overlayCategory;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAppComponentFactory(@Nullable String appComponentFactory) {
+        this.appComponentFactory = appComponentFactory;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setBackupAgentName(@Nullable String backupAgentName) {
+        this.backupAgentName = backupAgentName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setClassLoaderName(@Nullable String classLoaderName) {
+        this.classLoaderName = classLoaderName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setClassName(@Nullable String className) {
+        this.className = className == null ? null : className.trim();
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setManageSpaceActivityName(@Nullable String manageSpaceActivityName) {
+        this.manageSpaceActivityName = manageSpaceActivityName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setPermission(@Nullable String permission) {
+        this.permission = permission;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTaskAffinity(@Nullable String taskAffinity) {
+        this.taskAffinity = taskAffinity;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setZygotePreloadName(@Nullable String zygotePreloadName) {
+        this.zygotePreloadName = zygotePreloadName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackage setAttributionsAreUserVisible(boolean attributionsAreUserVisible) {
+        setBoolean(Booleans.ATTRIBUTIONS_ARE_USER_VISIBLE, attributionsAreUserVisible);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage setResetEnabledSettingsOnAppDataCleared(
+            boolean resetEnabledSettingsOnAppDataCleared) {
+        setBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED,
+                resetEnabledSettingsOnAppDataCleared);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLocaleConfigRes(int value) {
+        mLocaleConfigRes = value;
+        return this;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageInternal.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageInternal.java
new file mode 100644
index 0000000..5457785
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageInternal.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageInfo;
+
+import com.android.internal.R;
+
+/**
+ * Methods which would've been a part of {@link PkgWithoutStatePackageInfo} or {@link
+ * PkgWithoutStateAppInfo}, but are removed/deprecated.
+ * <p>
+ * This is different from {@link ParsingPackageHidden}. The methods in that interface cannot be
+ * accessed by anyone except the parsing utilities, whereas the methods in this interface are valid
+ * and can be accessed by any internal caller that needs it.
+ *
+ * @hide
+ */
+interface ParsingPackageInternal {
+
+    /**
+     * @see PackageInfo#overlayCategory
+     * @see R.styleable#AndroidManifestResourceOverlay_category
+     */
+    @Nullable
+    String getOverlayCategory();
+
+    /**
+     * @see PackageInfo#overlayPriority
+     * @see R.styleable#AndroidManifestResourceOverlay_priority
+     */
+    int getOverlayPriority();
+
+    /**
+     * @see PackageInfo#overlayTarget
+     * @see R.styleable#AndroidManifestResourceOverlay_targetPackage
+     */
+    @Nullable
+    String getOverlayTarget();
+
+    /**
+     * @see PackageInfo#targetOverlayableName
+     * @see R.styleable#AndroidManifestResourceOverlay_targetName
+     */
+    @Nullable
+    String getOverlayTargetOverlayableName();
+
+    /**
+     * @see PackageInfo#mOverlayIsStatic
+     */
+    boolean isOverlayIsStatic();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
new file mode 100644
index 0000000..4b659a14
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.SigningDetails;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseIntArray;
+
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
+
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Everything written by {@link ParsingPackage} and readable back.
+ *
+ * @hide
+ */
+public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutStatePackageInfo,
+        ParsingPackageInternal {
+
+    /**
+     * The names of packages to adopt ownership of permissions from, parsed under {@link
+     * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
+     *
+     * @see R.styleable#AndroidManifestOriginalPackage_name
+     */
+    @NonNull
+    List<String> getAdoptPermissions();
+
+    /**
+     * @see R.styleable#AndroidManifestApexSystemService
+     */
+    @NonNull
+    List<ParsedApexSystemService> getApexSystemServices();
+
+    @NonNull
+    List<ParsedAttribution> getAttributions();
+
+    /**
+     * Permissions requested but not in the manifest. These may have been split or migrated from
+     * previous versions/definitions.
+     */
+    @NonNull
+    List<String> getImplicitPermissions();
+
+    @NonNull
+    List<ParsedUsesPermission> getUsesPermissions();
+
+    /**
+     * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
+     * ParsingPackageUtils#TAG_KEY_SETS}.
+     *
+     * @see R.styleable#AndroidManifestKeySet
+     * @see R.styleable#AndroidManifestPublicKey
+     */
+    @NonNull
+    Map<String, ArraySet<PublicKey>> getKeySetMapping();
+
+    /**
+     * Library names this package is declared as, for use by other packages with "uses-library".
+     *
+     * @see R.styleable#AndroidManifestLibrary
+     */
+    @NonNull
+    List<String> getLibraryNames();
+
+    /**
+     * TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
+     */
+    @Nullable
+    Bundle getMetaData();
+
+    @Nullable
+    Set<String> getMimeGroups();
+
+    /**
+     * @see R.styleable#AndroidManifestExtensionSdk
+     */
+    @Nullable
+    SparseIntArray getMinExtensionVersions();
+
+    /**
+     * For system use to migrate from an old package name to a new one, moving over data if
+     * available.
+     *
+     * @see R.styleable#AndroidManifestOriginalPackage}
+     */
+    @NonNull
+    List<String> getOriginalPackages();
+
+    /**
+     * Map of overlayable name to actor name.
+     */
+    @NonNull
+    Map<String, String> getOverlayables();
+
+    /**
+     * @see android.content.pm.PermissionGroupInfo
+     */
+    @NonNull
+    List<ParsedPermissionGroup> getPermissionGroups();
+
+    /**
+     * Used to determine the default preferred handler of an {@link Intent}.
+     * <p>
+     * Map of component className to intent info inside that component. TODO(b/135203078): Is this
+     * actually used/working?
+     */
+    @NonNull
+    List<Pair<String, ParsedIntentInfo>> getPreferredActivityFilters();
+
+    /**
+     * @see android.content.pm.ProcessInfo
+     */
+    @NonNull
+    Map<String, ParsedProcess> getProcesses();
+
+    /**
+     * System protected broadcasts.
+     *
+     * @see R.styleable#AndroidManifestProtectedBroadcast
+     */
+    @NonNull
+    List<String> getProtectedBroadcasts();
+
+    /**
+     * Intents that this package may query or require and thus requires visibility into.
+     *
+     * @see R.styleable#AndroidManifestQueriesIntent
+     */
+    @NonNull
+    List<Intent> getQueriesIntents();
+
+    /**
+     * Other packages that this package may query or require and thus requires visibility into.
+     *
+     * @see R.styleable#AndroidManifestQueriesPackage
+     */
+    @NonNull
+    List<String> getQueriesPackages();
+
+    /**
+     * Authorities that this package may query or require and thus requires visibility into.
+     *
+     * @see R.styleable#AndroidManifestQueriesProvider
+     */
+    @NonNull
+    Set<String> getQueriesProviders();
+
+    /**
+     * SHA-512 hash of the only APK that can be used to update a system package.
+     *
+     * @see R.styleable#AndroidManifestRestrictUpdate
+     */
+    @Nullable
+    byte[] getRestrictUpdateHash();
+
+    /**
+     * The signature data of all APKs in this package, which must be exactly the same across the
+     * base and splits.
+     */
+    SigningDetails getSigningDetails();
+
+    /**
+     * Returns the properties set on the application
+     */
+    @NonNull
+    Map<String, Property> getProperties();
+
+    /**
+     * Flags of any split APKs; ordered by parsed splitName
+     */
+    @Nullable
+    int[] getSplitFlags();
+
+    /**
+     * @see R.styleable#AndroidManifestSdkLibrary_name
+     */
+    @Nullable
+    String getSdkLibName();
+
+    /**
+     * @see R.styleable#AndroidManifestSdkLibrary_versionMajor
+     */
+    int getSdkLibVersionMajor();
+
+    /**
+     * @see R.styleable#AndroidManifestStaticLibrary_name
+     */
+    @Nullable
+    String getStaticSharedLibName();
+
+    /**
+     * @see R.styleable#AndroidManifestStaticLibrary_version
+     */
+    long getStaticSharedLibVersion();
+
+    /**
+     * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
+     * ParsingPackageUtils#TAG_KEY_SETS}.
+     *
+     * @see R.styleable#AndroidManifestUpgradeKeySet
+     */
+    @NonNull
+    Set<String> getUpgradeKeySets();
+
+    /**
+     * @see R.styleable#AndroidManifestUsesLibrary
+     */
+    @NonNull
+    List<String> getUsesLibraries();
+
+    /**
+     * @see R.styleable#AndroidManifestUsesNativeLibrary
+     */
+    @NonNull
+    List<String> getUsesNativeLibraries();
+
+    /**
+     * Like {@link #getUsesLibraries()}, but marked optional by setting {@link
+     * R.styleable#AndroidManifestUsesLibrary_required} to false . Application is expected to handle
+     * absence manually.
+     *
+     * @see R.styleable#AndroidManifestUsesLibrary
+     */
+    @NonNull
+    List<String> getUsesOptionalLibraries();
+
+    /**
+     * Like {@link #getUsesNativeLibraries()}, but marked optional by setting {@link
+     * R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is expected to
+     * handle absence manually.
+     *
+     * @see R.styleable#AndroidManifestUsesNativeLibrary
+     */
+    @NonNull
+    List<String> getUsesOptionalNativeLibraries();
+
+    /**
+     * TODO(b/135203078): Move static library stuff to an inner data class
+     *
+     * @see R.styleable#AndroidManifestUsesStaticLibrary
+     */
+    @NonNull
+    List<String> getUsesStaticLibraries();
+
+    /**
+     * @see R.styleable#AndroidManifestUsesStaticLibrary_certDigest
+     */
+    @Nullable
+    String[][] getUsesStaticLibrariesCertDigests();
+
+    /**
+     * @see R.styleable#AndroidManifestUsesStaticLibrary_version
+     */
+    @Nullable
+    long[] getUsesStaticLibrariesVersions();
+
+    /**
+     * TODO(b/135203078): Move SDK library stuff to an inner data class
+     *
+     * @see R.styleable#AndroidManifestUsesSdkLibrary
+     */
+    @NonNull
+    List<String> getUsesSdkLibraries();
+
+    /**
+     * @see R.styleable#AndroidManifestUsesSdkLibrary_certDigest
+     */
+    @Nullable
+    String[][] getUsesSdkLibrariesCertDigests();
+
+    /**
+     * @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+     */
+    @Nullable
+    long[] getUsesSdkLibrariesVersionsMajor();
+
+    boolean hasPreserveLegacyExternalStorage();
+
+    /**
+     * @see R.styleable#AndroidManifestApplication_forceQueryable
+     */
+    boolean isForceQueryable();
+
+    /**
+     * @see ApplicationInfo#FLAG_IS_GAME
+     */
+    @Deprecated
+    boolean isGame();
+
+    /**
+     * The install time abi override to choose 32bit abi's when multiple abi's are present. This is
+     * only meaningful for multiarch applications. The use32bitAbi attribute is ignored if
+     * cpuAbiOverride is also set.
+     *
+     * @see R.attr#use32bitAbi
+     */
+    boolean isUse32BitAbi();
+
+    /**
+     * Set if the any of components are visible to instant applications.
+     *
+     * @see R.styleable#AndroidManifestActivity_visibleToInstantApps
+     * @see R.styleable#AndroidManifestProvider_visibleToInstantApps
+     * @see R.styleable#AndroidManifestService_visibleToInstantApps
+     */
+    boolean isVisibleToInstantApps();
+
+    /**
+     * Whether the enabled settings of components in the application should be reset to the default,
+     * when the application's user data is cleared.
+     *
+     * @see R.styleable#AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared
+     */
+    boolean isResetEnabledSettingsOnAppDataCleared();
+
+    /**
+     * The resource ID used to provide the application's locales configuration.
+     *
+     * @see R.styleable#AndroidManifestApplication_localeConfig
+     */
+    int getLocaleConfigRes();
+
+    /**
+     * @see R.styleable#AndroidManifest_inheritKeyStoreKeys
+     */
+    boolean shouldInheritKeyStoreKeys();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
new file mode 100644
index 0000000..bf7c55f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -0,0 +1,3232 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Build.VERSION_CODES.DONUT;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import android.annotation.AnyRes;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleableRes;
+import android.app.ActivityThread;
+import android.app.ResourcesManager;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseInput.DeferredError;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.ext.SdkExtensions;
+import android.permission.PermissionManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TypedValue;
+import android.util.apk.ApkSignatureVerifier;
+
+import com.android.internal.R;
+import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+import com.android.server.pm.permission.CompatibilityPermissionInfo;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityUtils;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.component.ParsedApexSystemServiceUtils;
+import com.android.server.pm.pkg.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedAttributionUtils;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedInstrumentationUtils;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedIntentInfoUtils;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedPermissionUtils;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProcessUtils;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProviderUtils;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.component.ParsedServiceUtils;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
+import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
+import com.android.server.pm.split.DefaultSplitAssetLoader;
+import com.android.server.pm.split.SplitAssetDependencyLoader;
+import com.android.server.pm.split.SplitAssetLoader;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
+ * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
+ *
+ * @hide
+ */
+public class ParsingPackageUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    public static final boolean DEBUG_JAR = false;
+    public static final boolean DEBUG_BACKUP = false;
+    public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+    public static final float ASPECT_RATIO_NOT_SET = -1f;
+
+    /**
+     * File name in an APK for the Android manifest.
+     */
+    public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+
+    /**
+     * Path prefix for apps on expanded storage
+     */
+    public static final String MNT_EXPAND = "/mnt/expand/";
+
+    public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+    public static final String TAG_APPLICATION = "application";
+    public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+    public static final String TAG_EAT_COMMENT = "eat-comment";
+    public static final String TAG_FEATURE_GROUP = "feature-group";
+    public static final String TAG_INSTRUMENTATION = "instrumentation";
+    public static final String TAG_KEY_SETS = "key-sets";
+    public static final String TAG_MANIFEST = "manifest";
+    public static final String TAG_ORIGINAL_PACKAGE = "original-package";
+    public static final String TAG_OVERLAY = "overlay";
+    public static final String TAG_PACKAGE = "package";
+    public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+    public static final String TAG_ATTRIBUTION = "attribution";
+    public static final String TAG_PERMISSION = "permission";
+    public static final String TAG_PERMISSION_GROUP = "permission-group";
+    public static final String TAG_PERMISSION_TREE = "permission-tree";
+    public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+    public static final String TAG_QUERIES = "queries";
+    public static final String TAG_RESTRICT_UPDATE = "restrict-update";
+    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
+    public static final String TAG_SUPPORTS_INPUT = "supports-input";
+    public static final String TAG_USES_CONFIGURATION = "uses-configuration";
+    public static final String TAG_USES_FEATURE = "uses-feature";
+    public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+    public static final String TAG_USES_PERMISSION = "uses-permission";
+    public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+    public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+    public static final String TAG_USES_SDK = "uses-sdk";
+    public static final String TAG_USES_SPLIT = "uses-split";
+    public static final String TAG_PROFILEABLE = "profileable";
+    public static final String TAG_RECEIVER = "receiver";
+
+    public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+    public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
+    public static final String METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES =
+            "android.can_display_on_remote_devices";
+    public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
+            "android.activity_window_layout_affinity";
+    public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
+
+    public static final int SDK_VERSION = Build.VERSION.SDK_INT;
+    public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
+
+    public static boolean sCompatibilityModeEnabled = true;
+    public static boolean sUseRoundIcon = false;
+
+    public static final int PARSE_DEFAULT_INSTALL_LOCATION =
+            PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+    public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
+
+    /**
+     * If set to true, we will only allow package files that exactly match the DTD. Otherwise, we
+     * try to get as much from the package as we can without failing. This should normally be set to
+     * false, to support extensions to the DTD in future versions.
+     */
+    public static final boolean RIGID_PARSER = false;
+
+    public static final int PARSE_MUST_BE_APK = 1 << 0;
+    public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
+    public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
+    public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
+    public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
+    public static final int PARSE_ENFORCE_CODE = 1 << 6;
+    /**
+     * This flag is applied in the ApkLiteParser. Used by OverlayConfigParser to ignore the checks
+     * of required system property within the overlay tag.
+     */
+    public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
+    public static final int PARSE_CHATTY = 1 << 31;
+
+    @IntDef(flag = true, prefix = { "PARSE_" }, value = {
+            PARSE_CHATTY,
+            PARSE_COLLECT_CERTIFICATES,
+            PARSE_ENFORCE_CODE,
+            PARSE_EXTERNAL_STORAGE,
+            PARSE_IGNORE_PROCESSES,
+            PARSE_IS_SYSTEM_DIR,
+            PARSE_MUST_BE_APK,
+            PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ParseFlags {}
+
+    /**
+     * @see #parseDefault(ParseInput, File, int, List, boolean)
+     */
+    @NonNull
+    public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
+            @ParseFlags int parseFlags,
+            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+            boolean collectCertificates) {
+        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
+        return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates);
+    }
+
+    /**
+     * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
+     * request, without caching the input object and without querying the internal system state for
+     * feature support.
+     */
+    @NonNull
+    public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
+            @ParseFlags int parseFlags,
+            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+            boolean collectCertificates) {
+        ParseResult<ParsingPackage> result;
+
+        ParsingPackageUtils parser = new ParsingPackageUtils(false, null /*separateProcesses*/,
+                null /*displayMetrics*/, splitPermissions, new Callback() {
+            @Override
+            public boolean hasFeature(String feature) {
+                // Assume the device doesn't support anything. This will affect permission
+                // parsing and will force <uses-permission/> declarations to include all
+                // requiredNotFeature permissions and exclude all requiredFeature
+                // permissions. This mirrors the old behavior.
+                return false;
+            }
+
+            @Override
+            public ParsingPackage startParsingPackage(
+                    @NonNull String packageName,
+                    @NonNull String baseApkPath,
+                    @NonNull String path,
+                    @NonNull TypedArray manifestArray, boolean isCoreApp) {
+                return new ParsingPackageImpl(packageName, baseApkPath, path, manifestArray);
+            }
+        });
+        result = parser.parsePackage(input, file, parseFlags);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        final ParsingPackage pkg = result.getResult();
+        if (collectCertificates) {
+            final ParseResult<SigningDetails> ret =
+                    ParsingPackageUtils.getSigningDetails(input, pkg, false /*skipVerify*/);
+            if (ret.isError()) {
+                return input.error(ret);
+            }
+            pkg.setSigningDetails(ret.getResult());
+        }
+
+        // Need to call this to finish the parsing stage
+        pkg.hideAsParsed();
+
+        return input.success(pkg);
+    }
+
+    private boolean mOnlyCoreApps;
+    private String[] mSeparateProcesses;
+    private DisplayMetrics mDisplayMetrics;
+    @NonNull
+    private List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
+    private Callback mCallback;
+
+    public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
+            DisplayMetrics displayMetrics,
+            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+            @NonNull Callback callback) {
+        mOnlyCoreApps = onlyCoreApps;
+        mSeparateProcesses = separateProcesses;
+        mDisplayMetrics = displayMetrics;
+        mSplitPermissionInfos = splitPermissions;
+        mCallback = callback;
+    }
+
+    /**
+     * Parse the package at the given location. Automatically detects if the package is a monolithic
+     * style (single APK file) or cluster style (directory of APKs).
+     * <p>
+     * This performs validity checking on cluster style packages, such as requiring identical
+     * package name and version codes, a single base APK, and unique split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that must be done separately
+     * in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
+     * <p>
+     * If {@code useCaches} is true, the package parser might return a cached result from a previous
+     * parse of the same {@code packageFile} with the same {@code flags}. Note that this method does
+     * not check whether {@code packageFile} has changed since the last parse, it's up to callers to
+     * do so.
+     */
+    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackage(input, packageFile, flags);
+        } else {
+            return parseMonolithicPackage(input, packageFile, flags);
+        }
+    }
+
+    /**
+     * Parse all APKs contained in the given directory, treating them as a single package. This also
+     * performs validity checking, such as requiring identical package name and version codes, a
+     * single base APK, and unique split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that must be done separately
+     * in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
+     */
+    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
+            int flags) {
+        final ParseResult<PackageLite> liteResult =
+                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
+        if (liteResult.isError()) {
+            return input.error(liteResult);
+        }
+
+        final PackageLite lite = liteResult.getResult();
+        if (mOnlyCoreApps && !lite.isCoreApp()) {
+            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
+                    "Not a coreApp: " + packageDir);
+        }
+
+        // Build the split dependency tree.
+        SparseArray<int[]> splitDependencies = null;
+        final SplitAssetLoader assetLoader;
+        if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) {
+            try {
+                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+                return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+            }
+        } else {
+            assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        }
+
+        try {
+            final File baseApk = new File(lite.getBaseApkPath());
+            final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
+                    lite.getPath(), assetLoader, flags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            ParsingPackage pkg = result.getResult();
+            if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
+                pkg.asSplit(
+                        lite.getSplitNames(),
+                        lite.getSplitApkPaths(),
+                        lite.getSplitRevisionCodes(),
+                        splitDependencies
+                );
+                final int num = lite.getSplitNames().length;
+
+                for (int i = 0; i < num; i++) {
+                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
+                    final ParseResult<ParsingPackage> split =
+                            parseSplitApk(input, pkg, i, splitAssets, flags);
+                    if (split.isError()) {
+                        return input.error(split);
+                    }
+                }
+            }
+
+            pkg.setUse32BitAbi(lite.isUse32bitAbi());
+            return input.success(pkg);
+        } catch (IllegalArgumentException e) {
+            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
+                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    /**
+     * Parse the given APK file, treating it as as a single monolithic package.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that must be done separately
+     * in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
+     */
+    private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
+            int flags) {
+        final ParseResult<PackageLite> liteResult =
+                ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
+        if (liteResult.isError()) {
+            return input.error(liteResult);
+        }
+
+        final PackageLite lite = liteResult.getResult();
+        if (mOnlyCoreApps && !lite.isCoreApp()) {
+            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
+                    "Not a coreApp: " + apkFile);
+        }
+
+        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        try {
+            final ParseResult<ParsingPackage> result = parseBaseApk(input,
+                    apkFile,
+                    apkFile.getCanonicalPath(),
+                    assetLoader, flags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            return input.success(result.getResult()
+                    .setUse32BitAbi(lite.isUse32bitAbi()));
+        } catch (IOException e) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to get path: " + apkFile, e);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
+            String codePath, SplitAssetLoader assetLoader, int flags) {
+        final String apkPath = apkFile.getAbsolutePath();
+
+        String volumeUuid = null;
+        if (apkPath.startsWith(MNT_EXPAND)) {
+            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
+            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
+        }
+
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+
+        final AssetManager assets;
+        try {
+            assets = assetLoader.getBaseAssetManager();
+        } catch (IllegalArgumentException e) {
+            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
+                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
+        }
+        final int cookie = assets.findCookieForPath(apkPath);
+        if (cookie == 0) {
+            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Failed adding asset path: " + apkPath);
+        }
+
+        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
+                ANDROID_MANIFEST_FILENAME)) {
+            final Resources res = new Resources(assets, mDisplayMetrics, null);
+
+            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
+                    parser, flags);
+            if (result.isError()) {
+                return input.error(result.getErrorCode(),
+                        apkPath + " (at " + parser.getPositionDescription() + "): "
+                                + result.getErrorMessage());
+            }
+
+            final ParsingPackage pkg = result.getResult();
+            if (assets.containsAllocatedTable()) {
+                final ParseResult<?> deferResult = input.deferError(
+                        "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
+                                + " the resources.arsc of installed APKs to be stored uncompressed"
+                                + " and aligned on a 4-byte boundary",
+                        DeferredError.RESOURCES_ARSC_COMPRESSED);
+                if (deferResult.isError()) {
+                    return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
+                            deferResult.getErrorMessage());
+                }
+            }
+
+            ApkAssets apkAssets = assetLoader.getBaseApkAssets();
+            boolean definesOverlayable = false;
+            try {
+                definesOverlayable = apkAssets.definesOverlayable();
+            } catch (IOException ignored) {
+                // Will fail if there's no packages in the ApkAssets, which can be treated as false
+            }
+
+            if (definesOverlayable) {
+                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String packageName = packageNames.valueAt(index);
+                    Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
+                    if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
+                        for (String overlayable : overlayableToActor.keySet()) {
+                            pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
+                        }
+                    }
+                }
+            }
+
+            pkg.setVolumeUuid(volumeUuid);
+
+            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
+                final ParseResult<SigningDetails> ret =
+                        getSigningDetails(input, pkg, false /*skipVerify*/);
+                if (ret.isError()) {
+                    return input.error(ret);
+                }
+                pkg.setSigningDetails(ret.getResult());
+            } else {
+                pkg.setSigningDetails(SigningDetails.UNKNOWN);
+            }
+
+            return input.success(pkg);
+        } catch (Exception e) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        }
+    }
+
+    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
+            ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
+        final String apkPath = pkg.getSplitCodePaths()[splitIndex];
+
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+
+        // This must always succeed, as the path has been added to the AssetManager before.
+        final int cookie = assets.findCookieForPath(apkPath);
+        if (cookie == 0) {
+            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Failed adding asset path: " + apkPath);
+        }
+        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
+                ANDROID_MANIFEST_FILENAME)) {
+            Resources res = new Resources(assets, mDisplayMetrics, null);
+            ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
+                    parser, flags, splitIndex);
+            if (parseResult.isError()) {
+                return input.error(parseResult.getErrorCode(),
+                        apkPath + " (at " + parser.getPositionDescription() + "): "
+                                + parseResult.getErrorMessage());
+            }
+
+            return parseResult;
+        } catch (Exception e) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>base APK</em>. When adding new features you need to consider
+     * whether they should be supported by split APKs and child packages.
+     *
+     * @param apkPath The package apk file path
+     * @param res     The resources from which to resolve values
+     * @param parser  The manifest parser
+     * @param flags   Flags how to parse
+     * @return Parsed package or null on error.
+     */
+    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
+            String codePath, Resources res, XmlResourceParser parser, int flags)
+            throws XmlPullParserException, IOException {
+        final String splitName;
+        final String pkgName;
+
+        ParseResult<Pair<String, String>> packageSplitResult =
+                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
+        if (packageSplitResult.isError()) {
+            return input.error(packageSplitResult);
+        }
+
+        Pair<String, String> packageSplit = packageSplitResult.getResult();
+        pkgName = packageSplit.first;
+        splitName = packageSplit.second;
+
+        if (!TextUtils.isEmpty(splitName)) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                    "Expected base APK, but found split " + splitName
+            );
+        }
+
+        final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
+        try {
+            final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
+                    "coreApp",false);
+            final ParsingPackage pkg = mCallback.startParsingPackage(
+                    pkgName, apkPath, codePath, manifestArray, isCoreApp);
+            final ParseResult<ParsingPackage> result =
+                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
+            if (result.isError()) {
+                return result;
+            }
+
+            return input.success(pkg);
+        } finally {
+            manifestArray.recycle();
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>split APK</em>.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable of doing, so many
+     * valid features of a base APK have been carefully omitted here.
+     *
+     * @param pkg builder to fill
+     * @return false on failure
+     */
+    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, int flags, int splitIndex)
+            throws XmlPullParserException, IOException {
+        // We parsed manifest tag earlier; just skip past it
+        final ParseResult<Pair<String, String>> packageSplitResult =
+                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
+        if (packageSplitResult.isError()) {
+            return input.error(packageSplitResult);
+        }
+
+        int type;
+
+        boolean foundApp = false;
+
+        int outerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String tagName = parser.getName();
+            if (TAG_APPLICATION.equals(tagName)) {
+                if (foundApp) {
+                    if (RIGID_PARSER) {
+                        result = input.error("<manifest> has more than one <application>");
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        result = input.success(null);
+                    }
+                } else {
+                    foundApp = true;
+                    result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
+                }
+            } else {
+                result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (!foundApp) {
+            ParseResult<?> deferResult = input.deferError(
+                    "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG);
+            if (deferResult.isError()) {
+                return input.error(deferResult);
+            }
+        }
+
+        return input.success(pkg);
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>split APK</em> manifest.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable of doing, so many
+     * valid features of a base APK have been carefully omitted here.
+     */
+    private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+        try {
+            pkg.setSplitHasCode(splitIndex, sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_hasCode, true));
+
+            final String classLoaderName = sa.getString(
+                    R.styleable.AndroidManifestApplication_classLoader);
+            if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
+                    classLoaderName)) {
+                pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
+            } else {
+                return input.error("Invalid class loader name: " + classLoaderName);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        // If the loaded component did not specify a split, inherit the split name
+        // based on the split it is defined in.
+        // This is used to later load the correct split when starting this
+        // component.
+        String defaultSplitName = pkg.getSplitNames()[splitIndex];
+
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            ParsedMainComponent mainComponent = null;
+
+            final ParseResult result;
+            String tagName = parser.getName();
+            boolean isActivity = false;
+            switch (tagName) {
+                case "activity":
+                    isActivity = true;
+                    // fall-through
+                case "receiver":
+                    ParseResult<ParsedActivity> activityResult =
+                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
+                                    res, parser, flags, sUseRoundIcon, defaultSplitName, input);
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        if (isActivity) {
+                            pkg.addActivity(activity);
+                        } else {
+                            pkg.addReceiver(activity);
+                        }
+                        mainComponent = activity;
+                    }
+                    result = activityResult;
+                    break;
+                case "service":
+                    ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
+                            mSeparateProcesses, pkg, res, parser, flags, sUseRoundIcon,
+                            defaultSplitName, input);
+                    if (serviceResult.isSuccess()) {
+                        ParsedService service = serviceResult.getResult();
+                        pkg.addService(service);
+                        mainComponent = service;
+                    }
+                    result = serviceResult;
+                    break;
+                case "provider":
+                    ParseResult<ParsedProvider> providerResult =
+                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
+                                    flags, sUseRoundIcon, defaultSplitName, input);
+                    if (providerResult.isSuccess()) {
+                        ParsedProvider provider = providerResult.getResult();
+                        pkg.addProvider(provider);
+                        mainComponent = provider;
+                    }
+                    result = providerResult;
+                    break;
+                case "activity-alias":
+                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
+                            sUseRoundIcon, defaultSplitName, input);
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        pkg.addActivity(activity);
+                        mainComponent = activity;
+                    }
+
+                    result = activityResult;
+                    break;
+                default:
+                    result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(pkg);
+    }
+
+    /**
+     * For parsing non-MainComponents. Main ones have an order and some special handling which is
+     * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
+     * XmlResourceParser, int, int)}.
+     */
+    private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+        switch (tag) {
+            case "meta-data":
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<meta-data>", input);
+                if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
+                    pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
+                }
+                return metaDataResult;
+            case "property":
+                ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<property>", input);
+                if (propertyResult.isSuccess()) {
+                    pkg.addProperty(propertyResult.getResult());
+                }
+                return propertyResult;
+            case "uses-sdk-library":
+                return parseUsesSdkLibrary(input, pkg, res, parser);
+            case "uses-static-library":
+                return parseUsesStaticLibrary(input, pkg, res, parser);
+            case "uses-library":
+                return parseUsesLibrary(input, pkg, res, parser);
+            case "uses-native-library":
+                return parseUsesNativeLibrary(input, pkg, res, parser);
+            case "uses-package":
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                return input.success(null);
+            default:
+                return ParsingUtils.unknownTag("<application>", pkg, parser, input);
+        }
+    }
+
+    private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
+            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
+        if (sharedUserResult.isError()) {
+            return sharedUserResult;
+        }
+
+        pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
+                R.styleable.AndroidManifest_installLocation, sa))
+                .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
+                        R.styleable.AndroidManifest_targetSandboxVersion, sa))
+                /* Set the global "on SD card" flag */
+                .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
+                .setInheritKeyStoreKeys(bool(false,
+                        R.styleable.AndroidManifest_inheritKeyStoreKeys, sa));
+
+        boolean foundApp = false;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            final ParseResult result;
+
+            // <application> has special logic, so it's handled outside the general method
+            if (TAG_APPLICATION.equals(tagName)) {
+                if (foundApp) {
+                    if (RIGID_PARSER) {
+                        result = input.error("<manifest> has more than one <application>");
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        result = input.success(null);
+                    }
+                } else {
+                    foundApp = true;
+                    result = parseBaseApplication(input, pkg, res, parser, flags);
+                }
+            } else {
+                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
+            ParseResult<?> deferResult = input.deferError(
+                    "<manifest> does not contain an <application> or <instrumentation>",
+                    DeferredError.MISSING_APP_TAG);
+            if (deferResult.isError()) {
+                return input.error(deferResult);
+            }
+        }
+
+        if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
+            return input.error(
+                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Combination <attribution> tags are not valid"
+            );
+        }
+
+        if (ParsedPermissionUtils.declareDuplicatePermission(pkg)) {
+            return input.error(
+                    INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Declare duplicate permissions with different protection levels."
+            );
+        }
+
+        convertCompatPermissions(pkg);
+
+        convertSplitPermissions(pkg);
+
+        // At this point we can check if an application is not supporting densities and hence
+        // cannot be windowed / resized. Note that an SDK version of 0 is common for
+        // pre-Doughnut applications.
+        if (pkg.getTargetSdkVersion() < DONUT
+                || (!pkg.isSupportsSmallScreens()
+                && !pkg.isSupportsNormalScreens()
+                && !pkg.isSupportsLargeScreens()
+                && !pkg.isSupportsExtraLargeScreens()
+                && !pkg.isResizeable()
+                && !pkg.isAnyDensity())) {
+            adjustPackageToBeUnresizeableAndUnpipable(pkg);
+        }
+
+        return input.success(pkg);
+    }
+
+    private ParseResult parseBaseApkTag(String tag, ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
+            throws IOException, XmlPullParserException {
+        switch (tag) {
+            case TAG_OVERLAY:
+                return parseOverlay(input, pkg, res, parser);
+            case TAG_KEY_SETS:
+                return parseKeySets(input, pkg, res, parser);
+            case "feature": // TODO moltmann: Remove
+            case TAG_ATTRIBUTION:
+                return parseAttribution(input, pkg, res, parser);
+            case TAG_PERMISSION_GROUP:
+                return parsePermissionGroup(input, pkg, res, parser);
+            case TAG_PERMISSION:
+                return parsePermission(input, pkg, res, parser);
+            case TAG_PERMISSION_TREE:
+                return parsePermissionTree(input, pkg, res, parser);
+            case TAG_USES_PERMISSION:
+            case TAG_USES_PERMISSION_SDK_M:
+            case TAG_USES_PERMISSION_SDK_23:
+                return parseUsesPermission(input, pkg, res, parser);
+            case TAG_USES_CONFIGURATION:
+                return parseUsesConfiguration(input, pkg, res, parser);
+            case TAG_USES_FEATURE:
+                return parseUsesFeature(input, pkg, res, parser);
+            case TAG_FEATURE_GROUP:
+                return parseFeatureGroup(input, pkg, res, parser);
+            case TAG_USES_SDK:
+                return parseUsesSdk(input, pkg, res, parser);
+            case TAG_SUPPORT_SCREENS:
+                return parseSupportScreens(input, pkg, res, parser);
+            case TAG_PROTECTED_BROADCAST:
+                return parseProtectedBroadcast(input, pkg, res, parser);
+            case TAG_INSTRUMENTATION:
+                return parseInstrumentation(input, pkg, res, parser);
+            case TAG_ORIGINAL_PACKAGE:
+                return parseOriginalPackage(input, pkg, res, parser);
+            case TAG_ADOPT_PERMISSIONS:
+                return parseAdoptPermissions(input, pkg, res, parser);
+            case TAG_USES_GL_TEXTURE:
+            case TAG_COMPATIBLE_SCREENS:
+            case TAG_SUPPORTS_INPUT:
+            case TAG_EAT_COMMENT:
+                // Just skip this tag
+                XmlUtils.skipCurrentTag(parser);
+                return input.success(pkg);
+            case TAG_RESTRICT_UPDATE:
+                return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+            case TAG_QUERIES:
+                return parseQueries(input, pkg, res, parser);
+            default:
+                return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
+            ParsingPackage pkg, TypedArray sa) {
+        int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
+        if ((maxSdkVersion != 0) && maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT) {
+            return input.success(pkg);
+        }
+
+        String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
+        if (TextUtils.isEmpty(str)) {
+            return input.success(pkg);
+        }
+
+        if (!"android".equals(pkg.getPackageName())) {
+            ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, str,
+                    true, true);
+            if (nameResult.isError()) {
+                return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                        "<manifest> specifies bad sharedUserId name \"" + str + "\": "
+                                + nameResult.getErrorMessage());
+            }
+        }
+
+        return input.success(pkg
+                .setSharedUserId(str.intern())
+                .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
+    }
+
+    private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        // we've encountered the 'key-sets' tag
+        // all the keys and keysets that we want must be defined here
+        // so we're going to iterate over the parser and pull out the things we want
+        int outerDepth = parser.getDepth();
+        int currentKeySetDepth = -1;
+        int type;
+        String currentKeySet = null;
+        ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
+        ArraySet<String> upgradeKeySets = new ArraySet<>();
+        ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
+        ArraySet<String> improperKeySets = new ArraySet<>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG) {
+                if (parser.getDepth() == currentKeySetDepth) {
+                    currentKeySet = null;
+                    currentKeySetDepth = -1;
+                }
+                continue;
+            }
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "key-set": {
+                    if (currentKeySet != null) {
+                        return input.error("Improperly nested 'key-set' tag at "
+                                + parser.getPositionDescription());
+                    }
+                    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
+                    try {
+                        final String keysetName = sa.getNonResourceString(
+                                R.styleable.AndroidManifestKeySet_name);
+                        definedKeySets.put(keysetName, new ArraySet<>());
+                        currentKeySet = keysetName;
+                        currentKeySetDepth = parser.getDepth();
+                    } finally {
+                        sa.recycle();
+                    }
+                } break;
+                case "public-key": {
+                    if (currentKeySet == null) {
+                        return input.error("Improperly nested 'key-set' tag at "
+                                + parser.getPositionDescription());
+                    }
+                    TypedArray sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestPublicKey);
+                    try {
+                        final String publicKeyName = nonResString(
+                                R.styleable.AndroidManifestPublicKey_name, sa);
+                        final String encodedKey = nonResString(
+                                R.styleable.AndroidManifestPublicKey_value, sa);
+                        if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
+                            return input.error("'public-key' " + publicKeyName
+                                    + " must define a public-key value on first use at "
+                                    + parser.getPositionDescription());
+                        } else if (encodedKey != null) {
+                            PublicKey currentKey =
+                                    FrameworkParsingPackageUtils.parsePublicKey(encodedKey);
+                            if (currentKey == null) {
+                                Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
+                                        + parser.getPositionDescription() + " key-set "
+                                        + currentKeySet
+                                        + " will not be added to the package's defined key-sets.");
+                                improperKeySets.add(currentKeySet);
+                                XmlUtils.skipCurrentTag(parser);
+                                continue;
+                            }
+                            if (publicKeys.get(publicKeyName) == null
+                                    || publicKeys.get(publicKeyName).equals(currentKey)) {
+
+                                /* public-key first definition, or matches old definition */
+                                publicKeys.put(publicKeyName, currentKey);
+                            } else {
+                                return input.error("Value of 'public-key' " + publicKeyName
+                                        + " conflicts with previously defined value at "
+                                        + parser.getPositionDescription());
+                            }
+                        }
+                        definedKeySets.get(currentKeySet).add(publicKeyName);
+                        XmlUtils.skipCurrentTag(parser);
+                    } finally {
+                        sa.recycle();
+                    }
+                } break;
+                case "upgrade-key-set": {
+                    TypedArray sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestUpgradeKeySet);
+                    try {
+                        String name = sa.getNonResourceString(
+                                R.styleable.AndroidManifestUpgradeKeySet_name);
+                        upgradeKeySets.add(name);
+                        XmlUtils.skipCurrentTag(parser);
+                    } finally {
+                        sa.recycle();
+                    }
+                } break;
+                default:
+                    ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
+                            input);
+                    if (result.isError()) {
+                        return input.error(result);
+                    }
+                    break;
+            }
+        }
+        String packageName = pkg.getPackageName();
+        Set<String> publicKeyNames = publicKeys.keySet();
+        if (publicKeyNames.removeAll(definedKeySets.keySet())) {
+            return input.error("Package" + packageName
+                    + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
+        }
+
+        for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
+            final String keySetName = e.getKey();
+            if (e.getValue().size() == 0) {
+                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+                        + "'key-set' " + keySetName + " has no valid associated 'public-key'."
+                        + " Not including in package's defined key-sets.");
+                continue;
+            } else if (improperKeySets.contains(keySetName)) {
+                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+                        + "'key-set' " + keySetName + " contained improper 'public-key'"
+                        + " tags. Not including in package's defined key-sets.");
+                continue;
+            }
+
+            for (String s : e.getValue()) {
+                pkg.addKeySet(keySetName, publicKeys.get(s));
+            }
+        }
+        if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
+            pkg.setUpgradeKeySets(upgradeKeySets);
+        } else {
+            return input.error("Package" + packageName
+                    + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
+        }
+
+        return input.success(pkg);
+    }
+
+    private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
+                parser, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addAttribution(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
+                pkg, res, parser, sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addPermissionGroup(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
+                pkg, res, parser, sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addPermission(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
+                pkg, res, parser, sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addPermission(result.getResult()));
+    }
+
+    private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String name = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesPermission_name);
+
+            int maxSdkVersion = 0;
+            TypedValue val = sa.peekValue(
+                    R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
+            if (val != null) {
+                if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
+                    maxSdkVersion = val.data;
+                }
+            }
+
+            final ArraySet<String> requiredFeatures = new ArraySet<>();
+            String feature = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature,
+                    0);
+            if (feature != null) {
+                requiredFeatures.add(feature);
+            }
+
+            final ArraySet<String> requiredNotFeatures = new ArraySet<>();
+            feature = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable
+                            .AndroidManifestUsesPermission_requiredNotFeature,
+                    0);
+            if (feature != null) {
+                requiredNotFeatures.add(feature);
+            }
+
+            final int usesPermissionFlags = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_usesPermissionFlags,
+                0);
+
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG
+                    || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                final ParseResult<?> result;
+                switch (parser.getName()) {
+                    case "required-feature":
+                        result = parseRequiredFeature(input, res, parser);
+                        if (result.isSuccess()) {
+                            requiredFeatures.add((String) result.getResult());
+                        }
+                        break;
+
+                    case "required-not-feature":
+                        result = parseRequiredNotFeature(input, res, parser);
+                        if (result.isSuccess()) {
+                            requiredNotFeatures.add((String) result.getResult());
+                        }
+                        break;
+
+                    default:
+                        result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input);
+                        break;
+                }
+
+                if (result.isError()) {
+                    return input.error(result);
+                }
+            }
+
+            // Can only succeed from here on out
+            ParseResult<ParsingPackage> success = input.success(pkg);
+
+            if (name == null) {
+                return success;
+            }
+
+            if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+                return success;
+            }
+
+            if (mCallback != null) {
+                // Only allow requesting this permission if the platform supports all of the
+                // "required-feature"s.
+                for (int i = requiredFeatures.size() - 1; i >= 0; i--) {
+                    if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) {
+                        return success;
+                    }
+                }
+
+                // Only allow requesting this permission if the platform does not supports any of
+                // the "required-not-feature"s.
+                for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) {
+                    if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) {
+                        return success;
+                    }
+                }
+            }
+
+            // Quietly ignore duplicate permission requests, but fail loudly if
+            // the two requests have conflicting flags
+            boolean found = false;
+            final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
+            final int size = usesPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ParsedUsesPermission usesPermission = usesPermissions.get(i);
+                if (Objects.equals(usesPermission.getName(), name)) {
+                    if (usesPermission.getUsesPermissionFlags() != usesPermissionFlags) {
+                        return input.error("Conflicting uses-permissions flags: "
+                                + name + " in package: " + pkg.getPackageName() + " at: "
+                                + parser.getPositionDescription());
+                    } else {
+                        Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+                                + name + " in package: " + pkg.getPackageName() + " at: "
+                                + parser.getPositionDescription());
+                    }
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found) {
+                pkg.addUsesPermission(new ParsedUsesPermissionImpl(name, usesPermissionFlags));
+            }
+            return success;
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res,
+            AttributeSet attrs) {
+        final TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestRequiredFeature);
+        try {
+            final String featureName = sa.getString(
+                    R.styleable.AndroidManifestRequiredFeature_name);
+            return TextUtils.isEmpty(featureName)
+                    ? input.error("Feature name is missing from <required-feature> tag.")
+                    : input.success(featureName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res,
+            AttributeSet attrs) {
+        final TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestRequiredNotFeature);
+        try {
+            final String featureName = sa.getString(
+                    R.styleable.AndroidManifestRequiredNotFeature_name);
+            return TextUtils.isEmpty(featureName)
+                    ? input.error("Feature name is missing from <required-not-feature> tag.")
+                    : input.success(featureName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        ConfigurationInfo cPref = new ConfigurationInfo();
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
+        try {
+            cPref.reqTouchScreen = sa.getInt(
+                    R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
+                    Configuration.TOUCHSCREEN_UNDEFINED);
+            cPref.reqKeyboardType = sa.getInt(
+                    R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
+                    Configuration.KEYBOARD_UNDEFINED);
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
+                    false)) {
+                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+            }
+            cPref.reqNavigation = sa.getInt(
+                    R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
+                    Configuration.NAVIGATION_UNDEFINED);
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
+                    false)) {
+                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+            }
+            pkg.addConfigPreference(cPref);
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        FeatureInfo fi = parseFeatureInfo(res, parser);
+        pkg.addReqFeature(fi);
+
+        if (fi.name == null) {
+            ConfigurationInfo cPref = new ConfigurationInfo();
+            cPref.reqGlEsVersion = fi.reqGlEsVersion;
+            pkg.addConfigPreference(cPref);
+        }
+
+        return input.success(pkg);
+    }
+
+    private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
+        FeatureInfo fi = new FeatureInfo();
+        TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
+            fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
+            if (fi.name == null) {
+                fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
+                        FeatureInfo.GL_ES_VERSION_UNDEFINED);
+            }
+            if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
+                fi.flags |= FeatureInfo.FLAG_REQUIRED;
+            }
+            return fi;
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        FeatureGroupInfo group = new FeatureGroupInfo();
+        ArrayList<FeatureInfo> features = null;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final String innerTagName = parser.getName();
+            if (innerTagName.equals("uses-feature")) {
+                FeatureInfo featureInfo = parseFeatureInfo(res, parser);
+                // FeatureGroups are stricter and mandate that
+                // any <uses-feature> declared are mandatory.
+                featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+                features = ArrayUtils.add(features, featureInfo);
+            } else {
+                Slog.w(TAG,
+                        "Unknown element under <feature-group>: " + innerTagName
+                                + " at " + pkg.getBaseApkPath() + " "
+                                + parser.getPositionDescription());
+            }
+        }
+
+        if (features != null) {
+            group.features = new FeatureInfo[features.size()];
+            group.features = features.toArray(group.features);
+        }
+
+        pkg.addFeatureGroup(group);
+        return input.success(pkg);
+    }
+
+    private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        if (SDK_VERSION > 0) {
+            TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
+            try {
+                int minVers = ParsingUtils.DEFAULT_MIN_SDK_VERSION;
+                String minCode = null;
+                int targetVers = ParsingUtils.DEFAULT_TARGET_SDK_VERSION;
+                String targetCode = null;
+
+                TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+                if (val != null) {
+                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                        minCode = val.string.toString();
+                    } else {
+                        // If it's not a string, it's an integer.
+                        minVers = val.data;
+                    }
+                }
+
+                val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+                if (val != null) {
+                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                        targetCode = val.string.toString();
+                        if (minCode == null) {
+                            minCode = targetCode;
+                        }
+                    } else {
+                        // If it's not a string, it's an integer.
+                        targetVers = val.data;
+                    }
+                } else {
+                    targetVers = minVers;
+                    targetCode = minCode;
+                }
+
+                ParseResult<Integer> targetSdkVersionResult = FrameworkParsingPackageUtils
+                        .computeTargetSdkVersion(targetVers, targetCode, SDK_CODENAMES, input);
+                if (targetSdkVersionResult.isError()) {
+                    return input.error(targetSdkVersionResult);
+                }
+
+                int targetSdkVersion = targetSdkVersionResult.getResult();
+
+                ParseResult<?> deferResult =
+                        input.enableDeferredError(pkg.getPackageName(), targetSdkVersion);
+                if (deferResult.isError()) {
+                    return input.error(deferResult);
+                }
+
+                ParseResult<Integer> minSdkVersionResult = FrameworkParsingPackageUtils
+                        .computeMinSdkVersion(minVers, minCode, SDK_VERSION, SDK_CODENAMES, input);
+                if (minSdkVersionResult.isError()) {
+                    return input.error(minSdkVersionResult);
+                }
+
+                int minSdkVersion = minSdkVersionResult.getResult();
+
+                pkg.setMinSdkVersion(minSdkVersion)
+                        .setTargetSdkVersion(targetSdkVersion);
+
+                int type;
+                final int innerDepth = parser.getDepth();
+                SparseIntArray minExtensionVersions = null;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
+
+                    final ParseResult result;
+                    if (parser.getName().equals("extension-sdk")) {
+                        if (minExtensionVersions == null) {
+                            minExtensionVersions = new SparseIntArray();
+                        }
+                        result = parseExtensionSdk(input, res, parser, minExtensionVersions);
+                        XmlUtils.skipCurrentTag(parser);
+                    } else {
+                        result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
+                    }
+
+                    if (result.isError()) {
+                        return input.error(result);
+                    }
+                }
+                pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
+            } finally {
+                sa.recycle();
+            }
+        }
+        return input.success(pkg);
+    }
+
+    @Nullable
+    private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
+        if (input == null) {
+            return null;
+        }
+        SparseIntArray output = new SparseIntArray(input.size());
+        for (int i = 0; i < input.size(); i++) {
+            output.put(input.keyAt(i), input.valueAt(i));
+        }
+        return output;
+    }
+
+    private static ParseResult<SparseIntArray> parseExtensionSdk(
+            ParseInput input, Resources res, XmlResourceParser parser,
+            SparseIntArray minExtensionVersions) {
+        int sdkVersion;
+        int minVersion;
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
+        try {
+            sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
+            minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
+        } finally {
+            sa.recycle();
+        }
+
+        if (sdkVersion < 0) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "<extension-sdk> must specify an sdkVersion >= 0");
+        }
+        if (minVersion < 0) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "<extension-sdk> must specify minExtensionVersion >= 0");
+        }
+
+        try {
+            int version = SdkExtensions.getExtensionVersion(sdkVersion);
+            if (version < minVersion) {
+                return input.error(
+                        PackageManager.INSTALL_FAILED_OLDER_SDK,
+                        "Package requires " + sdkVersion + " extension version " + minVersion
+                                + " which exceeds device version " + version);
+            }
+        } catch (RuntimeException e) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Specified sdkVersion " + sdkVersion + " is not valid");
+        }
+        minExtensionVersions.put(sdkVersion, minVersion);
+        return input.success(minExtensionVersions);
+    }
+
+    private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
+            TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
+            try {
+                final String hash = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestRestrictUpdate_hash,
+                        0);
+
+                if (hash != null) {
+                    final int hashLength = hash.length();
+                    final byte[] hashBytes = new byte[hashLength / 2];
+                    for (int i = 0; i < hashLength; i += 2) {
+                        hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
+                                << 4)
+                                + Character.digit(hash.charAt(i + 1), 16));
+                    }
+                    pkg.setRestrictUpdateHash(hashBytes);
+                } else {
+                    pkg.setRestrictUpdateHash(null);
+                }
+            } finally {
+                sa.recycle();
+            }
+        }
+        return input.success(pkg);
+    }
+
+    private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (parser.getName().equals("intent")) {
+                ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(
+                        null /*className*/, pkg, res, parser, true /*allowGlobs*/,
+                        true /*allowAutoVerify*/, input);
+                if (result.isError()) {
+                    return input.error(result);
+                }
+
+                IntentFilter intentInfo = result.getResult().getIntentFilter();
+
+                Uri data = null;
+                String dataType = null;
+                String host = null;
+                final int numActions = intentInfo.countActions();
+                final int numSchemes = intentInfo.countDataSchemes();
+                final int numTypes = intentInfo.countDataTypes();
+                final int numHosts = intentInfo.getHosts().length;
+                if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
+                    return input.error("intent tags must contain either an action or data.");
+                }
+                if (numActions > 1) {
+                    return input.error("intent tag may have at most one action.");
+                }
+                if (numTypes > 1) {
+                    return input.error("intent tag may have at most one data type.");
+                }
+                if (numSchemes > 1) {
+                    return input.error("intent tag may have at most one data scheme.");
+                }
+                if (numHosts > 1) {
+                    return input.error("intent tag may have at most one data host.");
+                }
+                Intent intent = new Intent();
+                for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+                    intent.addCategory(intentInfo.getCategory(i));
+                }
+                if (numHosts == 1) {
+                    host = intentInfo.getHosts()[0];
+                }
+                if (numSchemes == 1) {
+                    data = new Uri.Builder()
+                            .scheme(intentInfo.getDataScheme(0))
+                            .authority(host)
+                            .path(IntentFilter.WILDCARD_PATH)
+                            .build();
+                }
+                if (numTypes == 1) {
+                    dataType = intentInfo.getDataType(0);
+                    // The dataType may have had the '/' removed for the dynamic mimeType feature.
+                    // If we detect that case, we add the * back.
+                    if (!dataType.contains("/")) {
+                        dataType = dataType + "/*";
+                    }
+                    if (data == null) {
+                        data = new Uri.Builder()
+                                .scheme("content")
+                                .authority(IntentFilter.WILDCARD)
+                                .path(IntentFilter.WILDCARD_PATH)
+                                .build();
+                    }
+                }
+                intent.setDataAndType(data, dataType);
+                if (numActions == 1) {
+                    intent.setAction(intentInfo.getAction(0));
+                }
+                pkg.addQueriesIntent(intent);
+            } else if (parser.getName().equals("package")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestQueriesPackage);
+                final String packageName = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestQueriesPackage_name, 0);
+                if (TextUtils.isEmpty(packageName)) {
+                    return input.error("Package name is missing from package tag.");
+                }
+                pkg.addQueriesPackage(packageName.intern());
+            } else if (parser.getName().equals("provider")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestQueriesProvider);
+                try {
+                    final String authorities = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestQueriesProvider_authorities, 0);
+                    if (TextUtils.isEmpty(authorities)) {
+                        return input.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Authority missing from provider tag."
+                        );
+                    }
+                    StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
+                    while (authoritiesTokenizer.hasMoreElements()) {
+                        pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
+                    }
+                } finally {
+                    sa.recycle();
+                }
+            }
+        }
+        return input.success(pkg);
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>base APK</em> manifest.
+     * <p>
+     * When adding new features, carefully consider if they should also be supported by split APKs.
+     * <p>
+     * This method should avoid using a getter for fields set by this method. Prefer assigning a
+     * local variable and using it. Otherwise there's an ordering problem which can be broken if any
+     * code moves around.
+     */
+    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
+            throws XmlPullParserException, IOException {
+        final String pkgName = pkg.getPackageName();
+        int targetSdk = pkg.getTargetSdkVersion();
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+        try {
+            // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
+            // This case can only happen in unit tests where we sometimes need to create fakes
+            // of various package parser data structures.
+            if (sa == null) {
+                return input.error("<application> does not contain any attributes");
+            }
+
+            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
+                    0);
+            if (name != null) {
+                String packageName = pkg.getPackageName();
+                String outInfoName = ParsingUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
+                    return input.error("<application> invalid android:name");
+                } else if (outInfoName == null) {
+                    return input.error("Empty class name in package " + packageName);
+                }
+
+                pkg.setClassName(outInfoName);
+            }
+
+            TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
+            if (labelValue != null) {
+                pkg.setLabelRes(labelValue.resourceId);
+                if (labelValue.resourceId == 0) {
+                    pkg.setNonLocalizedLabel(labelValue.coerceToString());
+                }
+            }
+
+            parseBaseAppBasicFlags(pkg, sa);
+
+            String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
+                    R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
+            if (manageSpaceActivity != null) {
+                String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
+                        manageSpaceActivity);
+
+                if (manageSpaceActivityName == null) {
+                    return input.error("Empty class name in package " + pkgName);
+                }
+
+                pkg.setManageSpaceActivityName(manageSpaceActivityName);
+            }
+
+            if (pkg.isAllowBackup()) {
+                // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
+                // and restoreAnyVersion are only relevant if backup is possible for the
+                // given application.
+                String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
+                        R.styleable.AndroidManifestApplication_backupAgent, sa);
+                if (backupAgent != null) {
+                    String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
+                    if (backupAgentName == null) {
+                        return input.error("Empty class name in package " + pkgName);
+                    }
+
+                    if (DEBUG_BACKUP) {
+                        Slog.v(TAG, "android:backupAgent = " + backupAgentName
+                                + " from " + pkgName + "+" + backupAgent);
+                    }
+
+                    pkg.setBackupAgentName(backupAgentName)
+                            .setKillAfterRestore(bool(true,
+                                    R.styleable.AndroidManifestApplication_killAfterRestore, sa))
+                            .setRestoreAnyVersion(bool(false,
+                                    R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
+                            .setFullBackupOnly(bool(false,
+                                    R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
+                            .setBackupInForeground(bool(false,
+                                    R.styleable.AndroidManifestApplication_backupInForeground, sa));
+                }
+
+                TypedValue v = sa.peekValue(
+                        R.styleable.AndroidManifestApplication_fullBackupContent);
+                int fullBackupContent = 0;
+
+                if (v != null) {
+                    fullBackupContent = v.resourceId;
+
+                    if (v.resourceId == 0) {
+                        if (DEBUG_BACKUP) {
+                            Slog.v(TAG, "fullBackupContent specified as boolean=" +
+                                    (v.data == 0 ? "false" : "true"));
+                        }
+                        // "false" => -1, "true" => 0
+                        fullBackupContent = v.data == 0 ? -1 : 0;
+                    }
+
+                    pkg.setFullBackupContent(fullBackupContent);
+                }
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
+                }
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
+                // Check if persistence is based on a feature being present
+                final String requiredFeature = sa.getNonResourceString(R.styleable
+                        .AndroidManifestApplication_persistentWhenFeatureAvailable);
+                pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
+            }
+
+            if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
+                pkg.setResizeableActivity(sa.getBoolean(
+                        R.styleable.AndroidManifestApplication_resizeableActivity, true));
+            } else {
+                pkg.setResizeableActivityViaSdkVersion(
+                        targetSdk >= Build.VERSION_CODES.N);
+            }
+
+            String taskAffinity;
+            if (targetSdk >= Build.VERSION_CODES.FROYO) {
+                taskAffinity = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_taskAffinity,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                taskAffinity = sa.getNonResourceString(
+                        R.styleable.AndroidManifestApplication_taskAffinity);
+            }
+
+            ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
+                    pkgName, pkgName, taskAffinity, input);
+            if (taskAffinityResult.isError()) {
+                return input.error(taskAffinityResult);
+            }
+
+            pkg.setTaskAffinity(taskAffinityResult.getResult());
+            String factory = sa.getNonResourceString(
+                    R.styleable.AndroidManifestApplication_appComponentFactory);
+            if (factory != null) {
+                String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
+                if (appComponentFactory == null) {
+                    return input.error("Empty class name in package " + pkgName);
+                }
+
+                pkg.setAppComponentFactory(appComponentFactory);
+            }
+
+            CharSequence pname;
+            if (targetSdk >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(
+                        R.styleable.AndroidManifestApplication_process);
+            }
+            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+                    pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
+            if (processNameResult.isError()) {
+                return input.error(processNameResult);
+            }
+
+            String processName = processNameResult.getResult();
+            pkg.setProcessName(processName);
+
+            if (pkg.isCantSaveState()) {
+                // A heavy-weight application can not be in a custom process.
+                // We can do direct compare because we intern all strings.
+                if (processName != null && !processName.equals(pkgName)) {
+                    return input.error(
+                            "cantSaveState applications can not use custom processes");
+                }
+            }
+
+            String classLoaderName = pkg.getClassLoaderName();
+            if (classLoaderName != null
+                    && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+                return input.error("Invalid class loader name: " + classLoaderName);
+            }
+
+            pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
+            pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
+            if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
+                final boolean v = sa.getBoolean(
+                        R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
+                pkg.setNativeHeapZeroInitialized(
+                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
+            }
+            if (sa.hasValue(
+                    R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
+                pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
+                                .AndroidManifestApplication_requestRawExternalStorageAccess,
+                        false));
+            }
+            if (sa.hasValue(
+                    R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) {
+                pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable
+                                .AndroidManifestApplication_requestForegroundServiceExemption,
+                        false));
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        boolean hasActivityOrder = false;
+        boolean hasReceiverOrder = false;
+        boolean hasServiceOrder = false;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String tagName = parser.getName();
+            boolean isActivity = false;
+            switch (tagName) {
+                case "activity":
+                    isActivity = true;
+                    // fall-through
+                case "receiver":
+                    ParseResult<ParsedActivity> activityResult =
+                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
+                                    res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
+                                    input);
+
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        if (isActivity) {
+                            hasActivityOrder |= (activity.getOrder() != 0);
+                            pkg.addActivity(activity);
+                        } else {
+                            hasReceiverOrder |= (activity.getOrder() != 0);
+                            pkg.addReceiver(activity);
+                        }
+                    }
+
+                    result = activityResult;
+                    break;
+                case "service":
+                    ParseResult<ParsedService> serviceResult =
+                            ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
+                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
+                                    input);
+                    if (serviceResult.isSuccess()) {
+                        ParsedService service = serviceResult.getResult();
+                        hasServiceOrder |= (service.getOrder() != 0);
+                        pkg.addService(service);
+                    }
+
+                    result = serviceResult;
+                    break;
+                case "provider":
+                    ParseResult<ParsedProvider> providerResult =
+                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
+                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
+                                    input);
+                    if (providerResult.isSuccess()) {
+                        pkg.addProvider(providerResult.getResult());
+                    }
+
+                    result = providerResult;
+                    break;
+                case "activity-alias":
+                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
+                            parser, sUseRoundIcon, null /*defaultSplitName*/,
+                            input);
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        hasActivityOrder |= (activity.getOrder() != 0);
+                        pkg.addActivity(activity);
+                    }
+
+                    result = activityResult;
+                    break;
+                case "apex-system-service":
+                    ParseResult<ParsedApexSystemService> systemServiceResult =
+                            ParsedApexSystemServiceUtils.parseApexSystemService(res,
+                                    parser, input);
+                    if (systemServiceResult.isSuccess()) {
+                        ParsedApexSystemService systemService =
+                                systemServiceResult.getResult();
+                        pkg.addApexSystemService(systemService);
+                    }
+
+                    result = systemServiceResult;
+                    break;
+                default:
+                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty(
+                pkg.getSdkLibName())) {
+            // Add a hidden app detail activity to normal apps which forwards user to App Details
+            // page.
+            ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
+            if (a.isError()) {
+                // Error should be impossible here, as the only failure case as of SDK R is a
+                // string validation error on a constant ":app_details" string passed in by the
+                // parsing code itself. For this reason, this is just a hard failure instead of
+                // deferred.
+                return input.error(a);
+            }
+
+            pkg.addActivity(a.getResult());
+        }
+
+        if (hasActivityOrder) {
+            pkg.sortActivities();
+        }
+        if (hasReceiverOrder) {
+            pkg.sortReceivers();
+        }
+        if (hasServiceOrder) {
+            pkg.sortServices();
+        }
+
+        // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
+        // every activity info has had a chance to set it from its attributes.
+        setMaxAspectRatio(pkg);
+        setMinAspectRatio(pkg);
+        setSupportsSizeChanges(pkg);
+
+        pkg.setHasDomainUrls(hasDomainURLs(pkg));
+
+        return input.success(pkg);
+    }
+
+    /**
+     * Collection of single-line, no (or little) logic assignments. Separated for readability.
+     * <p>
+     * Flags are separated by type and by default value. They are sorted alphabetically within each
+     * section.
+     */
+    private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
+        int targetSdk = pkg.getTargetSdkVersion();
+        //@formatter:off
+        // CHECKSTYLE:off
+        pkg
+                // Default true
+                .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
+                .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
+                .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
+                .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
+                .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
+                .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
+                .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
+                // Default false
+                .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
+                .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
+                .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa))
+                .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
+                .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
+                .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
+                .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
+                .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
+                .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
+                .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
+                .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
+                .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
+                .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
+                .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
+                .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
+                .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
+                .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
+                .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
+                .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
+                .setAttributionsAreUserVisible(bool(false, R.styleable.AndroidManifestApplication_attributionsAreUserVisible, sa))
+                .setResetEnabledSettingsOnAppDataCleared(bool(false,
+                    R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
+                    sa))
+                // targetSdkVersion gated
+                .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
+                .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
+                .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
+                .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
+                // Ints Default 0
+                .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
+                // Ints
+                .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
+                // Floats Default 0f
+                .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
+                .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
+                // Resource ID
+                .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
+                .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
+                .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))
+                .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
+                .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
+                .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
+                .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
+                .setDataExtractionRules(
+                        resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa))
+                .setLocaleConfigRes(resId(R.styleable.AndroidManifestApplication_localeConfig, sa))
+                // Strings
+                .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
+                .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
+                .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
+                .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
+                // Non-Config String
+                .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
+        // CHECKSTYLE:on
+        //@formatter:on
+    }
+
+    /**
+     * For parsing non-MainComponents. Main ones have an order and some special handling which is
+     * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
+     * XmlResourceParser, int)}.
+     */
+    private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, int flags)
+            throws IOException, XmlPullParserException {
+        switch (tag) {
+            case "meta-data":
+                // TODO(b/135203078): I have no idea what this comment means
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                final ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<meta-data>", input);
+                if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
+                    pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
+                }
+                return metaDataResult;
+            case "property":
+                final ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<property>", input);
+                if (propertyResult.isSuccess()) {
+                    pkg.addProperty(propertyResult.getResult());
+                }
+                return propertyResult;
+            case "sdk-library":
+                return parseSdkLibrary(pkg, res, parser, input);
+            case "static-library":
+                return parseStaticLibrary(pkg, res, parser, input);
+            case "library":
+                return parseLibrary(pkg, res, parser, input);
+            case "uses-sdk-library":
+                return parseUsesSdkLibrary(input, pkg, res, parser);
+            case "uses-static-library":
+                return parseUsesStaticLibrary(input, pkg, res, parser);
+            case "uses-library":
+                return parseUsesLibrary(input, pkg, res, parser);
+            case "uses-native-library":
+                return parseUsesNativeLibrary(input, pkg, res, parser);
+            case "processes":
+                return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
+            case "uses-package":
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                return input.success(null);
+            case "profileable":
+                return parseProfileable(input, pkg, res, parser);
+            default:
+                return ParsingUtils.unknownTag("<application>", pkg, parser, input);
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseSdkLibrary(
+            ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSdkLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestSdkLibrary_name);
+            final int versionMajor = sa.getInt(
+                    R.styleable.AndroidManifestSdkLibrary_versionMajor,
+                    -1);
+
+            // Fail if malformed.
+            if (lname == null || versionMajor < 0) {
+                return input.error("Bad sdk-library declaration name: " + lname
+                        + " version: " + versionMajor);
+            } else if (pkg.getSharedUserId() != null) {
+                return input.error(
+                        PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                        "sharedUserId not allowed in SDK library"
+                );
+            } else if (pkg.getSdkLibName() != null) {
+                return input.error("Multiple SDKs for package "
+                        + pkg.getPackageName());
+            }
+
+            return input.success(pkg.setSdkLibName(lname.intern())
+                    .setSdkLibVersionMajor(versionMajor)
+                    .setSdkLibrary(true));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseStaticLibrary(
+            ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestStaticLibrary_name);
+            final int version = sa.getInt(
+                    R.styleable.AndroidManifestStaticLibrary_version, -1);
+            final int versionMajor = sa.getInt(
+                    R.styleable.AndroidManifestStaticLibrary_versionMajor,
+                    0);
+
+            // Since the app canot run without a static lib - fail if malformed
+            if (lname == null || version < 0) {
+                return input.error("Bad static-library declaration name: " + lname
+                        + " version: " + version);
+            } else if (pkg.getSharedUserId() != null) {
+                return input.error(
+                        PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                        "sharedUserId not allowed in static shared library"
+                );
+            } else if (pkg.getStaticSharedLibName() != null) {
+                return input.error("Multiple static-shared libs for package "
+                        + pkg.getPackageName());
+            }
+
+            return input.success(pkg.setStaticSharedLibName(lname.intern())
+                    .setStaticSharedLibVersion(
+                            PackageInfo.composeLongVersionCode(versionMajor, version))
+                    .setStaticSharedLibrary(true));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseLibrary(
+            ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
+
+            if (lname != null) {
+                lname = lname.intern();
+                if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
+                    pkg.addLibraryName(lname);
+                }
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseUsesSdkLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdkLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesSdkLibrary_name);
+            final int versionMajor = sa.getInt(
+                    R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
+            String certSha256Digest = sa.getNonResourceString(R.styleable
+                    .AndroidManifestUsesSdkLibrary_certDigest);
+
+            // Since an APK providing a static shared lib can only provide the lib - fail if
+            // malformed
+            if (lname == null || versionMajor < 0 || certSha256Digest == null) {
+                return input.error("Bad uses-sdk-library declaration name: " + lname
+                        + " version: " + versionMajor + " certDigest" + certSha256Digest);
+            }
+
+            // Can depend only on one version of the same library
+            List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
+            if (usesSdkLibraries.contains(lname)) {
+                return input.error(
+                        "Depending on multiple versions of SDK library " + lname);
+            }
+
+            lname = lname.intern();
+            // We allow ":" delimiters in the SHA declaration as this is the format
+            // emitted by the certtool making it easy for developers to copy/paste.
+            certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+            if ("".equals(certSha256Digest)) {
+                // Test-only uses-sdk-library empty certificate digest override.
+                certSha256Digest = SystemProperties.get(
+                        "debug.pm.uses_sdk_library_default_cert_digest", "");
+                // Validate the overridden digest.
+                try {
+                    HexEncoding.decode(certSha256Digest, false);
+                } catch (IllegalArgumentException e) {
+                    certSha256Digest = "";
+                }
+            }
+
+            ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+            if (certResult.isError()) {
+                return input.error(certResult);
+            }
+            String[] additionalCertSha256Digests = certResult.getResult();
+
+            final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+            certSha256Digests[0] = certSha256Digest;
+            System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+                    1, additionalCertSha256Digests.length);
+
+            return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesLibrary_name);
+            final int version = sa.getInt(
+                    R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
+            String certSha256Digest = sa.getNonResourceString(R.styleable
+                    .AndroidManifestUsesStaticLibrary_certDigest);
+
+            // Since an APK providing a static shared lib can only provide the lib - fail if
+            // malformed
+            if (lname == null || version < 0 || certSha256Digest == null) {
+                return input.error("Bad uses-static-library declaration name: " + lname
+                        + " version: " + version + " certDigest" + certSha256Digest);
+            }
+
+            // Can depend only on one version of the same library
+            List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+            if (usesStaticLibraries.contains(lname)) {
+                return input.error(
+                        "Depending on multiple versions of static library " + lname);
+            }
+
+            lname = lname.intern();
+            // We allow ":" delimiters in the SHA declaration as this is the format
+            // emitted by the certtool making it easy for developers to copy/paste.
+            certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+            // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
+            String[] additionalCertSha256Digests = EmptyArray.STRING;
+            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
+                ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+                if (certResult.isError()) {
+                    return input.error(certResult);
+                }
+                additionalCertSha256Digests = certResult.getResult();
+            }
+
+            final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+            certSha256Digests[0] = certSha256Digest;
+            System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+                    1, additionalCertSha256Digests.length);
+
+            return input.success(pkg.addUsesStaticLibrary(lname, version, certSha256Digests));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
+            boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
+
+            if (lname != null) {
+                lname = lname.intern();
+                if (req) {
+                    // Upgrade to treat as stronger constraint
+                    pkg.addUsesLibrary(lname)
+                            .removeUsesOptionalLibrary(lname);
+                } else {
+                    // Ignore if someone already defined as required
+                    if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
+                        pkg.addUsesOptionalLibrary(lname);
+                    }
+                }
+            }
+
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesNativeLibrary_name);
+            boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
+                    true);
+
+            if (lname != null) {
+                if (req) {
+                    // Upgrade to treat as stronger constraint
+                    pkg.addUsesNativeLibrary(lname)
+                            .removeUsesOptionalNativeLibrary(lname);
+                } else {
+                    // Ignore if someone already defined as required
+                    if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
+                        pkg.addUsesOptionalNativeLibrary(lname);
+                    }
+                }
+            }
+
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
+            throws IOException, XmlPullParserException {
+        ParseResult<ArrayMap<String, ParsedProcess>> result =
+                ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
+                        input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(pkg.setProcesses(result.getResult()));
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
+        try {
+            ParsingPackage newPkg = pkg.setProfileableByShell(pkg.isProfileableByShell()
+                    || bool(false, R.styleable.AndroidManifestProfileable_shell, sa));
+            return input.success(newPkg.setProfileable(newPkg.isProfileable()
+                    && bool(true, R.styleable.AndroidManifestProfileable_enabled, sa)));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
+            Resources resources, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        String[] certSha256Digests = EmptyArray.STRING;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final String nodeName = parser.getName();
+            if (nodeName.equals("additional-certificate")) {
+                TypedArray sa = resources.obtainAttributes(parser,
+                        R.styleable.AndroidManifestAdditionalCertificate);
+                try {
+                    String certSha256Digest = sa.getNonResourceString(
+                            R.styleable.AndroidManifestAdditionalCertificate_certDigest);
+
+                    if (TextUtils.isEmpty(certSha256Digest)) {
+                        return input.error("Bad additional-certificate declaration with empty"
+                                + " certDigest:" + certSha256Digest);
+                    }
+
+
+                    // We allow ":" delimiters in the SHA declaration as this is the format
+                    // emitted by the certtool making it easy for developers to copy/paste.
+                    certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+                    certSha256Digests = ArrayUtils.appendElement(String.class,
+                            certSha256Digests, certSha256Digest);
+                } finally {
+                    sa.recycle();
+                }
+            }
+        }
+
+        return input.success(certSha256Digests);
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    @NonNull
+    private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
+            ParsingPackage pkg) {
+        String packageName = pkg.getPackageName();
+        ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName(
+                packageName, packageName, ":app_details", input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        String taskAffinity = result.getResult();
+
+        // Build custom App Details activity info instead of parsing it from xml
+        return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
+                pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
+                pkg.isBaseHardwareAccelerated()));
+    }
+
+    /**
+     * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+     *
+     * This is distinct from any of the functionality of app links domain verification, and cannot
+     * be converted to remain backwards compatible. It's possible the presence of this flag does
+     * not indicate a valid package for domain verification.
+     */
+    private static boolean hasDomainURLs(ParsingPackage pkg) {
+        final List<ParsedActivity> activities = pkg.getActivities();
+        final int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            List<ParsedIntentInfo> filters = activity.getIntents();
+            final int filtersSize = filters.size();
+            for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
+                IntentFilter aii = filters.get(filtersIndex).getIntentFilter();
+                if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
+                if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
+                if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                        aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the max aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private static void setMaxAspectRatio(ParsingPackage pkg) {
+        // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
+        // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
+        float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+
+        float packageMaxAspectRatio = pkg.getMaxAspectRatio();
+        if (packageMaxAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            maxAspectRatio = packageMaxAspectRatio;
+        } else {
+            Bundle appMetaData = pkg.getMetaData();
+            if (appMetaData != null && appMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
+                maxAspectRatio = appMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
+            }
+        }
+
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            // If the max aspect ratio for the activity has already been set, skip.
+            if (activity.getMaxAspectRatio() != ASPECT_RATIO_NOT_SET) {
+                continue;
+            }
+
+            // By default we prefer to use a values defined on the activity directly than values
+            // defined on the application. We do not check the styled attributes on the activity
+            // as it would have already been set when we processed the activity. We wait to
+            // process the meta data here since this method is called at the end of processing
+            // the application and all meta data is guaranteed.
+            final float activityAspectRatio = activity.getMetaData() != null
+                    ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
+                    : maxAspectRatio;
+
+            ComponentMutateUtils.setMaxAspectRatio(activity, activity.getResizeMode(),
+                    activityAspectRatio);
+        }
+    }
+
+    /**
+     * Sets the min aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private void setMinAspectRatio(ParsingPackage pkg) {
+        // Use the application max aspect ration as default if set.
+        final float minAspectRatio = pkg.getMinAspectRatio();
+
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            if (activity.getMinAspectRatio() == ASPECT_RATIO_NOT_SET) {
+                ComponentMutateUtils.setMinAspectRatio(activity, activity.getResizeMode(),
+                        minAspectRatio);
+            }
+        }
+    }
+
+    private void setSupportsSizeChanges(ParsingPackage pkg) {
+        final Bundle appMetaData = pkg.getMetaData();
+        final boolean supportsSizeChanges = appMetaData != null
+                && appMetaData.getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false);
+
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            if (supportsSizeChanges || (activity.getMetaData() != null
+                    && activity.getMetaData().getBoolean(
+                            METADATA_SUPPORTS_SIZE_CHANGES, false))) {
+                ComponentMutateUtils.setSupportsSizeChanges(activity, true);
+            }
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
+        try {
+            String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
+            int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
+
+            if (target == null) {
+                return input.error("<overlay> does not specify a target package");
+            } else if (priority < 0 || priority > 9999) {
+                return input.error("<overlay> priority must be between 0 and 9999");
+            }
+
+            // check to see if overlay should be excluded based on system property condition
+            String propName = sa.getString(
+                    R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
+            String propValue = sa.getString(
+                    R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
+            if (!FrameworkParsingPackageUtils.checkRequiredSystemProperties(propName, propValue)) {
+                String message = "Skipping target and overlay pair " + target + " and "
+                        + pkg.getBaseApkPath()
+                        + ": overlay ignored due to required system property: "
+                        + propName + " with value: " + propValue;
+                Slog.i(TAG, message);
+                return input.skip(message);
+            }
+
+            return input.success(pkg.setOverlay(true)
+                    .setOverlayTarget(target)
+                    .setOverlayPriority(priority)
+                    .setOverlayTargetOverlayableName(
+                            sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
+                    .setOverlayCategory(
+                            sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
+                    .setOverlayIsStatic(
+                            bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
+            if (name != null) {
+                pkg.addProtectedBroadcast(name);
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
+        try {
+            int requiresSmallestWidthDp = anInt(0,
+                    R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
+            int compatibleWidthLimitDp = anInt(0,
+                    R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
+            int largestWidthLimitDp = anInt(0,
+                    R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
+
+            // This is a trick to get a boolean and still able to detect
+            // if a value was actually set.
+            return input.success(pkg
+                    .setSupportsSmallScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
+                    .setSupportsNormalScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
+                    .setSupportsLargeScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
+                    .setSupportsExtraLargeScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
+                    .setResizeable(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
+                    .setAnyDensity(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
+                    .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
+                    .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
+                    .setLargestWidthLimitDp(largestWidthLimitDp));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
+                pkg, res, parser, sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addInstrumentation(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+        try {
+            String orig = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestOriginalPackage_name,
+                    0);
+            if (!pkg.getPackageName().equals(orig)) {
+                pkg.addOriginalPackage(orig);
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+        try {
+            String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
+            if (name != null) {
+                pkg.addAdoptPermission(name);
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static void convertCompatPermissions(ParsingPackage pkg) {
+        for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+            final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+            if (pkg.getTargetSdkVersion() >= info.getSdkVersion()) {
+                break;
+            }
+            if (!pkg.getRequestedPermissions().contains(info.getName())) {
+                pkg.addImplicitPermission(info.getName());
+            }
+        }
+    }
+
+    private void convertSplitPermissions(ParsingPackage pkg) {
+        final int listSize = mSplitPermissionInfos.size();
+        for (int is = 0; is < listSize; is++) {
+            final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
+            List<String> requestedPermissions = pkg.getRequestedPermissions();
+            if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
+                    || !requestedPermissions.contains(spi.getSplitPermission())) {
+                continue;
+            }
+            final List<String> newPerms = spi.getNewPermissions();
+            for (int in = 0; in < newPerms.size(); in++) {
+                final String perm = newPerms.get(in);
+                if (!requestedPermissions.contains(perm)) {
+                    pkg.addImplicitPermission(perm);
+                }
+            }
+        }
+    }
+
+    /**
+     * This is a pre-density application which will get scaled - instead of being pixel perfect.
+     * This type of application is not resizable.
+     *
+     * @param pkg The package which needs to be marked as unresizable.
+     */
+    private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            ComponentMutateUtils.setResizeMode(activity, RESIZE_MODE_UNRESIZEABLE);
+            ComponentMutateUtils.setExactFlags(activity,
+                    activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
+        }
+    }
+
+    /**
+     * Parse a meta data defined on the enclosing tag.
+     * <p>Meta data can be defined by either &lt;meta-data&gt; or &lt;property&gt; elements.
+     */
+    public static ParseResult<Property> parseMetaData(ParsingPackage pkg, ParsedComponent component,
+            Resources res, XmlResourceParser parser, String tagName, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
+        try {
+            final Property property;
+            final String name = TextUtils.safeIntern(
+                    nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
+            if (name == null) {
+                return input.error(tagName + " requires an android:name attribute");
+            }
+
+            final String packageName = pkg.getPackageName();
+            final String className = component != null ? component.getName() : null;
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
+            if (v != null && v.resourceId != 0) {
+                property = new Property(name, v.resourceId, true, packageName, className);
+            } else {
+                v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
+                if (v != null) {
+                    if (v.type == TypedValue.TYPE_STRING) {
+                        final CharSequence cs = v.coerceToString();
+                        final String stringValue = cs != null ? cs.toString() : null;
+                        property = new Property(name, stringValue, packageName, className);
+                    } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+                        property = new Property(name, v.data != 0, packageName, className);
+                    } else if (v.type >= TypedValue.TYPE_FIRST_INT
+                            && v.type <= TypedValue.TYPE_LAST_INT) {
+                        property = new Property(name, v.data, false, packageName, className);
+                    } else if (v.type == TypedValue.TYPE_FLOAT) {
+                        property = new Property(name, v.getFloat(), packageName, className);
+                    } else {
+                        if (!RIGID_PARSER) {
+                            Slog.w(TAG,
+                                    tagName + " only supports string, integer, float, color, "
+                                            + "boolean, and resource reference types: "
+                                            + parser.getName() + " at "
+                                            + pkg.getBaseApkPath() + " "
+                                            + parser.getPositionDescription());
+                            property = null;
+                        } else {
+                            return input.error(tagName + " only supports string, integer, float, "
+                                    + "color, boolean, and resource reference types");
+                        }
+                    }
+                } else {
+                    return input.error(tagName + " requires an android:value "
+                            + "or android:resource attribute");
+                }
+            }
+            return input.success(property);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    /**
+     * Collect certificates from all the APKs described in the given package. Also asserts that
+     * all APK contents are signed correctly and consistently.
+     *
+     * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
+     *  call if requested. Leaving this as an optional method for the caller means we have to
+     *  construct a dummy ParseInput.
+     */
+    @CheckResult
+    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
+            ParsingPackageRead pkg, boolean skipVerify) {
+        SigningDetails signingDetails = SigningDetails.UNKNOWN;
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+        try {
+            ParseResult<SigningDetails> result = getSigningDetails(
+                    input,
+                    pkg.getBaseApkPath(),
+                    skipVerify,
+                    pkg.isStaticSharedLibrary(),
+                    signingDetails,
+                    pkg.getTargetSdkVersion()
+            );
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            signingDetails = result.getResult();
+
+            String[] splitCodePaths = pkg.getSplitCodePaths();
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                for (int i = 0; i < splitCodePaths.length; i++) {
+                    result = getSigningDetails(
+                            input,
+                            splitCodePaths[i],
+                            skipVerify,
+                            pkg.isStaticSharedLibrary(),
+                            signingDetails,
+                            pkg.getTargetSdkVersion()
+                    );
+                    if (result.isError()) {
+                        return input.error(result);
+                    }
+                }
+            }
+            return result;
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    @CheckResult
+    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
+            String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
+            @NonNull SigningDetails existingSigningDetails, int targetSdk) {
+        int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+                targetSdk);
+        if (isStaticSharedLibrary) {
+            // must use v2 signing scheme
+            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+        }
+        final ParseResult<SigningDetails> verified;
+        if (skipVerify) {
+            // systemDir APKs are already trusted, save time by not verifying; since the
+            // signature is not verified and some system apps can have their V2+ signatures
+            // stripped allow pulling the certs from the jar signature.
+            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
+                    SigningDetails.SignatureSchemeVersion.JAR);
+        } else {
+            verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
+        }
+
+        if (verified.isError()) {
+            return input.error(verified);
+        }
+
+        // Verify that entries are signed consistently with the first pkg
+        // we encountered. Note that for splits, certificates may have
+        // already been populated during an earlier parse of a base APK.
+        if (existingSigningDetails == SigningDetails.UNKNOWN) {
+            return verified;
+        } else {
+            if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
+                    verified.getResult().getSignatures())) {
+                return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                        baseCodePath + " has mismatched certificates");
+            }
+
+            return input.success(existingSigningDetails);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
+        sCompatibilityModeEnabled = compatibilityModeEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    public static void readConfigUseRoundIcon(Resources r) {
+        if (r != null) {
+            sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+            return;
+        }
+
+        final ApplicationInfo androidAppInfo;
+        try {
+            androidAppInfo = ActivityThread.getPackageManager().getApplicationInfo(
+                    "android", 0 /* flags */,
+                    UserHandle.myUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        final Resources systemResources = Resources.getSystem();
+
+        // Create in-flight as this overlayable resource is only used when config changes
+        final Resources overlayableRes = ResourcesManager.getInstance().getResources(
+                null /* activityToken */,
+                null /* resDir */,
+                null /* splitResDirs */,
+                androidAppInfo.resourceDirs,
+                androidAppInfo.overlayPaths,
+                androidAppInfo.sharedLibraryFiles,
+                null /* overrideDisplayId */,
+                null /* overrideConfig */,
+                systemResources.getCompatibilityInfo(),
+                systemResources.getClassLoader(),
+                null /* loaders */);
+
+        sUseRoundIcon = overlayableRes.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+    }
+
+    /*
+     The following set of methods makes code easier to read by re-ordering the TypedArray methods.
+
+     The first parameter is the default, which is the most important to understand for someone
+     reading through the parsing code.
+
+     That's followed by the attribute name, which is usually irrelevant during reading because
+     it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
+     the "setSomeValue" part is enough to communicate what the line does.
+
+     Last comes the TypedArray, which is by far the least important since each try-with-resources
+     should only have 1.
+    */
+
+    // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
+    // is important to specify when adding an attribute.
+    private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getBoolean(attribute, defaultValue);
+    }
+
+    private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getFloat(attribute, defaultValue);
+    }
+
+    private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getFloat(attribute, 0f);
+    }
+
+    private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getInt(attribute, defaultValue);
+    }
+
+    private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getInteger(attribute, defaultValue);
+    }
+
+    private static int anInt(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getInt(attribute, 0);
+    }
+
+    @AnyRes
+    private static int resId(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getResourceId(attribute, 0);
+    }
+
+    private static String string(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getString(attribute);
+    }
+
+    private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
+            TypedArray sa) {
+        return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
+    }
+
+    private static String nonResString(@StyleableRes int index, TypedArray sa) {
+        return sa.getNonResourceString(index);
+    }
+
+    /**
+     * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
+     */
+    public static void writeKeySetMapping(@NonNull Parcel dest,
+            @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
+        if (keySetMapping == null) {
+            dest.writeInt(-1);
+            return;
+        }
+
+        final int N = keySetMapping.size();
+        dest.writeInt(N);
+
+        for (String key : keySetMapping.keySet()) {
+            dest.writeString(key);
+            ArraySet<PublicKey> keys = keySetMapping.get(key);
+            if (keys == null) {
+                dest.writeInt(-1);
+                continue;
+            }
+
+            final int M = keys.size();
+            dest.writeInt(M);
+            for (int j = 0; j < M; j++) {
+                dest.writeSerializable(keys.valueAt(j));
+            }
+        }
+    }
+
+    /**
+     * Reads a keyset mapping from the given parcel at the given data position. May return
+     * {@code null} if the serialized mapping was {@code null}.
+     */
+    @NonNull
+    public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
+        final int N = in.readInt();
+        if (N == -1) {
+            return null;
+        }
+
+        ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
+        for (int i = 0; i < N; ++i) {
+            String key = in.readString();
+            final int M = in.readInt();
+            if (M == -1) {
+                keySetMapping.put(key, null);
+                continue;
+            }
+
+            ArraySet<PublicKey> keys = new ArraySet<>(M);
+            for (int j = 0; j < M; ++j) {
+                PublicKey pk = (PublicKey) in.readSerializable();
+                keys.add(pk);
+            }
+
+            keySetMapping.put(key, keys);
+        }
+
+        return keySetMapping;
+    }
+
+
+    /**
+     * Callback interface for retrieving information that may be needed while parsing
+     * a package.
+     */
+    public interface Callback {
+        boolean hasFeature(String feature);
+
+        ParsingPackage startParsingPackage(@NonNull String packageName,
+                @NonNull String baseApkPath, @NonNull String path,
+                @NonNull TypedArray manifestArray, boolean isCoreApp);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
new file mode 100644
index 0000000..95fec36
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide **/
+public class ParsingUtils {
+
+    public static final String TAG = "PackageParsing";
+
+    public static final String ANDROID_RES_NAMESPACE = "http://schemas.android.com/apk/res/android";
+
+    public static final int DEFAULT_MIN_SDK_VERSION = 1;
+    public static final int DEFAULT_TARGET_SDK_VERSION = 0;
+
+    public static final int NOT_SET = -1;
+
+    @Nullable
+    public static String buildClassName(String pkg, CharSequence clsSeq) {
+        if (clsSeq == null || clsSeq.length() <= 0) {
+            return null;
+        }
+        String cls = clsSeq.toString();
+        char c = cls.charAt(0);
+        if (c == '.') {
+            return pkg + cls;
+        }
+        if (cls.indexOf('.') < 0) {
+            StringBuilder b = new StringBuilder(pkg);
+            b.append('.');
+            b.append(cls);
+            return b.toString();
+        }
+        return cls;
+    }
+
+    @NonNull
+    public static ParseResult unknownTag(String parentTag, ParsingPackage pkg,
+            XmlResourceParser parser, ParseInput input) throws IOException, XmlPullParserException {
+        if (RIGID_PARSER) {
+            return input.error("Bad element under " + parentTag + ": " + parser.getName());
+        }
+        Slog.w(TAG, "Unknown element under " + parentTag + ": "
+                + parser.getName() + " at " + pkg.getBaseApkPath() + " "
+                + parser.getPositionDescription());
+        XmlUtils.skipCurrentTag(parser);
+        return input.success(null); // Type doesn't matter
+    }
+
+    /**
+     * Use with {@link Parcel#writeTypedList(List)}
+     *
+     * @see Parcel#createTypedArrayList(Parcelable.Creator)
+     */
+    @NonNull
+    public static <Interface, Impl extends Interface> List<Interface> createTypedInterfaceList(
+            @NonNull Parcel parcel, @NonNull Parcelable.Creator<Impl> creator) {
+        int size = parcel.readInt();
+        if (size < 0) {
+            return new ArrayList<>();
+        }
+        ArrayList<Interface> list = new ArrayList<Interface>(size);
+        while (size > 0) {
+            list.add(parcel.readTypedObject(creator));
+            size--;
+        }
+        return list;
+    }
+
+    public static class StringPairListParceler implements
+            Parcelling<List<Pair<String, ParsedIntentInfo>>> {
+
+        @Override
+        public void parcel(List<Pair<String, ParsedIntentInfo>> item, Parcel dest,
+                int parcelFlags) {
+            if (item == null) {
+                dest.writeInt(-1);
+                return;
+            }
+
+            final int size = item.size();
+            dest.writeInt(size);
+
+            for (int index = 0; index < size; index++) {
+                Pair<String, ParsedIntentInfo> pair = item.get(index);
+                dest.writeString(pair.first);
+                dest.writeParcelable(pair.second, parcelFlags);
+            }
+        }
+
+        @Override
+        public List<Pair<String, ParsedIntentInfo>> unparcel(Parcel source) {
+            int size = source.readInt();
+            if (size == -1) {
+                return null;
+            }
+
+            if (size == 0) {
+                return new ArrayList<>(0);
+            }
+
+            final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                list.add(Pair.create(source.readString(), source.readParcelable(
+                        ParsedIntentInfoImpl.class.getClassLoader(), ParsedIntentInfo.class)));
+            }
+
+            return list;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
new file mode 100644
index 0000000..a323e20
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.util.SparseArray;
+
+/**
+ * Container for fields that are eventually exposed through {@link ApplicationInfo}.
+ * <p>
+ * The following are dependent on system state and explicitly removed from this interface. They must
+ * be accessed by other means:
+ * <ul>
+ *    <li>{@link ApplicationInfo#credentialProtectedDataDir}</li>
+ *    <li>{@link ApplicationInfo#dataDir}</li>
+ *    <li>{@link ApplicationInfo#deviceProtectedDataDir}</li>
+ *    <li>{@link ApplicationInfo#enabledSetting}</li>
+ *    <li>{@link ApplicationInfo#enabled}</li>
+ *    <li>{@link ApplicationInfo#FLAG_INSTALLED}</li>
+ *    <li>{@link ApplicationInfo#FLAG_STOPPED}</li>
+ *    <li>{@link ApplicationInfo#FLAG_SUSPENDED}</li>
+ *    <li>{@link ApplicationInfo#FLAG_UPDATED_SYSTEM_APP}</li>
+ *    <li>{@link ApplicationInfo#hiddenUntilInstalled}</li>
+ *    <li>{@link ApplicationInfo#primaryCpuAbi}</li>
+ *    <li>{@link ApplicationInfo#PRIVATE_FLAG_HIDDEN}</li>
+ *    <li>{@link ApplicationInfo#PRIVATE_FLAG_INSTANT}</li>
+ *    <li>{@link ApplicationInfo#PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER}</li>
+ *    <li>{@link ApplicationInfo#PRIVATE_FLAG_VIRTUAL_PRELOAD}</li>
+ *    <li>{@link ApplicationInfo#resourceDirs}</li>
+ *    <li>{@link ApplicationInfo#secondaryCpuAbi}</li>
+ *    <li>{@link ApplicationInfo#seInfoUser}</li>
+ *    <li>{@link ApplicationInfo#seInfo}</li>
+ *    <li>{@link ApplicationInfo#sharedLibraryFiles}</li>
+ *    <li>{@link ApplicationInfo#sharedLibraryInfos}</li>
+ *    <li>{@link ApplicationInfo#uid}</li>
+ * </ul>
+ * The following are derived from other fields and thus not provided specifically:
+ * <ul>
+ *    <li>{@link ApplicationInfo#getBaseResourcePath}</li>
+ *    <li>{@link ApplicationInfo#getResourcePath}</li>
+ *    <li>{@link ApplicationInfo#getSplitResourcePaths}</li>
+ *    <li>{@link ApplicationInfo#publicSourceDir}</li>
+ *    <li>{@link ApplicationInfo#scanPublicSourceDir}</li>
+ *    <li>{@link ApplicationInfo#splitPublicSourceDirs}</li>
+ *    <li>{@link ApplicationInfo#storageUuid}</li>
+ * </ul>
+ * The following were deprecated at migration time and thus removed from this interface:
+ * <ul>
+ *    <li>{@link ApplicationInfo#FLAG_IS_GAME}</li>
+ *    <li>{@link ApplicationInfo#targetSandboxVersion}</li>
+ *    <li>{@link ApplicationInfo#versionCode}</li>
+ * </ul>
+ * TODO: The following fields are just not available at all. Never filled, even by legacy parsing?
+ * <ul>
+ *    <li>{@link ApplicationInfo#FLAG_IS_DATA_ONLY}</li>
+ * </ul>
+ *
+ * @hide
+ */
+public interface PkgWithoutStateAppInfo {
+
+    /**
+     * @see ApplicationInfo#areAttributionsUserVisible()
+     * @see R.styleable#AndroidManifestApplication_attributionsAreUserVisible
+     */
+    @Nullable
+    boolean areAttributionsUserVisible();
+
+    /**
+     * @see ApplicationInfo#appComponentFactory
+     * @see R.styleable#AndroidManifestApplication_appComponentFactory
+     */
+    @Nullable
+    String getAppComponentFactory();
+
+    /**
+     * @see ApplicationInfo#AUTO_REVOKE_ALLOWED
+     * @see ApplicationInfo#AUTO_REVOKE_DISCOURAGED
+     * @see ApplicationInfo#AUTO_REVOKE_DISALLOWED
+     */
+    int getAutoRevokePermissions();
+
+    /**
+     * @see ApplicationInfo#backupAgentName
+     * @see R.styleable#AndroidManifestApplication_backupAgent
+     */
+    @Nullable
+    String getBackupAgentName();
+
+    /**
+     * @see ApplicationInfo#banner
+     * @see R.styleable#AndroidManifestApplication_banner
+     */
+    int getBanner();
+
+    /**
+     * @see ApplicationInfo#sourceDir
+     * @see ApplicationInfo#getBaseCodePath
+     */
+    @NonNull
+    String getBaseApkPath();
+
+    /**
+     * @see ApplicationInfo#category
+     * @see R.styleable#AndroidManifestApplication_appCategory
+     */
+    int getCategory();
+
+    /**
+     * @see ApplicationInfo#classLoaderName
+     * @see R.styleable#AndroidManifestApplication_classLoader
+     */
+    @Nullable
+    String getClassLoaderName();
+
+    /**
+     * @see ApplicationInfo#className
+     * @see R.styleable#AndroidManifestApplication_name
+     */
+    @Nullable
+    String getClassName();
+
+    /**
+     * @see ApplicationInfo#compatibleWidthLimitDp
+     * @see R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
+     */
+    int getCompatibleWidthLimitDp();
+
+    /**
+     * @see ApplicationInfo#compileSdkVersion
+     * @see R.styleable#AndroidManifest_compileSdkVersion
+     */
+    int getCompileSdkVersion();
+
+    /**
+     * @see ApplicationInfo#compileSdkVersionCodename
+     * @see R.styleable#AndroidManifest_compileSdkVersionCodename
+     */
+    @Nullable
+    String getCompileSdkVersionCodeName();
+
+    /**
+     * @see ApplicationInfo#dataExtractionRulesRes
+     * @see R.styleable#AndroidManifestApplication_dataExtractionRules
+     */
+    int getDataExtractionRules();
+
+    /**
+     * @see ApplicationInfo#descriptionRes
+     * @see R.styleable#AndroidManifestApplication_description
+     */
+    int getDescriptionRes();
+
+    /**
+     * @see ApplicationInfo#fullBackupContent
+     * @see R.styleable#AndroidManifestApplication_fullBackupContent
+     */
+    int getFullBackupContent();
+
+    /**
+     * @see ApplicationInfo#getGwpAsanMode()
+     * @see R.styleable#AndroidManifestApplication_gwpAsanMode
+     */
+    @ApplicationInfo.GwpAsanMode
+    int getGwpAsanMode();
+
+    /**
+     * @see ApplicationInfo#iconRes
+     * @see R.styleable#AndroidManifestApplication_icon
+     */
+    int getIconRes();
+
+    /**
+     * @see ApplicationInfo#installLocation
+     * @see R.styleable#AndroidManifest_installLocation
+     */
+    int getInstallLocation();
+
+    /**
+     * @see ApplicationInfo#labelRes
+     * @see R.styleable#AndroidManifestApplication_label
+     */
+    int getLabelRes();
+
+    /**
+     * @see ApplicationInfo#largestWidthLimitDp
+     * @see R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
+     */
+    int getLargestWidthLimitDp();
+
+    /**
+     * @see ApplicationInfo#logo
+     * @see R.styleable#AndroidManifestApplication_logo
+     */
+    int getLogo();
+
+    /**
+     * @see ApplicationInfo#longVersionCode
+     */
+    long getLongVersionCode();
+
+    /**
+     * @see ApplicationInfo#manageSpaceActivityName
+     * @see R.styleable#AndroidManifestApplication_manageSpaceActivity
+     */
+    @Nullable
+    String getManageSpaceActivityName();
+
+    /**
+     * @see ApplicationInfo#maxAspectRatio
+     * @see R.styleable#AndroidManifestApplication_maxAspectRatio
+     */
+    float getMaxAspectRatio();
+
+    /**
+     * @see ApplicationInfo#getMemtagMode()
+     * @see R.styleable#AndroidManifestApplication_memtagMode
+     */
+    @ApplicationInfo.MemtagMode
+    int getMemtagMode();
+
+    /**
+     * @see ApplicationInfo#minAspectRatio
+     * @see R.styleable#AndroidManifestApplication_minAspectRatio
+     */
+    float getMinAspectRatio();
+
+    /**
+     * @see ApplicationInfo#minSdkVersion
+     * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
+     */
+    int getMinSdkVersion();
+
+    /**
+     * @see ApplicationInfo#getNativeHeapZeroInitialized()
+     * @see R.styleable#AndroidManifestApplication_nativeHeapZeroInitialized
+     */
+    @ApplicationInfo.NativeHeapZeroInitialized
+    int getNativeHeapZeroInitialized();
+
+    /**
+     * @see ApplicationInfo#networkSecurityConfigRes
+     * @see R.styleable#AndroidManifestApplication_networkSecurityConfig
+     */
+    int getNetworkSecurityConfigRes();
+
+    /**
+     * If {@link R.styleable#AndroidManifestApplication_label} is a string literal, this is it.
+     * Otherwise, it's stored as {@link #getLabelRes()}.
+     *
+     * @see ApplicationInfo#nonLocalizedLabel
+     * @see R.styleable#AndroidManifestApplication_label
+     */
+    @Nullable
+    CharSequence getNonLocalizedLabel();
+
+    /**
+     * @see ApplicationInfo#scanSourceDir
+     * @see ApplicationInfo#getCodePath
+     */
+    @NonNull
+    String getPath();
+
+    /**
+     * @see ApplicationInfo#permission
+     * @see R.styleable#AndroidManifestApplication_permission
+     */
+    @Nullable
+    String getPermission();
+
+    /**
+     * @see ApplicationInfo#processName
+     * @see R.styleable#AndroidManifestApplication_process
+     */
+    @NonNull
+    String getProcessName();
+
+    /**
+     * @see ApplicationInfo#requiresSmallestWidthDp
+     * @see R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
+     */
+    int getRequiresSmallestWidthDp();
+
+    /**
+     * Whether or not the app requested explicitly resizeable Activities. Null value means nothing
+     * was explicitly requested.
+     *
+     * @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
+     * @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE
+     */
+    @Nullable
+    Boolean getResizeableActivity();
+
+    /**
+     * @see ApplicationInfo#roundIconRes
+     * @see R.styleable#AndroidManifestApplication_roundIcon
+     */
+    int getRoundIconRes();
+
+    /**
+     * @see ApplicationInfo#splitClassLoaderNames
+     * @see R.styleable#AndroidManifestApplication_classLoader
+     */
+    @Nullable
+    String[] getSplitClassLoaderNames();
+
+    /**
+     * @see ApplicationInfo#splitSourceDirs
+     * @see ApplicationInfo#getSplitCodePaths
+     */
+    @Nullable
+    String[] getSplitCodePaths();
+
+    /**
+     * @see ApplicationInfo#splitDependencies
+     */
+    @Nullable
+    SparseArray<int[]> getSplitDependencies();
+
+    /**
+     * @see ApplicationInfo#targetSdkVersion
+     * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
+     */
+    int getTargetSdkVersion();
+
+    /**
+     * @see ApplicationInfo#targetSandboxVersion
+     * @see R.styleable#AndroidManifest_targetSandboxVersion
+     */
+    int getTargetSandboxVersion();
+
+    /**
+     * @see ApplicationInfo#taskAffinity
+     * @see R.styleable#AndroidManifestApplication_taskAffinity
+     */
+    @Nullable
+    String getTaskAffinity();
+
+    /**
+     * @see ApplicationInfo#theme
+     * @see R.styleable#AndroidManifestApplication_theme
+     */
+    int getTheme();
+
+    /**
+     * @see ApplicationInfo#uiOptions
+     * @see R.styleable#AndroidManifestApplication_uiOptions
+     */
+    int getUiOptions();
+
+    /**
+     * @see ApplicationInfo#volumeUuid
+     */
+    @Nullable
+    String getVolumeUuid();
+
+    /**
+     * @see ApplicationInfo#zygotePreloadName
+     */
+    @Nullable
+    String getZygotePreloadName();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION
+     * @see R.styleable#AndroidManifestApplication_requestForegroundServiceExemption
+     */
+    boolean hasRequestForegroundServiceExemption();
+
+    /**
+     * @see ApplicationInfo#getRequestRawExternalStorageAccess()
+     * @see R.styleable#AndroidManifestApplication_requestRawExternalStorageAccess
+     */
+    Boolean hasRequestRawExternalStorageAccess();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
+     */
+    boolean isAllowAudioPlaybackCapture();
+
+    /**
+     * @see ApplicationInfo#FLAG_ALLOW_BACKUP
+     */
+    boolean isAllowBackup();
+
+    /**
+     * @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA
+     */
+    boolean isAllowClearUserData();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
+     */
+    boolean isAllowClearUserDataOnFailedRestore();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
+     */
+    boolean isAllowNativeHeapPointerTagging();
+
+    /**
+     * @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING
+     */
+    boolean isAllowTaskReparenting();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
+     * android.os.Build.VERSION_CODES#DONUT}.
+     *
+     * @see R.styleable#AndroidManifestSupportsScreens_anyDensity
+     * @see ApplicationInfo#FLAG_SUPPORTS_SCREEN_DENSITIES
+     */
+    boolean isAnyDensity();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND
+     */
+    boolean isBackupInForeground();
+
+    /**
+     * @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED
+     */
+    boolean isBaseHardwareAccelerated();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE
+     */
+    boolean isCantSaveState();
+
+    /**
+     * @see ApplicationInfo#crossProfile
+     */
+    boolean isCrossProfile();
+
+    /**
+     * @see ApplicationInfo#FLAG_DEBUGGABLE
+     */
+    boolean isDebuggable();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE
+     */
+    boolean isDefaultToDeviceProtectedStorage();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE
+     */
+    boolean isDirectBootAware();
+
+    /**
+     * @see ApplicationInfo#enabled
+     * @see R.styleable#AndroidManifestApplication_enabled
+     */
+    boolean isEnabled();
+
+    /**
+     * @see ApplicationInfo#FLAG_EXTERNAL_STORAGE
+     */
+    boolean isExternalStorage();
+
+    /**
+     * @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS
+     */
+    boolean isExtractNativeLibs();
+
+    /**
+     * @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY
+     */
+    boolean isFullBackupOnly();
+
+    /**
+     * @see ApplicationInfo#FLAG_HAS_CODE
+     */
+    boolean isHasCode();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS
+     */
+    boolean isHasDomainUrls();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA
+     */
+    boolean isHasFragileUserData();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
+     */
+    boolean isIsolatedSplitLoading();
+
+    /**
+     * @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE
+     */
+    boolean isKillAfterRestore();
+
+    /**
+     * @see ApplicationInfo#FLAG_LARGE_HEAP
+     */
+    boolean isLargeHeap();
+
+    /**
+     * @see ApplicationInfo#FLAG_MULTIARCH
+     */
+    boolean isMultiArch();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
+     * @see ApplicationInfo#isResourceOverlay()
+     */
+    boolean isOverlay();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE
+     */
+    boolean isPartiallyDirectBootAware();
+
+    /**
+     * @see ApplicationInfo#FLAG_PERSISTENT
+     */
+    boolean isPersistent();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_EXT_PROFILEABLE
+     */
+    boolean isProfileable();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL
+     */
+    boolean isProfileableByShell();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE
+     */
+    boolean isRequestLegacyExternalStorage();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
+     * android.os.Build.VERSION_CODES#DONUT}.
+     *
+     * @see R.styleable#AndroidManifestSupportsScreens_resizeable
+     * @see ApplicationInfo#FLAG_RESIZEABLE_FOR_SCREENS
+     */
+    boolean isResizeable();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
+     */
+    boolean isResizeableActivityViaSdkVersion();
+
+    /**
+     * @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION
+     */
+    boolean isRestoreAnyVersion();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY
+     */
+    boolean isStaticSharedLibrary();
+
+    /**
+     * True means that this package/app contains an SDK library.
+     */
+    boolean isSdkLibrary();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
+     * android.os.Build.VERSION_CODES#GINGERBREAD}.
+     *
+     * @see R.styleable#AndroidManifestSupportsScreens_xlargeScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_XLARGE_SCREENS
+     */
+    boolean isSupportsExtraLargeScreens();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
+     * android.os.Build.VERSION_CODES#DONUT}.
+     *
+     * @see R.styleable#AndroidManifestSupportsScreens_largeScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_LARGE_SCREENS
+     */
+    boolean isSupportsLargeScreens();
+
+    /**
+     * If omitted from manifest, returns true.
+     *
+     * @see R.styleable#AndroidManifestSupportsScreens_normalScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_NORMAL_SCREENS
+     */
+    boolean isSupportsNormalScreens();
+
+    /**
+     * @see ApplicationInfo#FLAG_SUPPORTS_RTL
+     */
+    boolean isSupportsRtl();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
+     * android.os.Build.VERSION_CODES#DONUT}.
+     *
+     * @see R.styleable#AndroidManifestSupportsScreens_smallScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_SMALL_SCREENS
+     */
+    boolean isSupportsSmallScreens();
+
+    /**
+     * @see ApplicationInfo#FLAG_TEST_ONLY
+     */
+    boolean isTestOnly();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX
+     */
+    boolean isUseEmbeddedDex();
+
+    /**
+     * @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC
+     */
+    boolean isUsesCleartextTraffic();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API
+     */
+    boolean isUsesNonSdkApi();
+
+    /**
+     * @see ApplicationInfo#FLAG_VM_SAFE_MODE
+     */
+    boolean isVmSafeMode();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java
new file mode 100644
index 0000000..2bc4ee7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+
+import com.android.internal.R;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+/**
+ * Container for fields that are eventually exposed through {@link PackageInfo}.
+ * <p>
+ * The following are dependent on system state and explicitly removed from this interface. They must
+ * be accessed by other means:
+ * <ul>
+ *    <li>{@link PackageInfo#firstInstallTime}</li>
+ *    <li>{@link PackageInfo#lastUpdateTime}</li>
+ *    <li>{@link PackageInfo#gids}</li>
+ * </ul>
+ * The following are derived from other fields and thus not provided specifically:
+ * <ul>
+ *    <li>{@link PackageInfo#requestedPermissionsFlags}</li>
+ * </ul>
+ * The following were deprecated at migration time and thus removed from this interface:
+ * <ul>
+ *    <li>{@link PackageInfo#mOverlayIsStatic}</li>
+ *    <li>{@link PackageInfo#overlayCategory}</li>
+ *    <li>{@link PackageInfo#overlayPriority}</li>
+ *    <li>{@link PackageInfo#overlayTarget}</li>
+ *    <li>{@link PackageInfo#signatures}</li>
+ *    <li>{@link PackageInfo#targetOverlayableName}</li>
+ *    <li>{@link PackageInfo#versionCodeMajor}</li>
+ *    <li>{@link PackageInfo#versionCode}</li>
+ * </ul>
+ * The following are retrieved through other APIs:
+ * <ul>
+ *    <li>{@link PackageInfo#signingInfo}</li>
+ *    <li>{@link PackageInfo#isApex}</li>
+ * </ul>
+ *
+ * @hide
+ */
+public interface PkgWithoutStatePackageInfo {
+
+    /**
+     * Set of Activities parsed from the manifest.
+     *
+     * This contains minimal system state and does not
+     * provide the same information as {@link ActivityInfo}. Effective state can be queried through
+     * {@link android.content.pm.PackageManager#getActivityInfo(ComponentName, int)} or by
+     * combining state from from com.android.server.pm.pkg.PackageState and
+     * {@link PackageUserState}.
+     *
+     * @see ActivityInfo
+     * @see PackageInfo#activities
+     */
+    @NonNull
+    List<ParsedActivity> getActivities();
+
+    /**
+     * @see PackageInfo#baseRevisionCode
+     */
+    int getBaseRevisionCode();
+
+    /**
+     * @see PackageInfo#compileSdkVersion
+     * @see R.styleable#AndroidManifest_compileSdkVersion
+     */
+    int getCompileSdkVersion();
+
+    /**
+     * @see ApplicationInfo#compileSdkVersionCodename
+     * @see R.styleable#AndroidManifest_compileSdkVersionCodename
+     */
+    @Nullable
+    String getCompileSdkVersionCodeName();
+
+    /**
+     * @see PackageInfo#configPreferences
+     * @see R.styleable#AndroidManifestUsesConfiguration
+     */
+    @NonNull
+    List<ConfigurationInfo> getConfigPreferences();
+
+    /**
+     * @see PackageInfo#featureGroups
+     * @see R.styleable#AndroidManifestUsesFeature
+     */
+    @NonNull
+    List<FeatureGroupInfo> getFeatureGroups();
+
+    /**
+     * @see InstrumentationInfo
+     * @see PackageInfo#instrumentation
+     */
+    @NonNull
+    List<ParsedInstrumentation> getInstrumentations();
+
+    /**
+     * @see PackageInfo#getLongVersionCode()
+     */
+    long getLongVersionCode();
+
+    /**
+     * @see PackageInfo#packageName
+     */
+    String getPackageName();
+
+    /**
+     * @see PermissionInfo
+     * @see PackageInfo#permissions
+     */
+    @NonNull
+    List<ParsedPermission> getPermissions();
+
+    /**
+     * Set of {@link android.content.ContentProvider ContentProviders} parsed from the manifest.
+     *
+     * This contains minimal system state and does not
+     * provide the same information as {@link ProviderInfo}. Effective state can be queried through
+     * {@link android.content.pm.PackageManager#getProviderInfo(ComponentName, int)} or by
+     * combining state from from com.android.server.pm.pkg.PackageState and
+     * {@link PackageUserState}.
+     *
+     * @see ProviderInfo
+     * @see PackageInfo#providers
+     */
+    @NonNull
+    List<ParsedProvider> getProviders();
+
+    /**
+     * Set of {@link android.content.BroadcastReceiver BroadcastReceivers} parsed from the manifest.
+     *
+     * This contains minimal system state and does not
+     * provide the same information as {@link ActivityInfo}. Effective state can be queried through
+     * {@link android.content.pm.PackageManager#getReceiverInfo(ComponentName, int)} or by
+     * combining state from from com.android.server.pm.pkg.PackageState and
+     * {@link PackageUserState}.
+     *
+     * Since they share several attributes, receivers are parsed as {@link ParsedActivity}, even
+     * though they represent different functionality.
+     *
+     * TODO(b/135203078): Reconsider this and maybe make ParsedReceiver so it's not so confusing
+     *
+     * @see ActivityInfo
+     * @see PackageInfo#receivers
+     */
+    @NonNull
+    List<ParsedActivity> getReceivers();
+
+    /**
+     * @see PackageInfo#reqFeatures
+     * @see R.styleable#AndroidManifestUsesFeature
+     */
+    @NonNull
+    List<FeatureInfo> getRequestedFeatures();
+
+    /**
+     * All the permissions declared. This is an effective set, and may include permissions
+     * transformed from split/migrated permissions from previous versions, so may not be exactly
+     * what the package declares in its manifest.
+     *
+     * @see PackageInfo#requestedPermissions
+     * @see R.styleable#AndroidManifestUsesPermission
+     */
+    @NonNull
+    List<String> getRequestedPermissions();
+
+    /**
+     * @see PackageInfo#requiredAccountType
+     * @see R.styleable#AndroidManifestApplication_requiredAccountType
+     */
+    @Nullable
+    String getRequiredAccountType();
+
+    /**
+     * The restricted account authenticator type that is used by this application.
+     *
+     * @see PackageInfo#restrictedAccountType
+     * @see R.styleable#AndroidManifestApplication_restrictedAccountType
+     */
+    @Nullable
+    String getRestrictedAccountType();
+
+    /**
+     * Set of {@link android.app.Service Services} parsed from the manifest.
+     *
+     * This contains minimal system state and does not
+     * provide the same information as {@link ServiceInfo}. Effective state can be queried through
+     * {@link android.content.pm.PackageManager#getServiceInfo(ComponentName, int)} or by
+     * combining state from from com.android.server.pm.pkg.PackageState and
+     * {@link PackageUserState}.
+     *
+     * @see ServiceInfo
+     * @see PackageInfo#services
+     */
+    @NonNull
+    List<ParsedService> getServices();
+
+    /**
+     * @see PackageInfo#sharedUserId
+     * @see R.styleable#AndroidManifest_sharedUserId
+     */
+    @Nullable
+    String getSharedUserId();
+
+    /**
+     * @see PackageInfo#sharedUserLabel
+     * @see R.styleable#AndroidManifest_sharedUserLabel
+     */
+    int getSharedUserLabel();
+
+    /**
+     * TODO(b/135203078): Move split stuff to an inner data class
+     *
+     * @see ApplicationInfo#splitNames
+     * @see PackageInfo#splitNames
+     */
+    @Nullable
+    String[] getSplitNames();
+
+    /**
+     * @see PackageInfo#splitRevisionCodes
+     */
+    int[] getSplitRevisionCodes();
+
+    /**
+     * @see PackageInfo#versionName
+     */
+    @Nullable
+    String getVersionName();
+
+    /**
+     * @see PackageInfo#requiredForAllUsers
+     * @see R.styleable#AndroidManifestApplication_requiredForAllUsers
+     */
+    boolean isRequiredForAllUsers();
+}
diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
new file mode 100644
index 0000000..2bd7cf8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.split;
+
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.os.Build;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+
+/**
+ * Loads the base and split APKs into a single AssetManager.
+ * @hide
+ */
+public class DefaultSplitAssetLoader implements SplitAssetLoader {
+    private final String mBaseApkPath;
+    private final String[] mSplitApkPaths;
+    private final @ParseFlags int mFlags;
+    private AssetManager mCachedAssetManager;
+
+    private ApkAssets mBaseApkAssets;
+
+    public DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
+        mBaseApkPath = pkg.getBaseApkPath();
+        mSplitApkPaths = pkg.getSplitApkPaths();
+        mFlags = flags;
+    }
+
+    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+            throws IllegalArgumentException {
+        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
+                && !ApkLiteParseUtils.isApkPath(path)) {
+            throw new IllegalArgumentException("Invalid package file: " + path);
+        }
+
+        try {
+            return ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
+        }
+    }
+
+    @Override
+    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
+        if (mCachedAssetManager != null) {
+            return mCachedAssetManager;
+        }
+
+        ApkAssets[] apkAssets = new ApkAssets[(mSplitApkPaths != null
+                ? mSplitApkPaths.length : 0) + 1];
+
+        // Load the base.
+        int splitIdx = 0;
+        apkAssets[splitIdx++] = mBaseApkAssets = loadApkAssets(mBaseApkPath, mFlags);
+
+        // Load any splits.
+        if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
+            for (String apkPath : mSplitApkPaths) {
+                apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
+            }
+        }
+
+        AssetManager assets = new AssetManager();
+        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+        mCachedAssetManager = assets;
+        return mCachedAssetManager;
+    }
+
+    @Override
+    public AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException {
+        return getBaseAssetManager();
+    }
+
+    @Override
+    public ApkAssets getBaseApkAssets() {
+        return mBaseApkAssets;
+    }
+
+    @Override
+    public void close() throws Exception {
+        IoUtils.closeQuietly(mCachedAssetManager);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
new file mode 100644
index 0000000..ae42e09
--- /dev/null
+++ b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.split;
+
+import android.annotation.NonNull;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.split.SplitDependencyLoader;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.os.Build;
+import android.util.SparseArray;
+
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
+ * is to be used when an application opts-in to isolated split loading.
+ * @hide
+ */
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArgumentException>
+        implements SplitAssetLoader {
+    private final String[] mSplitPaths;
+    private final @ParseFlags int mFlags;
+    private final ApkAssets[][] mCachedSplitApks;
+    private final AssetManager[] mCachedAssetManagers;
+
+    public SplitAssetDependencyLoader(PackageLite pkg,
+            SparseArray<int[]> dependencies, @ParseFlags int flags) {
+        super(dependencies);
+
+        // The base is inserted into index 0, so we need to shift all the splits by 1.
+        mSplitPaths = new String[pkg.getSplitApkPaths().length + 1];
+        mSplitPaths[0] = pkg.getBaseApkPath();
+        System.arraycopy(pkg.getSplitApkPaths(), 0, mSplitPaths, 1, pkg.getSplitApkPaths().length);
+
+        mFlags = flags;
+        mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
+        mCachedAssetManagers = new AssetManager[mSplitPaths.length];
+    }
+
+    @Override
+    protected boolean isSplitCached(int splitIdx) {
+        return mCachedAssetManagers[splitIdx] != null;
+    }
+
+    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+            throws IllegalArgumentException {
+        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
+                && !ApkLiteParseUtils.isApkPath(path)) {
+            throw new IllegalArgumentException("Invalid package file: " + path);
+        }
+
+        try {
+            return ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
+        }
+    }
+
+    private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+        final AssetManager assets = new AssetManager();
+        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+        return assets;
+    }
+
+    @Override
+    protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+            int parentSplitIdx) throws IllegalArgumentException {
+        final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+        // Include parent ApkAssets.
+        if (parentSplitIdx >= 0) {
+            Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
+        }
+
+        // Include this ApkAssets.
+        assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+        // Load and include all config splits for this feature.
+        for (int configSplitIdx : configSplitIndices) {
+            assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
+        }
+
+        // Cache the results.
+        mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+        mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
+    }
+
+    @Override
+    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
+        loadDependenciesForSplit(0);
+        return mCachedAssetManagers[0];
+    }
+
+    @Override
+    public AssetManager getSplitAssetManager(int idx) throws IllegalArgumentException {
+        // Since we insert the base at position 0, and ParsingPackageUtils keeps splits separate
+        // from the base, we need to adjust the index.
+        loadDependenciesForSplit(idx + 1);
+        return mCachedAssetManagers[idx + 1];
+    }
+
+    @Override
+    public ApkAssets getBaseApkAssets() {
+        return mCachedSplitApks[0][0];
+    }
+
+    @Override
+    public void close() throws Exception {
+        for (AssetManager assets : mCachedAssetManagers) {
+            IoUtils.closeQuietly(assets);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java b/services/core/java/com/android/server/pm/split/SplitAssetLoader.java
new file mode 100644
index 0000000..8450159
--- /dev/null
+++ b/services/core/java/com/android/server/pm/split/SplitAssetLoader.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.split;
+
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+
+/**
+ * Simple interface for loading base Assets and Splits. Used by ParsingPackageUtils when parsing
+ * split APKs.
+ *
+ * @hide
+ */
+public interface SplitAssetLoader extends AutoCloseable {
+    AssetManager getBaseAssetManager() throws IllegalArgumentException;
+    AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException;
+
+    ApkAssets getBaseApkAssets();
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index d47f510..e078120 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -22,8 +22,8 @@
 import android.compat.annotation.EnabledSince;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.ArraySet;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index d1603f5..d0b50d27 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -31,8 +31,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.pkg.PackageUserStateUtils;
 import android.content.pm.verify.domain.DomainOwner;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager;
@@ -63,6 +61,8 @@
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 27a16e9..17a5fd0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -94,6 +94,7 @@
     private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
     private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
     private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
+    private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
 
     /** Interface that allows reading the device state configuration. */
     interface ReadableConfig {
@@ -141,8 +142,8 @@
                         for (int i = 0; i < configFlagStrings.size(); i++) {
                             final String configFlagString = configFlagStrings.get(i);
                             switch (configFlagString) {
-                                case "FLAG_CANCEL_STICKY_REQUESTS":
-                                    flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS;
+                                case FLAG_CANCEL_OVERRIDE_REQUESTS:
+                                    flags |= DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
                                     break;
                                 default:
                                     Slog.w(TAG, "Parsed unknown flag with name: "
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index a5969a8..54ece73 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -138,7 +138,8 @@
         // By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user
         // 10 on devices with headless system user enabled.
         // In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL.
-        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null,
+                Context.RECEIVER_EXPORTED);
 
         mHasTelephony =
                 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
index 6084c67..20b7ccd 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.TaskInfo;
 import android.content.Intent;
 
 /**
@@ -52,6 +53,25 @@
             @Nullable String callingPackage);
 
     /**
+     * Check whether a notification permission prompt should be shown for the given package. A
+     * prompt should be shown if the app targets S-, is currently running in a visible, focused
+     * task, has the REVIEW_REQUIRED flag set on its implicit notification permission, and has
+     * created at least one notification channel (even if it has since been deleted).
+     * @param packageName The package whose permission is being checked
+     * @param userId The user for whom the package is being started
+     * @param taskId The task the notification prompt should be attached to
+     */
+    public abstract void showNotificationPromptIfNeeded(@NonNull String packageName, int userId,
+            int taskId);
+
+    /**
+     * Determine if a particular task is in the proper state to show a system-triggered permission
+     * prompt. A prompt can be shown if the task is focused, visible, and running.
+     * @param taskInfo The task to be checked
+     */
+    public abstract boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo);
+
+    /**
      * @return Whether the policy is initialized for a user.
      */
     public abstract boolean isInitialized(@UserIdInt int userId);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index ad43514..c9a8701 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.policy;
 
+import static android.Manifest.permission.POST_NOTIFICATIONS;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
@@ -25,16 +26,24 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
+import android.app.TaskInfo;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -43,6 +52,7 @@
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PermissionInfo;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -53,6 +63,7 @@
 import android.telecom.TelecomManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.LongSparseLongArray;
 import android.util.Pair;
 import android.util.Slog;
@@ -67,17 +78,21 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
 import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.wm.ActivityInterceptorCallback;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -89,11 +104,15 @@
  */
 public final class PermissionPolicyService extends SystemService {
     private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName();
+    private static final String SYSTEM_PKG = "android";
     private static final boolean DEBUG = false;
     private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000;
 
     private final Object mLock = new Object();
 
+    @GuardedBy("mLock")
+    private boolean mBootCompleted = false;
+
     private IAppOpsCallback mAppOpsCallback;
 
     /** Whether the user is started but not yet stopped */
@@ -118,24 +137,39 @@
     @GuardedBy("mLock")
     private final SparseBooleanArray mIsUidSyncScheduled = new SparseBooleanArray();
 
+    /**
+     * This change reflects the presence of the new Notification Permission
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    private static final long NOTIFICATION_PERM_CHANGE_ID = 194833441L;
+
     private List<String> mAppOpPermissions;
 
+    private Context mContext;
+    private PackageManagerInternal mPackageManagerInternal;
+    private NotificationManagerInternal mNotificationManager;
+    private PermissionManagerServiceInternal mPermissionManagerService;
+    private final PackageManager mPackageManager;
+
     public PermissionPolicyService(@NonNull Context context) {
         super(context);
 
+        mContext = context;
+        mPackageManager = context.getPackageManager();
         LocalServices.addService(PermissionPolicyInternal.class, new Internal());
     }
 
     @Override
     public void onStart() {
-        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+        mPackageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
-        final PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
+        PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
                 PermissionManagerServiceInternal.class);
         final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
 
-        packageManagerInternal.getPackageList(new PackageListObserver() {
+        mPackageManagerInternal.getPackageList(new PackageListObserver() {
             @Override
             public void onPackageAdded(String packageName, int uid) {
                 final int userId = UserHandle.getUserId(uid);
@@ -207,10 +241,10 @@
             final PermissionInfo appOpPermissionInfo = appOpPermissionInfos.get(i);
 
             switch (appOpPermissionInfo.name) {
-                case android.Manifest.permission.ACCESS_NOTIFICATIONS:
-                case android.Manifest.permission.MANAGE_IPSEC_TUNNELS:
+                case Manifest.permission.ACCESS_NOTIFICATIONS:
+                case Manifest.permission.MANAGE_IPSEC_TUNNELS:
                     continue;
-                case android.Manifest.permission.REQUEST_INSTALL_PACKAGES:
+                case Manifest.permission.REQUEST_INSTALL_PACKAGES:
                     // Settings allows the user to control the app op if it's not in the default
                     // mode, regardless of whether the app has requested the permission, so we
                     // should not reset it.
@@ -251,7 +285,7 @@
                 }
                 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                 // If there is no valid package for the given UID, return immediately
-                if (packageManagerInternal.getPackage(uid) == null) {
+                if (mPackageManagerInternal.getPackage(uid) == null) {
                     return;
                 }
 
@@ -343,6 +377,18 @@
                 }
             }
         }
+
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            ((Internal) LocalServices.getService(PermissionPolicyInternal.class))
+                    .onActivityManagerReady();
+        }
+
+        if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+            synchronized (mLock) {
+                mBootCompleted = true;
+            }
+        }
+
     }
 
     /**
@@ -748,10 +794,12 @@
 
             String permissionName = permissionInfo.name;
             String packageName = packageInfo.packageName;
+            UserHandle user = UserHandle.getUserHandleForUid(packageInfo.applicationInfo.uid);
             int permissionFlags = mPackageManager.getPermissionFlags(permissionName,
                     packageName, mContext.getUser());
             boolean isReviewRequired = (permissionFlags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
-            if (isReviewRequired) {
+            if (isReviewRequired && !CompatChanges.isChangeEnabled(
+                    NOTIFICATION_PERM_CHANGE_ID, packageName, user)) {
                 return;
             }
 
@@ -953,6 +1001,33 @@
 
     private class Internal extends PermissionPolicyInternal {
 
+        private ActivityInterceptorCallback mActivityInterceptorCallback =
+                new ActivityInterceptorCallback() {
+                    @Nullable
+                    @Override
+                    public ActivityInterceptorCallback.ActivityInterceptResult intercept(
+                            ActivityInterceptorInfo info) {
+                        return null;
+                    }
+
+                    @Override
+                    public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
+                        super.onActivityLaunched(taskInfo, activityInfo);
+                        clearNotificationReviewFlagsIfNeeded(activityInfo.packageName,
+                                UserHandle.of(taskInfo.userId));
+                        showNotificationPromptIfNeeded(activityInfo.packageName,
+                                taskInfo.userId, taskInfo.taskId);
+                    }
+                };
+
+        private void onActivityManagerReady() {
+            ActivityTaskManagerInternal atm =
+                    LocalServices.getService(ActivityTaskManagerInternal.class);
+            atm.registerActivityStartInterceptor(
+                    ActivityInterceptorCallback.PERMISSION_POLICY_ORDERED_ID,
+                    mActivityInterceptorCallback);
+        }
+
         @Override
         public boolean checkStartActivity(@NonNull Intent intent, int callingUid,
                 @Nullable String callingPackage) {
@@ -962,9 +1037,53 @@
                         + callingPackage + " (uid=" + callingUid + ")");
                 return false;
             }
+
+            if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
+                    && (callingUid != Process.SYSTEM_UID || !SYSTEM_PKG.equals(callingPackage))) {
+                return false;
+            }
+
             return true;
         }
 
+        public void showNotificationPromptIfNeeded(@NonNull String packageName, int userId,
+                int taskId) {
+            UserHandle user = UserHandle.of(userId);
+            if (packageName == null || taskId == ActivityTaskManager.INVALID_TASK_ID
+                    || !shouldForceShowNotificationPermissionRequest(packageName, user)) {
+                return;
+            }
+
+            launchNotificationPermissionRequestDialog(packageName, user, taskId);
+        }
+
+        private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle userId) {
+            if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, userId)) {
+                return;
+            }
+            mPackageManager.updatePermissionFlags(POST_NOTIFICATIONS, packageName,
+                    FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId);
+        }
+
+        private void launchNotificationPermissionRequestDialog(String pkgName, UserHandle user,
+                int taskId) {
+            Intent grantPermission = mPackageManager
+                    .buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS });
+            grantPermission.setAction(
+                    PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
+            grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);
+
+            ActivityOptions options = new ActivityOptions(new Bundle());
+            options.setTaskOverlay(true, false);
+            options.setLaunchTaskId(taskId);
+            try {
+                mContext.startActivityAsUser(grantPermission, options.toBundle(), user);
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "couldn't start grant permission dialog"
+                        + "for other package " + pkgName, e);
+            }
+        }
+
         @Override
         public boolean isInitialized(int userId) {
             return isStarted(userId);
@@ -977,6 +1096,12 @@
             }
         }
 
+        @Override
+        public boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo) {
+            return taskInfo != null && taskInfo.isFocused && taskInfo.isVisible
+                    && taskInfo.isRunning;
+        }
+
         /**
          * Check if the intent action is removed for the calling package (often based on target SDK
          * version). If the action is removed, we'll silently cancel the activity launch.
@@ -1010,5 +1135,49 @@
                     return false;
             }
         }
+
+        private boolean shouldForceShowNotificationPermissionRequest(@NonNull String pkgName,
+                @NonNull UserHandle user) {
+            AndroidPackage pkg = mPackageManagerInternal.getPackage(pkgName);
+            if (pkg == null || pkg.getPackageName() == null
+                    || Objects.equals(pkgName, mPackageManager.getPermissionControllerPackageName())
+                    || pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+                Slog.w(LOG_TAG, "Cannot check for Notification prompt, no package for "
+                        + pkgName + " or pkg is Permission Controller");
+                return false;
+            }
+
+            synchronized (mLock) {
+                if (!mBootCompleted) {
+                    return false;
+                }
+            }
+
+            try {
+                if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, UserHandle.USER_SYSTEM)
+                        == 0) {
+                    return false;
+                }
+            } catch (Settings.SettingNotFoundException e) {
+                return false;
+            }
+
+            if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
+                    || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
+                    pkg.getPackageName(), user)) {
+                return false;
+            }
+
+            int uid = user.getUid(pkg.getUid());
+            if (mNotificationManager == null) {
+                mNotificationManager = LocalServices.getService(NotificationManagerInternal.class);
+            }
+            boolean hasCreatedNotificationChannels = mNotificationManager
+                    .getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0;
+            boolean needsReview = (mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName,
+                    user) & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
+            return hasCreatedNotificationChannels && needsReview;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 28f65cf..7dd9425 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5401,18 +5401,18 @@
         if (!mVibrator.hasVibrator()) {
             return false;
         }
-        final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
-        if (hapticsDisabled && !always) {
-            return false;
-        }
-
         VibrationEffect effect = getVibrationEffect(effectId);
         if (effect == null) {
             return false;
         }
-
-        mVibrator.vibrate(uid, packageName, effect, reason, getVibrationAttributes(effectId));
+        VibrationAttributes attrs = getVibrationAttributes(effectId);
+        if (always) {
+            attrs = new VibrationAttributes.Builder(attrs)
+                    .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
+                            VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+                    .build();
+        }
+        mVibrator.vibrate(uid, packageName, effect, reason, attrs);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 70a804b..73ec2cd 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -545,7 +545,7 @@
     /**
      * Called when there has been user activity.
      */
-    public void onUserActivity(int event, int uid) {
+    public void onUserActivity(int displayGroupId, int event, int uid) {
         if (DEBUG) {
             Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
         }
@@ -560,7 +560,8 @@
             if (!mUserActivityPending) {
                 mUserActivityPending = true;
                 Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
-                msg.arg1 = event;
+                msg.arg1 = displayGroupId;
+                msg.arg2 = event;
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
             }
@@ -633,14 +634,15 @@
     /**
      * Called when the screen policy changes.
      */
-    public void onScreenPolicyUpdate(int newPolicy) {
+    public void onScreenPolicyUpdate(int displayGroupId, int newPolicy) {
         if (DEBUG) {
             Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy);
         }
 
         synchronized (mLock) {
             Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY);
-            msg.arg1 = newPolicy;
+            msg.arg1 = displayGroupId;
+            msg.arg2 = newPolicy;
             msg.setAsynchronous(true);
             mHandler.sendMessage(msg);
         }
@@ -675,7 +677,7 @@
         mSuspendBlocker.release();
     }
 
-    private void sendUserActivity(int event) {
+    private void sendUserActivity(int displayGroupId, int event) {
         synchronized (mLock) {
             if (!mUserActivityPending) {
                 return;
@@ -686,7 +688,7 @@
         tm.notifyUserActivity();
         mPolicy.userActivity();
         mFaceDownDetector.userActivity(event);
-        mScreenUndimDetector.userActivity();
+        mScreenUndimDetector.userActivity(displayGroupId);
     }
 
     void postEnhancedDischargePredictionBroadcast(long delayMs) {
@@ -840,8 +842,8 @@
         mSuspendBlocker.release();
     }
 
-    private void screenPolicyChanging(int screenPolicy) {
-        mScreenUndimDetector.recordScreenPolicy(screenPolicy);
+    private void screenPolicyChanging(int displayGroupId, int screenPolicy) {
+        mScreenUndimDetector.recordScreenPolicy(displayGroupId, screenPolicy);
     }
 
     private void lockProfile(@UserIdInt int userId) {
@@ -866,7 +868,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_USER_ACTIVITY:
-                    sendUserActivity(msg.arg1);
+                    sendUserActivity(msg.arg1, msg.arg2);
                     break;
                 case MSG_BROADCAST:
                     sendNextBroadcast();
@@ -885,7 +887,7 @@
                     showWiredChargingStarted(msg.arg1);
                     break;
                 case MSG_SCREEN_POLICY:
-                    screenPolicyChanging(msg.arg1);
+                    screenPolicyChanging(msg.arg1, msg.arg2);
                     break;
             }
         }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1455326..abfa016 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -49,6 +49,7 @@
 import android.database.ContentObserver;
 import android.hardware.SensorManager;
 import android.hardware.SystemSensorManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -60,6 +61,7 @@
 import android.os.BatterySaverPolicyConfig;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.IPowerManager;
 import android.os.Looper;
@@ -100,7 +102,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.os.BackgroundThread;
@@ -288,7 +289,6 @@
     private BatteryManagerInternal mBatteryManagerInternal;
     private DisplayManagerInternal mDisplayManagerInternal;
     private IBatteryStats mBatteryStats;
-    private IAppOpsService mAppOps;
     private WindowManagerPolicy mPolicy;
     private Notifier mNotifier;
     private WirelessChargerDetector mWirelessChargerDetector;
@@ -296,7 +296,7 @@
     private DreamManagerInternal mDreamManager;
     private LogicalLight mAttentionLight;
 
-    private InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
+    private final InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
     private final AmbientDisplaySuppressionController mAmbientDisplaySuppressionController;
 
     private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER);
@@ -316,10 +316,10 @@
 
     // Table of all suspend blockers.
     // There should only be a few of these.
-    private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>();
+    private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<>();
 
     // Table of all wake locks acquired by applications.
-    private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>();
+    private final ArrayList<WakeLock> mWakeLocks = new ArrayList<>();
 
     // A bitfield that summarizes the state of all active wakelocks.
     private int mWakeLockSummary;
@@ -352,8 +352,6 @@
     private long mLastScreenBrightnessBoostTime;
     private boolean mScreenBrightnessBoostInProgress;
 
-    private DisplayGroupPowerChangeListener mDisplayGroupPowerChangeListener;
-
     // The suspend blocker used to keep the CPU alive while the device is booting.
     private final SuspendBlocker mBootingSuspendBlocker;
 
@@ -936,7 +934,7 @@
          * Handler for asynchronous operations performed by the power manager.
          */
         Handler createHandler(Looper looper, Handler.Callback callback) {
-            return new Handler(looper, callback, true /*async*/);
+            return new Handler(looper, callback, /* async= */ true);
         }
 
         void invalidateIsInteractiveCaches() {
@@ -971,7 +969,7 @@
         mInjector = injector;
 
         mHandlerThread = new ServiceThread(TAG,
-                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+                Process.THREAD_PRIORITY_DISPLAY, /* allowIo= */ false);
         mHandlerThread.start();
         mHandler = injector.createHandler(mHandlerThread.getLooper(),
                 new PowerManagerHandlerCallback());
@@ -1151,22 +1149,25 @@
                             PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
                 }
+
+                mContext.getSystemService(DeviceStateManager.class).registerCallback(
+                        new HandlerExecutor(mHandler), new DeviceStateListener());
             }
         }
     }
 
-    public void systemReady(IAppOpsService appOps) {
+    public void systemReady() {
         synchronized (mLock) {
             mSystemReady = true;
-            mAppOps = appOps;
             mDreamManager = getLocalService(DreamManagerInternal.class);
             mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
             mPolicy = getLocalService(WindowManagerPolicy.class);
             mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
             mAttentionDetector.systemReady(mContext);
             mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP, new PowerGroup());
-            mDisplayGroupPowerChangeListener = new DisplayGroupPowerChangeListener();
-            mDisplayManagerInternal.registerDisplayGroupListener(mDisplayGroupPowerChangeListener);
+            DisplayGroupPowerChangeListener displayGroupPowerChangeListener =
+                    new DisplayGroupPowerChangeListener();
+            mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener);
 
             SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
 
@@ -1718,6 +1719,7 @@
     }
 
     // Called from native code.
+    @SuppressWarnings("unused")
     private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
         userActivityInternal(displayId, eventTime, event, flags, Process.SYSTEM_UID);
     }
@@ -1752,7 +1754,7 @@
             if (userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
                     mClock.uptimeMillis(),
                     PowerManager.USER_ACTIVITY_EVENT_ATTENTION,
-                    0 /* flags */,
+                    /* flags= */ 0,
                     Process.SYSTEM_UID)) {
                 updatePowerStateLocked();
             }
@@ -1792,7 +1794,7 @@
                 mLastInteractivePowerHintTime = eventTime;
             }
 
-            mNotifier.onUserActivity(event, uid);
+            mNotifier.onUserActivity(powerGroup.getGroupId(), event, uid);
             mAttentionDetector.onUserActivity(eventTime, event);
 
             if (mUserInactiveOverrideFromWindowManager) {
@@ -2041,6 +2043,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     @GuardedBy("mLock")
     private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,
             int opUid, String opPackageName, String details) {
@@ -2486,9 +2489,8 @@
      * Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
      * Note that most wake-locks are ignored when the system is asleep.
      *
-     * This function must have no other side-effects.
+     * This function must have no other side effects.
      */
-    @SuppressWarnings("deprecation")
     @GuardedBy("mLock")
     private void updateWakeLockSummaryLocked(int dirty) {
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS | DIRTY_DISPLAY_GROUP_WAKEFULNESS))
@@ -2591,6 +2593,7 @@
     }
 
     /** Get wake lock summary flags that correspond to the given wake lock. */
+    @SuppressWarnings("deprecation")
     private int getWakeLockSummaryFlags(WakeLock wakeLock) {
         switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.PARTIAL_WAKE_LOCK:
@@ -3165,7 +3168,7 @@
         if (mDreamManager != null) {
             // Restart the dream whenever the sandman is summoned.
             if (startDreaming) {
-                mDreamManager.stopDream(false /*immediate*/);
+                mDreamManager.stopDream(/* immediate= */ false);
                 mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
             }
             isDreaming = mDreamManager.isDreaming();
@@ -3249,7 +3252,7 @@
 
         // Stop dream.
         if (isDreaming) {
-            mDreamManager.stopDream(false /*immediate*/);
+            mDreamManager.stopDream(/* immediate= */ false);
         }
     }
 
@@ -3372,7 +3375,7 @@
 
                 final boolean ready = mDisplayManagerInternal.requestPowerState(groupId,
                         displayPowerRequest, mRequestWaitForNegativeProximity);
-                mNotifier.onScreenPolicyUpdate(displayPowerRequest.policy);
+                mNotifier.onScreenPolicyUpdate(groupId, displayPowerRequest.policy);
 
                 if (DEBUG_SPEW) {
                     Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
@@ -3513,7 +3516,7 @@
                 mDirty |= DIRTY_PROXIMITY_POSITIVE;
                 userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
                         mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                        0 /* flags */, Process.SYSTEM_UID);
+                        /* flags= */ 0, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
         }
@@ -4297,7 +4300,7 @@
         if (sQuiescent) {
             // Pass the optional "quiescent" argument to the bootloader to let it know
             // that it should not turn the screen/lights on.
-            if (reason != ""){
+            if (!"".equals(reason)) {
                 reason += ",";
             }
             reason = reason + "quiescent";
@@ -5407,8 +5410,8 @@
                 ws = new WorkSource();
                 // XXX should WorkSource have a way to set uids as an int[] instead of adding them
                 // one at a time?
-                for (int i = 0; i < uids.length; i++) {
-                    ws.add(uids[i]);
+                for (int uid : uids) {
+                    ws.add(uid);
                 }
             }
             updateWakeLockWorkSource(lock, ws, null);
@@ -5949,7 +5952,8 @@
             // if uid is of root's, we permit this operation straight away
             if (uid != Process.ROOT_UID) {
                 if (!Settings.checkAndNoteWriteSettingsOperation(mContext, uid,
-                        Settings.getPackageNameForUid(mContext, uid), true)) {
+                        Settings.getPackageNameForUid(mContext, uid), /* attributionTag= */ null,
+                        /* throwException= */ true)) {
                     return;
                 }
             }
@@ -6113,6 +6117,7 @@
             for (String arg : args) {
                 if (arg.equals("--proto")) {
                     isDumpProto = true;
+                    break;
                 }
             }
             try {
@@ -6349,4 +6354,25 @@
             return interceptPowerKeyDownInternal(event);
         }
     }
+
+    /**
+     * Listens to changes in device state and updates the interactivity time.
+     * Any changes to the device state are treated as user interactions.
+     */
+    class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
+        private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+
+        @Override
+        public void onStateChanged(int deviceState) {
+            if (mDeviceState != deviceState) {
+                mDeviceState = deviceState;
+                // Device-state interactions are applied to the default display so that they
+                // are reflected only with the default power group.
+                userActivityInternal(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_DEVICE_STATE, /* flags= */0,
+                        Process.SYSTEM_UID);
+            }
+        }
+    };
+
 }
diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java
index 951bc1f..c4929c2 100644
--- a/services/core/java/com/android/server/power/ScreenUndimDetector.java
+++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java
@@ -28,6 +28,7 @@
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.util.Slog;
+import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
@@ -123,8 +124,8 @@
      * Launches a message that figures out the screen transitions and detects user undims. Must be
      * called by the parent that is trying to update the screen policy.
      */
-    public void recordScreenPolicy(int newPolicy) {
-        if (newPolicy == mCurrentScreenPolicy) {
+    public void recordScreenPolicy(int displayGroupId, int newPolicy) {
+        if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP || newPolicy == mCurrentScreenPolicy) {
             return;
         }
 
@@ -268,7 +269,10 @@
      * The user interacted with the screen after an undim, indicating the phone is in use.
      * We use this event for logging.
      */
-    public void userActivity() {
+    public void userActivity(int displayGroupId) {
+        if (displayGroupId != Display.DEFAULT_DISPLAY) {
+            return;
+        }
         if (mUndimOccurredTime != 1 && mInteractionAfterUndimTime == -1) {
             mInteractionAfterUndimTime = mClock.getCurrentTime();
         }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index e94575c4..b03db66 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -213,7 +213,7 @@
         CloseDialogReceiver(Context context) {
             mContext = context;
             IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-            context.registerReceiver(this, filter);
+            context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index ef0079e..ca67597 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -35,7 +35,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
 import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
 import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -313,12 +312,12 @@
         return mStartWallTime;
     }
 
-    public PowerStatsLogger(Context context, File dataStoragePath,
+    public PowerStatsLogger(Context context, Looper looper, File dataStoragePath,
             String meterFilename, String meterCacheFilename,
             String modelFilename, String modelCacheFilename,
             String residencyFilename, String residencyCacheFilename,
             IPowerStatsHALWrapper powerStatsHALWrapper) {
-        super(Looper.getMainLooper());
+        super(looper);
         mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
         if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
         mPowerStatsHALWrapper = powerStatsHALWrapper;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index bb52c1d..9953ca8 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -28,6 +28,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.power.PowerStatsInternal;
 import android.util.Slog;
@@ -79,6 +80,9 @@
     private StatsPullAtomCallbackImpl mPullAtomCallback;
     @Nullable
     private PowerStatsInternal mPowerStatsInternal;
+    @Nullable
+    @GuardedBy("this")
+    private Looper mLooper;
 
     @VisibleForTesting
     static class Injector {
@@ -127,12 +131,12 @@
             }
         }
 
-        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
-                String meterFilename, String meterCacheFilename,
+        PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+                File dataStoragePath, String meterFilename, String meterCacheFilename,
                 String modelFilename, String modelCacheFilename,
                 String residencyFilename, String residencyCacheFilename,
                 IPowerStatsHALWrapper powerStatsHALWrapper) {
-            return new PowerStatsLogger(context, dataStoragePath,
+            return new PowerStatsLogger(context, looper, dataStoragePath,
                 meterFilename, meterCacheFilename,
                 modelFilename, modelCacheFilename,
                 residencyFilename, residencyCacheFilename,
@@ -229,11 +233,11 @@
             mDataStoragePath = mInjector.createDataStoragePath();
 
             // Only start logger and triggers if initialization is successful.
-            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath,
-                mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(),
-                mInjector.createModelFilename(), mInjector.createModelCacheFilename(),
-                mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(),
-                getPowerStatsHal());
+            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(),
+                    mDataStoragePath, mInjector.createMeterFilename(),
+                    mInjector.createMeterCacheFilename(), mInjector.createModelFilename(),
+                    mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(),
+                    mInjector.createResidencyCacheFilename(), getPowerStatsHal());
             mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
             mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
         } else {
@@ -245,6 +249,17 @@
         return mInjector.getPowerStatsHALWrapperImpl();
     }
 
+    private Looper getLooper() {
+        synchronized (this) {
+            if (mLooper == null) {
+                HandlerThread thread = new HandlerThread(TAG);
+                thread.start();
+                return thread.getLooper();
+            }
+            return mLooper;
+        }
+    }
+
     public PowerStatsService(Context context) {
         this(context, new Injector());
     }
@@ -260,9 +275,7 @@
         private final Handler mHandler;
 
         LocalService() {
-            HandlerThread thread = new HandlerThread(TAG);
-            thread.start();
-            mHandler = new Handler(thread.getLooper());
+            mHandler = new Handler(getLooper());
         }
 
 
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 0686265..21d4cbb 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.speech.IRecognitionListener;
 import android.speech.IRecognitionService;
+import android.speech.IRecognitionSupportCallback;
 import android.speech.RecognitionService;
 import android.speech.SpeechRecognizer;
 import android.util.Log;
@@ -211,6 +212,30 @@
         }
     }
 
+    void checkRecognitionSupport(
+            Intent recognizerIntent,
+            IRecognitionSupportCallback callback) {
+
+        if (!mConnected) {
+            try {
+                callback.onError(SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to report the connection broke to the caller.", e);
+                e.printStackTrace();
+            }
+            return;
+        }
+        run(service -> service.checkRecognitionSupport(recognizerIntent, callback));
+    }
+
+    void triggerModelDownload(Intent recognizerIntent) {
+        if (!mConnected) {
+            Slog.e(TAG, "#downloadModel failed due to connection.");
+            return;
+        }
+        run(service -> service.triggerModelDownload(recognizerIntent));
+    }
+
     void shutdown() {
         synchronized (mLock) {
             if (this.mListener == null) {
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 5442e5b..ae23b9e 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -33,6 +33,7 @@
 import android.speech.IRecognitionListener;
 import android.speech.IRecognitionService;
 import android.speech.IRecognitionServiceManagerCallback;
+import android.speech.IRecognitionSupportCallback;
 import android.speech.RecognitionService;
 import android.speech.SpeechRecognizer;
 import android.util.Slog;
@@ -169,6 +170,18 @@
                                 clientToken.unlinkToDeath(deathRecipient, 0);
                             }
                         }
+
+                        @Override
+                        public void checkRecognitionSupport(
+                                Intent recognizerIntent,
+                                IRecognitionSupportCallback callback) {
+                            service.checkRecognitionSupport(recognizerIntent, callback);
+                        }
+
+                        @Override
+                        public void triggerModelDownload(Intent recognizerIntent) {
+                            service.triggerModelDownload(recognizerIntent);
+                        }
                     });
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Error creating a speech recognition session", e);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b1cc517..b7ca4de 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -28,18 +28,11 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkIdentity.OEM_PAID;
 import static android.net.NetworkIdentity.OEM_PRIVATE;
-import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
-import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkTemplate.MATCH_ETHERNET;
-import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
-import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD;
-import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
-import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
-import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
-import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
 import static android.net.NetworkTemplate.getAllCollapsedRatTypes;
 import static android.os.Debug.getIonHeapsSizeKb;
 import static android.os.Process.LAST_SHARED_APPLICATION_GID;
@@ -128,7 +121,6 @@
 import android.os.OutcomeReceiver;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
@@ -238,7 +230,6 @@
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -1179,9 +1170,10 @@
             }
             case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
                 final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
-                        buildTemplateWifiWildcard(), /*includeTags=*/true);
+                        new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
                 final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate(
-                        buildTemplateMobileWildcard(), /*includeTags=*/true);
+                        new NetworkTemplate.Builder(MATCH_MOBILE)
+                        .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
                 if (wifiStats != null && cellularStats != null) {
                     final NetworkStats stats = wifiStats.add(cellularStats);
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
@@ -1333,8 +1325,8 @@
     @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
         final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of(
                 new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET),
-                new Pair(MATCH_MOBILE_WILDCARD, TRANSPORT_CELLULAR),
-                new Pair(MATCH_WIFI_WILDCARD, TRANSPORT_WIFI)
+                new Pair(MATCH_MOBILE, TRANSPORT_CELLULAR),
+                new Pair(MATCH_WIFI, TRANSPORT_WIFI)
         );
         final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE};
 
@@ -1343,12 +1335,11 @@
         for (Pair<Integer, Integer> ruleAndTransport : matchRulesAndTransports) {
             final Integer matchRule = ruleAndTransport.first;
             for (final int oemManaged : oemManagedTypes) {
-                /* A null subscriberId will set wildcard=true, since we aren't trying to select a
-                   specific ssid or subscriber. */
-                final NetworkTemplate template = new NetworkTemplate(matchRule,
-                        /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
-                        METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
-                        oemManaged);
+                // Subscriber Ids and Wifi Network Keys will not be set since the purpose is to
+                // slice statistics of different OEM managed networks among all network types.
+                // Thus, specifying networks through their identifiers are not needed.
+                final NetworkTemplate template = new NetworkTemplate.Builder(matchRule)
+                        .setOemManaged(oemManaged).build();
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, false);
                 final Integer transport = ruleAndTransport.second;
                 if (stats != null) {
@@ -1367,10 +1358,18 @@
      * Create a snapshot of NetworkStats for a given transport.
      */
     @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
-        final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
-                ? NetworkTemplate.buildTemplateMobileWithRatType(
-                /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES)
-                : NetworkTemplate.buildTemplateWifiWildcard();
+        NetworkTemplate template = null;
+        switch (transport) {
+            case TRANSPORT_CELLULAR:
+                template = new NetworkTemplate.Builder(MATCH_MOBILE)
+                        .setMeteredness(METERED_YES).build();
+                break;
+            case TRANSPORT_WIFI:
+                template = new NetworkTemplate.Builder(MATCH_WIFI).build();
+                break;
+            default:
+                Log.wtf(TAG, "Unexpected transport.");
+        }
         return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
     }
 
@@ -1409,8 +1408,10 @@
         final List<NetworkStatsExt> ret = new ArrayList<>();
         for (final int ratType : getAllCollapsedRatTypes()) {
             final NetworkTemplate template =
-                    buildTemplateMobileWithRatType(subInfo.subscriberId, ratType,
-                    METERED_YES);
+                    new NetworkTemplate.Builder(MATCH_MOBILE)
+                    .setSubscriberIds(Set.of(subInfo.subscriberId))
+                    .setRatType(ratType)
+                    .setMeteredness(METERED_YES).build();
             final NetworkStats stats =
                     getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
             if (stats != null) {
@@ -2329,11 +2330,7 @@
         List<ProcessMemoryState> managedProcessList =
                 LocalServices.getService(ActivityManagerInternal.class)
                         .getMemoryStateForProcesses();
-        managedProcessList.sort(Comparator.comparingInt(x -> x.oomScore));
         for (ProcessMemoryState process : managedProcessList) {
-            if (process.uid == Process.SYSTEM_UID) {
-                continue;
-            }
             KernelAllocationStats.ProcessDmabuf proc =
                     KernelAllocationStats.getDmabufAllocations(process.pid);
             if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
@@ -2350,6 +2347,32 @@
                             proc.mappedSizeKb,
                             proc.mappedBuffersCount));
         }
+        SparseArray<String> processCmdlines = getProcessCmdlines();
+        managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
+        int size = processCmdlines.size();
+        for (int i = 0; i < size; ++i) {
+            int pid = processCmdlines.keyAt(i);
+            int uid = getUidForPid(pid);
+            // ignore root processes (unlikely to be interesting)
+            if (uid <= 0) {
+                continue;
+            }
+            KernelAllocationStats.ProcessDmabuf proc =
+                    KernelAllocationStats.getDmabufAllocations(pid);
+            if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
+                continue;
+            }
+            pulledData.add(
+                    FrameworkStatsLog.buildStatsEvent(
+                            atomTag,
+                            uid,
+                            processCmdlines.valueAt(i),
+                            -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/,
+                            proc.retainedSizeKb,
+                            proc.retainedBuffersCount,
+                            proc.mappedSizeKb,
+                            proc.mappedBuffersCount));
+        }
         return StatsManager.PULL_SUCCESS;
     }
 
@@ -3432,7 +3455,11 @@
                     convertTimeZoneSuggestionToProtoBytes(
                             metricsState.getLatestTelephonySuggestion()),
                     convertTimeZoneSuggestionToProtoBytes(
-                            metricsState.getLatestGeolocationSuggestion())
+                            metricsState.getLatestGeolocationSuggestion()),
+                    metricsState.isTelephonyTimeZoneFallbackSupported(),
+                    metricsState.getDeviceTimeZoneId(),
+                    metricsState.isEnhancedMetricsCollectionEnabled(),
+                    metricsState.getGeoDetectionRunInBackgroundEnabled()
             ));
         } catch (RuntimeException e) {
             Slog.e(TAG, "Getting time zone detection state failed: ", e);
@@ -3479,6 +3506,14 @@
                         android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
                         zoneIdOrdinal);
             }
+            String[] zoneIds = suggestion.getZoneIds();
+            if (zoneIds != null) {
+                for (String zoneId : zoneIds) {
+                    protoOutputStream.write(
+                            android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_IDS,
+                            zoneId);
+                }
+            }
         }
         protoOutputStream.flush();
         closeQuietly(byteArrayOutputStream);
@@ -4584,7 +4619,8 @@
         int matchContentFrameRateUserPreference =
                 displayManager.getMatchContentFrameRateUserPreference();
         byte[] userDisabledHdrTypes = toBytes(displayManager.getUserDisabledHdrTypes());
-        Display.Mode userPreferredDisplayMode = displayManager.getUserPreferredDisplayMode();
+        Display.Mode userPreferredDisplayMode =
+                displayManager.getGlobalUserPreferredDisplayMode();
         int userPreferredWidth = userPreferredDisplayMode != null
                 ? userPreferredDisplayMode.getPhysicalWidth() : -1;
         int userPreferredHeight = userPreferredDisplayMode != null
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index cdab91b..411f3dc 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -136,7 +136,8 @@
             @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName);
 
     /** @see com.android.internal.statusbar.IStatusBar#showTransient */
-    void showTransient(int displayId, @InternalInsetsType int[] types);
+    void showTransient(int displayId, @InternalInsetsType int[] types,
+            boolean isGestureOnSystemBar);
 
     /** @see com.android.internal.statusbar.IStatusBar#abortTransient */
     void abortTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 4eae939..0edd06a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -18,7 +18,11 @@
 
 import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
 import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
+import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
+import static android.app.StatusBarManager.NavBarModeOverride;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -35,6 +39,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.om.IOverlayManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
@@ -57,8 +62,10 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.NotificationStats;
 import android.service.quicksettings.TileService;
 import android.text.TextUtils;
@@ -75,6 +82,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IAddTileResultCallback;
@@ -150,6 +158,8 @@
     private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>();
     private static final long REQUEST_TIME_OUT = TimeUnit.MINUTES.toNanos(5);
 
+    private IOverlayManager mOverlayManager;
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
             mBar.asBinder().unlinkToDeath(this,0);
@@ -252,6 +262,18 @@
         mTileRequestTracker = new TileRequestTracker(mContext);
     }
 
+    private IOverlayManager getOverlayManager() {
+        // No need to synchronize; worst-case scenario it will be fetched twice.
+        if (mOverlayManager == null) {
+            mOverlayManager = IOverlayManager.Stub.asInterface(
+                    ServiceManager.getService(Context.OVERLAY_SERVICE));
+            if (mOverlayManager == null) {
+                Slog.w("StatusBarManager", "warning: no OVERLAY_SERVICE");
+            }
+        }
+        return mOverlayManager;
+    }
+
     @Override
     public void onDisplayAdded(int displayId) {}
 
@@ -563,11 +585,12 @@
         }
 
         @Override
-        public void showTransient(int displayId, @InternalInsetsType int[] types) {
+        public void showTransient(int displayId, @InternalInsetsType int[] types,
+                boolean isGestureOnSystemBar) {
             getUiState(displayId).showTransient(types);
             if (mBar != null) {
                 try {
-                    mBar.showTransient(displayId, types);
+                    mBar.showTransient(displayId, types, isGestureOnSystemBar);
                 } catch (RemoteException ex) { }
             }
         }
@@ -1291,6 +1314,11 @@
         });
     }
 
+    @VisibleForTesting
+    void registerOverlayManager(IOverlayManager overlayManager) {
+        mOverlayManager = overlayManager;
+    }
+
     /**
      * @param clearNotificationEffects whether to consider notifications as "shown" and stop
      *     LED, vibration, and ringing
@@ -1846,6 +1874,74 @@
         return mContext.getResources().getStringArray(R.array.config_statusBarIcons);
     }
 
+    /**
+     * Sets or removes the navigation bar mode override.
+     *
+     * @param navBarModeOverride the mode of the navigation bar override to be set.
+     */
+    public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) {
+        enforceStatusBar();
+        if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE
+                && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) {
+            throw new UnsupportedOperationException(
+                    "Supplied navBarModeOverride not supported: " + navBarModeOverride);
+        }
+
+        final int userId = mCurrentUserId;
+        final long userIdentity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.NAV_BAR_KIDS_MODE, navBarModeOverride, userId);
+
+            IOverlayManager overlayManager = getOverlayManager();
+            if (overlayManager != null && navBarModeOverride == NAV_BAR_MODE_OVERRIDE_KIDS
+                    && isPackageSupported(NAV_BAR_MODE_3BUTTON_OVERLAY)) {
+                overlayManager.setEnabledExclusiveInCategory(NAV_BAR_MODE_3BUTTON_OVERLAY, userId);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } finally {
+            Binder.restoreCallingIdentity(userIdentity);
+        }
+    }
+
+    /**
+     * Gets the navigation bar mode override. Returns default value if no override is set.
+     *
+     * @hide
+     */
+    public @NavBarModeOverride int getNavBarModeOverride() {
+        enforceStatusBar();
+
+        int navBarKidsMode = NAV_BAR_MODE_OVERRIDE_NONE;
+        final int userId = mCurrentUserId;
+        final long userIdentity = Binder.clearCallingIdentity();
+        try {
+            navBarKidsMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.NAV_BAR_KIDS_MODE, userId);
+        } catch (Settings.SettingNotFoundException ex) {
+            return navBarKidsMode;
+        } finally {
+            Binder.restoreCallingIdentity(userIdentity);
+        }
+        return navBarKidsMode;
+    }
+
+    private boolean isPackageSupported(String packageName) {
+        if (packageName == null) {
+            return false;
+        }
+        try {
+            return mContext.getPackageManager().getPackageInfo(packageName,
+                    PackageManager.PackageInfoFlags.of(0)) != null;
+        } catch (PackageManager.NameNotFoundException ignored) {
+            if (SPEW) {
+                Slog.d(TAG, "Package not found: " + packageName);
+            }
+        }
+        return false;
+    }
+
     /** @hide */
     public void passThroughShellCommand(String[] args, FileDescriptor fd) {
         enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 3093509..c0207f0 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -37,6 +37,7 @@
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.provider.DeviceConfig;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.DataUnit;
@@ -255,11 +256,14 @@
     private void checkHigh() {
         final StorageManager storage = getContext().getSystemService(StorageManager.class);
         // Check every mounted private volume to see if they're under the high storage threshold
-        // which is StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space
+        // which is storageThresholdPercentHigh of total space
+        final int storageThresholdPercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY,
+                StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final File file = vol.getPath();
-            if (file.getUsableSpace() < file.getTotalSpace()
-                    * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
+            if (file.getUsableSpace() < file.getTotalSpace() * storageThresholdPercentHigh / 100) {
                 final PackageManagerService pms = (PackageManagerService) ServiceManager
                         .getService("package");
                 try {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index e98fa28..fe74167 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -528,12 +528,13 @@
                     callback.onFailure();
                     return;
                 }
-                textClassifierServiceConsumer.accept(serviceState.mService);
+                consumeServiceNoExceptLocked(textClassifierServiceConsumer, serviceState.mService);
             } else {
                 serviceState.mPendingRequests.add(
                         new PendingRequest(
                                 methodName,
-                                () -> textClassifierServiceConsumer.accept(serviceState.mService),
+                                () -> consumeServiceNoExceptLocked(
+                                        textClassifierServiceConsumer, serviceState.mService),
                                 callback::onFailure, callback.asBinder(),
                                 this,
                                 serviceState,
@@ -542,6 +543,16 @@
         }
     }
 
+    private static void consumeServiceNoExceptLocked(
+            @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
+            @Nullable ITextClassifierService service) {
+        try {
+            textClassifierServiceConsumer.accept(service);
+        } catch (RuntimeException | Error e) {
+            Slog.e(LOG_TAG, "Exception when consume textClassifierService: " + e);
+        }
+    }
+
     private static ITextClassifierCallback wrap(ITextClassifierCallback orig) {
         return new CallbackWrapper(orig);
     }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index b23f11a..36ab111d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -281,15 +281,7 @@
                 LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
                 LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
                 LocationTimeZoneProviderController.MetricsLogger metricsLogger =
-                        new LocationTimeZoneProviderController.MetricsLogger() {
-                            @Override
-                            public void onStateChange(
-                                    @LocationTimeZoneProviderController.State String state) {
-                                // TODO b/200279201 - wire this up to metrics code
-                                // No-op.
-                            }
-                        };
-
+                        new RealControllerMetricsLogger();
                 boolean recordStateChanges = mServiceConfigAccessor.getRecordStateChangesForTests();
                 LocationTimeZoneProviderController controller =
                         new LocationTimeZoneProviderController(mThreadingDomain, metricsLogger,
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java
new file mode 100644
index 0000000..9cb36ef
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__CERTAIN;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__DESTROYED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__FAILED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__INITIALIZING;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__PROVIDERS_INITIALIZING;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNCERTAIN;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNKNOWN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_PROVIDERS_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNKNOWN;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.MetricsLogger} which logs
+ * using {@link FrameworkStatsLog}.
+ */
+final class RealControllerMetricsLogger
+        implements LocationTimeZoneProviderController.MetricsLogger {
+
+    RealControllerMetricsLogger() {
+    }
+
+    @Override
+    public void onStateChange(@State String state) {
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED,
+                metricsState(state));
+    }
+
+    private static int metricsState(@State String state) {
+        switch (state) {
+            case STATE_PROVIDERS_INITIALIZING:
+                // Disable lint check (line length) for generated long constant name.
+                // CHECKSTYLE:OFF Generated code
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__PROVIDERS_INITIALIZING;
+                // CHECKSTYLE:ON Generated code
+            case STATE_STOPPED:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__STOPPED;
+            case STATE_INITIALIZING:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__INITIALIZING;
+            case STATE_CERTAIN:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__CERTAIN;
+            case STATE_UNCERTAIN:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNCERTAIN;
+            case STATE_DESTROYED:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__DESTROYED;
+            case STATE_FAILED:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__FAILED;
+            case STATE_UNKNOWN:
+            default:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNKNOWN;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
index e19ec84..fe543ad 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
@@ -41,12 +41,12 @@
  * The real implementation of {@link ProviderMetricsLogger} which logs using
  * {@link FrameworkStatsLog}.
  */
-public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+final class RealProviderMetricsLogger implements ProviderMetricsLogger {
 
     @IntRange(from = 0, to = 1)
     private final int mProviderIndex;
 
-    public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+    RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
         mProviderIndex = providerIndex;
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 59f8e54..06ce4a4 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -16,6 +16,8 @@
 
 package com.android.server.trust;
 
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE;
+
 import android.annotation.TargetApi;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -99,6 +101,7 @@
     // Trust state
     private boolean mTrusted;
     private CharSequence mMessage;
+    private boolean mDisplayTrustGrantedMessage;
     private boolean mTrustDisabledByDpm;
     private boolean mManagingTrust;
     private IBinder mSetTrustAgentFeaturesToken;
@@ -123,6 +126,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_GRANT_TRUST:
+                    // TODO(b/213631675): Respect FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
                     if (!isConnected()) {
                         Log.w(TAG, "Agent is not connected, cannot grant trust: "
                                 + mName.flattenToShortString());
@@ -131,6 +135,7 @@
                     mTrusted = true;
                     mMessage = (CharSequence) msg.obj;
                     int flags = msg.arg1;
+                    mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
                     long durationMs = msg.getData().getLong(DATA_DURATION);
                     if (durationMs > 0) {
                         final long duration;
@@ -165,6 +170,7 @@
                     // Fall through.
                 case MSG_REVOKE_TRUST:
                     mTrusted = false;
+                    mDisplayTrustGrantedMessage = false;
                     mMessage = null;
                     mHandler.removeMessages(MSG_TRUST_TIMEOUT);
                     if (msg.what == MSG_REVOKE_TRUST) {
@@ -198,6 +204,7 @@
                     mManagingTrust = msg.arg1 != 0;
                     if (!mManagingTrust) {
                         mTrusted = false;
+                        mDisplayTrustGrantedMessage = false;
                         mMessage = null;
                     }
                     mTrustManagerService.mArchive.logManagingTrust(mUserId, mName, mManagingTrust);
@@ -270,12 +277,13 @@
     private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
 
         @Override
-        public void grantTrust(CharSequence userMessage, long durationMs, int flags) {
-            if (DEBUG) Slog.d(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs
+        public void grantTrust(CharSequence message, long durationMs, int flags) {
+            if (DEBUG) {
+                Slog.d(TAG, "enableTrust(" + message + ", durationMs = " + durationMs
                         + ", flags = " + flags + ")");
+            }
 
-            Message msg = mHandler.obtainMessage(
-                    MSG_GRANT_TRUST, flags, 0, userMessage);
+            Message msg = mHandler.obtainMessage(MSG_GRANT_TRUST, flags, 0, message);
             msg.getData().putLong(DATA_DURATION, durationMs);
             msg.sendToTarget();
         }
@@ -460,6 +468,19 @@
     }
 
     /**
+     * @see android.service.trust.TrustAgentService#onUserRequestedUnlock()
+     */
+    public void onUserRequestedUnlock() {
+        try {
+            if (mTrustAgentService != null) {
+                mTrustAgentService.onUserRequestedUnlock();
+            }
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
      * @see android.service.trust.TrustAgentService#onUnlockLockout(int)
      */
     public void onUnlockLockout(int timeoutMs) {
@@ -578,6 +599,14 @@
         return mMessage;
     }
 
+    /**
+     * Whether the trust agent would like to display {@link #getMessage()} to the user when trust
+     * is granted.
+     */
+    public boolean shouldDisplayTrustGrantedMessage() {
+        return mDisplayTrustGrantedMessage;
+    }
+
     public void destroy() {
         mHandler.removeMessages(MSG_RESTART_TIMEOUT);
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4b71742..9bed24d 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -46,6 +46,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
@@ -74,7 +75,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -122,6 +122,9 @@
     private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
     private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
     private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
+    public static final int MSG_USER_REQUESTED_UNLOCK = 16;
+
+    private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
 
     private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
     private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
@@ -388,7 +391,6 @@
         }
     }
 
-
     public void updateTrust(int userId, int flags) {
         updateTrust(userId, flags, false /* isFromUnlock */);
     }
@@ -428,7 +430,7 @@
             changed = mUserIsTrusted.get(userId) != trusted;
             mUserIsTrusted.put(userId, trusted);
         }
-        dispatchOnTrustChanged(trusted, userId, flags);
+        dispatchOnTrustChanged(trusted, userId, flags, getTrustGrantedMessages(userId));
         if (changed) {
             refreshDeviceLockedForUser(userId);
             if (!trusted) {
@@ -635,11 +637,18 @@
         }
     }
 
+    private void refreshDeviceLockedForUser(int userId) {
+        refreshDeviceLockedForUser(userId, UserHandle.USER_NULL);
+    }
+
     /**
      * Update the user's locked state. Only applicable to users with a real keyguard
      * ({@link UserInfo#supportsSwitchToByUser}) and unsecured managed profiles.
+     *
+     * If this is called due to an unlock operation set unlockedUser to prevent the lock from
+     * being prematurely reset for that user while keyguard is still in the process of going away.
      */
-    private void refreshDeviceLockedForUser(int userId) {
+    private void refreshDeviceLockedForUser(int userId, int unlockedUser) {
         if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_SYSTEM) {
             Log.e(TAG, "refreshDeviceLockedForUser(userId=" + userId + "): Invalid user handle,"
                     + " must be USER_ALL or a specific user.", new Throwable("here"));
@@ -675,6 +684,7 @@
             boolean trusted = aggregateIsTrusted(id);
             boolean showingKeyguard = true;
             boolean biometricAuthenticated = false;
+            boolean currentUserIsUnlocked = false;
 
             if (mCurrentUser == id) {
                 synchronized(mUsersUnlockedByBiometric) {
@@ -683,10 +693,17 @@
                 try {
                     showingKeyguard = wm.isKeyguardLocked();
                 } catch (RemoteException e) {
+                    Log.w(TAG, "Unable to check keyguard lock state", e);
                 }
+                currentUserIsUnlocked = unlockedUser == id;
             }
-            boolean deviceLocked = secure && showingKeyguard && !trusted &&
-                    !biometricAuthenticated;
+            final boolean deviceLocked = secure && showingKeyguard && !trusted
+                    && !biometricAuthenticated;
+            if (deviceLocked && currentUserIsUnlocked) {
+                // keyguard is finishing but may not have completed going away yet
+                continue;
+            }
+
             setDeviceLockedForUser(id, deviceLocked);
         }
     }
@@ -933,6 +950,24 @@
         return false;
     }
 
+    private List<String> getTrustGrantedMessages(int userId) {
+        if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+            return new ArrayList<>();
+        }
+
+        List<String> trustGrantedMessages = new ArrayList<>();
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == userId
+                    && info.agent.isTrusted()
+                    && info.agent.shouldDisplayTrustGrantedMessage()
+                    && !TextUtils.isEmpty(info.agent.getMessage())) {
+                trustGrantedMessages.add(info.agent.getMessage().toString());
+            }
+        }
+        return trustGrantedMessages;
+    }
+
     private boolean aggregateIsTrustManaged(int userId) {
         if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
             return false;
@@ -963,6 +998,15 @@
         }
     }
 
+    private void dispatchUserRequestedUnlock(int userId) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == userId) {
+                info.agent.onUserRequestedUnlock();
+            }
+        }
+    }
+
     private void dispatchUnlockLockout(int timeoutMs, int userId) {
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
@@ -993,7 +1037,8 @@
         }
     }
 
-    private void dispatchOnTrustChanged(boolean enabled, int userId, int flags) {
+    private void dispatchOnTrustChanged(boolean enabled, int userId, int flags,
+            @NonNull List<String> trustGrantedMessages) {
         if (DEBUG) {
             Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x"
                     + Integer.toHexString(flags) + ")");
@@ -1001,7 +1046,7 @@
         if (!enabled) flags = 0;
         for (int i = 0; i < mTrustListeners.size(); i++) {
             try {
-                mTrustListeners.get(i).onTrustChanged(enabled, userId, flags);
+                mTrustListeners.get(i).onTrustChanged(enabled, userId, flags, trustGrantedMessages);
             } catch (DeadObjectException e) {
                 Slog.d(TAG, "Removing dead TrustListener.");
                 mTrustListeners.remove(i);
@@ -1092,6 +1137,12 @@
         }
 
         @Override
+        public void reportUserRequestedUnlock(int userId) throws RemoteException {
+            enforceReportPermission();
+            mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, userId).sendToTarget();
+        }
+
+        @Override
         public void reportUnlockLockout(int timeoutMs, int userId) throws RemoteException {
             enforceReportPermission();
             mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_LOCKOUT, timeoutMs, userId)
@@ -1306,13 +1357,20 @@
         }
 
         @Override
-        public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
+        public void clearAllBiometricRecognized(
+                BiometricSourceType biometricSource, int unlockedUser) {
             enforceReportPermission();
             synchronized(mUsersUnlockedByBiometric) {
                 mUsersUnlockedByBiometric.clear();
             }
-            mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
-                    0 /* arg2 */).sendToTarget();
+            Message message = mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER,
+                    UserHandle.USER_ALL, 0 /* arg2 */);
+            if (unlockedUser >= 0) {
+                Bundle bundle = new Bundle();
+                bundle.putInt(REFRESH_DEVICE_LOCKED_EXCEPT_USER, unlockedUser);
+                message.setData(bundle);
+            }
+            message.sendToTarget();
         }
     };
 
@@ -1364,6 +1422,9 @@
                 case MSG_DISPATCH_UNLOCK_ATTEMPT:
                     dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
                     break;
+                case MSG_USER_REQUESTED_UNLOCK:
+                    dispatchUserRequestedUnlock(msg.arg1);
+                    break;
                 case MSG_DISPATCH_UNLOCK_LOCKOUT:
                     dispatchUnlockLockout(msg.arg1, msg.arg2);
                     break;
@@ -1404,9 +1465,11 @@
                     break;
                 case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
                     if (msg.arg2 == 1) {
-                        updateTrust(msg.arg1, 0 /* flags */, true);
+                        updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */);
                     }
-                    refreshDeviceLockedForUser(msg.arg1);
+                    final int unlockedUser = msg.getData().getInt(
+                            REFRESH_DEVICE_LOCKED_EXCEPT_USER, UserHandle.USER_NULL);
+                    refreshDeviceLockedForUser(msg.arg1, unlockedUser);
                     break;
                 case MSG_SCHEDULE_TRUST_TIMEOUT:
                     handleScheduleTrustTimeout(msg.arg1, msg.arg2);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 568e4b8..e786fa2 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -69,6 +69,7 @@
 import android.media.tv.TvInputService;
 import android.media.tv.TvStreamConfig;
 import android.media.tv.TvTrackInfo;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -1850,18 +1851,19 @@
         }
 
         @Override
-        public void setIAppNotificationEnabled(IBinder sessionToken, boolean enabled, int userId) {
+        public void setInteractiveAppNotificationEnabled(
+                IBinder sessionToken, boolean enabled, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "setIAppNotificationEnabled");
+                    userId, "setInteractiveAppNotificationEnabled");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .setIAppNotificationEnabled(enabled);
+                                .setInteractiveAppNotificationEnabled(enabled);
                     } catch (RemoteException | SessionNotFoundException e) {
-                        Slog.e(TAG, "error in setIAppNotificationEnabled", e);
+                        Slog.e(TAG, "error in setInteractiveAppNotificationEnabled", e);
                     }
                 }
             } finally {
@@ -2538,6 +2540,31 @@
         }
 
         @Override
+        public int getClientPriority(int useCase, String sessionId) {
+            final int callingPid = Binder.getCallingPid();
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                int clientPid = TvInputManager.UNKNOWN_CLIENT_PID;
+                if (sessionId != null) {
+                    synchronized (mLock) {
+                        try {
+                            clientPid = getClientPidLocked(sessionId);
+                        } catch (ClientPidNotFoundException e) {
+                            Slog.e(TAG, "error in getClientPriority", e);
+                        }
+                    }
+                } else {
+                    clientPid = callingPid;
+                }
+                TunerResourceManager trm = (TunerResourceManager)
+                        mContext.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
+                return trm.getClientPriority(useCase, clientPid);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) {
             if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -2585,9 +2612,9 @@
 
         @GuardedBy("mLock")
         private int getClientPidLocked(String sessionId)
-                throws IllegalStateException {
+                throws ClientPidNotFoundException {
             if (mSessionIdToSessionStateMap.get(sessionId) == null) {
-                throw new IllegalStateException("Client Pid not found with sessionId "
+                throw new ClientPidNotFoundException("Client Pid not found with sessionId "
                         + sessionId);
             }
             return mSessionIdToSessionStateMap.get(sessionId).callingPid;
@@ -3512,6 +3539,23 @@
         }
 
         @Override
+        public void onSignalStrength(int strength) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onSignalStrength(" + strength + ")");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onSignalStrength(strength, mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onSignalStrength", e);
+                }
+            }
+        }
+
+        @Override
         public void onTuned(Uri channelUri) {
             synchronized (mLock) {
                 if (DEBUG) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
deleted file mode 100644
index a2bf2fe..0000000
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ /dev/null
@@ -1,2025 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.tv.interactive;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
-import android.graphics.Rect;
-import android.media.tv.AdRequest;
-import android.media.tv.AdResponse;
-import android.media.tv.BroadcastInfoRequest;
-import android.media.tv.BroadcastInfoResponse;
-import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppClient;
-import android.media.tv.interactive.ITvIAppManager;
-import android.media.tv.interactive.ITvIAppManagerCallback;
-import android.media.tv.interactive.ITvIAppService;
-import android.media.tv.interactive.ITvIAppServiceCallback;
-import android.media.tv.interactive.ITvIAppSession;
-import android.media.tv.interactive.ITvIAppSessionCallback;
-import android.media.tv.interactive.TvIAppInfo;
-import android.media.tv.interactive.TvIAppService;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.InputChannel;
-import android.view.Surface;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
-import com.android.server.SystemService;
-import com.android.server.utils.Slogf;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-
-/**
- * This class provides a system service that manages interactive TV applications.
- */
-public class TvIAppManagerService extends SystemService {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "TvIAppManagerService";
-    // A global lock.
-    private final Object mLock = new Object();
-    private final Context mContext;
-    // ID of the current user.
-    @GuardedBy("mLock")
-    private int mCurrentUserId = UserHandle.USER_SYSTEM;
-    // IDs of the running profiles. Their parent user ID should be mCurrentUserId.
-    @GuardedBy("mLock")
-    private final Set<Integer> mRunningProfiles = new HashSet<>();
-    // A map from user id to UserState.
-    @GuardedBy("mLock")
-    private final SparseArray<UserState> mUserStates = new SparseArray<>();
-
-    private final UserManager mUserManager;
-
-    /**
-     * Initializes the system service.
-     * <p>
-     * Subclasses must define a single argument constructor that accepts the context
-     * and passes it to super.
-     * </p>
-     *
-     * @param context The system server context.
-     */
-    public TvIAppManagerService(Context context) {
-        super(context);
-        mContext = context;
-        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
-    }
-
-    @GuardedBy("mLock")
-    private void buildTvIAppServiceListLocked(int userId, String[] updatedPackages) {
-        UserState userState = getOrCreateUserStateLocked(userId);
-        userState.mPackageSet.clear();
-
-        if (DEBUG) {
-            Slogf.d(TAG, "buildTvIAppServiceListLocked");
-        }
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> services = pm.queryIntentServicesAsUser(
-                new Intent(TvIAppService.SERVICE_INTERFACE),
-                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
-                userId);
-        List<TvIAppInfo> iAppList = new ArrayList<>();
-
-        for (ResolveInfo ri : services) {
-            ServiceInfo si = ri.serviceInfo;
-            // TODO: add BIND_TV_IAPP permission and check it here
-
-            ComponentName component = new ComponentName(si.packageName, si.name);
-            try {
-                TvIAppInfo info = new TvIAppInfo.Builder(mContext, component).build();
-                iAppList.add(info);
-            } catch (Exception e) {
-                Slogf.e(TAG, "failed to load TV IApp service " + si.name, e);
-                continue;
-            }
-            userState.mPackageSet.add(si.packageName);
-        }
-
-        // sort the iApp list by iApp service id
-        Collections.sort(iAppList, Comparator.comparing(TvIAppInfo::getId));
-        Map<String, TvIAppState> iAppMap = new HashMap<>();
-        ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size());
-        for (TvIAppInfo info : iAppList) {
-            String iAppServiceId = info.getId();
-            if (DEBUG) {
-                Slogf.d(TAG, "add " + iAppServiceId);
-            }
-            // Running count of IApp for each IApp service
-            Integer count = tiasAppCount.get(iAppServiceId);
-            count = count == null ? 1 : count + 1;
-            tiasAppCount.put(iAppServiceId, count);
-            TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
-            if (iAppState == null) {
-                iAppState = new TvIAppState();
-            }
-            iAppState.mInfo = info;
-            iAppState.mUid = getIAppUid(info);
-            iAppState.mComponentName = info.getComponent();
-            iAppMap.put(iAppServiceId, iAppState);
-            iAppState.mIAppNumber = count;
-        }
-
-        for (String iAppServiceId : iAppMap.keySet()) {
-            if (!userState.mIAppMap.containsKey(iAppServiceId)) {
-                notifyIAppServiceAddedLocked(userState, iAppServiceId);
-            } else if (updatedPackages != null) {
-                // Notify the package updates
-                ComponentName component = iAppMap.get(iAppServiceId).mInfo.getComponent();
-                for (String updatedPackage : updatedPackages) {
-                    if (component.getPackageName().equals(updatedPackage)) {
-                        updateServiceConnectionLocked(component, userId);
-                        notifyIAppServiceUpdatedLocked(userState, iAppServiceId);
-                        break;
-                    }
-                }
-            }
-        }
-
-        for (String iAppServiceId : userState.mIAppMap.keySet()) {
-            if (!iAppMap.containsKey(iAppServiceId)) {
-                TvIAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
-                ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent());
-                if (serviceState != null) {
-                    abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId);
-                }
-                notifyIAppServiceRemovedLocked(userState, iAppServiceId);
-            }
-        }
-
-        userState.mIAppMap.clear();
-        userState.mIAppMap = iAppMap;
-    }
-
-    @GuardedBy("mLock")
-    private void notifyIAppServiceAddedLocked(UserState userState, String iAppServiceId) {
-        if (DEBUG) {
-            Slog.d(TAG, "notifyIAppServiceAddedLocked(iAppServiceId=" + iAppServiceId + ")");
-        }
-        int n = userState.mCallbacks.beginBroadcast();
-        for (int i = 0; i < n; ++i) {
-            try {
-                userState.mCallbacks.getBroadcastItem(i).onIAppServiceAdded(iAppServiceId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report added IApp service to callback", e);
-            }
-        }
-        userState.mCallbacks.finishBroadcast();
-    }
-
-    @GuardedBy("mLock")
-    private void notifyIAppServiceRemovedLocked(UserState userState, String iAppServiceId) {
-        if (DEBUG) {
-            Slog.d(TAG, "notifyIAppServiceRemovedLocked(iAppServiceId=" + iAppServiceId + ")");
-        }
-        int n = userState.mCallbacks.beginBroadcast();
-        for (int i = 0; i < n; ++i) {
-            try {
-                userState.mCallbacks.getBroadcastItem(i).onIAppServiceRemoved(iAppServiceId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report removed IApp service to callback", e);
-            }
-        }
-        userState.mCallbacks.finishBroadcast();
-    }
-
-    @GuardedBy("mLock")
-    private void notifyIAppServiceUpdatedLocked(UserState userState, String iAppServiceId) {
-        if (DEBUG) {
-            Slog.d(TAG, "notifyIAppServiceUpdatedLocked(iAppServiceId=" + iAppServiceId + ")");
-        }
-        int n = userState.mCallbacks.beginBroadcast();
-        for (int i = 0; i < n; ++i) {
-            try {
-                userState.mCallbacks.getBroadcastItem(i).onIAppServiceUpdated(iAppServiceId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report updated IApp service to callback", e);
-            }
-        }
-        userState.mCallbacks.finishBroadcast();
-    }
-
-    @GuardedBy("mLock")
-    private void notifyStateChangedLocked(
-            UserState userState, String iAppServiceId, int type, int state) {
-        if (DEBUG) {
-            Slog.d(TAG, "notifyRteStateChanged(iAppServiceId="
-                    + iAppServiceId + ", type=" + type + ", state=" + state + ")");
-        }
-        int n = userState.mCallbacks.beginBroadcast();
-        for (int i = 0; i < n; ++i) {
-            try {
-                userState.mCallbacks.getBroadcastItem(i).onStateChanged(iAppServiceId, type, state);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report RTE state changed", e);
-            }
-        }
-        userState.mCallbacks.finishBroadcast();
-    }
-
-    private int getIAppUid(TvIAppInfo info) {
-        try {
-            return getContext().getPackageManager().getApplicationInfo(
-                    info.getServiceInfo().packageName, 0).uid;
-        } catch (PackageManager.NameNotFoundException e) {
-            Slogf.w(TAG, "Unable to get UID for  " + info, e);
-            return Process.INVALID_UID;
-        }
-    }
-
-    @Override
-    public void onStart() {
-        if (DEBUG) {
-            Slogf.d(TAG, "onStart");
-        }
-        publishBinderService(Context.TV_IAPP_SERVICE, new BinderService());
-    }
-
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            registerBroadcastReceivers();
-        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
-            synchronized (mLock) {
-                buildTvIAppServiceListLocked(mCurrentUserId, null);
-            }
-        }
-    }
-
-    private void registerBroadcastReceivers() {
-        PackageMonitor monitor = new PackageMonitor() {
-            private void buildTvIAppServiceList(String[] packages) {
-                int userId = getChangingUserId();
-                synchronized (mLock) {
-                    if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
-                        buildTvIAppServiceListLocked(userId, packages);
-                    }
-                }
-            }
-
-            @Override
-            public void onPackageUpdateFinished(String packageName, int uid) {
-                if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
-                // This callback is invoked when the TV iApp service is reinstalled.
-                // In this case, isReplacing() always returns true.
-                buildTvIAppServiceList(new String[] { packageName });
-            }
-
-            @Override
-            public void onPackagesAvailable(String[] packages) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
-                }
-                // This callback is invoked when the media on which some packages exist become
-                // available.
-                if (isReplacing()) {
-                    buildTvIAppServiceList(packages);
-                }
-            }
-
-            @Override
-            public void onPackagesUnavailable(String[] packages) {
-                // This callback is invoked when the media on which some packages exist become
-                // unavailable.
-                if (DEBUG)  {
-                    Slogf.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
-                            + ")");
-                }
-                if (isReplacing()) {
-                    buildTvIAppServiceList(packages);
-                }
-            }
-
-            @Override
-            public void onSomePackagesChanged() {
-                if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
-                if (isReplacing()) {
-                    if (DEBUG) Slogf.d(TAG, "Skipped building TV iApp list due to replacing");
-                    // When the package is updated, buildTvIAppServiceListLocked is called in other
-                    // methods instead.
-                    return;
-                }
-                buildTvIAppServiceList(null);
-            }
-
-            @Override
-            public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                // The iApp list needs to be updated in any cases, regardless of whether
-                // it happened to the whole package or a specific component. Returning true so that
-                // the update can be handled in {@link #onSomePackagesChanged}.
-                return true;
-            }
-        };
-        monitor.register(mContext, null, UserHandle.ALL, true);
-
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-        intentFilter.addAction(Intent.ACTION_USER_STARTED);
-        intentFilter.addAction(Intent.ACTION_USER_STOPPED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                } else if (Intent.ACTION_USER_STARTED.equals(action)) {
-                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                    startUser(userId);
-                } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                    stopUser(userId);
-                }
-            }
-        }, UserHandle.ALL, intentFilter, null, null);
-    }
-
-    private void switchUser(int userId) {
-        synchronized (mLock) {
-            if (mCurrentUserId == userId) {
-                return;
-            }
-            UserInfo userInfo = mUserManager.getUserInfo(userId);
-            if (userInfo.isProfile()) {
-                Slog.w(TAG, "cannot switch to a profile!");
-                return;
-            }
-
-            for (int runningId : mRunningProfiles) {
-                releaseSessionOfUserLocked(runningId);
-                unbindServiceOfUserLocked(runningId);
-            }
-            mRunningProfiles.clear();
-            releaseSessionOfUserLocked(mCurrentUserId);
-            unbindServiceOfUserLocked(mCurrentUserId);
-
-            mCurrentUserId = userId;
-            buildTvIAppServiceListLocked(userId, null);
-        }
-    }
-
-    private void removeUser(int userId) {
-        synchronized (mLock) {
-            UserState userState = getUserStateLocked(userId);
-            if (userState == null) {
-                return;
-            }
-            // Release all created sessions.
-            for (SessionState state : userState.mSessionStateMap.values()) {
-                if (state.mSession != null) {
-                    try {
-                        state.mSession.release();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in release", e);
-                    }
-                }
-            }
-            userState.mSessionStateMap.clear();
-
-            // Unregister all callbacks and unbind all services.
-            for (ServiceState serviceState : userState.mServiceStateMap.values()) {
-                if (serviceState.mService != null) {
-                    if (serviceState.mCallback != null) {
-                        try {
-                            serviceState.mService.unregisterCallback(serviceState.mCallback);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "error in unregisterCallback", e);
-                        }
-                    }
-                    mContext.unbindService(serviceState.mConnection);
-                }
-            }
-            userState.mServiceStateMap.clear();
-
-            // Clear everything else.
-            userState.mIAppMap.clear();
-            userState.mPackageSet.clear();
-            userState.mClientStateMap.clear();
-            userState.mCallbacks.kill();
-
-            mRunningProfiles.remove(userId);
-            mUserStates.remove(userId);
-
-            if (userId == mCurrentUserId) {
-                switchUser(UserHandle.USER_SYSTEM);
-            }
-        }
-    }
-
-    private void startUser(int userId) {
-        synchronized (mLock) {
-            if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
-                // user already started
-                return;
-            }
-            UserInfo userInfo = mUserManager.getUserInfo(userId);
-            UserInfo parentInfo = mUserManager.getProfileParent(userId);
-            if (userInfo.isProfile()
-                    && parentInfo != null
-                    && parentInfo.id == mCurrentUserId) {
-                // only the children of the current user can be started in background
-                startProfileLocked(userId);
-            }
-        }
-    }
-
-    private void stopUser(int userId) {
-        synchronized (mLock) {
-            if (userId == mCurrentUserId) {
-                switchUser(ActivityManager.getCurrentUser());
-                return;
-            }
-
-            releaseSessionOfUserLocked(userId);
-            unbindServiceOfUserLocked(userId);
-            mRunningProfiles.remove(userId);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void startProfileLocked(int userId) {
-        mRunningProfiles.add(userId);
-        buildTvIAppServiceListLocked(userId, null);
-    }
-
-    @GuardedBy("mLock")
-    private void releaseSessionOfUserLocked(int userId) {
-        UserState userState = getUserStateLocked(userId);
-        if (userState == null) {
-            return;
-        }
-        List<SessionState> sessionStatesToRelease = new ArrayList<>();
-        for (SessionState sessionState : userState.mSessionStateMap.values()) {
-            if (sessionState.mSession != null) {
-                sessionStatesToRelease.add(sessionState);
-            }
-        }
-        for (SessionState sessionState : sessionStatesToRelease) {
-            try {
-                sessionState.mSession.release();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "error in release", e);
-            }
-            clearSessionAndNotifyClientLocked(sessionState);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void unbindServiceOfUserLocked(int userId) {
-        UserState userState = getUserStateLocked(userId);
-        if (userState == null) {
-            return;
-        }
-        for (Iterator<ComponentName> it = userState.mServiceStateMap.keySet().iterator();
-                it.hasNext(); ) {
-            ComponentName component = it.next();
-            ServiceState serviceState = userState.mServiceStateMap.get(component);
-            if (serviceState != null && serviceState.mSessionTokens.isEmpty()) {
-                if (serviceState.mCallback != null) {
-                    try {
-                        serviceState.mService.unregisterCallback(serviceState.mCallback);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in unregisterCallback", e);
-                    }
-                }
-                mContext.unbindService(serviceState.mConnection);
-                it.remove();
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void clearSessionAndNotifyClientLocked(SessionState state) {
-        if (state.mClient != null) {
-            try {
-                state.mClient.onSessionReleased(state.mSeq);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "error in onSessionReleased", e);
-            }
-        }
-        removeSessionStateLocked(state.mSessionToken, state.mUserId);
-    }
-
-    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
-            String methodName) {
-        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
-                false, methodName, null);
-    }
-
-    @GuardedBy("mLock")
-    private UserState getOrCreateUserStateLocked(int userId) {
-        UserState userState = getUserStateLocked(userId);
-        if (userState == null) {
-            userState = new UserState(userId);
-            mUserStates.put(userId, userState);
-        }
-        return userState;
-    }
-
-    @GuardedBy("mLock")
-    private UserState getUserStateLocked(int userId) {
-        return mUserStates.get(userId);
-    }
-
-    @GuardedBy("mLock")
-    private ServiceState getServiceStateLocked(ComponentName component, int userId) {
-        UserState userState = getOrCreateUserStateLocked(userId);
-        ServiceState serviceState = userState.mServiceStateMap.get(component);
-        if (serviceState == null) {
-            throw new IllegalStateException("Service state not found for " + component + " (userId="
-                    + userId + ")");
-        }
-        return serviceState;
-    }
-
-    @GuardedBy("mLock")
-    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
-        UserState userState = getOrCreateUserStateLocked(userId);
-        return getSessionStateLocked(sessionToken, callingUid, userState);
-    }
-
-    @GuardedBy("mLock")
-    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid,
-            UserState userState) {
-        SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
-        if (sessionState == null) {
-            throw new SessionNotFoundException("Session state not found for token " + sessionToken);
-        }
-        // Only the application that requested this session or the system can access it.
-        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
-            throw new SecurityException("Illegal access to the session with token " + sessionToken
-                    + " from uid " + callingUid);
-        }
-        return sessionState;
-    }
-
-    @GuardedBy("mLock")
-    private ITvIAppSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
-        return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
-    }
-
-    @GuardedBy("mLock")
-    private ITvIAppSession getSessionLocked(SessionState sessionState) {
-        ITvIAppSession session = sessionState.mSession;
-        if (session == null) {
-            throw new IllegalStateException("Session not yet created for token "
-                    + sessionState.mSessionToken);
-        }
-        return session;
-    }
-
-    private final class BinderService extends ITvIAppManager.Stub {
-
-        @Override
-        public List<TvIAppInfo> getTvIAppServiceList(int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "getTvIAppServiceList");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    List<TvIAppInfo> iAppList = new ArrayList<>();
-                    for (TvIAppState state : userState.mIAppMap.values()) {
-                        iAppList.add(state.mInfo);
-                    }
-                    return iAppList;
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void prepare(String tiasId, int type, int userId) {
-            // TODO: bind service
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "prepare");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(tiasId);
-                    if (iAppState == null) {
-                        Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId);
-                        return;
-                    }
-                    ComponentName componentName = iAppState.mInfo.getComponent();
-                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
-                    if (serviceState == null) {
-                        serviceState = new ServiceState(
-                                componentName, tiasId, resolvedUserId, true, type);
-                        userState.mServiceStateMap.put(componentName, serviceState);
-                        updateServiceConnectionLocked(componentName, resolvedUserId);
-                    } else if (serviceState.mService != null) {
-                        serviceState.mService.prepare(type);
-                    } else {
-                        serviceState.mPendingPrepare = true;
-                        serviceState.mPendingPrepareType = type;
-                        updateServiceConnectionLocked(componentName, resolvedUserId);
-                    }
-                }
-            } catch (RemoteException e) {
-                Slogf.e(TAG, "error in prepare", e);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void notifyAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "notifyAppLinkInfo");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(tiasId);
-                    if (iAppState == null) {
-                        Slogf.e(TAG, "failed to notifyAppLinkInfo - unknown TIAS id "
-                                + tiasId);
-                        return;
-                    }
-                    ComponentName componentName = iAppState.mInfo.getComponent();
-                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
-                    if (serviceState == null) {
-                        serviceState = new ServiceState(
-                                componentName, tiasId, resolvedUserId);
-                        serviceState.addPendingAppLink(appLinkInfo);
-                        userState.mServiceStateMap.put(componentName, serviceState);
-                        updateServiceConnectionLocked(componentName, resolvedUserId);
-                    } else if (serviceState.mService != null) {
-                        serviceState.mService.notifyAppLinkInfo(appLinkInfo);
-                    } else {
-                        serviceState.addPendingAppLink(appLinkInfo);
-                        updateServiceConnectionLocked(componentName, resolvedUserId);
-                    }
-                }
-            } catch (RemoteException e) {
-                Slogf.e(TAG, "error in notifyAppLinkInfo", e);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void sendAppLinkCommand(String tiasId, Bundle command, int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "sendAppLinkCommand");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(tiasId);
-                    if (iAppState == null) {
-                        Slogf.e(TAG, "failed to sendAppLinkCommand - unknown TIAS id "
-                                + tiasId);
-                        return;
-                    }
-                    ComponentName componentName = iAppState.mInfo.getComponent();
-                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
-                    if (serviceState == null) {
-                        serviceState = new ServiceState(
-                                componentName, tiasId, resolvedUserId);
-                        serviceState.addPendingAppLinkCommand(command);
-                        userState.mServiceStateMap.put(componentName, serviceState);
-                    } else if (serviceState.mService != null) {
-                        serviceState.mService.sendAppLinkCommand(command);
-                    } else {
-                        serviceState.addPendingAppLinkCommand(command);
-                    }
-                }
-            } catch (RemoteException e) {
-                Slogf.e(TAG, "error in sendAppLinkCommand", e);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
-                int seq, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
-                    userId, "createSession");
-            final long identity = Binder.clearCallingIdentity();
-
-            try {
-                synchronized (mLock) {
-                    if (userId != mCurrentUserId && !mRunningProfiles.contains(userId)) {
-                        // Only current user and its running profiles can create sessions.
-                        // Let the client get onConnectionFailed callback for this case.
-                        sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
-                        return;
-                    }
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
-                    if (iAppState == null) {
-                        Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
-                        sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
-                        return;
-                    }
-                    ServiceState serviceState =
-                            userState.mServiceStateMap.get(iAppState.mComponentName);
-                    if (serviceState == null) {
-                        int tiasUid = PackageManager.getApplicationInfoAsUserCached(
-                                iAppState.mComponentName.getPackageName(), 0, resolvedUserId).uid;
-                        serviceState = new ServiceState(
-                                iAppState.mComponentName, iAppServiceId, resolvedUserId);
-                        userState.mServiceStateMap.put(iAppState.mComponentName, serviceState);
-                    }
-                    // Send a null token immediately while reconnecting.
-                    if (serviceState.mReconnecting) {
-                        sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
-                        return;
-                    }
-
-                    // Create a new session token and a session state.
-                    IBinder sessionToken = new Binder();
-                    SessionState sessionState = new SessionState(sessionToken, iAppServiceId, type,
-                            iAppState.mComponentName, client, seq, callingUid,
-                            callingPid, resolvedUserId);
-
-                    // Add them to the global session state map of the current user.
-                    userState.mSessionStateMap.put(sessionToken, sessionState);
-
-                    // Also, add them to the session state map of the current service.
-                    serviceState.mSessionTokens.add(sessionToken);
-
-                    if (serviceState.mService != null) {
-                        if (!createSessionInternalLocked(serviceState.mService, sessionToken,
-                                resolvedUserId)) {
-                            removeSessionStateLocked(sessionToken, resolvedUserId);
-                        }
-                    } else {
-                        updateServiceConnectionLocked(iAppState.mComponentName, resolvedUserId);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void releaseSession(IBinder sessionToken, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "releaseSession");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void notifyTuned(IBinder sessionToken, Uri channelUri, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "notifyTuned(sessionToken=" + sessionToken
-                        + ", Uri=" + channelUri + ")");
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "notifyTuned");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).notifyTuned(channelUri);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in notifyTuned", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void notifyTrackSelected(IBinder sessionToken, int type, String trackId,
-                int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "notifyTrackSelected(sessionToken=" + sessionToken
-                        + ", type=" + type + ", trackId=" + trackId + ")");
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "notifyTrackSelected");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).notifyTrackSelected(type, trackId);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in notifyTrackSelected", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void notifyTracksChanged(IBinder sessionToken, List<TvTrackInfo> tracks,
-                int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "notifyTracksChanged(sessionToken=" + sessionToken
-                        + ", tracks=" + tracks + ")");
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "notifyTracksChanged");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).notifyTracksChanged(tracks);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in notifyTracksChanged", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void startIApp(IBinder sessionToken, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "startIApp");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).startIApp();
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in start", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void stopIApp(IBinder sessionToken, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "BinderService#stop(userId=%d)", userId);
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "stopIApp");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).stopIApp();
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in stop", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void createBiInteractiveApp(
-                IBinder sessionToken, Uri biIAppUri, Bundle params, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "createBiInteractiveApp(biIAppUri=%s,params=%s)", biIAppUri, params);
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "createBiInteractiveApp");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).createBiInteractiveApp(
-                                biIAppUri, params);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in createBiInteractiveApp", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void destroyBiInteractiveApp(IBinder sessionToken, String biIAppId, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "destroyBiInteractiveApp(biIAppId=%s)", biIAppId);
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "destroyBiInteractiveApp");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).destroyBiInteractiveApp(biIAppId);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in destroyBiInteractiveApp", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void sendCurrentChannelUri(IBinder sessionToken, Uri channelUri, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "sendCurrentChannelUri(channelUri=%s)", channelUri.toString());
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "sendCurrentChannelUri");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).sendCurrentChannelUri(channelUri);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in sendCurrentChannelUri", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void sendCurrentChannelLcn(IBinder sessionToken, int lcn, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "sendCurrentChannelLcn(lcn=%d)", lcn);
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "sendCurrentChannelLcn");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).sendCurrentChannelLcn(lcn);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in sendCurrentChannelLcn", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void sendStreamVolume(IBinder sessionToken, float volume, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "sendStreamVolume(volume=%f)", volume);
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "sendStreamVolume");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).sendStreamVolume(volume);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in sendStreamVolume", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void sendTrackInfoList(IBinder sessionToken, List<TvTrackInfo> tracks, int userId) {
-            if (DEBUG) {
-                Slogf.d(TAG, "sendTrackInfoList(tracks=%s)", tracks.toString());
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "sendTrackInfoList");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).sendTrackInfoList(tracks);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in sendTrackInfoList", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "setSurface");
-            SessionState sessionState = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).setSurface(surface);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in setSurface", e);
-                    }
-                }
-            } finally {
-                if (surface != null) {
-                    // surface is not used in TvIAppManagerService.
-                    surface.release();
-                }
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
-                int height, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "dispatchSurfaceChanged");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
-                                height);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in dispatchSurfaceChanged", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void notifyBroadcastInfoResponse(IBinder sessionToken,
-                BroadcastInfoResponse response, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
-                    "notifyBroadcastInfoResponse");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).notifyBroadcastInfoResponse(response);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in notifyBroadcastInfoResponse", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void notifyAdResponse(IBinder sessionToken, AdResponse response, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
-                    "notifyAdResponse");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                resolvedUserId);
-                        getSessionLocked(sessionState).notifyAdResponse(response);
-                    } catch (RemoteException | SessionNotFoundException e) {
-                        Slogf.e(TAG, "error in notifyAdResponse", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
-            int callingPid = Binder.getCallingPid();
-            int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
-                    "registerCallback");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    if (!userState.mCallbacks.register(callback)) {
-                        Slog.e(TAG, "client process has already died");
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void unregisterCallback(ITvIAppManagerCallback callback, int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "unregisterCallback");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    userState.mCallbacks.unregister(callback);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void createMediaView(IBinder sessionToken, IBinder windowToken, Rect frame,
-                int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "createMediaView");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .createMediaView(windowToken, frame);
-                    } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
-                        Slog.e(TAG, "error in createMediaView", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void relayoutMediaView(IBinder sessionToken, Rect frame, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "relayoutMediaView");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .relayoutMediaView(frame);
-                    } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
-                        Slog.e(TAG, "error in relayoutMediaView", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void removeMediaView(IBinder sessionToken, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "removeMediaView");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .removeMediaView();
-                    } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
-                        Slog.e(TAG, "error in removeMediaView", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void sendSessionTokenToClientLocked(ITvIAppClient client, String iAppServiceId,
-            IBinder sessionToken, InputChannel channel, int seq) {
-        try {
-            client.onSessionCreated(iAppServiceId, sessionToken, channel, seq);
-        } catch (RemoteException e) {
-            Slogf.e(TAG, "error in onSessionCreated", e);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private boolean createSessionInternalLocked(ITvIAppService service, IBinder sessionToken,
-            int userId) {
-        UserState userState = getOrCreateUserStateLocked(userId);
-        SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
-        if (DEBUG) {
-            Slogf.d(TAG, "createSessionInternalLocked(iAppServiceId="
-                    + sessionState.mIAppServiceId + ")");
-        }
-        InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
-
-        // Set up a callback to send the session token.
-        ITvIAppSessionCallback callback = new SessionCallback(sessionState, channels);
-
-        boolean created = true;
-        // Create a session. When failed, send a null token immediately.
-        try {
-            service.createSession(
-                    channels[1], callback, sessionState.mIAppServiceId, sessionState.mType);
-        } catch (RemoteException e) {
-            Slogf.e(TAG, "error in createSession", e);
-            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mIAppServiceId, null,
-                    null, sessionState.mSeq);
-            created = false;
-        }
-        channels[1].dispose();
-        return created;
-    }
-
-    @GuardedBy("mLock")
-    @Nullable
-    private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
-        SessionState sessionState = null;
-        try {
-            sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-            UserState userState = getOrCreateUserStateLocked(userId);
-            if (sessionState.mSession != null) {
-                sessionState.mSession.asBinder().unlinkToDeath(sessionState, 0);
-                sessionState.mSession.release();
-            }
-        } catch (RemoteException | SessionNotFoundException e) {
-            Slogf.e(TAG, "error in releaseSession", e);
-        } finally {
-            if (sessionState != null) {
-                sessionState.mSession = null;
-            }
-        }
-        removeSessionStateLocked(sessionToken, userId);
-        return sessionState;
-    }
-
-    @GuardedBy("mLock")
-    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
-        UserState userState = getOrCreateUserStateLocked(userId);
-
-        // Remove the session state from the global session state map of the current user.
-        SessionState sessionState = userState.mSessionStateMap.remove(sessionToken);
-
-        if (sessionState == null) {
-            Slogf.e(TAG, "sessionState null, no more remove session action!");
-            return;
-        }
-
-        // Also remove the session token from the session token list of the current client and
-        // service.
-        ClientState clientState = userState.mClientStateMap.get(sessionState.mClient.asBinder());
-        if (clientState != null) {
-            clientState.mSessionTokens.remove(sessionToken);
-            if (clientState.isEmpty()) {
-                userState.mClientStateMap.remove(sessionState.mClient.asBinder());
-                sessionState.mClient.asBinder().unlinkToDeath(clientState, 0);
-            }
-        }
-
-        ServiceState serviceState = userState.mServiceStateMap.get(sessionState.mComponent);
-        if (serviceState != null) {
-            serviceState.mSessionTokens.remove(sessionToken);
-        }
-        updateServiceConnectionLocked(sessionState.mComponent, userId);
-    }
-
-    @GuardedBy("mLock")
-    private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
-            String iAppServiceId, int userId) {
-        // Let clients know the create session requests are failed.
-        UserState userState = getOrCreateUserStateLocked(userId);
-        List<SessionState> sessionsToAbort = new ArrayList<>();
-        for (IBinder sessionToken : serviceState.mSessionTokens) {
-            SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
-            if (sessionState.mSession == null
-                    && (iAppServiceId == null
-                    || sessionState.mIAppServiceId.equals(iAppServiceId))) {
-                sessionsToAbort.add(sessionState);
-            }
-        }
-        for (SessionState sessionState : sessionsToAbort) {
-            removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
-            sendSessionTokenToClientLocked(sessionState.mClient,
-                    sessionState.mIAppServiceId, null, null, sessionState.mSeq);
-        }
-        updateServiceConnectionLocked(serviceState.mComponent, userId);
-    }
-
-    @GuardedBy("mLock")
-    private void updateServiceConnectionLocked(ComponentName component, int userId) {
-        UserState userState = getOrCreateUserStateLocked(userId);
-        ServiceState serviceState = userState.mServiceStateMap.get(component);
-        if (serviceState == null) {
-            return;
-        }
-        if (serviceState.mReconnecting) {
-            if (!serviceState.mSessionTokens.isEmpty()) {
-                // wait until all the sessions are removed.
-                return;
-            }
-            serviceState.mReconnecting = false;
-        }
-
-        boolean shouldBind = (!serviceState.mSessionTokens.isEmpty())
-                || (serviceState.mPendingPrepare) || (!serviceState.mPendingAppLinkInfo.isEmpty());
-
-        if (serviceState.mService == null && shouldBind) {
-            // This means that the service is not yet connected but its state indicates that we
-            // have pending requests. Then, connect the service.
-            if (serviceState.mBound) {
-                // We have already bound to the service so we don't try to bind again until after we
-                // unbind later on.
-                return;
-            }
-            if (DEBUG) {
-                Slogf.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
-            }
-
-            Intent i = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
-            serviceState.mBound = mContext.bindServiceAsUser(
-                    i, serviceState.mConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
-                    new UserHandle(userId));
-        } else if (serviceState.mService != null && !shouldBind) {
-            // This means that the service is already connected but its state indicates that we have
-            // nothing to do with it. Then, disconnect the service.
-            if (DEBUG) {
-                Slogf.d(TAG, "unbindService(service=" + component + ")");
-            }
-            mContext.unbindService(serviceState.mConnection);
-            userState.mServiceStateMap.remove(component);
-        }
-    }
-
-    private static final class UserState {
-        private final int mUserId;
-        // A mapping from the TV IApp ID to its TvIAppState.
-        private Map<String, TvIAppState> mIAppMap = new HashMap<>();
-        // A mapping from the token of a client to its state.
-        private final Map<IBinder, ClientState> mClientStateMap = new HashMap<>();
-        // A mapping from the name of a TV IApp service to its state.
-        private final Map<ComponentName, ServiceState> mServiceStateMap = new HashMap<>();
-        // A mapping from the token of a TV IApp session to its state.
-        private final Map<IBinder, SessionState> mSessionStateMap = new HashMap<>();
-
-        // A set of all TV IApp service packages.
-        private final Set<String> mPackageSet = new HashSet<>();
-
-        // A list of callbacks.
-        private final RemoteCallbackList<ITvIAppManagerCallback> mCallbacks =
-                new RemoteCallbackList<>();
-
-        private UserState(int userId) {
-            mUserId = userId;
-        }
-    }
-
-    private static final class TvIAppState {
-        private String mIAppServiceId;
-        private ComponentName mComponentName;
-        private TvIAppInfo mInfo;
-        private int mUid;
-        private int mIAppNumber;
-    }
-
-    private final class SessionState implements IBinder.DeathRecipient {
-        private final IBinder mSessionToken;
-        private ITvIAppSession mSession;
-        private final String mIAppServiceId;
-        private final int mType;
-        private final ITvIAppClient mClient;
-        private final int mSeq;
-        private final ComponentName mComponent;
-
-        // The UID of the application that created the session.
-        // The application is usually the TV app.
-        private final int mCallingUid;
-
-        // The PID of the application that created the session.
-        // The application is usually the TV app.
-        private final int mCallingPid;
-
-        private final int mUserId;
-
-        private SessionState(IBinder sessionToken, String iAppServiceId, int type,
-                ComponentName componentName, ITvIAppClient client, int seq, int callingUid,
-                int callingPid, int userId) {
-            mSessionToken = sessionToken;
-            mIAppServiceId = iAppServiceId;
-            mComponent = componentName;
-            mType = type;
-            mClient = client;
-            mSeq = seq;
-            mCallingUid = callingUid;
-            mCallingPid = callingPid;
-            mUserId = userId;
-        }
-
-        @Override
-        public void binderDied() {
-        }
-    }
-
-    private final class ClientState implements IBinder.DeathRecipient {
-        private final List<IBinder> mSessionTokens = new ArrayList<>();
-
-        private IBinder mClientToken;
-        private final int mUserId;
-
-        ClientState(IBinder clientToken, int userId) {
-            mClientToken = clientToken;
-            mUserId = userId;
-        }
-
-        public boolean isEmpty() {
-            return mSessionTokens.isEmpty();
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                UserState userState = getOrCreateUserStateLocked(mUserId);
-                // DO NOT remove the client state of clientStateMap in this method. It will be
-                // removed in releaseSessionLocked().
-                ClientState clientState = userState.mClientStateMap.get(mClientToken);
-                if (clientState != null) {
-                    while (clientState.mSessionTokens.size() > 0) {
-                        IBinder sessionToken = clientState.mSessionTokens.get(0);
-                        releaseSessionLocked(
-                                sessionToken, Process.SYSTEM_UID, mUserId);
-                        // the releaseSessionLocked function may return before the sessionToken
-                        // is removed if the related sessionState is null. So need to check again
-                        // to avoid death circulation.
-                        if (clientState.mSessionTokens.contains(sessionToken)) {
-                            Slogf.d(TAG, "remove sessionToken " + sessionToken + " for "
-                                    + mClientToken);
-                            clientState.mSessionTokens.remove(sessionToken);
-                        }
-                    }
-                }
-                mClientToken = null;
-            }
-        }
-    }
-
-    private final class ServiceState {
-        private final List<IBinder> mSessionTokens = new ArrayList<>();
-        private final ServiceConnection mConnection;
-        private final ComponentName mComponent;
-        private final String mIAppServiceId;
-        private final List<Bundle> mPendingAppLinkInfo = new ArrayList<>();
-        private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
-
-        private boolean mPendingPrepare = false;
-        private Integer mPendingPrepareType = null;
-        private ITvIAppService mService;
-        private ServiceCallback mCallback;
-        private boolean mBound;
-        private boolean mReconnecting;
-
-        private ServiceState(ComponentName component, String tias, int userId) {
-            this(component, tias, userId, false, null);
-        }
-
-        private ServiceState(ComponentName component, String tias, int userId,
-                boolean pendingPrepare, Integer prepareType) {
-            mComponent = component;
-            mPendingPrepare = pendingPrepare;
-            mPendingPrepareType = prepareType;
-            mConnection = new IAppServiceConnection(component, userId);
-            mIAppServiceId = tias;
-        }
-
-        private void addPendingAppLink(Bundle info) {
-            mPendingAppLinkInfo.add(info);
-        }
-
-        private void addPendingAppLinkCommand(Bundle command) {
-            mPendingAppLinkCommand.add(command);
-        }
-    }
-
-    private final class IAppServiceConnection implements ServiceConnection {
-        private final ComponentName mComponent;
-        private final int mUserId;
-
-        private IAppServiceConnection(ComponentName component, int userId) {
-            mComponent = component;
-            mUserId = userId;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName component, IBinder service) {
-            if (DEBUG) {
-                Slogf.d(TAG, "onServiceConnected(component=" + component + ")");
-            }
-            synchronized (mLock) {
-                UserState userState = getUserStateLocked(mUserId);
-                if (userState == null) {
-                    // The user was removed while connecting.
-                    mContext.unbindService(this);
-                    return;
-                }
-                ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
-                serviceState.mService = ITvIAppService.Stub.asInterface(service);
-
-                if (serviceState.mPendingPrepare) {
-                    final long identity = Binder.clearCallingIdentity();
-                    try {
-                        serviceState.mService.prepare(serviceState.mPendingPrepareType);
-                        serviceState.mPendingPrepare = false;
-                        serviceState.mPendingPrepareType = null;
-                    } catch (RemoteException e) {
-                        Slogf.e(TAG, "error in prepare when onServiceConnected", e);
-                    } finally {
-                        Binder.restoreCallingIdentity(identity);
-                    }
-                }
-
-                if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
-                    for (Iterator<Bundle> it = serviceState.mPendingAppLinkInfo.iterator();
-                            it.hasNext(); ) {
-                        Bundle appLinkInfo = it.next();
-                        final long identity = Binder.clearCallingIdentity();
-                        try {
-                            serviceState.mService.notifyAppLinkInfo(appLinkInfo);
-                            it.remove();
-                        } catch (RemoteException e) {
-                            Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfo
-                                    + ") when onServiceConnected", e);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    }
-                }
-
-                if (!serviceState.mPendingAppLinkCommand.isEmpty()) {
-                    for (Iterator<Bundle> it = serviceState.mPendingAppLinkCommand.iterator();
-                            it.hasNext(); ) {
-                        Bundle command = it.next();
-                        final long identity = Binder.clearCallingIdentity();
-                        try {
-                            serviceState.mService.sendAppLinkCommand(command);
-                            it.remove();
-                        } catch (RemoteException e) {
-                            Slogf.e(TAG, "error in sendAppLinkCommand(" + command
-                                    + ") when onServiceConnected", e);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    }
-                }
-
-                List<IBinder> tokensToBeRemoved = new ArrayList<>();
-
-                // And create sessions, if any.
-                for (IBinder sessionToken : serviceState.mSessionTokens) {
-                    if (!createSessionInternalLocked(
-                            serviceState.mService, sessionToken, mUserId)) {
-                        tokensToBeRemoved.add(sessionToken);
-                    }
-                }
-
-                for (IBinder sessionToken : tokensToBeRemoved) {
-                    removeSessionStateLocked(sessionToken, mUserId);
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName component) {
-            if (DEBUG) {
-                Slogf.d(TAG, "onServiceDisconnected(component=" + component + ")");
-            }
-            if (!mComponent.equals(component)) {
-                throw new IllegalArgumentException("Mismatched ComponentName: "
-                        + mComponent + " (expected), " + component + " (actual).");
-            }
-            synchronized (mLock) {
-                UserState userState = getOrCreateUserStateLocked(mUserId);
-                ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
-                if (serviceState != null) {
-                    serviceState.mReconnecting = true;
-                    serviceState.mBound = false;
-                    serviceState.mService = null;
-                    serviceState.mCallback = null;
-
-                    abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
-                }
-            }
-        }
-    }
-
-    private final class ServiceCallback extends ITvIAppServiceCallback.Stub {
-        private final ComponentName mComponent;
-        private final int mUserId;
-
-        ServiceCallback(ComponentName component, int userId) {
-            mComponent = component;
-            mUserId = userId;
-        }
-
-        @Override
-        public void onStateChanged(int type, int state) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
-                    String iAppServiceId = serviceState.mIAppServiceId;
-                    UserState userState = getUserStateLocked(mUserId);
-                    notifyStateChangedLocked(userState, iAppServiceId, type, state);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    private final class SessionCallback extends ITvIAppSessionCallback.Stub {
-        private final SessionState mSessionState;
-        private final InputChannel[] mInputChannels;
-
-        SessionCallback(SessionState sessionState, InputChannel[] channels) {
-            mSessionState = sessionState;
-            mInputChannels = channels;
-        }
-
-        @Override
-        public void onSessionCreated(ITvIAppSession session) {
-            if (DEBUG) {
-                Slogf.d(TAG, "onSessionCreated(iAppServiceId="
-                        + mSessionState.mIAppServiceId + ")");
-            }
-            synchronized (mLock) {
-                mSessionState.mSession = session;
-                if (session != null && addSessionTokenToClientStateLocked(session)) {
-                    sendSessionTokenToClientLocked(
-                            mSessionState.mClient,
-                            mSessionState.mIAppServiceId,
-                            mSessionState.mSessionToken,
-                            mInputChannels[0],
-                            mSessionState.mSeq);
-                } else {
-                    removeSessionStateLocked(mSessionState.mSessionToken, mSessionState.mUserId);
-                    sendSessionTokenToClientLocked(mSessionState.mClient,
-                            mSessionState.mIAppServiceId, null, null, mSessionState.mSeq);
-                }
-                mInputChannels[0].dispose();
-            }
-        }
-
-        @Override
-        public void onLayoutSurface(int left, int top, int right, int bottom) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
-                            + ", right=" + right + ", bottom=" + bottom + ",)");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onLayoutSurface(left, top, right, bottom,
-                            mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onLayoutSurface", e);
-                }
-            }
-        }
-
-        @Override
-        public void onBroadcastInfoRequest(BroadcastInfoRequest request) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onBroadcastInfoRequest (requestId="
-                            + request.getRequestId() + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onBroadcastInfoRequest(request, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onBroadcastInfoRequest", e);
-                }
-            }
-        }
-
-        @Override
-        public void onRemoveBroadcastInfo(int requestId) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onRemoveBroadcastInfo (requestId=" + requestId + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onRemoveBroadcastInfo(requestId, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onRemoveBroadcastInfo", e);
-                }
-            }
-        }
-
-        @Override
-        public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
-                Bundle parameters) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
-                            + parameters.toString() + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onCommandRequest(cmdType, parameters, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onCommandRequest", e);
-                }
-            }
-        }
-
-        @Override
-        public void onSetVideoBounds(Rect rect) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onSetVideoBounds(rect=" + rect + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onSetVideoBounds(rect, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onSetVideoBounds", e);
-                }
-            }
-        }
-
-        @Override
-        public void onRequestCurrentChannelUri() {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onRequestCurrentChannelUri");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onRequestCurrentChannelUri(mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onRequestCurrentChannelUri", e);
-                }
-            }
-        }
-
-        @Override
-        public void onRequestCurrentChannelLcn() {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onRequestCurrentChannelLcn");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onRequestCurrentChannelLcn(mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onRequestCurrentChannelLcn", e);
-                }
-            }
-        }
-
-        @Override
-        public void onRequestStreamVolume() {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onRequestStreamVolume");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onRequestStreamVolume(mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onRequestStreamVolume", e);
-                }
-            }
-        }
-
-        @Override
-        public void onRequestTrackInfoList() {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onRequestTrackInfoList");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onRequestTrackInfoList(mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onRequestTrackInfoList", e);
-                }
-            }
-        }
-
-        @Override
-        public void onAdRequest(AdRequest request) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onAdRequest (id=" + request.getId() + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onAdRequest(request, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onAdRequest", e);
-                }
-            }
-        }
-
-        @Override
-        public void onSessionStateChanged(int state) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onSessionStateChanged (state=" + state + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onSessionStateChanged(state, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onSessionStateChanged", e);
-                }
-            }
-        }
-
-        @Override
-        public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri
-                            + ", biIAppId=" + biIAppId + ")");
-                }
-                if (mSessionState.mSession == null || mSessionState.mClient == null) {
-                    return;
-                }
-                try {
-                    mSessionState.mClient.onBiInteractiveAppCreated(
-                            biIAppUri, biIAppId, mSessionState.mSeq);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "error in onBiInteractiveAppCreated", e);
-                }
-            }
-        }
-
-        @GuardedBy("mLock")
-        private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
-            try {
-                session.asBinder().linkToDeath(mSessionState, 0);
-            } catch (RemoteException e) {
-                Slogf.e(TAG, "session process has already died", e);
-                return false;
-            }
-
-            IBinder clientToken = mSessionState.mClient.asBinder();
-            UserState userState = getOrCreateUserStateLocked(mSessionState.mUserId);
-            ClientState clientState = userState.mClientStateMap.get(clientToken);
-            if (clientState == null) {
-                clientState = new ClientState(clientToken, mSessionState.mUserId);
-                try {
-                    clientToken.linkToDeath(clientState, 0);
-                } catch (RemoteException e) {
-                    Slogf.e(TAG, "client process has already died", e);
-                    return false;
-                }
-                userState.mClientStateMap.put(clientToken, clientState);
-            }
-            clientState.mSessionTokens.add(mSessionState.mSessionToken);
-            return true;
-        }
-    }
-
-    private static class SessionNotFoundException extends IllegalArgumentException {
-        SessionNotFoundException(String name) {
-            super(name);
-        }
-    }
-
-    private static class ClientPidNotFoundException extends IllegalArgumentException {
-        ClientPidNotFoundException(String name) {
-            super(name);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
new file mode 100644
index 0000000..b3649a7
--- /dev/null
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -0,0 +1,2323 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tv.interactive;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.AppLinkInfo;
+import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManager;
+import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
+import android.media.tv.interactive.ITvInteractiveAppService;
+import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
+import android.media.tv.interactive.ITvInteractiveAppSession;
+import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
+import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.media.tv.interactive.TvInteractiveAppService;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.Surface;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.server.SystemService;
+import com.android.server.utils.Slogf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * This class provides a system service that manages interactive TV applications.
+ */
+public class TvInteractiveAppManagerService extends SystemService {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvInteractiveAppManagerService";
+    // A global lock.
+    private final Object mLock = new Object();
+    private final Context mContext;
+    // ID of the current user.
+    @GuardedBy("mLock")
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
+    // IDs of the running profiles. Their parent user ID should be mCurrentUserId.
+    @GuardedBy("mLock")
+    private final Set<Integer> mRunningProfiles = new HashSet<>();
+    // A map from user id to UserState.
+    @GuardedBy("mLock")
+    private final SparseArray<UserState> mUserStates = new SparseArray<>();
+
+    // TODO: remove mGetServiceListCalled if onBootPhrase work correctly
+    @GuardedBy("mLock")
+    private boolean mGetServiceListCalled = false;
+
+    private final UserManager mUserManager;
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public TvInteractiveAppManagerService(Context context) {
+        super(context);
+        mContext = context;
+        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+    }
+
+    @GuardedBy("mLock")
+    private void buildTvInteractiveAppServiceListLocked(int userId, String[] updatedPackages) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        userState.mPackageSet.clear();
+
+        if (DEBUG) {
+            Slogf.d(TAG, "buildTvInteractiveAppServiceListLocked");
+        }
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+                new Intent(TvInteractiveAppService.SERVICE_INTERFACE),
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+                userId);
+        List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
+
+        for (ResolveInfo ri : services) {
+            ServiceInfo si = ri.serviceInfo;
+            // TODO: add BIND_TV_INTERACTIVE_APP permission and check it here
+
+            ComponentName component = new ComponentName(si.packageName, si.name);
+            try {
+                TvInteractiveAppInfo info =
+                        new TvInteractiveAppInfo(mContext, component);
+                iAppList.add(info);
+            } catch (Exception e) {
+                Slogf.e(TAG, "failed to load TV Interactive App service " + si.name, e);
+                continue;
+            }
+            userState.mPackageSet.add(si.packageName);
+        }
+
+        // sort the iApp list by iApp service id
+        Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppInfo::getId));
+        Map<String, TvInteractiveAppState> iAppMap = new HashMap<>();
+        ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size());
+        for (TvInteractiveAppInfo info : iAppList) {
+            String iAppServiceId = info.getId();
+            if (DEBUG) {
+                Slogf.d(TAG, "add " + iAppServiceId);
+            }
+            // Running count of Interactive App for each Interactive App service
+            Integer count = tiasAppCount.get(iAppServiceId);
+            count = count == null ? 1 : count + 1;
+            tiasAppCount.put(iAppServiceId, count);
+            TvInteractiveAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+            if (iAppState == null) {
+                iAppState = new TvInteractiveAppState();
+            }
+            iAppState.mInfo = info;
+            iAppState.mUid = getInteractiveAppUid(info);
+            iAppState.mComponentName = info.getComponent();
+            iAppMap.put(iAppServiceId, iAppState);
+            iAppState.mIAppNumber = count;
+        }
+
+        for (String iAppServiceId : iAppMap.keySet()) {
+            if (!userState.mIAppMap.containsKey(iAppServiceId)) {
+                notifyInteractiveAppServiceAddedLocked(userState, iAppServiceId);
+            } else if (updatedPackages != null) {
+                // Notify the package updates
+                ComponentName component = iAppMap.get(iAppServiceId).mInfo.getComponent();
+                for (String updatedPackage : updatedPackages) {
+                    if (component.getPackageName().equals(updatedPackage)) {
+                        updateServiceConnectionLocked(component, userId);
+                        notifyInteractiveAppServiceUpdatedLocked(userState, iAppServiceId);
+                        break;
+                    }
+                }
+            }
+        }
+
+        for (String iAppServiceId : userState.mIAppMap.keySet()) {
+            if (!iAppMap.containsKey(iAppServiceId)) {
+                TvInteractiveAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
+                ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent());
+                if (serviceState != null) {
+                    abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId);
+                }
+                notifyInteractiveAppServiceRemovedLocked(userState, iAppServiceId);
+            }
+        }
+
+        userState.mIAppMap.clear();
+        userState.mIAppMap = iAppMap;
+    }
+
+    @GuardedBy("mLock")
+    private void notifyInteractiveAppServiceAddedLocked(UserState userState, String iAppServiceId) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyInteractiveAppServiceAddedLocked(iAppServiceId="
+                    + iAppServiceId + ")");
+        }
+        int n = userState.mCallbacks.beginBroadcast();
+        for (int i = 0; i < n; ++i) {
+            try {
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onInteractiveAppServiceAdded(iAppServiceId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "failed to report added Interactive App service to callback", e);
+            }
+        }
+        userState.mCallbacks.finishBroadcast();
+    }
+
+    @GuardedBy("mLock")
+    private void notifyInteractiveAppServiceRemovedLocked(
+            UserState userState, String iAppServiceId) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyInteractiveAppServiceRemovedLocked(iAppServiceId="
+                    + iAppServiceId + ")");
+        }
+        int n = userState.mCallbacks.beginBroadcast();
+        for (int i = 0; i < n; ++i) {
+            try {
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onInteractiveAppServiceRemoved(iAppServiceId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "failed to report removed Interactive App service to callback", e);
+            }
+        }
+        userState.mCallbacks.finishBroadcast();
+    }
+
+    @GuardedBy("mLock")
+    private void notifyInteractiveAppServiceUpdatedLocked(
+            UserState userState, String iAppServiceId) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyInteractiveAppServiceUpdatedLocked(iAppServiceId="
+                    + iAppServiceId + ")");
+        }
+        int n = userState.mCallbacks.beginBroadcast();
+        for (int i = 0; i < n; ++i) {
+            try {
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onInteractiveAppServiceUpdated(iAppServiceId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "failed to report updated Interactive App service to callback", e);
+            }
+        }
+        userState.mCallbacks.finishBroadcast();
+    }
+
+    @GuardedBy("mLock")
+    private void notifyStateChangedLocked(
+            UserState userState, String iAppServiceId, int type, int state, int err) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyRteStateChanged(iAppServiceId="
+                    + iAppServiceId + ", type=" + type + ", state=" + state + ", err=" + err + ")");
+        }
+        int n = userState.mCallbacks.beginBroadcast();
+        for (int i = 0; i < n; ++i) {
+            try {
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onStateChanged(iAppServiceId, type, state, err);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "failed to report RTE state changed", e);
+            }
+        }
+        userState.mCallbacks.finishBroadcast();
+    }
+
+    private int getInteractiveAppUid(TvInteractiveAppInfo info) {
+        try {
+            return getContext().getPackageManager().getApplicationInfo(
+                    info.getServiceInfo().packageName, 0).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            Slogf.w(TAG, "Unable to get UID for  " + info, e);
+            return Process.INVALID_UID;
+        }
+    }
+
+    @Override
+    public void onStart() {
+        if (DEBUG) {
+            Slogf.d(TAG, "onStart");
+        }
+        publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService());
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            registerBroadcastReceivers();
+        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            synchronized (mLock) {
+                buildTvInteractiveAppServiceListLocked(mCurrentUserId, null);
+            }
+        }
+    }
+
+    private void registerBroadcastReceivers() {
+        PackageMonitor monitor = new PackageMonitor() {
+            private void buildTvInteractiveAppServiceList(String[] packages) {
+                int userId = getChangingUserId();
+                synchronized (mLock) {
+                    if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
+                        buildTvInteractiveAppServiceListLocked(userId, packages);
+                    }
+                }
+            }
+
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
+                // This callback is invoked when the TV interactive App service is reinstalled.
+                // In this case, isReplacing() always returns true.
+                buildTvInteractiveAppServiceList(new String[] { packageName });
+            }
+
+            @Override
+            public void onPackagesAvailable(String[] packages) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
+                }
+                // This callback is invoked when the media on which some packages exist become
+                // available.
+                if (isReplacing()) {
+                    buildTvInteractiveAppServiceList(packages);
+                }
+            }
+
+            @Override
+            public void onPackagesUnavailable(String[] packages) {
+                // This callback is invoked when the media on which some packages exist become
+                // unavailable.
+                if (DEBUG)  {
+                    Slogf.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
+                            + ")");
+                }
+                if (isReplacing()) {
+                    buildTvInteractiveAppServiceList(packages);
+                }
+            }
+
+            @Override
+            public void onSomePackagesChanged() {
+                if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
+                if (isReplacing()) {
+                    if (DEBUG) {
+                        Slogf.d(TAG, "Skipped building TV interactive App list due to replacing");
+                    }
+                    // When the package is updated, buildTvInteractiveAppServiceListLocked is called
+                    // in other methods instead.
+                    return;
+                }
+                buildTvInteractiveAppServiceList(null);
+            }
+
+            @Override
+            public boolean onPackageChanged(String packageName, int uid, String[] components) {
+                // The interactive App list needs to be updated in any cases, regardless of whether
+                // it happened to the whole package or a specific component. Returning true so that
+                // the update can be handled in {@link #onSomePackagesChanged}.
+                return true;
+            }
+        };
+        monitor.register(mContext, null, UserHandle.ALL, true);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        intentFilter.addAction(Intent.ACTION_USER_STARTED);
+        intentFilter.addAction(Intent.ACTION_USER_STOPPED);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    startUser(userId);
+                } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    stopUser(userId);
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+    }
+
+    private void switchUser(int userId) {
+        synchronized (mLock) {
+            if (mCurrentUserId == userId) {
+                return;
+            }
+            UserInfo userInfo = mUserManager.getUserInfo(userId);
+            if (userInfo.isProfile()) {
+                Slog.w(TAG, "cannot switch to a profile!");
+                return;
+            }
+
+            for (int runningId : mRunningProfiles) {
+                releaseSessionOfUserLocked(runningId);
+                unbindServiceOfUserLocked(runningId);
+            }
+            mRunningProfiles.clear();
+            releaseSessionOfUserLocked(mCurrentUserId);
+            unbindServiceOfUserLocked(mCurrentUserId);
+
+            mCurrentUserId = userId;
+            buildTvInteractiveAppServiceListLocked(userId, null);
+        }
+    }
+
+    private void removeUser(int userId) {
+        synchronized (mLock) {
+            UserState userState = getUserStateLocked(userId);
+            if (userState == null) {
+                return;
+            }
+            // Release all created sessions.
+            for (SessionState state : userState.mSessionStateMap.values()) {
+                if (state.mSession != null) {
+                    try {
+                        state.mSession.release();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in release", e);
+                    }
+                }
+            }
+            userState.mSessionStateMap.clear();
+
+            // Unregister all callbacks and unbind all services.
+            for (ServiceState serviceState : userState.mServiceStateMap.values()) {
+                if (serviceState.mService != null) {
+                    if (serviceState.mCallback != null) {
+                        try {
+                            serviceState.mService.unregisterCallback(serviceState.mCallback);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "error in unregisterCallback", e);
+                        }
+                    }
+                    mContext.unbindService(serviceState.mConnection);
+                }
+            }
+            userState.mServiceStateMap.clear();
+
+            // Clear everything else.
+            userState.mIAppMap.clear();
+            userState.mPackageSet.clear();
+            userState.mClientStateMap.clear();
+            userState.mCallbacks.kill();
+
+            mRunningProfiles.remove(userId);
+            mUserStates.remove(userId);
+
+            if (userId == mCurrentUserId) {
+                switchUser(UserHandle.USER_SYSTEM);
+            }
+        }
+    }
+
+    private void startUser(int userId) {
+        synchronized (mLock) {
+            if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
+                // user already started
+                return;
+            }
+            UserInfo userInfo = mUserManager.getUserInfo(userId);
+            UserInfo parentInfo = mUserManager.getProfileParent(userId);
+            if (userInfo.isProfile()
+                    && parentInfo != null
+                    && parentInfo.id == mCurrentUserId) {
+                // only the children of the current user can be started in background
+                startProfileLocked(userId);
+            }
+        }
+    }
+
+    private void stopUser(int userId) {
+        synchronized (mLock) {
+            if (userId == mCurrentUserId) {
+                switchUser(ActivityManager.getCurrentUser());
+                return;
+            }
+
+            releaseSessionOfUserLocked(userId);
+            unbindServiceOfUserLocked(userId);
+            mRunningProfiles.remove(userId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void startProfileLocked(int userId) {
+        mRunningProfiles.add(userId);
+        buildTvInteractiveAppServiceListLocked(userId, null);
+    }
+
+    @GuardedBy("mLock")
+    private void releaseSessionOfUserLocked(int userId) {
+        UserState userState = getUserStateLocked(userId);
+        if (userState == null) {
+            return;
+        }
+        List<SessionState> sessionStatesToRelease = new ArrayList<>();
+        for (SessionState sessionState : userState.mSessionStateMap.values()) {
+            if (sessionState.mSession != null) {
+                sessionStatesToRelease.add(sessionState);
+            }
+        }
+        for (SessionState sessionState : sessionStatesToRelease) {
+            try {
+                sessionState.mSession.release();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "error in release", e);
+            }
+            clearSessionAndNotifyClientLocked(sessionState);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void unbindServiceOfUserLocked(int userId) {
+        UserState userState = getUserStateLocked(userId);
+        if (userState == null) {
+            return;
+        }
+        for (Iterator<ComponentName> it = userState.mServiceStateMap.keySet().iterator();
+                it.hasNext(); ) {
+            ComponentName component = it.next();
+            ServiceState serviceState = userState.mServiceStateMap.get(component);
+            if (serviceState != null && serviceState.mSessionTokens.isEmpty()) {
+                if (serviceState.mCallback != null) {
+                    try {
+                        serviceState.mService.unregisterCallback(serviceState.mCallback);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in unregisterCallback", e);
+                    }
+                }
+                mContext.unbindService(serviceState.mConnection);
+                it.remove();
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void clearSessionAndNotifyClientLocked(SessionState state) {
+        if (state.mClient != null) {
+            try {
+                state.mClient.onSessionReleased(state.mSeq);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "error in onSessionReleased", e);
+            }
+        }
+        removeSessionStateLocked(state.mSessionToken, state.mUserId);
+    }
+
+    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
+            String methodName) {
+        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
+                false, methodName, null);
+    }
+
+    @GuardedBy("mLock")
+    private UserState getOrCreateUserStateLocked(int userId) {
+        UserState userState = getUserStateLocked(userId);
+        if (userState == null) {
+            userState = new UserState(userId);
+            mUserStates.put(userId, userState);
+        }
+        return userState;
+    }
+
+    @GuardedBy("mLock")
+    private UserState getUserStateLocked(int userId) {
+        return mUserStates.get(userId);
+    }
+
+    @GuardedBy("mLock")
+    private ServiceState getServiceStateLocked(ComponentName component, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        ServiceState serviceState = userState.mServiceStateMap.get(component);
+        if (serviceState == null) {
+            throw new IllegalStateException("Service state not found for " + component + " (userId="
+                    + userId + ")");
+        }
+        return serviceState;
+    }
+
+    @GuardedBy("mLock")
+    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        return getSessionStateLocked(sessionToken, callingUid, userState);
+    }
+
+    @GuardedBy("mLock")
+    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid,
+            UserState userState) {
+        SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
+        if (sessionState == null) {
+            throw new SessionNotFoundException("Session state not found for token " + sessionToken);
+        }
+        // Only the application that requested this session or the system can access it.
+        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
+            throw new SecurityException("Illegal access to the session with token " + sessionToken
+                    + " from uid " + callingUid);
+        }
+        return sessionState;
+    }
+
+    @GuardedBy("mLock")
+    private ITvInteractiveAppSession getSessionLocked(
+            IBinder sessionToken, int callingUid, int userId) {
+        return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
+    }
+
+    @GuardedBy("mLock")
+    private ITvInteractiveAppSession getSessionLocked(SessionState sessionState) {
+        ITvInteractiveAppSession session = sessionState.mSession;
+        if (session == null) {
+            throw new IllegalStateException("Session not yet created for token "
+                    + sessionState.mSessionToken);
+        }
+        return session;
+    }
+
+    private final class BinderService extends ITvInteractiveAppManager.Stub {
+
+        @Override
+        public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "getTvInteractiveAppServiceList");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    if (!mGetServiceListCalled) {
+                        buildTvInteractiveAppServiceListLocked(userId, null);
+                        mGetServiceListCalled = true;
+                    }
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
+                    for (TvInteractiveAppState state : userState.mIAppMap.values()) {
+                        iAppList.add(state.mInfo);
+                    }
+                    return iAppList;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void prepare(String tiasId, int type, int userId) {
+            // TODO: bind service
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "prepare");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
+                    if (iAppState == null) {
+                        Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId);
+                        return;
+                    }
+                    ComponentName componentName = iAppState.mInfo.getComponent();
+                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+                    if (serviceState == null) {
+                        serviceState = new ServiceState(
+                                componentName, tiasId, resolvedUserId, true, type);
+                        userState.mServiceStateMap.put(componentName, serviceState);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    } else if (serviceState.mService != null) {
+                        serviceState.mService.prepare(type);
+                    } else {
+                        serviceState.mPendingPrepare = true;
+                        serviceState.mPendingPrepareType = type;
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    }
+                }
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "error in prepare", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void registerAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "registerAppLinkInfo: " + appLinkInfo);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
+                    if (iAppState == null) {
+                        Slogf.e(TAG, "failed to registerAppLinkInfo - unknown TIAS id "
+                                + tiasId);
+                        return;
+                    }
+                    ComponentName componentName = iAppState.mInfo.getComponent();
+                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+                    if (serviceState == null) {
+                        serviceState = new ServiceState(
+                                componentName, tiasId, resolvedUserId);
+                        serviceState.addPendingAppLink(appLinkInfo, true);
+                        userState.mServiceStateMap.put(componentName, serviceState);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    } else if (serviceState.mService != null) {
+                        serviceState.mService.registerAppLinkInfo(appLinkInfo);
+                    } else {
+                        serviceState.addPendingAppLink(appLinkInfo, true);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    }
+                }
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "error in registerAppLinkInfo", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void unregisterAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "unregisterAppLinkInfo: " + appLinkInfo);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
+                    if (iAppState == null) {
+                        Slogf.e(TAG, "failed to unregisterAppLinkInfo - unknown TIAS id "
+                                + tiasId);
+                        return;
+                    }
+                    ComponentName componentName = iAppState.mInfo.getComponent();
+                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+                    if (serviceState == null) {
+                        serviceState = new ServiceState(
+                                componentName, tiasId, resolvedUserId);
+                        serviceState.addPendingAppLink(appLinkInfo, false);
+                        userState.mServiceStateMap.put(componentName, serviceState);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    } else if (serviceState.mService != null) {
+                        serviceState.mService.unregisterAppLinkInfo(appLinkInfo);
+                    } else {
+                        serviceState.addPendingAppLink(appLinkInfo, false);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    }
+                }
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "error in unregisterAppLinkInfo", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void sendAppLinkCommand(String tiasId, Bundle command, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "sendAppLinkCommand");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
+                    if (iAppState == null) {
+                        Slogf.e(TAG, "failed to sendAppLinkCommand - unknown TIAS id "
+                                + tiasId);
+                        return;
+                    }
+                    ComponentName componentName = iAppState.mInfo.getComponent();
+                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+                    if (serviceState == null) {
+                        serviceState = new ServiceState(
+                                componentName, tiasId, resolvedUserId);
+                        serviceState.addPendingAppLinkCommand(command);
+                        userState.mServiceStateMap.put(componentName, serviceState);
+                    } else if (serviceState.mService != null) {
+                        serviceState.mService.sendAppLinkCommand(command);
+                    } else {
+                        serviceState.addPendingAppLinkCommand(command);
+                    }
+                }
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "error in sendAppLinkCommand", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void createSession(
+                final ITvInteractiveAppClient client, final String iAppServiceId, int type,
+                int seq, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+                    userId, "createSession");
+            final long identity = Binder.clearCallingIdentity();
+
+            try {
+                synchronized (mLock) {
+                    if (userId != mCurrentUserId && !mRunningProfiles.contains(userId)) {
+                        // Only current user and its running profiles can create sessions.
+                        // Let the client get onConnectionFailed callback for this case.
+                        sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
+                        return;
+                    }
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+                    if (iAppState == null) {
+                        Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
+                        sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
+                        return;
+                    }
+                    ServiceState serviceState =
+                            userState.mServiceStateMap.get(iAppState.mComponentName);
+                    if (serviceState == null) {
+                        int tiasUid = PackageManager.getApplicationInfoAsUserCached(
+                                iAppState.mComponentName.getPackageName(), 0, resolvedUserId).uid;
+                        serviceState = new ServiceState(
+                                iAppState.mComponentName, iAppServiceId, resolvedUserId);
+                        userState.mServiceStateMap.put(iAppState.mComponentName, serviceState);
+                    }
+                    // Send a null token immediately while reconnecting.
+                    if (serviceState.mReconnecting) {
+                        sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
+                        return;
+                    }
+
+                    // Create a new session token and a session state.
+                    IBinder sessionToken = new Binder();
+                    SessionState sessionState = new SessionState(sessionToken, iAppServiceId, type,
+                            iAppState.mComponentName, client, seq, callingUid,
+                            callingPid, resolvedUserId);
+
+                    // Add them to the global session state map of the current user.
+                    userState.mSessionStateMap.put(sessionToken, sessionState);
+
+                    // Also, add them to the session state map of the current service.
+                    serviceState.mSessionTokens.add(sessionToken);
+
+                    if (serviceState.mService != null) {
+                        if (!createSessionInternalLocked(serviceState.mService, sessionToken,
+                                resolvedUserId)) {
+                            removeSessionStateLocked(sessionToken, resolvedUserId);
+                        }
+                    } else {
+                        updateServiceConnectionLocked(iAppState.mComponentName, resolvedUserId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void releaseSession(IBinder sessionToken, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "releaseSession");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyTuned(IBinder sessionToken, Uri channelUri, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "notifyTuned(sessionToken=" + sessionToken
+                        + ", Uri=" + channelUri + ")");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "notifyTuned");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyTuned(channelUri);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyTuned", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyTrackSelected(IBinder sessionToken, int type, String trackId,
+                int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "notifyTrackSelected(sessionToken=" + sessionToken
+                        + ", type=" + type + ", trackId=" + trackId + ")");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "notifyTrackSelected");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyTrackSelected(type, trackId);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyTrackSelected", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyTracksChanged(IBinder sessionToken, List<TvTrackInfo> tracks,
+                int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "notifyTracksChanged(sessionToken=" + sessionToken
+                        + ", tracks=" + tracks + ")");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "notifyTracksChanged");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyTracksChanged(tracks);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyTracksChanged", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyVideoAvailable(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyVideoAvailable");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyVideoAvailable();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyVideoAvailable", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyVideoUnavailable(IBinder sessionToken, int reason, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyVideoUnavailable");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyVideoUnavailable(reason);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyVideoUnavailable", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyContentAllowed(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyContentAllowed");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyContentAllowed();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyContentAllowed", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyContentBlocked(IBinder sessionToken, String rating, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyContentBlocked");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyContentBlocked(rating);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyContentBlocked", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifySignalStrength(IBinder sessionToken, int strength, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifySignalStrength");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifySignalStrength(strength);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifySignalStrength", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void startInteractiveApp(IBinder sessionToken, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "startInteractiveApp");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).startInteractiveApp();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in start", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void stopInteractiveApp(IBinder sessionToken, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "BinderService#stop(userId=%d)", userId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "stopInteractiveApp");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).stopInteractiveApp();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in stop", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void resetInteractiveApp(IBinder sessionToken, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "BinderService#reset(userId=%d)", userId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "resetInteractiveApp");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).resetInteractiveApp();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in reset", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void createBiInteractiveApp(
+                IBinder sessionToken, Uri biIAppUri, Bundle params, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "createBiInteractiveApp(biIAppUri=%s,params=%s)", biIAppUri, params);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "createBiInteractiveApp");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).createBiInteractiveApp(
+                                biIAppUri, params);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in createBiInteractiveApp", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void destroyBiInteractiveApp(IBinder sessionToken, String biIAppId, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "destroyBiInteractiveApp(biIAppId=%s)", biIAppId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "destroyBiInteractiveApp");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).destroyBiInteractiveApp(biIAppId);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in destroyBiInteractiveApp", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void setTeletextAppEnabled(IBinder sessionToken, boolean enable, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "setTeletextAppEnabled(enable=%d)", enable);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "setTeletextAppEnabled");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).setTeletextAppEnabled(enable);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in setTeletextAppEnabled", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void sendCurrentChannelUri(IBinder sessionToken, Uri channelUri, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "sendCurrentChannelUri(channelUri=%s)", channelUri.toString());
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "sendCurrentChannelUri");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).sendCurrentChannelUri(channelUri);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in sendCurrentChannelUri", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void sendCurrentChannelLcn(IBinder sessionToken, int lcn, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "sendCurrentChannelLcn(lcn=%d)", lcn);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "sendCurrentChannelLcn");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).sendCurrentChannelLcn(lcn);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in sendCurrentChannelLcn", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void sendStreamVolume(IBinder sessionToken, float volume, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "sendStreamVolume(volume=%f)", volume);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "sendStreamVolume");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).sendStreamVolume(volume);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in sendStreamVolume", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void sendTrackInfoList(IBinder sessionToken, List<TvTrackInfo> tracks, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "sendTrackInfoList(tracks=%s)", tracks.toString());
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "sendTrackInfoList");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).sendTrackInfoList(tracks);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in sendTrackInfoList", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void sendCurrentTvInputId(IBinder sessionToken, String inputId, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "sendCurrentTvInputId(inputId=%s)", inputId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "sendCurrentTvInputId");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).sendCurrentTvInputId(inputId);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in sendCurrentTvInputId", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "setSurface");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).setSurface(surface);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in setSurface", e);
+                    }
+                }
+            } finally {
+                if (surface != null) {
+                    // surface is not used in TvInteractiveAppManagerService.
+                    surface.release();
+                }
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
+                int height, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "dispatchSurfaceChanged");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+                                height);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in dispatchSurfaceChanged", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyBroadcastInfoResponse(IBinder sessionToken,
+                BroadcastInfoResponse response, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyBroadcastInfoResponse");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyBroadcastInfoResponse(response);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyBroadcastInfoResponse", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyAdResponse(IBinder sessionToken, AdResponse response, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyAdResponse");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyAdResponse(response);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyAdResponse", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void registerCallback(final ITvInteractiveAppManagerCallback callback, int userId) {
+            int callingPid = Binder.getCallingPid();
+            int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "registerCallback");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    if (!userState.mCallbacks.register(callback)) {
+                        Slog.e(TAG, "client process has already died");
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void unregisterCallback(ITvInteractiveAppManagerCallback callback, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "unregisterCallback");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    userState.mCallbacks.unregister(callback);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void createMediaView(IBinder sessionToken, IBinder windowToken, Rect frame,
+                int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "createMediaView");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .createMediaView(windowToken, frame);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in createMediaView", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void relayoutMediaView(IBinder sessionToken, Rect frame, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "relayoutMediaView");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .relayoutMediaView(frame);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in relayoutMediaView", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void removeMediaView(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "removeMediaView");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .removeMediaView();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in removeMediaView", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void sendSessionTokenToClientLocked(
+            ITvInteractiveAppClient client, String iAppServiceId, IBinder sessionToken,
+            InputChannel channel, int seq) {
+        try {
+            client.onSessionCreated(iAppServiceId, sessionToken, channel, seq);
+        } catch (RemoteException e) {
+            Slogf.e(TAG, "error in onSessionCreated", e);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean createSessionInternalLocked(
+            ITvInteractiveAppService service, IBinder sessionToken, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
+        if (DEBUG) {
+            Slogf.d(TAG, "createSessionInternalLocked(iAppServiceId="
+                    + sessionState.mIAppServiceId + ")");
+        }
+        InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
+
+        // Set up a callback to send the session token.
+        ITvInteractiveAppSessionCallback callback = new SessionCallback(sessionState, channels);
+
+        boolean created = true;
+        // Create a session. When failed, send a null token immediately.
+        try {
+            service.createSession(
+                    channels[1], callback, sessionState.mIAppServiceId, sessionState.mType);
+        } catch (RemoteException e) {
+            Slogf.e(TAG, "error in createSession", e);
+            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mIAppServiceId, null,
+                    null, sessionState.mSeq);
+            created = false;
+        }
+        channels[1].dispose();
+        return created;
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+        SessionState sessionState = null;
+        try {
+            sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+            UserState userState = getOrCreateUserStateLocked(userId);
+            if (sessionState.mSession != null) {
+                sessionState.mSession.asBinder().unlinkToDeath(sessionState, 0);
+                sessionState.mSession.release();
+            }
+        } catch (RemoteException | SessionNotFoundException e) {
+            Slogf.e(TAG, "error in releaseSession", e);
+        } finally {
+            if (sessionState != null) {
+                sessionState.mSession = null;
+            }
+        }
+        removeSessionStateLocked(sessionToken, userId);
+        return sessionState;
+    }
+
+    @GuardedBy("mLock")
+    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+
+        // Remove the session state from the global session state map of the current user.
+        SessionState sessionState = userState.mSessionStateMap.remove(sessionToken);
+
+        if (sessionState == null) {
+            Slogf.e(TAG, "sessionState null, no more remove session action!");
+            return;
+        }
+
+        // Also remove the session token from the session token list of the current client and
+        // service.
+        ClientState clientState = userState.mClientStateMap.get(sessionState.mClient.asBinder());
+        if (clientState != null) {
+            clientState.mSessionTokens.remove(sessionToken);
+            if (clientState.isEmpty()) {
+                userState.mClientStateMap.remove(sessionState.mClient.asBinder());
+                sessionState.mClient.asBinder().unlinkToDeath(clientState, 0);
+            }
+        }
+
+        ServiceState serviceState = userState.mServiceStateMap.get(sessionState.mComponent);
+        if (serviceState != null) {
+            serviceState.mSessionTokens.remove(sessionToken);
+        }
+        updateServiceConnectionLocked(sessionState.mComponent, userId);
+    }
+
+    @GuardedBy("mLock")
+    private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
+            String iAppServiceId, int userId) {
+        // Let clients know the create session requests are failed.
+        UserState userState = getOrCreateUserStateLocked(userId);
+        List<SessionState> sessionsToAbort = new ArrayList<>();
+        for (IBinder sessionToken : serviceState.mSessionTokens) {
+            SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
+            if (sessionState.mSession == null
+                    && (iAppServiceId == null
+                    || sessionState.mIAppServiceId.equals(iAppServiceId))) {
+                sessionsToAbort.add(sessionState);
+            }
+        }
+        for (SessionState sessionState : sessionsToAbort) {
+            removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
+            sendSessionTokenToClientLocked(sessionState.mClient,
+                    sessionState.mIAppServiceId, null, null, sessionState.mSeq);
+        }
+        updateServiceConnectionLocked(serviceState.mComponent, userId);
+    }
+
+    @GuardedBy("mLock")
+    private void updateServiceConnectionLocked(ComponentName component, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        ServiceState serviceState = userState.mServiceStateMap.get(component);
+        if (serviceState == null) {
+            return;
+        }
+        if (serviceState.mReconnecting) {
+            if (!serviceState.mSessionTokens.isEmpty()) {
+                // wait until all the sessions are removed.
+                return;
+            }
+            serviceState.mReconnecting = false;
+        }
+
+        boolean shouldBind = (!serviceState.mSessionTokens.isEmpty())
+                || (serviceState.mPendingPrepare)
+                || (!serviceState.mPendingAppLinkInfo.isEmpty());
+
+        if (serviceState.mService == null && shouldBind) {
+            // This means that the service is not yet connected but its state indicates that we
+            // have pending requests. Then, connect the service.
+            if (serviceState.mBound) {
+                // We have already bound to the service so we don't try to bind again until after we
+                // unbind later on.
+                return;
+            }
+            if (DEBUG) {
+                Slogf.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
+            }
+
+            Intent i =
+                    new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
+            serviceState.mBound = mContext.bindServiceAsUser(
+                    i, serviceState.mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                    new UserHandle(userId));
+        } else if (serviceState.mService != null && !shouldBind) {
+            // This means that the service is already connected but its state indicates that we have
+            // nothing to do with it. Then, disconnect the service.
+            if (DEBUG) {
+                Slogf.d(TAG, "unbindService(service=" + component + ")");
+            }
+            mContext.unbindService(serviceState.mConnection);
+            userState.mServiceStateMap.remove(component);
+        }
+    }
+
+    private static final class UserState {
+        private final int mUserId;
+        // A mapping from the TV Interactive App ID to its TvInteractiveAppState.
+        private Map<String, TvInteractiveAppState> mIAppMap = new HashMap<>();
+        // A mapping from the token of a client to its state.
+        private final Map<IBinder, ClientState> mClientStateMap = new HashMap<>();
+        // A mapping from the name of a TV Interactive App service to its state.
+        private final Map<ComponentName, ServiceState> mServiceStateMap = new HashMap<>();
+        // A mapping from the token of a TV Interactive App session to its state.
+        private final Map<IBinder, SessionState> mSessionStateMap = new HashMap<>();
+
+        // A set of all TV Interactive App service packages.
+        private final Set<String> mPackageSet = new HashSet<>();
+
+        // A list of callbacks.
+        private final RemoteCallbackList<ITvInteractiveAppManagerCallback> mCallbacks =
+                new RemoteCallbackList<>();
+
+        private UserState(int userId) {
+            mUserId = userId;
+        }
+    }
+
+    private static final class TvInteractiveAppState {
+        private String mIAppServiceId;
+        private ComponentName mComponentName;
+        private TvInteractiveAppInfo mInfo;
+        private int mUid;
+        private int mIAppNumber;
+    }
+
+    private final class SessionState implements IBinder.DeathRecipient {
+        private final IBinder mSessionToken;
+        private ITvInteractiveAppSession mSession;
+        private final String mIAppServiceId;
+        private final int mType;
+        private final ITvInteractiveAppClient mClient;
+        private final int mSeq;
+        private final ComponentName mComponent;
+
+        // The UID of the application that created the session.
+        // The application is usually the TV app.
+        private final int mCallingUid;
+
+        // The PID of the application that created the session.
+        // The application is usually the TV app.
+        private final int mCallingPid;
+
+        private final int mUserId;
+
+        private SessionState(IBinder sessionToken, String iAppServiceId, int type,
+                ComponentName componentName, ITvInteractiveAppClient client, int seq,
+                int callingUid, int callingPid, int userId) {
+            mSessionToken = sessionToken;
+            mIAppServiceId = iAppServiceId;
+            mComponent = componentName;
+            mType = type;
+            mClient = client;
+            mSeq = seq;
+            mCallingUid = callingUid;
+            mCallingPid = callingPid;
+            mUserId = userId;
+        }
+
+        @Override
+        public void binderDied() {
+        }
+    }
+
+    private final class ClientState implements IBinder.DeathRecipient {
+        private final List<IBinder> mSessionTokens = new ArrayList<>();
+
+        private IBinder mClientToken;
+        private final int mUserId;
+
+        ClientState(IBinder clientToken, int userId) {
+            mClientToken = clientToken;
+            mUserId = userId;
+        }
+
+        public boolean isEmpty() {
+            return mSessionTokens.isEmpty();
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                UserState userState = getOrCreateUserStateLocked(mUserId);
+                // DO NOT remove the client state of clientStateMap in this method. It will be
+                // removed in releaseSessionLocked().
+                ClientState clientState = userState.mClientStateMap.get(mClientToken);
+                if (clientState != null) {
+                    while (clientState.mSessionTokens.size() > 0) {
+                        IBinder sessionToken = clientState.mSessionTokens.get(0);
+                        releaseSessionLocked(
+                                sessionToken, Process.SYSTEM_UID, mUserId);
+                        // the releaseSessionLocked function may return before the sessionToken
+                        // is removed if the related sessionState is null. So need to check again
+                        // to avoid death circulation.
+                        if (clientState.mSessionTokens.contains(sessionToken)) {
+                            Slogf.d(TAG, "remove sessionToken " + sessionToken + " for "
+                                    + mClientToken);
+                            clientState.mSessionTokens.remove(sessionToken);
+                        }
+                    }
+                }
+                mClientToken = null;
+            }
+        }
+    }
+
+    private final class ServiceState {
+        private final List<IBinder> mSessionTokens = new ArrayList<>();
+        private final ServiceConnection mConnection;
+        private final ComponentName mComponent;
+        private final String mIAppServiceId;
+        private final List<Pair<AppLinkInfo, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
+        private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
+
+        private boolean mPendingPrepare = false;
+        private Integer mPendingPrepareType = null;
+        private ITvInteractiveAppService mService;
+        private ServiceCallback mCallback;
+        private boolean mBound;
+        private boolean mReconnecting;
+
+        private ServiceState(ComponentName component, String tias, int userId) {
+            this(component, tias, userId, false, null);
+        }
+
+        private ServiceState(ComponentName component, String tias, int userId,
+                boolean pendingPrepare, Integer prepareType) {
+            mComponent = component;
+            mPendingPrepare = pendingPrepare;
+            mPendingPrepareType = prepareType;
+            mConnection = new InteractiveAppServiceConnection(component, userId);
+            mIAppServiceId = tias;
+        }
+
+        private void addPendingAppLink(AppLinkInfo info, boolean register) {
+            mPendingAppLinkInfo.add(Pair.create(info, register));
+        }
+
+        private void addPendingAppLinkCommand(Bundle command) {
+            mPendingAppLinkCommand.add(command);
+        }
+    }
+
+    private final class InteractiveAppServiceConnection implements ServiceConnection {
+        private final ComponentName mComponent;
+        private final int mUserId;
+
+        private InteractiveAppServiceConnection(ComponentName component, int userId) {
+            mComponent = component;
+            mUserId = userId;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onServiceConnected(component=" + component + ")");
+            }
+            synchronized (mLock) {
+                UserState userState = getUserStateLocked(mUserId);
+                if (userState == null) {
+                    // The user was removed while connecting.
+                    mContext.unbindService(this);
+                    return;
+                }
+                ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
+                serviceState.mService = ITvInteractiveAppService.Stub.asInterface(service);
+
+                // Register a callback, if we need to.
+                if (serviceState.mCallback == null) {
+                    serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+                    try {
+                        serviceState.mService.registerCallback(serviceState.mCallback);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in registerCallback", e);
+                    }
+                }
+
+                if (serviceState.mPendingPrepare) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        serviceState.mService.prepare(serviceState.mPendingPrepareType);
+                        serviceState.mPendingPrepare = false;
+                        serviceState.mPendingPrepareType = null;
+                    } catch (RemoteException e) {
+                        Slogf.e(TAG, "error in prepare when onServiceConnected", e);
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+
+                if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
+                    for (Iterator<Pair<AppLinkInfo, Boolean>> it =
+                            serviceState.mPendingAppLinkInfo.iterator();
+                            it.hasNext(); ) {
+                        Pair<AppLinkInfo, Boolean> appLinkInfoPair = it.next();
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            if (appLinkInfoPair.second) {
+                                serviceState.mService.registerAppLinkInfo(appLinkInfoPair.first);
+                            } else {
+                                serviceState.mService.unregisterAppLinkInfo(appLinkInfoPair.first);
+                            }
+                            it.remove();
+                        } catch (RemoteException e) {
+                            Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfoPair
+                                    + ") when onServiceConnected", e);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                    }
+                }
+
+                if (!serviceState.mPendingAppLinkCommand.isEmpty()) {
+                    for (Iterator<Bundle> it = serviceState.mPendingAppLinkCommand.iterator();
+                            it.hasNext(); ) {
+                        Bundle command = it.next();
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            serviceState.mService.sendAppLinkCommand(command);
+                            it.remove();
+                        } catch (RemoteException e) {
+                            Slogf.e(TAG, "error in sendAppLinkCommand(" + command
+                                    + ") when onServiceConnected", e);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                    }
+                }
+
+                List<IBinder> tokensToBeRemoved = new ArrayList<>();
+
+                // And create sessions, if any.
+                for (IBinder sessionToken : serviceState.mSessionTokens) {
+                    if (!createSessionInternalLocked(
+                            serviceState.mService, sessionToken, mUserId)) {
+                        tokensToBeRemoved.add(sessionToken);
+                    }
+                }
+
+                for (IBinder sessionToken : tokensToBeRemoved) {
+                    removeSessionStateLocked(sessionToken, mUserId);
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onServiceDisconnected(component=" + component + ")");
+            }
+            if (!mComponent.equals(component)) {
+                throw new IllegalArgumentException("Mismatched ComponentName: "
+                        + mComponent + " (expected), " + component + " (actual).");
+            }
+            synchronized (mLock) {
+                UserState userState = getOrCreateUserStateLocked(mUserId);
+                ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
+                if (serviceState != null) {
+                    serviceState.mReconnecting = true;
+                    serviceState.mBound = false;
+                    serviceState.mService = null;
+                    serviceState.mCallback = null;
+
+                    abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
+                }
+            }
+        }
+    }
+
+    private final class ServiceCallback extends ITvInteractiveAppServiceCallback.Stub {
+        private final ComponentName mComponent;
+        private final int mUserId;
+
+        ServiceCallback(ComponentName component, int userId) {
+            mComponent = component;
+            mUserId = userId;
+        }
+
+        @Override
+        public void onStateChanged(int type, int state, int error) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
+                    String iAppServiceId = serviceState.mIAppServiceId;
+                    UserState userState = getUserStateLocked(mUserId);
+                    notifyStateChangedLocked(userState, iAppServiceId, type, state, error);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    private final class SessionCallback extends ITvInteractiveAppSessionCallback.Stub {
+        private final SessionState mSessionState;
+        private final InputChannel[] mInputChannels;
+
+        SessionCallback(SessionState sessionState, InputChannel[] channels) {
+            mSessionState = sessionState;
+            mInputChannels = channels;
+        }
+
+        @Override
+        public void onSessionCreated(ITvInteractiveAppSession session) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onSessionCreated(iAppServiceId="
+                        + mSessionState.mIAppServiceId + ")");
+            }
+            synchronized (mLock) {
+                mSessionState.mSession = session;
+                if (session != null && addSessionTokenToClientStateLocked(session)) {
+                    sendSessionTokenToClientLocked(
+                            mSessionState.mClient,
+                            mSessionState.mIAppServiceId,
+                            mSessionState.mSessionToken,
+                            mInputChannels[0],
+                            mSessionState.mSeq);
+                } else {
+                    removeSessionStateLocked(mSessionState.mSessionToken, mSessionState.mUserId);
+                    sendSessionTokenToClientLocked(mSessionState.mClient,
+                            mSessionState.mIAppServiceId, null, null, mSessionState.mSeq);
+                }
+                mInputChannels[0].dispose();
+            }
+        }
+
+        @Override
+        public void onLayoutSurface(int left, int top, int right, int bottom) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+                            + ", right=" + right + ", bottom=" + bottom + ",)");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onLayoutSurface(left, top, right, bottom,
+                            mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onLayoutSurface", e);
+                }
+            }
+        }
+
+        @Override
+        public void onBroadcastInfoRequest(BroadcastInfoRequest request) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onBroadcastInfoRequest (requestId="
+                            + request.getRequestId() + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onBroadcastInfoRequest(request, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onBroadcastInfoRequest", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRemoveBroadcastInfo(int requestId) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRemoveBroadcastInfo (requestId=" + requestId + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRemoveBroadcastInfo(requestId, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRemoveBroadcastInfo", e);
+                }
+            }
+        }
+
+        @Override
+        public void onCommandRequest(
+                @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
+                Bundle parameters) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+                            + parameters.toString() + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onCommandRequest(cmdType, parameters, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onCommandRequest", e);
+                }
+            }
+        }
+
+        @Override
+        public void onSetVideoBounds(Rect rect) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onSetVideoBounds(rect=" + rect + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onSetVideoBounds(rect, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onSetVideoBounds", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentChannelUri() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestCurrentChannelUri");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestCurrentChannelUri(mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestCurrentChannelUri", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentChannelLcn() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestCurrentChannelLcn");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestCurrentChannelLcn(mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestCurrentChannelLcn", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRequestStreamVolume() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestStreamVolume");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestStreamVolume(mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestStreamVolume", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRequestTrackInfoList() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestTrackInfoList");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestTrackInfoList(mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestTrackInfoList", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentTvInputId() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestCurrentTvInputId");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestCurrentTvInputId(mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestCurrentTvInputId", e);
+                }
+            }
+        }
+
+        @Override
+        public void onAdRequest(AdRequest request) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onAdRequest (id=" + request.getId() + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onAdRequest(request, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onAdRequest", e);
+                }
+            }
+        }
+
+        @Override
+        public void onSessionStateChanged(int state, int err) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onSessionStateChanged (state=" + state + ", err=" + err + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onSessionStateChanged(state, err, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onSessionStateChanged", e);
+                }
+            }
+        }
+
+        @Override
+        public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri
+                            + ", biIAppId=" + biIAppId + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onBiInteractiveAppCreated(
+                            biIAppUri, biIAppId, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onBiInteractiveAppCreated", e);
+                }
+            }
+        }
+
+        @Override
+        public void onTeletextAppStateChanged(int state) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onTeletextAppStateChanged (state=" + state + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onTeletextAppStateChanged(state, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onTeletextAppStateChanged", e);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        private boolean addSessionTokenToClientStateLocked(ITvInteractiveAppSession session) {
+            try {
+                session.asBinder().linkToDeath(mSessionState, 0);
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "session process has already died", e);
+                return false;
+            }
+
+            IBinder clientToken = mSessionState.mClient.asBinder();
+            UserState userState = getOrCreateUserStateLocked(mSessionState.mUserId);
+            ClientState clientState = userState.mClientStateMap.get(clientToken);
+            if (clientState == null) {
+                clientState = new ClientState(clientToken, mSessionState.mUserId);
+                try {
+                    clientToken.linkToDeath(clientState, 0);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "client process has already died", e);
+                    return false;
+                }
+                userState.mClientStateMap.put(clientToken, clientState);
+            }
+            clientState.mSessionTokens.add(mSessionState.mSessionToken);
+            return true;
+        }
+    }
+
+    private static class SessionNotFoundException extends IllegalArgumentException {
+        SessionNotFoundException(String name) {
+            super(name);
+        }
+    }
+
+    private static class ClientPidNotFoundException extends IllegalArgumentException {
+        ClientPidNotFoundException(String name) {
+            super(name);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index e508260..af705d5 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -21,6 +21,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.media.IResourceManagerService;
 import android.media.tv.TvInputManager;
 import android.media.tv.tunerresourcemanager.CasSessionRequest;
@@ -288,6 +289,23 @@
         }
 
         @Override
+        public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) {
+            enforceTunerAccessPermission("transferOwner");
+            enforceTrmAccessPermission("transferOwner");
+            synchronized (mLock) {
+                if (!checkClientExists(currentOwnerId)) {
+                    Slog.e(TAG, "currentOwnerId:" + currentOwnerId + " does not exit");
+                    return false;
+                }
+                if (!checkClientExists(newOwnerId)) {
+                    Slog.e(TAG, "newOwnerId:" + newOwnerId + " does not exit");
+                    return false;
+                }
+                return transferOwnerInternal(resourceType, currentOwnerId, newOwnerId);
+            }
+        }
+
+        @Override
         public boolean requestDemux(@NonNull TunerDemuxRequest request,
                     @NonNull int[] demuxHandle) throws RemoteException {
             enforceTunerAccessPermission("requestDemux");
@@ -387,7 +405,11 @@
                 if (fe == null) {
                     throw new RemoteException("Releasing frontend does not exist.");
                 }
-                if (fe.getOwnerClientId() != clientId) {
+                int ownerClientId = fe.getOwnerClientId();
+                ClientProfile ownerProfile = getClientProfile(ownerClientId);
+                if (ownerClientId != clientId
+                        && (ownerProfile != null
+                              && !ownerProfile.getShareFeClientIds().contains(clientId))) {
                     throw new RemoteException(
                             "Client is not the current owner of the releasing fe.");
                 }
@@ -543,6 +565,12 @@
         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump!");
+                return;
+            }
+
             synchronized (mLock) {
                 if (mClientProfiles != null) {
                     pw.println("ClientProfiles:");
@@ -612,6 +640,21 @@
             }
         }
 
+        @Override
+        public int getClientPriority(int useCase, int pid) throws RemoteException {
+            enforceTrmAccessPermission("getClientPriority");
+            synchronized (mLock) {
+                return TunerResourceManagerService.this.getClientPriority(
+                        useCase, checkIsForeground(pid));
+            }
+        }
+        @Override
+        public int getConfigPriority(int useCase, boolean isForeground) throws RemoteException {
+            enforceTrmAccessPermission("getConfigPriority");
+            synchronized (mLock) {
+                return TunerResourceManagerService.this.getClientPriority(useCase, isForeground);
+            }
+        }
     }
 
     /**
@@ -969,6 +1012,83 @@
         getClientProfile(targetClientId).shareFrontend(selfClientId);
     }
 
+    private boolean transferFeOwner(int currentOwnerId, int newOwnerId) {
+        ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
+        ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
+        // change the owner of all the inUse frontend
+        newOwnerProfile.shareFrontend(currentOwnerId);
+        currentOwnerProfile.stopSharingFrontend(newOwnerId);
+        for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
+            getFrontendResource(inUseHandle).setOwner(newOwnerId);
+        }
+        // double check there is no other resources tied to the previous owner
+        for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
+            int ownerId = getFrontendResource(inUseHandle).getOwnerClientId();
+            if (ownerId != newOwnerId) {
+                Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle
+                        + ", " + ownerId + ", " + newOwnerId);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean transferFeCiCamOwner(int currentOwnerId, int newOwnerId) {
+        ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
+        ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
+
+        // link ciCamId to the new profile
+        int ciCamId = currentOwnerProfile.getInUseCiCamId();
+        newOwnerProfile.useCiCam(ciCamId);
+
+        // set the new owner Id
+        CiCamResource ciCam = getCiCamResource(ciCamId);
+        ciCam.setOwner(newOwnerId);
+
+        // unlink cicam resource from the original owner profile
+        currentOwnerProfile.releaseCiCam();
+        return true;
+    }
+
+    private boolean transferLnbOwner(int currentOwnerId, int newOwnerId) {
+        ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
+        ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
+
+        Set<Integer> inUseLnbHandles = new HashSet<>();
+        for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) {
+            // link lnb handle to the new profile
+            newOwnerProfile.useLnb(lnbHandle);
+
+            // set new owner Id
+            LnbResource lnb = getLnbResource(lnbHandle);
+            lnb.setOwner(newOwnerId);
+
+            inUseLnbHandles.add(lnbHandle);
+        }
+
+        // unlink lnb handles from the original owner
+        for (Integer lnbHandle : inUseLnbHandles) {
+            currentOwnerProfile.releaseLnb(lnbHandle);
+        }
+
+        return true;
+    }
+
+    @VisibleForTesting
+    protected boolean transferOwnerInternal(int resourceType, int currentOwnerId, int newOwnerId) {
+        switch (resourceType) {
+            case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND:
+                return transferFeOwner(currentOwnerId, newOwnerId);
+            case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM:
+                return transferFeCiCamOwner(currentOwnerId, newOwnerId);
+            case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB:
+                return transferLnbOwner(currentOwnerId, newOwnerId);
+            default:
+                Slog.e(TAG, "transferOwnerInternal. unsupported resourceType: " + resourceType);
+                return false;
+        }
+    }
+
     @VisibleForTesting
     protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 3442704..6aa06e8 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -104,7 +104,8 @@
 import java.util.Objects;
 
 /** Manages uri grants. */
-public class UriGrantsManagerService extends IUriGrantsManager.Stub {
+public class UriGrantsManagerService extends IUriGrantsManager.Stub implements
+        UriMetricsHelper.PersistentUriGrantsProvider {
     private static final boolean DEBUG = false;
     private static final String TAG = "UriGrantsManagerService";
     // Maximum number of persisted Uri grants a package is allowed
@@ -115,6 +116,7 @@
     private final H mH;
     ActivityManagerInternal mAmInternal;
     PackageManagerInternal mPmInternal;
+    UriMetricsHelper mMetricsHelper;
 
     /** File storing persisted {@link #mGrantedUriPermissions}. */
     private final AtomicFile mGrantFile;
@@ -168,16 +170,19 @@
     }
 
     public static final class Lifecycle extends SystemService {
+        private final Context mContext;
         private final UriGrantsManagerService mService;
 
         public Lifecycle(Context context) {
             super(context);
+            mContext = context;
             mService = new UriGrantsManagerService();
         }
 
         @Override
         public void onStart() {
             publishBinderService(Context.URI_GRANTS_SERVICE, mService);
+            mService.mMetricsHelper = new UriMetricsHelper(mContext, mService);
             mService.start();
         }
 
@@ -186,6 +191,7 @@
             if (phase == PHASE_SYSTEM_SERVICES_READY) {
                 mService.mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
                 mService.mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+                mService.mMetricsHelper.registerPuller();
             }
         }
 
@@ -1298,20 +1304,50 @@
         return false;
     }
 
+    @Override
+    public ArrayList<UriPermission> providePersistentUriGrants() {
+        final ArrayList<UriPermission> result = new ArrayList<>();
+
+        synchronized (mLock) {
+            final int size = mGrantedUriPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+                final int permissionsForPackageSize = perms.size();
+                for (int j = 0; j < permissionsForPackageSize; j++) {
+                    final UriPermission permission = perms.valueAt(j);
+
+                    if (permission.persistedModeFlags != 0) {
+                        result.add(permission);
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
     private void writeGrantedUriPermissions() {
         if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
 
         final long startTime = SystemClock.uptimeMillis();
 
+        int persistentUriPermissionsCount = 0;
+
         // Snapshot permissions so we can persist without lock
         ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
         synchronized (mLock) {
             final int size = mGrantedUriPermissions.size();
             for (int i = 0; i < size; i++) {
                 final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                for (UriPermission perm : perms.values()) {
-                    if (perm.persistedModeFlags != 0) {
-                        persist.add(perm.snapshot());
+
+                final int permissionsForPackageSize = perms.size();
+                for (int j = 0; j < permissionsForPackageSize; j++) {
+                    final UriPermission permission = perms.valueAt(j);
+
+                    if (permission.persistedModeFlags != 0) {
+                        persistentUriPermissionsCount++;
+                        persist.add(permission.snapshot());
                     }
                 }
             }
@@ -1345,6 +1381,8 @@
                 mGrantFile.failWrite(fos);
             }
         }
+
+        mMetricsHelper.reportPersistentUriFlushed(persistentUriPermissionsCount);
     }
 
     final class H extends Handler {
diff --git a/services/core/java/com/android/server/uri/UriMetricsHelper.java b/services/core/java/com/android/server/uri/UriMetricsHelper.java
new file mode 100644
index 0000000..dbc9599
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriMetricsHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.uri;
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.SparseArray;
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+final class UriMetricsHelper {
+
+    private static final StatsManager.PullAtomMetadata DAILY_PULL_METADATA =
+            new StatsManager.PullAtomMetadata.Builder()
+                    .setCoolDownMillis(TimeUnit.DAYS.toMillis(1))
+                    .build();
+
+
+    private final Context mContext;
+    private final PersistentUriGrantsProvider mPersistentUriGrantsProvider;
+
+    UriMetricsHelper(Context context, PersistentUriGrantsProvider provider) {
+        mContext = context;
+        mPersistentUriGrantsProvider = provider;
+    }
+
+    void registerPuller() {
+        final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_AMOUNT_PER_PACKAGE,
+                DAILY_PULL_METADATA,
+                DIRECT_EXECUTOR,
+                (atomTag, data) -> {
+                    reportPersistentUriPermissionsPerPackage(data);
+                    return StatsManager.PULL_SUCCESS;
+                });
+    }
+
+    void reportPersistentUriFlushed(int amount) {
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_FLUSHED,
+                amount
+        );
+    }
+
+    private void reportPersistentUriPermissionsPerPackage(List<StatsEvent> data) {
+        final ArrayList<UriPermission> persistentUriGrants =
+                mPersistentUriGrantsProvider.providePersistentUriGrants();
+
+        final SparseArray<Integer> perUidCount = new SparseArray<>();
+
+        final int persistentUriGrantsSize = persistentUriGrants.size();
+        for (int i = 0; i < persistentUriGrantsSize; i++) {
+            final UriPermission uriPermission = persistentUriGrants.get(i);
+
+            perUidCount.put(
+                    uriPermission.targetUid,
+                    perUidCount.get(uriPermission.targetUid, 0) + 1
+            );
+        }
+
+        final int perUidCountSize = perUidCount.size();
+        for (int i = 0; i < perUidCountSize; i++) {
+            final int uid = perUidCount.keyAt(i);
+            final int amount = perUidCount.valueAt(i);
+
+            data.add(
+                    FrameworkStatsLog.buildStatsEvent(
+                            FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_AMOUNT_PER_PACKAGE,
+                            uid,
+                            amount
+                    )
+            );
+        }
+    }
+
+    interface PersistentUriGrantsProvider {
+        ArrayList<UriPermission> providePersistentUriGrants();
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index a31c56a..a17e792 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -21,6 +21,7 @@
 import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,15 +39,19 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -92,6 +97,10 @@
     @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
     @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
 
+    @NonNull
+    private final List<CarrierPrivilegesListener> mCarrierPrivilegesChangedListeners =
+            new ArrayList<>();
+
     @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
 
     public TelephonySubscriptionTracker(
@@ -126,22 +135,71 @@
                 };
     }
 
-    /** Registers the receivers, and starts tracking subscriptions. */
+    /**
+     * Registers the receivers, and starts tracking subscriptions.
+     *
+     * <p>Must always be run on the VcnManagementService thread.
+     */
     public void register() {
         final HandlerExecutor executor = new HandlerExecutor(mHandler);
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+        filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);
 
-        mContext.registerReceiver(
-                this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler);
+        mContext.registerReceiver(this, filter, null, mHandler);
         mSubscriptionManager.addOnSubscriptionsChangedListener(
                 executor, mSubscriptionChangedListener);
         mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
+
+        registerCarrierPrivilegesListeners();
     }
 
-    /** Unregisters the receivers, and stops tracking subscriptions. */
+    private void registerCarrierPrivilegesListeners() {
+        final HandlerExecutor executor = new HandlerExecutor(mHandler);
+        final int modemCount = mTelephonyManager.getActiveModemCount();
+        try {
+            for (int i = 0; i < modemCount; i++) {
+                CarrierPrivilegesListener carrierPrivilegesListener =
+                        new CarrierPrivilegesListener() {
+                            @Override
+                            public void onCarrierPrivilegesChanged(
+                                    @NonNull List<String> privilegedPackageNames,
+                                    @NonNull int[] privilegedUids) {
+                                // Re-trigger the synchronous check (which is also very cheap due
+                                // to caching in CarrierPrivilegesTracker). This allows consistency
+                                // with the onSubscriptionsChangedListener and broadcasts.
+                                handleSubscriptionsChanged();
+                            }
+                        };
+
+                mTelephonyManager.addCarrierPrivilegesListener(
+                        i, executor, carrierPrivilegesListener);
+                mCarrierPrivilegesChangedListeners.add(carrierPrivilegesListener);
+            }
+        } catch (IllegalArgumentException e) {
+            Slog.wtf(TAG, "Encounted exception registering carrier privileges listeners", e);
+        }
+    }
+
+    /**
+     * Unregisters the receivers, and stops tracking subscriptions.
+     *
+     * <p>Must always be run on the VcnManagementService thread.
+     */
     public void unregister() {
         mContext.unregisterReceiver(this);
         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
         mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
+
+        unregisterCarrierPrivilegesListeners();
+    }
+
+    private void unregisterCarrierPrivilegesListeners() {
+        for (CarrierPrivilegesListener carrierPrivilegesListener :
+                mCarrierPrivilegesChangedListeners) {
+            mTelephonyManager.removeCarrierPrivilegesListener(carrierPrivilegesListener);
+        }
+        mCarrierPrivilegesChangedListeners.clear();
     }
 
     /**
@@ -178,8 +236,6 @@
             // group.
             if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
                     && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
-                // TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker
-
                 final TelephonyManager subIdSpecificTelephonyManager =
                         mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
 
@@ -214,12 +270,39 @@
      */
     @Override
     public void onReceive(Context context, Intent intent) {
-        // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
-        // already was for an identified carrier, we can stop waiting for initial load to complete
-        if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
-            return;
+        switch (intent.getAction()) {
+            case ACTION_CARRIER_CONFIG_CHANGED:
+                handleActionCarrierConfigChanged(context, intent);
+                break;
+            case ACTION_MULTI_SIM_CONFIG_CHANGED:
+                handleActionMultiSimConfigChanged(context, intent);
+                break;
+            default:
+                Slog.v(TAG, "Unknown intent received with action: " + intent.getAction());
+        }
+    }
+
+    private void handleActionMultiSimConfigChanged(Context context, Intent intent) {
+        unregisterCarrierPrivilegesListeners();
+
+        // Clear invalid slotIds from the mReadySubIdsBySlotId map.
+        final int modemCount = mTelephonyManager.getActiveModemCount();
+        final Iterator<Integer> slotIdIterator = mReadySubIdsBySlotId.keySet().iterator();
+        while (slotIdIterator.hasNext()) {
+            final int slotId = slotIdIterator.next();
+
+            if (slotId >= modemCount) {
+                slotIdIterator.remove();
+            }
         }
 
+        registerCarrierPrivilegesListeners();
+        handleSubscriptionsChanged();
+    }
+
+    private void handleActionCarrierConfigChanged(Context context, Intent intent) {
+        // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
+        // already was for an identified carrier, we can stop waiting for initial load to complete
         final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
         final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
 
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index e0cc8e1..f29c40f 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -39,10 +39,13 @@
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnManager.VcnErrorCode;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.provider.Settings;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -57,6 +60,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -148,6 +152,10 @@
     @NonNull private final VcnContentResolver mContentResolver;
     @NonNull private final ContentObserver mMobileDataSettingsObserver;
 
+    @NonNull
+    private final Map<Integer, VcnUserMobileDataStateListener> mMobileDataStateListeners =
+            new ArrayMap<>();
+
     /**
      * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
      *
@@ -221,6 +229,9 @@
         // Update mIsMobileDataEnabled before starting handling of NetworkRequests.
         mIsMobileDataEnabled = getMobileDataStatus();
 
+        // Register mobile data state listeners.
+        updateMobileDataStateListeners();
+
         // Register to receive cached and future NetworkRequests
         mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
     }
@@ -348,6 +359,12 @@
             gatewayConnection.teardownAsynchronously();
         }
 
+        // Unregister MobileDataStateListeners
+        for (VcnUserMobileDataStateListener listener : mMobileDataStateListeners.values()) {
+            getTelephonyManager().unregisterTelephonyCallback(listener);
+        }
+        mMobileDataStateListeners.clear();
+
         mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
     }
 
@@ -454,11 +471,40 @@
             gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
         }
 
+        updateMobileDataStateListeners();
+
         // Update the mobile data state after updating the subscription snapshot as a change in
         // subIds for a subGroup may affect the mobile data state.
         handleMobileDataToggled();
     }
 
+    private void updateMobileDataStateListeners() {
+        final Set<Integer> subIdsInGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+        final HandlerExecutor executor = new HandlerExecutor(this);
+
+        // Register new callbacks
+        for (int subId : subIdsInGroup) {
+            if (!mMobileDataStateListeners.containsKey(subId)) {
+                final VcnUserMobileDataStateListener listener =
+                        new VcnUserMobileDataStateListener();
+
+                getTelephonyManagerForSubid(subId).registerTelephonyCallback(executor, listener);
+                mMobileDataStateListeners.put(subId, listener);
+            }
+        }
+
+        // Unregister old callbacks
+        Iterator<Entry<Integer, VcnUserMobileDataStateListener>> iterator =
+                mMobileDataStateListeners.entrySet().iterator();
+        while (iterator.hasNext()) {
+            final Entry<Integer, VcnUserMobileDataStateListener> entry = iterator.next();
+            if (!subIdsInGroup.contains(entry.getKey())) {
+                getTelephonyManager().unregisterTelephonyCallback(entry.getValue());
+                iterator.remove();
+            }
+        }
+    }
+
     private void handleMobileDataToggled() {
         final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
         mIsMobileDataEnabled = getMobileDataStatus();
@@ -493,11 +539,8 @@
     }
 
     private boolean getMobileDataStatus() {
-        final TelephonyManager genericTelMan =
-                mVcnContext.getContext().getSystemService(TelephonyManager.class);
-
         for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
-            if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
+            if (getTelephonyManagerForSubid(subId).isDataEnabled()) {
                 return true;
             }
         }
@@ -517,6 +560,14 @@
         return request.canBeSatisfiedBy(builder.build());
     }
 
+    private TelephonyManager getTelephonyManager() {
+        return mVcnContext.getContext().getSystemService(TelephonyManager.class);
+    }
+
+    private TelephonyManager getTelephonyManagerForSubid(int subid) {
+        return getTelephonyManager().createForSubscriptionId(subid);
+    }
+
     private String getLogPrefix() {
         return "["
                 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
@@ -670,6 +721,16 @@
         }
     }
 
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    class VcnUserMobileDataStateListener extends TelephonyCallback
+            implements TelephonyCallback.UserMobileDataStateListener {
+
+        @Override
+        public void onUserMobileDataStateChanged(boolean enabled) {
+            sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
+        }
+    }
+
     /** External dependencies used by Vcn, for injection in tests */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class Dependencies {
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 4576957..c96c1ee 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -20,8 +20,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 
 import static com.android.server.VcnManagementService.LOCAL_LOG;
 
@@ -29,10 +29,10 @@
 import android.annotation.Nullable;
 import android.net.NetworkCapabilities;
 import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnCellUnderlyingNetworkPriority;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
 import android.net.vcn.VcnManager;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
-import android.net.vcn.VcnWifiUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.telephony.SubscriptionManager;
@@ -44,7 +44,8 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.VcnContext;
 
-import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /** @hide */
@@ -76,7 +77,7 @@
     public static int calculatePriorityClass(
             VcnContext vcnContext,
             UnderlyingNetworkRecord networkRecord,
-            LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
@@ -94,7 +95,7 @@
         }
 
         int priorityIndex = 0;
-        for (VcnUnderlyingNetworkPriority nwPriority : underlyingNetworkPriorities) {
+        for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) {
             if (checkMatchesPriorityRule(
                     vcnContext,
                     nwPriority,
@@ -113,16 +114,38 @@
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static boolean checkMatchesPriorityRule(
             VcnContext vcnContext,
-            VcnUnderlyingNetworkPriority networkPriority,
+            VcnUnderlyingNetworkTemplate networkPriority,
             UnderlyingNetworkRecord networkRecord,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
-        // TODO: Check Network Quality reported by metric monitors/probers.
-
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
-        if (!networkPriority.allowMetered() && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+        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);
+        if (meteredMatch == MATCH_REQUIRED && !isMetered
+                || meteredMatch == MATCH_FORBIDDEN && isMetered) {
+            return false;
+        }
+
+        // Fails bandwidth requirements if either (a) less than exit threshold, or (b), not
+        // selected, but less than entry threshold
+        if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
+                || (caps.getLinkUpstreamBandwidthKbps()
+                                < networkPriority.getMinEntryUpstreamBandwidthKbps()
+                        && !isSelectedUnderlyingNetwork)) {
+            return false;
+        }
+
+        if (caps.getLinkDownstreamBandwidthKbps()
+                        < networkPriority.getMinExitDownstreamBandwidthKbps()
+                || (caps.getLinkDownstreamBandwidthKbps()
+                                < networkPriority.getMinEntryDownstreamBandwidthKbps()
+                        && !isSelectedUnderlyingNetwork)) {
             return false;
         }
 
@@ -130,32 +153,32 @@
             return true;
         }
 
-        if (networkPriority instanceof VcnWifiUnderlyingNetworkPriority) {
+        if (networkPriority instanceof VcnWifiUnderlyingNetworkTemplate) {
             return checkMatchesWifiPriorityRule(
-                    (VcnWifiUnderlyingNetworkPriority) networkPriority,
+                    (VcnWifiUnderlyingNetworkTemplate) networkPriority,
                     networkRecord,
                     currentlySelected,
                     carrierConfig);
         }
 
-        if (networkPriority instanceof VcnCellUnderlyingNetworkPriority) {
+        if (networkPriority instanceof VcnCellUnderlyingNetworkTemplate) {
             return checkMatchesCellPriorityRule(
                     vcnContext,
-                    (VcnCellUnderlyingNetworkPriority) networkPriority,
+                    (VcnCellUnderlyingNetworkTemplate) networkPriority,
                     networkRecord,
                     subscriptionGroup,
                     snapshot);
         }
 
         logWtf(
-                "Got unknown VcnUnderlyingNetworkPriority class: "
+                "Got unknown VcnUnderlyingNetworkTemplate class: "
                         + networkPriority.getClass().getSimpleName());
         return false;
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static boolean checkMatchesWifiPriorityRule(
-            VcnWifiUnderlyingNetworkPriority networkPriority,
+            VcnWifiUnderlyingNetworkTemplate networkPriority,
             UnderlyingNetworkRecord networkRecord,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
@@ -166,19 +189,19 @@
         }
 
         // TODO: Move the Network Quality check to the network metric monitor framework.
-        if (networkPriority.getNetworkQuality()
-                > getWifiQuality(networkRecord, currentlySelected, carrierConfig)) {
+        if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
             return false;
         }
 
-        if (networkPriority.getSsid() != null && networkPriority.getSsid() != caps.getSsid()) {
+        if (!networkPriority.getSsids().isEmpty()
+                && !networkPriority.getSsids().contains(caps.getSsid())) {
             return false;
         }
 
         return true;
     }
 
-    private static int getWifiQuality(
+    private static boolean isWifiRssiAcceptable(
             UnderlyingNetworkRecord networkRecord,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
@@ -189,20 +212,20 @@
 
         if (isSelectedNetwork
                 && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
-            return NETWORK_QUALITY_OK;
+            return true;
         }
 
         if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
-            return NETWORK_QUALITY_OK;
+            return true;
         }
 
-        return NETWORK_QUALITY_ANY;
+        return false;
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static boolean checkMatchesCellPriorityRule(
             VcnContext vcnContext,
-            VcnCellUnderlyingNetworkPriority networkPriority,
+            VcnCellUnderlyingNetworkTemplate networkPriority,
             UnderlyingNetworkRecord networkRecord,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot) {
@@ -226,26 +249,31 @@
                         .getSystemService(TelephonyManager.class)
                         .createForSubscriptionId(subId);
 
-        if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) {
+        if (!networkPriority.getOperatorPlmnIds().isEmpty()) {
             final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
-            if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) {
+            if (!networkPriority.getOperatorPlmnIds().contains(plmnId)) {
                 return false;
             }
         }
 
-        if (!networkPriority.getAllowedSpecificCarrierIds().isEmpty()) {
+        if (!networkPriority.getSimSpecificCarrierIds().isEmpty()) {
             final int carrierId = subIdSpecificTelephonyMgr.getSimSpecificCarrierId();
-            if (!networkPriority.getAllowedSpecificCarrierIds().contains(carrierId)) {
+            if (!networkPriority.getSimSpecificCarrierIds().contains(carrierId)) {
                 return false;
             }
         }
 
-        if (!networkPriority.allowRoaming() && !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+        final int roamingMatch = networkPriority.getRoaming();
+        final boolean isRoaming = !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        if (roamingMatch == MATCH_REQUIRED && !isRoaming
+                || roamingMatch == MATCH_FORBIDDEN && isRoaming) {
             return false;
         }
 
-        if (networkPriority.requireOpportunistic()) {
-            if (!isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+        final int opportunisticMatch = networkPriority.getOpportunistic();
+        final boolean isOpportunistic = isOpportunistic(snapshot, caps.getSubscriptionIds());
+        if (opportunisticMatch == MATCH_REQUIRED) {
+            if (!isOpportunistic) {
                 return false;
             }
 
@@ -265,6 +293,8 @@
                             .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
                 return false;
             }
+        } else if (opportunisticMatch == MATCH_FORBIDDEN && !isOpportunistic) {
+            return false;
         }
 
         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 cd124ee..ca2e449 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -33,7 +33,7 @@
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.VcnGatewayConnectionConfig;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.ParcelUuid;
@@ -498,10 +498,10 @@
         pw.println(
                 "Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network));
 
-        pw.println("VcnUnderlyingNetworkPriority list:");
+        pw.println("VcnUnderlyingNetworkTemplate list:");
         pw.increaseIndent();
         int index = 0;
-        for (VcnUnderlyingNetworkPriority priority :
+        for (VcnUnderlyingNetworkTemplate priority :
                 mConnectionConfig.getVcnUnderlyingNetworkPriorities()) {
             pw.println("Priority index: " + index);
             priority.dump(pw);
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 27ba854..c0488b1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -21,7 +21,7 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 
@@ -32,7 +32,7 @@
 import com.android.server.vcn.VcnContext;
 
 import java.util.Comparator;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -77,7 +77,7 @@
 
     static Comparator<UnderlyingNetworkRecord> getComparator(
             VcnContext vcnContext,
-            LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
@@ -87,7 +87,7 @@
                     NetworkPriorityClassifier.calculatePriorityClass(
                             vcnContext,
                             left,
-                            underlyingNetworkPriorities,
+                            underlyingNetworkTemplates,
                             subscriptionGroup,
                             snapshot,
                             currentlySelected,
@@ -96,7 +96,7 @@
                     NetworkPriorityClassifier.calculatePriorityClass(
                             vcnContext,
                             right,
-                            underlyingNetworkPriorities,
+                            underlyingNetworkTemplates,
                             subscriptionGroup,
                             snapshot,
                             currentlySelected,
@@ -133,7 +133,7 @@
     void dump(
             VcnContext vcnContext,
             IndentingPrintWriter pw,
-            LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
@@ -145,7 +145,7 @@
                 NetworkPriorityClassifier.calculatePriorityClass(
                         vcnContext,
                         this,
-                        underlyingNetworkPriorities,
+                        underlyingNetworkTemplates,
                         subscriptionGroup,
                         snapshot,
                         currentlySelected,
diff --git a/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java b/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java
index 0690d3b..8189e74 100644
--- a/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java
+++ b/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java
@@ -21,18 +21,16 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.MathUtils;
+import android.util.Range;
 
 import java.util.List;
 
 /**
- * Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and
+ * Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRangeHz()} and
  * amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
  *
- * <p>Devices with no frequency control will collapse all frequencies to zero and leave
- * amplitudes unchanged.
- *
- * <p>The frequency value returned in segments will be absolute, converted with
- * {@link VibratorInfo#getAbsoluteFrequency(float)}.
+ * <p>Devices with no frequency control will collapse all frequencies to the resonant frequency and
+ * leave amplitudes unchanged.
  */
 final class ClippingAmplitudeAndFrequencyAdapter
         implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
@@ -52,29 +50,39 @@
     }
 
     private StepSegment apply(StepSegment segment, VibratorInfo info) {
-        float clampedFrequency = clampFrequency(info, segment.getFrequency());
+        float clampedFrequency = clampFrequency(info, segment.getFrequencyHz());
         return new StepSegment(
                 clampAmplitude(info, clampedFrequency, segment.getAmplitude()),
-                info.getAbsoluteFrequency(clampedFrequency),
+                clampedFrequency,
                 (int) segment.getDuration());
     }
 
     private RampSegment apply(RampSegment segment, VibratorInfo info) {
-        float clampedStartFrequency = clampFrequency(info, segment.getStartFrequency());
-        float clampedEndFrequency = clampFrequency(info, segment.getEndFrequency());
+        float clampedStartFrequency = clampFrequency(info, segment.getStartFrequencyHz());
+        float clampedEndFrequency = clampFrequency(info, segment.getEndFrequencyHz());
         return new RampSegment(
                 clampAmplitude(info, clampedStartFrequency, segment.getStartAmplitude()),
                 clampAmplitude(info, clampedEndFrequency, segment.getEndAmplitude()),
-                info.getAbsoluteFrequency(clampedStartFrequency),
-                info.getAbsoluteFrequency(clampedEndFrequency),
+                clampedStartFrequency,
+                clampedEndFrequency,
                 (int) segment.getDuration());
     }
 
-    private float clampFrequency(VibratorInfo info, float frequency) {
-        return info.getFrequencyRange().clamp(frequency);
+    private float clampFrequency(VibratorInfo info, float frequencyHz) {
+        Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
+        if (frequencyHz == 0 || frequencyRangeHz == null)  {
+            return info.getResonantFrequency();
+        }
+        return frequencyRangeHz.clamp(frequencyHz);
     }
 
-    private float clampAmplitude(VibratorInfo info, float frequency, float amplitude) {
-        return MathUtils.min(amplitude, info.getMaxAmplitude(frequency));
+    private float clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude) {
+        Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
+        if (frequencyRangeHz == null) {
+            // No frequency range was specified, leave amplitude unchanged, the frequency will be
+            // clamped to the device's resonant frequency.
+            return amplitude;
+        }
+        return MathUtils.min(amplitude, info.getMaxAmplitude(frequencyHz));
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/RampDownAdapter.java b/services/core/java/com/android/server/vibrator/RampDownAdapter.java
index e97ed4c..8fec162 100644
--- a/services/core/java/com/android/server/vibrator/RampDownAdapter.java
+++ b/services/core/java/com/android/server/vibrator/RampDownAdapter.java
@@ -90,13 +90,13 @@
 
             if (previousSegment instanceof StepSegment) {
                 float previousAmplitude = ((StepSegment) previousSegment).getAmplitude();
-                float previousFrequency = ((StepSegment) previousSegment).getFrequency();
+                float previousFrequency = ((StepSegment) previousSegment).getFrequencyHz();
 
                 replacementSegments =
                         createStepsDown(previousAmplitude, previousFrequency, offDuration);
             } else if (previousSegment instanceof RampSegment) {
                 float previousAmplitude = ((RampSegment) previousSegment).getEndAmplitude();
-                float previousFrequency = ((RampSegment) previousSegment).getEndFrequency();
+                float previousFrequency = ((RampSegment) previousSegment).getEndFrequencyHz();
 
                 if (offDuration <= mRampDownDuration) {
                     // Replace the zero amplitude segment with a ramp down of same duration, to
@@ -177,12 +177,12 @@
         repeatIndex++;
         if (lastSegment instanceof StepSegment) {
             float previousAmplitude = ((StepSegment) lastSegment).getAmplitude();
-            float previousFrequency = ((StepSegment) lastSegment).getFrequency();
+            float previousFrequency = ((StepSegment) lastSegment).getFrequencyHz();
             segments.addAll(createStepsDown(previousAmplitude, previousFrequency,
                     Math.min(offDuration, mRampDownDuration)));
         } else if (lastSegment instanceof RampSegment) {
             float previousAmplitude = ((RampSegment) lastSegment).getEndAmplitude();
-            float previousFrequency = ((RampSegment) lastSegment).getEndFrequency();
+            float previousFrequency = ((RampSegment) lastSegment).getEndFrequencyHz();
             segments.add(createRampDown(previousAmplitude, previousFrequency,
                     Math.min(offDuration, mRampDownDuration)));
         }
@@ -214,10 +214,10 @@
         if (segment instanceof RampSegment) {
             RampSegment ramp = (RampSegment) segment;
             return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(),
-                    ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration);
+                    ramp.getStartFrequencyHz(), ramp.getEndFrequencyHz(), (int) newDuration);
         } else if (segment instanceof StepSegment) {
             StepSegment step = (StepSegment) segment;
-            return new StepSegment(step.getAmplitude(), step.getFrequency(), (int) newDuration);
+            return new StepSegment(step.getAmplitude(), step.getFrequencyHz(), (int) newDuration);
         }
         return segment;
     }
diff --git a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
index 64624a2..c592a70 100644
--- a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
+++ b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
@@ -21,6 +21,7 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.util.MathUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -52,7 +53,7 @@
             if (!(segment instanceof RampSegment)) {
                 continue;
             }
-            List<StepSegment> steps = apply((RampSegment) segment);
+            List<StepSegment> steps = apply(info, (RampSegment) segment);
             segments.remove(i);
             segments.addAll(i, steps);
             int addedSegments = steps.size() - 1;
@@ -65,11 +66,12 @@
         return repeatIndex;
     }
 
-    private List<StepSegment> apply(RampSegment ramp) {
+    private List<StepSegment> apply(VibratorInfo info, RampSegment ramp) {
         if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
             // Amplitude is the same, so return a single step to simulate this ramp.
             return Arrays.asList(
-                    new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(),
+                    new StepSegment(ramp.getStartAmplitude(),
+                            fillEmptyFrequency(info, ramp.getStartFrequencyHz()),
                             (int) ramp.getDuration()));
         }
 
@@ -77,17 +79,21 @@
         int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
         for (int i = 0; i < stepCount - 1; i++) {
             float pos = (float) i / stepCount;
+            // Fill zero frequency values with the device resonant frequency before interpolating.
+            float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
+            float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
             steps.add(new StepSegment(
-                    interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
-                    interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos),
+                    MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
+                    MathUtils.lerp(startFrequencyHz, endFrequencyHz, pos),
                     mStepDuration));
         }
         int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
-        steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration));
+        float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
+        steps.add(new StepSegment(ramp.getEndAmplitude(), endFrequencyHz, duration));
         return steps;
     }
 
-    private static float interpolate(float start, float end, float position) {
-        return start + position * (end - start);
+    private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
+        return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index 6f5adac..5ace389 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -21,6 +21,7 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.util.MathUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -41,18 +42,18 @@
             // The vibrator does not have PWLE capability, so keep the segments unchanged.
             return repeatIndex;
         }
-        convertStepsToRamps(segments);
+        convertStepsToRamps(info, segments);
         repeatIndex = splitLongRampSegments(info, segments, repeatIndex);
         return repeatIndex;
     }
 
-    private void convertStepsToRamps(List<VibrationEffectSegment> segments) {
+    private void convertStepsToRamps(VibratorInfo info, List<VibrationEffectSegment> segments) {
         int segmentCount = segments.size();
         // Convert steps that require frequency control to ramps.
         for (int i = 0; i < segmentCount; i++) {
             VibrationEffectSegment segment = segments.get(i);
-            if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) {
-                segments.set(i, convertStepToRamp((StepSegment) segment));
+            if (isStep(segment) && ((StepSegment) segment).getFrequencyHz() != 0) {
+                segments.set(i, convertStepToRamp(info, (StepSegment) segment));
             }
         }
         // Convert steps that are next to ramps to also become ramps, so they can be composed
@@ -60,10 +61,10 @@
         for (int i = 0; i < segmentCount; i++) {
             if (segments.get(i) instanceof RampSegment) {
                 for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) {
-                    segments.set(j, convertStepToRamp((StepSegment) segments.get(j)));
+                    segments.set(j, convertStepToRamp(info, (StepSegment) segments.get(j)));
                 }
                 for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); j++) {
-                    segments.set(j, convertStepToRamp((StepSegment) segments.get(j)));
+                    segments.set(j, convertStepToRamp(info, (StepSegment) segments.get(j)));
                 }
             }
         }
@@ -92,7 +93,7 @@
                 continue;
             }
             segments.remove(i);
-            segments.addAll(i, splitRampSegment(ramp, splits));
+            segments.addAll(i, splitRampSegment(info, ramp, splits));
             int addedSegments = splits - 1;
             if (repeatIndex > i) {
                 repeatIndex += addedSegments;
@@ -104,31 +105,40 @@
         return repeatIndex;
     }
 
-    private static RampSegment convertStepToRamp(StepSegment segment) {
+    private static RampSegment convertStepToRamp(VibratorInfo info, StepSegment segment) {
+        float frequencyHz = fillEmptyFrequency(info, segment.getFrequencyHz());
         return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
-                segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
+                frequencyHz, frequencyHz, (int) segment.getDuration());
     }
 
-    private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) {
+    private static List<RampSegment> splitRampSegment(VibratorInfo info, RampSegment ramp,
+            int splits) {
         List<RampSegment> ramps = new ArrayList<>(splits);
+        float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
+        float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
         long splitDuration = ramp.getDuration() / splits;
         float previousAmplitude = ramp.getStartAmplitude();
-        float previousFrequency = ramp.getStartFrequency();
+        float previousFrequency = startFrequencyHz;
         long accumulatedDuration = 0;
 
         for (int i = 1; i < splits; i++) {
             accumulatedDuration += splitDuration;
+            float durationRatio = (float) accumulatedDuration / ramp.getDuration();
+            float interpolatedFrequency =
+                    MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio);
+            float interpolatedAmplitude =
+                    MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), durationRatio);
             RampSegment rampSplit = new RampSegment(
-                    previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration),
-                    previousFrequency, interpolateFrequency(ramp, accumulatedDuration),
+                    previousAmplitude, interpolatedAmplitude,
+                    previousFrequency, interpolatedFrequency,
                     (int) splitDuration);
             ramps.add(rampSplit);
             previousAmplitude = rampSplit.getEndAmplitude();
-            previousFrequency = rampSplit.getEndFrequency();
+            previousFrequency = rampSplit.getEndFrequencyHz();
         }
 
         ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
-                ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration)));
+                endFrequencyHz, (int) (ramp.getDuration() - accumulatedDuration)));
 
         return ramps;
     }
@@ -137,18 +147,7 @@
         return segment instanceof StepSegment;
     }
 
-    private static float interpolateAmplitude(RampSegment ramp, long duration) {
-        return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration,
-                ramp.getDuration());
-    }
-
-    private static float interpolateFrequency(RampSegment ramp, long duration) {
-        return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration,
-                ramp.getDuration());
-    }
-
-    private static float interpolate(float start, float end, long duration, long totalDuration) {
-        float position = (float) duration / totalDuration;
-        return start + position * (end - start);
+    private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
+        return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 1d6e158..0c15ee7 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -373,7 +373,7 @@
             final long token = proto.start(fieldId);
             proto.write(StepSegmentProto.DURATION, segment.getDuration());
             proto.write(StepSegmentProto.AMPLITUDE, segment.getAmplitude());
-            proto.write(StepSegmentProto.FREQUENCY, segment.getFrequency());
+            proto.write(StepSegmentProto.FREQUENCY, segment.getFrequencyHz());
             proto.end(token);
         }
 
@@ -382,8 +382,8 @@
             proto.write(RampSegmentProto.DURATION, segment.getDuration());
             proto.write(RampSegmentProto.START_AMPLITUDE, segment.getStartAmplitude());
             proto.write(RampSegmentProto.END_AMPLITUDE, segment.getEndAmplitude());
-            proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequency());
-            proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequency());
+            proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequencyHz());
+            proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequencyHz());
             proto.end(token);
         }
 
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index f481772..a528f06 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -74,6 +74,12 @@
     public int getExternalVibrationScale(int usageHint) {
         int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
         int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+        if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            // Bypassing user settings, or it has changed between checking and scaling. Use default.
+            return SCALE_NONE;
+        }
+
         int scaleLevel = currentIntensity - defaultIntensity;
 
         if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
@@ -97,6 +103,12 @@
     public <T extends VibrationEffect> T scale(VibrationEffect effect, int usageHint) {
         int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
         int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+        if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            // Bypassing user settings, or it has changed between checking and scaling. Use default.
+            currentIntensity = defaultIntensity;
+        }
+
         int newEffectStrength = intensityToEffectStrength(currentIntensity);
         effect = effect.applyEffectStrength(newEffectStrength).resolve(mDefaultVibrationAmplitude);
         ScaleLevel scale = mScaleLevels.get(currentIntensity - defaultIntensity);
@@ -121,6 +133,12 @@
      */
     public PrebakedSegment scale(PrebakedSegment prebaked, int usageHint) {
         int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+        if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            // Bypassing user settings, or it has changed between checking and scaling. Use default.
+            currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
+        }
+
         int newEffectStrength = intensityToEffectStrength(currentIntensity);
         return prebaked.applyEffectStrength(newEffectStrength);
     }
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 1ee115d..eafd9d7 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,13 +16,16 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
 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_PHYSICAL_EMULATION;
 import static android.os.VibrationAttributes.USAGE_RINGTONE;
 import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -44,8 +47,11 @@
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.os.Vibrator.VibrationIntensity;
+import android.os.vibrator.VibrationConfig;
 import android.provider.Settings;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -91,6 +97,15 @@
                     USAGE_ALARM,
                     USAGE_COMMUNICATION_REQUEST));
 
+    /**
+     * Usage allowed for vibrations when {@link Settings.System#VIBRATE_ON} is disabled.
+     *
+     * <p>The only allowed usage is accessibility, which is applied when the user enables talkback.
+     * Other usages that must ignore this setting should use
+     * {@link VibrationAttributes#FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF}.
+     */
+    private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY;
+
     /** Listener for changes on vibration settings. */
     interface OnVibratorSettingsChanged {
         /** Callback triggered when any of the vibrator settings change. */
@@ -109,53 +124,33 @@
     private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
     private final SparseArray<VibrationEffect> mFallbackEffects;
 
-    private final int mRampStepDuration;
-    private final int mRampDownDuration;
+    private final VibrationConfig mVibrationConfig;
 
     @GuardedBy("mLock")
     @Nullable
-    private Vibrator mVibrator;
-    @GuardedBy("mLock")
-    @Nullable
     private AudioManager mAudioManager;
 
     @GuardedBy("mLock")
     private boolean mVibrateInputDevices;
     @GuardedBy("mLock")
-    private boolean mVibrateWhenRinging;
-    @GuardedBy("mLock")
-    private boolean mApplyRampingRinger;
-    @GuardedBy("mLock")
-    private int mHapticFeedbackIntensity;
-    @GuardedBy("mLock")
-    private int mHardwareFeedbackIntensity;
-    @GuardedBy("mLock")
-    private int mNotificationIntensity;
-    @GuardedBy("mLock")
-    private int mRingIntensity;
+    private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray();
     @GuardedBy("mLock")
     private boolean mBatterySaverMode;
+    @GuardedBy("mLock")
+    private boolean mVibrateOn;
 
     VibrationSettings(Context context, Handler handler) {
-        this(context, handler,
-                context.getResources().getInteger(
-                        com.android.internal.R.integer.config_vibrationWaveformRampDownDuration),
-                context.getResources().getInteger(
-                        com.android.internal.R.integer.config_vibrationWaveformRampStepDuration));
+        this(context, handler, new VibrationConfig(context.getResources()));
     }
 
     @VisibleForTesting
-    VibrationSettings(Context context, Handler handler, int rampDownDuration,
-            int rampStepDuration) {
+    VibrationSettings(Context context, Handler handler, VibrationConfig config) {
         mContext = context;
+        mVibrationConfig = config;
         mSettingObserver = new SettingsObserver(handler);
         mUidObserver = new UidObserver();
         mUserReceiver = new UserObserver();
 
-        // TODO(b/191150049): move these to vibrator static config file
-        mRampDownDuration = rampDownDuration;
-        mRampStepDuration = rampStepDuration;
-
         VibrationEffect clickEffect = createEffectFromResource(
                 com.android.internal.R.array.config_virtualKeyVibePattern);
         VibrationEffect doubleClickEffect = createEffectFromResource(
@@ -179,13 +174,12 @@
 
     public void onSystemReady() {
         synchronized (mLock) {
-            mVibrator = mContext.getSystemService(Vibrator.class);
             mAudioManager = mContext.getSystemService(AudioManager.class);
         }
         try {
             ActivityManager.getService().registerUidObserver(mUidObserver,
                     ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
-                    ActivityManager.PROCESS_STATE_UNKNOWN, null);
+                    ActivityManager.PROCESS_STATE_UNKNOWN, mContext.getOpPackageName());
         } catch (RemoteException e) {
             // ignored; both services live in system_server
         }
@@ -214,12 +208,22 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiver(mUserReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
 
+        // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
+        registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_ON));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
+        registerSettingsObserver(Settings.System.getUriFor(
+                Settings.System.HAPTIC_FEEDBACK_ENABLED));
+        registerSettingsObserver(
+                Settings.System.getUriFor(Settings.System.ALARM_VIBRATION_INTENSITY));
         registerSettingsObserver(
                 Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
         registerSettingsObserver(
+                Settings.System.getUriFor(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY));
+        registerSettingsObserver(
+                Settings.System.getUriFor(Settings.System.MEDIA_VIBRATION_INTENSITY));
+        registerSettingsObserver(
                 Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
         registerSettingsObserver(
                 Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
@@ -253,7 +257,7 @@
      * devices without PWLE support.
      */
     public int getRampStepDuration() {
-        return mRampStepDuration;
+        return mVibrationConfig.getRampStepDurationMs();
     }
 
     /**
@@ -261,7 +265,7 @@
      * when a vibration is cancelled or finished at non-zero amplitude.
      */
     public int getRampDownDuration() {
-        return mRampDownDuration;
+        return mVibrationConfig.getRampDownDurationMs();
     }
 
     /**
@@ -270,25 +274,8 @@
      * @param usageHint one of VibrationAttributes.USAGE_*
      * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
      */
-    public int getDefaultIntensity(int usageHint) {
-        if (usageHint == USAGE_ALARM) {
-            return Vibrator.VIBRATION_INTENSITY_HIGH;
-        }
-        synchronized (mLock) {
-            if (mVibrator != null) {
-                switch (usageHint) {
-                    case USAGE_RINGTONE:
-                        return mVibrator.getDefaultRingVibrationIntensity();
-                    case USAGE_NOTIFICATION:
-                        return mVibrator.getDefaultNotificationVibrationIntensity();
-                    case USAGE_TOUCH:
-                    case USAGE_HARDWARE_FEEDBACK:
-                    case USAGE_PHYSICAL_EMULATION:
-                        return mVibrator.getDefaultHapticFeedbackIntensity();
-                }
-            }
-        }
-        return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+    public int getDefaultIntensity(@VibrationAttributes.Usage int usageHint) {
+        return mVibrationConfig.getDefaultVibrationIntensity(usageHint);
     }
 
     /**
@@ -297,23 +284,10 @@
      * @param usageHint one of VibrationAttributes.USAGE_*
      * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
      */
-    public int getCurrentIntensity(int usageHint) {
+    public int getCurrentIntensity(@VibrationAttributes.Usage int usageHint) {
+        int defaultIntensity = getDefaultIntensity(usageHint);
         synchronized (mLock) {
-            switch (usageHint) {
-                case USAGE_RINGTONE:
-                    return mRingIntensity;
-                case USAGE_NOTIFICATION:
-                    return mNotificationIntensity;
-                case USAGE_TOUCH:
-                    return mHapticFeedbackIntensity;
-                case USAGE_HARDWARE_FEEDBACK:
-                case USAGE_PHYSICAL_EMULATION:
-                    return mHardwareFeedbackIntensity;
-                case USAGE_ALARM:
-                    return Vibrator.VIBRATION_INTENSITY_HIGH;
-                default:
-                    return Vibrator.VIBRATION_INTENSITY_MEDIUM;
-            }
+            return mCurrentVibrationIntensities.get(usageHint, defaultIntensity);
         }
     }
 
@@ -352,9 +326,14 @@
                 return Vibration.Status.IGNORED_FOR_POWER;
             }
 
-            int intensity = getCurrentIntensity(usage);
-            if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
-                return Vibration.Status.IGNORED_FOR_SETTINGS;
+            if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
+                if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
+                    return Vibration.Status.IGNORED_FOR_SETTINGS;
+                }
+
+                if (getCurrentIntensity(usage) == Vibrator.VIBRATION_INTENSITY_OFF) {
+                    return Vibration.Status.IGNORED_FOR_SETTINGS;
+                }
             }
 
             if (!shouldVibrateForRingerModeLocked(usage)) {
@@ -371,7 +350,7 @@
      * for touch and ringtone usages only. All other usages are allowed by this method.
      */
     @GuardedBy("mLock")
-    private boolean shouldVibrateForRingerModeLocked(int usageHint) {
+    private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) {
         // If audio manager was not loaded yet then assume most restrictive mode.
         int ringerMode = (mAudioManager == null)
                 ? AudioManager.RINGER_MODE_SILENT
@@ -379,18 +358,9 @@
 
         switch (usageHint) {
             case USAGE_TOUCH:
-                // Touch feedback disabled when phone is on silent mode.
-                return ringerMode != AudioManager.RINGER_MODE_SILENT;
             case USAGE_RINGTONE:
-                switch (ringerMode) {
-                    case AudioManager.RINGER_MODE_SILENT:
-                        return false;
-                    case AudioManager.RINGER_MODE_VIBRATE:
-                        return true;
-                    default:
-                        // Ringtone vibrations also depend on 2 other settings:
-                        return mVibrateWhenRinging || mApplyRampingRinger;
-                }
+                // Touch feedback and ringtone disabled when phone is on silent mode.
+                return ringerMode != AudioManager.RINGER_MODE_SILENT;
             default:
                 // All other usages ignore ringer mode settings.
                 return true;
@@ -401,64 +371,91 @@
     @VisibleForTesting
     void updateSettings() {
         synchronized (mLock) {
-            mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
-            mApplyRampingRinger = getSystemSetting(Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
-            mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                    getDefaultIntensity(USAGE_TOUCH));
-            mHardwareFeedbackIntensity = getSystemSetting(
-                    Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
-                    getHardwareFeedbackIntensityWhenSettingIsMissing(mHapticFeedbackIntensity));
-            mNotificationIntensity = getSystemSetting(
-                    Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                    getDefaultIntensity(USAGE_NOTIFICATION));
-            mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
+            mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
+            mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
+
+            int alarmIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
+                    getDefaultIntensity(USAGE_ALARM));
+            int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH);
+            int hapticFeedbackIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1),
+                    defaultHapticFeedbackIntensity);
+            int positiveHapticFeedbackIntensity = toPositiveIntensity(
+                    hapticFeedbackIntensity, defaultHapticFeedbackIntensity);
+            int hardwareFeedbackIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1),
+                    positiveHapticFeedbackIntensity);
+            int mediaIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1),
+                    getDefaultIntensity(USAGE_MEDIA));
+            int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION);
+            int notificationIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1),
+                    defaultNotificationIntensity);
+            int positiveNotificationIntensity = toPositiveIntensity(
+                    notificationIntensity, defaultNotificationIntensity);
+            int ringIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
                     getDefaultIntensity(USAGE_RINGTONE));
-            mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
+
+
+            mCurrentVibrationIntensities.clear();
+            mCurrentVibrationIntensities.put(USAGE_ALARM, alarmIntensity);
+            mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity);
+            mCurrentVibrationIntensities.put(USAGE_MEDIA, mediaIntensity);
+            mCurrentVibrationIntensities.put(USAGE_UNKNOWN, mediaIntensity);
+
+            // Communication request is not disabled by the notification setting.
+            mCurrentVibrationIntensities.put(USAGE_COMMUNICATION_REQUEST,
+                    positiveNotificationIntensity);
+
+            if (!loadBooleanSetting(Settings.System.VIBRATE_WHEN_RINGING)
+                    && !loadBooleanSetting(Settings.System.APPLY_RAMPING_RINGER)) {
+                // Make sure deprecated boolean setting still disables ringtone vibrations.
+                mCurrentVibrationIntensities.put(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_OFF);
+            } else {
+                mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity);
+            }
+
+            // This should adapt the behavior preceding the introduction of this new setting
+            // key, which is to apply HAPTIC_FEEDBACK_INTENSITY, unless it's disabled.
+            mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);
+            mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity);
+
+            if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) {
+                // Make sure deprecated boolean setting still disables touch vibrations.
+                mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF);
+            } else {
+                mCurrentVibrationIntensities.put(USAGE_TOUCH, hapticFeedbackIntensity);
+            }
+
+            // A11y is not disabled by any haptic feedback setting.
+            mCurrentVibrationIntensities.put(USAGE_ACCESSIBILITY, positiveHapticFeedbackIntensity);
         }
         notifyListeners();
     }
 
-    /**
-     * Return the value to be used for {@link Settings.System#HARDWARE_HAPTIC_FEEDBACK_INTENSITY}
-     * when the value was not set by the user.
-     *
-     * <p>This should adapt the behavior preceding the introduction of this new setting key, which
-     * is to apply {@link Settings.System#HAPTIC_FEEDBACK_INTENSITY} unless it's disabled.
-     */
-    private int getHardwareFeedbackIntensityWhenSettingIsMissing(int hapticFeedbackIntensity) {
-        if (hapticFeedbackIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
-            return getDefaultIntensity(USAGE_HARDWARE_FEEDBACK);
-        }
-        return hapticFeedbackIntensity;
-    }
-
     @Override
     public String toString() {
         synchronized (mLock) {
+            StringBuilder vibrationIntensitiesString = new StringBuilder("{");
+            for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+                int usage = mCurrentVibrationIntensities.keyAt(i);
+                int intensity = mCurrentVibrationIntensities.valueAt(i);
+                vibrationIntensitiesString.append(VibrationAttributes.usageToString(usage))
+                        .append("=(").append(intensityToString(intensity))
+                        .append(",default:").append(intensityToString(getDefaultIntensity(usage)))
+                        .append("), ");
+            }
+            vibrationIntensitiesString.append('}');
             return "VibrationSettings{"
-                    + "mVibrateInputDevices=" + mVibrateInputDevices
-                    + ", mVibrateWhenRinging=" + mVibrateWhenRinging
-                    + ", mApplyRampingRinger=" + mApplyRampingRinger
+                    + "mVibratorConfig=" + mVibrationConfig
+                    + ", mVibrateInputDevices=" + mVibrateInputDevices
                     + ", mBatterySaverMode=" + mBatterySaverMode
+                    + ", mVibrateOn=" + mVibrateOn
+                    + ", mVibrationIntensities=" + vibrationIntensitiesString
                     + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
-                    + ", mHapticChannelMaxVibrationAmplitude="
-                    + getHapticChannelMaxVibrationAmplitude()
-                    + ", mRampStepDuration=" + mRampStepDuration
-                    + ", mRampDownDuration=" + mRampDownDuration
-                    + ", mHardwareHapticFeedbackIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
-                    + ", mHapticFeedbackIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_TOUCH))
-                    + ", mHapticFeedbackDefaultIntensity="
-                    + intensityToString(getDefaultIntensity(USAGE_TOUCH))
-                    + ", mNotificationIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
-                    + ", mNotificationDefaultIntensity="
-                    + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
-                    + ", mRingIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_RINGTONE))
-                    + ", mRingDefaultIntensity="
-                    + intensityToString(getDefaultIntensity(USAGE_RINGTONE))
                     + '}';
         }
     }
@@ -466,16 +463,30 @@
     /** Write current settings into given {@link ProtoOutputStream}. */
     public void dumpProto(ProtoOutputStream proto) {
         synchronized (mLock) {
+            proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
+            proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
+            proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
+                    getCurrentIntensity(USAGE_ALARM));
+            proto.write(VibratorManagerServiceDumpProto.ALARM_DEFAULT_INTENSITY,
+                    getDefaultIntensity(USAGE_ALARM));
+            proto.write(VibratorManagerServiceDumpProto.HARDWARE_FEEDBACK_INTENSITY,
+                    getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+            proto.write(VibratorManagerServiceDumpProto.HARDWARE_FEEDBACK_DEFAULT_INTENSITY,
+                    getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
             proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
-                    mHapticFeedbackIntensity);
+                    getCurrentIntensity(USAGE_TOUCH));
             proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
                     getDefaultIntensity(USAGE_TOUCH));
+            proto.write(VibratorManagerServiceDumpProto.MEDIA_INTENSITY,
+                    getCurrentIntensity(USAGE_MEDIA));
+            proto.write(VibratorManagerServiceDumpProto.MEDIA_DEFAULT_INTENSITY,
+                    getDefaultIntensity(USAGE_MEDIA));
             proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY,
-                    mNotificationIntensity);
+                    getCurrentIntensity(USAGE_NOTIFICATION));
             proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
                     getDefaultIntensity(USAGE_NOTIFICATION));
             proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY,
-                    mRingIntensity);
+                    getCurrentIntensity(USAGE_RINGTONE));
             proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY,
                     getDefaultIntensity(USAGE_RINGTONE));
         }
@@ -506,13 +517,29 @@
         }
     }
 
-    private float getHapticChannelMaxVibrationAmplitude() {
-        synchronized (mLock) {
-            return mVibrator == null ? Float.NaN : mVibrator.getHapticChannelMaximumAmplitude();
+    @VibrationIntensity
+    private int toPositiveIntensity(int value, @VibrationIntensity int defaultValue) {
+        if (value == Vibrator.VIBRATION_INTENSITY_OFF) {
+            return defaultValue;
         }
+        return toIntensity(value, defaultValue);
     }
 
-    private int getSystemSetting(String settingName, int defaultValue) {
+    @VibrationIntensity
+    private int toIntensity(int value, @VibrationIntensity int defaultValue) {
+        if ((value < Vibrator.VIBRATION_INTENSITY_OFF)
+                || (value > Vibrator.VIBRATION_INTENSITY_HIGH)) {
+            return defaultValue;
+        }
+        return value;
+    }
+
+    private boolean loadBooleanSetting(String settingKey) {
+        return Settings.System.getIntForUser(mContext.getContentResolver(),
+                settingKey, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
+    private int loadSystemSetting(String settingName, int defaultValue) {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
                 settingName, defaultValue, UserHandle.USER_CURRENT);
     }
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 4a1b95b..47b3e1a 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -36,8 +36,6 @@
 /** Controls a single vibrator. */
 final class VibratorController {
     private static final String TAG = "VibratorController";
-    // TODO(b/167947076): load suggested range from config
-    private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
 
     private final Object mLock = new Object();
 
@@ -74,8 +72,7 @@
         mNativeWrapper = nativeWrapper;
         mNativeWrapper.init(vibratorId, listener);
         VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
-        mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
-                vibratorInfoBuilder);
+        mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
         mVibratorInfo = vibratorInfoBuilder.build();
 
         if (!mVibratorInfoLoadSuccessful) {
@@ -126,8 +123,7 @@
             }
             int vibratorId = mVibratorInfo.getId();
             VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
-            mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
-                    vibratorInfoBuilder);
+            mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
             mVibratorInfo = vibratorInfoBuilder.build();
             if (!mVibratorInfoLoadSuccessful) {
                 Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
@@ -419,8 +415,7 @@
 
         private static native void alwaysOnDisable(long nativePtr, long id);
 
-        private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange,
-                VibratorInfo.Builder infoBuilder);
+        private static native boolean getInfo(long nativePtr, VibratorInfo.Builder infoBuilder);
 
         private long mNativePtr = 0;
 
@@ -490,8 +485,8 @@
         /**
          * Loads device vibrator metadata and returns true if all metadata was loaded successfully.
          */
-        public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
-            return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder);
+        public boolean getInfo(VibratorInfo.Builder infoBuilder) {
+            return getInfo(mNativePtr, infoBuilder);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 478e86e..a95b6c9 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -80,6 +80,9 @@
     private static final boolean DEBUG = false;
     private static final VibrationAttributes DEFAULT_ATTRIBUTES =
             new VibrationAttributes.Builder().build();
+    private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
+            VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
+                    | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 
     /** Lifecycle responsible for initializing this class at the right system server phases. */
     public static class Lifecycle extends SystemService {
@@ -975,12 +978,12 @@
             usage = VibrationAttributes.USAGE_TOUCH;
         }
         int flags = attrs.getFlags();
-        if (attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+        if ((flags & ATTRIBUTES_ALL_BYPASS_FLAGS) != 0) {
             if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
                     || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
                     || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
-                // Remove bypass policy flag from attributes if the app does not have permissions.
-                flags &= ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+                // Remove bypass flags from attributes if the app does not have permissions.
+                flags &= ~ATTRIBUTES_ALL_BYPASS_FLAGS;
             }
         }
         if ((usage == attrs.getUsage()) && (flags == attrs.getFlags())) {
@@ -1756,17 +1759,23 @@
                 }
                 if (hasFrequencies) {
                     frequencies.add(Float.parseFloat(getNextArgRequired()));
-                } else {
-                    frequencies.add(0f);
                 }
             }
 
             VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform();
             for (int i = 0; i < durations.size(); i++) {
                 if (isContinuous) {
-                    waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i));
+                    if (hasFrequencies) {
+                        waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i));
+                    } else {
+                        waveform.addRamp(amplitudes.get(i), durations.get(i));
+                    }
                 } else {
-                    waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i));
+                    if (hasFrequencies) {
+                        waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i));
+                    } else {
+                        waveform.addStep(amplitudes.get(i), durations.get(i));
+                    }
                 }
             }
             composition.addEffect(waveform.build(repeat), delay);
@@ -1865,7 +1874,7 @@
                 pw.println("    If -c is provided, the waveform is continuous and will ramp");
                 pw.println("    between values; otherwise each entry is a fixed step.");
                 pw.println("    Duration is in milliseconds; amplitude is a scale of 1-255;");
-                pw.println("    frequency is a relative value around resonant frequency 0;");
+                pw.println("    frequency is an absolute value in hertz;");
                 pw.println("  prebaked [-w delay] [-b] <effect-id>");
                 pw.println("    Vibrates with prebaked effect; ignored when device is on DND ");
                 pw.println("    (Do Not Disturb) mode; touch feedback strength user setting ");
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 39d7a15..7697490 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -177,7 +177,7 @@
                         }
                     }
                 }
-            }, intentFilter);
+            }, intentFilter, Context.RECEIVER_NOT_EXPORTED);
         }
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2ec42b4..ee2cc7b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -81,7 +81,9 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.SELinux;
+import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -92,6 +94,7 @@
 import android.service.wallpaper.WallpaperService;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -420,7 +423,7 @@
                 Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
             }
 
-            needsExtraction = wallpaper.primaryColors == null;
+            needsExtraction = wallpaper.primaryColors == null || wallpaper.mIsColorExtractedFromDim;
         }
 
         if (needsExtraction) {
@@ -491,12 +494,17 @@
         String cropFile = null;
         boolean defaultImageWallpaper = false;
         int wallpaperId;
+        float dimAmount;
+
+        synchronized (mLock) {
+            wallpaper.mIsColorExtractedFromDim = false;
+        }
 
         if (wallpaper.equals(mFallbackWallpaper)) {
             synchronized (mLock) {
                 if (mFallbackWallpaper.primaryColors != null) return;
             }
-            final WallpaperColors colors = extractDefaultImageWallpaperColors();
+            final WallpaperColors colors = extractDefaultImageWallpaperColors(wallpaper);
             synchronized (mLock) {
                 mFallbackWallpaper.primaryColors = colors;
             }
@@ -513,18 +521,19 @@
                 defaultImageWallpaper = true;
             }
             wallpaperId = wallpaper.wallpaperId;
+            dimAmount = wallpaper.mWallpaperDimAmount;
         }
 
         WallpaperColors colors = null;
         if (cropFile != null) {
             Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
             if (bitmap != null) {
-                colors = WallpaperColors.fromBitmap(bitmap);
+                colors = WallpaperColors.fromBitmap(bitmap, dimAmount);
                 bitmap.recycle();
             }
         } else if (defaultImageWallpaper) {
             // There is no crop and source file because this is default image wallpaper.
-            colors = extractDefaultImageWallpaperColors();
+            colors = extractDefaultImageWallpaperColors(wallpaper);
         }
 
         if (colors == null) {
@@ -544,11 +553,13 @@
         }
     }
 
-    private WallpaperColors extractDefaultImageWallpaperColors() {
+    private WallpaperColors extractDefaultImageWallpaperColors(WallpaperData wallpaper) {
         if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
+        float dimAmount;
 
         synchronized (mLock) {
             if (mCacheDefaultImageWallpaperColors != null) return mCacheDefaultImageWallpaperColors;
+            dimAmount = wallpaper.mWallpaperDimAmount;
         }
 
         WallpaperColors colors = null;
@@ -561,7 +572,7 @@
             final BitmapFactory.Options options = new BitmapFactory.Options();
             final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
             if (bitmap != null) {
-                colors = WallpaperColors.fromBitmap(bitmap);
+                colors = WallpaperColors.fromBitmap(bitmap, dimAmount);
                 bitmap.recycle();
             }
         } catch (OutOfMemoryError e) {
@@ -948,6 +959,23 @@
         WallpaperObserver wallpaperObserver;
 
         /**
+         * The dim amount to be applied to the wallpaper.
+         */
+        float mWallpaperDimAmount = 0.0f;
+
+        /**
+         * A map to keep track of the dimming set by different applications. The key is the calling
+         * UID and the value is the dim amount.
+         */
+        ArrayMap<Integer, Float> mUidToDimAmount = new ArrayMap<>();
+
+        /**
+         * Whether we need to extract the wallpaper colors again to calculate the dark hints
+         * after dimming is applied.
+         */
+        boolean mIsColorExtractedFromDim;
+
+        /**
          * List of callbacks registered they should each be notified when the wallpaper is changed.
          */
         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
@@ -1487,6 +1515,15 @@
                         Slog.w(TAG, "Failed to register local colors areas", e);
                     }
                 }
+
+                if (mWallpaper.mWallpaperDimAmount != 0f) {
+                    try {
+                        connector.mEngine.applyDimming(mWallpaper.mWallpaperDimAmount);
+                        notifyWallpaperColorsChanged(mWallpaper, FLAG_SYSTEM);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to dim wallpaper", e);
+                    }
+                }
             }
         }
 
@@ -2536,6 +2573,98 @@
         if (purgeAreas.size() > 0) engine.removeLocalColorsAreas(purgeAreas);
     }
 
+    /**
+     * Returns true if the lock screen wallpaper exists (different wallpaper from the system)
+     */
+    @Override
+    public boolean lockScreenWallpaperExists() {
+        synchronized (mLock) {
+            return mLockWallpaperMap.get(mCurrentUserId) != null;
+        }
+    }
+
+    /**
+     * Sets wallpaper dim amount for the calling UID. This only applies to FLAG_SYSTEM wallpaper as
+     * the lock screen does not have a wallpaper component, so we use mWallpaperMap.
+     *
+     * @param dimAmount Dim amount which would be blended with the system default dimming.
+     */
+    @Override
+    public void setWallpaperDimAmount(float dimAmount) throws RemoteException {
+        checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
+        int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
+                WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
+
+                if (dimAmount == 0.0f) {
+                    wallpaper.mUidToDimAmount.remove(uid);
+                } else {
+                    wallpaper.mUidToDimAmount.put(uid, dimAmount);
+                }
+
+                float maxDimAmount = getHighestDimAmountFromMap(wallpaper.mUidToDimAmount);
+                wallpaper.mWallpaperDimAmount = maxDimAmount;
+                // Also set the dim amount to the lock screen wallpaper if the lock and home screen
+                // do not share the same wallpaper
+                if (lockWallpaper != null) {
+                    lockWallpaper.mWallpaperDimAmount = maxDimAmount;
+                }
+
+                if (wallpaper.connection != null) {
+                    wallpaper.connection.forEachDisplayConnector(connector -> {
+                        if (connector.mEngine != null) {
+                            try {
+                                connector.mEngine.applyDimming(maxDimAmount);
+                            } catch (RemoteException e) {
+                                Slog.w(TAG,
+                                        "Can't apply dimming on wallpaper display connector", e);
+                            }
+                        }
+                    });
+                    // Need to extract colors again to re-calculate dark hints after
+                    // applying dimming.
+                    wallpaper.mIsColorExtractedFromDim = true;
+                    notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
+                    if (lockWallpaper != null) {
+                        lockWallpaper.mIsColorExtractedFromDim = true;
+                        notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+                    }
+                    saveSettingsLocked(wallpaper.userId);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public float getWallpaperDimAmount() {
+        checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
+        synchronized (mLock) {
+            WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+            return data.mWallpaperDimAmount;
+        }
+    }
+
+    /**
+     * Gets the highest dim amount among all the calling UIDs that set the wallpaper dim amount.
+     * Return 0f as default value to indicate no application has dimmed the wallpaper.
+     *
+     * @param uidToDimAmountMap Map of UIDs to dim amounts
+     */
+    private float getHighestDimAmountFromMap(ArrayMap<Integer, Float> uidToDimAmountMap) {
+        float maxDimAmount = 0.0f;
+        for (Map.Entry<Integer, Float> entry : uidToDimAmountMap.entrySet()) {
+            if (entry.getValue() > maxDimAmount) {
+                maxDimAmount = entry.getValue();
+            }
+        }
+        return maxDimAmount;
+    }
+
     @Override
     public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
             throws RemoteException {
@@ -2562,7 +2691,8 @@
             if (wallpaperData == null) {
                 return null;
             }
-            shouldExtract = wallpaperData.primaryColors == null;
+            shouldExtract = wallpaperData.primaryColors == null
+                    || wallpaperData.mIsColorExtractedFromDim;
         }
 
         if (shouldExtract) {
@@ -2664,6 +2794,7 @@
         lockWP.cropHint.set(sysWP.cropHint);
         lockWP.allowBackup = sysWP.allowBackup;
         lockWP.primaryColors = sysWP.primaryColors;
+        lockWP.mWallpaperDimAmount = sysWP.mWallpaperDimAmount;
 
         // Migrate the bitmap files outright; no need to copy
         try {
@@ -3191,6 +3322,18 @@
             out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
         }
 
+        out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
+        int dimAmountsCount = wallpaper.mUidToDimAmount.size();
+        out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
+        if (dimAmountsCount > 0) {
+            int index = 0;
+            for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
+                out.attributeInt(null, "dimUID" + index, entry.getKey());
+                out.attributeFloat(null, "dimValue" + index, entry.getValue());
+                index++;
+            }
+        }
+
         if (wallpaper.primaryColors != null) {
             int colorsCount = wallpaper.primaryColors.getMainColors().size();
             out.attributeInt(null, "colorsCount", colorsCount);
@@ -3267,6 +3410,10 @@
         return parser.getAttributeInt(null, name, defValue);
     }
 
+    private float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) {
+        return parser.getAttributeFloat(null, name, defValue);
+    }
+
     /**
      * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
      * happen during user switch.  The async user switch observer may not have received
@@ -3471,6 +3618,17 @@
         wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
         wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
         wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+        wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
+        int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
+        if (dimAmountsCount > 0) {
+            ArrayMap<Integer, Float> allDimAmounts = new ArrayMap<>(dimAmountsCount);
+            for (int i = 0; i < dimAmountsCount; i++) {
+                int uid = getAttributeInt(parser, "dimUID" + i, 0);
+                float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f);
+                allDimAmounts.put(uid, dimValue);
+            }
+            wallpaper.mUidToDimAmount = allDimAmounts;
+        }
         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
         int allColorsCount =  getAttributeInt(parser, "allColorsCount", 0);
         if (allColorsCount > 0) {
@@ -3637,6 +3795,14 @@
         return false;
     }
 
+    @Override // Binder call
+    public void onShellCommand(FileDescriptor in, FileDescriptor out,
+            FileDescriptor err, String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) {
+        new WallpaperManagerShellCommand(WallpaperManagerService.this).exec(this, in, out, err,
+                args, callback, resultReceiver);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -3664,6 +3830,13 @@
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
+                pw.print("  mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
+                pw.print("  isColorExtracted="); pw.println(wallpaper.mIsColorExtractedFromDim);
+                pw.println("  mUidToDimAmount:");
+                for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
+                    pw.print("    UID="); pw.print(entry.getKey());
+                    pw.print(" dimAmount="); pw.println(entry.getValue());
+                }
                 if (wallpaper.connection != null) {
                     WallpaperConnection conn = wallpaper.connection;
                     pw.print("  Wallpaper connection ");
@@ -3695,6 +3868,7 @@
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
+                pw.print("  mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
             }
             pw.println("Fallback wallpaper state:");
             pw.print(" User "); pw.print(mFallbackWallpaper.userId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
new file mode 100644
index 0000000..fc827b4
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.util.Log;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell Command class to run adb commands on the wallpaper service
+ */
+public class WallpaperManagerShellCommand extends ShellCommand {
+    private static final String TAG = "WallpaperManagerShellCommand";
+
+    private final WallpaperManagerService mService;
+
+    public WallpaperManagerShellCommand(WallpaperManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            onHelp();
+            return 1;
+        }
+        switch(cmd) {
+            case "set-dim-amount":
+                return setWallpaperDimAmount();
+            case "get-dim-amount":
+                return getWallpaperDimAmount();
+            case "-h":
+            case "help":
+                onHelp();
+                return 0;
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("Wallpaper manager commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println("  set-dim-amount DIMMING");
+        pw.println("    Sets the current dimming value to DIMMING (a number between 0 and 1).");
+        pw.println();
+        pw.println("  get-dim-amount");
+        pw.println("    Get the current wallpaper dim amount.");
+    }
+
+    /**
+     * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
+     * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
+     */
+    private int setWallpaperDimAmount() {
+        float dimAmount = Float.parseFloat(getNextArgRequired());
+        try {
+            mService.setWallpaperDimAmount(dimAmount);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Can't set wallpaper dim amount");
+        }
+        getOutPrintWriter().println("Dimming the wallpaper to: " + dimAmount);
+        return 0;
+    }
+
+    /**
+     * Gets the current additional dim amount set on the wallpaper. 0f means no application has
+     * added any dimming on top of the system default dim amount.
+     */
+    private int getWallpaperDimAmount() {
+        float dimAmount = mService.getWallpaperDimAmount();
+        getOutPrintWriter().println("The current wallpaper dim amount is: " + dimAmount);
+        return 0;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 30cd3c4..4df2e17 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -53,7 +53,8 @@
      */
     @IntDef(suffix = { "_ORDERED_ID" }, value = {
             FIRST_ORDERED_ID,
-            COMMUNAL_MODE_ORDERED_ID,
+            PERMISSION_POLICY_ORDERED_ID,
+            VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
             LAST_ORDERED_ID // Update this when adding new ids
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -65,15 +66,21 @@
     static final int FIRST_ORDERED_ID = 0;
 
     /**
-     * The identifier for {@link com.android.server.communal.CommunalManagerService} interceptor.
+     * The identifier for {@link com.android.server.policy.PermissionPolicyService} interceptor
      */
-    public static final int COMMUNAL_MODE_ORDERED_ID = 1;
+    public static final int PERMISSION_POLICY_ORDERED_ID = 1;
+
+    /**
+     * The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
+     * interceptor.
+     */
+    public static final int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3;
 
     /**
      * The final id, used by the framework to determine the valid range of ids. Update this when
      * adding new ids.
      */
-    static final int LAST_ORDERED_ID = COMMUNAL_MODE_ORDERED_ID;
+    static final int LAST_ORDERED_ID = VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
 
     /**
      * Data class for storing the various arguments needed for activity interception.
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 4b33f0e..a8dd856 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -11,8 +11,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
@@ -483,10 +481,6 @@
             case WINDOWING_MODE_FULLSCREEN:
                 mWindowState = WINDOW_STATE_STANDARD;
                 break;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
-                break;
             case WINDOWING_MODE_FREEFORM:
                 mWindowState = WINDOW_STATE_FREEFORM;
                 break;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0b0b704..76434c7 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1546,10 +1546,17 @@
     void onDisplayChanged(DisplayContent dc) {
         DisplayContent prevDc = mDisplayContent;
         super.onDisplayChanged(dc);
-        if (prevDc == null || prevDc == mDisplayContent) {
+        if (prevDc == mDisplayContent) {
             return;
         }
 
+        mDisplayContent.onRunningActivityChanged();
+
+        if (prevDc == null) {
+            return;
+        }
+        prevDc.onRunningActivityChanged();
+
         // TODO(b/169035022): move to a more-appropriate place.
         mTransitionController.collect(this);
         if (prevDc.mOpeningApps.remove(this)) {
@@ -1623,6 +1630,11 @@
         }
         // Trigger TaskInfoChanged to update the camera compat UI.
         getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+        // TaskOrganizerController#onTaskInfoChanged adds pending task events to the queue waiting
+        // for the surface placement to be ready. So need to trigger surface placement to dispatch
+        // events to avoid stale state for the camera compat control.
+        getDisplayContent().setLayoutNeeded();
+        mWmService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
     void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
@@ -2134,7 +2146,7 @@
                         return activityInfo != null ? activityInfo.applicationInfo : null;
                     });
 
-        final int typeParameter = mWmService.mStartingSurfaceController
+        final int typeParameter = StartingSurfaceController
                 .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
                         allowTaskSnapshot, activityCreated, useEmpty, useLegacy, activityAllDrawn);
 
@@ -2752,9 +2764,13 @@
     }
 
     boolean isResizeable() {
+        return isResizeable(/* checkPictureInPictureSupport */ true);
+    }
+
+    boolean isResizeable(boolean checkPictureInPictureSupport) {
         return mAtmService.mForceResizableActivities
                 || ActivityInfo.isResizeableMode(info.resizeMode)
-                || info.supportsPictureInPicture()
+                || (info.supportsPictureInPicture() && checkPictureInPictureSupport)
                 // If the activity can be embedded, it should inherit the bounds of task fragment.
                 || isEmbedded();
     }
@@ -3900,6 +3916,7 @@
 
         // Reset the last saved PiP snap fraction on removal.
         mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent);
+        mDisplayContent.onRunningActivityChanged();
         mWmService.mEmbeddedWindowController.onActivityRemoved(this);
         mRemovingFromDisplay = false;
     }
@@ -6616,13 +6633,20 @@
         return splashScreenThemeResId;
     }
 
+    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
+            boolean startActivity, ActivityRecord sourceRecord) {
+        showStartingWindow(prev, newTask, taskSwitch, isProcessRunning(), startActivity,
+                sourceRecord);
+    }
+
     /**
      * @param prev Previous activity which contains a starting window.
+     * @param processRunning Whether the client process is running.
      * @param startActivity Whether this activity is just created from starter.
      * @param sourceRecord The source activity which start this activity.
      */
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
-            boolean startActivity, ActivityRecord sourceRecord) {
+            boolean processRunning, boolean startActivity, ActivityRecord sourceRecord) {
         if (mTaskOverlay) {
             // We don't show starting window for overlay activities.
             return;
@@ -6647,7 +6671,7 @@
                 && task.getActivity((r) -> !r.finishing && r != this) == null;
 
         final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
-                prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
+                prev, newTask || newSingleActivity, taskSwitch, processRunning,
                 allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty, allDrawn);
         if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
             Slog.d(TAG, "Scheduled starting window for " + this);
@@ -7340,12 +7364,17 @@
 
     @VisibleForTesting
     void clearSizeCompatMode() {
+        final float lastSizeCompatScale = mSizeCompatScale;
         mInSizeCompatModeForBounds = false;
         mSizeCompatScale = 1f;
         mSizeCompatBounds = null;
         mCompatDisplayInsets = null;
+        if (mSizeCompatScale != lastSizeCompatScale) {
+            forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+        }
 
-        onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+        // Clear config override in #updateCompatDisplayInsets().
+        onRequestedOverrideConfigurationChanged(EMPTY);
     }
 
     @Override
@@ -7660,10 +7689,16 @@
             // orientation with insets applied.
             return;
         }
-        // Activity should be resizable if the task is.
+        // Not using Task#isResizeable() or ActivityRecord#isResizeable() directly because app
+        // compatibility testing showed that android:supportsPictureInPicture="true" alone is not
+        // sufficient signal for not letterboxing an app.
+        // TODO(214602463): Remove multi-window check since orientation and aspect ratio
+        // restrictions should always be applied in multi-window.
         final boolean isResizeable = task != null
-                ? task.isResizeable() || isResizeable()
-                : isResizeable();
+                // Activity should be resizable if the task is.
+                ? task.isResizeable(/* checkPictureInPictureSupport */ false)
+                        || isResizeable(/* checkPictureInPictureSupport */ false)
+                : isResizeable(/* checkPictureInPictureSupport */ false);
         if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable) {
             // Ignore orientation request for resizable apps in multi window.
             return;
@@ -7894,6 +7929,7 @@
         final int contentH = resolvedAppBounds.height();
         final int viewportW = containerAppBounds.width();
         final int viewportH = containerAppBounds.height();
+        final float lastSizeCompatScale = mSizeCompatScale;
         // Only allow to scale down.
         mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
                 ? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
@@ -7912,6 +7948,9 @@
         } else {
             mSizeCompatBounds = null;
         }
+        if (mSizeCompatScale != lastSizeCompatScale) {
+            forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+        }
 
         // Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal
         // decor if needed. Horizontal position is adjusted in
@@ -8179,8 +8218,13 @@
         final float maxAspectRatio = info.getMaxAspectRatio();
         final Task rootTask = getRootTask();
         final float minAspectRatio = getMinAspectRatio();
+        // Not using ActivityRecord#isResizeable() directly because app compatibility testing
+        // showed that android:supportsPictureInPicture="true" alone is not sufficient signal for
+        // not letterboxing an app.
+        // TODO(214602463): Remove multi-window check since orientation and aspect ratio
+        // restrictions should always be applied in multi-window.
         if (task == null || rootTask == null
-                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
+                || (inMultiWindowMode() && isResizeable(/* checkPictureInPictureSupport */ false)
                 && !fixedOrientationLetterboxed)
                 || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
                 || isInVrUiMode(getConfiguration())) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 9353f6d..316bf20 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -22,10 +22,10 @@
 
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
 import android.os.IBinder;
 import android.os.InputConstants;
 import android.os.Looper;
+import android.os.Process;
 import android.util.Slog;
 import android.view.InputChannel;
 import android.view.InputEvent;
@@ -46,7 +46,6 @@
      * Feature flag for making Activities consume all touches within their task bounds.
      */
     @ChangeId
-    @Disabled
     static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
 
     private static final String TAG = "ActivityRecordInputSink";
@@ -113,14 +112,12 @@
     }
 
     private InputWindowHandle createInputWindowHandle() {
-        InputWindowHandle inputWindowHandle = new InputWindowHandle(
-                mActivityRecord.getInputApplicationHandle(false),
+        InputWindowHandle inputWindowHandle = new InputWindowHandle(null,
                 mActivityRecord.getDisplayId());
-        inputWindowHandle.replaceTouchableRegionWithCrop(
-                mActivityRecord.getParentSurfaceControl());
+        inputWindowHandle.replaceTouchableRegionWithCrop = true;
         inputWindowHandle.name = mName;
-        inputWindowHandle.ownerUid = mActivityRecord.getUid();
-        inputWindowHandle.ownerPid = mActivityRecord.getPid();
+        inputWindowHandle.ownerUid = Process.myUid();
+        inputWindowHandle.ownerPid = Process.myPid();
         inputWindowHandle.layoutParamsFlags =
                 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index ecc8587..87fb290 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -455,6 +455,10 @@
             // Lock the loop to ensure the activities launched in a sequence.
             synchronized (mService.mGlobalLock) {
                 mService.deferWindowLayout();
+                // To avoid creating multiple starting window when creating starting multiples
+                // activities, we defer the creation of the starting window once all start request
+                // are processed
+                mService.mWindowManager.mStartingSurfaceController.beginDeferAddStartingWindow();
                 try {
                     for (int i = 0; i < starters.length; i++) {
                         final int startResult = starters[i].setResultTo(resultTo)
@@ -480,6 +484,7 @@
                         }
                     }
                 } finally {
+                    mService.mWindowManager.mStartingSurfaceController.endDeferAddStartingWindow();
                     mService.continueWindowLayout();
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 65f9e83d..e119a9a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,8 +30,6 @@
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
@@ -1926,27 +1924,6 @@
 
     private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
             Task targetTask) {
-        final Task sourceRootTask = mSourceRootTask != null ? mSourceRootTask
-                : mRootWindowContainer.getTopDisplayFocusedRootTask();
-        if (sourceRootTask != null && sourceRootTask.inSplitScreenWindowingMode()
-                && (mOptions == null
-                        || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) {
-            int windowingMode =
-                    targetTask != null ? targetTask.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-            if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
-                if (sourceRootTask.inSplitScreenPrimaryWindowingMode()) {
-                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-                } else if (sourceRootTask.inSplitScreenSecondaryWindowingMode()) {
-                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-                }
-            }
-
-            if (mOptions == null) {
-                mOptions = ActivityOptions.makeBasic();
-            }
-            mOptions.setLaunchWindowingMode(windowingMode);
-        }
-
         mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
                 sourceRecord, mOptions, mRequest, PHASE_BOUNDS, mLaunchParams);
         mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index a9142ef..23508d9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -687,4 +687,7 @@
 
     /** Get the most recent task excluding the first running task (the one on the front most). */
     public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground();
+
+    /** Get the app tasks for a package */
+    public abstract List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid);
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 15ebe28..ed9dcef 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -135,6 +135,7 @@
 import android.app.IActivityClientController;
 import android.app.IActivityController;
 import android.app.IActivityTaskManager;
+import android.app.IAppTask;
 import android.app.IApplicationThread;
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
@@ -220,10 +221,10 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationRunner;
-import android.view.IRemoteAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
+import android.window.BackNavigationInfo;
 import android.window.IWindowOrganizerController;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
 import android.window.TaskSnapshot;
@@ -436,9 +437,9 @@
     private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT =
             START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * MINUTE_IN_MILLIS;
 
-    // Activity tokens of system activities that are delegating their call to
-    // #startActivityByCaller, keyed by the permissionToken granted to the delegate.
-    final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>();
+    // The component name of the delegated activities that are allowed to call
+    // #startActivityAsCaller with the one-time used permission token.
+    final HashMap<IBinder, ComponentName> mStartActivitySources = new HashMap<>();
 
     // Permission tokens that have expired, but we remember for error reporting.
     final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>();
@@ -457,7 +458,7 @@
     private final ClientLifecycleManager mLifecycleManager;
 
     @Nullable
-    private final BackGestureController mBackGestureController;
+    private final BackNavigationController mBackNavigationController;
 
     private TaskChangeNotificationController mTaskChangeNotificationController;
     /** The controller for all operations related to locktask. */
@@ -835,8 +836,6 @@
         mSystemThread = ActivityThread.currentActivityThread();
         mUiContext = mSystemThread.getSystemUiContext();
         mLifecycleManager = new ClientLifecycleManager();
-        mBackGestureController = BackGestureController.isEnabled() ? new BackGestureController()
-                : null;
         mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this);
         mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
@@ -844,6 +843,8 @@
         mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
         mTaskFragmentOrganizerController =
                 mWindowOrganizerController.mTaskFragmentOrganizerController;
+        mBackNavigationController = BackNavigationController.isEnabled()
+                ? new BackNavigationController() : null;
     }
 
     public void onSystemReady() {
@@ -1021,6 +1022,9 @@
             mLockTaskController.setWindowManager(wm);
             mTaskSupervisor.setWindowManager(wm);
             mRootWindowContainer.setWindowManager(wm);
+            if (mBackNavigationController != null) {
+                mBackNavigationController.setTaskSnapshotController(wm.mTaskSnapshotController);
+            }
         }
     }
 
@@ -1512,7 +1516,7 @@
     }
 
     @Override
-    public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
+    public IBinder requestStartActivityPermissionToken(ComponentName componentName) {
         int callingUid = Binder.getCallingUid();
         if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
             throw new SecurityException("Only the system process can request a permission token, "
@@ -1520,7 +1524,7 @@
         }
         IBinder permissionToken = new Binder();
         synchronized (mGlobalLock) {
-            mStartActivitySources.put(permissionToken, delegatorToken);
+            mStartActivitySources.put(permissionToken, componentName);
         }
 
         Message expireMsg = PooledLambda.obtainMessage(
@@ -1545,7 +1549,7 @@
         // 1)  The caller is an activity that is part of the core framework, and then only when it
         //     is running as the system.
         // 2)  The caller provides a valid permissionToken.  Permission tokens are one-time use and
-        //     can only be requested by a system activity, which may then delegate this call to
+        //     can only be requested from system uid, which may then delegate this call to
         //     another app.
         final ActivityRecord sourceRecord;
         final int targetUid;
@@ -1556,18 +1560,26 @@
             if (resultTo == null) {
                 throw new SecurityException("Must be called from an activity");
             }
-            final IBinder sourceToken;
+
+            sourceRecord = ActivityRecord.isInAnyTask(resultTo);
+            if (sourceRecord == null) {
+                throw new SecurityException("Called with bad activity token: " + resultTo);
+            }
+            if (sourceRecord.app == null) {
+                throw new SecurityException("Called without a process attached to activity");
+            }
+
+            final ComponentName componentName;
             if (permissionToken != null) {
                 // To even attempt to use a permissionToken, an app must also have this signature
                 // permission.
                 mAmInternal.enforceCallingPermission(
                         android.Manifest.permission.START_ACTIVITY_AS_CALLER,
                         "startActivityAsCaller");
-                // If called with a permissionToken, we want the sourceRecord from the delegator
-                // activity that requested this token.
-                sourceToken = mStartActivitySources.remove(permissionToken);
-                if (sourceToken == null) {
-                    // Invalid permissionToken, check if it recently expired.
+                // If called with a permissionToken, the caller must be the same component that
+                // was allowed to use the permissionToken.
+                componentName = mStartActivitySources.remove(permissionToken);
+                if (!sourceRecord.mActivityComponent.equals(componentName)) {
                     if (mExpiredStartAsCallerTokens.contains(permissionToken)) {
                         throw new SecurityException("Called with expired permission token: "
                                 + permissionToken);
@@ -1577,33 +1589,21 @@
                     }
                 }
             } else {
-                // This method was called directly by the source.
-                sourceToken = resultTo;
-            }
-
-            sourceRecord = ActivityRecord.isInAnyTask(sourceToken);
-            if (sourceRecord == null) {
-                throw new SecurityException("Called with bad activity token: " + sourceToken);
-            }
-            if (sourceRecord.app == null) {
-                throw new SecurityException("Called without a process attached to activity");
-            }
-
-            // Whether called directly or from a delegate, the source activity must be from the
-            // android package.
-            if (!sourceRecord.info.packageName.equals("android")) {
-                throw new SecurityException("Must be called from an activity that is "
-                        + "declared in the android package");
-            }
-
-            if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
-                // This is still okay, as long as this activity is running under the
-                // uid of the original calling activity.
-                if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
-                    throw new SecurityException(
-                            "Calling activity in uid " + sourceRecord.app.mUid
-                                    + " must be system uid or original calling uid "
-                                    + sourceRecord.launchedFromUid);
+                // Whether called directly or from a delegate, the source activity must be from the
+                // android package.
+                if (!sourceRecord.info.packageName.equals("android")) {
+                    throw new SecurityException("Must be called from an activity that is "
+                            + "declared in the android package");
+                }
+                if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
+                    // This is still okay, as long as this activity is running under the
+                    // uid of the original calling activity.
+                    if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
+                        throw new SecurityException(
+                                "Calling activity in uid " + sourceRecord.app.mUid
+                                        + " must be system uid or original calling uid "
+                                        + sourceRecord.launchedFromUid);
+                    }
                 }
             }
             if (ignoreTargetSecurity) {
@@ -1771,11 +1771,13 @@
     }
 
     @Override
-    public void startBackPreview(IRemoteAnimationRunner runner) {
-        if (mBackGestureController == null) {
-            return;
+    public BackNavigationInfo startBackNavigation() {
+        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+                "startBackNavigation()");
+        if (mBackNavigationController == null) {
+            return null;
         }
-        mBackGestureController.startBackPreview();
+        return mBackNavigationController.startBackNavigation(getTopDisplayFocusedRootTask());
     }
 
     /**
@@ -2558,12 +2560,15 @@
 
     @Override
     public List<IBinder> getAppTasks(String callingPackage) {
-        int callingUid = Binder.getCallingUid();
         assertPackageMatchesCallingUid(callingPackage);
+        return getAppTasks(callingPackage, Binder.getCallingUid());
+    }
+
+    private List<IBinder> getAppTasks(String pkgName, int uid) {
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mRecentTasks.getAppTasksList(callingUid, callingPackage);
+                return mRecentTasks.getAppTasksList(uid, pkgName);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -6668,5 +6673,16 @@
             }
             return targetTask;
         }
+
+        @Override
+        public List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid) {
+            ArrayList<ActivityManager.AppTask> tasks = new ArrayList<>();
+            List<IBinder> appTasks = ActivityTaskManagerService.this.getAppTasks(pkgName, uid);
+            int numAppTasks = appTasks.size();
+            for (int i = 0; i < numAppTasks; i++) {
+                tasks.add(new ActivityManager.AppTask(IAppTask.Stub.asInterface(appTasks.get(i))));
+            }
+            return tasks;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3cecce2..dd394ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -36,7 +36,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -364,6 +363,17 @@
     /** Check if placing task or activity on specified display is allowed. */
     boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
             ActivityInfo activityInfo) {
+        return canPlaceEntityOnDisplay(displayId, callingPid, callingUid, null /* task */,
+                activityInfo);
+    }
+
+    boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid, Task task) {
+        return canPlaceEntityOnDisplay(displayId, callingPid, callingUid, task,
+                null /* activityInfo */);
+    }
+
+    private boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+            Task task, ActivityInfo activityInfo) {
         if (displayId == DEFAULT_DISPLAY) {
             // No restrictions for the default display.
             return true;
@@ -372,12 +382,31 @@
             // Can't launch on secondary displays if feature is not supported.
             return false;
         }
+
         if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
             // Can't place activities to a display that has restricted launch rules.
             // In this case the request should be made by explicitly adding target display id and
             // by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
             return false;
         }
+
+        final DisplayContent displayContent =
+                mRootWindowContainer.getDisplayContentOrCreate(displayId);
+        if (displayContent != null && displayContent.mDwpcHelper.hasController()) {
+            final ArrayList<ActivityInfo> activities = new ArrayList<>();
+            if (activityInfo != null) {
+                activities.add(activityInfo);
+            }
+            if (task != null) {
+                task.forAllActivities((r) -> {
+                    activities.add(r.info);
+                });
+            }
+            if (!displayContent.mDwpcHelper.canContainActivities(activities)) {
+                return false;
+            }
+        }
+
         return true;
     }
 
@@ -2169,10 +2198,7 @@
             boolean forceNonResizable) {
         final boolean isSecondaryDisplayPreferred = preferredTaskDisplayArea != null
                 && preferredTaskDisplayArea.getDisplayId() != DEFAULT_DISPLAY;
-        final boolean inSplitScreenMode = actualRootTask != null
-                && actualRootTask.getDisplayArea().isSplitScreenModeActivated();
-        if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
-                && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
+        if (!task.isActivityTypeStandardOrUndefined()) {
             return;
         }
 
@@ -2513,10 +2539,8 @@
                                 == PERMISSION_GRANTED) {
                     mRecentTasks.setFreezeTaskListReordering();
                 }
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                        || activityOptions.getLaunchRootTask() != null) {
-                    // Don't move home activity forward if we are launching into primary split or
-                    // there is a launch root set.
+                if (activityOptions.getLaunchRootTask() != null) {
+                    // Don't move home activity forward if there is a launch root set.
                     moveHomeTaskForward = false;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 721907c..475a9fb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -799,6 +799,21 @@
     }
 
     /**
+     * Returns {@code true} if a given {@link WindowContainer} is an embedded Task in
+     * {@link com.android.wm.shell.TaskView}.
+     *
+     * Note that this is a short term workaround to support Android Auto until it migrate to
+     * ShellTransition. This should only be used by {@link #getAnimationTargets}.
+     *
+     * TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled.
+     */
+    private static boolean isTaskViewTask(WindowContainer wc) {
+        // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
+        // it is not guaranteed to work this logic in the future version.
+        return wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+    }
+
+    /**
      * Find WindowContainers to be animated from a set of opening and closing apps. We will promote
      * animation targets to higher level in the window hierarchy if possible.
      *
@@ -844,7 +859,13 @@
             siblings.add(current);
             boolean canPromote = true;
 
-            if (parent == null || !parent.canCreateRemoteAnimationTarget()
+            if (isTaskViewTask(current)) {
+                // Don't animate an embedded Task in app transition. This is a short term workaround
+                // to prevent conflict of surface hierarchy changes between legacy app transition
+                // and TaskView (b/205189147).
+                // TODO(b/213312721): Remove this once ShellTransition is enabled.
+                continue;
+            } else if (parent == null || !parent.canCreateRemoteAnimationTarget()
                     || !parent.canBeAnimationTarget()
                     // We cannot promote the animation on Task's parent when the task is in
                     // clearing task in case the animating get stuck when performing the opening
@@ -887,7 +908,13 @@
                 for (int j = 0; j < parent.getChildCount(); ++j) {
                     final WindowContainer sibling = parent.getChildAt(j);
                     if (candidates.remove(sibling)) {
-                        siblings.add(sibling);
+                        if (!isTaskViewTask(sibling)) {
+                            // Don't animate an embedded Task in app transition. This is a short
+                            // term workaround to prevent conflict of surface hierarchy changes
+                            // between legacy app transition and TaskView (b/205189147).
+                            // TODO(b/213312721): Remove this once ShellTransition is enabled.
+                            siblings.add(sibling);
+                        }
                     } else if (sibling != current && sibling.isVisible()) {
                         canPromote = false;
                     }
diff --git a/services/core/java/com/android/server/wm/BackGestureController.java b/services/core/java/com/android/server/wm/BackGestureController.java
deleted file mode 100644
index f8f6254..0000000
--- a/services/core/java/com/android/server/wm/BackGestureController.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.os.SystemProperties;
-
-/**
- * Controller to handle actions related to the back gesture on the server side.
- */
-public class BackGestureController {
-
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-
-    public static boolean isEnabled() {
-        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
-    }
-
-    /**
-     * Start a remote animation the back gesture.
-     */
-    public void startBackPreview() {
-    }
-}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
new file mode 100644
index 0000000..a8779fa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+import android.window.TaskSnapshot;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+/**
+ * Controller to handle actions related to the back gesture on the server side.
+ */
+class BackNavigationController {
+
+    private static final String TAG = "BackNavigationController";
+    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+    @Nullable
+    private TaskSnapshotController mTaskSnapshotController;
+
+    /**
+     * Returns true if the back predictability feature is enabled
+     */
+    static boolean isEnabled() {
+        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+    }
+
+    /**
+     * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
+     * back gesture animation.
+     *
+     * @param task the currently focused {@link Task}.
+     * @return a {@link BackNavigationInfo} instance containing the required leashes and metadata
+     * for the animation.
+     */
+    @Nullable
+    BackNavigationInfo startBackNavigation(@NonNull Task task) {
+        return startBackNavigation(task, null);
+    }
+
+    /**
+     * @param tx, a transaction to be used for the attaching the animation leash.
+     *            This is used in tests. If null, the object will be initialized with a new {@link
+     *            android.view.SurfaceControl.Transaction}
+     * @see #startBackNavigation(Task)
+     */
+    @VisibleForTesting
+    @Nullable
+    BackNavigationInfo startBackNavigation(@NonNull Task task,
+            @Nullable SurfaceControl.Transaction tx) {
+
+        if (tx == null) {
+            tx = new SurfaceControl.Transaction();
+        }
+
+        int backType = BackNavigationInfo.TYPE_UNDEFINED;
+        Task prevTask = task;
+        ActivityRecord prev;
+        WindowContainer<?> removedWindowContainer;
+        ActivityRecord activityRecord;
+        SurfaceControl animationLeashParent;
+        WindowConfiguration taskWindowConfiguration;
+        SurfaceControl animLeash;
+        HardwareBuffer screenshotBuffer = null;
+        int prevTaskId;
+        int prevUserId;
+
+        synchronized (task.mWmService.mGlobalLock) {
+            activityRecord = task.topRunningActivity();
+            removedWindowContainer = activityRecord;
+            taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
+
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, topRunningActivity=%s",
+                    task, activityRecord);
+
+            // IME is visible, back gesture will dismiss it, nothing to preview.
+            if (task.getDisplayContent().getImeContainer().isVisible()) {
+                return null;
+            }
+
+            // Current Activity is home, there is no previous activity to display
+            if (activityRecord.isActivityTypeHome()) {
+                return null;
+            }
+
+            prev = task.getActivity(
+                    (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity());
+
+            if (prev != null) {
+                backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
+            } else if (task.returnsToHomeRootTask()) {
+                prevTask = null;
+                removedWindowContainer = task;
+                backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+            } else if (activityRecord.isRootOfTask()) {
+                // TODO(208789724): Create single source of truth for this, maybe in
+                //  RootWindowContainer
+                // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic
+                prevTask = task.mRootWindowContainer.getTaskBelow(task);
+                removedWindowContainer = task;
+                if (prevTask.isActivityTypeHome()) {
+                    backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+                } else {
+                    prev = prevTask.getTopNonFinishingActivity();
+                    backType = BackNavigationInfo.TYPE_CROSS_TASK;
+                }
+            }
+
+            prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
+            prevUserId = prevTask != null ? prevTask.mUserId : 0;
+
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s",
+                    prev != null ? prev.mActivityComponent : null);
+
+            //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
+            // implemented. For now we simply have the mBackScreenshots hash map that dumbly
+            // saves the screenshots.
+            if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) {
+                screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent);
+            }
+
+            // Prepare a leash to animate the current top window
+            animLeash = removedWindowContainer.makeAnimationLeash()
+                    .setName("BackPreview Leash")
+                    .setHidden(false)
+                    .setBLASTLayer()
+                    .build();
+            removedWindowContainer.reparentSurfaceControl(tx, animLeash);
+
+            animationLeashParent = removedWindowContainer.getAnimationLeashParent();
+        }
+
+        SurfaceControl.Builder builder = new SurfaceControl.Builder()
+                .setName("BackPreview Screenshot")
+                .setParent(animationLeashParent)
+                .setHidden(false)
+                .setBLASTLayer();
+        SurfaceControl screenshotSurface = builder.build();
+
+        // Find a screenshot of the previous activity
+
+        if (needsScreenshot(backType) && prevTask != null) {
+            if (screenshotBuffer == null) {
+                screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
+            }
+        }
+        tx.apply();
+
+        WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
+        try {
+            activityRecord.token.linkToDeath(
+                    () -> resetSurfaces(finalRemovedWindowContainer), 0);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to link to death", e);
+            resetSurfaces(removedWindowContainer);
+            return null;
+        }
+
+        return new BackNavigationInfo(backType,
+                animLeash,
+                screenshotSurface,
+                screenshotBuffer,
+                taskWindowConfiguration,
+                new RemoteCallback(result -> resetSurfaces(finalRemovedWindowContainer
+                )));
+    }
+
+
+    private HardwareBuffer getActivitySnapshot(@NonNull Task task,
+            ComponentName activityComponent) {
+        // Check if we have a screenshot of the previous activity, indexed by its
+        // component name.
+        SurfaceControl.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
+                .get(activityComponent.flattenToString());
+        return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
+
+    }
+
+    private HardwareBuffer getTaskSnapshot(int taskId, int userId) {
+        if (mTaskSnapshotController == null) {
+            return null;
+        }
+        TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(taskId,
+                userId, true /* restoreFromDisk */, false  /* isLowResolution */);
+        return snapshot != null ? snapshot.getHardwareBuffer() : null;
+    }
+
+    private boolean needsScreenshot(int backType) {
+        switch (backType) {
+            case BackNavigationInfo.TYPE_RETURN_TO_HOME:
+            case BackNavigationInfo.TYPE_DIALOG_CLOSE:
+                return false;
+        }
+        return true;
+    }
+
+    private void resetSurfaces(@NonNull WindowContainer<?> windowContainer) {
+        synchronized (windowContainer.mWmService.mGlobalLock) {
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Back: Reset surfaces");
+            SurfaceControl.Transaction tx = windowContainer.getSyncTransaction();
+            SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
+            if (surfaceControl != null) {
+                tx.reparent(surfaceControl,
+                        windowContainer.getParent().getSurfaceControl());
+                tx.apply();
+            }
+        }
+    }
+
+    void setTaskSnapshotController(@Nullable TaskSnapshotController taskSnapshotController) {
+        mTaskSnapshotController = taskSnapshotController;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 47622bc..5919806 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -24,18 +24,17 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
 import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
 
 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
 import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder;
 
+import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.text.TextUtils;
@@ -88,6 +87,9 @@
      */
     public abstract TaskDisplayArea getDefaultTaskDisplayArea();
 
+    /** Returns the {@link TaskDisplayArea} specified by launch options. */
+    public abstract TaskDisplayArea getTaskDisplayArea(@Nullable Bundle options);
+
     /** Provider for platform-default display area policy. */
     static final class DefaultProvider implements DisplayAreaPolicy.Provider {
         @Override
@@ -134,11 +136,6 @@
                         .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
                                 TYPE_NOTIFICATION_SHADE)
                         .build())
-                        .addFeature(new Feature.Builder(wmService.mPolicy,
-                                "OneHandedBackgroundPanel",
-                                FEATURE_ONE_HANDED_BACKGROUND_PANEL)
-                                .upTo(TYPE_WALLPAPER)
-                                .build())
                         .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
                                 FEATURE_ONE_HANDED)
                                 .all()
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 3d7ac6c..8e21d96 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -27,12 +27,19 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityOptions;
 import android.os.Bundle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.window.DisplayAreaOrganizer;
+import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.util.ArrayList;
@@ -43,6 +50,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 
 /**
  * A builder for instantiating a complex {@link DisplayAreaPolicy}
@@ -149,6 +157,8 @@
      **/
     @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
 
+    @Nullable private Function<Bundle, TaskDisplayArea> mSelectTaskDisplayAreaFunc;
+
     /** Defines the root hierarchy for the whole logical display. */
     DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
         mRootHierarchyBuilder = rootHierarchyBuilder;
@@ -176,14 +186,25 @@
     }
 
     /**
+     * The policy will use this function to find the {@link TaskDisplayArea}.
+     * @see DefaultSelectTaskDisplayAreaFunction as an example.
+     */
+    DisplayAreaPolicyBuilder setSelectTaskDisplayAreaFunc(
+            Function<Bundle, TaskDisplayArea> selectTaskDisplayAreaFunc) {
+        mSelectTaskDisplayAreaFunc = selectTaskDisplayAreaFunc;
+        return this;
+    }
+
+    /**
      * Makes sure the setting meets the requirement:
      * 1. {@link #mRootHierarchyBuilder} must be set.
      * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids.
      * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids.
      * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container.
      * 5. There must be exactly one {@link HierarchyBuilder} that contains the default
-     *    {@link TaskDisplayArea} with id {@link FEATURE_DEFAULT_TASK_CONTAINER}.
-     * 6. None of the ids is greater than {@link FEATURE_VENDOR_LAST}.
+     *    {@link TaskDisplayArea} with id
+     *    {@link DisplayAreaOrganizer#FEATURE_DEFAULT_TASK_CONTAINER}.
+     * 6. None of the ids is greater than {@link DisplayAreaOrganizer#FEATURE_VENDOR_LAST}.
      */
     private void validate() {
         if (mRootHierarchyBuilder == null) {
@@ -250,7 +271,7 @@
      * {@link Feature} below the same {@link RootDisplayArea} must have unique ids, but
      * {@link Feature} below different {@link RootDisplayArea} can have the same id so that we can
      * organize them together.
-     * None of the ids is greater than {@link FEATURE_VENDOR_LAST}
+     * None of the ids is greater than {@link DisplayAreaOrganizer#FEATURE_VENDOR_LAST}
      *
      * @param uniqueIdSet ids of {@link RootDisplayArea} and {@link TaskDisplayArea} that must be
      *                    unique,
@@ -323,7 +344,7 @@
                     mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
         }
         return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
-                mSelectRootForWindowFunc);
+                mSelectRootForWindowFunc, mSelectTaskDisplayAreaFunc);
     }
 
     /**
@@ -368,6 +389,51 @@
     }
 
     /**
+     * The default function to find {@link TaskDisplayArea} if there's no other function set
+     * through {@link #setSelectTaskDisplayAreaFunc(Function)}.
+     * <p>
+     * This function returns {@link TaskDisplayArea} specified by
+     * {@link ActivityOptions#getLaunchTaskDisplayArea()} if it is not {@code null}. Otherwise,
+     * returns {@link DisplayContent#getDefaultTaskDisplayArea()}.
+     * </p>
+     */
+    private static class DefaultSelectTaskDisplayAreaFunction implements
+            Function<Bundle, TaskDisplayArea> {
+        private final TaskDisplayArea mDefaultTaskDisplayArea;
+        private final int mDisplayId;
+
+        DefaultSelectTaskDisplayAreaFunction(TaskDisplayArea defaultTaskDisplayArea) {
+            mDefaultTaskDisplayArea = defaultTaskDisplayArea;
+            mDisplayId = defaultTaskDisplayArea.getDisplayId();
+        }
+
+        @Override
+        public TaskDisplayArea apply(@Nullable Bundle options) {
+            if (options == null) {
+                return mDefaultTaskDisplayArea;
+            }
+            final ActivityOptions activityOptions = new ActivityOptions(options);
+            final WindowContainerToken tdaToken = activityOptions.getLaunchTaskDisplayArea();
+            if (tdaToken == null) {
+                return mDefaultTaskDisplayArea;
+            }
+            final TaskDisplayArea tda = WindowContainer.fromBinder(tdaToken.asBinder())
+                    .asTaskDisplayArea();
+            if (tda == null) {
+                ProtoLog.w(WM_DEBUG_WINDOW_ORGANIZER, "The TaskDisplayArea with %s does not "
+                        + "exist.", tdaToken);
+                return mDefaultTaskDisplayArea;
+            }
+            if (tda.getDisplayId() != mDisplayId) {
+                throw new IllegalArgumentException("The specified TaskDisplayArea must attach "
+                        + "to Display#" + mDisplayId + ", but it is in Display#"
+                        + tda.getDisplayId());
+            }
+            return tda;
+        }
+    }
+
+    /**
      *  Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
      * {@link RootDisplayArea}
      */
@@ -722,11 +788,13 @@
     static class Result extends DisplayAreaPolicy {
         final List<RootDisplayArea> mDisplayAreaGroupRoots;
         final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
+        private final Function<Bundle, TaskDisplayArea> mSelectTaskDisplayAreaFunc;
         private final TaskDisplayArea mDefaultTaskDisplayArea;
 
         Result(WindowManagerService wmService, RootDisplayArea root,
                 List<RootDisplayArea> displayAreaGroupRoots,
-                BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
+                BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc,
+                Function<Bundle, TaskDisplayArea> selectTaskDisplayAreaFunc) {
             super(wmService, root);
             mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
             mSelectRootForWindowFunc = selectRootForWindowFunc;
@@ -740,6 +808,9 @@
                 throw new IllegalStateException(
                         "No display area with FEATURE_DEFAULT_TASK_CONTAINER");
             }
+            mSelectTaskDisplayAreaFunc = selectTaskDisplayAreaFunc != null
+                    ? selectTaskDisplayAreaFunc
+                    : new DefaultSelectTaskDisplayAreaFunction(mDefaultTaskDisplayArea);
         }
 
         @Override
@@ -796,6 +867,12 @@
         public TaskDisplayArea getDefaultTaskDisplayArea() {
             return mDefaultTaskDisplayArea;
         }
+
+        @NonNull
+        @Override
+        public TaskDisplayArea getTaskDisplayArea(@Nullable Bundle options) {
+            return mSelectTaskDisplayAreaFunc.apply(options);
+        }
     }
 
     static class PendingArea {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c740f7b..e449dde 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -122,6 +122,7 @@
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
 import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
 import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
+import static com.android.server.wm.DisplayContentProto.KEEP_CLEAR_AREAS;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
@@ -169,6 +170,7 @@
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
 import android.graphics.Insets;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -218,12 +220,10 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
-import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.DisplayWindowPolicyController;
 import android.window.IDisplayAreaOrganizer;
 import android.window.TransitionRequestInfo;
 
@@ -699,12 +699,11 @@
     boolean mDontMoveToTop;
 
     /**
-     * The policy controller of the windows that can be displayed on the virtual display.
+     * The helper of policy controller.
      *
-     * @see DisplayWindowPolicyController
+     * @see DisplayWindowPolicyControllerHelper
      */
-    @Nullable
-    DisplayWindowPolicyController mDisplayWindowPolicyController;
+    DisplayWindowPolicyControllerHelper mDwpcHelper;
 
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -841,7 +840,6 @@
             }
             w.mSurfacePlacementNeeded = true;
             w.mLayoutNeeded = false;
-            w.prelayout();
             final boolean firstLayout = !w.isLaidOut();
             getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
             w.mLayoutSeq = mLayoutSeq;
@@ -884,7 +882,6 @@
             }
             w.mSurfacePlacementNeeded = true;
             w.mLayoutNeeded = false;
-            w.prelayout();
             getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
             w.mLayoutSeq = mLayoutSeq;
             if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
@@ -1053,8 +1050,6 @@
 
         mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
         mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
-        mTransitionController.registerLegacyListener(
-                mWmService.mActivityManagerAppTransitionNotifier);
         mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
         mAppTransitionController = new AppTransitionController(mWmService, this);
         mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
@@ -1425,7 +1420,7 @@
             mWaitingForConfig = true;
             if (mTransitionController.isShellTransitionsEnabled()) {
                 requestChangeTransitionIfNeeded(changes, null /* displayChange */);
-            } else {
+            } else if (mLastHasContent) {
                 mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
             }
             sendNewConfiguration();
@@ -1702,8 +1697,7 @@
             case SOFT_INPUT_STATE_HIDDEN:
                 return false;
         }
-        return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface
-                && mInputMethodWindow.mViewVisibility == View.VISIBLE;
+        return r.mLastImeShown;
     }
 
     /** Returns {@code true} if the top activity is transformed with the new rotation of display. */
@@ -2007,6 +2001,7 @@
     }
 
     void configureDisplayPolicy() {
+        mRootWindowContainer.updateDisplayImePolicyCache();
         mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
         mDisplayRotation.configure(mBaseDisplayWidth, mBaseDisplayHeight);
     }
@@ -2724,6 +2719,7 @@
     void onDisplayChanged(DisplayContent dc) {
         super.onDisplayChanged(dc);
         updateSystemGestureExclusionLimit();
+        updateKeepClearAreas();
     }
 
     void updateSystemGestureExclusionLimit() {
@@ -2741,8 +2737,7 @@
                 mDisplayInfo.copyFrom(newDisplayInfo);
             }
 
-            mDisplayWindowPolicyController =
-                    displayManagerInternal.getDisplayWindowPolicyController(mDisplayId);
+            mDwpcHelper = new DisplayWindowPolicyControllerHelper(this);
         }
 
         updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
@@ -2783,7 +2778,10 @@
         if (displayMetricsChanged || physicalDisplayChanged) {
             if (physicalDisplayChanged) {
                 // Reapply the window settings as the underlying physical display has changed.
-                mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+                // Do not include rotation settings here, postpone them until the display
+                // metrics are updated as rotation settings might depend on them
+                mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this,
+                        /* includeRotationSettings */ false);
             }
 
             // If there is an override set for base values - use it, otherwise use new values.
@@ -3222,6 +3220,7 @@
      */
     void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes,
             @Nullable TransitionRequestInfo.DisplayChange displayChange) {
+        if (!mLastHasContent) return;
         final TransitionController controller = mTransitionController;
         if (controller.isCollecting()) {
             if (displayChange != null) {
@@ -3330,6 +3329,9 @@
             }
         }
         proto.write(IME_POLICY, getImePolicy());
+        for (Rect r : getKeepClearAreas()) {
+            r.dumpDebug(proto, KEEP_CLEAR_AREAS);
+        }
         proto.end(token);
     }
 
@@ -3389,6 +3391,13 @@
             pw.println(mSystemGestureExclusion);
         }
 
+        final List<Rect> keepClearAreas = getKeepClearAreas();
+        if (!keepClearAreas.isEmpty()) {
+            pw.println();
+            pw.print("  keepClearAreas=");
+            pw.println(keepClearAreas);
+        }
+
         pw.println();
         pw.println(prefix + "Display areas in top down Z order:");
         dumpChildDisplayArea(pw, subPrefix, dumpAll);
@@ -3451,10 +3460,7 @@
         mInputMonitor.dump(pw, "  ");
         pw.println();
         mInsetsStateController.dump(prefix, pw);
-        if (mDisplayWindowPolicyController != null) {
-            pw.println();
-            mDisplayWindowPolicyController.dump(prefix, pw);
-        }
+        mDwpcHelper.dump(prefix, pw);
     }
 
     @Override
@@ -3619,6 +3625,7 @@
         }
 
         adjustForImeIfNeeded();
+        updateKeepClearAreas();
 
         // We may need to schedule some toast windows to be removed. The toasts for an app that
         // does not have input focus are removed within a timeout to prevent apps to redress
@@ -3682,6 +3689,11 @@
         return true;
     }
 
+    /** Update the top activity and the uids of non-finishing activity */
+    void onRunningActivityChanged() {
+        mDwpcHelper.onRunningActivityChanged();
+    }
+
     /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */
     void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) {
         // Only record the TaskDisplayArea that handles orientation request.
@@ -3940,6 +3952,9 @@
         }
     }
 
+    // IMPORTANT: When introducing new dependencies in this method, make sure that
+    // changes to those result in RootWindowContainer.updateDisplayImePolicyCache()
+    // being called.
     @DisplayImePolicy int getImePolicy() {
         if (!isTrusted()) {
             return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
@@ -3988,11 +4003,12 @@
         if (target == mImeLayeringTarget) {
             return;
         }
-        // Prepare the IME screenshot for the last IME target when its task is applying app
-        // transition. This is for the better IME transition to keep IME visibility when
-        // transitioning to the next task.
+        // If the IME target is the input target, before it changes, prepare the IME screenshot
+        // for the last IME target when its task is applying app transition. This is for the
+        // better IME transition to keep IME visibility when transitioning to the next task.
         if (mImeLayeringTarget != null && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
-                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+                && mImeLayeringTarget == mImeInputTarget) {
             attachAndShowImeScreenshotOnTarget();
         }
 
@@ -4102,7 +4118,7 @@
                 "IME-snapshot-surface");
         t.setBuffer(imeSurface, buffer);
         t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
-        t.setRelativeLayer(imeSurface, activity.getSurfaceControl(), 1);
+        t.setLayer(imeSurface, 1);
         t.setPosition(imeSurface, mInputMethodWindow.getDisplayFrame().left,
                 mInputMethodWindow.getDisplayFrame().top);
         return imeSurface;
@@ -4147,9 +4163,6 @@
      * which controls the visibility and animation of the input method window.
      */
     void updateImeInputAndControlTarget(WindowState target) {
-        if (target != null && target.mActivityRecord != null) {
-            target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
-        }
         if (mImeInputTarget != target) {
             ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
             setImeInputTarget(target);
@@ -4157,6 +4170,11 @@
                     .getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
             updateImeControlTarget();
         }
+        // Unfreeze IME insets after the new target updated, in case updateAboveInsetsState may
+        // deliver unrelated IME insets change to the non-IME requester.
+        if (target != null && target.mActivityRecord != null) {
+            target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+        }
     }
 
     void updateImeControlTarget() {
@@ -4198,10 +4216,10 @@
         final SurfaceControl newParent = computeImeParent();
         if (newParent != null && newParent != mInputMethodSurfaceParent) {
             mInputMethodSurfaceParent = newParent;
-            getPendingTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent);
+            getSyncTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent);
             // When surface parent is removed, the relative layer will also be removed. We need to
             // do a force update to make sure there is a layer set for the new parent.
-            assignRelativeLayerForIme(getPendingTransaction(), true /* forceUpdate */);
+            assignRelativeLayerForIme(getSyncTransaction(), true /* forceUpdate */);
             scheduleAnimation();
         }
     }
@@ -4995,8 +5013,11 @@
         // exists so it get's layered above the starting window.
         if (imeTarget != null && !(imeTarget.mActivityRecord != null
                 && imeTarget.mActivityRecord.hasStartingWindow())) {
+            final WindowToken imeControlTargetToken =
+                    mImeControlTarget != null && mImeControlTarget.getWindow() != null
+                            ? mImeControlTarget.getWindow().mToken : null;
             final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null
-                    && imeTarget == mImeControlTarget
+                    && imeTarget.mToken == imeControlTargetToken
                     && !imeTarget.inMultiWindowMode()
                     && imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS,
                             ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null;
@@ -5087,6 +5108,11 @@
         return mLastHasContent;
     }
 
+    @VisibleForTesting
+    void setLastHasContent() {
+        mLastHasContent = true;
+    }
+
     void registerPointerEventListener(@NonNull PointerEventListener listener) {
         mPointerEventDispatcher.registerInputEventListener(listener);
     }
@@ -5468,19 +5494,28 @@
         mSystemGestureExclusionListeners.unregister(listener);
     }
 
+    void updateKeepClearAreas() {
+        mWmService.mDisplayNotificationController.dispatchKeepClearAreasChanged(
+                this, getKeepClearAreas());
+    }
+
     /**
-     * @see IWindowManager#setForwardedInsets
+     * Returns all keep-clear areas from visible windows on this display.
      */
-    public void setForwardedInsets(Insets insets) {
-        if (insets == null) {
-            insets = Insets.NONE;
-        }
-        if (mDisplayPolicy.getForwardedInsets().equals(insets)) {
-            return;
-        }
-        mDisplayPolicy.setForwardedInsets(insets);
-        setLayoutNeeded();
-        mWmService.mWindowPlacerLocked.requestTraversal();
+    ArrayList<Rect> getKeepClearAreas() {
+        final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>();
+        final Matrix tmpMatrix = new Matrix();
+        final float[] tmpFloat9 = new float[9];
+        forAllWindows(w -> {
+            if (w.isVisible() && !w.inPinnedWindowingMode()) {
+                keepClearAreas.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
+            }
+
+            // We stop traversing when we reach the base of a fullscreen app.
+            return w.getWindowType() == TYPE_BASE_APPLICATION
+                    && w.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+        }, true);
+        return keepClearAreas;
     }
 
     protected MetricsLogger getMetricsLogger() {
@@ -6457,9 +6492,8 @@
 
     DisplayArea findAreaForWindowType(int windowType, Bundle options,
             boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
-        // TODO(b/159767464): figure out how to find an appropriate TDA.
         if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
-            return getDefaultTaskDisplayArea();
+            return mDisplayAreaPolicy.getTaskDisplayArea(options);
         }
         // Return IME container here because it could be in one of sub RootDisplayAreas depending on
         // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 49a51d5..1888554 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.Display.TYPE_INTERNAL;
 import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
@@ -306,10 +305,10 @@
     private WindowState mRoundedCornerWindow;
 
     /**
-     * Windows to determine the color of status bar. See {@link #mNavBarColorWindowCandidate} for
-     * the conditions of being candidate window.
+     * A collection of {@link AppearanceRegion} to indicate that which region of status bar applies
+     * which appearance.
      */
-    private final ArrayList<WindowState> mStatusBarColorWindows = new ArrayList<>();
+    private final ArrayList<AppearanceRegion> mStatusBarAppearanceRegionList = new ArrayList<>();
 
     /**
      * Windows to determine opacity and background of translucent status bar. The window needs to be
@@ -324,7 +323,7 @@
     private InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
     private AppearanceRegion[] mLastStatusBarAppearanceRegions;
 
-    /** The union of checked bounds while fetching {@link #mStatusBarColorWindows}. */
+    /** The union of checked bounds while building {@link #mStatusBarAppearanceRegionList}. */
     private final Rect mStatusBarColorCheckedBounds = new Rect();
 
     /** The union of checked bounds while fetching {@link #mStatusBarBackgroundWindows}. */
@@ -337,6 +336,7 @@
     private long mPendingPanicGestureUptime;
 
     private static final Rect sTmpRect = new Rect();
+    private static final Rect sTmpRect2 = new Rect();
     private static final Rect sTmpLastParentFrame = new Rect();
     private static final Rect sTmpDisplayCutoutSafe = new Rect();
     private static final Rect sTmpDisplayFrame = new Rect();
@@ -360,16 +360,6 @@
 
     private int mDisplayCutoutTouchableRegionSize;
 
-    /**
-     * The area covered by system windows which belong to another display. Forwarded insets is set
-     * in case this is a virtual display, this is displayed on another display that has insets, and
-     * the bounds of this display is overlapping with the insets of the host display (e.g. IME is
-     * displayed on the host display, and it covers a part of this virtual display.)
-     * The forwarded insets is used to compute display frames of this virtual display, which will
-     * be then used to layout windows in the virtual display.
-     */
-    @NonNull private Insets mForwardedInsets = Insets.NONE;
-
     private RefreshRatePolicy mRefreshRatePolicy;
 
     /**
@@ -405,7 +395,7 @@
                         WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
                                 ? getStatusBar() : getNavigationBar();
                         if (targetBar != null) {
-                            requestTransientBars(targetBar);
+                            requestTransientBars(targetBar, true /* isGestureOnSystemBar */);
                         }
                     }
                     break;
@@ -448,26 +438,25 @@
         // TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context.
         mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
                 new SystemGesturesPointerEventListener.Callbacks() {
+
                     @Override
                     public void onSwipeFromTop() {
                         synchronized (mLock) {
-                            if (mStatusBar != null) {
-                                requestTransientBars(mStatusBar);
-                            }
-                            checkAltBarSwipeForTransientBars(ALT_BAR_TOP,
-                                    false /* allowForAllPositions */);
+                            final WindowState bar = mStatusBar != null
+                                    ? mStatusBar
+                                    : findAltBarMatchingPosition(ALT_BAR_TOP);
+                            requestTransientBars(bar, true /* isGestureOnSystemBar */);
                         }
                     }
 
                     @Override
                     public void onSwipeFromBottom() {
                         synchronized (mLock) {
-                            if (mNavigationBar != null
-                                    && mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                                requestTransientBars(mNavigationBar);
-                            }
-                            checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM,
-                                    false /* allowForAllPositions */);
+                            final WindowState bar = mNavigationBar != null
+                                        && mNavigationBarPosition == NAV_BAR_BOTTOM
+                                    ? mNavigationBar
+                                    : findAltBarMatchingPosition(ALT_BAR_BOTTOM);
+                            requestTransientBars(bar, true /* isGestureOnSystemBar */);
                         }
                     }
 
@@ -477,13 +466,8 @@
                         synchronized (mLock) {
                             mDisplayContent.calculateSystemGestureExclusion(
                                     excludedRegion, null /* outUnrestricted */);
-                            final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
-                                    !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
-                            if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_RIGHT
-                                    || allowSideSwipe)) {
-                                requestTransientBars(mNavigationBar);
-                            }
-                            checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT, allowSideSwipe);
+                            requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_RIGHT,
+                                    ALT_BAR_RIGHT);
                         }
                         excludedRegion.recycle();
                     }
@@ -494,17 +478,33 @@
                         synchronized (mLock) {
                             mDisplayContent.calculateSystemGestureExclusion(
                                     excludedRegion, null /* outUnrestricted */);
-                            final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
-                                    !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
-                            if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_LEFT
-                                    || allowSideSwipe)) {
-                                requestTransientBars(mNavigationBar);
-                            }
-                            checkAltBarSwipeForTransientBars(ALT_BAR_LEFT, allowSideSwipe);
+                            requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_LEFT,
+                                    ALT_BAR_LEFT);
                         }
                         excludedRegion.recycle();
                     }
 
+                    private void requestTransientBarsForSideSwipe(Region excludedRegion,
+                            int navBarSide, int altBarSide) {
+                        final WindowState barMatchingSide = mNavigationBar != null
+                                        && mNavigationBarPosition == navBarSide
+                                ? mNavigationBar
+                                : findAltBarMatchingPosition(altBarSide);
+                        final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
+                                !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
+                        if (barMatchingSide == null && !allowSideSwipe) {
+                            return;
+                        }
+
+                        // Request transient bars on the matching bar, or any bar if we always allow
+                        // side swipes to show the bars
+                        final boolean isGestureOnSystemBar = barMatchingSide != null;
+                        final WindowState bar = barMatchingSide != null
+                                ? barMatchingSide
+                                : findTransientNavOrAltBar();
+                        requestTransientBars(bar, isGestureOnSystemBar);
+                    }
+
                     @Override
                     public void onFling(int duration) {
                         if (mService.mPowerManagerInternal != null) {
@@ -654,21 +654,39 @@
         mHandler.post(mGestureNavigationSettingsObserver::register);
     }
 
-    private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos,
-            boolean allowForAllPositions) {
-        if (mStatusBarAlt != null && (mStatusBarAltPosition == pos || allowForAllPositions)) {
-            requestTransientBars(mStatusBarAlt);
+    /**
+     * Returns the first non-null alt bar window matching the given position.
+     */
+    private WindowState findAltBarMatchingPosition(@WindowManagerPolicy.AltBarPosition int pos) {
+        if (mStatusBarAlt != null && mStatusBarAltPosition == pos) {
+            return mStatusBarAlt;
         }
-        if (mNavigationBarAlt != null
-                && (mNavigationBarAltPosition == pos || allowForAllPositions)) {
-            requestTransientBars(mNavigationBarAlt);
+        if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
+            return mNavigationBarAlt;
         }
-        if (mClimateBarAlt != null && (mClimateBarAltPosition == pos || allowForAllPositions)) {
-            requestTransientBars(mClimateBarAlt);
+        if (mClimateBarAlt != null && mClimateBarAltPosition == pos) {
+            return mClimateBarAlt;
         }
-        if (mExtraNavBarAlt != null && (mExtraNavBarAltPosition == pos || allowForAllPositions)) {
-            requestTransientBars(mExtraNavBarAlt);
+        if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) {
+            return mExtraNavBarAlt;
         }
+        return null;
+    }
+
+    /**
+     * Finds the first non-null nav bar to request transient for.
+     */
+    private WindowState findTransientNavOrAltBar() {
+        if (mNavigationBar != null) {
+            return mNavigationBar;
+        }
+        if (mNavigationBarAlt != null) {
+            return mNavigationBarAlt;
+        }
+        if (mExtraNavBarAlt != null) {
+            return mExtraNavBarAlt;
+        }
+        return null;
     }
 
     void systemReady() {
@@ -1094,6 +1112,7 @@
                             if (!mNavButtonForcedVisible) {
                                 inOutFrame.inset(windowState.getLayoutingAttrs(
                                         displayFrames.mRotation).providedInternalInsets);
+                                inOutFrame.inset(win.mGivenContentInsets);
                             }
                         },
 
@@ -1162,9 +1181,12 @@
                                 break;
                         }
                         mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
-                                windowState, inOutFrame) -> inOutFrame.inset(
-                                windowState.getLayoutingAttrs(displayFrames.mRotation)
-                                        .providedInternalInsets), imeFrameProvider);
+                                windowState, inOutFrame) -> {
+                            inOutFrame.inset(
+                                    windowState.getLayoutingAttrs(displayFrames.mRotation)
+                                            .providedInternalInsets);
+                            inOutFrame.inset(win.mGivenContentInsets);
+                        }, imeFrameProvider);
                         mInsetsSourceWindowsExceptIme.add(win);
                     }
                 }
@@ -1411,33 +1433,6 @@
         return mForceShowSystemBars;
     }
 
-    // TODO: Should probably be moved into DisplayFrames.
-    /**
-     * Return the layout hints for a newly added window. These values are computed on the
-     * most recent layout, so they are not guaranteed to be correct.
-     *
-     * @param attrs The LayoutParams of the window.
-     * @param windowToken The token of the window.
-     * @param outInsetsState The insets state of this display from the client's perspective.
-     * @param localClient Whether the client is from the our process.
-     * @return Whether to always consume the system bars.
-     *         See {@link #areSystemBarsForcedShownLw()}.
-     */
-    boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
-            boolean localClient) {
-        final InsetsState state =
-                mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
-        final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken);
-        outInsetsState.set(state, hasCompatScale || localClient);
-        if (hasCompatScale) {
-            final float compatScale = windowToken != null
-                    ? windowToken.getSizeCompatScale()
-                    : mDisplayContent.mCompatibleScreenScale;
-            outInsetsState.scale(1f / compatScale);
-        }
-        return mForceShowSystemBars;
-    }
-
     /**
      * Computes the frames of display (its logical size, rotation and cutout should already be set)
      * used to layout window. This method only changes the given display frames, insets state and
@@ -1532,7 +1527,7 @@
         mTopFullscreenOpaqueWindowState = null;
         mNavBarColorWindowCandidate = null;
         mNavBarBackgroundWindow = null;
-        mStatusBarColorWindows.clear();
+        mStatusBarAppearanceRegionList.clear();
         mStatusBarBackgroundWindows.clear();
         mStatusBarColorCheckedBounds.setEmpty();
         mStatusBarBackgroundCheckedBounds.setEmpty();
@@ -1612,7 +1607,9 @@
                 mStatusBarBackgroundWindows.add(win);
                 mStatusBarBackgroundCheckedBounds.union(sTmpRect);
                 if (!mStatusBarColorCheckedBounds.contains(sTmpRect)) {
-                    mStatusBarColorWindows.add(win);
+                    mStatusBarAppearanceRegionList.add(new AppearanceRegion(
+                            win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
+                            new Rect(win.getFrame())));
                     mStatusBarColorCheckedBounds.union(sTmpRect);
                 }
             }
@@ -1632,13 +1629,10 @@
                 }
             }
         } else if (win.isDimming()) {
-            // For dimming window whose host bounds is overlapping with system bars, it can be
-            // used to determine colors but not opacity of system bars.
-            if (mStatusBar != null
-                    && sTmpRect.setIntersect(win.getBounds(), mStatusBar.getFrame())
-                    && !mStatusBarColorCheckedBounds.contains(sTmpRect)) {
-                mStatusBarColorWindows.add(win);
-                mStatusBarColorCheckedBounds.union(sTmpRect);
+            if (mStatusBar != null) {
+                addStatusBarAppearanceRegionsForDimmingWindow(
+                        win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
+                        mStatusBar.getFrame(), win.getBounds(), win.getFrame());
             }
             if (isOverlappingWithNavBar && mNavBarColorWindowCandidate == null) {
                 mNavBarColorWindowCandidate = win;
@@ -1646,6 +1640,48 @@
         }
     }
 
+    private void addStatusBarAppearanceRegionsForDimmingWindow(int appearance, Rect statusBarFrame,
+            Rect winBounds, Rect winFrame) {
+        if (!sTmpRect.setIntersect(winBounds, statusBarFrame)) {
+            return;
+        }
+        if (mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+            return;
+        }
+        if (appearance == 0 || !sTmpRect2.setIntersect(winFrame, statusBarFrame)) {
+            mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(winBounds)));
+            mStatusBarColorCheckedBounds.union(sTmpRect);
+            return;
+        }
+        // A dimming window can divide status bar into different appearance regions (up to 3).
+        // +---------+-------------+---------+
+        // |/////////|             |/////////| <-- Status Bar
+        // +---------+-------------+---------+
+        // |/////////|             |/////////|
+        // |/////////|             |/////////|
+        // |/////////|             |/////////|
+        // |/////////|             |/////////|
+        // |/////////|             |/////////|
+        // +---------+-------------+---------+
+        //      ^           ^           ^
+        //  dim layer     window    dim layer
+        mStatusBarAppearanceRegionList.add(new AppearanceRegion(appearance, new Rect(winFrame)));
+        if (!sTmpRect.equals(sTmpRect2)) {
+            if (sTmpRect.height() == sTmpRect2.height()) {
+                if (sTmpRect.left != sTmpRect2.left) {
+                    mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(
+                            winBounds.left, winBounds.top, sTmpRect2.left, winBounds.bottom)));
+                }
+                if (sTmpRect.right != sTmpRect2.right) {
+                    mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(
+                            sTmpRect2.right, winBounds.top, winBounds.right, winBounds.bottom)));
+                }
+            }
+            // We don't have vertical status bar yet, so we don't handle the other orientation.
+        }
+        mStatusBarColorCheckedBounds.union(sTmpRect);
+    }
+
     /**
      * Called following layout of all windows and after policy has been applied
      * to each window. If in this function you do
@@ -1687,8 +1723,7 @@
                 // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
                 // requests to hide the status bar.  Not sure if there is another way that to be the
                 // case though.
-                if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
-                        .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+                if (!topIsFullscreen) {
                     topAppHidesStatusBar = false;
                 }
             }
@@ -2119,18 +2154,6 @@
         }
     }
 
-    /**
-     * @see IWindowManager#setForwardedInsets
-     */
-    public void setForwardedInsets(@NonNull Insets forwardedInsets) {
-        mForwardedInsets = forwardedInsets;
-    }
-
-    @NonNull
-    public Insets getForwardedInsets() {
-        return mForwardedInsets;
-    }
-
     @NavigationBarPosition
     int navigationBarPosition(int displayRotation) {
         if (mNavigationBar != null) {
@@ -2170,8 +2193,8 @@
         updateSystemBarAttributes();
     }
 
-    private void requestTransientBars(WindowState swipeTarget) {
-        if (!mService.mPolicy.isUserSetupComplete()) {
+    private void requestTransientBars(WindowState swipeTarget, boolean isGestureOnSystemBar) {
+        if (swipeTarget == null || !mService.mPolicy.isUserSetupComplete()) {
             // Swipe-up for navigation bar is disabled during setup
             return;
         }
@@ -2207,7 +2230,8 @@
 
         if (controlTarget.canShowTransient()) {
             // Show transient bars if they are hidden; restore position if they are visible.
-            mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
+            mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE,
+                    isGestureOnSystemBar);
             controlTarget.showInsets(restorePositionTypes, false);
         } else {
             // Restore visibilities and positions of system bars.
@@ -2288,14 +2312,9 @@
         final String focusedApp = win.mAttrs.packageName;
         final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
                 || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
-        final AppearanceRegion[] appearanceRegions =
-                new AppearanceRegion[mStatusBarColorWindows.size()];
-        for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
-            final WindowState windowState = mStatusBarColorWindows.get(i);
-            appearanceRegions[i] = new AppearanceRegion(
-                    getStatusBarAppearance(windowState, windowState),
-                    new Rect(windowState.getFrame()));
-        }
+        final AppearanceRegion[] statusBarAppearanceRegions =
+                new AppearanceRegion[mStatusBarAppearanceRegionList.size()];
+        mStatusBarAppearanceRegionList.toArray(statusBarAppearanceRegions);
         if (mLastDisableFlags != disableFlags) {
             mLastDisableFlags = disableFlags;
             final String cause = win.toString();
@@ -2307,7 +2326,7 @@
                 && mRequestedVisibilities.equals(win.getRequestedVisibilities())
                 && Objects.equals(mFocusedApp, focusedApp)
                 && mLastFocusIsFullscreen == isFullscreen
-                && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
+                && Arrays.equals(mLastStatusBarAppearanceRegions, statusBarAppearanceRegions)) {
             return;
         }
         if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
@@ -2322,20 +2341,12 @@
         mRequestedVisibilities = requestedVisibilities;
         mFocusedApp = focusedApp;
         mLastFocusIsFullscreen = isFullscreen;
-        mLastStatusBarAppearanceRegions = appearanceRegions;
+        mLastStatusBarAppearanceRegions = statusBarAppearanceRegions;
         callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
-                appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
+                appearance, statusBarAppearanceRegions, isNavbarColorManagedByIme, behavior,
                 requestedVisibilities, focusedApp));
     }
 
-    private int getStatusBarAppearance(WindowState opaque, WindowState opaqueOrDimming) {
-        final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded();
-        final WindowState colorWin = onKeyguard ? mNotificationShade : opaqueOrDimming;
-        return isLightBarAllowed(colorWin, Type.statusBars()) && (colorWin == opaque || onKeyguard)
-                ? (colorWin.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS)
-                : 0;
-    }
-
     private void callStatusBarSafely(Consumer<StatusBarManagerInternal> consumer) {
         mHandler.post(() -> {
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -2394,8 +2405,7 @@
     private int updateSystemBarsLw(WindowState win, int disableFlags) {
         final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
         final boolean multiWindowTaskVisible =
-                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
-                        || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
+                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
         final boolean freeformRootTaskVisible =
                 defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
 
@@ -2423,7 +2433,8 @@
             // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
             mPendingPanicGestureUptime = 0;
             if (!isNavBarEmpty(disableFlags)) {
-                mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC);
+                mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC,
+                        true /* isGestureOnSystemBar */);
             }
         }
 
@@ -2729,11 +2740,10 @@
             pw.print(prefix); pw.print("mNavBarBackgroundWindow=");
             pw.println(mNavBarBackgroundWindow);
         }
-        if (!mStatusBarColorWindows.isEmpty()) {
-            pw.print(prefix); pw.println("mStatusBarColorWindows=");
-            for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
-                final WindowState win = mStatusBarColorWindows.get(i);
-                pw.print(prefixInner); pw.println(win);
+        if (mLastStatusBarAppearanceRegions != null) {
+            pw.print(prefix); pw.println("mLastStatusBarAppearanceRegions=");
+            for (int i = mLastStatusBarAppearanceRegions.length - 1; i >= 0; i--) {
+                pw.print(prefixInner);  pw.println(mLastStatusBarAppearanceRegions[i]);
             }
         }
         if (!mStatusBarBackgroundWindows.isEmpty()) {
@@ -2820,7 +2830,9 @@
     }
 
     void release() {
+        mDisplayContent.mTransitionController.unregisterLegacyListener(mAppTransitionListener);
         mHandler.post(mGestureNavigationSettingsObserver::unregister);
+        mImmersiveModeConfirmation.release();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index 4141090..276dbe9 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -17,11 +17,14 @@
 package com.android.server.wm;
 
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.IntArray;
 import android.view.IDisplayWindowListener;
 
+import java.util.List;
+
 /**
  * Manages dispatch of relevant hierarchy changes to interested listeners. Listeners are assumed
  * to be remote.
@@ -116,4 +119,16 @@
         }
         mDisplayListeners.finishBroadcast();
     }
+
+    void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> keepClearAreas) {
+        int count = mDisplayListeners.beginBroadcast();
+        for (int i = 0; i < count; ++i) {
+            try {
+                mDisplayListeners.getBroadcastItem(i).onKeepClearAreasChanged(
+                        display.mDisplayId, keepClearAreas);
+            } catch (RemoteException e) {
+            }
+        }
+        mDisplayListeners.finishBroadcast();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
new file mode 100644
index 0000000..60d2a5d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.window.DisplayWindowPolicyController;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+class DisplayWindowPolicyControllerHelper {
+
+    private final DisplayContent mDisplayContent;
+
+    /**
+     * The policy controller of the windows that can be displayed on the virtual display.
+     *
+     * @see DisplayWindowPolicyController
+     */
+    @Nullable
+    private DisplayWindowPolicyController mDisplayWindowPolicyController;
+
+    /**
+     * The top non-finishing activity of this display.
+     */
+    private ActivityRecord mTopRunningActivity = null;
+
+    /**
+     * All the uids of non-finishing activity on this display.
+     * @see DisplayWindowPolicyController#onRunningAppsChanged(ArraySet)
+     */
+    private ArraySet<Integer> mRunningUid = new ArraySet<>();
+
+    DisplayWindowPolicyControllerHelper(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
+        mDisplayWindowPolicyController = mDisplayContent.mWmService.mDisplayManagerInternal
+                .getDisplayWindowPolicyController(mDisplayContent.mDisplayId);
+    }
+
+    /**
+     * Return {@code true} if there is DisplayWindowPolicyController.
+     */
+    public boolean hasController() {
+        return mDisplayWindowPolicyController != null;
+    }
+
+    /**
+     * @see DisplayWindowPolicyController#canContainActivities(List)
+     */
+    public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+        if (mDisplayWindowPolicyController == null) {
+            return true;
+        }
+        return mDisplayWindowPolicyController.canContainActivities(activities);
+    }
+
+    /**
+     * @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int)
+     */
+    boolean keepActivityOnWindowFlagsChanged(ActivityInfo aInfo, int flagChanges,
+            int privateFlagChanges) {
+        if (mDisplayWindowPolicyController == null) {
+            return true;
+        }
+
+        if (!mDisplayWindowPolicyController.isInterestedWindowFlags(
+                flagChanges, privateFlagChanges)) {
+            return true;
+        }
+
+        return mDisplayWindowPolicyController.keepActivityOnWindowFlagsChanged(
+                aInfo, flagChanges, privateFlagChanges);
+    }
+
+    /** Update the top activity and the uids of non-finishing activity */
+    void onRunningActivityChanged() {
+        if (mDisplayWindowPolicyController == null) {
+            return;
+        }
+
+        // Update top activity.
+        ActivityRecord topActivity = mDisplayContent.getTopActivity(false /* includeFinishing */,
+                true /* includeOverlays */);
+        if (topActivity != mTopRunningActivity) {
+            mTopRunningActivity = topActivity;
+            mDisplayWindowPolicyController.onTopActivityChanged(
+                    topActivity == null ? null : topActivity.info.getComponentName(),
+                    topActivity == null
+                            ? UserHandle.USER_NULL : topActivity.info.applicationInfo.uid);
+        }
+
+        // Update running uid.
+        final boolean[] notifyChanged = {false};
+        ArraySet<Integer> runningUids = new ArraySet<>();
+        mDisplayContent.forAllActivities((r) -> {
+            if (!r.finishing) {
+                notifyChanged[0] |= runningUids.add(r.getUid());
+            }
+        });
+
+        // We need to compare the size because if it is the following case, we can't know the
+        // existence of 3 in the forAllActivities() loop.
+        // Old set: 1,2,3
+        // New set: 1,2
+        if (notifyChanged[0] || (mRunningUid.size() != runningUids.size())) {
+            mRunningUid = runningUids;
+            mDisplayWindowPolicyController.onRunningAppsChanged(runningUids);
+        }
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        if (mDisplayWindowPolicyController != null) {
+            pw.println();
+            mDisplayWindowPolicyController.dump(prefix, pw);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 8260fd6..483c799 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -244,6 +244,10 @@
     }
 
     void applySettingsToDisplayLocked(DisplayContent dc) {
+        applySettingsToDisplayLocked(dc, /* includeRotationSettings */ true);
+    }
+
+    void applySettingsToDisplayLocked(DisplayContent dc, boolean includeRotationSettings) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
         final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
 
@@ -282,6 +286,8 @@
         boolean dontMoveToTop = settings.mDontMoveToTop != null
                 ? settings.mDontMoveToTop : false;
         dc.mDontMoveToTop = dontMoveToTop;
+
+        if (includeRotationSettings) applyRotationSettingsToDisplayLocked(dc);
     }
 
     void applyRotationSettingsToDisplayLocked(DisplayContent dc) {
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index fc317a1..0e2d847 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -93,6 +93,18 @@
         return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
     }
 
+    boolean isOverlay(IBinder inputToken) {
+        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+        return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
+    }
+
+    void setIsOverlay(IBinder inputToken) {
+        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+        if (embeddedWindow != null) {
+            embeddedWindow.setIsOverlay();
+        }
+    }
+
     void remove(IWindow client) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
             if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) {
@@ -138,6 +150,12 @@
         public Session mSession;
         InputChannel mInputChannel;
         final int mWindowType;
+        // Track whether the EmbeddedWindow is a system hosted overlay via
+        // {@link OverlayHost}. In the case of client hosted overlays, the client
+        // view hierarchy will take care of invoking requestEmbeddedWindowFocus
+        // but for system hosted overlays we have to do this via tapOutsideDetection
+        // and this variable is mostly used for tracking that.
+        boolean mIsOverlay = false;
 
         /**
          * @param session  calling session to check ownership of the window
@@ -216,5 +234,39 @@
         public int getPid() {
             return mOwnerPid;
         }
+
+        void setIsOverlay() {
+            mIsOverlay = true;
+        }
+        boolean getIsOverlay() {
+            return mIsOverlay;
+        }
+
+        /**
+         * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
+         * so we need to participate inside handlePointerDownOutsideFocus logic
+         * however client hosted overlays will rely on the hosting view hierarchy
+         * to grant and revoke focus, and so the server side logic is not needed.
+         */
+        @Override
+        public boolean receiveFocusFromTapOutside() {
+            return mIsOverlay;
+        }
+
+        private void handleTap(boolean grantFocus) {
+            if (mInputChannel != null) {
+                mWmService.grantEmbeddedWindowFocus(mSession, mInputChannel.getToken(), grantFocus);
+            }
+        }
+
+        @Override
+        public void handleTapOutsideFocusOutsideSelf() {
+            handleTap(false);
+        }
+
+        @Override
+        public void handleTapOutsideFocusInsideSelf() {
+            handleTap(true);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 963f326..8d3e071 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -97,7 +97,7 @@
         // activities are actually behind other fullscreen activities, but still required
         // to be visible (such as performing Recents animation).
         final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
-                && mTaskFragment.isTopActivityFocusable()
+                && mTaskFragment.canBeResumed(starting)
                 && (starting == null || !starting.isDescendantOf(mTaskFragment));
 
         ArrayList<TaskFragment> adjacentTaskFragments = null;
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index c85e04d..2cefd99 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -256,7 +256,7 @@
                     false /* applyFixedTransformationHint */);
             for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
                 final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
-                if (leash != null) {
+                if (leash != null && leash.isValid()) {
                     rotator.applyTransform(t, leash);
                 }
             }
@@ -265,7 +265,7 @@
         // Hide the windows immediately because a screenshot layer should cover the screen.
         for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
             final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
-            if (leash != null) {
+            if (leash != null && leash.isValid()) {
                 t.setAlpha(leash, 0f);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index f3b9cdf..93bdf16 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -149,6 +149,11 @@
         }
     }
 
+    void release() {
+        mHandler.removeMessages(H.SHOW);
+        mHandler.removeMessages(H.HIDE);
+    }
+
     boolean onSettingChanged(int currentUserId) {
         final boolean changed = loadSetting(currentUserId, mContext);
         // Remove the window if the setting changes to be confirmed.
@@ -204,7 +209,12 @@
         if (mClingWindow != null) {
             if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
             // We don't care which root display area the window manager is specifying for removal.
-            getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
+            try {
+                getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
+            } catch (WindowManager.InvalidDisplayException e) {
+                Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e);
+                return;
+            }
             mClingWindow = null;
         }
     }
@@ -432,7 +442,11 @@
 
         // show the confirmation
         WindowManager.LayoutParams lp = getClingWindowLayoutParams();
-        getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+        try {
+            getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+        } catch (WindowManager.InvalidDisplayException e) {
+            Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e);
+        }
     }
 
     private final Runnable mConfirm = new Runnable() {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index e02e7c5..f91969b 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -24,6 +24,7 @@
 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
 
 import android.annotation.NonNull;
+import android.graphics.PointF;
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Slog;
@@ -219,6 +220,11 @@
     }
 
     @Override
+    public PointF getCursorPosition() {
+        return mService.getLatestMousePosition();
+    }
+
+    @Override
     public void onPointerDownOutsideFocus(IBinder touchedToken) {
         mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4225f21..44818a8 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -399,10 +399,11 @@
         if (recentsAnimationInputConsumer != null && focus != null) {
             final RecentsAnimationController recentsAnimationController =
                     mService.getRecentsAnimationController();
+            // Apply recents input consumer when the focusing window is in recents animation.
             final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null
                     && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord))
                     // Shell transitions doesn't use RecentsAnimationController
-                    || getWeak(mActiveRecentsActivity) != null;
+                    || getWeak(mActiveRecentsActivity) != null && focus.inTransition();
             if (shouldApplyRecentsInputConsumer) {
                 requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
                         recentsAnimationInputConsumer.mName);
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index c7d328a..5166b8a 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -36,5 +36,16 @@
 
     /* Owning pid of the target. */
     int getPid();
+
+    /**
+     * Indicates whether a target should receive focus from server side
+     * tap outside focus detection. For example, this is false in the case of
+     * EmbeddedWindows in a client view hierarchy, where the client will do internal
+     * tap detection and invoke grantEmbeddedWindowFocus itself
+     */
+    boolean receiveFocusFromTapOutside();
+
+    void handleTapOutsideFocusInsideSelf();
+    void handleTapOutsideFocusOutsideSelf();
 }
 
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index dff7ff9..9326a2e 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -160,7 +160,7 @@
         return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
     }
 
-    void showTransient(@InternalInsetsType int[] types) {
+    void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
         boolean changed = false;
         for (int i = types.length - 1; i >= 0; i--) {
             final @InternalInsetsType int type = types[i];
@@ -177,8 +177,8 @@
             StatusBarManagerInternal statusBarManagerInternal =
                     mPolicy.getStatusBarManagerInternal();
             if (statusBarManagerInternal != null) {
-                statusBarManagerInternal.showTransient(
-                        mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+                statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(),
+                        mShowingTransientTypes.toArray(), isGestureOnSystemBar);
             }
             updateBarControlTarget(mFocusedWin);
 
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index e33c440..a1468cc 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -187,7 +187,6 @@
 
             // Navigation bar doesn't get influenced by anything else
             if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
-                state.removeSource(ITYPE_IME);
                 state.removeSource(ITYPE_STATUS_BAR);
                 state.removeSource(ITYPE_CLIMATE_BAR);
                 state.removeSource(ITYPE_CAPTION_BAR);
@@ -367,6 +366,7 @@
         if (changed) {
             notifyInsetsChanged();
             mDisplayContent.updateSystemGestureExclusion();
+            mDisplayContent.updateKeepClearAreas();
             mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
         }
     }
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index cbb473c..1955e30 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -175,7 +175,7 @@
      * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
      * both it and a value of {@link
      * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
-     * and corners of the activity won't be rounded.
+     * corners of the activity won't be rounded.
      */
     void setLetterboxActivityCornersRadius(int cornersRadius) {
         mLetterboxActivityCornersRadius = cornersRadius;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 7d07357..8866343 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -150,7 +150,7 @@
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
                         mActivityRecord.mWmService.mTransactionFactory,
-                        mLetterboxConfiguration::isLetterboxActivityCornersRounded,
+                        this::shouldLetterboxHaveRoundedCorners,
                         this::getLetterboxBackgroundColor,
                         this::hasWallpaperBackgroudForLetterbox,
                         this::getLetterboxWallpaperBlurRadius,
@@ -175,6 +175,13 @@
         }
     }
 
+    private boolean shouldLetterboxHaveRoundedCorners() {
+        // TODO(b/214030873): remove once background is drawn for transparent activities
+        // Letterbox shouldn't have rounded corners if the activity is transparent
+        return mLetterboxConfiguration.isLetterboxActivityCornersRounded()
+                && mActivityRecord.fillsParent();
+    }
+
     float getHorizontalPositionMultiplier(Configuration parentConfiguration) {
         // Don't check resolved configuration because it may not be updated yet during
         // configuration change.
@@ -257,8 +264,6 @@
     @VisibleForTesting
     boolean shouldShowLetterboxUi(WindowState mainWindow) {
         return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
-                // Check that an activity isn't transparent.
-                && mActivityRecord.fillsParent()
                 // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
                 // WindowContainer#showWallpaper because the later will return true when this
                 // activity is using blurred wallpaper for letterbox backgroud.
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/OverlayHost.java
new file mode 100644
index 0000000..724e124
--- /dev/null
+++ b/services/core/java/com/android/server/wm/OverlayHost.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.content.res.Configuration;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to assist WindowContainer in the hosting of
+ * SurfacePackage based overlays. Manages overlays inside
+ * one parent control, and manages the lifetime of that parent control
+ * in order to obscure details from WindowContainer.
+ *
+ * Also handles multiplexing of event dispatch and tracking of overlays
+ * to make things easier for WindowContainer.
+ */
+class OverlayHost {
+    // Lazily initialized when required
+    SurfaceControl mSurfaceControl;
+    final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>();
+    final WindowManagerService mWmService;
+
+    OverlayHost(WindowManagerService wms) {
+        mWmService = wms;
+    }
+
+    void requireOverlaySurfaceControl() {
+        if (mSurfaceControl == null) {
+            final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
+                .setContainerLayer()
+                .setHidden(true)
+                .setName("Overlay Host Leash");
+
+            mSurfaceControl = b.build();
+        }
+    }
+
+    void setParent(SurfaceControl.Transaction t, SurfaceControl newParent) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+        t.reparent(mSurfaceControl, newParent);
+        if (newParent != null) {
+            t.show(mSurfaceControl);
+        } else {
+            t.hide(mSurfaceControl);
+        }
+    }
+
+    void setLayer(SurfaceControl.Transaction t, int layer) {
+        if (mSurfaceControl != null) {
+            t.setLayer(mSurfaceControl, layer);
+        }
+    }
+
+    void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
+        requireOverlaySurfaceControl();
+        mOverlays.add(p);
+
+        mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken());
+
+        SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+        t.reparent(p.getSurfaceControl(), mSurfaceControl)
+            .show(p.getSurfaceControl());
+        setParent(t,currentParent);
+        t.apply();
+    }
+
+    boolean removeOverlay(SurfaceControlViewHost.SurfacePackage p) {
+        final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+           if (l.getSurfaceControl().isSameSurface(p.getSurfaceControl())) {
+               mOverlays.remove(i);
+               t.reparent(l.getSurfaceControl(), null);
+               l.release();
+           }
+        }
+        t.apply();
+        return mOverlays.size() > 0;
+    }
+
+    void dispatchConfigurationChanged(Configuration c) {
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+           try {
+               l.getRemoteInterface().onConfigurationChanged(c);
+           } catch (Exception e) {
+               removeOverlay(l);
+           }
+        }
+    }
+
+    private void dispatchDetachedFromWindow() {
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+            SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+            try {
+                l.getRemoteInterface().onDispatchDetachedFromWindow();
+            } catch (Exception e) {
+                // Oh well we are tearing down anyway.
+            }
+            l.release();
+        }
+    }
+
+    void release() {
+        dispatchDetachedFromWindow();
+        mOverlays.clear();
+        final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+        t.remove(mSurfaceControl).apply();
+        mSurfaceControl = null;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index dca0bbd..a049d65 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -27,7 +27,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
@@ -1370,16 +1369,6 @@
         switch (task.getWindowingMode()) {
             case WINDOWING_MODE_PINNED:
                 return false;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (DEBUG_RECENTS_TRIM_TASKS) {
-                    Slog.d(TAG, "\ttop=" + task.getRootTask().getTopMostTask());
-                }
-                final Task rootTask = task.getRootTask();
-                if (rootTask != null && rootTask.getTopMostTask() == task) {
-                    // Only the non-top task of the primary split screen mode is visible
-                    return false;
-                }
-                break;
             case WINDOWING_MODE_MULTI_WINDOW:
                 // Ignore tasks that are always on top
                 if (task.isAlwaysOnTopWhenVisible()) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 535bbb7..d031bec 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -165,6 +165,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -983,32 +984,10 @@
             mWmService.checkDrawnWindowsLocked();
         }
 
-        final int N = mWmService.mPendingRemove.size();
-        if (N > 0) {
-            if (mWmService.mPendingRemoveTmp.length < N) {
-                mWmService.mPendingRemoveTmp = new WindowState[N + 10];
-            }
-            mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
-            mWmService.mPendingRemove.clear();
-            ArrayList<DisplayContent> displayList = new ArrayList();
-            for (i = 0; i < N; i++) {
-                final WindowState w = mWmService.mPendingRemoveTmp[i];
-                w.removeImmediately();
-                final DisplayContent displayContent = w.getDisplayContent();
-                if (displayContent != null && !displayList.contains(displayContent)) {
-                    displayList.add(displayContent);
-                }
-            }
-
-            for (int j = displayList.size() - 1; j >= 0; --j) {
-                final DisplayContent dc = displayList.get(j);
-                dc.assignWindowLayers(true /*setLayoutNeeded*/);
-            }
-        }
-
         forAllDisplays(dc -> {
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             dc.updateSystemGestureExclusion();
+            dc.updateKeepClearAreas();
             dc.updateTouchExcludeRegion();
         });
 
@@ -2051,11 +2030,22 @@
 
         try {
             final Task task = r.getTask();
-            final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
+
+            // Create a transition now to collect the current pinned Task dismiss. Only do the
+            // create here as the Task (trigger) to enter PIP is not ready yet.
+            final TransitionController transitionController = task.mTransitionController;
+            Transition newTransition = null;
+            if (transitionController.isCollecting()) {
+                transitionController.setReady(task, false /* ready */);
+            } else if (transitionController.getTransitionPlayer() != null) {
+                newTransition = transitionController.createTransition(TRANSIT_PIP);
+            }
 
             // This will change the root pinned task's windowing mode to its original mode, ensuring
             // we only have one root task that is in pinned mode.
+            final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
             if (rootPinnedTask != null) {
+                transitionController.collect(rootPinnedTask);
                 rootPinnedTask.dismissPip();
             }
 
@@ -2131,7 +2121,13 @@
                 // display area, so reparent.
                 rootTask.reparent(taskDisplayArea, true /* onTop */);
             }
-            rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
+
+            // The new PIP Task is ready, start the transition before updating the windowing mode.
+            if (newTransition != null) {
+                transitionController.requestStartTransition(newTransition, rootTask,
+                        null /* remoteTransition */, null /* displayChange */);
+            }
+            transitionController.collect(rootTask);
 
             // Defer the windowing mode change until after the transition to prevent the activity
             // from doing work and changing the activity visuals while animating
@@ -2536,9 +2532,16 @@
             // Drop any cached DisplayInfos associated with this display id - the values are now
             // out of date given this display changed event.
             mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
+            updateDisplayImePolicyCache();
         }
     }
 
+    void updateDisplayImePolicyCache() {
+        ArrayMap<Integer, Integer> displayImePolicyMap = new ArrayMap<>();
+        forAllDisplays(dc -> displayImePolicyMap.put(dc.getDisplayId(), dc.getImePolicy()));
+        mWmService.mDisplayImePolicyCache = Collections.unmodifiableMap(displayImePolicyMap);
+    }
+
     /** Update lists of UIDs that are present on displays and have access to them. */
     void updateUIDsPresentOnDisplay() {
         mDisplayAccessUIDs.clear();
@@ -3671,7 +3674,8 @@
 
             try {
                 if (mTaskSupervisor.realStartActivityLocked(r, mApp,
-                        mTop == r && r.isFocusable() /* andResume */, true /* checkConfig */)) {
+                        mTop == r && r.getTask().canBeResumed(r) /* andResume */,
+                        true /* checkConfig */)) {
                     mHasActivityStarted = true;
                 }
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 005544b..7acc0c5 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -73,6 +73,7 @@
 import android.view.SurfaceSession;
 import android.view.WindowManager;
 import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -490,6 +491,16 @@
         }
     }
 
+    @Override
+    public void reportKeepClearAreasChanged(IWindow window, List<Rect> keepClearAreas) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mService.reportKeepClearAreasChanged(this, window, keepClearAreas);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private void actionOnWallpaper(IBinder window,
             BiConsumer<WallpaperController, WindowState> action) {
         final WindowState windowState = mService.windowForClientLocked(this, window, true);
@@ -863,4 +874,10 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
+
+    @Override
+    public void setOnBackInvokedCallback(IWindow iWindow,
+            IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException {
+        // TODO: Set the callback to the WindowState of the window.
+    }
 }
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 5fe4076..eb73cd8 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -34,6 +34,7 @@
 import android.util.Slog;
 import android.window.TaskSnapshot;
 
+import java.util.ArrayList;
 import java.util.function.Supplier;
 
 /**
@@ -45,6 +46,14 @@
     private final WindowManagerService mService;
     private final SplashScreenExceptionList mSplashScreenExceptionsList;
 
+    // Cache status while deferring add starting window
+    boolean mInitProcessRunning;
+    boolean mInitNewTask;
+    boolean mInitTaskSwitch;
+    private final ArrayList<DeferringStartingWindowRecord> mDeferringAddStartActivities =
+            new ArrayList<>();
+    private boolean mDeferringAddStartingWindow;
+
     public StartingSurfaceController(WindowManagerService wm) {
         mService = wm;
         mSplashScreenExceptionsList = new SplashScreenExceptionList(wm.mContext.getMainExecutor());
@@ -70,7 +79,7 @@
         return mSplashScreenExceptionsList.isException(packageName, targetSdk, infoProvider);
     }
 
-    int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
+    static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
             boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
             boolean useEmpty, boolean useLegacy, boolean activityDrawn) {
         int parameter = 0;
@@ -142,6 +151,82 @@
         }
     }
 
+    private static final class DeferringStartingWindowRecord {
+        final ActivityRecord mDeferring;
+        final ActivityRecord mPrev;
+        final ActivityRecord mSource;
+
+        DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev,
+                ActivityRecord source) {
+            mDeferring = deferring;
+            mPrev = prev;
+            mSource = source;
+        }
+    }
+
+    /**
+     * Shows a starting window while starting a new activity. Do not use this method to create a
+     * starting window for an existing activity.
+     */
+    void showStartingWindow(ActivityRecord target, ActivityRecord prev,
+            boolean newTask, boolean isTaskSwitch, ActivityRecord source) {
+        if (mDeferringAddStartingWindow) {
+            addDeferringRecord(target, prev, newTask, isTaskSwitch, source);
+        } else {
+            target.showStartingWindow(prev, newTask, isTaskSwitch, true /* startActivity */,
+                    source);
+        }
+    }
+
+    /**
+     * Queueing the starting activity status while deferring add starting window.
+     * @see Task#startActivityLocked
+     */
+    private void addDeferringRecord(ActivityRecord deferring, ActivityRecord prev,
+            boolean newTask, boolean isTaskSwitch, ActivityRecord source) {
+        // Set newTask, taskSwitch, processRunning form first activity because those can change
+        // after first activity started.
+        if (mDeferringAddStartActivities.isEmpty()) {
+            mInitProcessRunning = deferring.isProcessRunning();
+            mInitNewTask = newTask;
+            mInitTaskSwitch = isTaskSwitch;
+        }
+        mDeferringAddStartActivities.add(new DeferringStartingWindowRecord(
+                deferring, prev, source));
+    }
+
+    private void showStartingWindowFromDeferringActivities() {
+        // Attempt to add starting window from the top-most activity.
+        for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) {
+            final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i);
+            next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch,
+                    mInitProcessRunning, true /* startActivity */, next.mSource);
+            // If one succeeds, it is done.
+            if (next.mDeferring.mStartingData != null) {
+                break;
+            }
+        }
+        mDeferringAddStartActivities.clear();
+    }
+
+    /**
+     * Begin deferring add starting window in one pass.
+     * This is used to deferring add starting window while starting multiples activities because
+     * system only need to provide a starting window to the top-visible activity.
+     * Most call {@link #endDeferAddStartingWindow} when starting activities process finished.
+     * @see #endDeferAddStartingWindow()
+     */
+    void beginDeferAddStartingWindow() {
+        mDeferringAddStartingWindow = true;
+    }
+
+    /**
+     * End deferring add starting window.
+     */
+    void endDeferAddStartingWindow() {
+        mDeferringAddStartingWindow = false;
+        showStartingWindowFromDeferringActivities();
+    }
 
     final class StartingSurface {
         private final Task mTask;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fad87e8..2331dc4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -30,7 +30,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
@@ -125,6 +124,8 @@
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
 import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
@@ -478,7 +479,6 @@
     // to layout without loading all the task snapshots
     final PersistedTaskSnapshotData mLastTaskSnapshotData;
 
-    private Dimmer mDimmer = new Dimmer(this);
     private final Rect mTmpDimBoundsRect = new Rect();
 
     /** @see #setCanAffectSystemUiFlags */
@@ -610,6 +610,8 @@
      */
     ActivityRecord mChildPipActivity;
 
+    boolean mLastSurfaceShowing = true;
+
     private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
             Intent _affinityIntent, String _affinity, String _rootAffinity,
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -926,13 +928,6 @@
             if (!animate) {
                 mTaskSupervisor.mNoAnimActivities.add(topActivity);
             }
-
-            if (toRootTaskWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    && moveRootTaskMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT) {
-                // Move recents to front so it is not behind root home task when going into docked
-                // mode
-                mTaskSupervisor.moveRecentsRootTaskToFront(reason);
-            }
         } finally {
             mAtmService.continueWindowLayout();
         }
@@ -1750,7 +1745,7 @@
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
         return mTaskSupervisor.canPlaceEntityOnDisplay(displayId,
-                -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
+                -1 /* don't check PID */, -1 /* don't check UID */, this);
     }
 
     /**
@@ -2327,8 +2322,7 @@
 
         final int windowingMode = getWindowingMode();
         if (!isActivityTypeStandardOrUndefined()
-                || windowingMode == WINDOWING_MODE_FULLSCREEN
-                || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
+                || windowingMode == WINDOWING_MODE_FULLSCREEN) {
             return isResizeable() ? rootTask.getRequestedOverrideBounds() : null;
         } else if (!getWindowConfiguration().persistTaskBounds()) {
             return rootTask.getRequestedOverrideBounds();
@@ -2713,10 +2707,14 @@
     }
 
     boolean isResizeable() {
+        return isResizeable(/* checkPictureInPictureSupport */ true);
+    }
+
+    boolean isResizeable(boolean checkPictureInPictureSupport) {
         final boolean forceResizable = mAtmService.mForceResizableActivities
                 && getActivityType() == ACTIVITY_TYPE_STANDARD;
         return forceResizable || ActivityInfo.isResizeableMode(mResizeMode)
-                || mSupportsPictureInPicture;
+                || (mSupportsPictureInPicture && checkPictureInPictureSupport);
     }
 
     /**
@@ -3309,6 +3307,17 @@
         if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
             scheduleAnimation();
         }
+
+        // We intend to let organizer manage task visibility but it doesn't
+        // have enough information until we finish shell transitions.
+        // In the mean time we do an easy fix here.
+        final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS);
+        if (mSurfaceControl != null) {
+            if (show != mLastSurfaceShowing) {
+                getSyncTransaction().setVisibility(mSurfaceControl, show);
+            }
+        }
+        mLastSurfaceShowing = show;
     }
 
     @Override
@@ -4624,25 +4633,7 @@
     }
 
     void moveToFront(String reason, Task task) {
-        if (inSplitScreenSecondaryWindowingMode()) {
-            // If the root task is in split-screen secondary mode, we need to make sure we move the
-            // primary split-screen root task forward in the case it is currently behind a
-            // fullscreen root task so both halves of the split-screen appear on-top and the
-            // fullscreen root task isn't cutting between them.
-            // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
-            final TaskDisplayArea taskDisplayArea = getDisplayArea();
-            final Task topFullScreenRootTask =
-                    taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            if (topFullScreenRootTask != null) {
-                final Task primarySplitScreenRootTask =
-                        taskDisplayArea.getRootSplitScreenPrimaryTask();
-                if (primarySplitScreenRootTask != null
-                        && topFullScreenRootTask.compareTo(primarySplitScreenRootTask) > 0) {
-                    primarySplitScreenRootTask.moveToFrontInner(reason + " splitScreenToTop",
-                            null /* task */);
-                }
-            }
-        } else if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {
+        if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {
             final Task adjacentTask = getAdjacentTaskFragment().asTask();
             if (adjacentTask != null) {
                 adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */);
@@ -4994,8 +4985,7 @@
             if (topFragment == f) {
                 return;
             }
-            if (!f.isFocusableAndVisible()) {
-                // No need to resume activity in TaskFragment that is not visible.
+            if (!f.canBeResumed(null /* starting */)) {
                 return;
             }
             resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
@@ -5151,8 +5141,8 @@
 
                 final ActivityRecord prev = baseTask.getActivity(
                         a -> a.mStartingData != null && a.showToCurrentUser());
-                r.showStartingWindow(prev, newTask, isTaskSwitch,
-                        true /* startActivity */, sourceRecord);
+                mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
+                        isTaskSwitch, sourceRecord);
             }
         } else {
             // If this is the first activity, don't do any fancy animations,
@@ -5973,7 +5963,19 @@
     }
 
     void reparent(TaskDisplayArea newParent, boolean onTop) {
-        reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        if (newParent == null) {
+            throw new IllegalArgumentException("Task can't reparent to null " + this);
+        }
+
+        if (getParent() == newParent) {
+            throw new IllegalArgumentException("Task=" + this + " already child of " + newParent);
+        }
+
+        if (canBeLaunchedOnDisplay(newParent.getDisplayId())) {
+            reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        } else {
+            Slog.w(TAG, "Task=" + this + " can't reparent to " + newParent);
+        }
     }
 
     void setLastRecentsAnimationTransaction(@NonNull PictureInPictureSurfaceTransaction transaction,
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 796a90a..dfb559f 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -38,7 +38,6 @@
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
 import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
-import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -1407,9 +1406,7 @@
 
             leafTask.forAllLeafTaskFragments((taskFrag) -> {
                 final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
-                if (resumedActivity != null
-                        && (taskFrag.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE
-                        || !taskFrag.isTopActivityFocusable())) {
+                if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
                     if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
                         someActivityPaused[0]++;
                     }
diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
new file mode 100644
index 0000000..d9dc9aa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.window.IOnFpsCallbackListener;
+
+import java.util.HashMap;
+
+final class TaskFpsCallbackController {
+
+    private final Context mContext;
+    private final HashMap<IOnFpsCallbackListener, Long> mTaskFpsCallbackListeners;
+    private final HashMap<IOnFpsCallbackListener, IBinder.DeathRecipient> mDeathRecipients;
+
+    TaskFpsCallbackController(Context context) {
+        mContext = context;
+        mTaskFpsCallbackListeners = new HashMap<>();
+        mDeathRecipients = new HashMap<>();
+    }
+
+    void registerCallback(int taskId, IOnFpsCallbackListener listener) {
+        if (mTaskFpsCallbackListeners.containsKey(listener)) {
+            return;
+        }
+
+        final long nativeListener = nativeRegister(listener, taskId);
+        mTaskFpsCallbackListeners.put(listener, nativeListener);
+
+        final IBinder.DeathRecipient deathRecipient = () -> unregisterCallback(listener);
+        try {
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDeathRecipients.put(listener, deathRecipient);
+        } catch (RemoteException e) {
+            // ignore
+        }
+    }
+
+    void unregisterCallback(IOnFpsCallbackListener listener) {
+        if (!mTaskFpsCallbackListeners.containsKey(listener)) {
+            return;
+        }
+
+        listener.asBinder().unlinkToDeath(mDeathRecipients.get(listener), 0);
+        mDeathRecipients.remove(listener);
+
+        nativeUnregister(mTaskFpsCallbackListeners.get(listener));
+        mTaskFpsCallbackListeners.remove(listener);
+    }
+
+    private static native long nativeRegister(IOnFpsCallbackListener listener, int taskId);
+    private static native void nativeUnregister(long ptr);
+}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 97cb512..177d2e6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -24,8 +24,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -39,6 +37,7 @@
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
@@ -100,6 +99,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
@@ -161,6 +161,8 @@
      */
     int mMinHeight;
 
+    Dimmer mDimmer = new Dimmer(this);
+
     /** This task fragment will be removed when the cleanup of its children are done. */
     private boolean mIsRemovalRequested;
 
@@ -252,6 +254,10 @@
     private final Rect mTmpStableBounds = new Rect();
     private final Rect mTmpNonDecorBounds = new Rect();
 
+    //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
+    // implemented
+    HashMap<String, SurfaceControl.ScreenshotHardwareBuffer> mBackScreenshots = new HashMap<>();
+
     private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
             new EnsureActivitiesVisibleHelper(this);
     private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
@@ -396,8 +402,16 @@
             Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
                     + mResumedActivity + " to:" + r + " reason:" + reason);
         }
+        final ActivityRecord prevR = mResumedActivity;
         mResumedActivity = r;
         mTaskSupervisor.updateTopResumedActivityIfNeeded();
+        if (r == null && prevR.mDisplayContent != null
+                && prevR.mDisplayContent.getFocusedRootTask() == null) {
+            // Only need to notify DWPC when no activity will resume.
+            prevR.mDisplayContent.onRunningActivityChanged();
+        } else if (r != null) {
+            r.mDisplayContent.onRunningActivityChanged();
+        }
     }
 
     @VisibleForTesting
@@ -780,13 +794,8 @@
             return TASK_FRAGMENT_VISIBILITY_VISIBLE;
         }
 
-        boolean gotRootSplitScreenFragment = false;
-        boolean gotOpaqueSplitScreenPrimary = false;
-        boolean gotOpaqueSplitScreenSecondary = false;
         boolean gotTranslucentFullscreen = false;
         boolean gotTranslucentAdjacent = false;
-        boolean gotTranslucentSplitScreenPrimary = false;
-        boolean gotTranslucentSplitScreenSecondary = false;
         boolean shouldBeVisible = true;
 
         // This TaskFragment is only considered visible if all its parent TaskFragments are
@@ -805,8 +814,6 @@
         }
 
         final List<TaskFragment> adjacentTaskFragments = new ArrayList<>();
-        final int windowingMode = getWindowingMode();
-        final boolean isAssistantType = isActivityTypeAssistant();
         for (int i = parent.getChildCount() - 1; i >= 0; --i) {
             final WindowContainer other = parent.getChildAt(i);
             if (other == null) continue;
@@ -854,37 +861,6 @@
                 }
                 // Multi-window TaskFragment that matches parent bounds would occlude other children
                 return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    && !gotOpaqueSplitScreenPrimary) {
-                gotRootSplitScreenFragment = true;
-                gotTranslucentSplitScreenPrimary = isTranslucent(other, starting);
-                gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                        && gotOpaqueSplitScreenPrimary) {
-                    // Can't be visible behind another opaque TaskFragment in split-screen-primary.
-                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-                }
-            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    && !gotOpaqueSplitScreenSecondary) {
-                gotRootSplitScreenFragment = true;
-                gotTranslucentSplitScreenSecondary = isTranslucent(other, starting);
-                gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        && gotOpaqueSplitScreenSecondary) {
-                    // Can't be visible behind another opaque TaskFragment in split-screen-secondary
-                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-                }
-            }
-            if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
-                // Can not be visible if we are in split-screen windowing mode and both halves of
-                // the screen are opaque.
-                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-            }
-            if (isAssistantType && gotRootSplitScreenFragment) {
-                // Assistant TaskFragment can't be visible behind split-screen. In addition to
-                // this not making sense, it also works around an issue here we boost the z-order
-                // of the assistant window surfaces in window manager whenever it is visible.
-                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
             }
 
             final TaskFragment otherTaskFrag = other.asTaskFragment();
@@ -910,34 +886,6 @@
             return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
         }
 
-        // Handle cases when there can be a translucent split-screen TaskFragment on top.
-        switch (windowingMode) {
-            case WINDOWING_MODE_FULLSCREEN:
-                if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
-                    // At least one of the split-screen TaskFragment that covers this one is
-                    // translucent.
-                    // When in split mode, home will be reparented to the secondary split while
-                    // leaving TaskFragments not supporting split below. Due to
-                    // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
-                    // the bottom, this makes sure TaskFragments not in split roots won't occlude
-                    // home task unexpectedly.
-                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-                }
-                break;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (gotTranslucentSplitScreenPrimary) {
-                    // Covered by translucent primary split-screen on top.
-                    return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
-                }
-                break;
-            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                if (gotTranslucentSplitScreenSecondary) {
-                    // Covered by translucent secondary split-screen on top.
-                    return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
-                }
-                break;
-        }
-
         // Lastly - check if there is a translucent fullscreen TaskFragment on top.
         return gotTranslucentFullscreen
                 ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
@@ -1355,6 +1303,17 @@
         return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
     }
 
+    /**
+     * Returns {@code true} is the activity in this TaskFragment can be resumed.
+     *
+     * @param starting The currently starting activity or {@code null} if there is none.
+     */
+    boolean canBeResumed(@Nullable ActivityRecord starting) {
+        // No need to resume activity in TaskFragment that is not visible.
+        return isTopActivityFocusable()
+                && getVisibility(starting) == TASK_FRAGMENT_VISIBILITY_VISIBLE;
+    }
+
     boolean isFocusableAndVisible() {
         return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
     }
@@ -1662,6 +1621,7 @@
 
     @Override
     void addChild(WindowContainer child, int index) {
+        ActivityRecord r = topRunningActivity();
         mClearedTaskForReuse = false;
 
         boolean isAddingActivity = child.asActivityRecord() != null;
@@ -1676,6 +1636,18 @@
         super.addChild(child, index);
 
         if (isAddingActivity && task != null) {
+
+            // TODO(b/207481538): temporary per-activity screenshoting
+            if (r != null && BackNavigationController.isEnabled()) {
+                ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
+                        r.mActivityComponent.flattenToString());
+                Rect outBounds = r.getBounds();
+                SurfaceControl.ScreenshotHardwareBuffer backBuffer = SurfaceControl.captureLayers(
+                        r.mSurfaceControl,
+                        new Rect(0, 0, outBounds.width(), outBounds.height()),
+                        1f);
+                mBackScreenshots.put(r.mActivityComponent.flattenToString(), backBuffer);
+            }
             child.asActivityRecord().inHistory = true;
             task.onDescendantActivityAdded(taskHadActivity, activityType, child.asActivityRecord());
         }
@@ -2269,6 +2241,14 @@
 
     void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
         super.removeChild(child);
+        if (BackNavigationController.isEnabled()) {
+            //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
+            // implemented
+            ActivityRecord r = child.asActivityRecord();
+            if (r != null) {
+                mBackScreenshots.remove(r.mActivityComponent.flattenToString());
+            }
+        }
         if (removeSelfIfPossible && (!mCreatedByOrganizer || mIsRemovalRequested) && !hasChild()) {
             removeImmediately("removeLastChild " + child);
         }
@@ -2338,6 +2318,34 @@
     }
 
     @Override
+    Dimmer getDimmer() {
+        // If the window is in an embedded TaskFragment, we want to dim at the TaskFragment.
+        if (asTask() == null) {
+            return mDimmer;
+        }
+
+        return super.getDimmer();
+    }
+
+    @Override
+    void prepareSurfaces() {
+        if (asTask() != null) {
+            super.prepareSurfaces();
+            return;
+        }
+
+        mDimmer.resetDimStates();
+        super.prepareSurfaces();
+
+        // Bounds need to be relative, as the dim layer is a child.
+        final Rect dimBounds = getBounds();
+        dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+        if (mDimmer.updateDims(getPendingTransaction(), dimBounds)) {
+            scheduleAnimation();
+        }
+    }
+
+    @Override
     boolean canBeAnimationTarget() {
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index c7fdefc..123ca88 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -22,6 +22,7 @@
 import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -497,6 +498,23 @@
         return null;
     }
 
+    private boolean shouldSendEventWhenTaskInvisible(@NonNull Task task,
+            @NonNull PendingTaskFragmentEvent event) {
+        final TaskFragmentOrganizerState state =
+                mTaskFragmentOrganizerState.get(event.mTaskFragmentOrg.asBinder());
+        final TaskFragmentInfo lastInfo = state.mLastSentTaskFragmentInfos.get(event.mTaskFragment);
+        final TaskFragmentInfo info = event.mTaskFragment.getTaskFragmentInfo();
+        // Send an info changed callback if this event is for the last activities to finish in a
+        // Task so that the {@link TaskFragmentOrganizer} can delete this TaskFragment. Otherwise,
+        // the Task may be removed before it becomes visible again to send this event because it no
+        // longer has activities. As a result, the organizer will never get this info changed event
+        // and will not delete the TaskFragment because the organizer thinks the TaskFragment still
+        // has running activities.
+        return event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED
+                && task.topRunningActivity() == null && lastInfo != null
+                && lastInfo.getRunningActivityCount() > 0 && info.getRunningActivityCount() == 0;
+    }
+
     void dispatchPendingEvents() {
         if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
                 || mPendingTaskFragmentEvents.isEmpty()) {
@@ -510,7 +528,8 @@
             final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
             final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
             if (task != null && (task.lastActiveTime <= event.mDeferTime
-                    || !isTaskVisible(task, visibleTasks, invisibleTasks))) {
+                    || !(isTaskVisible(task, visibleTasks, invisibleTasks)
+                    || shouldSendEventWhenTaskInvisible(task, event)))) {
                 // Defer sending events to the TaskFragment until the host task is active again.
                 event.mDeferTime = task.lastActiveTime;
                 continue;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 364246e..348cfb6 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -40,18 +40,17 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.view.BatchedInputEventReceiver;
-import android.view.Choreographer;
 import android.view.InputApplicationHandle;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
+import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
 import android.view.MotionEvent;
 import android.view.WindowManager;
@@ -73,7 +72,7 @@
     public static final int RESIZING_HINT_DURATION_MS = 0;
 
     private final WindowManagerService mService;
-    private WindowPositionerEventReceiver mInputEventReceiver;
+    private InputEventReceiver mInputEventReceiver;
     private DisplayContent mDisplayContent;
     private Rect mTmpRect = new Rect();
     private int mMinVisibleWidth;
@@ -100,105 +99,93 @@
     InputApplicationHandle mDragApplicationHandle;
     InputWindowHandle mDragWindowHandle;
 
-    private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
-        public WindowPositionerEventReceiver(
-                InputChannel inputChannel, Looper looper, Choreographer choreographer) {
-            super(inputChannel, looper, choreographer);
-        }
-
-        @Override
-        public void onInputEvent(InputEvent event) {
-            boolean handled = false;
-            try {
-                // All returns need to be in the try block to make sure the finishInputEvent is
-                // called correctly.
-                if (!(event instanceof MotionEvent)
-                        || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
-                    return;
-                }
-                final MotionEvent motionEvent = (MotionEvent) event;
-                if (mDragEnded) {
-                    // The drag has ended but the clean-up message has not been processed by
-                    // window manager. Drop events that occur after this until window manager
-                    // has a chance to clean-up the input handle.
-                    handled = true;
-                    return;
-                }
-
-                final float newX = motionEvent.getRawX();
-                final float newY = motionEvent.getRawY();
-
-                switch (motionEvent.getAction()) {
-                    case MotionEvent.ACTION_DOWN: {
-                        if (DEBUG_TASK_POSITIONING) {
-                            Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
-                        }
-                    } break;
-
-                    case MotionEvent.ACTION_MOVE: {
-                        if (DEBUG_TASK_POSITIONING){
-                            Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
-                        }
-                        synchronized (mService.mGlobalLock) {
-                            mDragEnded = notifyMoveLocked(newX, newY);
-                            mTask.getDimBounds(mTmpRect);
-                        }
-                        if (!mTmpRect.equals(mWindowDragBounds)) {
-                            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                                    "wm.TaskPositioner.resizeTask");
-                            mService.mAtmService.resizeTask(
-                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
-                            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-                        }
-                    } break;
-
-                    case MotionEvent.ACTION_UP: {
-                        if (DEBUG_TASK_POSITIONING) {
-                            Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
-                        }
-                        mDragEnded = true;
-                    } break;
-
-                    case MotionEvent.ACTION_CANCEL: {
-                        if (DEBUG_TASK_POSITIONING) {
-                            Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
-                        }
-                        mDragEnded = true;
-                    } break;
-                }
-
-                if (mDragEnded) {
-                    final boolean wasResizing = mResizing;
-                    synchronized (mService.mGlobalLock) {
-                        endDragLocked();
-                        mTask.getDimBounds(mTmpRect);
-                    }
-                    if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
-                        // We were using fullscreen surface during resizing. Request
-                        // resizeTask() one last time to restore surface to window size.
-                        mService.mAtmService.resizeTask(
-                                mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
-                    }
-
-                    // Post back to WM to handle clean-ups. We still need the input
-                    // event handler for the last finishInputEvent()!
-                    mService.mTaskPositioningController.finishTaskPositioning();
-                }
-                handled = true;
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception caught by drag handleMotion", e);
-            } finally {
-                finishInputEvent(event, handled);
-            }
-        }
-    }
-
     /** Use {@link #create(WindowManagerService)} instead. */
     @VisibleForTesting
     TaskPositioner(WindowManagerService service) {
         mService = service;
     }
 
+    private boolean onInputEvent(InputEvent event) {
+        // All returns need to be in the try block to make sure the finishInputEvent is
+        // called correctly.
+        if (!(event instanceof MotionEvent)
+                || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+            return false;
+        }
+        final MotionEvent motionEvent = (MotionEvent) event;
+        if (mDragEnded) {
+            // The drag has ended but the clean-up message has not been processed by
+            // window manager. Drop events that occur after this until window manager
+            // has a chance to clean-up the input handle.
+            return true;
+        }
+
+        final float newX = motionEvent.getRawX();
+        final float newY = motionEvent.getRawY();
+
+        switch (motionEvent.getAction()) {
+            case MotionEvent.ACTION_DOWN: {
+                if (DEBUG_TASK_POSITIONING) {
+                    Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
+                }
+            }
+            break;
+
+            case MotionEvent.ACTION_MOVE: {
+                if (DEBUG_TASK_POSITIONING) {
+                    Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
+                }
+                synchronized (mService.mGlobalLock) {
+                    mDragEnded = notifyMoveLocked(newX, newY);
+                    mTask.getDimBounds(mTmpRect);
+                }
+                if (!mTmpRect.equals(mWindowDragBounds)) {
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                            "wm.TaskPositioner.resizeTask");
+                    mService.mAtmService.resizeTask(
+                            mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
+                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                }
+            }
+            break;
+
+            case MotionEvent.ACTION_UP: {
+                if (DEBUG_TASK_POSITIONING) {
+                    Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
+                }
+                mDragEnded = true;
+            }
+            break;
+
+            case MotionEvent.ACTION_CANCEL: {
+                if (DEBUG_TASK_POSITIONING) {
+                    Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
+                }
+                mDragEnded = true;
+            }
+            break;
+        }
+
+        if (mDragEnded) {
+            final boolean wasResizing = mResizing;
+            synchronized (mService.mGlobalLock) {
+                endDragLocked();
+                mTask.getDimBounds(mTmpRect);
+            }
+            if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
+                // We were using fullscreen surface during resizing. Request
+                // resizeTask() one last time to restore surface to window size.
+                mService.mAtmService.resizeTask(
+                        mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
+            }
+
+            // Post back to WM to handle clean-ups. We still need the input
+            // event handler for the last finishInputEvent()!
+            mService.mTaskPositioningController.finishTaskPositioning();
+        }
+        return true;
+    }
+
     @VisibleForTesting
     Rect getWindowDragBounds() {
         return mWindowDragBounds;
@@ -221,9 +208,9 @@
         mDisplayContent = displayContent;
         mClientChannel = mService.mInputManager.createInputChannel(TAG);
 
-        mInputEventReceiver = new WindowPositionerEventReceiver(
+        mInputEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver(
                 mClientChannel, mService.mAnimationHandler.getLooper(),
-                mService.mAnimator.getChoreographer());
+                mService.mAnimator.getChoreographer(), this::onInputEvent);
 
         mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
                 DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a477108..b13c9a9 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -79,6 +79,7 @@
 import android.window.TransitionInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -1271,6 +1272,14 @@
                 change.setAllowEnterPip(topMostActivity != null
                         && topMostActivity.checkEnterPictureInPictureAppOpsState());
             }
+            final ActivityRecord activityRecord = target.asActivityRecord();
+            if (activityRecord != null) {
+                final Task arTask = activityRecord.getTask();
+                final int backgroundColor = ColorUtils.setAlphaComponent(
+                        arTask.getTaskDescription().getBackgroundColor(), 255);
+                change.setBackgroundColor(backgroundColor);
+            }
+
             out.addChange(change);
         }
 
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index ffe1462..fe968ec 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -462,6 +462,10 @@
         mLegacyListeners.add(listener);
     }
 
+    void unregisterLegacyListener(WindowManagerInternal.AppTransitionListener listener) {
+        mLegacyListeners.remove(listener);
+    }
+
     void dispatchLegacyAppTransitionPending() {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
             mLegacyListeners.get(i).onAppTransitionPendingLocked();
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index fe405e5..fc154a8 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -107,7 +107,7 @@
     /** Returns {@code true} if visibility is changed. */
     boolean updateWallpaperWindows(boolean visible) {
         boolean changed = false;
-        if (isVisible() != visible) {
+        if (mVisibleRequested != visible) {
             ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
                     token, visible);
             setVisibility(visible);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 61acb97..4006848 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -85,6 +85,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
 import android.view.TaskTransitionSpec;
@@ -312,6 +313,8 @@
 
     private final List<WindowContainerListener> mListeners = new ArrayList<>();
 
+    private OverlayHost mOverlayHost;
+
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
         mTransitionController = mWmService.mAtmService.getTransitionController();
@@ -341,6 +344,9 @@
         super.onConfigurationChanged(newParentConfig);
         updateSurfacePositionNonOrganized();
         scheduleAnimation();
+        if (mOverlayHost != null) {
+            mOverlayHost.dispatchConfigurationChanged(getConfiguration());
+        }
     }
 
     void reparent(WindowContainer newParent, int position) {
@@ -387,6 +393,8 @@
 
         if (mParent != null) {
             mParent.onChildAdded(this);
+        } else if (mSurfaceAnimator.hasLeash()) {
+            mSurfaceAnimator.cancelAnimation();
         }
         if (!mReparenting) {
             onSyncReparent(oldParent, mParent);
@@ -487,6 +495,11 @@
                 t.reparent(sc, mSurfaceControl);
             }
         }
+
+        if (mOverlayHost != null) {
+            mOverlayHost.setParent(t, mSurfaceControl);
+        }
+
         scheduleAnimation();
     }
 
@@ -632,6 +645,10 @@
             mLastSurfacePosition.set(0, 0);
             scheduleAnimation();
         }
+        if (mOverlayHost != null) {
+            mOverlayHost.release();
+            mOverlayHost = null;
+        }
 
         // This must happen after updating the surface so that sync transactions can be handled
         // properly.
@@ -2308,6 +2325,9 @@
                 wc.assignLayer(t, layer++);
             }
         }
+        if (mOverlayHost != null) {
+            mOverlayHost.setLayer(t, layer++);
+        }
     }
 
     void assignChildLayers() {
@@ -3570,4 +3590,18 @@
         void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                 @AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
     }
+
+    void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+        if (mOverlayHost == null) {
+            mOverlayHost = new OverlayHost(mWmService);
+        }
+        mOverlayHost.addOverlay(overlay, mSurfaceControl);
+    }
+
+    void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+        if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) {
+            mOverlayHost.release();
+            mOverlayHost = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 62c674b..b9fa297 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ClipData;
@@ -32,6 +35,7 @@
 import android.view.InputChannel;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControlViewHost;
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 
@@ -39,6 +43,7 @@
 import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
 
+import java.lang.annotation.Retention;
 import java.util.List;
 import java.util.Set;
 
@@ -608,6 +613,7 @@
     /**
      * Checks whether the specified IME client has IME focus or not.
      *
+     * @param windowToken The window token of the input method client
      * @param uid UID of the process to be queried
      * @param pid PID of the process to be queried
      * @param displayId Display ID reported from the client. Note that this method also verifies
@@ -615,7 +621,22 @@
      * @return {@code true} if the IME client specified with {@code uid}, {@code pid}, and
      *         {@code displayId} has IME focus
      */
-    public abstract boolean isInputMethodClientFocus(int uid, int pid, int displayId);
+    public abstract @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
+            int uid, int pid, int displayId);
+
+    @Retention(SOURCE)
+    @IntDef({
+            ImeClientFocusResult.HAS_IME_FOCUS,
+            ImeClientFocusResult.NOT_IME_TARGET_WINDOW,
+            ImeClientFocusResult.DISPLAY_ID_MISMATCH,
+            ImeClientFocusResult.INVALID_DISPLAY_ID
+    })
+    public @interface ImeClientFocusResult {
+        int HAS_IME_FOCUS = 0;
+        int NOT_IME_TARGET_WINDOW = -1;
+        int DISPLAY_ID_MISMATCH = -2;
+        int INVALID_DISPLAY_ID = -3;
+    }
 
     /**
      * Checks whether the given {@code uid} is allowed to use the given {@code displayId} or not.
@@ -767,4 +788,16 @@
      *         {@code false} otherwise.
      */
     public abstract boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken);
+
+    /**
+     * Internal methods for other parts of SystemServer to manage
+     * SurfacePackage based overlays on tasks.
+     *
+     * Callers prepare a view hierarchy with SurfaceControlViewHost
+     * and send the package to WM here. The remote view hierarchy will receive
+     * configuration change, lifecycle events, etc, forwarded over the
+     * ISurfaceControlViewHost interface inside the SurfacePackage.
+     */
+    public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
+    public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7be128b..026b9e1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -55,6 +55,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -142,6 +143,7 @@
 import android.Manifest.permission;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -169,14 +171,12 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.Insets;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.configstore.V1_0.OptionalBool;
-import android.hardware.configstore.V1_1.DisplayOrientation;
 import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
-import android.hardware.configstore.V1_1.OptionalDisplayOrientation;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
@@ -222,14 +222,12 @@
 import android.util.EventLog;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 import android.view.Display;
-import android.view.DisplayAddress;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -267,6 +265,7 @@
 import android.view.ScrollCaptureResponse;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceSession;
 import android.view.TaskTransitionSpec;
 import android.view.View;
@@ -281,6 +280,7 @@
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 import android.window.ClientWindowFrames;
+import android.window.IOnFpsCallbackListener;
 import android.window.TaskSnapshot;
 
 import com.android.internal.R;
@@ -438,8 +438,6 @@
      */
     static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
             SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
-    private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0;
-    private DisplayAddress mPrimaryDisplayPhysicalAddress;
 
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
@@ -588,18 +586,11 @@
     final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
 
     /**
-     * Windows whose animations have ended and now must be removed.
+     * Mapping of displayId to {@link DisplayImePolicy}.
+     * Note that this can be accessed without holding the lock.
      */
-    final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
-
-    /**
-     * Used when processing mPendingRemove to avoid working on the original array.
-     */
-    WindowState[] mPendingRemoveTmp = new WindowState[20];
-
-    // TODO: use WindowProcessController once go/wm-unified is done.
-    /** Mapping of process pids to configurations */
-    final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>();
+    volatile Map<Integer, Integer> mDisplayImePolicyCache = Collections.unmodifiableMap(
+            new ArrayMap<>());
 
     /**
      * Windows whose surface should be destroyed.
@@ -721,6 +712,7 @@
     final TaskSnapshotController mTaskSnapshotController;
 
     final BlurController mBlurController;
+    final TaskFpsCallbackController mTaskFpsCallbackController;
 
     boolean mIsTouchDevice;
     boolean mIsFakeTouchDevice;
@@ -751,6 +743,8 @@
     final WindowContextListenerController mWindowContextListenerController =
             new WindowContextListenerController();
 
+    private InputTarget mFocusedInputTarget;
+
     @VisibleForTesting
     final class SettingsObserver extends ContentObserver {
         private final Uri mDisplayInversionEnabledUri =
@@ -1365,6 +1359,7 @@
         mStartingSurfaceController = new StartingSurfaceController(this);
 
         mBlurController = new BlurController(mContext, mPowerManager);
+        mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
         mAccessibilityController = new AccessibilityController(this);
     }
 
@@ -1761,7 +1756,10 @@
                 activity.attachStartingWindow(win);
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
                         activity, win);
-            } else if (type == TYPE_INPUT_METHOD) {
+            } else if (type == TYPE_INPUT_METHOD
+                    // IME window is always touchable.
+                    // Ignore non-touchable windows e.g. Stylus InkWindow.java.
+                    && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) {
                 displayContent.setInputMethodWindowLocked(win);
                 imMayMove = false;
             } else if (type == TYPE_INPUT_METHOD_DIALOG) {
@@ -1796,8 +1794,7 @@
                 prepareNoneTransitionForRelaunching(activity);
             }
 
-            if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
-                    win.isClientLocal())) {
+            if (displayPolicy.areSystemBarsForcedShownLw()) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
             }
 
@@ -1844,6 +1841,7 @@
             displayContent.getInsetsStateController().updateAboveInsetsState(
                     win, false /* notifyInsetsChanged */);
 
+            outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
             getInsetsSourceControls(win, outActiveControls);
         }
 
@@ -2038,7 +2036,6 @@
             dc.mWinRemovedSinceNullFocus.add(win);
         }
         mEmbeddedWindowController.onWindowRemoved(win);
-        mPendingRemove.remove(win);
         mResizingWindows.remove(win);
         updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
         mWindowsChanged = true;
@@ -2257,6 +2254,15 @@
                     winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
                             & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
                 }
+                if (win.mActivityRecord != null
+                        && !displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
+                                win.mActivityRecord.info, flagChanges, privateFlagChanges)) {
+                    mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
+                            win.mActivityRecord.getTask()));
+                    Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
+                            + " can't remain on display " + displayContent.getDisplayId());
+                    return 0;
+                }
             }
 
             if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
@@ -2438,23 +2444,6 @@
             configChanged = displayContent.updateOrientation();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
-            final DisplayInfo displayInfo = win.getDisplayInfo();
-            int transformHint = displayInfo.rotation;
-            // If the window is on the primary display, use the panel orientation to adjust the
-            // transform hint
-            final boolean isPrimaryDisplay = displayInfo.address != null &&
-                    displayInfo.address.equals(mPrimaryDisplayPhysicalAddress);
-            if (isPrimaryDisplay) {
-                transformHint = (transformHint + mPrimaryDisplayOrientation) % 4;
-            }
-            outSurfaceControl.setTransformHint(
-                    SurfaceControl.rotationToBufferTransform(transformHint));
-            ProtoLog.v(WM_DEBUG_ORIENTATION,
-                    "Passing transform hint %d for window %s%s",
-                    transformHint, win,
-                    isPrimaryDisplay ? " on primary display with orientation "
-                            + mPrimaryDisplayOrientation : "");
-
             if (toBeDisplayed && win.mIsWallpaper) {
                 displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
             }
@@ -3838,6 +3827,40 @@
     }
 
     /**
+     * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned
+     * bitmap will be full size and will not include any secure content.
+     *
+     * @param taskId The task ID of the task for which a snapshot is requested.
+     * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could
+     * not be generated.
+     */
+    @Nullable public Bitmap captureTaskBitmap(int taskId) {
+        if (mTaskSnapshotController.shouldDisableSnapshots()) {
+            return null;
+        }
+
+        synchronized (mGlobalLock) {
+            final Task task = mRoot.anyTaskForId(taskId);
+            if (task == null) {
+                return null;
+            }
+
+            task.getBounds(mTmpRect);
+            final SurfaceControl sc = task.getSurfaceControl();
+            final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
+                    new SurfaceControl.LayerCaptureArgs.Builder(sc)
+                            .setSourceCrop(mTmpRect)
+                            .build());
+            if (buffer == null) {
+                Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId);
+                return null;
+            }
+
+            return buffer.asBitmap();
+        }
+    }
+
+    /**
      * In case a task write/delete operation was lost because the system crashed, this makes sure to
      * clean up the directory to remove obsolete files.
      *
@@ -4316,6 +4339,15 @@
         }
     }
 
+    void reportKeepClearAreasChanged(Session session, IWindow window, List<Rect> keepClearAreas) {
+        synchronized (mGlobalLock) {
+            final WindowState win = windowForClientLocked(session, window, true);
+            if (win.setKeepClearAreas(keepClearAreas)) {
+                win.getDisplayContent().updateKeepClearAreas();
+            }
+        }
+    }
+
     @Override
     public void registerDisplayFoldListener(IDisplayFoldListener listener) {
         mPolicy.registerDisplayFoldListener(listener);
@@ -4888,9 +4920,6 @@
         mTaskSnapshotController.systemReady();
         mHasWideColorGamutSupport = queryWideColorGamutSupport();
         mHasHdrSupport = queryHdrSupport();
-        mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation();
-        mPrimaryDisplayPhysicalAddress =
-            DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId());
         UiThread.getHandler().post(mSettingsObserver::loadSettings);
         IVrManager vrManager = IVrManager.Stub.asInterface(
                 ServiceManager.getService(Context.VR_SERVICE));
@@ -4953,39 +4982,6 @@
         return false;
     }
 
-    private static @Surface.Rotation int queryPrimaryDisplayOrientation() {
-        Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop =
-                SurfaceFlingerProperties.primary_display_orientation();
-        if (prop.isPresent()) {
-            switch (prop.get()) {
-                case ORIENTATION_90: return Surface.ROTATION_90;
-                case ORIENTATION_180: return Surface.ROTATION_180;
-                case ORIENTATION_270: return Surface.ROTATION_270;
-                case ORIENTATION_0:
-                default:
-                    return Surface.ROTATION_0;
-            }
-        }
-        try {
-            ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
-            OptionalDisplayOrientation primaryDisplayOrientation =
-                    surfaceFlinger.primaryDisplayOrientation();
-            if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) {
-                switch (primaryDisplayOrientation.value) {
-                    case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90;
-                    case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180;
-                    case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270;
-                    case DisplayOrientation.ORIENTATION_0:
-                    default:
-                        return Surface.ROTATION_0;
-                }
-            }
-        } catch (Exception e) {
-            // Use default value if we can't talk to config store.
-        }
-        return Surface.ROTATION_0;
-    }
-
     // Returns an input target which is mapped to the given input token. This can be a WindowState
     // or an embedded window.
     @Nullable InputTarget getInputTargetFromToken(IBinder inputToken) {
@@ -5013,6 +5009,7 @@
                 Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
                 return;
             }
+            mFocusedInputTarget = newTarget;
 
             mAccessibilityController.onFocusChanged(lastTarget, newTarget);
             ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
@@ -5076,6 +5073,7 @@
         public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
         public static final int LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED = 63;
         public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
+        public static final int REPARENT_TASK_TO_DEFAULT_DISPLAY = 65;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -5393,6 +5391,15 @@
                     }
                     break;
                 }
+                case REPARENT_TASK_TO_DEFAULT_DISPLAY: {
+                    synchronized (mGlobalLock) {
+                        Task task = (Task) msg.obj;
+                        task.reparent(mRoot.getDefaultTaskDisplayArea(), true /* onTop */);
+                        // Resume focusable root task after reparenting to another display area.
+                        task.resumeNextFocusAfterReparent();
+                    }
+                    break;
+                }
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
@@ -5673,6 +5680,11 @@
     }
 
     @Override
+    public void saveWindowTraceToFile() {
+        mWindowTracing.saveForBugreport(null /* printwriter */);
+    }
+
+    @Override
     public boolean isWindowTraceEnabled() {
         return mWindowTracing.isEnabled();
     }
@@ -6342,23 +6354,6 @@
                 }
             }
         }
-        if (mPendingRemove.size() > 0) {
-            pw.println();
-            pw.println("  Remove pending for:");
-            for (int i=mPendingRemove.size()-1; i>=0; i--) {
-                WindowState w = mPendingRemove.get(i);
-                if (windows == null || windows.contains(w)) {
-                    pw.print("  Remove #"); pw.print(i); pw.print(' ');
-                            pw.print(w);
-                    if (dumpAll) {
-                        pw.println(":");
-                        w.dump(pw, "    ", true);
-                    } else {
-                        pw.println();
-                    }
-                }
-            }
-        }
         if (mForceRemoves != null && mForceRemoves.size() > 0) {
             pw.println();
             pw.println("  Windows force removing:");
@@ -6914,6 +6909,7 @@
     void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
         synchronized (mGlobalLock) {
             mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
+            mRoot.updateDisplayImePolicyCache();
         }
     }
 
@@ -6969,23 +6965,6 @@
         }
     }
 
-    @Override
-    public void setForwardedInsets(int displayId, Insets insets) throws RemoteException {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-            if (dc == null) {
-                return;
-            }
-            final int callingUid = Binder.getCallingUid();
-            final int displayOwnerUid = dc.getDisplay().getOwnerUid();
-            if (callingUid != displayOwnerUid) {
-                throw new SecurityException(
-                        "Only owner of the display can set ForwardedInsets to it.");
-            }
-            dc.setForwardedInsets(insets);
-        }
-    }
-
     MousePositionTracker mMousePositionTracker = new MousePositionTracker();
 
     private static class MousePositionTracker implements PointerEventListener {
@@ -7072,6 +7051,13 @@
         }
     }
 
+    PointF getLatestMousePosition() {
+        synchronized (mMousePositionTracker) {
+            return new PointF(mMousePositionTracker.mLatestMouseX,
+                    mMousePositionTracker.mLatestMouseY);
+        }
+    }
+
     /**
      * Update a tap exclude region in the window identified by the provided id. Touches down on this
      * region will not:
@@ -7338,16 +7324,14 @@
         if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getDisplayImePolicy()")) {
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
         }
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-        if (dc == null) {
+        final Map<Integer, Integer> displayImePolicyCache = mDisplayImePolicyCache;
+        if (!displayImePolicyCache.containsKey(displayId)) {
             ProtoLog.w(WM_ERROR,
                     "Attempted to get IME policy of a display that does not exist: %d",
                     displayId);
             return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
         }
-        synchronized (mGlobalLock) {
-            return dc.getImePolicy();
-        }
+        return displayImePolicyCache.get(displayId);
     }
 
     @Override
@@ -7696,19 +7680,32 @@
         }
 
         @Override
-        public boolean isInputMethodClientFocus(int uid, int pid, int displayId) {
+        public @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
+                int uid, int pid, int displayId) {
             if (displayId == Display.INVALID_DISPLAY) {
-                return false;
+                return ImeClientFocusResult.INVALID_DISPLAY_ID;
             }
             synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
+                final WindowState window = mWindowMap.get(windowToken);
+                if (window == null) {
+                    return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
+                }
+                final int tokenDisplayId = window.getDisplayContent().getDisplayId();
+                if (tokenDisplayId != displayId) {
+                    Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
+                            + " from client: " + displayId
+                            + " from window: " + tokenDisplayId);
+                    return ImeClientFocusResult.DISPLAY_ID_MISMATCH;
+                }
                 if (displayContent == null
                         || displayContent.getDisplayId() != displayId
                         || !displayContent.hasAccess(uid)) {
-                    return false;
+                    return ImeClientFocusResult.INVALID_DISPLAY_ID;
                 }
+
                 if (displayContent.isInputMethodClientFocus(uid, pid)) {
-                    return true;
+                    return ImeClientFocusResult.HAS_IME_FOCUS;
                 }
                 // Okay, how about this...  what is the current focus?
                 // It seems in some cases we may not have moved the IM
@@ -7721,10 +7718,11 @@
                 final WindowState currentFocus = displayContent.mCurrentFocus;
                 if (currentFocus != null && currentFocus.mSession.mUid == uid
                         && currentFocus.mSession.mPid == pid) {
-                    return currentFocus.canBeImeTarget();
+                    return currentFocus.canBeImeTarget() ? ImeClientFocusResult.HAS_IME_FOCUS
+                            : ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
                 }
             }
-            return false;
+            return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
         }
 
         @Override
@@ -7823,9 +7821,7 @@
 
         @Override
         public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
-            synchronized (mGlobalLock) {
-                return WindowManagerService.this.getDisplayImePolicy(displayId);
-            }
+            return WindowManagerService.this.getDisplayImePolicy(displayId);
         }
 
         @Override
@@ -7920,6 +7916,28 @@
         public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
             return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
         }
+
+        @Override
+        public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+            synchronized (mGlobalLock) {
+                final Task task = mRoot.getRootTask(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("no task with taskId" + taskId);
+                }
+                task.addOverlay(overlay);
+            }
+        }
+
+        @Override
+        public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+            synchronized (mGlobalLock) {
+                final Task task = mRoot.getRootTask(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("no task with taskId" + taskId);
+                }
+                task.removeOverlay(overlay);
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
@@ -8157,21 +8175,14 @@
     }
 
     private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
-        WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
-        if (touchedWindow == null) {
-            // if a user taps outside the currently focused window onto an embedded window, treat
-            // it as if the host window was tapped.
-            touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken);
-        }
-
-        if (touchedWindow == null || !touchedWindow.canReceiveKeys(true /* fromUserTouch */)) {
+        InputTarget t = getInputTargetFromToken(touchedToken);
+        if (t == null || !t.receiveFocusFromTapOutside()) {
             // If the window that received the input event cannot receive keys, don't move the
             // display it's on to the top since that window won't be able to get focus anyway.
             return;
         }
-
         if (mRecentsAnimationController != null
-                && mRecentsAnimationController.getTargetAppMainWindow() == touchedWindow) {
+            && mRecentsAnimationController.getTargetAppMainWindow() == t) {
             // If there is an active recents animation and touched window is the target, then ignore
             // the touch. The target already handles touches using its own input monitor and we
             // don't want to trigger any lifecycle changes from focusing another window.
@@ -8181,13 +8192,11 @@
         }
 
         ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s",
-                touchedWindow);
-        final DisplayContent displayContent = touchedWindow.getDisplayContent();
-        if (!displayContent.isOnTop()) {
-            displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
-                    true /* includingParents */);
+                t);
+        if (mFocusedInputTarget != t && mFocusedInputTarget != null) {
+            mFocusedInputTarget.handleTapOutsideFocusOutsideSelf();
         }
-        handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
+        t.handleTapOutsideFocusInsideSelf();
     }
 
     @VisibleForTesting
@@ -8288,7 +8297,7 @@
 
         flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
 
-        final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
+        final int sanitizedFlags = flags & (FLAG_NOT_TOUCHABLE
                 | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
         h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
         h.layoutParamsType = type;
@@ -8468,6 +8477,7 @@
     public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
             InsetsState outInsetsState) {
         final boolean fromLocal = Binder.getCallingPid() == myPid();
+        final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -8476,9 +8486,20 @@
                     throw new WindowManager.InvalidDisplayException("Display#" + displayId
                             + "could not be found!");
                 }
-                final WindowToken windowToken = dc.getWindowToken(attrs.token);
-                return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, outInsetsState,
-                        fromLocal);
+                final WindowToken token = dc.getWindowToken(attrs.token);
+                final float overrideScale = mAtmService.mCompatModePackages.getCompatScale(
+                        attrs.packageName, uid);
+                final InsetsState state = dc.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
+                final boolean hasCompatScale =
+                        WindowState.hasCompatScale(attrs, token, overrideScale);
+                outInsetsState.set(state, hasCompatScale || fromLocal);
+                if (hasCompatScale) {
+                    final float compatScale = token != null && token.hasSizeCompatBounds()
+                            ? token.getSizeCompatScale() * overrideScale
+                            : overrideScale;
+                    outInsetsState.scale(1f / compatScale);
+                }
+                return dc.getDisplayPolicy().areSystemBarsForcedShownLw();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -8707,20 +8728,21 @@
     }
 
     boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
+        final Task imeTargetWindowTask;
         synchronized (mGlobalLock) {
             final WindowState imeTargetWindow = mWindowMap.get(imeTargetWindowToken);
             if (imeTargetWindow == null) {
                 return false;
             }
-            final Task imeTargetWindowTask = imeTargetWindow.getTask();
+            imeTargetWindowTask = imeTargetWindow.getTask();
             if (imeTargetWindowTask == null) {
                 return false;
             }
-            final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
-                    imeTargetWindowTask.mUserId, false /* isLowResolution */,
-                    false /* restoreFromDisk */);
-            return snapshot != null && snapshot.hasImeSurface();
         }
+        final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
+                imeTargetWindowTask.mUserId, false /* isLowResolution */,
+                false /* restoreFromDisk */);
+        return snapshot != null && snapshot.hasImeSurface();
     }
 
     @Override
@@ -8764,4 +8786,35 @@
 
         mTaskTransitionSpec = null;
     }
+
+    @Override
+    @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+    public void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
+            IOnFpsCallbackListener listener) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+                != PackageManager.PERMISSION_GRANTED) {
+            final int pid = Binder.getCallingPid();
+            throw new SecurityException("Access denied to process: " + pid
+                    + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
+        }
+
+        if (mRoot.anyTaskForId(taskId) == null) {
+            throw new IllegalArgumentException("no task with taskId: " + taskId);
+        }
+
+        mTaskFpsCallbackController.registerCallback(taskId, listener);
+    }
+
+    @Override
+    @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+    public void unregisterTaskFpsCallback(IOnFpsCallbackListener listener) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+                != PackageManager.PERMISSION_GRANTED) {
+            final int pid = Binder.getCallingPid();
+            throw new SecurityException("Access denied to process: " + pid
+                    + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
+        }
+
+        mTaskFpsCallbackController.unregisterCallback(listener);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 79dcbcb..455856c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -139,6 +139,7 @@
 
     void setWindowManager(WindowManagerService wms) {
         mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController);
+        mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
     }
 
     TransitionController getTransitionController() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 863e3ca..1f83767 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -172,6 +172,7 @@
 import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
+import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
 import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.REMOVED;
 import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
@@ -196,6 +197,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.gui.TouchOcclusionMode;
 import android.os.Binder;
@@ -442,9 +444,8 @@
 
     // Current transformation being applied.
     float mGlobalScale=1;
-    float mLastGlobalScale=1;
     float mInvGlobalScale=1;
-    float mOverrideScale = 1;
+    final float mOverrideScale;
     float mHScale=1, mVScale=1;
     float mLastHScale=1, mLastVScale=1;
 
@@ -471,6 +472,12 @@
      * Coordinates are relative to the window's position.
      */
     private final List<Rect> mExclusionRects = new ArrayList<>();
+    /**
+     * List of rects which should ideally not be covered by floating windows like Pip.
+     *
+     * Coordinates are relative to the window's position.
+     */
+    private final List<Rect> mKeepClearAreas = new ArrayList<>();
 
     // 0 = left, 1 = right
     private final int[] mLastRequestedExclusionHeight = {0, 0};
@@ -1012,6 +1019,55 @@
         }
     }
 
+    /**
+     * @return a list of rects that should ideally not be covered by floating windows like pip.
+     *         The returned rect coordinates are relative to the display origin.
+     */
+    List<Rect> getKeepClearAreas() {
+        final Matrix tmpMatrix = new Matrix();
+        final float[] tmpFloat9 = new float[9];
+        return getKeepClearAreas(tmpMatrix, tmpFloat9);
+    }
+
+    /**
+     * @param tmpMatrix a temporary matrix to be used for transformations
+     * @param float9 a temporary array of 9 floats
+     *
+     * @return a list of rects that should ideally not be covered by floating windows like pip.
+     *         The returned rect coordinates are relative to the display origin.
+     */
+    List<Rect> getKeepClearAreas(Matrix tmpMatrix, float[] float9) {
+        getTransformationMatrix(float9, tmpMatrix);
+
+        // Translate all keep-clear rects to screen coordinates.
+        final List<Rect> transformedKeepClearAreas = new ArrayList<Rect>();
+        final RectF tmpRect = new RectF();
+        Rect curr;
+        for (Rect r : mKeepClearAreas) {
+            tmpRect.set(r);
+            tmpMatrix.mapRect(tmpRect);
+            curr = new Rect();
+            tmpRect.roundOut(curr);
+            transformedKeepClearAreas.add(curr);
+        }
+        return transformedKeepClearAreas;
+    }
+
+    /**
+     * @param keepClearAreas the new keep-clear areas for this window. The rects should be defined
+     *                       in window coordinate space
+     *
+     * @return true if there is a change in the list of keep-clear areas; false otherwise
+     */
+    boolean setKeepClearAreas(List<Rect> keepClearAreas) {
+        if (mKeepClearAreas.equals(keepClearAreas)) {
+            return false;
+        }
+        mKeepClearAreas.clear();
+        mKeepClearAreas.addAll(keepClearAreas);
+        return true;
+    }
+
     interface PowerManagerWrapper {
         void wakeUp(long time, @WakeReason int reason, String details);
 
@@ -1091,6 +1147,7 @@
             mSubLayer = 0;
             mWinAnimator = null;
             mWpcForDisplayAreaConfigChanges = null;
+            mOverrideScale = 1f;
             return;
         }
         mDeathRecipient = deathRecipient;
@@ -1138,6 +1195,7 @@
         mLayer = 0;
         mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
                 mAttrs.packageName, s.mUid);
+        updateGlobalScale();
 
         // Make sure we initial all fields before adding to parentWindow, to prevent exception
         // during onDisplayChanged.
@@ -1167,6 +1225,23 @@
         mSession.windowAddedLocked();
     }
 
+    boolean updateGlobalScale() {
+        if (hasCompatScale()) {
+            if (mOverrideScale != 1f) {
+                mGlobalScale = mToken.hasSizeCompatBounds()
+                        ? mToken.getSizeCompatScale() * mOverrideScale
+                        : mOverrideScale;
+            } else {
+                mGlobalScale = mToken.getSizeCompatScale();
+            }
+            mInvGlobalScale = 1f / mGlobalScale;
+            return true;
+        }
+
+        mGlobalScale = mInvGlobalScale = 1f;
+        return false;
+    }
+
     /**
      * @return {@code true} if the application runs in size compatibility mode or has an app level
      * scaling override set.
@@ -1175,7 +1250,7 @@
      * @see ActivityRecord#hasSizeCompatBounds()
      */
     boolean hasCompatScale() {
-        return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
+        return hasCompatScale(mAttrs, mActivityRecord, mOverrideScale);
     }
 
     /**
@@ -1183,11 +1258,16 @@
      * @see android.content.res.CompatibilityInfo#supportsScreen
      * @see ActivityRecord#hasSizeCompatBounds()
      */
-    static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
-        return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
-                || (windowToken != null && windowToken.hasSizeCompatBounds()
-                // Exclude starting window because it is not displayed by the application.
-                && attrs.type != TYPE_APPLICATION_STARTING);
+    static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken token,
+            float overrideScale) {
+        if ((attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0) {
+            return true;
+        }
+        if (attrs.type == TYPE_APPLICATION_STARTING) {
+            // Exclude starting window because it is not displayed by the application.
+            return false;
+        }
+        return token != null && token.hasSizeCompatBounds() || overrideScale != 1f;
     }
 
     /**
@@ -1691,21 +1771,6 @@
                 && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
     }
 
-    void prelayout() {
-        if (hasCompatScale()) {
-            if (mOverrideScale != 1f) {
-                mGlobalScale = mToken.hasSizeCompatBounds()
-                        ? mToken.getSizeCompatScale() * mOverrideScale
-                        : mOverrideScale;
-            } else {
-                mGlobalScale = mToken.getSizeCompatScale();
-            }
-            mInvGlobalScale = 1 / mGlobalScale;
-        } else {
-            mGlobalScale = mInvGlobalScale = 1;
-        }
-    }
-
     @Override
     boolean hasContentToDisplay() {
         if (!mAppFreezing && isDrawn() && (mViewVisibility == View.VISIBLE
@@ -2547,7 +2612,8 @@
 
         if (DEBUG_INPUT_METHOD) {
             Slog.i(TAG_WM, "isVisibleRequestedOrAdding " + this + ": "
-                    + isVisibleRequestedOrAdding());
+                    + isVisibleRequestedOrAdding() + " isVisible: " + (isVisible()
+                    && mActivityRecord != null && mActivityRecord.isVisible()));
             if (!isVisibleRequestedOrAdding()) {
                 Slog.i(TAG_WM, "  mSurfaceController=" + mWinAnimator.mSurfaceController
                         + " relayoutCalled=" + mRelayoutCalled
@@ -2562,7 +2628,8 @@
                 }
             }
         }
-        return isVisibleRequestedOrAdding();
+        return isVisibleRequestedOrAdding()
+                || (isVisible() && mActivityRecord != null && mActivityRecord.isVisible());
     }
 
     private final class DeadWindowEventReceiver extends InputEventReceiver {
@@ -2926,7 +2993,6 @@
         @Override
         public void binderDied() {
             try {
-                boolean resetSplitScreenResizing = false;
                 synchronized (mWmService.mGlobalLock) {
                     final WindowState win = mWmService
                             .windowForClientLocked(mSession, mClient, false);
@@ -2942,16 +3008,6 @@
                         WindowState.this.removeIfPossible();
                     }
                 }
-                if (resetSplitScreenResizing) {
-                    try {
-                        // Note: this calls into ActivityManager, so we must *not* hold the window
-                        // manager lock while calling this.
-                        mWmService.mActivityTaskManager.setSplitScreenResizing(false);
-                    } catch (RemoteException e) {
-                        // Local call, shouldn't return RemoteException.
-                        throw e.rethrowAsRuntimeException();
-                    }
-                }
             } catch (IllegalArgumentException ex) {
                 // This will happen if the window has already been removed.
             }
@@ -4061,6 +4117,9 @@
         proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
         proto.write(HAS_COMPAT_SCALE, hasCompatScale());
         proto.write(GLOBAL_SCALE, mGlobalScale);
+        for (Rect r : getKeepClearAreas()) {
+            r.dumpDebug(proto, KEEP_CLEAR_AREAS);
+        }
         proto.end(token);
     }
 
@@ -4229,6 +4288,7 @@
         }
         pw.println(prefix + "isOnScreen=" + isOnScreen());
         pw.println(prefix + "isVisible=" + isVisible());
+        pw.println(prefix + "keepClearAreas=" + getKeepClearAreas());
         if (dumpAll) {
             final String visibilityString = mRequestedVisibilities.toString();
             if (!visibilityString.isEmpty()) {
@@ -4836,15 +4896,20 @@
             if (hasSurface) {
                 mWmService.mDestroySurface.add(this);
             }
-            if (mRemoveOnExit) {
-                mWmService.mPendingRemove.add(this);
-                mRemoveOnExit = false;
-            }
         }
         mAnimatingExit = false;
         getDisplayContent().mWallpaperController.hideWallpapers(this);
     }
 
+    @Override
+    boolean handleCompleteDeferredRemoval() {
+        if (mRemoveOnExit) {
+            mRemoveOnExit = false;
+            removeImmediately();
+        }
+        return super.handleCompleteDeferredRemoval();
+    }
+
     boolean clearAnimatingFlags() {
         boolean didSomething = false;
         // We don't want to clear it out for windows that get replaced, because the
@@ -5252,7 +5317,6 @@
             mLastVScale != newVScale ) {
             getPendingTransaction().setMatrix(getSurfaceControl(),
                 newHScale, 0, 0, newVScale);
-            mLastGlobalScale = mGlobalScale;
             mLastHScale = newHScale;
             mLastVScale = newVScale;
         }
@@ -5978,4 +6042,24 @@
     boolean isTrustedOverlay() {
         return mInputWindowHandle.isTrustedOverlay();
     }
+
+    public boolean receiveFocusFromTapOutside() {
+        return canReceiveKeys(true);
+    }
+
+    @Override
+    public void handleTapOutsideFocusOutsideSelf() {
+        // Nothing to do here since raising the other window will naturally take care of
+        // us loosing focus
+    }
+
+    @Override
+    public void handleTapOutsideFocusInsideSelf() {
+        final DisplayContent displayContent = getDisplayContent();
+        if (!displayContent.isOnTop()) {
+            displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
+                    true /* includingParents */);
+        }
+        mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
+    }
 }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7b4fd36..79a980f 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -68,14 +68,18 @@
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
         "com_android_server_sensor_SensorService.cpp",
+        "com_android_server_wm_TaskFpsCallbackController.cpp",
         "onload.cpp",
         ":lib_cachedAppOptimizer_native",
         ":lib_networkStatsFactory_native",
+        ":lib_gameManagerService_native",
     ],
 
     include_dirs: [
         "frameworks/base/libs",
         "frameworks/native/services",
+        "frameworks/native/libs/math/include",
+        "frameworks/native/libs/ui/include",
         "system/gatekeeper/include",
         "system/memory/libmeminfo/include",
     ],
@@ -103,6 +107,7 @@
         "libcrypto",
         "liblog",
         "libgraphicsenv",
+        "libgralloctypes",
         "libhardware",
         "libhardware_legacy",
         "libhidlbase",
@@ -157,6 +162,10 @@
         "[email protected]",
         "[email protected]",
         "[email protected]",
+        "[email protected]",
+        "[email protected]",
+        "android.hardware.graphics.common-V3-ndk",
+        "[email protected]",
         "[email protected]",
         "[email protected]",
         "[email protected]",
@@ -216,3 +225,10 @@
         "com_android_server_am_CachedAppOptimizer.cpp",
     ],
 }
+
+filegroup {
+    name: "lib_gameManagerService_native",
+    srcs: [
+        "com_android_server_app_GameManagerService.cpp",
+    ],
+}
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 4190a91..94bc22a 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -59,6 +59,9 @@
 
 namespace android {
 
+static bool cancelRunningCompaction;
+static bool compactionInProgress;
+
 // Legacy method for compacting processes, any new code should
 // use compactProcess instead.
 static inline void compactProcessProcfs(int pid, const std::string& compactionType) {
@@ -83,9 +86,18 @@
         // Skip compaction if failed to open pidfd with any error
         return -errno;
     }
+    compactionInProgress = true;
+    cancelRunningCompaction = false;
 
     int64_t totalBytesCompacted = 0;
     for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
+        if (CC_UNLIKELY(cancelRunningCompaction)) {
+            // There could be a significant delay betweenwhen a compaction
+            // is requested and when it is handled during this time
+            // our OOM adjust could have improved.
+            cancelRunningCompaction = false;
+            break;
+        }
         int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
         for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
             vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
@@ -95,11 +107,13 @@
         auto bytesCompacted =
                 process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
         if (CC_UNLIKELY(bytesCompacted == -1)) {
+            compactionInProgress = false;
             return -errno;
         }
 
         totalBytesCompacted += bytesCompacted;
     }
+    compactionInProgress = false;
 
     return totalBytesCompacted;
 }
@@ -228,6 +242,12 @@
     }
 }
 
+static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, jobject) {
+    if (compactionInProgress) {
+        cancelRunningCompaction = true;
+    }
+}
+
 static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
                                                                     jint compactionFlags) {
     compactProcessOrFallback(pid, compactionFlags);
@@ -279,6 +299,8 @@
 
 static const JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
+        {"cancelCompaction", "()V",
+         (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
         {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
         {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
         {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
diff --git a/services/core/jni/com_android_server_app_GameManagerService.cpp b/services/core/jni/com_android_server_app_GameManagerService.cpp
new file mode 100644
index 0000000..3028813
--- /dev/null
+++ b/services/core/jni/com_android_server_app_GameManagerService.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GameManagerService"
+
+#include <android/log.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "jni.h"
+
+namespace android {
+
+static void android_server_app_GameManagerService_nativeSetOverrideFrameRate(JNIEnv* env,
+                                                                             jclass clazz, jint uid,
+                                                                             jfloat frameRate) {
+    SurfaceComposerClient::setOverrideFrameRate(uid, frameRate);
+}
+
+static const JNINativeMethod gMethods[] = {
+        {"nativeSetOverrideFrameRate", "(IF)V",
+         (void*)android_server_app_GameManagerService_nativeSetOverrideFrameRate},
+};
+
+int register_android_server_app_GameManagerService(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/server/app/GameManagerService", gMethods,
+                                    NELEM(gMethods));
+}
+
+}; // namespace android
\ No newline at end of file
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 6aa6323..c71686a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -286,6 +286,7 @@
     void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
     void setCustomPointerIcon(const SpriteIcon& icon);
     void setMotionClassifierEnabled(bool enabled);
+    void notifyPointerDisplayIdChanged();
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -1494,6 +1495,18 @@
     mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
 }
 
+void NativeInputManager::notifyPointerDisplayIdChanged() {
+    int32_t pointerDisplayId = getPointerDisplayId();
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+        mLocked.pointerDisplayId = pointerDisplayId;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
 // ----------------------------------------------------------------------------
 
 static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
@@ -1573,6 +1586,13 @@
     return result;
 }
 
+static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                           jint deviceId, jint locationKeyCode) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    return (jint)im->getInputManager()->getReader().getKeyCodeForKeyLocation(deviceId,
+                                                                             locationKeyCode);
+}
+
 static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
                                        const std::shared_ptr<InputChannel>& inputChannel,
                                        void* data) {
@@ -1595,7 +1615,7 @@
 
     if (!inputChannel.ok()) {
         std::string message = inputChannel.error().message();
-        message += StringPrintf(" Status=%d", inputChannel.error().code());
+        message += StringPrintf(" Status=%d", static_cast<int>(inputChannel.error().code()));
         jniThrowRuntimeException(env, message.c_str());
         return nullptr;
     }
@@ -1629,7 +1649,7 @@
 
     if (!inputChannel.ok()) {
         std::string message = inputChannel.error().message();
-        message += StringPrintf(" Status=%d", inputChannel.error().code());
+        message += StringPrintf(" Status=%d", static_cast<int>(inputChannel.error().code()));
         jniThrowRuntimeException(env, message.c_str());
         return nullptr;
     }
@@ -2186,6 +2206,18 @@
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 }
 
+static void nativeNotifyPointerDisplayIdChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->notifyPointerDisplayIdChanged();
+}
+
+static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                                         jint displayId, jboolean isEligible) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->getInputManager()->getDispatcher().setDisplayEligibilityForPointerCapture(displayId,
+                                                                                  isEligible);
+}
+
 static void nativeChangeUniqueIdAssociation(JNIEnv* env, jclass /* clazz */, jlong ptr) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
     im->getInputManager()->getReader().requestRefreshConfiguration(
@@ -2312,6 +2344,7 @@
         {"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState},
         {"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState},
         {"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys},
+        {"nativeGetKeyCodeForKeyLocation", "(JII)I", (void*)nativeGetKeyCodeForKeyLocation},
         {"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;",
          (void*)nativeCreateInputChannel},
         {"nativeCreateInputMonitor", "(JIZLjava/lang/String;I)Landroid/view/InputChannel;",
@@ -2370,6 +2403,9 @@
         {"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay},
         {"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged},
         {"nativeChangeUniqueIdAssociation", "(J)V", (void*)nativeChangeUniqueIdAssociation},
+        {"nativeNotifyPointerDisplayIdChanged", "(J)V", (void*)nativeNotifyPointerDisplayIdChanged},
+        {"nativeSetDisplayEligibilityForPointerCapture", "(JIZ)V",
+         (void*)nativeSetDisplayEligibilityForPointerCapture},
         {"nativeSetMotionClassifierEnabled", "(JZ)V", (void*)nativeSetMotionClassifierEnabled},
         {"nativeGetSensorList", "(JI)[Landroid/hardware/input/InputSensorInfo;",
          (void*)nativeGetSensorList},
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index f66f119..0da8f7e 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -51,6 +51,7 @@
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 #include "gnss/AGnss.h"
+#include "gnss/AGnssRil.h"
 #include "gnss/GnssAntennaInfoCallback.h"
 #include "gnss/GnssBatching.h"
 #include "gnss/GnssConfiguration.h"
@@ -76,8 +77,6 @@
 static jmethodID method_psdsDownloadRequest;
 static jmethodID method_reportNiNotification;
 static jmethodID method_requestLocation;
-static jmethodID method_requestRefLocation;
-static jmethodID method_requestSetID;
 static jmethodID method_requestUtcTime;
 static jmethodID method_reportGnssServiceDied;
 static jmethodID method_reportGnssPowerStats;
@@ -126,7 +125,6 @@
 using android::hardware::hidl_death_recipient;
 
 using android::hardware::gnss::V1_0::GnssLocationFlags;
-using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnssNavigationMessage;
 using android::hardware::gnss::V1_0::IGnssNavigationMessageCallback;
 using android::hardware::gnss::V1_0::IGnssNi;
@@ -158,8 +156,6 @@
 using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
 using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
 using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
-using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
 
 using IMeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
 using IMeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::IMeasurementCorrections;
@@ -175,7 +171,9 @@
 using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPowerIndicationCallback;
 using android::hardware::gnss::PsdsType;
+
 using IAGnssAidl = android::hardware::gnss::IAGnss;
+using IAGnssRilAidl = android::hardware::gnss::IAGnssRil;
 using IGnssAidl = android::hardware::gnss::IGnss;
 using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
 using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
@@ -208,8 +206,6 @@
 sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
 sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
 sp<IGnssXtra> gnssXtraIface = nullptr;
-sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
-sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssPowerIndication> gnssPowerIndicationIface = nullptr;
 sp<IMeasurementCorrections_V1_0> gnssCorrectionsIface_V1_0 = nullptr;
@@ -224,6 +220,7 @@
 std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
 std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr;
 std::unique_ptr<android::gnss::GnssDebugInterface> gnssDebugIface = nullptr;
+std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -232,6 +229,11 @@
 namespace {
 
 // Returns true if location has lat/long information.
+bool hasLatLong(const GnssLocationAidl& location) {
+    return (location.gnssLocationFlags & GnssLocationAidl::HAS_LAT_LONG) != 0;
+}
+
+// Returns true if location has lat/long information.
 bool hasLatLong(const GnssLocation_V1_0& location) {
     return (static_cast<uint32_t>(location.gnssLocationFlags) &
             GnssLocationFlags::HAS_LAT_LONG) != 0;
@@ -248,6 +250,36 @@
     return value ? JNI_TRUE : JNI_FALSE;
 }
 
+static GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+                                           jdouble longitudeDegrees, jdouble altitudeMeters,
+                                           jfloat speedMetersPerSec, jfloat bearingDegrees,
+                                           jfloat horizontalAccuracyMeters,
+                                           jfloat verticalAccuracyMeters,
+                                           jfloat speedAccuracyMetersPerSecond,
+                                           jfloat bearingAccuracyDegrees, jlong timestamp,
+                                           jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+                                           jdouble elapsedRealtimeUncertaintyNanos) {
+    GnssLocationAidl location;
+    location.gnssLocationFlags = static_cast<int>(gnssLocationFlags);
+    location.latitudeDegrees = static_cast<double>(latitudeDegrees);
+    location.longitudeDegrees = static_cast<double>(longitudeDegrees);
+    location.altitudeMeters = static_cast<double>(altitudeMeters);
+    location.speedMetersPerSec = static_cast<double>(speedMetersPerSec);
+    location.bearingDegrees = static_cast<double>(bearingDegrees);
+    location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters);
+    location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters);
+    location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond);
+    location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees);
+    location.timestampMillis = static_cast<uint64_t>(timestamp);
+
+    location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags);
+    location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+    location.elapsedRealtime.timeUncertaintyNs =
+            static_cast<double>(elapsedRealtimeUncertaintyNanos);
+
+    return location;
+}
+
 static GnssLocation_V1_0 createGnssLocation_V1_0(
         jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
         jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
@@ -298,7 +330,8 @@
     Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
     Return<void> gnssStatusCb(const IGnssCallback_V1_0::GnssStatusValue status) override;
     Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override {
-        return gnssSvStatusCbImpl(svStatus);
+        return gnssSvStatusCbImpl<IGnssCallback_V1_0::GnssSvStatus, IGnssCallback_V1_0::GnssSvInfo>(
+                svStatus);
     }
     Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
     Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
@@ -318,73 +351,67 @@
     Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
     Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
     Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
-        return gnssSvStatusCbImpl(svInfoList);
+        return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_0::GnssSvInfo>,
+                                  IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
     }
 
     // New in 2.1
     Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
-        return gnssSvStatusCbImpl(svInfoList);
+        return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_1::GnssSvInfo>,
+                                  IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
     }
     Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
 
     // TODO: Reconsider allocation cost vs threadsafety on these statics
     static const char* sNmeaString;
     static size_t sNmeaStringLength;
+
+    template <class T>
+    static Return<void> gnssLocationCbImpl(const T& location);
+
+    template <class T_list, class T_sv_info>
+    static Return<void> gnssSvStatusCbImpl(const T_list& svStatus);
+
 private:
-    template<class T>
-    Return<void> gnssLocationCbImpl(const T& location);
-
-    template<class T>
-    Return<void> gnssSvStatusCbImpl(const T& svStatus);
-
-    template<class T>
-    uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
+    template <class T>
+    static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
         return 0;
     }
 
-    template<class T>
-    double getBasebandCn0DbHz(const T& svStatus, size_t i) {
+    template <class T>
+    static double getBasebandCn0DbHz(const T& svStatus, size_t i) {
         return 0.0;
     }
 
-    uint32_t getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
-        return svStatus.numSvs;
-    }
-
-    uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) {
+    template <class T>
+    static uint32_t getGnssSvInfoListSize(const T& svInfoList) {
         return svInfoList.size();
     }
 
-    uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) {
-        return svInfoList.size();
+    static const IGnssCallbackAidl::GnssSvInfo& getGnssSvInfoOfIndex(
+            const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
+        return svInfoList[i];
     }
 
-    const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+    static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
             const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
         return svStatus.gnssSvList.data()[i];
     }
 
-    const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+    static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
             const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
         return svInfoList[i].v1_0;
     }
 
-    const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+    static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
             const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
         return svInfoList[i].v2_0.v1_0;
     }
 
-    uint32_t getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
-        return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
-    }
-
-    uint32_t getConstellationType(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
+    template <class T>
+    static uint32_t getConstellationType(const T& svInfoList, size_t i) {
         return static_cast<uint32_t>(svInfoList[i].constellation);
     }
-
-    uint32_t getConstellationType(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
-        return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
-    }
 };
 
 Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
@@ -441,14 +468,43 @@
     return SVID_FLAGS_HAS_BASEBAND_CN0;
 }
 
+template <>
+uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(
+        const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) {
+    return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template <>
+double GnssCallback::getBasebandCn0DbHz(
+        const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
+    return svInfoList[i].basebandCN0DbHz;
+}
+
 template<>
 double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList,
         size_t i) {
     return svInfoList[i].basebandCN0DbHz;
 }
 
-template<class T>
-Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) {
+template <>
+uint32_t GnssCallback::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
+    return svStatus.numSvs;
+}
+
+template <>
+uint32_t GnssCallback::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus,
+                                            size_t i) {
+    return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
+}
+
+template <>
+uint32_t GnssCallback::getConstellationType(
+        const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+    return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
+}
+
+template <class T_list, class T_sv_info>
+Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) {
     JNIEnv* env = getJniEnv();
 
     uint32_t listSize = getGnssSvInfoListSize(svStatus);
@@ -476,7 +532,7 @@
             CONSTELLATION_TYPE_SHIFT_WIDTH = 8
         };
 
-        const IGnssCallback_V1_0::GnssSvInfo& info = getGnssSvInfoOfIndex(svStatus, i);
+        const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i);
         svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
             (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
             static_cast<uint32_t>(info.svFlag);
@@ -586,6 +642,16 @@
 class GnssCallbackAidl : public android::hardware::gnss::BnGnssCallback {
 public:
     Status gnssSetCapabilitiesCb(const int capabilities) override;
+    Status gnssStatusCb(const GnssStatusValue status) override;
+    Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
+    Status gnssLocationCb(const GnssLocationAidl& location) override;
+    Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override;
+    Status gnssAcquireWakelockCb() override;
+    Status gnssReleaseWakelockCb() override;
+    Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override;
+    Status gnssRequestTimeCb() override;
+    Status gnssRequestLocationCb(const bool independentFromGnss,
+                                 const bool isUserEmergency) override;
 };
 
 Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
@@ -596,6 +662,76 @@
     return Status::ok();
 }
 
+Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) {
+    GnssCallback::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) {
+    GnssCallback::gnssLocationCbImpl<GnssLocationAidl>(location);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
+    JNIEnv* env = getJniEnv();
+    /*
+     * The Java code will call back to read these values.
+     * We do this to avoid creating unnecessary String objects.
+     */
+    GnssCallback::sNmeaString = nmea.c_str();
+    GnssCallback::sNmeaStringLength = nmea.size();
+
+    env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssAcquireWakelockCb() {
+    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssReleaseWakelockCb() {
+    release_wake_lock(WAKE_LOCK_NAME);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) {
+    ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str());
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
+    jstring jstringName = env->NewStringUTF(info.name.c_str());
+    env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
+    if (jstringName) {
+        env->DeleteLocalRef(jstringName);
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestTimeCb() {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss,
+                                               const bool isUserEmergency) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
+                        boolToJbool(isUserEmergency));
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
 /*
  * GnssPowerIndicationCallback class implements the callback methods for the IGnssPowerIndication
  * interface.
@@ -770,34 +906,14 @@
     return result;
 }
 
-/*
- * AGnssRilCallback implements the callback methods required by the AGnssRil
- * interface.
- */
-struct AGnssRilCallback : IAGnssRilCallback {
-    Return<void> requestSetIdCb(uint32_t setIdFlag) override;
-    Return<void> requestRefLocCb() override;
-};
-
-Return<void> AGnssRilCallback::requestSetIdCb(uint32_t setIdFlag) {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdFlag);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-Return<void> AGnssRilCallback::requestRefLocCb() {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
 /* Initializes the GNSS service handle. */
 static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
     gnssHalAidl = waitForVintfService<IGnssAidl>();
     if (gnssHalAidl != nullptr) {
-        ALOGD("Successfully got GNSS AIDL handle.");
+        ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion());
+        if (gnssHalAidl->getInterfaceVersion() >= 2) {
+            return;
+        }
     }
 
     ALOGD("Trying IGnss_V2_1::getService()");
@@ -848,8 +964,6 @@
     method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
             "(IIIIILjava/lang/String;Ljava/lang/String;II)V");
     method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
-    method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V");
-    method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
     method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
     method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
     method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
@@ -927,6 +1041,7 @@
     gnss::GnssMeasurement_class_init_once(env, clazz);
     gnss::GnssNavigationMessage_class_init_once(env, clazz);
     gnss::AGnss_class_init_once(env, clazz);
+    gnss::AGnssRil_class_init_once(env, clazz);
     gnss::Utils_class_init_once(env);
 }
 
@@ -952,15 +1067,17 @@
 
     // TODO: linkToDeath for AIDL HAL
 
-    gnssHalDeathRecipient = new GnssDeathRecipient();
-    hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
-    if (!linked.isOk()) {
-        ALOGE("Transaction error in linking to GnssHAL death: %s",
-                linked.description().c_str());
-    } else if (!linked) {
-        ALOGW("Unable to link to GnssHal death notifications");
-    } else {
-        ALOGD("Link to death notification successful");
+    if (gnssHal != nullptr) {
+        gnssHalDeathRecipient = new GnssDeathRecipient();
+        hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to GnssHAL death: %s",
+                  linked.description().c_str());
+        } else if (!linked) {
+            ALOGW("Unable to link to GnssHal death notifications");
+        } else {
+            ALOGD("Link to death notification successful");
+        }
     }
 
     if (gnssHalAidl != nullptr) {
@@ -980,20 +1097,21 @@
         }
     }
 
-    if (gnssHal_V2_0 != nullptr) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<IAGnssRilAidl> agnssRilAidl;
+        auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) {
+            agnssRilIface = std::make_unique<gnss::AGnssRil>(agnssRilAidl);
+        }
+    } else if (gnssHal_V2_0 != nullptr) {
         auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
-        if (!agnssRil_V2_0.isOk()) {
-            ALOGD("Unable to get a handle to AGnssRil_V2_0");
-        } else {
-            agnssRilIface_V2_0 = agnssRil_V2_0;
-            agnssRilIface = agnssRilIface_V2_0;
+        if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) {
+            agnssRilIface = std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0);
         }
     } else if (gnssHal != nullptr) {
         auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
-        if (!agnssRil_V1_0.isOk()) {
-            ALOGD("Unable to get a handle to AGnssRil");
-        } else {
-            agnssRilIface = agnssRil_V1_0;
+        if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) {
+            agnssRilIface = std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0);
         }
     }
 
@@ -1163,7 +1281,7 @@
             gnssConfigurationIface =
                     std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
         }
-    } else {
+    } else if (gnssHal != nullptr) {
         auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
         if (checkHidlReturn(gnssConfiguration,
                             "Unable to get a handle to GnssConfiguration_V1_0")) {
@@ -1229,7 +1347,7 @@
 }
 
 static jboolean android_location_gnss_hal_GnssNative_is_supported(JNIEnv* /* env */, jclass) {
-    return (gnssHal != nullptr) ?  JNI_TRUE : JNI_FALSE;
+    return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported(
@@ -1263,26 +1381,26 @@
         return JNI_FALSE;
     }
 
-    Return<bool> result = false;
-
     // Set top level IGnss.hal callback.
-    sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
-    if (gnssHal_V2_1 != nullptr) {
-        result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
-    } else if (gnssHal_V2_0 != nullptr) {
-        result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
-    } else if (gnssHal_V1_1 != nullptr) {
-        result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
-    } else if (gnssHal != nullptr) {
-        result = gnssHal->setCallback(gnssCbIface);
+    if (gnssHal != nullptr) {
+        Return<bool> result = false;
+        sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
+        if (gnssHal_V2_1 != nullptr) {
+            result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
+        } else if (gnssHal_V2_0 != nullptr) {
+            result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
+        } else if (gnssHal_V1_1 != nullptr) {
+            result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
+        } else {
+            result = gnssHal->setCallback(gnssCbIface);
+        }
+        if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
+            return JNI_FALSE;
+        }
     }
 
-    if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
-        return JNI_FALSE;
-    }
-
-    sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
     if (gnssHalAidl != nullptr) {
+        sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
         auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
         if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
             return JNI_FALSE;
@@ -1298,7 +1416,7 @@
         }
     } else if (gnssXtraIface != nullptr) {
         sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
-        result = gnssXtraIface->setCallback(gnssXtraCbIface);
+        auto result = gnssXtraIface->setCallback(gnssXtraCbIface);
         if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
             gnssXtraIface = nullptr;
         } else {
@@ -1328,12 +1446,9 @@
         ALOGI("Unable to initialize IGnssNi interface.");
     }
 
-    // Set IAGnssRil.hal callback.
-    sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
-    if (agnssRilIface != nullptr) {
-        auto status = agnssRilIface->setCallback(aGnssRilCbIface);
-        checkHidlReturn(status, "IAGnssRil setCallback() failed.");
-    } else {
+    // Set IAGnssRil callback.
+    if (agnssRilIface == nullptr ||
+        !agnssRilIface->setCallback(std::make_unique<gnss::AGnssRilCallback>())) {
         ALOGI("Unable to initialize IAGnssRil interface.");
     }
 
@@ -1341,20 +1456,20 @@
     if (gnssVisibilityControlIface != nullptr) {
         sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
                 new GnssVisibilityControlCallback();
-        result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
+        auto result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
         checkHidlReturn(result, "IGnssVisibilityControl setCallback() failed.");
     }
 
     // Set IMeasurementCorrections.hal callback.
     if (gnssCorrectionsIface_V1_1 != nullptr) {
-            sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
-                    new MeasurementCorrectionsCallback();
-            result = gnssCorrectionsIface_V1_1->setCallback(gnssCorrectionsIfaceCbIface);
-            checkHidlReturn(result, "IMeasurementCorrections 1.1 setCallback() failed.");
+        sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
+                new MeasurementCorrectionsCallback();
+        auto result = gnssCorrectionsIface_V1_1->setCallback(gnssCorrectionsIfaceCbIface);
+        checkHidlReturn(result, "IMeasurementCorrections 1.1 setCallback() failed.");
     } else if (gnssCorrectionsIface_V1_0 != nullptr) {
         sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
                 new MeasurementCorrectionsCallback();
-        result = gnssCorrectionsIface_V1_0->setCallback(gnssCorrectionsIfaceCbIface);
+        auto result = gnssCorrectionsIface_V1_0->setCallback(gnssCorrectionsIfaceCbIface);
         checkHidlReturn(result, "IMeasurementCorrections 1.0 setCallback() failed.");
     } else {
         ALOGI("Unable to find IMeasurementCorrections.");
@@ -1388,6 +1503,15 @@
 static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
         JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
         jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->setPositionMode(static_cast<IGnssAidl::GnssPositionMode>(mode),
+                                                   static_cast<IGnssAidl::GnssPositionRecurrence>(
+                                                           recurrence),
+                                                   min_interval, preferred_accuracy, preferred_time,
+                                                   low_power_mode);
+        return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
+    }
+
     Return<bool> result = false;
     if (gnssHal_V1_1 != nullptr) {
          result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
@@ -1408,6 +1532,11 @@
 }
 
 static jboolean android_location_gnss_hal_GnssNative_start(JNIEnv* /* env */, jclass) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->start();
+        return checkAidlStatus(status, "IGnssAidl start() failed.");
+    }
+
     if (gnssHal == nullptr) {
         return JNI_FALSE;
     }
@@ -1417,6 +1546,11 @@
 }
 
 static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jclass) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->stop();
+        return checkAidlStatus(status, "IGnssAidl stop() failed.");
+    }
+
     if (gnssHal == nullptr) {
         return JNI_FALSE;
     }
@@ -1427,6 +1561,12 @@
 
 static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
                                                                     jint flags) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags));
+        checkAidlStatus(status, "IGnssAidl deleteAidingData() failed.");
+        return;
+    }
+
     if (gnssHal == nullptr) {
         return;
     }
@@ -1436,31 +1576,13 @@
 }
 
 static void android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid(
-        JNIEnv* /* env */, jclass, jint type, jint mcc, jint mnc, jint lac, jint cid) {
-    IAGnssRil_V1_0::AGnssRefLocation location;
-
+        JNIEnv* /* env */, jclass, jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac,
+        jint pcid, jint arfcn) {
     if (agnssRilIface == nullptr) {
         ALOGE("%s: IAGnssRil interface not available.", __func__);
         return;
     }
-
-    switch (static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type)) {
-        case IAGnssRil_V1_0::AGnssRefLocationType::GSM_CELLID:
-        case IAGnssRil_V1_0::AGnssRefLocationType::UMTS_CELLID:
-          location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type);
-          location.cellID.mcc = mcc;
-          location.cellID.mnc = mnc;
-          location.cellID.lac = lac;
-          location.cellID.cid = cid;
-          break;
-        default:
-            ALOGE("Neither a GSM nor a UMTS cellid (%s:%d).", __FUNCTION__, __LINE__);
-            return;
-            break;
-    }
-
-    auto result = agnssRilIface->setRefLocation(location);
-    checkHidlReturn(result, "IAGnssRil setRefLocation() failed.");
+    agnssRilIface->setRefLocation(type, mcc, mnc, lac, cid, tac, pcid, arfcn);
 }
 
 static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass, jint type,
@@ -1469,10 +1591,7 @@
         ALOGE("%s: IAGnssRil interface not available.", __func__);
         return;
     }
-
-    ScopedJniString jniSetId{env, setid_string};
-    auto result = agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
-    checkHidlReturn(result, "IAGnssRil setSetId() failed.");
+    agnssRilIface->setSetId(type, setid_string);
 }
 
 static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass,
@@ -1490,10 +1609,15 @@
 static void android_location_gnss_hal_GnssNative_inject_time(JNIEnv* /* env */, jclass, jlong time,
                                                              jlong timeReference,
                                                              jint uncertainty) {
-    if (gnssHal == nullptr) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty);
+        checkAidlStatus(status, "IGnssAidl injectTime() failed.");
         return;
     }
 
+    if (gnssHal == nullptr) {
+        return;
+    }
     auto result = gnssHal->injectTime(time, timeReference, uncertainty);
     checkHidlReturn(result, "IGnss injectTime() failed.");
 }
@@ -1505,6 +1629,19 @@
         jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
         jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
         jdouble elapsedRealtimeUncertaintyNanos) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        GnssLocationAidl location =
+                createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                   altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                   horizontalAccuracyMeters, verticalAccuracyMeters,
+                                   speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+                                   elapsedRealtimeFlags, elapsedRealtimeNanos,
+                                   elapsedRealtimeUncertaintyNanos);
+        auto status = gnssHalAidl->injectBestLocation(location);
+        checkAidlStatus(status, "IGnssAidl injectBestLocation() failed.");
+        return;
+    }
+
     if (gnssHal_V2_0 != nullptr) {
         GnssLocation_V2_0 location = createGnssLocation_V2_0(
                 gnssLocationFlags,
@@ -1546,15 +1683,31 @@
     ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
 }
 
-static void android_location_gnss_hal_GnssNative_inject_location(JNIEnv* /* env */, jclass,
-                                                                 jdouble latitude,
-                                                                 jdouble longitude,
-                                                                 jfloat accuracy) {
-    if (gnssHal == nullptr) {
+static void android_location_gnss_hal_GnssNative_inject_location(
+        JNIEnv* /* env */, jclass, jint gnssLocationFlags, jdouble latitudeDegrees,
+        jdouble longitudeDegrees, jdouble altitudeMeters, jfloat speedMetersPerSec,
+        jfloat bearingDegrees, jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
+        jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+        jdouble elapsedRealtimeUncertaintyNanos) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        GnssLocationAidl location =
+                createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                   altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                   horizontalAccuracyMeters, verticalAccuracyMeters,
+                                   speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+                                   elapsedRealtimeFlags, elapsedRealtimeNanos,
+                                   elapsedRealtimeUncertaintyNanos);
+        auto status = gnssHalAidl->injectLocation(location);
+        checkAidlStatus(status, "IGnssAidl injectLocation() failed.");
         return;
     }
 
-    auto result = gnssHal->injectLocation(latitude, longitude, accuracy);
+    if (gnssHal == nullptr) {
+        return;
+    }
+    auto result =
+            gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters);
     checkHidlReturn(result, "IGnss injectLocation() failed.");
 }
 
@@ -1675,31 +1828,12 @@
                                                                        jstring apn,
                                                                        jlong networkHandle,
                                                                        jshort capabilities) {
-    if (agnssRilIface_V2_0 != nullptr) {
-        ScopedJniString jniApn{env, apn};
-        IAGnssRil_V2_0::NetworkAttributes networkAttributes = {
-            .networkHandle = static_cast<uint64_t>(networkHandle),
-            .isConnected = static_cast<bool>(connected),
-            .capabilities = static_cast<uint16_t>(capabilities),
-            .apn = jniApn
-        };
-
-        auto result = agnssRilIface_V2_0->updateNetworkState_2_0(networkAttributes);
-        checkHidlReturn(result, "IAGnssRil updateNetworkState_2_0() failed.");
-    } else if (agnssRilIface != nullptr) {
-        ScopedJniString jniApn{env, apn};
-        hidl_string hidlApn{jniApn};
-        auto result = agnssRilIface->updateNetworkState(connected,
-                static_cast<IAGnssRil_V1_0::NetworkType>(type), roaming);
-        checkHidlReturn(result, "IAGnssRil updateNetworkState() failed.");
-
-        if (!hidlApn.empty()) {
-            result = agnssRilIface->updateNetworkAvailability(available, hidlApn);
-            checkHidlReturn(result, "IAGnssRil updateNetworkAvailability() failed.");
-        }
-    } else {
+    if (agnssRilIface == nullptr) {
         ALOGE("%s: IAGnssRil interface not available.", __func__);
+        return;
     }
+    agnssRilIface->updateNetworkState(connected, type, roaming, available, apn, networkHandle,
+                                      capabilities);
 }
 
 static jboolean android_location_gnss_hal_GnssNative_is_geofence_supported(JNIEnv* /* env */,
@@ -2139,11 +2273,12 @@
 }
 
 static jboolean android_location_gnss_hal_GnssNative_start_batch(JNIEnv*, jclass, jlong periodNanos,
+                                                                 jfloat minUpdateDistanceMeters,
                                                                  jboolean wakeOnFifoFull) {
     if (gnssBatchingIface == nullptr) {
         return JNI_FALSE; // batching not supported
     }
-    return gnssBatchingIface->start(periodNanos, wakeOnFifoFull);
+    return gnssBatchingIface->start(periodNanos, minUpdateDistanceMeters, wakeOnFifoFull);
 }
 
 static void android_location_gnss_hal_GnssNative_flush_batch(JNIEnv*, jclass) {
@@ -2207,7 +2342,7 @@
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_time)},
         {"native_inject_best_location", "(IDDDFFFFFFJIJD)V",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_best_location)},
-        {"native_inject_location", "(DDF)V",
+        {"native_inject_location", "(IDDDFFFFFFJIJD)V",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_location)},
         {"native_supports_psds", "()Z",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_supports_psds)},
@@ -2215,7 +2350,7 @@
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_psds_data)},
         {"native_agps_set_id", "(ILjava/lang/String;)V",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_agps_set_id)},
-        {"native_agps_set_ref_location_cellid", "(IIIII)V",
+        {"native_agps_set_ref_location_cellid", "(IIIIJIII)V",
          reinterpret_cast<void*>(
                  android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid)},
         {"native_set_agps_server", "(ILjava/lang/String;I)V",
@@ -2233,7 +2368,7 @@
         /* name, signature, funcPtr */
         {"native_get_batch_size", "()I",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_get_batch_size)},
-        {"native_start_batch", "(JZ)Z",
+        {"native_start_batch", "(JFZ)Z",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start_batch)},
         {"native_flush_batch", "()V",
          reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_flush_batch)},
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 546b075..b484796 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -61,8 +61,8 @@
 static struct {
     jfieldID startAmplitude;
     jfieldID endAmplitude;
-    jfieldID startFrequency;
-    jfieldID endFrequency;
+    jfieldID startFrequencyHz;
+    jfieldID endFrequencyHz;
     jfieldID duration;
 } sRampClassInfo;
 
@@ -157,8 +157,8 @@
             static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startAmplitude));
     pwle.endAmplitude = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endAmplitude));
     pwle.startFrequency =
-            static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequency));
-    pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequency));
+            static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequencyHz));
+    pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequencyHz));
     pwle.duration = static_cast<int32_t>(env->GetIntField(ramp, sRampClassInfo.duration));
     return pwle;
 }
@@ -363,7 +363,7 @@
 }
 
 static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
-                                jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
+                                jobject vibratorInfoBuilder) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
     if (wrapper == nullptr) {
         ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
@@ -437,9 +437,9 @@
         env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
                                  reinterpret_cast<jfloat*>(amplitudes.data()));
     }
-    jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
-                                              minFrequency, resonantFrequency, frequencyResolution,
-                                              suggestedSafeRange, maxAmplitudes);
+    jobject frequencyMapping =
+            env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, resonantFrequency,
+                           minFrequency, frequencyResolution, maxAmplitudes);
     env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
                           frequencyMapping);
 
@@ -463,7 +463,7 @@
         {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
         {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
         {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
-        {"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
+        {"getInfo", "(JLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
 };
 
 int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -481,13 +481,13 @@
     jclass rampClass = FindClassOrDie(env, "android/os/vibrator/RampSegment");
     sRampClassInfo.startAmplitude = GetFieldIDOrDie(env, rampClass, "mStartAmplitude", "F");
     sRampClassInfo.endAmplitude = GetFieldIDOrDie(env, rampClass, "mEndAmplitude", "F");
-    sRampClassInfo.startFrequency = GetFieldIDOrDie(env, rampClass, "mStartFrequency", "F");
-    sRampClassInfo.endFrequency = GetFieldIDOrDie(env, rampClass, "mEndFrequency", "F");
+    sRampClassInfo.startFrequencyHz = GetFieldIDOrDie(env, rampClass, "mStartFrequencyHz", "F");
+    sRampClassInfo.endFrequencyHz = GetFieldIDOrDie(env, rampClass, "mEndFrequencyHz", "F");
     sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I");
 
     jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping");
     sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
-    sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
+    sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFF[F)V");
 
     jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
     sVibratorInfoBuilderClassInfo.setCapabilities =
diff --git a/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp b/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
new file mode 100644
index 0000000..0202306
--- /dev/null
+++ b/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "TaskFpsCallbackController"
+
+#include <android/gui/BnFpsListener.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+namespace {
+
+struct {
+    jclass mClass;
+    jmethodID mDispatchOnFpsReported;
+} gCallbackClassInfo;
+
+struct TaskFpsCallback : public gui::BnFpsListener {
+    TaskFpsCallback(JNIEnv* env, jobject listener) : mListener(env->NewWeakGlobalRef(listener)) {}
+
+    binder::Status onFpsReported(float fps) override {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onFpsReported.");
+
+        jobject listener = env->NewGlobalRef(mListener);
+        if (listener == NULL) {
+            // Weak reference went out of scope
+            return binder::Status::ok();
+        }
+        env->CallStaticVoidMethod(gCallbackClassInfo.mClass,
+                                  gCallbackClassInfo.mDispatchOnFpsReported, listener,
+                                  static_cast<jfloat>(fps));
+        env->DeleteGlobalRef(listener);
+
+        if (env->ExceptionCheck()) {
+            ALOGE("TaskFpsCallback.onFpsReported() failed.");
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+        return binder::Status::ok();
+    }
+
+protected:
+    virtual ~TaskFpsCallback() {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->DeleteWeakGlobalRef(mListener);
+    }
+
+private:
+    jweak mListener;
+};
+
+jlong nativeRegister(JNIEnv* env, jclass clazz, jobject obj, jint taskId) {
+    TaskFpsCallback* callback = new TaskFpsCallback(env, obj);
+
+    if (SurfaceComposerClient::addFpsListener(taskId, callback) != OK) {
+        constexpr auto error_msg = "Couldn't addFpsListener";
+        ALOGE(error_msg);
+        jniThrowRuntimeException(env, error_msg);
+    }
+    callback->incStrong((void*)nativeRegister);
+
+    return reinterpret_cast<jlong>(callback);
+}
+
+void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<TaskFpsCallback> callback = reinterpret_cast<TaskFpsCallback*>(ptr);
+
+    if (SurfaceComposerClient::removeFpsListener(callback) != OK) {
+        constexpr auto error_msg = "Couldn't removeFpsListener";
+        ALOGE(error_msg);
+        jniThrowRuntimeException(env, error_msg);
+    }
+
+    callback->decStrong((void*)nativeRegister);
+}
+
+static const JNINativeMethod gMethods[] = {
+        /* name, signature, funcPtr */
+        {"nativeRegister", "(Landroid/window/IOnFpsCallbackListener;I)J", (void*)nativeRegister},
+        {"nativeUnregister", "(J)V", (void*)nativeUnregister}};
+
+} // namespace
+
+int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "com/android/server/wm/TaskFpsCallbackController",
+                                       gMethods, NELEM(gMethods));
+    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    jclass clazz = env->FindClass("android/window/TaskFpsCallback");
+    gCallbackClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
+    gCallbackClassInfo.mDispatchOnFpsReported =
+            env->GetStaticMethodID(clazz, "dispatchOnFpsReported",
+                                   "(Landroid/window/IOnFpsCallbackListener;F)V");
+    return 0;
+}
+
+} // namespace android
diff --git a/services/core/jni/gnss/AGnss.cpp b/services/core/jni/gnss/AGnss.cpp
index 00403d6..091fffd 100644
--- a/services/core/jni/gnss/AGnss.cpp
+++ b/services/core/jni/gnss/AGnss.cpp
@@ -43,7 +43,7 @@
 
 jboolean AGnss::dataConnOpen(JNIEnv* env, jlong networkHandle, jstring apn, jint apnIpType) {
     ScopedJniString jniApn{env, apn};
-    auto status = mIAGnss->dataConnOpen(networkHandle, String16(jniApn.c_str()),
+    auto status = mIAGnss->dataConnOpen(networkHandle, std::string(jniApn.c_str()),
                                         static_cast<IAGnss::ApnIpType>(apnIpType));
     return checkAidlStatus(status,
                            "IAGnssAidl dataConnOpen() failed. APN and its IP type not set.");
@@ -61,8 +61,8 @@
 
 jboolean AGnss::setServer(JNIEnv* env, jint type, jstring hostname, jint port) {
     ScopedJniString jniHostName{env, hostname};
-    auto status =
-            mIAGnss->setServer(static_cast<AGnssType>(type), String16(jniHostName.c_str()), port);
+    auto status = mIAGnss->setServer(static_cast<AGnssType>(type), std::string(jniHostName.c_str()),
+                                     port);
     return checkAidlStatus(status, "IAGnssAidl setServer() failed. Host name and port not set.");
 }
 
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
new file mode 100644
index 0000000..d760b4d
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "AGnssRilJni"
+
+#include "AGnssRil.h"
+
+#include "Utils.h"
+
+using android::hardware::gnss::IAGnssRil;
+using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
+using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
+
+namespace android::gnss {
+
+// Implementation of AGnssRil (AIDL HAL)
+
+AGnssRil::AGnssRil(const sp<IAGnssRil>& iAGnssRil) : mIAGnssRil(iAGnssRil) {
+    assert(mIAGnssRil != nullptr);
+}
+
+jboolean AGnssRil::setCallback(const std::unique_ptr<AGnssRilCallback>& callback) {
+    auto status = mIAGnssRil->setCallback(callback->getAidl());
+    return checkAidlStatus(status, "IAGnssRilAidl setCallback() failed.");
+}
+
+jboolean AGnssRil::setSetId(jint type, const jstring& setid_string) {
+    JNIEnv* env = getJniEnv();
+    ScopedJniString jniSetId{env, setid_string};
+    auto status = mIAGnssRil->setSetId((IAGnssRil::SetIDType)type, jniSetId.c_str());
+    return checkAidlStatus(status, "IAGnssRilAidl setSetId() failed.");
+}
+
+jboolean AGnssRil::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac,
+                                  jint pcid, jint arfcn) {
+    IAGnssRil::AGnssRefLocation location;
+    location.type = static_cast<IAGnssRil::AGnssRefLocationType>(type);
+
+    switch (location.type) {
+        case IAGnssRil::AGnssRefLocationType::GSM_CELLID:
+        case IAGnssRil::AGnssRefLocationType::UMTS_CELLID:
+        case IAGnssRil::AGnssRefLocationType::LTE_CELLID:
+        case IAGnssRil::AGnssRefLocationType::NR_CELLID:
+            location.cellID.mcc = mcc;
+            location.cellID.mnc = mnc;
+            location.cellID.lac = lac;
+            location.cellID.cid = cid;
+            location.cellID.tac = tac;
+            location.cellID.pcid = pcid;
+            location.cellID.arfcn = arfcn;
+            break;
+        default:
+            ALOGE("Unknown cellid (%s:%d).", __FUNCTION__, __LINE__);
+            return JNI_FALSE;
+            break;
+    }
+
+    auto status = mIAGnssRil->setRefLocation(location);
+    return checkAidlStatus(status, "IAGnssRilAidl dataConnClosed() failed.");
+}
+
+jboolean AGnssRil::updateNetworkState(jboolean connected, jint type, jboolean roaming,
+                                      jboolean available, const jstring& apn, jlong networkHandle,
+                                      jshort capabilities) {
+    JNIEnv* env = getJniEnv();
+    ScopedJniString jniApn{env, apn};
+    IAGnssRil::NetworkAttributes networkAttributes;
+    networkAttributes.networkHandle = static_cast<int64_t>(networkHandle),
+    networkAttributes.isConnected = static_cast<bool>(connected),
+    networkAttributes.capabilities = static_cast<int32_t>(capabilities),
+    networkAttributes.apn = jniApn.c_str();
+
+    auto result = mIAGnssRil->updateNetworkState(networkAttributes);
+    return checkAidlStatus(result, "IAGnssRilAidl updateNetworkState() failed.");
+}
+
+// Implementation of AGnssRil_V1_0
+
+AGnssRil_V1_0::AGnssRil_V1_0(const sp<IAGnssRil_V1_0>& iAGnssRil) : mAGnssRil_V1_0(iAGnssRil) {
+    assert(mIAGnssRil_V1_0 != nullptr);
+}
+
+jboolean AGnssRil_V1_0::setCallback(const std::unique_ptr<AGnssRilCallback>& callback) {
+    auto result = mAGnssRil_V1_0->setCallback(callback->getV1_0());
+    return checkHidlReturn(result, "IAGnssRil_V1_0 setCallback() failed.");
+}
+
+jboolean AGnssRil_V1_0::setSetId(jint type, const jstring& setid_string) {
+    JNIEnv* env = getJniEnv();
+    ScopedJniString jniSetId{env, setid_string};
+    auto result = mAGnssRil_V1_0->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
+    return checkHidlReturn(result, "IAGnssRil_V1_0 setSetId() failed.");
+}
+
+jboolean AGnssRil_V1_0::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint,
+                                       jint, jint) {
+    IAGnssRil_V1_0::AGnssRefLocation location;
+    switch (static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type)) {
+        case IAGnssRil_V1_0::AGnssRefLocationType::GSM_CELLID:
+        case IAGnssRil_V1_0::AGnssRefLocationType::UMTS_CELLID:
+            location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type);
+            location.cellID.mcc = mcc;
+            location.cellID.mnc = mnc;
+            location.cellID.lac = lac;
+            location.cellID.cid = cid;
+            break;
+        default:
+            ALOGE("Neither a GSM nor a UMTS cellid (%s:%d).", __FUNCTION__, __LINE__);
+            return JNI_FALSE;
+            break;
+    }
+
+    auto result = mAGnssRil_V1_0->setRefLocation(location);
+    return checkHidlReturn(result, "IAGnssRil_V1_0 setRefLocation() failed.");
+}
+
+jboolean AGnssRil_V1_0::updateNetworkState(jboolean connected, jint type, jboolean roaming,
+                                           jboolean available, const jstring& apn,
+                                           jlong networkHandle, jshort capabilities) {
+    JNIEnv* env = getJniEnv();
+    ScopedJniString jniApn{env, apn};
+    hardware::hidl_string hidlApn{jniApn};
+    hardware::Return<bool> result(false);
+
+    if (!hidlApn.empty()) {
+        result = mAGnssRil_V1_0->updateNetworkAvailability(available, hidlApn);
+        checkHidlReturn(result, "IAGnssRil_V1_0 updateNetworkAvailability() failed.");
+    }
+
+    result = mAGnssRil_V1_0->updateNetworkState(connected,
+                                                static_cast<IAGnssRil_V1_0::NetworkType>(type),
+                                                roaming);
+    return checkHidlReturn(result, "IAGnssRil_V1_0 updateNetworkState() failed.");
+}
+
+// Implementation of AGnssRil_V2_0
+
+AGnssRil_V2_0::AGnssRil_V2_0(const sp<IAGnssRil_V2_0>& iAGnssRil)
+      : AGnssRil_V1_0{iAGnssRil}, mAGnssRil_V2_0(iAGnssRil) {
+    assert(mIAGnssRil_V2_0 != nullptr);
+}
+
+jboolean AGnssRil_V2_0::updateNetworkState(jboolean connected, jint type, jboolean roaming,
+                                           jboolean available, const jstring& apn,
+                                           jlong networkHandle, jshort capabilities) {
+    JNIEnv* env = getJniEnv();
+    ScopedJniString jniApn{env, apn};
+    IAGnssRil_V2_0::NetworkAttributes networkAttributes =
+            {.networkHandle = static_cast<uint64_t>(networkHandle),
+             .isConnected = static_cast<bool>(connected),
+             .capabilities = static_cast<uint16_t>(capabilities),
+             .apn = jniApn.c_str()};
+
+    auto result = mAGnssRil_V2_0->updateNetworkState_2_0(networkAttributes);
+    return checkHidlReturn(result, "AGnssRil_V2_0 updateNetworkState_2_0() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/AGnssRil.h b/services/core/jni/gnss/AGnssRil.h
new file mode 100644
index 0000000..ce14a77d
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRil.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_AGNSSRIL_H
+#define _ANDROID_SERVER_GNSS_AGNSSRIL_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IAGnssRil.h>
+#include <android/hardware/gnss/2.0/IAGnssRil.h>
+#include <android/hardware/gnss/BnAGnssRil.h>
+#include <log/log.h>
+
+#include "AGnssRilCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class AGnssRilInterface {
+public:
+    virtual ~AGnssRilInterface() {}
+    virtual jboolean setCallback(const std::unique_ptr<AGnssRilCallback>& callback) = 0;
+    virtual jboolean setSetId(jint type, const jstring& setid_string) = 0;
+    virtual jboolean setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac,
+                                    jint pcid, jint arfcn) = 0;
+    virtual jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming,
+                                        jboolean available, const jstring& apn, jlong networkHandle,
+                                        jshort capabilities) = 0;
+};
+
+class AGnssRil : public AGnssRilInterface {
+public:
+    AGnssRil(const sp<android::hardware::gnss::IAGnssRil>& iAGnssRil);
+    jboolean setCallback(const std::unique_ptr<AGnssRilCallback>& callback) override;
+    jboolean setSetId(jint type, const jstring& setid_string) override;
+    jboolean setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac, jint pcid,
+                            jint arfcn) override;
+    jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available,
+                                const jstring& apn, jlong networkHandle,
+                                jshort capabilities) override;
+
+private:
+    const sp<android::hardware::gnss::IAGnssRil> mIAGnssRil;
+};
+
+class AGnssRil_V1_0 : public AGnssRilInterface {
+public:
+    AGnssRil_V1_0(const sp<android::hardware::gnss::V1_0::IAGnssRil>& iAGnssRil);
+    jboolean setCallback(const std::unique_ptr<AGnssRilCallback>& callback) override;
+    jboolean setSetId(jint type, const jstring& setid_string) override;
+    jboolean setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint, jint,
+                            jint) override;
+    jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available,
+                                const jstring& apn, jlong networkHandle,
+                                jshort capabilities) override;
+
+private:
+    const sp<android::hardware::gnss::V1_0::IAGnssRil> mAGnssRil_V1_0;
+};
+
+class AGnssRil_V2_0 : public AGnssRil_V1_0 {
+public:
+    AGnssRil_V2_0(const sp<android::hardware::gnss::V2_0::IAGnssRil>& iAGnssRil);
+    jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available,
+                                const jstring& apn, jlong networkHandle,
+                                jshort capabilities) override;
+
+private:
+    const sp<android::hardware::gnss::V2_0::IAGnssRil> mAGnssRil_V2_0;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_AGNSSRIL_H
diff --git a/services/core/jni/gnss/AGnssRilCallback.cpp b/services/core/jni/gnss/AGnssRilCallback.cpp
new file mode 100644
index 0000000..b63ccc2
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRilCallback.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AGnssRilCbJni"
+
+#include "AGnssRilCallback.h"
+
+namespace android::gnss {
+
+jmethodID method_requestSetID;
+jmethodID method_requestRefLocation;
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+void AGnssRil_class_init_once(JNIEnv* env, jclass clazz) {
+    method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
+    method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V");
+}
+
+Status AGnssRilCallbackAidl::requestSetIdCb(int setIdflag) {
+    AGnssRilCallbackUtil::requestSetIdCb(setIdflag);
+    return Status::ok();
+}
+
+Status AGnssRilCallbackAidl::requestRefLocCb() {
+    AGnssRilCallbackUtil::requestRefLocCb();
+    return Status::ok();
+}
+
+Return<void> AGnssRilCallback_V1_0::requestSetIdCb(uint32_t setIdflag) {
+    AGnssRilCallbackUtil::requestSetIdCb(setIdflag);
+    return Void();
+}
+
+Return<void> AGnssRilCallback_V1_0::requestRefLocCb() {
+    AGnssRilCallbackUtil::requestRefLocCb();
+    return Void();
+}
+
+void AGnssRilCallbackUtil::requestSetIdCb(int setIdflag) {
+    ALOGD("%s. setIdflag: %d, ", __func__, setIdflag);
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdflag);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void AGnssRilCallbackUtil::requestRefLocCb() {
+    ALOGD("%s.", __func__);
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/AGnssRilCallback.h b/services/core/jni/gnss/AGnssRilCallback.h
new file mode 100644
index 0000000..2d12089
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRilCallback.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_AGNSSRILCALLBACK_H
+#define _ANDROID_SERVER_GNSS_AGNSSRILCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IAGnssRil.h>
+#include <android/hardware/gnss/BnAGnssRilCallback.h>
+#include <log/log.h>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void AGnssRil_class_init_once(JNIEnv* env, jclass clazz);
+
+/*
+ * AGnssRilCallbackAidl class implements the callback methods required by the
+ * android::hardware::gnss::IAGnssRil interface.
+ */
+class AGnssRilCallbackAidl : public android::hardware::gnss::BnAGnssRilCallback {
+public:
+    binder::Status requestSetIdCb(int setIdflag) override;
+    binder::Status requestRefLocCb() override;
+};
+
+/*
+ * AGnssRilCallback_V1_0 implements callback methods required by the IAGnssRilCallback 1.0
+ * interface.
+ */
+class AGnssRilCallback_V1_0 : public android::hardware::gnss::V1_0::IAGnssRilCallback {
+public:
+    // Methods from ::android::hardware::gps::V1_0::IAGnssRilCallback follow.
+    hardware::Return<void> requestSetIdCb(uint32_t setIdflag) override;
+    hardware::Return<void> requestRefLocCb() override;
+};
+
+class AGnssRilCallback {
+public:
+    AGnssRilCallback() {}
+    sp<AGnssRilCallbackAidl> getAidl() {
+        if (callbackAidl == nullptr) {
+            callbackAidl = sp<AGnssRilCallbackAidl>::make();
+        }
+        return callbackAidl;
+    }
+
+    sp<AGnssRilCallback_V1_0> getV1_0() {
+        if (callbackV1_0 == nullptr) {
+            callbackV1_0 = sp<AGnssRilCallback_V1_0>::make();
+        }
+        return callbackV1_0;
+    }
+
+private:
+    sp<AGnssRilCallbackAidl> callbackAidl;
+    sp<AGnssRilCallback_V1_0> callbackV1_0;
+};
+
+struct AGnssRilCallbackUtil {
+    static void requestSetIdCb(int setIdflag);
+    static void requestRefLocCb();
+
+private:
+    AGnssRilCallbackUtil() = delete;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_AGNSSRILCALLBACK_H
\ No newline at end of file
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 63f5f52..d8de5a6 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -25,6 +25,8 @@
     srcs: [
         "AGnss.cpp",
         "AGnssCallback.cpp",
+        "AGnssRil.cpp",
+        "AGnssRilCallback.cpp",
         "GnssAntennaInfoCallback.cpp",
         "GnssBatching.cpp",
         "GnssBatchingCallback.cpp",
diff --git a/services/core/jni/gnss/GnssBatching.cpp b/services/core/jni/gnss/GnssBatching.cpp
index b66bf21..7f936b9a 100644
--- a/services/core/jni/gnss/GnssBatching.cpp
+++ b/services/core/jni/gnss/GnssBatching.cpp
@@ -47,9 +47,12 @@
     return size;
 }
 
-jboolean GnssBatching::start(long periodNanos, bool wakeOnFifoFull) {
-    int flags = (wakeOnFifoFull) ? IGnssBatching::WAKEUP_ON_FIFO_FULL : 0;
-    auto status = mIGnssBatching->start(periodNanos, flags);
+jboolean GnssBatching::start(long periodNanos, float minUpdateDistanceMeters, bool wakeOnFifoFull) {
+    IGnssBatching::Options options;
+    options.flags = (wakeOnFifoFull) ? IGnssBatching::WAKEUP_ON_FIFO_FULL : 0;
+    options.periodNanos = periodNanos;
+    options.minDistanceMeters = minUpdateDistanceMeters;
+    auto status = mIGnssBatching->start(options);
     return checkAidlStatus(status, "IGnssBatchingAidl start() failed.");
 }
 
@@ -88,9 +91,13 @@
     return static_cast<jint>(result);
 }
 
-jboolean GnssBatching_V1_0::start(long periodNanos, bool wakeOnFifoFull) {
+jboolean GnssBatching_V1_0::start(long periodNanos, float minUpdateDistanceMeters,
+                                  bool wakeOnFifoFull) {
     IGnssBatching_V1_0::Options options;
     options.periodNanos = periodNanos;
+    if (minUpdateDistanceMeters > 0) {
+        ALOGW("minUpdateDistanceMeters is not supported in 1.0 GNSS HAL.");
+    }
     if (wakeOnFifoFull) {
         options.flags = static_cast<uint8_t>(IGnssBatching_V1_0::Flag::WAKEUP_ON_FIFO_FULL);
     } else {
diff --git a/services/core/jni/gnss/GnssBatching.h b/services/core/jni/gnss/GnssBatching.h
index a98ca9b..eda02ce 100644
--- a/services/core/jni/gnss/GnssBatching.h
+++ b/services/core/jni/gnss/GnssBatching.h
@@ -38,7 +38,8 @@
     virtual ~GnssBatchingInterface() {}
     virtual jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) = 0;
     virtual jint getBatchSize() = 0;
-    virtual jboolean start(long periodNanos, bool wakeupOnFifoFull) = 0;
+    virtual jboolean start(long periodNanos, float minUpdateDistanceMeters,
+                           bool wakeupOnFifoFull) = 0;
     virtual jboolean stop() = 0;
     virtual jboolean flush() = 0;
     virtual jboolean cleanup() = 0;
@@ -49,7 +50,7 @@
     GnssBatching(const sp<android::hardware::gnss::IGnssBatching>& iGnssBatching);
     jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
     jint getBatchSize() override;
-    jboolean start(long periodNanos, bool wakeupOnFifoFull) override;
+    jboolean start(long periodNanos, float minUpdateDistanceMeters, bool wakeupOnFifoFull) override;
     jboolean stop() override;
     jboolean flush() override;
     jboolean cleanup() override;
@@ -63,7 +64,7 @@
     GnssBatching_V1_0(const sp<android::hardware::gnss::V1_0::IGnssBatching>& iGnssBatching);
     jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
     jint getBatchSize() override;
-    jboolean start(long periodNanos, bool wakeupOnFifoFull) override;
+    jboolean start(long periodNanos, float minUpdateDistanceMeters, bool wakeupOnFifoFull) override;
     jboolean stop() override;
     jboolean flush() override;
     jboolean cleanup() override;
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 34ae469..fbdeec6 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -27,12 +27,16 @@
 using hardware::gnss::GnssData;
 using hardware::gnss::GnssMeasurement;
 using hardware::gnss::SatellitePvt;
+using GnssAgc = hardware::gnss::GnssData::GnssAgc;
 
 namespace {
 jclass class_arrayList;
 jclass class_clockInfo;
 jclass class_correlationVectorBuilder;
+jclass class_gnssAgc;
+jclass class_gnssAgcBuilder;
 jclass class_gnssMeasurementsEvent;
+jclass class_gnssMeasurementsEventBuilder;
 jclass class_gnssMeasurement;
 jclass class_gnssClock;
 jclass class_positionEcef;
@@ -47,7 +51,16 @@
 jmethodID method_correlationVectorBuilderSetMagnitude;
 jmethodID method_correlationVectorBuilderSetSamplingStartMeters;
 jmethodID method_correlationVectorBuilderSetSamplingWidthMeters;
-jmethodID method_gnssMeasurementsEventCtor;
+jmethodID method_gnssAgcBuilderCtor;
+jmethodID method_gnssAgcBuilderSetLevelDb;
+jmethodID method_gnssAgcBuilderSetConstellationType;
+jmethodID method_gnssAgcBuilderSetCarrierFrequencyHz;
+jmethodID method_gnssAgcBuilderBuild;
+jmethodID method_gnssMeasurementsEventBuilderCtor;
+jmethodID method_gnssMeasurementsEventBuilderSetClock;
+jmethodID method_gnssMeasurementsEventBuilderSetMeasurements;
+jmethodID method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls;
+jmethodID method_gnssMeasurementsEventBuilderBuild;
 jmethodID method_gnssMeasurementsSetCorrelationVectors;
 jmethodID method_gnssMeasurementsSetSatellitePvt;
 jmethodID method_gnssClockCtor;
@@ -69,12 +82,55 @@
 void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) {
     method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData",
                                                     "(Landroid/location/GnssMeasurementsEvent;)V");
+
+    // Initialize GnssMeasurement related classes and methods
     jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
     class_gnssMeasurementsEvent = (jclass)env->NewGlobalRef(gnssMeasurementsEventClass);
-    method_gnssMeasurementsEventCtor =
-            env->GetMethodID(class_gnssMeasurementsEvent, "<init>",
-                             "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+    jclass gnssMeasurementsEventBuilderClass =
+            env->FindClass("android/location/GnssMeasurementsEvent$Builder");
+    class_gnssMeasurementsEventBuilder =
+            (jclass)env->NewGlobalRef(gnssMeasurementsEventBuilderClass);
+    method_gnssMeasurementsEventBuilderCtor =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "<init>", "()V");
+    method_gnssMeasurementsEventBuilderSetClock =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "setClock",
+                             "(Landroid/location/GnssClock;)"
+                             "Landroid/location/GnssMeasurementsEvent$Builder;");
+    method_gnssMeasurementsEventBuilderSetMeasurements =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "setMeasurements",
+                             "([Landroid/location/GnssMeasurement;)"
+                             "Landroid/location/GnssMeasurementsEvent$Builder;");
+    method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "setGnssAutomaticGainControls",
+                             "([Landroid/location/GnssAutomaticGainControl;)"
+                             "Landroid/location/GnssMeasurementsEvent$Builder;");
+    method_gnssMeasurementsEventBuilderBuild =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "build",
+                             "()Landroid/location/GnssMeasurementsEvent;");
 
+    // Initialize GnssAgc related classes and methods
+    jclass gnssAgcClass = env->FindClass("android/location/GnssAutomaticGainControl");
+    class_gnssAgc = (jclass)env->NewGlobalRef(gnssAgcClass);
+    jclass gnssAgcBuilderClass =
+            env->FindClass("android/location/GnssAutomaticGainControl$Builder");
+    class_gnssAgcBuilder = (jclass)env->NewGlobalRef(gnssAgcBuilderClass);
+    method_gnssAgcBuilderCtor = env->GetMethodID(class_gnssAgcBuilder, "<init>", "()V");
+    method_gnssAgcBuilderSetLevelDb =
+            env->GetMethodID(class_gnssAgcBuilder, "setLevelDb",
+                             "(D)"
+                             "Landroid/location/GnssAutomaticGainControl$Builder;");
+    method_gnssAgcBuilderSetConstellationType =
+            env->GetMethodID(class_gnssAgcBuilder, "setConstellationType",
+                             "(I)"
+                             "Landroid/location/GnssAutomaticGainControl$Builder;");
+    method_gnssAgcBuilderSetCarrierFrequencyHz =
+            env->GetMethodID(class_gnssAgcBuilder, "setCarrierFrequencyHz",
+                             "(J)"
+                             "Landroid/location/GnssAutomaticGainControl$Builder;");
+    method_gnssAgcBuilderBuild = env->GetMethodID(class_gnssAgcBuilder, "build",
+                                                  "()Landroid/location/GnssAutomaticGainControl;");
+
+    // Initialize GnssMeasurement related classes and methods
     jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
     class_gnssMeasurement = (jclass)env->NewGlobalRef(gnssMeasurementClass);
     method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
@@ -152,14 +208,25 @@
 }
 
 void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
-                        jobjectArray measurementArray) {
-    jobject gnssMeasurementsEvent =
-            env->NewObject(class_gnssMeasurementsEvent, method_gnssMeasurementsEventCtor, clock,
-                           measurementArray);
+                        jobjectArray measurementArray, jobjectArray gnssAgcArray) {
+    jobject gnssMeasurementsEventBuilderObject =
+            env->NewObject(class_gnssMeasurementsEventBuilder,
+                           method_gnssMeasurementsEventBuilderCtor);
+    env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                          method_gnssMeasurementsEventBuilderSetClock, clock);
+    env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                          method_gnssMeasurementsEventBuilderSetMeasurements, measurementArray);
+    env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                          method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
+                          gnssAgcArray);
+    jobject gnssMeasurementsEventObject =
+            env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                                  method_gnssMeasurementsEventBuilderBuild);
 
-    env->CallVoidMethod(callbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
+    env->CallVoidMethod(callbacksObj, method_reportMeasurementData, gnssMeasurementsEventObject);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    env->DeleteLocalRef(gnssMeasurementsEvent);
+    env->DeleteLocalRef(gnssMeasurementsEventBuilderObject);
+    env->DeleteLocalRef(gnssMeasurementsEventObject);
 }
 
 template <class T_Measurement, class T_Flags>
@@ -289,12 +356,17 @@
     JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor);
     translateGnssClock(env, data, gnssClockJavaObject);
     jobject clock = gnssClockJavaObject.get();
-
     jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements);
-    setMeasurementData(env, mCallbacksObj, clock, measurementArray);
+
+    jobjectArray gnssAgcArray = nullptr;
+    if (data.gnssAgcs.has_value()) {
+        gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs.value());
+    }
+    setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray);
 
     env->DeleteLocalRef(clock);
     env->DeleteLocalRef(measurementArray);
+    env->DeleteLocalRef(gnssAgcArray);
 }
 
 void GnssMeasurementCallbackAidl::translateSingleGnssMeasurement(JNIEnv* env,
@@ -436,6 +508,38 @@
     return gnssMeasurementArray;
 }
 
+jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
+        JNIEnv* env, const std::vector<std::optional<GnssAgc>>& agcs) {
+    if (agcs.size() == 0) {
+        return nullptr;
+    }
+
+    jobjectArray gnssAgcArray =
+            env->NewObjectArray(agcs.size(), class_gnssAgc, nullptr /* initialElement */);
+
+    for (uint16_t i = 0; i < agcs.size(); ++i) {
+        if (!agcs[i].has_value()) {
+            continue;
+        }
+        const GnssAgc& gnssAgc = agcs[i].value();
+
+        jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor);
+        env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
+                              gnssAgc.agcLevelDb);
+        env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetConstellationType,
+                              (int)gnssAgc.constellation);
+        env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetCarrierFrequencyHz,
+                              gnssAgc.carrierFrequencyHz);
+        jobject agcObject = env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderBuild);
+
+        env->SetObjectArrayElement(gnssAgcArray, i, agcObject);
+        env->DeleteLocalRef(agcBuilderObject);
+        env->DeleteLocalRef(agcObject);
+    }
+
+    return gnssAgcArray;
+}
+
 void GnssMeasurementCallbackAidl::translateGnssClock(JNIEnv* env, const GnssData& data,
                                                      JavaObject& object) {
     setElapsedRealtimeFields<ElapsedRealtime, ElapsedRealtime>(data.elapsedRealtime, object);
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 32200fd..9b346312 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -48,7 +48,7 @@
 void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz);
 
 void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
-                        jobjectArray measurementArray);
+                        jobjectArray measurementArray, jobjectArray gnssAgcArray);
 
 class GnssMeasurementCallbackAidl : public hardware::gnss::BnGnssMeasurementCallback {
 public:
@@ -62,6 +62,8 @@
 
     jobjectArray translateAllGnssMeasurements(
             JNIEnv* env, const std::vector<hardware::gnss::GnssMeasurement>& measurements);
+    jobjectArray translateAllGnssAgcs(
+            JNIEnv* env, const std::vector<std::optional<hardware::gnss::GnssData::GnssAgc>>& agcs);
 
     void translateAndSetGnssData(const hardware::gnss::GnssData& data);
 
@@ -139,7 +141,7 @@
     size_t count = getMeasurementCount(data);
     jobjectArray measurementArray =
             translateAllGnssMeasurements(env, data.measurements.data(), count);
-    setMeasurementData(env, mCallbacksObj, clock, measurementArray);
+    setMeasurementData(env, mCallbacksObj, clock, measurementArray, nullptr);
 
     env->DeleteLocalRef(clock);
     env->DeleteLocalRef(measurementArray);
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index d339ef1..ba5b3f5 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -64,6 +64,8 @@
 int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
 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_wm_TaskFpsCallbackController(JNIEnv* env);
 };
 
 using namespace android;
@@ -121,5 +123,7 @@
     register_android_server_stats_pull_StatsPullAtomService(env);
     register_android_server_sensor_SensorService(vm, env);
     register_android_server_companion_virtual_InputController(env);
+    register_android_server_app_GameManagerService(env);
+    register_com_android_server_wm_TaskFpsCallbackController(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 2f4dd57..baf2ede 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -58,6 +58,15 @@
                 <xs:element type="sensorDetails" name="proxSensor">
                     <xs:annotation name="final"/>
                 </xs:element>
+
+                <!-- Length of the ambient light horizon used to calculate the long & short term
+                estimates of ambient light in milliseconds.-->
+                <xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonLong">
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonShort">
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 5b2b87c..6f97431 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -18,6 +18,8 @@
 
   public class DisplayConfiguration {
     ctor public DisplayConfiguration();
+    method public final java.math.BigInteger getAmbientLightHorizonLong();
+    method public final java.math.BigInteger getAmbientLightHorizonShort();
     method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
     method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
     method public final com.android.server.display.config.SensorDetails getLightSensor();
@@ -29,6 +31,8 @@
     method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+    method public final void setAmbientLightHorizonLong(java.math.BigInteger);
+    method public final void setAmbientLightHorizonShort(java.math.BigInteger);
     method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index df9ab50..f19202a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -145,6 +145,10 @@
     private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_ENABLED =
             "preferential-network-service-enabled";
     private static final String TAG_USB_DATA_SIGNALING = "usb-data-signaling";
+    private static final String TAG_WIFI_MIN_SECURITY = "wifi-min-security";
+    private static final String TAG_SSID_ALLOWLIST = "ssid-allowlist";
+    private static final String TAG_SSID_DENYLIST = "ssid-denylist";
+    private static final String TAG_SSID = "ssid";
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
     private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -237,6 +241,14 @@
     // List of package names to keep cached.
     List<String> keepUninstalledPackages;
 
+    // The allowlist of SSIDs the device may connect to.
+    // By default, the allowlist restriction is deactivated.
+    List<String> mSsidAllowlist;
+
+    // The denylist of SSIDs the device may not connect to.
+    // By default, the denylist restriction is deactivated.
+    List<String> mSsidDenylist;
+
     // TODO: review implementation decisions with frameworks team
     boolean specifiesGlobalProxy = false;
     String globalProxySpec = null;
@@ -298,6 +310,8 @@
     private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
     boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
 
+    int mWifiMinimumSecurityLevel = DevicePolicyManager.WIFI_SECURITY_OPEN;
+
     ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
         this.info = info;
         this.isParent = isParent;
@@ -574,6 +588,15 @@
         if (mUsbDataSignalingEnabled != USB_DATA_SIGNALING_ENABLED_DEFAULT) {
             writeAttributeValueToXml(out, TAG_USB_DATA_SIGNALING, mUsbDataSignalingEnabled);
         }
+        if (mWifiMinimumSecurityLevel != DevicePolicyManager.WIFI_SECURITY_OPEN) {
+            writeAttributeValueToXml(out, TAG_WIFI_MIN_SECURITY, mWifiMinimumSecurityLevel);
+        }
+        if (mSsidAllowlist != null && !mSsidAllowlist.isEmpty()) {
+            writeAttributeValuesToXml(out, TAG_SSID_ALLOWLIST, TAG_SSID, mSsidAllowlist);
+        }
+        if (mSsidDenylist != null && !mSsidDenylist.isEmpty()) {
+            writeAttributeValuesToXml(out, TAG_SSID_DENYLIST, TAG_SSID, mSsidDenylist);
+        }
     }
 
     void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -826,6 +849,14 @@
             } else if (TAG_USB_DATA_SIGNALING.equals(tag)) {
                 mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
                         USB_DATA_SIGNALING_ENABLED_DEFAULT);
+            } else if (TAG_WIFI_MIN_SECURITY.equals(tag)) {
+                mWifiMinimumSecurityLevel = parser.getAttributeInt(null, ATTR_VALUE);
+            } else if (TAG_SSID_ALLOWLIST.equals(tag)) {
+                mSsidAllowlist = new ArrayList<>();
+                readAttributeValues(parser, TAG_SSID, mSsidAllowlist);
+            } else if (TAG_SSID_DENYLIST.equals(tag)) {
+                mSsidDenylist = new ArrayList<>();
+                readAttributeValues(parser, TAG_SSID, mSsidDenylist);
             } else {
                 Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
@@ -1184,5 +1215,14 @@
 
         pw.print("mUsbDataSignaling=");
         pw.println(mUsbDataSignalingEnabled);
+
+        pw.print("mWifiMinimumSecurityLevel=");
+        pw.println(mWifiMinimumSecurityLevel);
+
+        pw.print("mSsidAllowlist=");
+        pw.println(mSsidAllowlist);
+
+        pw.print("mSsidDenylist=");
+        pw.println(mSsidDenylist);
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 55ab8c3..9b87b9d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,16 +17,21 @@
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyDrawableResource;
 import android.app.admin.DevicePolicySafetyChecker;
+import android.app.admin.DevicePolicyStringResource;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.ManagedProfileProvisioningParams;
+import android.app.admin.ParcelableResource;
 import android.content.ComponentName;
 import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.server.SystemService;
 
+import java.util.List;
+
 /**
  * Defines the required interface for IDevicePolicyManager implemenation.
  *
@@ -161,4 +166,27 @@
     public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) {
         return false;
     }
+
+    @Override
+    public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){}
+
+    @Override
+    public void resetDrawables(@NonNull int[] drawableIds){}
+
+    @Override
+    public ParcelableResource getDrawable(
+            int drawableId, int drawableStyle, int drawableSource) {
+        return null;
+    }
+
+    @Override
+    public void setStrings(@NonNull List<DevicePolicyStringResource> strings){}
+
+    @Override
+    public void resetStrings(String[] stringIds){}
+
+    @Override
+    public ParcelableResource getString(String stringId) {
+        return null;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
new file mode 100644
index 0000000..9a98235
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -0,0 +1,508 @@
+/*
+ * 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.devicepolicy;
+
+import static android.app.admin.DevicePolicyResources.Drawable.Source.UPDATABLE_DRAWABLE_SOURCES;
+import static android.app.admin.DevicePolicyResources.Drawable.Style;
+import static android.app.admin.DevicePolicyResources.Drawable.Style.UPDATABLE_DRAWABLE_STYLES;
+import static android.app.admin.DevicePolicyResources.Drawable.UPDATABLE_DRAWABLE_IDS;
+import static android.app.admin.DevicePolicyResources.Strings.UPDATABLE_STRING_IDS;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyDrawableResource;
+import android.app.admin.DevicePolicyResources;
+import android.app.admin.DevicePolicyStringResource;
+import android.app.admin.ParcelableResource;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A helper class for {@link DevicePolicyManagerService} to store/retrieve updated device
+ * management resources.
+ */
+class DeviceManagementResourcesProvider {
+    private static final String TAG = "DevicePolicyManagerService";
+
+    private static final String UPDATED_RESOURCES_XML = "updated_resources.xml";
+    private static final String TAG_ROOT = "root";
+    private static final String TAG_DRAWABLE_STYLE_ENTRY = "drawable-style-entry";
+    private static final String TAG_DRAWABLE_SOURCE_ENTRY = "drawable-source-entry";
+    private static final String ATTR_DRAWABLE_STYLE_SIZE = "drawable-style-size";
+    private static final String ATTR_DRAWABLE_SOURCE_SIZE = "drawable-source-size";
+    private static final String ATTR_DRAWABLE_STYLE = "drawable-style";
+    private static final String ATTR_DRAWABLE_SOURCE = "drawable-source";
+    private static final String ATTR_DRAWABLE_ID = "drawable-id";
+    private static final String TAG_STRING_ENTRY = "string-entry";
+    private static final String ATTR_SOURCE_ID = "source-id";
+
+    /**
+     * Map of <drawable_id, <style_id, resource_value>>
+     */
+    private final Map<Integer, Map<Integer, ParcelableResource>>
+            mUpdatedDrawablesForStyle = new HashMap<>();
+
+    /**
+     * Map of <drawable_id, <source_id, resource_value>>
+     */
+    private final Map<Integer, Map<Integer, ParcelableResource>>
+            mUpdatedDrawablesForSource = new HashMap<>();
+
+    /**
+     * Map of <string_id, resource_value>
+     */
+    private final Map<String, ParcelableResource> mUpdatedStrings = new HashMap<>();
+
+    private final Object mLock = new Object();
+    private final Injector mInjector;
+
+    DeviceManagementResourcesProvider() {
+        this(new Injector());
+    }
+
+    DeviceManagementResourcesProvider(Injector injector) {
+        mInjector = requireNonNull(injector);
+    }
+
+    /**
+     * Returns {@code false} if no resources were updated.
+     */
+    boolean updateDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
+        boolean updated = false;
+        for (int i = 0; i < drawables.size(); i++) {
+            int drawableId = drawables.get(i).getDrawableId();
+            int drawableStyle = drawables.get(i).getDrawableStyle();
+            int drawableSource = drawables.get(i).getDrawableSource();
+            ParcelableResource resource = drawables.get(i).getResource();
+
+            Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+
+            if (drawableSource == DevicePolicyResources.Drawable.Source.UNDEFINED) {
+                updated |= updateDrawable(drawableId, drawableStyle, resource);
+            } else {
+                updated |= updateDrawableForSource(drawableId, drawableSource, resource);
+            }
+        }
+        if (!updated) {
+            return false;
+        }
+        synchronized (mLock) {
+            write();
+            return true;
+        }
+    }
+
+    private boolean updateDrawable(
+            int drawableId, int drawableStyle, ParcelableResource updatableResource) {
+        if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
+            Log.w(TAG, "Updating a resource for an unknown drawable id " + drawableId);
+        }
+        if (!UPDATABLE_DRAWABLE_STYLES.contains(drawableStyle)) {
+            Log.w(TAG, "Updating a resource for an unknown style id " + drawableStyle);
+        }
+        synchronized (mLock) {
+            if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
+                mUpdatedDrawablesForStyle.put(drawableId, new HashMap<>());
+            }
+            ParcelableResource current = mUpdatedDrawablesForStyle.get(drawableId).get(
+                    drawableStyle);
+            if (updatableResource.equals(current)) {
+                return false;
+            }
+            mUpdatedDrawablesForStyle.get(drawableId).put(drawableStyle, updatableResource);
+            return true;
+        }
+    }
+
+    // TODO(b/214576716): change this to respect style
+    private boolean updateDrawableForSource(
+            int drawableId, int drawableSource, ParcelableResource updatableResource) {
+        if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
+            Log.w(TAG, "Updating a resource for an unknown drawable id " + drawableId);
+        }
+        if (!UPDATABLE_DRAWABLE_SOURCES.contains(drawableSource)) {
+            Log.w(TAG, "Updating a resource for an unknown source id " + drawableSource);
+        }
+        synchronized (mLock) {
+            if (!mUpdatedDrawablesForSource.containsKey(drawableId)) {
+                mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
+            }
+            ParcelableResource current = mUpdatedDrawablesForSource.get(drawableId).get(
+                    drawableSource);
+            if (updatableResource.equals(current)) {
+                return false;
+            }
+            mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, updatableResource);
+            return true;
+        }
+    }
+
+    /**
+     * Returns {@code false} if no resources were removed.
+     */
+    boolean removeDrawables(@NonNull int[] drawableIds) {
+        synchronized (mLock) {
+            boolean removed = false;
+            for (int i = 0; i < drawableIds.length; i++) {
+                int drawableId = drawableIds[i];
+                removed |= mUpdatedDrawablesForStyle.remove(drawableId) != null
+                        || mUpdatedDrawablesForSource.remove(drawableId) != null;
+            }
+            if (!removed) {
+                return false;
+            }
+            write();
+            return true;
+        }
+    }
+
+    @Nullable
+    ParcelableResource getDrawable(
+            int drawableId, int drawableStyle, int drawableSource) {
+        if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
+            Log.w(TAG, "Getting an updated resource for an unknown drawable id " + drawableId);
+        }
+        if (!UPDATABLE_DRAWABLE_STYLES.contains(drawableStyle)) {
+            Log.w(TAG, "Getting an updated resource for an unknown drawable style "
+                    + drawableStyle);
+        }
+        if (!UPDATABLE_DRAWABLE_SOURCES.contains(drawableSource)) {
+            Log.w(TAG, "Getting an updated resource for an unknown drawable Source "
+                    + drawableSource);
+        }
+        if (mUpdatedDrawablesForSource.containsKey(drawableId)
+                && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
+            return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource);
+        }
+        if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
+            Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
+            return null;
+        }
+        if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) {
+            return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle);
+        }
+
+        if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(Style.DEFAULT)) {
+            return mUpdatedDrawablesForStyle.get(drawableId).get(Style.DEFAULT);
+        }
+        Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
+        return null;
+    }
+
+    /**
+     * Returns {@code false} if no resources were updated.
+     */
+    boolean updateStrings(@NonNull List<DevicePolicyStringResource> strings) {
+        boolean updated = false;
+        for (int i = 0; i < strings.size(); i++) {
+            String stringId = strings.get(i).getStringId();
+            ParcelableResource resource = strings.get(i).getResource();
+
+            Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+            updated |= updateString(stringId, resource);
+        }
+        if (!updated) {
+            return false;
+        }
+        synchronized (mLock) {
+            write();
+            return true;
+        }
+    }
+
+    private boolean updateString(String stringId, ParcelableResource updatableResource) {
+        if (!UPDATABLE_STRING_IDS.contains(stringId)) {
+            Log.w(TAG, "Updating a resource for an unknown string id " + stringId);
+        }
+        synchronized (mLock) {
+            ParcelableResource current = mUpdatedStrings.get(stringId);
+            if (updatableResource.equals(current)) {
+                return false;
+            }
+            mUpdatedStrings.put(stringId, updatableResource);
+            return true;
+        }
+    }
+
+    /**
+     * Returns {@code false} if no resources were removed.
+     */
+    boolean removeStrings(@NonNull String[] stringIds) {
+        synchronized (mLock) {
+            boolean removed = false;
+            for (int i = 0; i < stringIds.length; i++) {
+                String stringId = stringIds[i];
+                removed |= mUpdatedStrings.remove(stringId) != null;
+            }
+            if (!removed) {
+                return false;
+            }
+            write();
+            return true;
+        }
+    }
+
+    @Nullable
+    ParcelableResource getString(String stringId) {
+        if (!UPDATABLE_STRING_IDS.contains(stringId)) {
+            Log.w(TAG, "Getting an updated resource for an unknown string id " + stringId);
+        }
+
+        if (mUpdatedStrings.containsKey(stringId)) {
+            return mUpdatedStrings.get(stringId);
+        }
+
+        Log.d(TAG, "No updated string found for string id " + stringId);
+        return null;
+    }
+
+    private void write() {
+        Log.d(TAG, "Writing updated resources to file.");
+        new ResourcesReaderWriter().writeToFileLocked();
+    }
+
+    void load() {
+        synchronized (mLock) {
+            new ResourcesReaderWriter().readFromFileLocked();
+        }
+    }
+
+    private File getResourcesFile() {
+        return new File(mInjector.environmentGetDataSystemDirectory(), UPDATED_RESOURCES_XML);
+    }
+
+    private class ResourcesReaderWriter {
+        private final File mFile;
+        private ResourcesReaderWriter() {
+            mFile = getResourcesFile();
+        }
+
+        void writeToFileLocked() {
+            Log.d(TAG, "Writing to " + mFile);
+
+            AtomicFile f = new AtomicFile(mFile);
+            FileOutputStream outputStream = null;
+            try {
+                outputStream = f.startWrite();
+                TypedXmlSerializer out = Xml.resolveSerializer(outputStream);
+
+                // Root tag
+                out.startDocument(null, true);
+                out.startTag(null, TAG_ROOT);
+
+                // Actual content
+                writeInner(out);
+
+                // Close root
+                out.endTag(null, TAG_ROOT);
+                out.endDocument();
+                out.flush();
+
+                // Commit the content.
+                f.finishWrite(outputStream);
+                outputStream = null;
+
+            } catch (IOException e) {
+                Log.e(TAG, "Exception when writing", e);
+                if (outputStream != null) {
+                    f.failWrite(outputStream);
+                }
+            }
+        }
+
+        void readFromFileLocked() {
+            if (!mFile.exists()) {
+                Log.d(TAG, "" + mFile + " doesn't exist");
+                return;
+            }
+
+            Log.d(TAG, "Reading from " + mFile);
+            AtomicFile f = new AtomicFile(mFile);
+            InputStream input = null;
+            try {
+                input = f.openRead();
+                TypedXmlPullParser parser = Xml.resolvePullParser(input);
+
+                int type;
+                int depth = 0;
+                while ((type = parser.next()) != TypedXmlPullParser.END_DOCUMENT) {
+                    switch (type) {
+                        case TypedXmlPullParser.START_TAG:
+                            depth++;
+                            break;
+                        case TypedXmlPullParser.END_TAG:
+                            depth--;
+                            // fallthrough
+                        default:
+                            continue;
+                    }
+                    // Check the root tag
+                    String tag = parser.getName();
+                    if (depth == 1) {
+                        if (!TAG_ROOT.equals(tag)) {
+                            Log.e(TAG, "Invalid root tag: " + tag);
+                            return;
+                        }
+                        continue;
+                    }
+                    // readInner() will only see START_TAG at depth >= 2.
+                    if (!readInner(parser, depth, tag)) {
+                        return; // Error
+                    }
+                }
+            } catch (XmlPullParserException | IOException e) {
+                Log.e(TAG, "Error parsing resources file", e);
+            } finally {
+                IoUtils.closeQuietly(input);
+            }
+        }
+
+        void writeInner(TypedXmlSerializer out) throws IOException {
+            if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) {
+                for (Map.Entry<Integer, Map<Integer, ParcelableResource>> drawableEntry
+                        : mUpdatedDrawablesForStyle.entrySet()) {
+                    out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
+                    out.attributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
+                    out.attributeInt(
+                            /* namespace= */ null,
+                            ATTR_DRAWABLE_STYLE_SIZE,
+                            drawableEntry.getValue().size());
+                    int counter = 0;
+                    for (Map.Entry<Integer, ParcelableResource> styleEntry
+                            : drawableEntry.getValue().entrySet()) {
+                        out.attributeInt(
+                                /* namespace= */ null,
+                                ATTR_DRAWABLE_STYLE + (counter++),
+                                styleEntry.getKey());
+                        styleEntry.getValue().writeToXmlFile(out);
+                    }
+                    out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
+                }
+            }
+            if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) {
+                for (Map.Entry<Integer, Map<Integer, ParcelableResource>> drawableEntry
+                        : mUpdatedDrawablesForSource.entrySet()) {
+                    out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
+                    out.attributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
+                    out.attributeInt(
+                            /* namespace= */ null,
+                            ATTR_DRAWABLE_SOURCE_SIZE,
+                            drawableEntry.getValue().size());
+                    int counter = 0;
+                    for (Map.Entry<Integer, ParcelableResource> sourceEntry
+                            : drawableEntry.getValue().entrySet()) {
+                        out.attributeInt(
+                                /* namespace= */ null,
+                                ATTR_DRAWABLE_SOURCE + (counter++),
+                                sourceEntry.getKey());
+                        sourceEntry.getValue().writeToXmlFile(out);
+                    }
+                    out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
+                }
+            }
+            if (mUpdatedStrings != null && !mUpdatedStrings.isEmpty()) {
+                for (Map.Entry<String, ParcelableResource> entry
+                        : mUpdatedStrings.entrySet()) {
+                    out.startTag(/* namespace= */ null, TAG_STRING_ENTRY);
+                    out.attribute(
+                            /* namespace= */ null,
+                            ATTR_SOURCE_ID,
+                            entry.getKey());
+                    entry.getValue().writeToXmlFile(out);
+                    out.endTag(/* namespace= */ null, TAG_STRING_ENTRY);
+                }
+            }
+        }
+
+        private boolean readInner(
+                TypedXmlPullParser parser, int depth, String tag)
+                throws XmlPullParserException, IOException {
+            if (depth > 2) {
+                return true; // Ignore
+            }
+            switch (tag) {
+                case TAG_DRAWABLE_STYLE_ENTRY:
+                    int drawableId = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID);
+                    mUpdatedDrawablesForStyle.put(
+                            drawableId,
+                            new HashMap<>());
+                    int size = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE);
+                    for (int i = 0; i < size; i++) {
+                        int style = parser.getAttributeInt(
+                                /* namespace= */ null, ATTR_DRAWABLE_STYLE + i);
+                        mUpdatedDrawablesForStyle.get(drawableId).put(
+                                style,
+                                ParcelableResource.createFromXml(parser));
+                    }
+                    break;
+                case TAG_DRAWABLE_SOURCE_ENTRY:
+                    drawableId = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID);
+                    mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
+                    size = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE);
+                    for (int i = 0; i < size; i++) {
+                        int source = parser.getAttributeInt(
+                                /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i);
+                        mUpdatedDrawablesForSource.get(drawableId).put(
+                                source,
+                                ParcelableResource.createFromXml(parser));
+                    }
+                    break;
+                case TAG_STRING_ENTRY:
+                    String sourceId = parser.getAttributeValue(
+                            /* namespace= */ null, ATTR_SOURCE_ID);
+                    mUpdatedStrings.put(
+                            sourceId, ParcelableResource.createFromXml(parser));
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected tag: " + tag);
+                    return false;
+            }
+            return true;
+        }
+    }
+
+    public static class Injector {
+        File environmentGetDataSystemDirectory() {
+            return Environment.getDataSystemDirectory();
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index df8953c..40196db 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -26,6 +26,7 @@
 import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
@@ -56,6 +57,9 @@
 import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
 import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
 import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_STRING;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION;
@@ -96,6 +100,18 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
 import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
@@ -109,6 +125,7 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -161,6 +178,7 @@
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyDrawableResource;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.DeviceOwnerType;
@@ -171,12 +189,14 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DevicePolicyManagerLiteInternal;
 import android.app.admin.DevicePolicySafetyChecker;
+import android.app.admin.DevicePolicyStringResource;
 import android.app.admin.DeviceStateCache;
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.NetworkEvent;
 import android.app.admin.ParcelableGranteeMap;
+import android.app.admin.ParcelableResource;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.PasswordPolicy;
 import android.app.admin.SecurityLog;
@@ -231,6 +251,7 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivitySettingsManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.ProfileNetworkPreference;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.net.VpnManager;
@@ -258,6 +279,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManager.UserRestrictionSource;
 import android.os.storage.StorageManager;
 import android.permission.AdminPermissionControlParams;
 import android.permission.IPermissionManager;
@@ -286,6 +308,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.DebugUtils;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Pair;
@@ -713,6 +736,8 @@
     // Guarded by mHandler
     private @UserIdInt int mNetworkLoggingNotificationUserId = UserHandle.USER_NULL;
 
+    private final DeviceManagementResourcesProvider mDeviceManagementResourcesProvider;
+
     private static final boolean ENABLE_LOCK_GUARD = true;
 
     /**
@@ -1737,6 +1762,10 @@
         void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
             mSafetyChecker = safetyChecker;
         }
+
+        DeviceManagementResourcesProvider getDeviceManagementResourcesProvider() {
+            return new DeviceManagementResourcesProvider();
+        }
     }
 
     /**
@@ -1789,6 +1818,8 @@
         mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
         mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
 
+        mDeviceManagementResourcesProvider = mInjector.getDeviceManagementResourcesProvider();
+
         // "Lite" interface is available even when the device doesn't have the feature
         LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService);
         if (!mHasFeature) {
@@ -1835,6 +1866,8 @@
         loadOwners();
 
         performPolicyVersionUpgrade();
+
+        mDeviceManagementResourcesProvider.load();
     }
 
     /**
@@ -2216,7 +2249,7 @@
      * a managed profile.
      */
     @GuardedBy("getLockObject()")
-    private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
+    private void applyProfileRestrictionsIfDeviceOwnerLocked() {
         final int doUserId = mOwners.getDeviceOwnerUserId();
         if (doUserId == UserHandle.USER_NULL) {
             if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping application of restriction.");
@@ -2224,7 +2257,17 @@
         }
 
         final UserHandle doUserHandle = UserHandle.of(doUserId);
-        // Set the restriction if not set.
+
+        // Based on  CDD : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support,
+        // creation of clone profile is not allowed in case device owner is set.
+        // Enforcing this restriction on setting up of device owner.
+        if (!mUserManager.hasUserRestriction(
+                UserManager.DISALLOW_ADD_CLONE_PROFILE, doUserHandle)) {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+                    doUserHandle);
+        }
+        // Creation of managed profile is restricted in case device owner is set, enforcing this
+        // restriction by setting user level restriction at time of device owner setup.
         if (!mUserManager.hasUserRestriction(
                 UserManager.DISALLOW_ADD_MANAGED_PROFILE, doUserHandle)) {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
@@ -3133,7 +3176,7 @@
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
                 synchronized (getLockObject()) {
                     migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
-                    applyManagedProfileRestrictionIfDeviceOwnerLocked();
+                    applyProfileRestrictionsIfDeviceOwnerLocked();
                 }
                 maybeStartSecurityLogMonitorOnActivityManagerReady();
                 break;
@@ -3758,6 +3801,12 @@
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
                     userHandle);
         }
+        // When a device owner is set, the system automatically restricts adding a clone profile.
+        // Remove this restriction when the device owner is cleared.
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, userHandle)) {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
+                    userHandle);
+        }
     }
 
     /**
@@ -6909,12 +6958,8 @@
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
 
         if (TextUtils.isEmpty(wipeReasonForUser)) {
-            if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) {
-                wipeReasonForUser = mContext.getString(R.string.device_ownership_relinquished);
-            } else {
-                wipeReasonForUser = mContext.getString(
-                        R.string.work_profile_deleted_description_dpm_wipe);
-            }
+            wipeReasonForUser = getGenericWipeReason(
+                    calledByProfileOwnerOnOrgOwnedDevice, calledOnParentInstance);
         }
 
         int userId = admin != null ? admin.getUserHandle().getIdentifier()
@@ -6965,6 +7010,18 @@
         wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId);
     }
 
+    private String getGenericWipeReason(
+            boolean calledByProfileOwnerOnOrgOwnedDevice, boolean calledOnParentInstance) {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance
+                ? dpm.getString(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+                        () -> mContext.getString(
+                                R.string.device_ownership_relinquished))
+                : dpm.getString(WORK_PROFILE_DELETED_GENERIC_MESSAGE,
+                        () -> mContext.getString(
+                                R.string.work_profile_deleted_description_dpm_wipe));
+    }
+
     /**
      * Clears device wide policies enforced by COPE PO when relinquishing the device. This method
      * should be invoked once the admin is gone, so that all methods that rely on calculating
@@ -7049,7 +7106,7 @@
         Notification notification =
                 new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
                         .setSmallIcon(android.R.drawable.stat_sys_warning)
-                        .setContentTitle(mContext.getString(R.string.work_profile_deleted))
+                        .setContentTitle(getWorkProfileDeletedTitle())
                         .setContentText(wipeReasonForUser)
                         .setColor(mContext.getColor(R.color.system_notification_accent_color))
                         .setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser))
@@ -7057,6 +7114,12 @@
         mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification);
     }
 
+    private String getWorkProfileDeletedTitle() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(WORK_PROFILE_DELETED_TITLE,
+                () -> mContext.getString(R.string.work_profile_deleted));
+    }
+
     private void clearWipeProfileNotification() {
         mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PROFILE_WIPED);
     }
@@ -7287,12 +7350,10 @@
             // able to do so).
             // IMPORTANT: Call without holding the lock to prevent deadlock.
             try {
-                String wipeReasonForUser = mContext.getString(
-                        R.string.work_profile_deleted_reason_maximum_password_failure);
                 wipeDataNoLock(strictestAdmin.info.getComponent(),
                         /*flags=*/ 0,
                         /*reason=*/ "reportFailedPasswordAttempt()",
-                        wipeReasonForUser,
+                        getFailedPasswordAttemptWipeMessage(),
                         userId);
             } catch (SecurityException e) {
                 Slogf.w(LOG_TAG, "Failed to wipe user " + userId
@@ -7306,6 +7367,13 @@
         }
     }
 
+    private String getFailedPasswordAttemptWipeMessage() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+                () -> mContext.getString(
+                        R.string.work_profile_deleted_reason_maximum_password_failure));
+    }
+
     /**
      * Returns which user should be wiped if this admin's maximum filed password attempts policy is
      * violated.
@@ -8450,6 +8518,12 @@
                 // on the primary profile).
                 mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
                         UserHandle.of(userId));
+                // Restrict adding a clone profile when a device owner is set on the device.
+                // That is to prevent the co-existence of a clone profile and a device owner
+                // on the same device.
+                // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+                mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+                        UserHandle.of(userId));
                 // TODO Send to system too?
                 sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
             });
@@ -8922,7 +8996,7 @@
         mOwners.writeProfileOwner(userId);
         deleteTransferOwnershipBundleLocked(userId);
         toggleBackupServiceActive(userId, true);
-        applyManagedProfileRestrictionIfDeviceOwnerLocked();
+        applyProfileRestrictionsIfDeviceOwnerLocked();
         setNetworkLoggingActiveInternal(false);
     }
 
@@ -12377,8 +12451,8 @@
         Notification notification = new Notification.Builder(mContext,
                 SystemNotificationChannels.DEVICE_ADMIN)
                 .setSmallIcon(R.drawable.ic_info_outline)
-                .setContentTitle(mContext.getString(R.string.location_changed_notification_title))
-                .setContentText(mContext.getString(R.string.location_changed_notification_text))
+                .setContentTitle(getLocationChangedTitle())
+                .setContentText(getLocationChangedText())
                 .setColor(mContext.getColor(R.color.system_notification_accent_color))
                 .setShowWhen(true)
                 .setContentIntent(locationSettingsIntent)
@@ -12388,6 +12462,18 @@
                 notification);
     }
 
+    private String getLocationChangedTitle() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(LOCATION_CHANGED_TITLE,
+                () -> mContext.getString(R.string.location_changed_notification_title));
+    }
+
+    private String getLocationChangedText() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(LOCATION_CHANGED_MESSAGE,
+                () -> mContext.getString(R.string.location_changed_notification_text));
+    }
+
     @Override
     public boolean setTime(ComponentName who, long millis) {
         Objects.requireNonNull(who, "ComponentName is null");
@@ -12978,11 +13064,19 @@
                     Slogf.e(LOG_TAG, "appLabel is inexplicably null");
                     return null;
                 }
-                return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
-                        .getResources().getString(R.string.printing_disabled_by, appLabel);
+                DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+                return dpm.getString(
+                        PRINTING_DISABLED_NAMED_ADMIN,
+                        () -> getDefaultPrintingDisabledMsg(appLabel),
+                        appLabel);
             }
         }
 
+        private String getDefaultPrintingDisabledMsg(CharSequence appLabel) {
+            return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
+                        .getResources().getString(R.string.printing_disabled_by, appLabel);
+        }
+
         @Override
         protected DevicePolicyCache getDevicePolicyCache() {
             return mPolicyCache;
@@ -13211,12 +13305,17 @@
 
     /**
      * @param restriction The restriction enforced by admin. It could be any user restriction or
-     *                    policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
-     *                    {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+     *                    policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA},
+     *                    {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE} and  {@link
+     *                    DevicePolicyManager#POLICY_SUSPEND_PACKAGES}.
      */
     private Bundle getEnforcingAdminAndUserDetailsInternal(int userId, String restriction) {
         Bundle result = null;
-        if (restriction == null) {
+
+        // For POLICY_SUSPEND_PACKAGES return PO or DO to keep the behavior same as
+        // before the bug fix for b/192245204.
+        if (restriction == null || DevicePolicyManager.POLICY_SUSPEND_PACKAGES.equals(
+                restriction)) {
             ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
             if (profileOwner != null) {
                 result = new Bundle();
@@ -13271,14 +13370,29 @@
             try {
                 List<UserManager.EnforcingUser> sources = mUserManager
                         .getUserRestrictionSources(restriction, UserHandle.of(userId));
-                if (sources == null || sources.isEmpty()) {
+                if (sources == null) {
                     // The restriction is not enforced.
                     return null;
-                } else if (sources.size() > 1) {
+                }
+                int sizeBefore = sources.size();
+                if (sizeBefore > 1) {
+                    Slogf.d(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): "
+                            + "%d sources found, excluding those set by UserManager",
+                            userId, restriction, sizeBefore);
+                    sources = getDevicePolicySources(sources);
+                }
+                if (sources.isEmpty()) {
+                    // The restriction is not enforced (or is just enforced by the system)
+                    return null;
+                }
+
+                if (sources.size() > 1) {
                     // In this case, we'll show an admin support dialog that does not
                     // specify the admin.
                     // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
                     // the admin for the calling user.
+                    Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple "
+                            + "sources for restriction %s on user %d", restriction, userId);
                     result = new Bundle();
                     result.putInt(Intent.EXTRA_USER_ID, userId);
                     return result;
@@ -13324,6 +13438,32 @@
     }
 
     /**
+     *  Excludes restrictions imposed by UserManager.
+     */
+    private List<UserManager.EnforcingUser> getDevicePolicySources(
+            List<UserManager.EnforcingUser> sources) {
+        int sizeBefore = sources.size();
+        List<UserManager.EnforcingUser> realSources = new ArrayList<>(sizeBefore);
+        for (int i = 0; i < sizeBefore; i++) {
+            UserManager.EnforcingUser source = sources.get(i);
+            int type = source.getUserRestrictionSource();
+            if (type != UserManager.RESTRICTION_SOURCE_PROFILE_OWNER
+                    && type != UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+                // TODO(b/128928355): add unit test
+                Slogf.d(LOG_TAG, "excluding source of type %s at index %d",
+                        userRestrictionSourceToString(type), i);
+                continue;
+            }
+            realSources.add(source);
+        }
+        return realSources;
+    }
+
+    private static String userRestrictionSourceToString(@UserRestrictionSource int source) {
+        return DebugUtils.flagsToString(UserManager.class, "RESTRICTION_", source);
+    }
+
+    /**
      * @param restriction The restriction enforced by admin. It could be any user restriction or
      *                    policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
      *                    {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
@@ -15468,16 +15608,18 @@
         // Simple notification clicks are immutable
         final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent,
                 PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+
+        final String title = getNetworkLoggingTitle();
+        final String text = getNetworkLoggingText();
         Notification notification =
                 new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
                 .setSmallIcon(R.drawable.ic_info_outline)
-                .setContentTitle(mContext.getString(R.string.network_logging_notification_title))
-                .setContentText(mContext.getString(R.string.network_logging_notification_text))
-                .setTicker(mContext.getString(R.string.network_logging_notification_title))
+                .setContentTitle(title)
+                .setContentText(text)
+                .setTicker(title)
                 .setShowWhen(true)
                 .setContentIntent(pendingIntent)
-                .setStyle(new Notification.BigTextStyle()
-                        .bigText(mContext.getString(R.string.network_logging_notification_text)))
+                .setStyle(new Notification.BigTextStyle().bigText(text))
                 .build();
         Slogf.i(LOG_TAG, "Sending network logging notification to user %d",
                 mNetworkLoggingNotificationUserId);
@@ -15486,6 +15628,18 @@
                 UserHandle.of(mNetworkLoggingNotificationUserId));
     }
 
+    private String getNetworkLoggingTitle() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(NETWORK_LOGGING_TITLE,
+                () -> mContext.getString(R.string.network_logging_notification_title));
+    }
+
+    private String getNetworkLoggingText() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(NETWORK_LOGGING_MESSAGE,
+                () -> mContext.getString(R.string.network_logging_notification_text));
+    }
+
     private void handleCancelNetworkLoggingNotification() {
         if (mNetworkLoggingNotificationUserId == UserHandle.USER_NULL) {
             // Happens when setNetworkLoggingActive(false) is called before called with true
@@ -16978,10 +17132,8 @@
                 0 /* requestCode */, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
-        final String buttonText =
-                mContext.getString(R.string.personal_apps_suspended_turn_profile_on);
-        final Notification.Action turnProfileOnButton =
-                new Notification.Action.Builder(null /* icon */, buttonText, pendingIntent).build();
+        final Notification.Action turnProfileOnButton = new Notification.Action.Builder(
+                /* icon= */ null, getPersonalAppSuspensionButtonText(), pendingIntent).build();
 
         final String text;
         final boolean ongoing;
@@ -16993,26 +17145,24 @@
                     mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_DATE);
             final String time = DateUtils.formatDateTime(
                     mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_TIME);
-            text = mContext.getString(
-                    R.string.personal_apps_suspension_soon_text, date, time, maxDays);
+            text = getPersonalAppSuspensionSoonText(date, time, maxDays);
             ongoing = false;
         } else {
-            text = mContext.getString(R.string.personal_apps_suspension_text);
+            text = getPersonalAppSuspensionText();
             ongoing = true;
         }
         final int color = mContext.getColor(R.color.personal_apps_suspension_notification_color);
         final Bundle extras = new Bundle();
         // TODO: Create a separate string for this.
-        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                mContext.getString(R.string.notification_work_profile_content_description));
+        extras.putString(
+                Notification.EXTRA_SUBSTITUTE_APP_NAME, getWorkProfileContentDescription());
 
         final Notification notification =
                 new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
                         .setSmallIcon(R.drawable.ic_corp_badge_no_background)
                         .setOngoing(ongoing)
                         .setAutoCancel(false)
-                        .setContentTitle(mContext.getString(
-                                R.string.personal_apps_suspension_title))
+                        .setContentTitle(getPersonalAppSuspensionTitle())
                         .setContentText(text)
                         .setStyle(new Notification.BigTextStyle().bigText(text))
                         .setColor(color)
@@ -17023,6 +17173,38 @@
                 SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification);
     }
 
+    private String getPersonalAppSuspensionButtonText() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+                () -> mContext.getString(R.string.personal_apps_suspended_turn_profile_on));
+    }
+
+    private String getPersonalAppSuspensionTitle() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+                () -> mContext.getString(R.string.personal_apps_suspension_title));
+    }
+
+    private String getPersonalAppSuspensionText() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+                () -> mContext.getString(R.string.personal_apps_suspension_text));
+    }
+
+    private String getPersonalAppSuspensionSoonText(String date, String time, int maxDays) {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+                () -> mContext.getString(
+                        R.string.personal_apps_suspension_soon_text, date, time, maxDays),
+                date, time, maxDays);
+    }
+
+    private String getWorkProfileContentDescription() {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getString(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION,
+                () -> mContext.getString(R.string.notification_work_profile_content_description));
+    }
+
     @Override
     public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMillis) {
         Objects.requireNonNull(who, "ComponentName is null");
@@ -17784,10 +17966,19 @@
         }
         int networkPreference = preferentialNetworkServiceEnabled
                 ? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
+        ProfileNetworkPreference.Builder preferenceBuilder =
+                new ProfileNetworkPreference.Builder();
+        if (preferentialNetworkServiceEnabled) {
+            preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+            preferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+        } else {
+            preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+        }
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceBuilder.build());
         mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.getConnectivityManager().setProfileNetworkPreference(
-                        UserHandle.of(userId),
-                        networkPreference,
+                mInjector.getConnectivityManager().setProfileNetworkPreferences(
+                        UserHandle.of(userId), preferences,
                         null /* executor */, null /* listener */));
     }
 
@@ -17908,4 +18099,211 @@
                         && mInjector.getUsbManager().getUsbHalVersion() >= UsbManager.USB_HAL_V1_3
         );
     }
+
+    private void validateCurrentWifiMeetsAdminRequirements() {
+        mInjector.binderWithCleanCallingIdentity(
+                () -> mInjector.getWifiManager().validateCurrentWifiMeetsAdminRequirements());
+    }
+
+    @Override
+    public void setMinimumRequiredWifiSecurityLevel(int level) {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+                "Wi-Fi minimum security level can only be controlled by a device owner or "
+                        + "a profile owner on an organization-owned device.");
+
+        boolean valueChanged = false;
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            if (admin.mWifiMinimumSecurityLevel != level) {
+                admin.mWifiMinimumSecurityLevel = level;
+                saveSettingsLocked(caller.getUserId());
+                valueChanged = true;
+            }
+        }
+        if (valueChanged) validateCurrentWifiMeetsAdminRequirements();
+    }
+
+    @Override
+    public int getMinimumRequiredWifiSecurityLevel() {
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                    UserHandle.USER_SYSTEM);
+            return (admin == null) ? DevicePolicyManager.WIFI_SECURITY_OPEN
+                    : admin.mWifiMinimumSecurityLevel;
+        }
+    }
+
+    @Override
+    public void setSsidAllowlist(List<String> ssids) {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+                "SSID allowlist can only be controlled by a device owner or "
+                        + "a profile owner on an organization-owned device.");
+
+        Collections.sort(ssids);
+        boolean changed = false;
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            if (!ssids.equals(admin.mSsidAllowlist)) {
+                admin.mSsidAllowlist = ssids;
+                admin.mSsidDenylist = null;
+                changed = true;
+            }
+            if (changed) saveSettingsLocked(caller.getUserId());
+        }
+        if (changed) validateCurrentWifiMeetsAdminRequirements();
+    }
+
+    @Override
+    public List<String> getSsidAllowlist() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+                        || isSystemUid(caller),
+                "SSID allowlist can only be retrieved by a device owner or "
+                        + "a profile owner on an organization-owned device or a system app.");
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                    UserHandle.USER_SYSTEM);
+            return (admin == null || admin.mSsidAllowlist == null) ? new ArrayList<>()
+                    : admin.mSsidAllowlist;
+        }
+    }
+
+    @Override
+    public void setSsidDenylist(List<String> ssids) {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+                "SSID denylist can only be controlled by a device owner or "
+                        + "a profile owner on an organization-owned device.");
+
+        Collections.sort(ssids);
+        boolean changed = false;
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            if (!ssids.equals(admin.mSsidDenylist)) {
+                admin.mSsidDenylist = ssids;
+                admin.mSsidAllowlist = null;
+                changed = true;
+            }
+            if (changed) saveSettingsLocked(caller.getUserId());
+        }
+        if (changed) validateCurrentWifiMeetsAdminRequirements();
+    }
+
+    @Override
+    public List<String> getSsidDenylist() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+                        || isSystemUid(caller),
+                "SSID denylist can only be retrieved by a device owner or "
+                        + "a profile owner on an organization-owned device or a system app.");
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                    UserHandle.USER_SYSTEM);
+            return (admin == null || admin.mSsidDenylist == null) ? new ArrayList<>()
+                    : admin.mSsidDenylist;
+        }
+    }
+
+    @Override
+    public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+        Objects.requireNonNull(drawables, "drawables must be provided.");
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mDeviceManagementResourcesProvider.updateDrawables(drawables)) {
+                sendDrawableUpdatedBroadcast(
+                        drawables.stream().mapToInt(d -> d.getDrawableId()).toArray());
+            }
+        });
+    }
+
+    @Override
+    public void resetDrawables(@NonNull int[] drawableIds) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+        Objects.requireNonNull(drawableIds, "drawableIds must be provided.");
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mDeviceManagementResourcesProvider.removeDrawables(drawableIds)) {
+                sendDrawableUpdatedBroadcast(drawableIds);
+            }
+        });
+    }
+
+    @Override
+    public ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource) {
+        return mInjector.binderWithCleanCallingIdentity(() ->
+                mDeviceManagementResourcesProvider.getDrawable(
+                        drawableId, drawableStyle, drawableSource));
+    }
+
+    private void sendDrawableUpdatedBroadcast(int[] drawableIds) {
+        final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
+        intent.putExtra(EXTRA_RESOURCE_ID, drawableIds);
+        intent.putExtra(EXTRA_RESOURCE_TYPE_DRAWABLE, /* value= */ true);
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+        List<UserInfo> users = mUserManager.getAliveUsers();
+        for (int i = 0; i < users.size(); i++) {
+            UserHandle user = users.get(i).getUserHandle();
+            mContext.sendBroadcastAsUser(intent, user);
+        }
+    }
+
+    @Override
+    public void setStrings(@NonNull List<DevicePolicyStringResource> strings) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+        Objects.requireNonNull(strings, "strings must be provided.");
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mDeviceManagementResourcesProvider.updateStrings(strings))
+            sendStringsUpdatedBroadcast(
+                    strings.stream().map(s -> s.getStringId()).toArray(String[]::new));
+        });
+    }
+
+    @Override
+    public void resetStrings(String[] stringIds) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mDeviceManagementResourcesProvider.removeStrings(stringIds)) {
+                sendStringsUpdatedBroadcast(stringIds);
+            }
+        });
+    }
+
+    @Override
+    public ParcelableResource getString(String stringId) {
+        return mInjector.binderWithCleanCallingIdentity(() ->
+                mDeviceManagementResourcesProvider.getString(stringId));
+    }
+
+    private void sendStringsUpdatedBroadcast(String[] stringIds) {
+        final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
+        intent.putExtra(EXTRA_RESOURCE_ID, stringIds);
+        intent.putExtra(EXTRA_RESOURCE_TYPE_STRING, /* value= */ true);
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+        List<UserInfo> users = mUserManager.getAliveUsers();
+        for (int i = 0; i < users.size(); i++) {
+            UserHandle user = users.get(i).getUserHandle();
+            mContext.sendBroadcastAsUser(intent, user);
+        }
+    }
 }
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index f2ad068..9fe090a 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -10,6 +10,12 @@
     },
     {
       "name": "CtsIncrementalInstallHostTestCases"
+    },
+    {
+      "name": "libincfs-test"
+    },
+    {
+      "name": "service.incremental_test"
     }
   ],
   "presubmit-large": [
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 29797a5..ad8753d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -54,6 +54,7 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivityModuleConnector;
 import android.net.NetworkStackClient;
+import android.net.TrafficStats;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -103,6 +104,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.ILockSettings;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.ambientcontext.AmbientContextManagerService;
 import com.android.server.appbinding.AppBindingService;
 import com.android.server.art.ArtManagerLocal;
 import com.android.server.attention.AttentionManagerService;
@@ -115,7 +117,6 @@
 import com.android.server.broadcastradio.BroadcastRadioService;
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
-import com.android.server.communal.CommunalManagerService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.compat.PlatformCompatNative;
 import com.android.server.connectivity.PacProxyService;
@@ -195,7 +196,7 @@
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
-import com.android.server.tv.interactive.TvIAppManagerService;
+import com.android.server.tv.interactive.TvInteractiveAppManagerService;
 import com.android.server.tv.tunerresourcemanager.TunerResourceManagerService;
 import com.android.server.twilight.TwilightService;
 import com.android.server.uri.UriGrantsManagerService;
@@ -260,12 +261,10 @@
             "com.android.server.companion.virtual.VirtualDeviceManagerService";
     private static final String STATS_COMPANION_APEX_PATH =
             "/apex/com.android.os.statsd/javalib/service-statsd.jar";
-    private static final String SCHEDULING_APEX_PATH =
-            "/apex/com.android.scheduling/javalib/service-scheduling.jar";
-    private static final String REBOOT_READINESS_LIFECYCLE_CLASS =
-            "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle";
     private static final String CONNECTIVITY_SERVICE_APEX_PATH =
             "/apex/com.android.tethering/javalib/service-connectivity.jar";
+    private static final String NEARBY_SERVICE_APEX_PATH =
+            "/apex/com.android.nearby/javalib/service-nearby.jar";
     private static final String STATS_COMPANION_LIFECYCLE_CLASS =
             "com.android.server.stats.StatsCompanion$Lifecycle";
     private static final String STATS_PULL_ATOM_SERVICE_CLASS =
@@ -276,6 +275,8 @@
             "com.android.server.usb.UsbService$Lifecycle";
     private static final String MIDI_SERVICE_CLASS =
             "com.android.server.midi.MidiService$Lifecycle";
+    private static final String NEARBY_SERVICE_CLASS =
+            "com.android.server.nearby.NearbyService";
     private static final String WIFI_APEX_SERVICE_JAR_PATH =
             "/apex/com.android.wifi/javalib/service-wifi.jar";
     private static final String WIFI_SERVICE_CLASS =
@@ -310,6 +311,8 @@
             "com.android.clockwork.connectivity.WearConnectivityService";
     private static final String WEAR_POWER_SERVICE_CLASS =
             "com.android.clockwork.power.WearPowerService";
+    private static final String HEALTH_SERVICE_CLASS =
+            "com.google.android.clockwork.healthservices.HealthService";
     private static final String WEAR_SIDEKICK_SERVICE_CLASS =
             "com.google.android.clockwork.sidekick.SidekickService";
     private static final String WEAR_DISPLAYOFFLOAD_SERVICE_CLASS =
@@ -406,8 +409,6 @@
     private static final String SAFETY_CENTER_SERVICE_CLASS =
             "com.android.safetycenter.SafetyCenterService";
 
-    private static final String SUPPLEMENTALPROCESS_APEX_PATH =
-            "/apex/com.android.supplementalprocess/javalib/service-supplementalprocess.jar";
     private static final String SUPPLEMENTALPROCESS_SERVICE_CLASS =
             "com.android.server.supplementalprocess.SupplementalProcessManagerService$Lifecycle";
 
@@ -920,12 +921,6 @@
             startBootstrapServices(t);
             startCoreServices(t);
             startOtherServices(t);
-            // Apex services must be the last category of services to start. No other service must
-            // be starting after this point. This is to prevent unnessary stability issues when
-            // these apexes are updated outside of OTA; and to avoid breaking dependencies from
-            // system into apexes.
-            // TODO(satayev): lock mSystemServiceManager.startService to stop accepting new services
-            // after this step
             startApexServices(t);
         } catch (Throwable ex) {
             Slog.e("System", "******************************************");
@@ -1460,13 +1455,18 @@
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
             t.traceEnd();
 
-            t.traceBegin("StartTelecomLoaderService");
-            mSystemServiceManager.startService(TelecomLoaderService.class);
-            t.traceEnd();
+            // TelecomLoader hooks into classes with defined HFP logic,
+            // so check for either telephony or microphone.
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
+                mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                t.traceBegin("StartTelecomLoaderService");
+                mSystemServiceManager.startService(TelecomLoaderService.class);
+                t.traceEnd();
+            }
 
             t.traceBegin("StartTelephonyRegistry");
             telephonyRegistry = new TelephonyRegistry(
-                    context, new TelephonyRegistry.ConfigurationProvider());
+                context, new TelephonyRegistry.ConfigurationProvider());
             ServiceManager.addService("telephony.registry", telephonyRegistry);
             t.traceEnd();
 
@@ -1811,6 +1811,7 @@
             startRotationResolverService(context, t);
             startSystemCaptionsManagerService(context, t);
             startTextToSpeechManagerService(context, t);
+            startAmbientContextService(t);
 
             // System Speech Recognition Service
             t.traceBegin("StartSpeechRecognitionManagerService");
@@ -1903,8 +1904,9 @@
 
             t.traceBegin("StartNetworkStatsService");
             try {
-                networkStats = NetworkStatsService.create(context, networkManagement);
+                networkStats = NetworkStatsService.create(context);
                 ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+                TrafficStats.init(context);
             } catch (Throwable e) {
                 reportWtf("starting NetworkStats Service", e);
             }
@@ -1980,6 +1982,16 @@
             }
             t.traceEnd();
 
+            // Start Nearby Service.
+            t.traceBegin("StartNearbyService");
+            try {
+                mSystemServiceManager.startServiceFromJar(NEARBY_SERVICE_CLASS,
+                        NEARBY_SERVICE_APEX_PATH);
+            } catch (Throwable e) {
+                reportWtf("starting NearbyService", e);
+            }
+            t.traceEnd();
+
             t.traceBegin("StartConnectivityService");
             // This has to be called after NetworkManagementService, NetworkStatsService
             // and NetworkPolicyManager because ConnectivityService needs to take these
@@ -2371,8 +2383,8 @@
 
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)
                     || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
-                t.traceBegin("StartTvIAppManager");
-                mSystemServiceManager.startService(TvIAppManagerService.class);
+                t.traceBegin("StartTvInteractiveAppManager");
+                mSystemServiceManager.startService(TvInteractiveAppManagerService.class);
                 t.traceEnd();
             }
 
@@ -2500,6 +2512,10 @@
             mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS);
             t.traceEnd();
 
+            t.traceBegin("StartHealthService");
+            mSystemServiceManager.startService(HEALTH_SERVICE_CLASS);
+            t.traceEnd();
+
             t.traceBegin("StartWearConnectivityService");
             mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
             t.traceEnd();
@@ -2541,12 +2557,6 @@
                 STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH);
         t.traceEnd();
 
-        // Reboot Readiness
-        t.traceBegin("StartRebootReadinessManagerService");
-        mSystemServiceManager.startServiceFromJar(
-                REBOOT_READINESS_LIFECYCLE_CLASS, SCHEDULING_APEX_PATH);
-        t.traceEnd();
-
         // Statsd pulled atoms
         t.traceBegin("StartStatsPullAtomService");
         mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS);
@@ -2564,18 +2574,19 @@
 
         // Supplemental Process
         t.traceBegin("StartSupplementalProcessManagerService");
-        mSystemServiceManager.startServiceFromJar(SUPPLEMENTALPROCESS_SERVICE_CLASS,
-                SUPPLEMENTALPROCESS_APEX_PATH);
+        mSystemServiceManager.startService(SUPPLEMENTALPROCESS_SERVICE_CLASS);
         t.traceEnd();
 
         if (safeMode) {
             mActivityManagerService.enterSafeMode();
         }
 
-        // MMS service broker
-        t.traceBegin("StartMmsService");
-        mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
-        t.traceEnd();
+        if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            // MMS service broker
+            t.traceBegin("StartMmsService");
+            mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
+            t.traceEnd();
+        }
 
         if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOFILL)) {
             t.traceBegin("StartAutoFillService");
@@ -2669,7 +2680,7 @@
         t.traceBegin("MakePowerManagerServiceReady");
         try {
             // TODO: use boot phase
-            mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
+            mPowerManagerService.systemReady();
         } catch (Throwable e) {
             reportWtf("making Power Manager Service ready", e);
         }
@@ -2750,12 +2761,6 @@
         mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
         t.traceEnd();
 
-        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_COMMUNAL_MODE)) {
-            t.traceBegin("CommunalManagerService");
-            mSystemServiceManager.startService(CommunalManagerService.class);
-            t.traceEnd();
-        }
-
         // These are needed to propagate to the runnable below.
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
@@ -2991,9 +2996,7 @@
             t.traceEnd();
             t.traceBegin("MakeTelephonyRegistryReady");
             try {
-                if (telephonyRegistryF != null) {
-                    telephonyRegistryF.systemRunning();
-                }
+                if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
             } catch (Throwable e) {
                 reportWtf("Notifying TelephonyRegistry running", e);
             }
@@ -3007,15 +3010,15 @@
                 reportWtf("Notifying MediaRouterService running", e);
             }
             t.traceEnd();
-            t.traceBegin("MakeMmsServiceReady");
-            try {
-                if (mmsServiceF != null) {
-                    mmsServiceF.systemRunning();
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                t.traceBegin("MakeMmsServiceReady");
+                try {
+                    if (mmsServiceF != null) mmsServiceF.systemRunning();
+                } catch (Throwable e) {
+                    reportWtf("Notifying MmsService running", e);
                 }
-            } catch (Throwable e) {
-                reportWtf("Notifying MmsService running", e);
+                t.traceEnd();
             }
-            t.traceEnd();
 
             t.traceBegin("IncidentDaemonReady");
             try {
@@ -3051,11 +3054,14 @@
 
     /**
      * Starts system services defined in apexes.
+     *
+     * <p>Apex services must be the last category of services to start. No other service must be
+     * starting after this point. This is to prevent unnecessary stability issues when these apexes
+     * are updated outside of OTA; and to avoid breaking dependencies from system into apexes.
      */
     private void startApexServices(@NonNull TimingsTraceAndSlog t) {
         t.traceBegin("startApexServices");
         Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
-        // TODO(satayev): filter out already started services
         // TODO(satayev): introduce android:order for services coming the same apexes
         for (String name : new TreeSet<>(services.keySet())) {
             String jarPath = services.get(name);
@@ -3067,6 +3073,10 @@
             }
             t.traceEnd();
         }
+
+        // make sure no other services are started after this point
+        mSystemServiceManager.sealStartedServices();
+
         t.traceEnd(); // startApexServices
     }
 
@@ -3154,6 +3164,17 @@
 
     }
 
+    private void startAmbientContextService(@NonNull TimingsTraceAndSlog t) {
+        if (!AmbientContextManagerService.isDetectionServiceConfigured()) {
+            Slog.d(TAG, "AmbientContextDetectionService is not configured on this device");
+            return;
+        }
+
+        t.traceBegin("StartAmbientContextService");
+        mSystemServiceManager.startService(AmbientContextManagerService.class);
+        t.traceEnd();
+    }
+
     private static void startSystemUi(Context context, WindowManagerService windowManager) {
         PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
         Intent intent = new Intent();
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/services/midi/OWNERS
@@ -0,0 +1 @@
[email protected]
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index ca31efc..d562786 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -18,9 +18,11 @@
 
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -33,6 +35,7 @@
 import android.media.midi.IMidiDeviceOpenCallback;
 import android.media.midi.IMidiDeviceServer;
 import android.media.midi.IMidiManager;
+import android.media.midi.MidiDevice;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceService;
 import android.media.midi.MidiDeviceStatus;
@@ -55,6 +58,7 @@
 import org.xmlpull.v1.XmlPullParser;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -96,9 +100,12 @@
             = new HashMap<MidiDeviceInfo, Device>();
 
     // list of all Bluetooth devices, keyed by BluetoothDevice
-     private final HashMap<BluetoothDevice, Device> mBluetoothDevices
+    private final HashMap<BluetoothDevice, Device> mBluetoothDevices
             = new HashMap<BluetoothDevice, Device>();
 
+    private final HashMap<BluetoothDevice, MidiDevice> mBleMidiDeviceMap =
+            new HashMap<BluetoothDevice, MidiDevice>();
+
     // list of all devices, keyed by IMidiDeviceServer
     private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>();
 
@@ -569,10 +576,45 @@
         }
     }
 
+    private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "MidiService, action is null");
+                return;
+            }
+
+            switch (action) {
+                case BluetoothDevice.ACTION_ACL_CONNECTED: {
+                    Log.d(TAG, "ACTION_ACL_CONNECTED");
+                    BluetoothDevice btDevice =
+                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    openBluetoothDevice(btDevice);
+                }
+                break;
+
+                case BluetoothDevice.ACTION_ACL_DISCONNECTED: {
+                    Log.d(TAG, "ACTION_ACL_DISCONNECTED");
+                    BluetoothDevice btDevice =
+                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    closeBluetoothDevice(btDevice);
+                }
+                break;
+            }
+        }
+    };
+
     public MidiService(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
 
+        // Setup broadcast receivers
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        context.registerReceiver(mBleMidiReceiver, filter);
+
         mBluetoothServiceUid = -1;
     }
 
@@ -643,13 +685,31 @@
     private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0];
 
     public MidiDeviceInfo[] getDevices() {
+        return getDevicesForTransport(MidiManager.TRANSPORT_MIDI_BYTE_STREAM);
+    }
+
+    /**
+    * @hide
+    */
+    public MidiDeviceInfo[] getDevicesForTransport(int transport) {
         ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>();
         int uid = Binder.getCallingUid();
 
         synchronized (mDevicesByInfo) {
             for (Device device : mDevicesByInfo.values()) {
                 if (device.isUidAllowed(uid)) {
-                    deviceInfos.add(device.getDeviceInfo());
+                    // UMP devices have protocols that are not PROTOCOL_UNKNOWN
+                    if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
+                        if (device.getDeviceInfo().getDefaultProtocol()
+                                != MidiDeviceInfo.PROTOCOL_UNKNOWN) {
+                            deviceInfos.add(device.getDeviceInfo());
+                        }
+                    } else if (transport == MidiManager.TRANSPORT_MIDI_BYTE_STREAM) {
+                        if (device.getDeviceInfo().getDefaultProtocol()
+                                == MidiDeviceInfo.PROTOCOL_UNKNOWN) {
+                            deviceInfos.add(device.getDeviceInfo());
+                        }
+                    }
                 }
             }
         }
@@ -683,9 +743,43 @@
         }
     }
 
+    private void openBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        Log.d(TAG, "openBluetoothDevice() device: " + bluetoothDevice);
+
+        MidiManager midiManager = mContext.getSystemService(MidiManager.class);
+        midiManager.openBluetoothDevice(bluetoothDevice,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice device) {
+                        synchronized (mBleMidiDeviceMap) {
+                            mBleMidiDeviceMap.put(bluetoothDevice, device);
+                        }
+                    }
+                }, null);
+    }
+
+    private void closeBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        Log.d(TAG, "closeBluetoothDevice() device: " + bluetoothDevice);
+
+        MidiDevice midiDevice;
+        synchronized (mBleMidiDeviceMap) {
+            midiDevice = mBleMidiDeviceMap.remove(bluetoothDevice);
+        }
+
+        if (midiDevice != null) {
+            try {
+                midiDevice.close();
+            } catch (IOException ex) {
+                Log.e(TAG, "Exception closing BLE-MIDI device" + ex);
+            }
+        }
+    }
+
     @Override
     public void openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice,
             IMidiDeviceOpenCallback callback) {
+        Log.d(TAG, "openBluetoothDevice()");
+
         Client client = getClient(token);
         if (client == null) return;
 
@@ -718,7 +812,7 @@
     @Override
     public MidiDeviceInfo registerDeviceServer(IMidiDeviceServer server, int numInputPorts,
             int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
-            Bundle properties, int type) {
+            Bundle properties, int type, int defaultProtocol) {
         int uid = Binder.getCallingUid();
         if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
             throw new SecurityException("only system can create USB devices");
@@ -728,7 +822,8 @@
 
         synchronized (mDevicesByInfo) {
             return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames,
-                    outputPortNames, properties, server, null, false, uid);
+                    outputPortNames, properties, server, null, false, uid,
+                    defaultProtocol);
         }
     }
 
@@ -769,7 +864,15 @@
         if (device == null) {
             throw new IllegalArgumentException("no such device for " + deviceInfo);
         }
-        return device.getDeviceStatus();
+        int uid = Binder.getCallingUid();
+        if (device.isUidAllowed(uid)) {
+            return device.getDeviceStatus();
+        } else {
+            Log.e(TAG, "getDeviceStatus() invalid UID = " + uid);
+            EventLog.writeEvent(0x534e4554, "203549963",
+                    uid, "getDeviceStatus: invalid uid");
+            return null;
+        }
     }
 
     @Override
@@ -797,11 +900,12 @@
     private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts,
             String[] inputPortNames, String[] outputPortNames, Bundle properties,
             IMidiDeviceServer server, ServiceInfo serviceInfo,
-            boolean isPrivate, int uid) {
+            boolean isPrivate, int uid, int defaultProtocol) {
 
         int id = mNextDeviceId++;
         MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
-                inputPortNames, outputPortNames, properties, isPrivate);
+                inputPortNames, outputPortNames, properties, isPrivate,
+                defaultProtocol);
 
         if (server != null) {
             try {
@@ -988,10 +1092,11 @@
 
                             synchronized (mDevicesByInfo) {
                                 addDeviceLocked(MidiDeviceInfo.TYPE_VIRTUAL,
-                                    numInputPorts, numOutputPorts,
-                                    inputPortNames.toArray(EMPTY_STRING_ARRAY),
-                                    outputPortNames.toArray(EMPTY_STRING_ARRAY),
-                                    properties, null, serviceInfo, isPrivate, uid);
+                                        numInputPorts, numOutputPorts,
+                                        inputPortNames.toArray(EMPTY_STRING_ARRAY),
+                                        outputPortNames.toArray(EMPTY_STRING_ARRAY),
+                                        properties, null, serviceInfo, isPrivate, uid,
+                                        MidiDeviceInfo.PROTOCOL_UNKNOWN);
                             }
                             // setting properties to null signals that we are no longer
                             // processing a <device>
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 8203c1b..5220c8f 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -20,7 +20,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivity
+import com.android.server.pm.pkg.component.ParsedActivity
 import android.os.Binder
 import android.os.UserHandle
 import android.util.ArrayMap
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
index ebad5af..93d70bb 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
@@ -24,7 +24,7 @@
 android_test_helper_app {
     name: "PackageManagerTestAppDeclaresStaticLibrary",
     manifest: "AndroidManifestDeclaresStaticLibrary.xml",
-    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
 }
 
 android_test_helper_app {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index d7e3195..cc663d9 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -23,8 +23,7 @@
 import android.content.pm.FeatureInfo
 import android.content.pm.PackageManager
 import android.content.pm.SigningDetails
-import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.component.*
+import com.android.server.pm.pkg.parsing.ParsingPackage
 import android.net.Uri
 import android.os.Bundle
 import android.os.Parcelable
@@ -34,6 +33,18 @@
 import com.android.internal.R
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl
+import com.android.server.pm.pkg.component.ParsedAttributionImpl
+import com.android.server.pm.pkg.component.ParsedComponentImpl
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
+import com.android.server.pm.pkg.component.ParsedPermissionImpl
+import com.android.server.pm.pkg.component.ParsedProcessImpl
+import com.android.server.pm.pkg.component.ParsedProviderImpl
+import com.android.server.pm.pkg.component.ParsedServiceImpl
+import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import java.security.KeyPairGenerator
@@ -130,6 +141,7 @@
         AndroidPackage::getLabelRes,
         AndroidPackage::getLargestWidthLimitDp,
         AndroidPackage::getLogo,
+        AndroidPackage::getLocaleConfigRes,
         AndroidPackage::getManageSpaceActivityName,
         AndroidPackage::getMemtagMode,
         AndroidPackage::getMinSdkVersion,
@@ -280,7 +292,13 @@
             PackageImpl::addAttribution,
             Triple("testTag", 13, listOf("testInherit")),
             transformGet = { it.singleOrNull()?.let { Triple(it.tag, it.label, it.inheritFrom) } },
-            transformSet = { it?.let { ParsedAttributionImpl(it.first, it.second, it.third) } }
+            transformSet = { it?.let {
+                ParsedAttributionImpl(
+                    it.first,
+                    it.second,
+                    it.third
+                )
+            } }
         ),
         getSetByValue2(
             AndroidPackage::getKeySetMapping,
@@ -293,12 +311,14 @@
             PackageImpl::addPermissionGroup,
             "test.permission.GROUP",
             transformGet = { it.singleOrNull()?.name },
-            transformSet = { ParsedPermissionGroupImpl().apply { setName(it) } }
+            transformSet = { ParsedPermissionGroupImpl()
+                .apply { setName(it) } }
         ),
         getSetByValue2(
             AndroidPackage::getPreferredActivityFilters,
             PackageImpl::addPreferredActivityFilter,
-            "TestClassName" to ParsedIntentInfoImpl().apply {
+            "TestClassName" to ParsedIntentInfoImpl()
+                .apply {
                 intentFilter.apply {
                     addDataScheme("http")
                     addDataAuthority("test.pm.server.android.com", null)
@@ -347,42 +367,48 @@
             PackageImpl::addActivity,
             "TestActivityName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedActivityImpl()
+                .apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getApexSystemServices,
             PackageImpl::addApexSystemService,
             "TestApexSystemServiceName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedApexSystemServiceImpl().apply { name = it } }
+            transformSet = { ParsedApexSystemServiceImpl()
+                .apply { name = it } }
         ),
         getSetByValue(
             AndroidPackage::getReceivers,
             PackageImpl::addReceiver,
             "TestReceiverName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedActivityImpl()
+                .apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getServices,
             PackageImpl::addService,
             "TestServiceName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedServiceImpl().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedServiceImpl()
+                .apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getProviders,
             PackageImpl::addProvider,
             "TestProviderName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedProviderImpl().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedProviderImpl()
+                .apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getInstrumentations,
             PackageImpl::addInstrumentation,
             "TestInstrumentationName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedInstrumentationImpl().apply { name = it } }
+            transformSet = { ParsedInstrumentationImpl()
+                .apply { name = it } }
         ),
         getSetByValue(
             AndroidPackage::getConfigPreferences,
@@ -407,7 +433,8 @@
             PackageImpl::addPermission,
             "test.PERMISSION",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedPermissionImpl().apply { name = it } }
+            transformSet = { ParsedPermissionImpl()
+                .apply { name = it } }
         ),
         getSetByValue(
             AndroidPackage::getUsesPermissions,
@@ -418,7 +445,12 @@
                 it.filterNot { it.name == "test.implicit.PERMISSION" }
                     .singleOrNull()?.name.orEmpty()
             },
-            transformSet = { ParsedUsesPermissionImpl(it, 0) }
+            transformSet = {
+                ParsedUsesPermissionImpl(
+                    it,
+                    0
+                )
+            }
         ),
         getSetByValue(
             AndroidPackage::getRequestedFeatures,
@@ -443,7 +475,8 @@
         getSetByValue(
             AndroidPackage::getProcesses,
             PackageImpl::setProcesses,
-            mapOf("testProcess" to ParsedProcessImpl().apply { name = "testProcessName" }),
+            mapOf("testProcess" to ParsedProcessImpl()
+                .apply { name = "testProcessName" }),
             compare = { first, second ->
                 equalBy(
                     first, second,
@@ -470,6 +503,11 @@
                     PackageManager.Property::getString
                 )
             }
+        ),
+        getSetByValue(
+            AndroidPackage::shouldInheritKeyStoreKeys,
+            ParsingPackage::setInheritKeyStoreKeys,
+            true
         )
     )
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 8170acf..a89b717 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -17,8 +17,8 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.ActivityInfo
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedActivity
+import com.android.server.pm.pkg.component.ParsedActivityImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
@@ -27,7 +27,8 @@
     ParsedActivityImpl::class
 ) {
 
-    override val defaultImpl = ParsedActivityImpl()
+    override val defaultImpl =
+        ParsedActivityImpl()
     override val creator = ParsedActivityImpl.CREATOR
 
     override val mainComponentSubclassBaseParams = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
index 503301b..4e44e96 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
@@ -16,15 +16,21 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedAttribution
-import android.content.pm.parsing.component.ParsedAttributionImpl
+import com.android.server.pm.pkg.component.ParsedAttribution
+import com.android.server.pm.pkg.component.ParsedAttributionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedAttributionTest : ParcelableComponentTest(ParsedAttribution::class,
+class ParsedAttributionTest : ParcelableComponentTest(
+    ParsedAttribution::class,
     ParsedAttributionImpl::class) {
 
-    override val defaultImpl = ParsedAttributionImpl("", 0, emptyList())
+    override val defaultImpl =
+        ParsedAttributionImpl(
+            "",
+            0,
+            emptyList()
+        )
     override val creator = ParsedAttributionImpl.CREATOR
 
     override val baseParams = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
index e978dd38..058f6d69 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
@@ -17,10 +17,9 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedComponent
-import android.content.pm.parsing.component.ParsedComponentImpl
-import android.content.pm.parsing.component.ParsedIntentInfo
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedComponent
+import com.android.server.pm.pkg.component.ParsedComponentImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import android.os.Bundle
 import android.os.Parcelable
 import kotlin.contracts.ExperimentalContracts
@@ -65,7 +64,8 @@
             ParsedComponentImpl::addIntent,
             "TestLabel",
             transformGet = { it.singleOrNull()?.nonLocalizedLabel },
-            transformSet = { ParsedIntentInfoImpl().setNonLocalizedLabel(it!!) },
+            transformSet = { ParsedIntentInfoImpl()
+                .setNonLocalizedLabel(it!!) },
         ),
         getSetByValue(
             ParsedComponent::getProperties,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
index f15b911..eeb30b7 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedInstrumentation
-import android.content.pm.parsing.component.ParsedInstrumentationImpl
+import com.android.server.pm.pkg.component.ParsedInstrumentation
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
@@ -26,7 +26,8 @@
     ParsedInstrumentationImpl::class
 ) {
 
-    override val defaultImpl = ParsedInstrumentationImpl()
+    override val defaultImpl =
+        ParsedInstrumentationImpl()
     override val creator = ParsedInstrumentationImpl.CREATOR
 
     override val subclassBaseParams = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
index f04e851..f27a51f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedIntentInfo
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfo
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import android.os.Parcelable
 import android.os.PatternMatcher
 import kotlin.contracts.ExperimentalContracts
@@ -28,7 +28,8 @@
     ParsedIntentInfoImpl::class,
 ) {
 
-    override val defaultImpl = ParsedIntentInfoImpl()
+    override val defaultImpl =
+        ParsedIntentInfoImpl()
     override val creator = ParsedIntentInfoImpl.CREATOR
 
     override val excludedMethods = listOf(
@@ -43,7 +44,8 @@
         ParsedIntentInfo::getNonLocalizedLabel,
     )
 
-    override fun initialObject() = ParsedIntentInfoImpl().apply {
+    override fun initialObject() = ParsedIntentInfoImpl()
+        .apply {
         intentFilter.apply {
             addAction("test.ACTION")
             addDataAuthority("testAuthority", "404")
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
index 214734a..a0d8c44 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
@@ -16,9 +16,8 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedMainComponent
-import android.content.pm.parsing.component.ParsedMainComponentImpl
-import android.content.pm.parsing.component.ParsedService
+import com.android.server.pm.pkg.component.ParsedMainComponent
+import com.android.server.pm.pkg.component.ParsedMainComponentImpl
 import android.os.Parcelable
 import java.util.Arrays
 import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
index f876ed0..57562ef 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedPermissionGroup
-import android.content.pm.parsing.component.ParsedPermissionGroupImpl
+import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
@@ -26,7 +26,8 @@
     ParsedPermissionGroupImpl::class,
 ) {
 
-    override val defaultImpl = ParsedPermissionGroupImpl()
+    override val defaultImpl =
+        ParsedPermissionGroupImpl()
     override val creator = ParsedPermissionGroupImpl.CREATOR
 
     override val subclassBaseParams = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
index 6f48e24..c72a44e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
@@ -16,10 +16,10 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedPermission
-import android.content.pm.parsing.component.ParsedPermissionGroup
-import android.content.pm.parsing.component.ParsedPermissionGroupImpl
-import android.content.pm.parsing.component.ParsedPermissionImpl
+import com.android.server.pm.pkg.component.ParsedPermission
+import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
+import com.android.server.pm.pkg.component.ParsedPermissionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
@@ -28,7 +28,8 @@
     ParsedPermissionImpl::class
 ) {
 
-    override val defaultImpl = ParsedPermissionImpl()
+    override val defaultImpl =
+        ParsedPermissionImpl()
     override val creator = ParsedPermissionImpl.CREATOR
 
     override val subclassExcludedMethods = listOf(
@@ -53,7 +54,8 @@
         getSetByValue(
             ParsedPermission::getParsedPermissionGroup,
             ParsedPermissionImpl::setParsedPermissionGroup,
-            ParsedPermissionGroupImpl().apply { name = "test.permission.group" },
+            ParsedPermissionGroupImpl()
+                .apply { name = "test.permission.group" },
             compare = { first, second -> equalBy(first, second, ParsedPermissionGroup::getName) }
         ),
     )
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index 005d3e8..8b9361a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -16,15 +16,16 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedProcess
-import android.content.pm.parsing.component.ParsedProcessImpl
+import com.android.server.pm.pkg.component.ParsedProcess
+import com.android.server.pm.pkg.component.ParsedProcessImpl
 import android.util.ArrayMap
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
 class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedProcessImpl::class) {
 
-    override val defaultImpl = ParsedProcessImpl()
+    override val defaultImpl =
+        ParsedProcessImpl()
     override val creator = ParsedProcessImpl.CREATOR
 
     override val excludedMethods = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index 78e4b79..037da24 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -17,15 +17,16 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.PathPermission
-import android.content.pm.parsing.component.ParsedProvider
-import android.content.pm.parsing.component.ParsedProviderImpl
+import com.android.server.pm.pkg.component.ParsedProvider
+import com.android.server.pm.pkg.component.ParsedProviderImpl
 import android.os.PatternMatcher
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
 class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class, ParsedProviderImpl::class) {
 
-    override val defaultImpl = ParsedProviderImpl()
+    override val defaultImpl =
+        ParsedProviderImpl()
     override val creator = ParsedProviderImpl.CREATOR
 
     override val mainComponentSubclassBaseParams = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
index 9363aa37..e2c9439 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
@@ -16,14 +16,15 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedService
-import android.content.pm.parsing.component.ParsedServiceImpl
+import com.android.server.pm.pkg.component.ParsedService
+import com.android.server.pm.pkg.component.ParsedServiceImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
 class ParsedServiceTest : ParsedMainComponentTest(ParsedService::class, ParsedServiceImpl::class) {
 
-    override val defaultImpl = ParsedServiceImpl()
+    override val defaultImpl =
+        ParsedServiceImpl()
     override val creator = ParsedServiceImpl.CREATOR
 
     override val mainComponentSubclassBaseParams = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
index 81e800f..ad60736 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import android.content.pm.parsing.component.ParsedUsesPermission
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl
+import com.android.server.pm.pkg.component.ParsedUsesPermission
+import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
@@ -26,7 +26,8 @@
     ParsedUsesPermissionImpl::class
 ) {
 
-    override val defaultImpl = ParsedUsesPermissionImpl("", 0)
+    override val defaultImpl =
+        ParsedUsesPermissionImpl("", 0)
     override val creator = ParsedUsesPermissionImpl.CREATOR
 
     override val baseParams = listOf(
@@ -34,5 +35,6 @@
         ParsedUsesPermission::getUsesPermissionFlags
     )
 
-    override fun initialObject() = ParsedUsesPermissionImpl("", 0)
+    override fun initialObject() =
+        ParsedUsesPermissionImpl("", 0)
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 33234d5..652dc38 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.Intent
 import android.content.pm.ApplicationInfo
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import android.os.Build
 import android.os.PatternMatcher
 import android.util.ArraySet
@@ -94,7 +94,8 @@
             val activityList = listOf(
                 ParsedActivityImpl().apply {
                     addIntent(
-                        ParsedIntentInfoImpl().apply {
+                        ParsedIntentInfoImpl()
+                            .apply {
                             intentFilter.apply {
                                 addAction(Intent.ACTION_VIEW)
                                 addCategory(Intent.CATEGORY_BROWSABLE)
@@ -110,7 +111,8 @@
                 },
                 ParsedActivityImpl().apply {
                     addIntent(
-                        ParsedIntentInfoImpl().apply {
+                        ParsedIntentInfoImpl()
+                            .apply {
                             intentFilter.apply {
                                 setAutoVerify(true)
                                 addAction(Intent.ACTION_VIEW)
@@ -270,7 +272,8 @@
             val activityList = listOf(
                     ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
@@ -285,7 +288,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     addAction(Intent.ACTION_VIEW)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -300,7 +304,8 @@
                     },
                     ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
@@ -316,7 +321,8 @@
                     },
                     ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
@@ -329,7 +335,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
@@ -342,7 +349,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -355,7 +363,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -365,7 +374,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -375,7 +385,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 089e9db..92cdb34 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -20,8 +20,8 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.SigningDetails
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Build
@@ -308,7 +308,8 @@
                 listOf(
                     ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     autoVerify = true
                                     addAction(Intent.ACTION_VIEW)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 334f503..878bee0 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -19,8 +19,8 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainOwner
 import android.content.pm.verify.domain.DomainVerificationInfo
@@ -526,7 +526,8 @@
                 ParsedActivityImpl().apply {
                     domains.forEach {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     autoVerify = true
                                     addAction(Intent.ACTION_VIEW)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index fb581d7..0369bab 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -20,8 +20,8 @@
 import android.content.pm.PackageManager
 import android.content.pm.Signature
 import android.content.pm.SigningDetails
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainOwner
 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
@@ -867,7 +867,8 @@
             whenever(targetSdkVersion) { Build.VERSION_CODES.S }
             whenever(isEnabled) { true }
 
-            fun baseIntent(domain: String) = ParsedIntentInfoImpl().apply {
+            fun baseIntent(domain: String) = ParsedIntentInfoImpl()
+                .apply {
                 intentFilter.apply {
                     addAction(Intent.ACTION_VIEW)
                     addCategory(Intent.CATEGORY_BROWSABLE)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index a397d56..3a602a8 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -19,8 +19,8 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Build
@@ -196,7 +196,8 @@
                 listOf(
                     ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     autoVerify = true
                                     addAction(Intent.ACTION_VIEW)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 728da49..ffc2877 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.Intent
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.verify.domain.DomainVerificationState
 import android.content.pm.verify.domain.DomainVerificationUserState
@@ -112,7 +112,8 @@
                 val activityList = listOf(
                     ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     autoVerify = true
                                     addAction(Intent.ACTION_VIEW)
@@ -126,7 +127,8 @@
                             }
                         )
                         addIntent(
-                            ParsedIntentInfoImpl().apply {
+                            ParsedIntentInfoImpl()
+                                .apply {
                                 intentFilter.apply {
                                     autoVerify = true
                                     addAction(Intent.ACTION_VIEW)
diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp
index 01e90a8..a6ed1ae 100644
--- a/services/tests/apexsystemservices/Android.bp
+++ b/services/tests/apexsystemservices/Android.bp
@@ -37,5 +37,8 @@
         "truth-prebuilt",
         "modules-utils-build-testing",
     ],
-    test_suites: ["device-tests"],
+    test_suites: [
+        "device-tests",
+        "mts-core",
+    ],
 }
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 8538603..36246e5 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -14,7 +14,7 @@
 java_defaults {
     name: "FrameworkMockingServicesTests-jni-defaults",
     jni_libs: [
-        "libactivitymanagermockingservicestestjni",
+        "libmockingservicestestjni",
     ],
 }
 
@@ -40,26 +40,26 @@
     ],
 
     static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.ext.truth",
         "frameworks-base-testutils",
+        "hamcrest-library",
+        "kotlin-test",
+        "mockingservicestests-utils-mockito",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "service-blobstore",
+        "service-jobscheduler",
+        "service-permission.impl",
         "services.core",
         "services.devicepolicy",
         "services.net",
         "services.usage",
-        "service-jobscheduler",
-        "service-permission.impl",
-        "service-blobstore",
-        "androidx.test.core",
-        "androidx.test.runner",
-        "androidx.test.ext.truth",
-        "mockito-target-extended-minus-junit4",
-        "platform-test-annotations",
-        "truth-prebuilt",
-        "hamcrest-library",
-        "servicestests-utils-mockito-extended",
-        "mockingservicestests-utils-mockito",
         "servicestests-core-utils",
+        "servicestests-utils-mockito-extended",
         "testables",
-        "kotlin-test",
+        "truth-prebuilt",
         // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
         "testng",
     ],
diff --git a/services/tests/mockingservicestests/OWNERS b/services/tests/mockingservicestests/OWNERS
index 0fb0c30..2bb1649 100644
--- a/services/tests/mockingservicestests/OWNERS
+++ b/services/tests/mockingservicestests/OWNERS
@@ -1 +1,5 @@
 include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
+per-file FakeGameClassifier.java = file:/GAME_MANAGER_OWNERS
+per-file FakeGameServiceProviderInstance = file:/GAME_MANAGER_OWNERS
+per-file FakeServiceConnector.java = file:/GAME_MANAGER_OWNERS
+per-file Game* = file:/GAME_MANAGER_OWNERS
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index a32bf2c..89b204b 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -8,7 +8,7 @@
 }
 
 cc_library_shared {
-    name: "libactivitymanagermockingservicestestjni",
+    name: "libmockingservicestestjni",
 
     cflags: [
         "-Wall",
@@ -19,12 +19,15 @@
 
     srcs: [
         ":lib_cachedAppOptimizer_native",
+        ":lib_gameManagerService_native",
         "onload.cpp",
     ],
 
     include_dirs: [
         "frameworks/base/libs",
         "frameworks/native/services",
+        "frameworks/native/libs/math/include",
+        "frameworks/native/libs/ui/include",
         "system/memory/libmeminfo/include",
     ],
 
@@ -33,10 +36,19 @@
         "libandroid_runtime",
         "libbase",
         "libbinder",
+        "libgralloctypes",
+        "libgui",
+        "libhidlbase",
         "liblog",
         "libmeminfo",
         "libnativehelper",
         "libprocessgroup",
         "libutils",
+        "[email protected]",
+        "[email protected]",
+        "[email protected]",
+        "android.hardware.graphics.common-V3-ndk",
+        "[email protected]",
+        "[email protected]",
     ],
 }
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index 147cc47..23ccb22 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -25,6 +25,7 @@
 
 namespace android {
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+int register_android_server_app_GameManagerService(JNIEnv* env);
 };
 
 using namespace android;
@@ -40,6 +41,7 @@
     }
     ALOG_ASSERT(env, "Could not retrieve the env!");
     register_android_server_am_CachedAppOptimizer(env);
+    register_android_server_app_GameManagerService(env);
     return JNI_VERSION_1_4;
 }
 
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
new file mode 100644
index 0000000..fec9b12
--- /dev/null
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.graphics.Bitmap;
+import android.platform.test.annotations.Presubmit;
+import android.service.games.GameSession.ScreenshotCallback;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControlViewHost;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.infra.AndroidFuture;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for the {@link android.service.games.GameSession}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@Presubmit
[email protected](setAsMainLooper = true)
+public final class GameSessionTest {
+    private static final long WAIT_FOR_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
+    private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+
+    @Mock
+    private IGameSessionController mMockGameSessionController;
+    @Mock
+    SurfaceControlViewHost mSurfaceControlViewHost;
+    private LifecycleTrackingGameSession mGameSession;
+    private MockitoSession mMockitoSession;
+
+    @Before
+    public void setUp() {
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .startMocking();
+
+        mGameSession = new LifecycleTrackingGameSession() {};
+        mGameSession.attach(mMockGameSessionController, /* taskId= */ 10,
+                InstrumentationRegistry.getContext(),
+                mSurfaceControlViewHost,
+                /* widthPx= */ 0, /* heightPx= */0);
+    }
+
+    @After
+    public void tearDown() {
+        mMockitoSession.finishMocking();
+    }
+
+    @Test
+    public void takeScreenshot_attachNotCalled_throwsIllegalStateException() throws Exception {
+        GameSession gameSession = new GameSession() {};
+
+        try {
+            gameSession.takeScreenshot(DIRECT_EXECUTOR,
+                    new ScreenshotCallback() {
+                        @Override
+                        public void onFailure(int statusCode) {
+                            fail();
+                        }
+
+                        @Override
+                        public void onSuccess(Bitmap bitmap) {
+                            fail();
+                        }
+                    });
+            fail();
+        } catch (IllegalStateException expected) {
+
+        }
+    }
+
+    @Test
+    public void takeScreenshot_gameManagerException_returnsInternalError() throws Exception {
+        doAnswer(invocation -> {
+            AndroidFuture result = invocation.getArgument(1);
+            result.completeExceptionally(new Exception());
+            return null;
+        }).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
+
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        mGameSession.takeScreenshot(DIRECT_EXECUTOR,
+                new ScreenshotCallback() {
+                    @Override
+                    public void onFailure(int statusCode) {
+                        assertEquals(ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
+                                statusCode);
+                        countDownLatch.countDown();
+                    }
+
+                    @Override
+                    public void onSuccess(Bitmap bitmap) {
+                        fail();
+                    }
+                });
+
+        assertTrue(countDownLatch.await(
+                WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void takeScreenshot_gameManagerError_returnsInternalError() throws Exception {
+        doAnswer(invocation -> {
+            AndroidFuture result = invocation.getArgument(1);
+            result.complete(GameScreenshotResult.createInternalErrorResult());
+            return null;
+        }).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
+
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        mGameSession.takeScreenshot(DIRECT_EXECUTOR,
+                new ScreenshotCallback() {
+                    @Override
+                    public void onFailure(int statusCode) {
+                        assertEquals(ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
+                                statusCode);
+                        countDownLatch.countDown();
+                    }
+
+                    @Override
+                    public void onSuccess(Bitmap bitmap) {
+                        fail();
+                    }
+                });
+
+        assertTrue(countDownLatch.await(
+                WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void takeScreenshot_gameManagerSuccess_returnsBitmap() throws Exception {
+        doAnswer(invocation -> {
+            AndroidFuture result = invocation.getArgument(1);
+            result.complete(GameScreenshotResult.createSuccessResult(TEST_BITMAP));
+            return null;
+        }).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
+
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        mGameSession.takeScreenshot(DIRECT_EXECUTOR,
+                new ScreenshotCallback() {
+                    @Override
+                    public void onFailure(int statusCode) {
+                        fail();
+                    }
+
+                    @Override
+                    public void onSuccess(Bitmap bitmap) {
+                        assertEquals(TEST_BITMAP, bitmap);
+                        countDownLatch.countDown();
+                    }
+                });
+
+        assertTrue(countDownLatch.await(
+                WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void moveState_InitializedToInitialized_noLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.INITIALIZED);
+
+        assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+    }
+
+    @Test
+    public void moveState_FullLifecycle_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+    }
+
+    @Test
+    public void moveState_DestroyedWhenInitialized_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+        // ON_CREATE is always called before ON_DESTROY.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+    }
+
+    @Test
+    public void moveState_DestroyedWhenFocused_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+        // The ON_GAME_TASK_UNFOCUSED lifecycle event is implied because the session is destroyed
+        // while in focus.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+    }
+
+    @Test
+    public void moveState_FocusCycled_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+        // Both cycles from focus and unfocus are captured.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED).inOrder();
+    }
+
+    @Test
+    public void moveState_MultipleFocusAndUnfocusCalls_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+        // The second TASK_FOCUSED call and the second TASK_UNFOCUSED call are ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED).inOrder();
+    }
+
+    @Test
+    public void moveState_CreatedAfterFocused_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+
+        // The second CREATED call is ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED).inOrder();
+    }
+
+    @Test
+    public void moveState_UnfocusedWithoutFocused_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+        // The TASK_UNFOCUSED call without an earlier TASK_FOCUSED call is ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE).inOrder();
+    }
+
+    @Test
+    public void moveState_NeverFocused_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+    }
+
+    @Test
+    public void moveState_MultipleFocusCalls_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+        // The extra TASK_FOCUSED moves are ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED).inOrder();
+    }
+
+    @Test
+    public void moveState_MultipleCreateCalls_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+
+        // The extra CREATE moves are ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE).inOrder();
+    }
+
+    @Test
+    public void moveState_FocusBeforeCreate_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+        // The TASK_FOCUSED move before CREATE is ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+    }
+
+    @Test
+    public void moveState_UnfocusBeforeCreate_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+        // The TASK_UNFOCUSED move before CREATE is ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+    }
+
+    @Test
+    public void moveState_FocusWhenDestroyed_ExpectedLifecycleCalls() throws Exception {
+        mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+        mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+        mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+        // The TASK_FOCUSED move after DESTROYED is ignored.
+        assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+                LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+    }
+
+    private static class LifecycleTrackingGameSession extends GameSession {
+        private enum LifecycleMethodCall {
+            ON_CREATE,
+            ON_DESTROY,
+            ON_GAME_TASK_FOCUSED,
+            ON_GAME_TASK_UNFOCUSED
+        }
+
+        final List<LifecycleMethodCall> mLifecycleMethodCalls = new ArrayList<>();
+
+        @Override
+        public void onCreate() {
+            mLifecycleMethodCalls.add(LifecycleMethodCall.ON_CREATE);
+        }
+
+        @Override
+        public void onDestroy() {
+            mLifecycleMethodCalls.add(LifecycleMethodCall.ON_DESTROY);
+        }
+
+        @Override
+        public void onGameTaskFocusChanged(boolean focused) {
+            if (focused) {
+                mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_FOCUSED);
+            } else {
+                mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED);
+            }
+        }
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
index 5a6275d..cc97b8f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
@@ -24,7 +24,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.same;
@@ -226,7 +225,7 @@
     }
 
     private void expectWipeNonSystemUser() {
-        when(mUserManager.removeUserOrSetEphemeral(anyInt(), anyBoolean()))
+        when(mUserManager.removeUserWhenPossible(any(), anyBoolean()))
                 .thenReturn(UserManager.REMOVE_RESULT_REMOVED);
     }
 
@@ -266,7 +265,7 @@
     }
 
     private void verifyWipeNonSystemUser() {
-        verify(mUserManager).removeUserOrSetEphemeral(anyInt(), anyBoolean());
+        verify(mUserManager).removeUserWhenPossible(any(), anyBoolean());
     }
 
     private void setPendingResultForUser(int userId) {
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 1c21645..0198253 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -28,6 +29,7 @@
 
 import android.Manifest;
 import android.app.GameManager;
+import android.app.GameModeInfo;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
@@ -190,7 +192,7 @@
     }
 
     private void mockDeviceConfigPerformance() {
-        String configString = "mode=2,downscaleFactor=0.5,useAngle=false";
+        String configString = "mode=2,downscaleFactor=0.5,useAngle=false,fps=90";
         when(DeviceConfig.getProperty(anyString(), anyString()))
                 .thenReturn(configString);
     }
@@ -203,13 +205,13 @@
     }
 
     private void mockDeviceConfigBattery() {
-        String configString = "mode=3,downscaleFactor=0.7";
+        String configString = "mode=3,downscaleFactor=0.7,fps=30";
         when(DeviceConfig.getProperty(anyString(), anyString()))
                 .thenReturn(configString);
     }
 
     private void mockDeviceConfigAll() {
-        String configString = "mode=3,downscaleFactor=0.7:mode=2,downscaleFactor=0.5";
+        String configString = "mode=3,downscaleFactor=0.7,fps=30:mode=2,downscaleFactor=0.5,fps=90";
         when(DeviceConfig.getProperty(anyString(), anyString()))
                 .thenReturn(configString);
     }
@@ -454,12 +456,12 @@
                 gameManagerService.getGameMode(mPackageName, USER_ID_2));
     }
 
-    private void checkReportedModes(int ...requiredModes) {
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-
-        startUser(gameManagerService, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+    private void checkReportedModes(GameManagerService gameManagerService, int ...requiredModes) {
+        if (gameManagerService == null) {
+            gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
+            startUser(gameManagerService, USER_ID_1);
+            gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        }
         ArraySet<Integer> reportedModes = new ArraySet<>();
         int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
         for (int mode : modes) {
@@ -472,12 +474,13 @@
         }
     }
 
-    private void checkDownscaling(int gameMode, String scaling) {
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-
-        startUser(gameManagerService, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+    private void checkDownscaling(GameManagerService gameManagerService,
+                int gameMode, String scaling) {
+        if (gameManagerService == null) {
+            gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
+            startUser(gameManagerService, USER_ID_1);
+            gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        }
         GameManagerService.GamePackageConfiguration config =
                 gameManagerService.getConfig(mPackageName);
         assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
@@ -496,6 +499,17 @@
         assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
     }
 
+    private void checkFps(GameManagerService gameManagerService, int gameMode, int fps) {
+        if (gameManagerService == null) {
+            gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
+            startUser(gameManagerService, USER_ID_1);
+            gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        }
+        GameManagerService.GamePackageConfiguration config =
+                gameManagerService.getConfig(mPackageName);
+        assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps);
+    }
+
     /**
      * Phenotype device config exists, but is only propagating the default value.
      */
@@ -503,7 +517,7 @@
     public void testDeviceConfigDefault() {
         mockDeviceConfigDefault();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+        checkReportedModes(null);
     }
 
     /**
@@ -513,7 +527,7 @@
     public void testDeviceConfigNone() {
         mockDeviceConfigNone();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+        checkReportedModes(null);
     }
 
     /**
@@ -523,7 +537,7 @@
     public void testDeviceConfigPerformance() {
         mockDeviceConfigPerformance();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
+        checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
     }
 
     /**
@@ -533,7 +547,7 @@
     public void testDeviceConfigBattery() {
         mockDeviceConfigBattery();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+        checkReportedModes(null, GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
     }
 
     /**
@@ -543,7 +557,7 @@
     public void testDeviceConfigAll() {
         mockDeviceConfigAll();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+        checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
                 GameManager.GAME_MODE_STANDARD);
     }
 
@@ -554,7 +568,7 @@
     public void testDeviceConfigInvalid() {
         mockDeviceConfigInvalid();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+        checkReportedModes(null);
     }
 
     /**
@@ -564,7 +578,171 @@
     public void testDeviceConfigMalformed() {
         mockDeviceConfigMalformed();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+        checkReportedModes(null);
+    }
+
+    /**
+     * Override device config for performance mode exists and is valid.
+     */
+    @Test
+    public void testSetDeviceConfigOverridePerformance() {
+        mockDeviceConfigPerformance();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+                GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+        checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
+    }
+
+    /**
+     * Override device config for battery mode exists and is valid.
+     */
+    @Test
+    public void testSetDeviceConfigOverrideBattery() {
+        mockDeviceConfigBattery();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
+                GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+        checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
+    }
+
+    /**
+     * Override device configs for both battery and performance modes exists and are valid.
+     */
+    @Test
+    public void testSetDeviceOverrideConfigAll() {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+                GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+        checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+        checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
+    }
+
+    /**
+     * Override device config for performance mode exists and is valid.
+     */
+    @Test
+    public void testResetDeviceConfigOverridePerformance() {
+        mockDeviceConfigPerformance();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+
+        gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_PERFORMANCE);
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+                GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
+    }
+
+    /**
+     * Override device config for battery mode exists and is valid.
+     */
+    @Test
+    public void testResetDeviceConfigOverrideBattery() {
+        mockDeviceConfigBattery();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+        gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY);
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
+                GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+        checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
+    }
+
+    /**
+     * Override device configs for both battery and performance modes exists and are valid.
+     */
+    @Test
+    public void testResetDeviceOverrideConfigAll() {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+        gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1, -1);
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+                GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+        checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
+    }
+
+    /**
+     * Override device configs for both battery and performance modes exists and are valid.
+     * Only one mode is reset, and the other mode still has overridden config
+     */
+    @Test
+    public void testResetDeviceOverrideConfigPartial() {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+
+        GameManagerService gameManagerService = new GameManagerService(
+                mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+        gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+        gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1,
+                GameManager.GAME_MODE_BATTERY);
+
+        checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+                GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+        checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+        checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
     }
 
     /**
@@ -575,10 +753,12 @@
         mockGameModeOptInAll();
         mockDeviceConfigNone();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+        checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
                 GameManager.GAME_MODE_STANDARD);
     }
 
+
+
     /**
      * BATTERY game mode is available through the app manifest opt-in.
      */
@@ -587,7 +767,7 @@
         mockGameModeOptInBattery();
         mockDeviceConfigNone();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+        checkReportedModes(null, GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
     }
 
     /**
@@ -598,7 +778,7 @@
         mockGameModeOptInPerformance();
         mockDeviceConfigNone();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
+        checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
     }
 
     /**
@@ -610,7 +790,7 @@
         mockGameModeOptInBattery();
         mockDeviceConfigPerformance();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+        checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
                 GameManager.GAME_MODE_STANDARD);
     }
 
@@ -623,7 +803,7 @@
         mockGameModeOptInPerformance();
         mockDeviceConfigBattery();
         mockModifyGameModeGranted();
-        checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+        checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
                 GameManager.GAME_MODE_STANDARD);
     }
 
@@ -634,7 +814,7 @@
     public void testInterventionAllowScalingDefault() throws Exception {
         mockDeviceConfigPerformance();
         mockModifyGameModeGranted();
-        checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
     }
 
     /**
@@ -645,7 +825,7 @@
         mockDeviceConfigPerformance();
         mockInterventionAllowDownscaleFalse();
         mockModifyGameModeGranted();
-        checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "1.0");
+        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "1.0");
     }
 
     /**
@@ -657,7 +837,7 @@
         mockDeviceConfigPerformance();
         mockInterventionAllowDownscaleTrue();
         mockModifyGameModeGranted();
-        checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
     }
 
     /**
@@ -708,6 +888,14 @@
         checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true);
     }
 
+    @Test
+    public void testInterventionFps() throws Exception {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        checkFps(null, GameManager.GAME_MODE_PERFORMANCE, 90);
+        checkFps(null, GameManager.GAME_MODE_BATTERY, 30);
+    }
+
     /**
      * PERFORMANCE game mode is configured through Phenotype, but the app has also opted into the
      * same mode. No interventions for this game mode should be available in this case.
@@ -776,4 +964,88 @@
         assertEquals(GameManager.GAME_MODE_STANDARD,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
     }
+
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
+    @Test
+    public void testGetGameModeInfoPermissionDenied() {
+        mockDeviceConfigAll();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+
+        // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+        mockModifyGameModeDenied();
+        assertThrows(SecurityException.class,
+                () -> gameManagerService.getGameModeInfo(mPackageName, USER_ID_1));
+    }
+
+    @Test
+    public void testGetGameModeInfoWithAllGameModesDefault() {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+        assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode());
+        assertEquals(3, gameModeInfo.getAvailableGameModes().length);
+    }
+
+    @Test
+    public void testGetGameModeInfoWithAllGameModes() {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode());
+        assertEquals(3, gameModeInfo.getAvailableGameModes().length);
+    }
+
+    @Test
+    public void testGetGameModeInfoWithBatteryMode() {
+        mockDeviceConfigBattery();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
+        GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+        assertEquals(GameManager.GAME_MODE_BATTERY, gameModeInfo.getActiveGameMode());
+        assertEquals(2, gameModeInfo.getAvailableGameModes().length);
+    }
+
+    @Test
+    public void testGetGameModeInfoWithPerformanceMode() {
+        mockDeviceConfigPerformance();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode());
+        assertEquals(2, gameModeInfo.getAvailableGameModes().length);
+    }
+
+    @Test
+    public void testGetGameModeInfoWithUnsupportedGameMode() {
+        mockDeviceConfigNone();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+        assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode());
+        assertEquals(0, gameModeInfo.getAvailableGameModes().length);
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index b6c706e..bdfa3bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -18,26 +18,43 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.server.app.GameServiceProviderInstanceImplTest.FakeGameService.GameServiceState;
 
+import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
 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.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.ITaskStackListener;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
-import android.os.IBinder;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.service.games.CreateGameSessionRequest;
+import android.service.games.CreateGameSessionResult;
+import android.service.games.GameScreenshotResult;
+import android.service.games.GameSessionViewHostConfiguration;
+import android.service.games.GameStartedEvent;
 import android.service.games.IGameService;
+import android.service.games.IGameServiceController;
 import android.service.games.IGameSession;
+import android.service.games.IGameSessionController;
 import android.service.games.IGameSessionService;
+import android.view.SurfaceControlViewHost.SurfacePackage;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -45,19 +62,21 @@
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+import com.android.internal.util.Preconditions;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerService;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
+import java.util.HashMap;
 
 
 /**
@@ -68,6 +87,9 @@
 @Presubmit
 public final class GameServiceProviderInstanceImplTest {
 
+    private static final GameSessionViewHostConfiguration
+            DEFAULT_GAME_SESSION_VIEW_HOST_CONFIGURATION =
+            new GameSessionViewHostConfiguration(1, 500, 800);
     private static final int USER_ID = 10;
     private static final String APP_A_PACKAGE = "com.package.app.a";
     private static final ComponentName APP_A_MAIN_ACTIVITY =
@@ -77,19 +99,23 @@
     private static final ComponentName GAME_A_MAIN_ACTIVITY =
             new ComponentName(GAME_A_PACKAGE, "com.package.game.a.MainActivity");
 
+    private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
+
     private MockitoSession mMockingSession;
     private GameServiceProviderInstance mGameServiceProviderInstance;
     @Mock
     private IActivityTaskManager mMockActivityTaskManager;
     @Mock
-    private IGameService mMockGameService;
+    private WindowManagerService mMockWindowManagerService;
     @Mock
-    private IGameSessionService mMockGameSessionService;
+    private WindowManagerInternal mMockWindowManagerInternal;
     private FakeGameClassifier mFakeGameClassifier;
+    private FakeGameService mFakeGameService;
     private FakeServiceConnector<IGameService> mFakeGameServiceConnector;
+    private FakeGameSessionService mFakeGameSessionService;
     private FakeServiceConnector<IGameSessionService> mFakeGameSessionServiceConnector;
     private ArrayList<ITaskStackListener> mTaskStackListeners;
-    private InOrder mInOrder;
+    private ArrayList<RunningTaskInfo> mRunningTaskInfos;
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException, RemoteException {
@@ -98,13 +124,13 @@
                 .strictness(Strictness.LENIENT)
                 .startMocking();
 
-        mInOrder = inOrder(mMockGameService, mMockGameSessionService);
-
         mFakeGameClassifier = new FakeGameClassifier();
         mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE);
 
-        mFakeGameServiceConnector = new FakeServiceConnector<>(mMockGameService);
-        mFakeGameSessionServiceConnector = new FakeServiceConnector<>(mMockGameSessionService);
+        mFakeGameService = new FakeGameService();
+        mFakeGameServiceConnector = new FakeServiceConnector<>(mFakeGameService);
+        mFakeGameSessionService = new FakeGameSessionService();
+        mFakeGameSessionServiceConnector = new FakeServiceConnector<>(mFakeGameSessionService);
 
         mTaskStackListeners = new ArrayList<>();
         doAnswer(invocation -> {
@@ -112,6 +138,10 @@
             return null;
         }).when(mMockActivityTaskManager).registerTaskStackListener(any());
 
+        mRunningTaskInfos = new ArrayList<>();
+        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
+                mRunningTaskInfos);
+
         doAnswer(invocation -> {
             mTaskStackListeners.remove(invocation.getArgument(0));
             return null;
@@ -122,6 +152,8 @@
                 ConcurrentUtils.DIRECT_EXECUTOR,
                 mFakeGameClassifier,
                 mMockActivityTaskManager,
+                mMockWindowManagerService,
+                mMockWindowManagerInternal,
                 mFakeGameServiceConnector,
                 mFakeGameSessionServiceConnector);
     }
@@ -135,8 +167,7 @@
     public void start_startsGameSession() throws Exception {
         mGameServiceProviderInstance.start();
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verifyNoMoreInteractions();
+        assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.CONNECTED);
         assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -145,9 +176,9 @@
     @Test
     public void start_multipleTimes_startsGameSessionOnce() throws Exception {
         mGameServiceProviderInstance.start();
+        mGameServiceProviderInstance.start();
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verifyNoMoreInteractions();
+        assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.CONNECTED);
         assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -157,9 +188,10 @@
     public void stop_neverStarted_doesNothing() throws Exception {
         mGameServiceProviderInstance.stop();
 
+
+        assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
-        mInOrder.verifyNoMoreInteractions();
     }
 
     @Test
@@ -167,9 +199,8 @@
         mGameServiceProviderInstance.start();
         mGameServiceProviderInstance.stop();
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameService).disconnected();
-        mInOrder.verifyNoMoreInteractions();
+        assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
+        assertThat(mFakeGameService.getConnectedCount()).isEqualTo(1);
         assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -183,11 +214,8 @@
         mGameServiceProviderInstance.start();
         mGameServiceProviderInstance.stop();
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameService).disconnected();
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameService).disconnected();
-        mInOrder.verifyNoMoreInteractions();
+        assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
+        assertThat(mFakeGameService.getConnectedCount()).isEqualTo(2);
         assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(2);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -199,9 +227,8 @@
         mGameServiceProviderInstance.stop();
         mGameServiceProviderInstance.stop();
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameService).disconnected();
-        mInOrder.verifyNoMoreInteractions();
+        assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
+        assertThat(mFakeGameService.getConnectedCount()).isEqualTo(1);
         assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -211,7 +238,6 @@
     public void gameTaskStarted_neverStarted_doesNothing() throws Exception {
         dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
 
-        mInOrder.verifyNoMoreInteractions();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
     }
@@ -220,35 +246,25 @@
     public void gameTaskRemoved_neverStarted_doesNothing() throws Exception {
         dispatchTaskRemoved(10);
 
-        mInOrder.verifyNoMoreInteractions();
         assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
         assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
     }
 
     @Test
-    public void gameTaskStarted_afterStopped_doesNothing() throws Exception {
+    public void gameTaskStarted_afterStopped_doesNotSendGameStartedEvent() throws Exception {
         mGameServiceProviderInstance.start();
         mGameServiceProviderInstance.stop();
         dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameService).disconnected();
-        mInOrder.verifyNoMoreInteractions();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+        assertThat(mFakeGameService.getGameStartedEvents()).isEmpty();
     }
 
     @Test
-    public void appTaskStarted_doesNothing() throws Exception {
+    public void appTaskStarted_doesNotSendGameStartedEvent() throws Exception {
         mGameServiceProviderInstance.start();
         dispatchTaskCreated(10, APP_A_MAIN_ACTIVITY);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verifyNoMoreInteractions();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+        assertThat(mFakeGameService.getGameStartedEvents()).isEmpty();
     }
 
     @Test
@@ -256,280 +272,410 @@
         mGameServiceProviderInstance.start();
         dispatchTaskCreated(10, null);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verifyNoMoreInteractions();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+        assertThat(mFakeGameService.getGameStartedEvents()).isEmpty();
     }
 
     @Test
-    public void gameTaskStarted_createsGameSession() throws Exception {
-        CreateGameSessionRequest createGameSessionRequest =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest);
+    public void gameSessionRequested_withoutTaskDispatch_doesNotCrashAndDoesNotCreateGameSession()
+            throws Exception {
+        mGameServiceProviderInstance.start();
 
+        mFakeGameService.requestCreateGameSession(10);
+
+        assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
+    }
+
+    @Test
+    public void gameTaskStarted_noSessionRequest_callsStartGame() throws Exception {
         mGameServiceProviderInstance.start();
         dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
-        mInOrder.verifyNoMoreInteractions();
+        GameStartedEvent expectedGameStartedEvent = new GameStartedEvent(10, GAME_A_PACKAGE);
+        assertThat(mFakeGameService.getGameStartedEvents())
+                .containsExactly(expectedGameStartedEvent).inOrder();
+    }
+
+    @Test
+    public void gameTaskStarted_requestToCreateGameSessionIncludesTaskConfiguration()
+            throws Exception {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation =
+                getOnlyElement(mFakeGameSessionService.getCapturedCreateInvocations());
+        assertThat(capturedCreateInvocation.mGameSessionViewHostConfiguration)
+                .isEqualTo(DEFAULT_GAME_SESSION_VIEW_HOST_CONFIGURATION);
+    }
+
+    @Test
+    public void gameTaskStarted_failsToDetermineTaskOverlayConfiguration_gameSessionNotCreated()
+            throws Exception {
+        mGameServiceProviderInstance.start();
+        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+
+        mFakeGameService.requestCreateGameSession(10);
+
+        assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
+    }
+
+    @Test
+    public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
         assertThat(gameSession10.mIsDestroyed).isFalse();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+        assertThat(gameSession10.mIsFocused).isFalse();
+    }
+
+    @Test
+    public void gameTaskStartedAndSessionRequested_secondSessionRequest_ignoredAndDoesNotCrash()
+            throws Exception {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+
+        mFakeGameService.requestCreateGameSession(10);
+        mFakeGameService.requestCreateGameSession(10);
+
+        CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10,
+                GAME_A_PACKAGE);
+        assertThat(getOnlyElement(
+                mFakeGameSessionService.getCapturedCreateInvocations()).mCreateGameSessionRequest)
+                .isEqualTo(expectedCreateGameSessionRequest);
+    }
+
+    @Test
+    public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
+        verifyNoMoreInteractions(mMockWindowManagerInternal);
+    }
+
+    @Test
+    public void gameTaskFocused_propagatedToGameSession() throws Exception {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        assertThat(gameSession10.mIsFocused).isFalse();
+
+        dispatchTaskFocused(10, /*focused=*/ true);
+        assertThat(gameSession10.mIsFocused).isTrue();
+
+        dispatchTaskFocused(10, /*focused=*/ false);
+        assertThat(gameSession10.mIsFocused).isFalse();
+    }
+
+    @Test
+    public void gameTaskAlreadyFocusedWhenGameSessionCreated_propagatedToGameSession()
+            throws Exception {
+        ActivityTaskManager.RootTaskInfo gameATaskInfo = new ActivityTaskManager.RootTaskInfo();
+        gameATaskInfo.taskId = 10;
+        when(mMockActivityTaskManager.getFocusedRootTaskInfo()).thenReturn(gameATaskInfo);
+
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        assertThat(gameSession10.mIsFocused).isTrue();
     }
 
     @Test
     public void gameTaskRemoved_whileAwaitingGameSessionAttached_destroysGameSession()
             throws Exception {
-        CreateGameSessionRequest createGameSessionRequest =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest);
-
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        dispatchTaskRemoved(10);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
-        mInOrder.verifyNoMoreInteractions();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        dispatchTaskRemoved(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
         assertThat(gameSession10.mIsDestroyed).isTrue();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
     }
 
     @Test
-    public void gameTaskRemoved_destroysGameSession() throws Exception {
-        CreateGameSessionRequest createGameSessionRequest =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest);
-
+    public void gameTaskRemoved_whileGameSessionAttached_destroysGameSession() throws Exception {
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
+
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
         dispatchTaskRemoved(10);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
-        mInOrder.verifyNoMoreInteractions();
         assertThat(gameSession10.mIsDestroyed).isTrue();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
     }
 
     @Test
-    public void gameTaskStarted_multipleTimes_createsMultipleGameSessions() throws Exception {
-        CreateGameSessionRequest createGameSessionRequest10 =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest10);
-
-        CreateGameSessionRequest createGameSessionRequest11 =
-                new CreateGameSessionRequest(11, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession11Future =
-                captureCreateGameSessionFuture(createGameSessionRequest11);
-
+    public void gameTaskRemoved_removesTaskOverlay() throws Exception {
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
 
-        dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession11 = new IGameSessionStub();
-        gameSession11Future.get().complete(gameSession11);
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
-        mInOrder.verifyNoMoreInteractions();
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        stopTask(10);
+
+        verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
+        verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10));
+        verifyNoMoreInteractions(mMockWindowManagerInternal);
+    }
+
+    @Test
+    public void gameTaskStartedAndSessionRequested_multipleTimes_createsMultipleGameSessions()
+            throws Exception {
+        mGameServiceProviderInstance.start();
+
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        startTask(11, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(11);
+
+        FakeGameSession gameSession11 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(11)
+                .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
+
         assertThat(gameSession10.mIsDestroyed).isFalse();
         assertThat(gameSession11.mIsDestroyed).isFalse();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
-        assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
     }
 
     @Test
-    public void gameTaskRemoved_afterMultipleCreated_destroysOnlyThatGameSession()
+    public void gameTaskStartedTwice_sessionRequestedSecondTimeOnly_createsOneGameSession()
             throws Exception {
-        CreateGameSessionRequest createGameSessionRequest10 =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest10);
-
-        CreateGameSessionRequest createGameSessionRequest11 =
-                new CreateGameSessionRequest(11, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession11Future =
-                captureCreateGameSessionFuture(createGameSessionRequest11);
-
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
 
-        dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession11 = new IGameSessionStub();
-        gameSession11Future.get().complete(gameSession11);
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        startTask(11, GAME_A_MAIN_ACTIVITY);
+
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        assertThat(gameSession10.mIsDestroyed).isFalse();
+        assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).hasSize(1);
+    }
+
+    @Test
+    public void gameTaskRemoved_multipleSessions_destroysOnlyThatGameSession()
+            throws Exception {
+        mGameServiceProviderInstance.start();
+
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        startTask(11, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(11);
+
+        FakeGameSession gameSession11 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(11)
+                .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
 
         dispatchTaskRemoved(10);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
-        mInOrder.verifyNoMoreInteractions();
         assertThat(gameSession10.mIsDestroyed).isTrue();
         assertThat(gameSession11.mIsDestroyed).isFalse();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
     }
 
     @Test
-    public void allGameTasksRemoved_destroysAllGameSessions() throws Exception {
-        CreateGameSessionRequest createGameSessionRequest10 =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest10);
-
-        CreateGameSessionRequest createGameSessionRequest11 =
-                new CreateGameSessionRequest(11, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession11Future =
-                captureCreateGameSessionFuture(createGameSessionRequest11);
-
+    public void allGameTasksRemoved_destroysAllGameSessionsAndGameSessionServiceIsDisconnected() {
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
 
-        dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession11 = new IGameSessionStub();
-        gameSession11Future.get().complete(gameSession11);
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        startTask(11, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(11);
+
+        FakeGameSession gameSession11 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(11)
+                .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
 
         dispatchTaskRemoved(10);
         dispatchTaskRemoved(11);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
-        mInOrder.verifyNoMoreInteractions();
         assertThat(gameSession10.mIsDestroyed).isTrue();
         assertThat(gameSession11.mIsDestroyed).isTrue();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
     }
 
     @Test
-    public void gameTasksCreated_afterAllPreviousSessionsDestroyed_createsSession()
+    public void createSessionRequested_afterAllPreviousSessionsDestroyed_createsSession()
             throws Exception {
-        CreateGameSessionRequest createGameSessionRequest10 =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest10);
-
-        CreateGameSessionRequest createGameSessionRequest11 =
-                new CreateGameSessionRequest(11, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession11Future =
-                captureCreateGameSessionFuture(createGameSessionRequest11);
-
-        CreateGameSessionRequest createGameSessionRequest12 =
-                new CreateGameSessionRequest(12, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> unusedGameSession12Future =
-                captureCreateGameSessionFuture(createGameSessionRequest12);
-
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
 
-        dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession11 = new IGameSessionStub();
-        gameSession11Future.get().complete(gameSession11);
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        startTask(11, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(11);
+
+        FakeGameSession gameSession11 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(11)
+                .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
 
         dispatchTaskRemoved(10);
         dispatchTaskRemoved(11);
 
-        dispatchTaskCreated(12, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession12 = new IGameSessionStub();
-        gameSession11Future.get().complete(gameSession12);
+        startTask(12, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(12);
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest12), any());
-        mInOrder.verifyNoMoreInteractions();
+        FakeGameSession gameSession12 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage12 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(12)
+                .complete(new CreateGameSessionResult(gameSession12, mockSurfacePackage12));
+
         assertThat(gameSession10.mIsDestroyed).isTrue();
         assertThat(gameSession11.mIsDestroyed).isTrue();
         assertThat(gameSession12.mIsDestroyed).isFalse();
-        assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(2);
     }
 
     @Test
     public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception {
-        CreateGameSessionRequest createGameSessionRequest10 =
-                new CreateGameSessionRequest(10, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession10Future =
-                captureCreateGameSessionFuture(createGameSessionRequest10);
-
-        CreateGameSessionRequest createGameSessionRequest11 =
-                new CreateGameSessionRequest(11, GAME_A_PACKAGE);
-        Supplier<AndroidFuture<IBinder>> gameSession11Future =
-                captureCreateGameSessionFuture(createGameSessionRequest11);
-
         mGameServiceProviderInstance.start();
-        dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession10 = new IGameSessionStub();
-        gameSession10Future.get().complete(gameSession10);
-        dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
-        IGameSessionStub gameSession11 = new IGameSessionStub();
-        gameSession11Future.get().complete(gameSession11);
+
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        startTask(11, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(11);
+
+        FakeGameSession gameSession11 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(11)
+                .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
+
         mGameServiceProviderInstance.stop();
 
-        mInOrder.verify(mMockGameService).connected();
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
-        mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
-        mInOrder.verify(mMockGameService).disconnected();
-        mInOrder.verifyNoMoreInteractions();
         assertThat(gameSession10.mIsDestroyed).isTrue();
         assertThat(gameSession11.mIsDestroyed).isTrue();
         assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
-        assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
         assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
-        assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
     }
 
-    private Supplier<AndroidFuture<IBinder>> captureCreateGameSessionFuture(
-            CreateGameSessionRequest expectedCreateGameSessionRequest) throws Exception {
-        final AtomicReference<AndroidFuture<IBinder>> gameSessionFuture = new AtomicReference<>();
-        doAnswer(invocation -> {
-            gameSessionFuture.set(invocation.getArgument(1));
-            return null;
-        }).when(mMockGameSessionService).create(eq(expectedCreateGameSessionRequest), any());
+    @Test
+    public void takeScreenshot_failureNoBitmapCaptured() throws Exception {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
 
-        return gameSessionFuture::get;
+        IGameSessionController gameSessionController = getOnlyElement(
+                mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
+        AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
+        gameSessionController.takeScreenshot(10, resultFuture);
+
+        GameScreenshotResult result = resultFuture.get();
+        assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR,
+                result.getStatus());
+        verify(mMockWindowManagerService).captureTaskBitmap(10);
     }
 
+    @Test
+    public void takeScreenshot_success() throws Exception {
+        when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP);
+
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        IGameSessionController gameSessionController = getOnlyElement(
+                mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
+        AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
+        gameSessionController.takeScreenshot(10, resultFuture);
+
+        GameScreenshotResult result = resultFuture.get();
+        assertEquals(GameScreenshotResult.GAME_SCREENSHOT_SUCCESS, result.getStatus());
+        assertEquals(TEST_BITMAP, result.getBitmap());
+    }
+
+    private void startTask(int taskId, ComponentName componentName) {
+        RunningTaskInfo runningTaskInfo = new RunningTaskInfo();
+        runningTaskInfo.taskId = taskId;
+        runningTaskInfo.displayId = 1;
+        runningTaskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 500, 800));
+        mRunningTaskInfos.add(runningTaskInfo);
+
+        dispatchTaskCreated(taskId, componentName);
+    }
+
+    private void stopTask(int taskId) {
+        mRunningTaskInfos.removeIf(runningTaskInfo -> runningTaskInfo.taskId == taskId);
+        dispatchTaskRemoved(taskId);
+    }
+
+
     private void dispatchTaskRemoved(int taskId) {
         dispatchTaskChangeEvent(taskStackListener -> {
             taskStackListener.onTaskRemoved(taskId);
@@ -542,6 +688,12 @@
         });
     }
 
+    private void dispatchTaskFocused(int taskId, boolean focused) {
+        dispatchTaskChangeEvent(taskStackListener -> {
+            taskStackListener.onTaskFocusChanged(taskId, focused);
+        });
+    }
+
     private void dispatchTaskChangeEvent(
             ThrowingConsumer<ITaskStackListener> taskStackListenerConsumer) {
         for (ITaskStackListener taskStackListener : mTaskStackListeners) {
@@ -549,12 +701,129 @@
         }
     }
 
-    private static class IGameSessionStub extends IGameSession.Stub {
-        boolean mIsDestroyed = false;
+    static final class FakeGameService extends IGameService.Stub {
+        private IGameServiceController mGameServiceController;
+
+        public enum GameServiceState {
+            DISCONNECTED,
+            CONNECTED,
+        }
+
+        private ArrayList<GameStartedEvent> mGameStartedEvents = new ArrayList<>();
+        private int mConnectedCount = 0;
+        private GameServiceState mGameServiceState = GameServiceState.DISCONNECTED;
+
+        public GameServiceState getState() {
+            return mGameServiceState;
+        }
+
+        public int getConnectedCount() {
+            return mConnectedCount;
+        }
+
+        public ArrayList<GameStartedEvent> getGameStartedEvents() {
+            return mGameStartedEvents;
+        }
 
         @Override
-        public void destroy() {
-            mIsDestroyed = true;
+        public void connected(IGameServiceController gameServiceController) {
+            Preconditions.checkState(mGameServiceState == GameServiceState.DISCONNECTED);
+
+            mGameServiceState = GameServiceState.CONNECTED;
+            mConnectedCount += 1;
+            mGameServiceController = gameServiceController;
+        }
+
+        @Override
+        public void disconnected() {
+            Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED);
+
+            mGameServiceState = GameServiceState.DISCONNECTED;
+            mGameServiceController = null;
+        }
+
+        @Override
+        public void gameStarted(GameStartedEvent gameStartedEvent) {
+            Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED);
+
+            mGameStartedEvents.add(gameStartedEvent);
+        }
+
+        public void requestCreateGameSession(int task) {
+            Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED);
+
+            try {
+                mGameServiceController.createGameSession(task);
+            } catch (RemoteException ex) {
+                throw new AssertionError(ex);
+            }
         }
     }
-}
+
+    static final class FakeGameSessionService extends IGameSessionService.Stub {
+
+        private final ArrayList<CapturedCreateInvocation> mCapturedCreateInvocations =
+                new ArrayList<>();
+        private final HashMap<Integer, AndroidFuture<CreateGameSessionResult>>
+                mPendingCreateGameSessionResultFutures =
+                new HashMap<>();
+
+        public static final class CapturedCreateInvocation {
+            private final IGameSessionController mGameSessionController;
+            private final CreateGameSessionRequest mCreateGameSessionRequest;
+            private final GameSessionViewHostConfiguration mGameSessionViewHostConfiguration;
+
+            CapturedCreateInvocation(
+                    IGameSessionController gameSessionController,
+                    CreateGameSessionRequest createGameSessionRequest,
+                    GameSessionViewHostConfiguration gameSessionViewHostConfiguration) {
+                mGameSessionController = gameSessionController;
+                mCreateGameSessionRequest = createGameSessionRequest;
+                mGameSessionViewHostConfiguration = gameSessionViewHostConfiguration;
+            }
+        }
+
+        public ArrayList<CapturedCreateInvocation> getCapturedCreateInvocations() {
+            return mCapturedCreateInvocations;
+        }
+
+        public AndroidFuture<CreateGameSessionResult> removePendingFutureForTaskId(int taskId) {
+            return mPendingCreateGameSessionResultFutures.remove(taskId);
+        }
+
+        @Override
+        public void create(
+                IGameSessionController gameSessionController,
+                CreateGameSessionRequest createGameSessionRequest,
+                GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
+                AndroidFuture createGameSessionResultFuture) {
+
+            mCapturedCreateInvocations.add(
+                    new CapturedCreateInvocation(
+                            gameSessionController,
+                            createGameSessionRequest,
+                            gameSessionViewHostConfiguration));
+
+            Preconditions.checkState(!mPendingCreateGameSessionResultFutures.containsKey(
+                    createGameSessionRequest.getTaskId()));
+            mPendingCreateGameSessionResultFutures.put(
+                    createGameSessionRequest.getTaskId(),
+                    createGameSessionResultFuture);
+        }
+    }
+
+    private static class FakeGameSession extends IGameSession.Stub {
+        boolean mIsDestroyed = false;
+        boolean mIsFocused = false;
+
+        @Override
+        public void onDestroyed() {
+            mIsDestroyed = true;
+        }
+
+        @Override
+        public void onTaskFocusChanged(boolean focused) {
+            mIsFocused = focused;
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
deleted file mode 100644
index 7ebf014..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.communal;
-
-import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-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.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.spy;
-
-import android.Manifest;
-import android.annotation.Nullable;
-import android.app.KeyguardManager;
-import android.app.communal.ICommunalManager;
-import android.app.compat.CompatChanges;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.test.mock.MockContentResolver;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.wm.ActivityInterceptorCallback;
-import com.android.server.wm.ActivityTaskManagerInternal;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.quality.Strictness;
-
-/**
- * Test class for {@link CommunalManagerService}.
- *
- * Build/Install/Run:
- *   atest FrameworksMockingServicesTests:CommunalManagerServiceTest
- */
-@RunWith(MockitoJUnitRunner.class)
-@SmallTest
-@Presubmit
-public class CommunalManagerServiceTest {
-    private static final int TEST_USER_ID = 1;
-    private static final int TEST_REAL_CALLING_UID = 2;
-    private static final int TEST_REAL_CALLING_PID = 3;
-    private static final String TEST_CALLING_PACKAGE = "com.test.caller";
-    private static final String TEST_PACKAGE_NAME = "com.test.package";
-
-    private MockitoSession mMockingSession;
-    private CommunalManagerService mService;
-
-    @Mock
-    private ActivityTaskManagerInternal mAtmInternal;
-    @Mock
-    private KeyguardManager mKeyguardManager;
-    @Mock
-    private DreamManagerInternal mDreamManagerInternal;
-
-    private ActivityInterceptorCallback mActivityInterceptorCallback;
-    private BroadcastReceiver mPackageReceiver;
-    private ActivityInfo mAInfo;
-    private ICommunalManager mBinder;
-    private ContextWrapper mContextSpy;
-
-    @Before
-    public final void setUp() {
-        mMockingSession = mockitoSession()
-                .initMocks(this)
-                .spyStatic(CommunalManagerService.class)
-                .mockStatic(CompatChanges.class)
-                .strictness(Strictness.WARN)
-                .startMocking();
-
-        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
-        MockContentResolver cr = new MockContentResolver(mContextSpy);
-        cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        when(mContextSpy.getContentResolver()).thenReturn(cr);
-
-        when(mContextSpy.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
-        addLocalServiceMock(ActivityTaskManagerInternal.class, mAtmInternal);
-        addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternal);
-
-        doNothing().when(mContextSpy).enforceCallingPermission(
-                eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
-        doNothing().when(mContextSpy).enforceCallingPermission(
-                eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
-
-        mService = new CommunalManagerService(mContextSpy);
-        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
-        ArgumentCaptor<ActivityInterceptorCallback> activityInterceptorCaptor =
-                ArgumentCaptor.forClass(ActivityInterceptorCallback.class);
-        verify(mAtmInternal).registerActivityStartInterceptor(eq(COMMUNAL_MODE_ORDERED_ID),
-                activityInterceptorCaptor.capture());
-        mActivityInterceptorCallback = activityInterceptorCaptor.getValue();
-
-        ArgumentCaptor<BroadcastReceiver> packageReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mContextSpy).registerReceiverAsUser(packageReceiverCaptor.capture(),
-                eq(UserHandle.SYSTEM), any(), any(), any(), anyInt());
-        mPackageReceiver = packageReceiverCaptor.getValue();
-
-        mBinder = mService.getBinderServiceInstance();
-
-        mAInfo = new ActivityInfo();
-        mAInfo.applicationInfo = new ApplicationInfo();
-        mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
-    }
-
-    @After
-    public void tearDown() {
-        FakeSettingsProvider.clearSettingsProvider();
-        if (mMockingSession != null) {
-            mMockingSession.finishMocking();
-        }
-    }
-
-    /**
-     * 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 ActivityInterceptorCallback.ActivityInterceptorInfo buildActivityInfo(Intent intent) {
-        return new ActivityInterceptorCallback.ActivityInterceptorInfo(
-                TEST_REAL_CALLING_UID,
-                TEST_REAL_CALLING_PID,
-                TEST_USER_ID,
-                TEST_CALLING_PACKAGE,
-                "featureId",
-                intent,
-                null,
-                mAInfo,
-                "resolvedType",
-                TEST_REAL_CALLING_PID,
-                TEST_REAL_CALLING_UID,
-                null);
-    }
-
-    private void allowPackages(String packages) {
-        Settings.Secure.putStringForUser(mContextSpy.getContentResolver(),
-                Settings.Secure.COMMUNAL_MODE_PACKAGES, packages, UserHandle.USER_SYSTEM);
-    }
-
-    private String getAllowedPackages() {
-        return Settings.Secure.getStringForUser(mContextSpy.getContentResolver(),
-                Settings.Secure.COMMUNAL_MODE_PACKAGES, UserHandle.USER_SYSTEM);
-    }
-
-    private void assertDoesIntercept() {
-        final Intent intent = new Intent(Intent.ACTION_MAIN);
-        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
-    }
-
-    private void assertDoesNotIntercept() {
-        final Intent intent = new Intent(Intent.ACTION_MAIN);
-        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
-    }
-
-    private Intent createPackageIntent(String packageName, @Nullable String action) {
-        return new Intent(action, Uri.parse("package:" + packageName));
-    }
-
-    @Test
-    public void testIsCommunalMode_isTrue() throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        assertThat(mBinder.isCommunalMode()).isTrue();
-    }
-
-    @Test
-    public void testIsCommunalMode_isFalse() throws RemoteException {
-        mBinder.setCommunalViewShowing(false);
-        assertThat(mBinder.isCommunalMode()).isFalse();
-    }
-
-    @Test
-    public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
-        mAInfo.flags = 0;
-        assertDoesNotIntercept();
-    }
-
-    @Test
-    public void testIntercept_unlocked_communalOn_appNotEnabled_showWhenLockedOff()
-            throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
-        mAInfo.flags = 0;
-        assertDoesNotIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOff_appNotEnabled_showWhenLockedOff() {
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        mAInfo.flags = 0;
-        assertDoesNotIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOff_allowlistEnabled()
-            throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
-                UserHandle.SYSTEM)).thenReturn(true);
-        mAInfo.flags = 0;
-        assertDoesIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOn_allowlistEnabled()
-            throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
-                UserHandle.SYSTEM)).thenReturn(true);
-        mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
-
-        allowPackages("package1,package2");
-        assertDoesIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOff_allowlistEnabled()
-            throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
-                UserHandle.SYSTEM)).thenReturn(true);
-        mAInfo.flags = 0;
-
-        allowPackages(TEST_PACKAGE_NAME);
-        assertDoesIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistEnabled()
-            throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
-                UserHandle.SYSTEM)).thenReturn(true);
-
-        mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
-
-        allowPackages(TEST_PACKAGE_NAME);
-        assertDoesNotIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistDisabled()
-            throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
-                UserHandle.SYSTEM)).thenReturn(false);
-
-        mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
-
-        allowPackages(TEST_PACKAGE_NAME);
-        assertDoesIntercept();
-    }
-
-    @Test
-    public void testIntercept_locked_communalOn_dream() throws RemoteException {
-        mBinder.setCommunalViewShowing(true);
-        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
-        when(mDreamManagerInternal.getActiveDreamComponent(false)).thenReturn(
-                new ComponentName(TEST_PACKAGE_NAME, "SomeClass"));
-
-        allowPackages(TEST_PACKAGE_NAME);
-        assertDoesNotIntercept();
-    }
-
-    @Test
-    public void testUpdateSettings_packageUninstalled() {
-        allowPackages("package1,package2");
-        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
-        mPackageReceiver.onReceive(mContextSpy,
-                createPackageIntent("package1", ACTION_PACKAGE_REMOVED));
-
-        assertThat(getAllowedPackages()).isEqualTo("package2");
-    }
-
-    @Test
-    public void testUpdateSettings_nullAction_doesNothing() {
-        allowPackages("package1,package2");
-        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
-        mPackageReceiver.onReceive(mContextSpy,
-                createPackageIntent("package1", null));
-
-        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-    }
-
-    @Test
-    public void testUpdateSettings_invalidPackage_doesNothing() {
-        allowPackages("package1,package2");
-        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
-        mPackageReceiver.onReceive(mContextSpy,
-                createPackageIntent("package3", ACTION_PACKAGE_REMOVED));
-
-        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-    }
-
-    @Test
-    public void testUpdateSettings_onBoot() {
-        allowPackages("package1,package2");
-        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
-
-        when(CommunalManagerService.isPackageInstalled(eq("package1"), any())).thenReturn(true);
-        when(CommunalManagerService.isPackageInstalled(eq("package2"), any())).thenReturn(false);
-
-        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
-        assertThat(getAllowedPackages()).isEqualTo("package1");
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index edf6816..1a5888e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -183,7 +183,8 @@
                 mOverridesToRemoveByPackageConfigCaptor.getValue().packageNameToOverridesToRemove;
         Map<Long, PackageOverride> addedOverrides;
         assertThat(packageNameToAddedOverrides.keySet()).containsExactly(PACKAGE_1, PACKAGE_3);
-        assertThat(packageNameToRemovedOverrides.keySet()).containsExactly(PACKAGE_3, PACKAGE_4);
+        assertThat(packageNameToRemovedOverrides.keySet()).containsExactly(PACKAGE_2, PACKAGE_3,
+                PACKAGE_4);
         // Package 1
         addedOverrides = packageNameToAddedOverrides.get(PACKAGE_1).overrides;
         assertThat(addedOverrides).hasSize(3);
@@ -193,6 +194,9 @@
                 new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
         assertThat(addedOverrides.get(789L)).isEqualTo(
                 new PackageOverride.Builder().setEnabled(false).build());
+        // Package 2
+        assertThat(packageNameToRemovedOverrides.get(PACKAGE_2).changeIds).containsExactly(123L,
+                456L, 789L);
         // Package 3
         addedOverrides = packageNameToAddedOverrides.get(PACKAGE_3).overrides;
         assertThat(addedOverrides).hasSize(1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 0c3e472..bdeb2b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -122,15 +122,14 @@
                 .when(() -> LocalServices.getService(ActivityManagerInternal.class));
         doReturn(mock(AppStandbyInternal.class))
                 .when(() -> LocalServices.getService(AppStandbyInternal.class));
+        doReturn(mock(BatteryManagerInternal.class))
+                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
         doReturn(mock(UsageStatsManagerInternal.class))
                 .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
         when(mContext.getString(anyInt())).thenReturn("some_test_string");
         // Called in BackgroundJobsController constructor.
         doReturn(mock(AppStateTrackerImpl.class))
                 .when(() -> LocalServices.getService(AppStateTracker.class));
-        // Called in BatteryController constructor.
-        doReturn(mock(BatteryManagerInternal.class))
-                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
         // Called in ConnectivityController constructor.
         when(mContext.getSystemService(ConnectivityManager.class))
                 .thenReturn(mock(ConnectivityManager.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
new file mode 100644
index 0000000..52d0494
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppGlobals;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
+import android.os.BatteryManagerInternal;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryControllerTest {
+    private static final int CALLING_UID = 1000;
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+    private static final int SOURCE_USER_ID = 0;
+
+    private BatteryController mBatteryController;
+    private BroadcastReceiver mPowerReceiver;
+    private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
+    private int mSourceUid;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private Context mContext;
+    @Mock
+    private BatteryManagerInternal mBatteryManagerInternal;
+    @Mock
+    private JobSchedulerService mJobSchedulerService;
+    @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+
+        // Called in StateController constructor.
+        when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+        when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        // Called in BatteryController constructor.
+        doReturn(mBatteryManagerInternal)
+                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+        // Used in JobStatus.
+        doReturn(mPackageManagerInternal)
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+        // Initialize real objects.
+        // Capture the listeners.
+        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        mBatteryController = new BatteryController(mJobSchedulerService);
+
+        verify(mContext).registerReceiver(receiverCaptor.capture(),
+                ArgumentMatchers.argThat(filter ->
+                        filter.hasAction(Intent.ACTION_POWER_CONNECTED)
+                                && filter.hasAction(Intent.ACTION_POWER_DISCONNECTED)));
+        mPowerReceiver = receiverCaptor.getValue();
+        try {
+            mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
+            // Need to do this since we're using a mock JS and not a real object.
+            doReturn(new ArraySet<>(new String[]{SOURCE_PACKAGE}))
+                    .when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+        setPowerConnected(false);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private void setBatteryNotLow(boolean notLow) {
+        doReturn(notLow).when(mJobSchedulerService).isBatteryNotLow();
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onBatteryStateChangedLocked();
+        }
+        waitForNonDelayedMessagesProcessed();
+    }
+
+    private void setCharging() {
+        doReturn(true).when(mJobSchedulerService).isBatteryCharging();
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onBatteryStateChangedLocked();
+        }
+        waitForNonDelayedMessagesProcessed();
+    }
+
+    private void setDischarging() {
+        doReturn(false).when(mJobSchedulerService).isBatteryCharging();
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onBatteryStateChangedLocked();
+        }
+        waitForNonDelayedMessagesProcessed();
+    }
+
+    private void setPowerConnected(boolean connected) {
+        Intent intent = new Intent(
+                connected ? Intent.ACTION_POWER_CONNECTED : Intent.ACTION_POWER_DISCONNECTED);
+        mPowerReceiver.onReceive(mContext, intent);
+    }
+
+    private void setUidBias(int uid, int bias) {
+        int prevBias = mJobSchedulerService.getUidBias(uid);
+        doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onUidBiasChangedLocked(uid, prevBias, bias);
+        }
+    }
+
+    private void trackJobs(JobStatus... jobs) {
+        for (JobStatus job : jobs) {
+            synchronized (mBatteryController.mLock) {
+                mBatteryController.maybeStartTrackingJobLocked(job, null);
+            }
+        }
+    }
+
+    private void waitForNonDelayedMessagesProcessed() {
+        JobSchedulerBackgroundThread.getHandler().runWithScissors(() -> {}, 15_000);
+    }
+
+    private JobInfo.Builder createBaseJobInfoBuilder(int jobId) {
+        return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestBatteryJobService"));
+    }
+
+    private JobInfo.Builder createBaseJobInfoBuilder(int jobId, String pkgName) {
+        return new JobInfo.Builder(jobId, new ComponentName(pkgName, "TestBatteryJobService"));
+    }
+
+    private JobStatus createJobStatus(String testTag, String packageName, int callingUid,
+            JobInfo jobInfo) {
+        JobStatus js = JobStatus.createFromJobInfo(
+                jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
+        js.serviceInfo = mock(ServiceInfo.class);
+        // Make sure tests aren't passing just because the default bucket is likely ACTIVE.
+        js.setStandbyBucket(FREQUENT_INDEX);
+        return js;
+    }
+
+    @Test
+    public void testBatteryNotLow() {
+        JobStatus job1 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(1).setRequiresBatteryNotLow(true).build());
+        JobStatus job2 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(2).setRequiresBatteryNotLow(true).build());
+
+        setBatteryNotLow(false);
+        trackJobs(job1);
+        assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+
+        setBatteryNotLow(true);
+        assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+
+        trackJobs(job2);
+        assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+    }
+
+    @Test
+    public void testCharging_BatteryNotLow() {
+        JobStatus job1 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(1)
+                        .setRequiresCharging(true)
+                        .setRequiresBatteryNotLow(true).build());
+        JobStatus job2 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(2)
+                        .setRequiresCharging(true)
+                        .setRequiresBatteryNotLow(false).build());
+
+        setBatteryNotLow(true);
+        setDischarging();
+        trackJobs(job1, job2);
+        assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        setCharging();
+        assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+    }
+
+    @Test
+    public void testTopPowerConnectedExemption() {
+        final int uid1 = mSourceUid;
+        final int uid2 = mSourceUid + 1;
+        final int uid3 = mSourceUid + 2;
+        JobStatus jobFg = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
+                createBaseJobInfoBuilder(1).setRequiresCharging(true).build());
+        JobStatus jobFgRunner = createJobStatus("testTopPowerConnectedExemption",
+                SOURCE_PACKAGE, uid1,
+                createBaseJobInfoBuilder(2).setRequiresCharging(true).build());
+        JobStatus jobFgLow = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
+                createBaseJobInfoBuilder(3)
+                        .setRequiresCharging(true)
+                        .setPriority(JobInfo.PRIORITY_LOW)
+                        .build());
+        JobStatus jobBg = createJobStatus("testTopPowerConnectedExemption",
+                "some.background.app", uid2,
+                createBaseJobInfoBuilder(4, "some.background.app")
+                        .setRequiresCharging(true)
+                        .build());
+        JobStatus jobLateFg = createJobStatus("testTopPowerConnectedExemption",
+                "switch.to.fg", uid3,
+                createBaseJobInfoBuilder(5, "switch.to.fg").setRequiresCharging(true).build());
+        JobStatus jobLateFgLow = createJobStatus("testTopPowerConnectedExemption",
+                "switch.to.fg", uid3,
+                createBaseJobInfoBuilder(6, "switch.to.fg")
+                        .setRequiresCharging(true)
+                        .setPriority(JobInfo.PRIORITY_MIN)
+                        .build());
+
+        setBatteryNotLow(false);
+        setDischarging();
+        setUidBias(uid1, JobInfo.BIAS_TOP_APP);
+        setUidBias(uid2, JobInfo.BIAS_DEFAULT);
+        setUidBias(uid3, JobInfo.BIAS_DEFAULT);
+
+        // Jobs are scheduled when power isn't connected.
+        setPowerConnected(false);
+        trackJobs(jobFg, jobFgLow, jobBg, jobLateFg, jobLateFgLow);
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        // Power is connected. TOP app should be allowed to start job DEFAULT+ jobs.
+        setPowerConnected(true);
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        // Test that newly scheduled job of TOP app is correctly allowed to run.
+        trackJobs(jobFgRunner);
+        assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        // Switch top app. New TOP app should be allowed to run job and the running job of
+        // previously TOP app should be allowed to continue to run.
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.prepareForExecutionLocked(jobFgRunner);
+        }
+        setUidBias(uid1, JobInfo.BIAS_DEFAULT);
+        setUidBias(uid2, JobInfo.BIAS_DEFAULT);
+        setUidBias(uid3, JobInfo.BIAS_TOP_APP);
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertTrue(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        setPowerConnected(false);
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index a9853bf..f61d6ca 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -48,18 +48,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.job.JobInfo;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManagerInternal;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicyManager;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
 import android.os.Build;
 import android.os.Looper;
 import android.os.SystemClock;
@@ -74,7 +70,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
@@ -88,8 +83,6 @@
     @Mock
     private Context mContext;
     @Mock
-    private BatteryManagerInternal mBatteryManagerInternal;
-    @Mock
     private ConnectivityManager mConnManager;
     @Mock
     private NetworkPolicyManager mNetPolicyManager;
@@ -115,9 +108,6 @@
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
 
-        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
-        LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal);
-
         when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
 
         // Freeze the clocks at this moment in time
@@ -158,18 +148,10 @@
                 .setMinimumNetworkChunkBytes(DataUnit.KIBIBYTES.toBytes(100))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
 
-        final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
-                .thenReturn(false);
+        when(mService.isBatteryCharging()).thenReturn(false);
         final ConnectivityController controller = new ConnectivityController(mService);
-        verify(mContext).registerReceiver(chargingCaptor.capture(),
-                ArgumentMatchers.argThat(filter ->
-                        filter.hasAction(BatteryManager.ACTION_CHARGING)
-                                && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
         when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
-        final BroadcastReceiver chargingReceiver = chargingCaptor.getValue();
-        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
+        controller.onBatteryStateChangedLocked();
 
         // Slow network is too slow
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
@@ -225,17 +207,15 @@
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
                         .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
         // Slow network is too slow, but device is charging and network is unmetered.
-        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
-                .thenReturn(true);
-        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
+        when(mService.isBatteryCharging()).thenReturn(true);
+        controller.onBatteryStateChangedLocked();
         assertTrue(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
                         .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
                 mConstants));
 
-        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
-                .thenReturn(false);
-        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
+        when(mService.isBatteryCharging()).thenReturn(false);
+        controller.onBatteryStateChangedLocked();
         when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);
 
         // Slow network is too slow
@@ -259,9 +239,8 @@
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
                         .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
         // Slow network is too slow, but device is charging and network is unmetered.
-        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
-                .thenReturn(true);
-        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
+        when(mService.isBatteryCharging()).thenReturn(true);
+        controller.onBatteryStateChangedLocked();
         assertTrue(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
                         .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 95912b2..8a954ca 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -200,8 +200,10 @@
     }
 
     private void setUidBias(int uid, int bias) {
+        int prevBias = mJobSchedulerService.getUidBias(uid);
+        doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
         synchronized (mPrefetchController.mLock) {
-            mPrefetchController.onUidBiasChangedLocked(uid, bias);
+            mPrefetchController.onUidBiasChangedLocked(uid, prevBias, bias);
         }
     }
 
@@ -381,6 +383,10 @@
 
         inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0))
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+        verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
+                .setWindow(
+                        anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
+                        anyLong(), eq(TAG_PREFETCH), any(), any());
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 300f93f..cfae9a3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -63,16 +63,13 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ServiceInfo;
-import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
@@ -126,7 +123,6 @@
     private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final int SOURCE_USER_ID = 0;
 
-    private BroadcastReceiver mChargingReceiver;
     private QuotaController mQuotaController;
     private QuotaController.QcConstants mQcConstants;
     private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
@@ -225,8 +221,6 @@
 
         // Initialize real objects.
         // Capture the listeners.
-        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
         ArgumentCaptor<IUidObserver> uidObserverCaptor =
                 ArgumentCaptor.forClass(IUidObserver.class);
         ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
@@ -236,11 +230,6 @@
         mQuotaController = new QuotaController(mJobSchedulerService,
                 mock(BackgroundJobsController.class), mock(ConnectivityController.class));
 
-        verify(mContext).registerReceiver(receiverCaptor.capture(),
-                ArgumentMatchers.argThat(filter ->
-                        filter.hasAction(BatteryManager.ACTION_CHARGING)
-                                && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
-        mChargingReceiver = receiverCaptor.getValue();
         verify(mPowerAllowlistInternal)
                 .registerTempAllowlistChangeListener(taChangeCaptor.capture());
         mTempAllowlistListener = taChangeCaptor.getValue();
@@ -280,13 +269,17 @@
     }
 
     private void setCharging() {
-        Intent intent = new Intent(BatteryManager.ACTION_CHARGING);
-        mChargingReceiver.onReceive(mContext, intent);
+        doReturn(true).when(mJobSchedulerService).isBatteryCharging();
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.onBatteryStateChangedLocked();
+        }
     }
 
     private void setDischarging() {
-        Intent intent = new Intent(BatteryManager.ACTION_DISCHARGING);
-        mChargingReceiver.onReceive(mContext, intent);
+        doReturn(false).when(mJobSchedulerService).isBatteryCharging();
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.onBatteryStateChangedLocked();
+        }
     }
 
     private void setProcessState(int procState) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
index 16ffda8..b7ab6f80 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -109,15 +109,19 @@
     public static class GnssHalBatchingMode {
 
         public final long PeriodNanos;
+        public final float MinUpdateDistanceMeters;
         public final boolean WakeOnFifoFull;
 
         GnssHalBatchingMode() {
             PeriodNanos = 0;
+            MinUpdateDistanceMeters = 0.0f;
             WakeOnFifoFull = false;
         }
 
-        public GnssHalBatchingMode(long periodNanos, boolean wakeOnFifoFull) {
+        public GnssHalBatchingMode(long periodNanos, float minUpdateDistanceMeters,
+                boolean wakeOnFifoFull) {
             PeriodNanos = periodNanos;
+            MinUpdateDistanceMeters = minUpdateDistanceMeters;
             WakeOnFifoFull = wakeOnFifoFull;
         }
 
@@ -132,12 +136,13 @@
 
             GnssHalBatchingMode that = (GnssHalBatchingMode) o;
             return PeriodNanos == that.PeriodNanos
+                    && MinUpdateDistanceMeters == that.MinUpdateDistanceMeters
                     && WakeOnFifoFull == that.WakeOnFifoFull;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(PeriodNanos, WakeOnFifoFull);
+            return Objects.hash(PeriodNanos, MinUpdateDistanceMeters, WakeOnFifoFull);
         }
     }
 
@@ -429,11 +434,15 @@
     }
 
     @Override
-    protected void injectLocation(double latitude, double longitude, float accuracy) {
+    protected void injectLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
+            double longitude, double altitude, float speed, float bearing, float horizontalAccuracy,
+            float verticalAccuracy, float speedAccuracy, float bearingAccuracy, long timestamp,
+            @GnssRealtimeFlags int elapsedRealtimeFlags, long elapsedRealtimeNanos,
+            double elapsedRealtimeUncertaintyNanos) {
         mState.mInjectedLocation = new Location("injected");
         mState.mInjectedLocation.setLatitude(latitude);
         mState.mInjectedLocation.setLongitude(longitude);
-        mState.mInjectedLocation.setAccuracy(accuracy);
+        mState.mInjectedLocation.setAccuracy(horizontalAccuracy);
     }
 
     @Override
@@ -566,9 +575,11 @@
     protected void cleanupBatching() {}
 
     @Override
-    protected boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+    protected boolean startBatch(long periodNanos, float minUpdateDistanceMeters,
+            boolean wakeOnFifoFull) {
         mState.mBatchingStarted = true;
-        mState.mBatchingMode = new GnssHalBatchingMode(periodNanos, wakeOnFifoFull);
+        mState.mBatchingMode = new GnssHalBatchingMode(periodNanos, minUpdateDistanceMeters,
+                wakeOnFifoFull);
         return true;
     }
 
@@ -669,7 +680,8 @@
     protected void setAgpsSetId(int type, String setId) {}
 
     @Override
-    protected void setAgpsReferenceLocationCellId(int type, int mcc, int mnc, int lac, int cid) {}
+    protected void setAgpsReferenceLocationCellId(int type, int mcc, int mnc, int lac, long cid,
+            int tac, int pcid, int arfcn) {}
 
     @Override
     protected boolean isPsdsSupported() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 44a8b30..555f4b8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -27,8 +27,6 @@
 import android.content.pm.Signature
 import android.content.pm.SigningDetails
 import android.content.pm.UserInfo
-import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.ParsingPackageUtils
 import android.content.pm.parsing.result.ParseTypeImpl
 import android.content.res.Resources
 import android.hardware.display.DisplayManager
@@ -68,12 +66,15 @@
 import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.pm.permission.PermissionManagerServiceInternal
+import com.android.server.pm.pkg.parsing.ParsingPackage
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
 import com.android.server.testutils.nullable
 import com.android.server.testutils.whenever
 import com.android.server.utils.WatchedArrayMap
+import libcore.util.HexEncoding
 import org.junit.Assert
 import org.junit.rules.TestRule
 import org.junit.runner.Description
@@ -140,6 +141,7 @@
                 .mockStatic(EventLog::class.java)
                 .mockStatic(LocalServices::class.java)
                 .mockStatic(DeviceConfig::class.java)
+                .mockStatic(HexEncoding::class.java)
                 .apply(withSession)
         session = apply.startMocking()
         whenever(mocks.settings.insertPackageSettingLPw(
@@ -214,6 +216,7 @@
         val displayMetrics: DisplayMetrics = mock()
         val domainVerificationManagerInternal: DomainVerificationManagerInternal = mock()
         val handler = TestHandler(null)
+        val defaultAppProvider: DefaultAppProvider = mock()
     }
 
     companion object {
@@ -292,6 +295,7 @@
         whenever(mocks.injector.domainVerificationManagerInternal)
             .thenReturn(mocks.domainVerificationManagerInternal)
         whenever(mocks.injector.handler) { mocks.handler }
+        whenever(mocks.injector.defaultAppProvider) { mocks.defaultAppProvider }
         wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig)
         whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP)
         whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
new file mode 100644
index 0000000..2735e3d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.SharedLibraryInfo
+import android.content.pm.VersionedPackage
+import android.os.Build
+import android.os.storage.StorageManager
+import android.util.ArrayMap
+import android.util.PackageUtils
+import com.android.server.SystemConfig.SharedLibraryEntry
+import com.android.server.compat.PlatformCompat
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.server.testutils.any
+import com.android.server.testutils.eq
+import com.android.server.testutils.mock
+import com.android.server.testutils.nullable
+import com.android.server.testutils.spy
+import com.android.server.testutils.whenever
+import com.android.server.utils.WatchedLongSparseArray
+import com.google.common.truth.Truth.assertThat
+import libcore.util.HexEncoding
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.io.File
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(JUnit4::class)
+class SharedLibrariesImplTest {
+
+    companion object {
+        const val TEST_LIB_NAME = "test.lib"
+        const val TEST_LIB_PACKAGE_NAME = "com.android.lib.test"
+        const val BUILTIN_LIB_NAME = "builtin.lib"
+        const val STATIC_LIB_NAME = "static.lib"
+        const val STATIC_LIB_VERSION = 7L
+        const val STATIC_LIB_PACKAGE_NAME = "com.android.lib.static.provider"
+        const val DYNAMIC_LIB_NAME = "dynamic.lib"
+        const val DYNAMIC_LIB_PACKAGE_NAME = "com.android.lib.dynamic.provider"
+        const val CONSUMER_PACKAGE_NAME = "com.android.lib.consumer"
+        const val VERSION_UNDEFINED = SharedLibraryInfo.VERSION_UNDEFINED.toLong()
+    }
+
+    @Rule
+    @JvmField
+    val mRule = MockSystemRule()
+
+    private val mExistingPackages: ArrayMap<String, AndroidPackage> = ArrayMap()
+    private val mExistingSettings: MutableMap<String, PackageSetting> = mutableMapOf()
+
+    private lateinit var mSharedLibrariesImpl: SharedLibrariesImpl
+    private lateinit var mPms: PackageManagerService
+    private lateinit var mSettings: Settings
+
+    @Mock
+    private lateinit var mDeletePackageHelper: DeletePackageHelper
+    @Mock
+    private lateinit var mStorageManager: StorageManager
+    @Mock
+    private lateinit var mFile: File
+    @Mock
+    private lateinit var mPlatformCompat: PlatformCompat
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        mRule.system().stageNominalSystemState()
+        addExistingPackages()
+
+        val testParams = PackageManagerServiceTestParams().apply {
+            packages = mExistingPackages
+        }
+        mPms = spy(PackageManagerService(mRule.mocks().injector, testParams))
+        mSettings = mRule.mocks().injector.settings
+        mSharedLibrariesImpl = SharedLibrariesImpl(mPms, mRule.mocks().injector)
+        mSharedLibrariesImpl.setDeletePackageHelper(mDeletePackageHelper)
+        addExistingSharedLibraries()
+
+        whenever(mSettings.getPackageLPr(any())) { mExistingSettings[arguments[0]] }
+        whenever(mRule.mocks().injector.getSystemService(StorageManager::class.java))
+            .thenReturn(mStorageManager)
+        whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
+        doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageNameLPr(any(), any())
+        whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
+            .thenReturn(PackageManager.DELETE_SUCCEEDED)
+        whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
+        wheneverStatic { HexEncoding.decode(STATIC_LIB_NAME, false) }
+                .thenReturn(PackageUtils.computeSha256DigestBytes(
+                        mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+                                .pkg.signingDetails.signatures!![0].toByteArray()))
+    }
+
+    @Test
+    fun snapshot_shouldSealed() {
+        val builtinLibs = mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME]
+        assertThat(builtinLibs).isNotNull()
+
+        assertFailsWith(IllegalStateException::class) {
+            mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME] = WatchedLongSparseArray()
+        }
+        assertFailsWith(IllegalStateException::class) {
+            builtinLibs!!.put(VERSION_UNDEFINED, libOfBuiltin(BUILTIN_LIB_NAME))
+        }
+    }
+
+    @Test
+    fun addBuiltInSharedLibrary() {
+        mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(TEST_LIB_NAME))
+
+        assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME)).isNotNull()
+        assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(TEST_LIB_NAME, VERSION_UNDEFINED))
+            .isNotNull()
+    }
+
+    @Test
+    fun addBuiltInSharedLibrary_withDuplicateLibName() {
+        val duplicate = libEntry(BUILTIN_LIB_NAME, "duplicate.path")
+        mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(duplicate)
+        val sharedLibInfo = mSharedLibrariesImpl
+            .getSharedLibraryInfo(BUILTIN_LIB_NAME, VERSION_UNDEFINED)
+
+        assertThat(sharedLibInfo).isNotNull()
+        assertThat(sharedLibInfo!!.path).isNotEqualTo(duplicate.filename)
+    }
+
+    @Test
+    fun commitSharedLibraryInfo_withStaticSharedLib() {
+        val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
+        mSharedLibrariesImpl.commitSharedLibraryInfoLPw(testInfo)
+        val sharedLibInfos = mSharedLibrariesImpl
+            .getStaticLibraryInfos(testInfo.declaringPackage.packageName)
+
+        assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME))
+            .isNotNull()
+        assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(testInfo.name, testInfo.longVersion))
+            .isNotNull()
+        assertThat(sharedLibInfos).isNotNull()
+        assertThat(sharedLibInfos.get(testInfo.longVersion)).isNotNull()
+    }
+
+    @Test
+    fun removeSharedLibrary() {
+        doAnswer { mutableListOf(VersionedPackage(CONSUMER_PACKAGE_NAME, 1L)) }.`when`(mPms)
+            .getPackagesUsingSharedLibrary(any(), any(), any(), any())
+        val staticInfo = mSharedLibrariesImpl
+            .getSharedLibraryInfo(STATIC_LIB_NAME, STATIC_LIB_VERSION)!!
+
+        mSharedLibrariesImpl.removeSharedLibraryLPw(STATIC_LIB_NAME, STATIC_LIB_VERSION)
+
+        assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(STATIC_LIB_NAME)).isNull()
+        assertThat(mSharedLibrariesImpl
+            .getStaticLibraryInfos(staticInfo.declaringPackage.packageName)).isNull()
+        verify(mExistingSettings[CONSUMER_PACKAGE_NAME]!!)
+            .setOverlayPathsForLibrary(any(), nullable(), any())
+    }
+
+    @Test
+    fun pruneUnusedStaticSharedLibraries() {
+        mSharedLibrariesImpl.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE, 0)
+
+        verify(mDeletePackageHelper)
+            .deletePackageX(eq(STATIC_LIB_PACKAGE_NAME), any(), any(), any(), any())
+    }
+
+    @Test
+    fun getLatestSharedLibraVersion() {
+        val newLibSetting = addPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
+            staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
+
+        val latestInfo =
+            mSharedLibrariesImpl.getLatestStaticSharedLibraVersionLPr(newLibSetting.pkg)!!
+
+        assertThat(latestInfo).isNotNull()
+        assertThat(latestInfo.name).isEqualTo(STATIC_LIB_NAME)
+        assertThat(latestInfo.longVersion).isEqualTo(STATIC_LIB_VERSION)
+    }
+
+    @Test
+    fun getStaticSharedLibLatestVersionSetting() {
+        val pair = createBasicAndroidPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
+            staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
+        val parsedPackage = pair.second as ParsedPackage
+        val scanRequest = ScanRequest(parsedPackage, null, null, null,
+            null, null, null, 0, 0, false, null, null)
+        val scanResult = ScanResult(scanRequest, true, null, null, false, 0, null, null, null)
+
+        val latestInfoSetting =
+            mSharedLibrariesImpl.getStaticSharedLibLatestVersionSetting(scanResult)!!
+
+        assertThat(latestInfoSetting).isNotNull()
+        assertThat(latestInfoSetting.packageName).isEqualTo(STATIC_LIB_PACKAGE_NAME)
+    }
+
+    @Test
+    fun updateSharedLibraries_withDynamicLibPackage() {
+        val testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
+        assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+        mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+                null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+        assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+        assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+    }
+
+    @Test
+    fun updateSharedLibraries_withStaticLibPackage() {
+        val testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+        assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+        mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+                null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+        assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+        assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+    }
+
+    @Test
+    fun updateSharedLibraries_withConsumerPackage() {
+        val testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
+        assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+        mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+                null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+        assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
+        assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+        assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
+    }
+
+    @Test
+    fun updateAllSharedLibraries() {
+        mExistingSettings.forEach {
+            assertThat(it.value.usesLibraryFiles).isEmpty()
+        }
+
+        mSharedLibrariesImpl.updateAllSharedLibrariesLPw(
+                null /* updatedPkg */, null /* updatedPkgSetting */, mExistingPackages)
+
+        var testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
+        assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+        assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+
+        testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+        assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
+        assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+        assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+
+        testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
+        assertThat(testPackageSetting.usesLibraryFiles).hasSize(3)
+        assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+        assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+        assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
+    }
+
+    @Test
+    fun getAllowedSharedLibInfos_withStaticSharedLibInfo() {
+        val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
+        val scanResult = ScanResult(mock(), true, null, null,
+            false, 0, null, testInfo, null)
+
+        val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult)
+
+        assertThat(allowedInfos).hasSize(1)
+        assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME)
+    }
+
+    @Test
+    fun getAllowedSharedLibInfos_withDynamicSharedLibInfo() {
+        val testInfo = libOfDynamic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME)
+        val pair = createBasicAndroidPackage(
+            TEST_LIB_PACKAGE_NAME, 10L, libraries = arrayOf(TEST_LIB_NAME))
+        val parsedPackage = pair.second.apply {
+            isSystem = true
+        } as ParsedPackage
+        val packageSetting = mRule.system()
+            .createBasicSettingBuilder(pair.first.parentFile, parsedPackage.hideAsFinal())
+            .setPkgFlags(ApplicationInfo.FLAG_SYSTEM).build()
+        val scanRequest = ScanRequest(parsedPackage, null, null, null,
+            null, null, null, 0, 0, false, null, null)
+        val scanResult = ScanResult(scanRequest, true, packageSetting, null,
+            false, 0, null, null, listOf(testInfo))
+
+        val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult)
+
+        assertThat(allowedInfos).hasSize(1)
+        assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME)
+    }
+
+    private fun addExistingPackages() {
+        // add a dynamic shared library that is using the builtin library
+        addPackage(DYNAMIC_LIB_PACKAGE_NAME, 1L,
+            libraries = arrayOf(DYNAMIC_LIB_NAME),
+            usesLibraries = arrayOf(BUILTIN_LIB_NAME))
+
+        // add a static shared library v7 that is using the dynamic shared library
+        addPackage(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_VERSION,
+            staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = STATIC_LIB_VERSION,
+            usesLibraries = arrayOf(DYNAMIC_LIB_NAME))
+
+        // add a consumer package that is using the dynamic and static shared library
+        addPackage(CONSUMER_PACKAGE_NAME, 1L,
+            usesLibraries = arrayOf(DYNAMIC_LIB_NAME),
+            usesStaticLibraries = arrayOf(STATIC_LIB_NAME),
+            usesStaticLibraryVersions = arrayOf(STATIC_LIB_VERSION))
+    }
+
+    private fun addExistingSharedLibraries() {
+        mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(BUILTIN_LIB_NAME))
+        mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
+            libOfDynamic(DYNAMIC_LIB_PACKAGE_NAME, DYNAMIC_LIB_NAME))
+        mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
+            libOfStatic(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_NAME, STATIC_LIB_VERSION))
+    }
+
+    private fun addPackage(
+        packageName: String,
+        version: Long,
+        libraries: Array<String>? = null,
+        staticLibrary: String? = null,
+        staticLibraryVersion: Long = 0L,
+        usesLibraries: Array<String>? = null,
+        usesStaticLibraries: Array<String>? = null,
+        usesStaticLibraryVersions: Array<Long>? = null
+    ): PackageSetting {
+        val pair = createBasicAndroidPackage(packageName, version, libraries, staticLibrary,
+            staticLibraryVersion, usesLibraries, usesStaticLibraries, usesStaticLibraryVersions)
+        val apkPath = pair.first
+        val parsingPackage = pair.second
+        val spyPkg = spy((parsingPackage as ParsedPackage).hideAsFinal())
+        mExistingPackages[packageName] = spyPkg
+
+        val spyPackageSetting = spy(mRule.system()
+            .createBasicSettingBuilder(apkPath.parentFile, spyPkg).build())
+        mExistingSettings[spyPackageSetting.packageName] = spyPackageSetting
+
+        return spyPackageSetting
+    }
+
+    private fun createBasicAndroidPackage(
+        packageName: String,
+        version: Long,
+        libraries: Array<String>? = null,
+        staticLibrary: String? = null,
+        staticLibraryVersion: Long = 0L,
+        usesLibraries: Array<String>? = null,
+        usesStaticLibraries: Array<String>? = null,
+        usesStaticLibraryVersions: Array<Long>? = null
+    ): Pair<File, PackageImpl> {
+        assertFalse { libraries != null && staticLibrary != null }
+        assertTrue { (usesStaticLibraries?.size ?: -1) == (usesStaticLibraryVersions?.size ?: -1) }
+
+        val pair = mRule.system()
+            .createBasicAndroidPackage(mRule.system().dataAppDirectory, packageName, version)
+        pair.second.apply {
+            setTargetSdkVersion(Build.VERSION_CODES.S)
+            libraries?.forEach { addLibraryName(it) }
+            staticLibrary?.let {
+                setStaticSharedLibName(it)
+                setStaticSharedLibVersion(staticLibraryVersion)
+                setStaticSharedLibrary(true)
+            }
+            usesLibraries?.forEach { addUsesLibrary(it) }
+            usesStaticLibraries?.forEachIndexed { index, s ->
+                addUsesStaticLibrary(s,
+                    usesStaticLibraryVersions?.get(index) ?: 0L,
+                        arrayOf(s))
+            }
+        }
+        return pair
+    }
+
+    private fun libEntry(libName: String, path: String? = null): SharedLibraryEntry =
+        SharedLibraryEntry(libName, path ?: builtinLibPath(libName),
+            arrayOfNulls(0), false /* isNative */)
+
+    private fun libOfBuiltin(libName: String): SharedLibraryInfo =
+        SharedLibraryInfo(builtinLibPath(libName),
+            null /* packageName */,
+            null /* codePaths */,
+            libName,
+            VERSION_UNDEFINED,
+            SharedLibraryInfo.TYPE_BUILTIN,
+            VersionedPackage(PLATFORM_PACKAGE_NAME, 0L /* versionCode */),
+            null /* dependentPackages */,
+            null /* dependencies */,
+            false /* isNative */)
+
+    private fun libOfStatic(
+        packageName: String,
+        libName: String,
+        version: Long
+    ): SharedLibraryInfo =
+        SharedLibraryInfo(null /* path */,
+            packageName,
+            listOf(apkPath(packageName)),
+            libName,
+            version,
+            SharedLibraryInfo.TYPE_STATIC,
+            VersionedPackage(packageName, version /* versionCode */),
+            null /* dependentPackages */,
+            null /* dependencies */,
+            false /* isNative */)
+
+    private fun libOfDynamic(packageName: String, libName: String): SharedLibraryInfo =
+        SharedLibraryInfo(null /* path */,
+            packageName,
+            listOf(apkPath(packageName)),
+            libName,
+            VERSION_UNDEFINED,
+            SharedLibraryInfo.TYPE_DYNAMIC,
+            VersionedPackage(packageName, 1L /* versionCode */),
+            null /* dependentPackages */,
+            null /* dependencies */,
+            false /* isNative */)
+
+    private fun builtinLibPath(libName: String): String = "/system/app/$libName/$libName.jar"
+
+    private fun apkPath(packageName: String): String =
+            File(mRule.system().dataAppDirectory, packageName).path
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index f2415b4..bdfdf77 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -158,10 +158,10 @@
 
         mStagingManager.restoreSessions(Arrays.asList(session1, session2), true);
 
-        assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed");
 
-        assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed");
     }
 
@@ -247,12 +247,12 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a "
                 + "staged session supposed to be activated");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -303,22 +303,22 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession1.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. "
                 + "Error: Failed for test");
 
         assertThat(apexSession2.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't "
                 + "activate nor fail. Marking it as failed anyway.");
 
         assertThat(apexSession3.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a "
                 + "staged session supposed to be activated");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -351,12 +351,12 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't "
                 + "activate nor fail. Marking it as failed anyway.");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -445,11 +445,11 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -754,7 +754,7 @@
                 /* isReady */ false,
                 /* isFailed */ false,
                 /* isApplied */false,
-                /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
+                /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.SESSION_NO_ERROR,
                 /* stagedSessionErrorMessage */ "no error");
 
         StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
new file mode 100644
index 0000000..fe7e2d9
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm
+
+import android.content.Intent
+import android.content.pm.PackageManagerInternal
+import android.content.pm.SuspendDialogInfo
+import android.os.Binder
+import android.os.Build
+import android.os.Bundle
+import android.os.PersistableBundle
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.ArrayMap
+import android.util.SparseArray
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.testutils.TestHandler
+import com.android.server.testutils.any
+import com.android.server.testutils.eq
+import com.android.server.testutils.nullable
+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.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+class SuspendPackageHelperTest {
+
+    companion object {
+        const val TEST_PACKAGE_1 = "com.android.test.package1"
+        const val TEST_PACKAGE_2 = "com.android.test.package2"
+        const val DEVICE_OWNER_PACKAGE = "com.android.test.owner"
+        const val NONEXISTENT_PACKAGE = "com.android.test.nonexistent"
+        const val DEVICE_ADMIN_PACKAGE = "com.android.test.known.device.admin"
+        const val DEFAULT_HOME_PACKAGE = "com.android.test.known.home"
+        const val DIALER_PACKAGE = "com.android.test.known.dialer"
+        const val INSTALLER_PACKAGE = "com.android.test.known.installer"
+        const val UNINSTALLER_PACKAGE = "com.android.test.known.uninstaller"
+        const val VERIFIER_PACKAGE = "com.android.test.known.verifier"
+        const val PERMISSION_CONTROLLER_PACKAGE = "com.android.test.known.permission"
+        const val TEST_USER_ID = 0
+    }
+
+    lateinit var pms: PackageManagerService
+    lateinit var suspendPackageHelper: SuspendPackageHelper
+    lateinit var testHandler: TestHandler
+    lateinit var defaultAppProvider: DefaultAppProvider
+    lateinit var packageSetting1: PackageStateInternal
+    lateinit var packageSetting2: PackageStateInternal
+    lateinit var ownerSetting: PackageStateInternal
+    lateinit var packagesToSuspend: Array<String>
+    lateinit var uidsToSuspend: IntArray
+
+    @Mock
+    lateinit var broadcastHelper: BroadcastHelper
+    @Mock
+    lateinit var protectedPackages: ProtectedPackages
+
+    @Captor
+    lateinit var bundleCaptor: ArgumentCaptor<Bundle>
+
+    @Rule
+    @JvmField
+    val rule = MockSystemRule()
+    var deviceOwnerUid = 0
+
+    @Before
+    @Throws(Exception::class)
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        rule.system().stageNominalSystemState()
+        pms = spy(createPackageManagerService(
+            TEST_PACKAGE_1, TEST_PACKAGE_2, DEVICE_OWNER_PACKAGE, DEVICE_ADMIN_PACKAGE,
+            DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE,
+            VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE))
+        suspendPackageHelper = SuspendPackageHelper(
+            pms, rule.mocks().injector, broadcastHelper, protectedPackages)
+        defaultAppProvider = rule.mocks().defaultAppProvider
+        testHandler = rule.mocks().handler
+        packageSetting1 = pms.getPackageStateInternal(TEST_PACKAGE_1)!!
+        packageSetting2 = pms.getPackageStateInternal(TEST_PACKAGE_2)!!
+        ownerSetting = pms.getPackageStateInternal(DEVICE_OWNER_PACKAGE)!!
+        deviceOwnerUid = UserHandle.getUid(TEST_USER_ID, ownerSetting.appId)
+        packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId)
+
+        whenever(protectedPackages.getDeviceOwnerOrProfileOwnerPackage(eq(TEST_USER_ID)))
+            .thenReturn(DEVICE_OWNER_PACKAGE)
+        whenever(rule.mocks().userManagerService.hasUserRestriction(
+            eq(UserManager.DISALLOW_APPS_CONTROL), eq(TEST_USER_ID))).thenReturn(true)
+        whenever(rule.mocks().userManagerService.hasUserRestriction(
+            eq(UserManager.DISALLOW_UNINSTALL_APPS), eq(TEST_USER_ID))).thenReturn(true)
+        mockKnownPackages(pms)
+    }
+
+    @Test
+    fun setPackagesSuspended() {
+        val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        val failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+
+        verify(pms).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
+            nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+            nullable(), nullable(), nullable())
+        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
+            nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), nullable(), nullable())
+        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
+            nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), nullable(), nullable())
+
+        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(failedNames).isEmpty()
+    }
+
+    @Test
+    fun setPackagesSuspended_emptyPackageName() {
+        var failedNames = suspendPackageHelper.setPackagesSuspended(null /* packageNames */,
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+        assertThat(failedNames).isNull()
+
+        failedNames = suspendPackageHelper.setPackagesSuspended(arrayOfNulls(0),
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+        assertThat(failedNames).isEmpty()
+    }
+
+    @Test
+    fun setPackagesSuspended_callerIsNotAllowed() {
+        val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, Binder.getCallingUid())
+
+        assertThat(failedNames).asList().hasSize(1)
+        assertThat(failedNames).asList().contains(TEST_PACKAGE_2)
+    }
+
+    @Test
+    fun setPackagesSuspended_callerSuspendItself() {
+        val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(DEVICE_OWNER_PACKAGE),
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+        assertThat(failedNames).asList().hasSize(1)
+        assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE)
+    }
+
+    @Test
+    fun setPackagesSuspended_nonexistentPackage() {
+        val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(NONEXISTENT_PACKAGE),
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+        assertThat(failedNames).asList().hasSize(1)
+        assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE)
+    }
+
+    @Test
+    fun setPackagesSuspended_knownPackages() {
+        val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
+            INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
+        val failedNames = suspendPackageHelper.setPackagesSuspended(knownPackages,
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
+
+        assertThat(failedNames.size).isEqualTo(knownPackages.size)
+        for (pkg in knownPackages) {
+            assertThat(failedNames).asList().contains(pkg)
+        }
+    }
+
+    @Test
+    fun setPackagesUnsuspended() {
+        val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+        failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+            false /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+
+        verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
+            nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+            nullable(), nullable(), nullable())
+        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+            nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
+            nullable(), nullable())
+        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+            nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
+            nullable(), nullable())
+
+        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(failedNames).isEmpty()
+    }
+
+    @Test
+    fun getUnsuspendablePackagesForUser() {
+        val suspendables = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        val unsuspendables = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
+            INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
+        val results = suspendPackageHelper.getUnsuspendablePackagesForUser(
+            suspendables + unsuspendables, TEST_USER_ID, deviceOwnerUid)
+
+        assertThat(results.size).isEqualTo(unsuspendables.size)
+        for (pkg in unsuspendables) {
+            assertThat(results).asList().contains(pkg)
+        }
+    }
+
+    @Test
+    fun getUnsuspendablePackagesForUser_callerIsNotAllowed() {
+        val suspendables = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        val results = suspendPackageHelper.getUnsuspendablePackagesForUser(
+            suspendables, TEST_USER_ID, Binder.getCallingUid())
+
+        assertThat(results.size).isEqualTo(suspendables.size)
+        for (pkg in suspendables) {
+            assertThat(results).asList().contains(pkg)
+        }
+    }
+
+    @Test
+    fun getSuspendedPackageAppExtras() {
+        val appExtras = PersistableBundle()
+        appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
+            true /* suspended */, appExtras, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+
+        val result = suspendPackageHelper.getSuspendedPackageAppExtras(
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)!!
+
+        assertThat(result.getString(TEST_PACKAGE_1)).isEqualTo(TEST_PACKAGE_1)
+    }
+
+    @Test
+    fun removeSuspensionsBySuspendingPackage() {
+        val appExtras = PersistableBundle()
+        appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+            true /* suspended */, appExtras, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+        assertThat(suspendPackageHelper.getSuspendingPackage(
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+        assertThat(suspendPackageHelper.getSuspendingPackage(
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
+        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
+
+        suspendPackageHelper.removeSuspensionsBySuspendingPackage(targetPackages,
+            { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID)
+
+        testHandler.flush()
+        verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
+            nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+            nullable(), nullable(), nullable())
+        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+            nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
+            nullable(), nullable())
+        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+            nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
+            nullable(), nullable())
+
+        assertThat(suspendPackageHelper.getSuspendingPackage(
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
+        assertThat(suspendPackageHelper.getSuspendingPackage(
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
+        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
+        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
+    }
+
+    @Test
+    fun getSuspendedPackageLauncherExtras() {
+        val launcherExtras = PersistableBundle()
+        launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
+            true /* suspended */, null /* appExtras */, launcherExtras,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+
+        val result = suspendPackageHelper.getSuspendedPackageLauncherExtras(
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)!!
+
+        assertThat(result.getString(TEST_PACKAGE_2)).isEqualTo(TEST_PACKAGE_2)
+    }
+
+    @Test
+    fun isPackageSuspended() {
+        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+
+        assertThat(suspendPackageHelper.isPackageSuspended(
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isTrue()
+    }
+
+    @Test
+    fun getSuspendingPackage() {
+        val launcherExtras = PersistableBundle()
+        launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
+            true /* suspended */, null /* appExtras */, launcherExtras,
+            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+
+        assertThat(suspendPackageHelper.getSuspendingPackage(
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+    }
+
+    @Test
+    fun getSuspendedDialogInfo() {
+        val dialogInfo = SuspendDialogInfo.Builder()
+            .setTitle(TEST_PACKAGE_1).build()
+        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
+            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+            dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        testHandler.flush()
+        assertThat(failedNames).isEmpty()
+
+        val result = suspendPackageHelper.getSuspendedDialogInfo(
+            TEST_PACKAGE_1, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
+
+        assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun sendPackagesSuspendedForUser_withSameVisibilityAllowList() {
+        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+        mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
+
+        suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+        testHandler.flush()
+        verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
+                anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
+
+        var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+        assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(changedUids).asList().containsExactly(
+                packageSetting1.appId, packageSetting2.appId)
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun sendPackagesSuspendedForUser_withDifferentVisibilityAllowList() {
+        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+        mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
+
+        suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+        testHandler.flush()
+        verify(broadcastHelper, times(2)).sendPackageBroadcast(
+                any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+                nullable(), any(), nullable())
+
+        bundleCaptor.allValues.forEach {
+            var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+            var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+            assertThat(changedPackages?.size).isEqualTo(1)
+            assertThat(changedUids?.size).isEqualTo(1)
+            assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+            assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
+        }
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun sendPackagesSuspendedForUser_withNullVisibilityAllowList() {
+        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+        mockAllowList(packageSetting2, null)
+
+        suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+        testHandler.flush()
+        verify(broadcastHelper, times(2)).sendPackageBroadcast(
+                any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+                nullable(), nullable(), nullable())
+
+        bundleCaptor.allValues.forEach {
+            var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+            var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+            assertThat(changedPackages?.size).isEqualTo(1)
+            assertThat(changedUids?.size).isEqualTo(1)
+            assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+            assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
+        }
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun sendPackagesSuspendModifiedForUser() {
+        suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+        testHandler.flush()
+        verify(broadcastHelper).sendPackageBroadcast(
+                eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
+                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(modifiedUids).asList().containsExactly(
+                packageSetting1.appId, packageSetting2.appId)
+    }
+
+    private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
+        this.put(TEST_USER_ID, uids)
+    }
+
+    private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
+        whenever(rule.mocks().appsFilter.getVisibilityAllowList(
+            argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java),
+            any() as ArrayMap<String, out PackageStateInternal>
+        ))
+            .thenReturn(list)
+    }
+
+    private fun mockKnownPackages(pms: PackageManagerService) {
+        Mockito.doAnswer { it.arguments[0] == DEVICE_ADMIN_PACKAGE }.`when`(pms)
+            .isPackageDeviceAdmin(any(), any())
+        Mockito.doReturn(DEFAULT_HOME_PACKAGE).`when`(defaultAppProvider)
+            .getDefaultHome(eq(TEST_USER_ID))
+        Mockito.doReturn(DIALER_PACKAGE).`when`(defaultAppProvider)
+            .getDefaultDialer(eq(TEST_USER_ID))
+        Mockito.doReturn(arrayOf(INSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal(
+            eq(PackageManagerInternal.PACKAGE_INSTALLER), eq(TEST_USER_ID))
+        Mockito.doReturn(arrayOf(UNINSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal(
+            eq(PackageManagerInternal.PACKAGE_UNINSTALLER), eq(TEST_USER_ID))
+        Mockito.doReturn(arrayOf(VERIFIER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal(
+            eq(PackageManagerInternal.PACKAGE_VERIFIER), eq(TEST_USER_ID))
+        Mockito.doReturn(arrayOf(PERMISSION_CONTROLLER_PACKAGE)).`when`(pms)
+            .getKnownPackageNamesInternal(
+                eq(PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER), eq(TEST_USER_ID))
+    }
+
+    private fun createPackageManagerService(vararg stageExistingPackages: String):
+            PackageManagerService {
+        stageExistingPackages.forEach {
+            rule.system().stageScanExistingPackage(it, 1L,
+                    rule.system().dataAppDirectory)
+        }
+        var pms = PackageManagerService(rule.mocks().injector,
+                false /*coreOnly*/,
+                false /*factoryTest*/,
+                MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+                false /*isEngBuild*/,
+                false /*isUserDebugBuild*/,
+                Build.VERSION_CODES.CUR_DEVELOPMENT,
+                Build.VERSION.INCREMENTAL,
+                false /*snapshotEnabled*/)
+        rule.system().validateFinalState()
+        return pms
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
deleted file mode 100644
index 02ee35b..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm
-
-import android.content.Intent
-import android.os.Build
-import android.os.Bundle
-import android.util.ArrayMap
-import android.util.SparseArray
-import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.testutils.any
-import com.android.server.testutils.eq
-import com.android.server.testutils.nullable
-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.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Captor
-import org.mockito.Mockito.argThat
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(JUnit4::class)
-class SuspendPackagesBroadcastTest {
-
-    companion object {
-        const val TEST_PACKAGE_1 = "com.android.test.package1"
-        const val TEST_PACKAGE_2 = "com.android.test.package2"
-        const val TEST_USER_ID = 0
-    }
-
-    lateinit var pms: PackageManagerService
-    lateinit var packageSetting1: PackageStateInternal
-    lateinit var packageSetting2: PackageStateInternal
-    lateinit var packagesToSuspend: Array<String>
-    lateinit var uidsToSuspend: IntArray
-
-    @Captor
-    lateinit var bundleCaptor: ArgumentCaptor<Bundle>
-
-    @Rule
-    @JvmField
-    val rule = MockSystemRule()
-
-    @Before
-    @Throws(Exception::class)
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        rule.system().stageNominalSystemState()
-        pms = spy(createPackageManagerService(TEST_PACKAGE_1, TEST_PACKAGE_2))
-        packageSetting1 = pms.getPackageStateInternal(TEST_PACKAGE_1)!!
-        packageSetting2 = pms.getPackageStateInternal(TEST_PACKAGE_2)!!
-        packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId)
-    }
-
-    @Test
-    @Throws(Exception::class)
-    fun sendPackagesSuspendedForUser_withSameVisibilityAllowList() {
-        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
-        mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
-
-        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
-        verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
-
-        var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
-        assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        assertThat(changedUids).asList().containsExactly(
-                packageSetting1.appId, packageSetting2.appId)
-    }
-
-    @Test
-    @Throws(Exception::class)
-    fun sendPackagesSuspendedForUser_withDifferentVisibilityAllowList() {
-        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
-        mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
-
-        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
-        verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
-
-        bundleCaptor.allValues.forEach {
-            var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-            var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
-            assertThat(changedPackages?.size).isEqualTo(1)
-            assertThat(changedUids?.size).isEqualTo(1)
-            assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-            assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
-        }
-    }
-
-    @Test
-    @Throws(Exception::class)
-    fun sendPackagesSuspendedForUser_withNullVisibilityAllowList() {
-        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
-        mockAllowList(packageSetting2, null)
-
-        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
-        verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
-
-        bundleCaptor.allValues.forEach {
-            var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-            var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
-            assertThat(changedPackages?.size).isEqualTo(1)
-            assertThat(changedUids?.size).isEqualTo(1)
-            assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-            assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
-        }
-    }
-
-    @Test
-    @Throws(Exception::class)
-    fun sendPackagesSuspendModifiedForUser() {
-        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
-        verify(pms).sendPackageBroadcast(
-                eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
-
-        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
-        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        assertThat(modifiedUids).asList().containsExactly(
-                packageSetting1.appId, packageSetting2.appId)
-    }
-
-    private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
-        this.put(TEST_USER_ID, uids)
-    }
-
-    private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
-        whenever(rule.mocks().appsFilter.getVisibilityAllowList(
-            argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java),
-            any() as ArrayMap<String, out PackageStateInternal>
-        ))
-            .thenReturn(list)
-    }
-
-    private fun createPackageManagerService(vararg stageExistingPackages: String):
-            PackageManagerService {
-        stageExistingPackages.forEach {
-            rule.system().stageScanExistingPackage(it, 1L,
-                    rule.system().dataAppDirectory)
-        }
-        var pms = PackageManagerService(rule.mocks().injector,
-                false /*coreOnly*/,
-                false /*factoryTest*/,
-                MockSystem.DEFAULT_VERSION_INFO.fingerprint,
-                false /*isEngBuild*/,
-                false /*isUserDebugBuild*/,
-                Build.VERSION_CODES.CUR_DEVELOPMENT,
-                Build.VERSION.INCREMENTAL,
-                false /*snapshotEnabled*/)
-        rule.system().validateFinalState()
-        return pms
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
new file mode 100644
index 0000000..0411b94
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.attention.AttentionManagerInternal;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
+import android.test.mock.MockContentResolver;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.PowerManagerService.BatteryReceiver;
+import com.android.server.power.PowerManagerService.Injector;
+import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
+import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.server.power.batterysaver.BatterySaverPolicy;
+import com.android.server.power.batterysaver.BatterySaverStateMachine;
+import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.power.PowerManagerService}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:PowerManagerServiceMockingTest
+ */
+public class PowerManagerServiceMockingTest {
+    private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+    private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
+
+    private static final float BRIGHTNESS_FACTOR = 0.7f;
+    private static final boolean BATTERY_SAVER_ENABLED = true;
+
+    @Mock private BatterySaverController mBatterySaverControllerMock;
+    @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
+    @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
+    @Mock private LightsManager mLightsManagerMock;
+    @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
+    @Mock private BatteryManagerInternal mBatteryManagerInternalMock;
+    @Mock private ActivityManagerInternal mActivityManagerInternalMock;
+    @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
+    @Mock private DreamManagerInternal mDreamManagerInternalMock;
+    @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
+    @Mock private Notifier mNotifierMock;
+    @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
+    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+    @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
+    @Mock private DeviceStateManager mDeviceStateManagerMock;
+
+    @Mock
+    private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
+
+    private PowerManagerService mService;
+    private PowerSaveState mPowerSaveState;
+    private ContextWrapper mContextSpy;
+    private BatteryReceiver mBatteryReceiver;
+    private UserSwitchedReceiver mUserSwitchedReceiver;
+    private Resources mResourcesSpy;
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        FakeSettingsProvider.clearSettingsProvider();
+
+        mPowerSaveState = new PowerSaveState.Builder()
+                .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
+                .setBrightnessFactor(BRIGHTNESS_FACTOR)
+                .build();
+        when(mBatterySaverPolicyMock.getBatterySaverPolicy(
+                eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
+                .thenReturn(mPowerSaveState);
+        when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
+        when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
+        when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
+                .thenReturn(true);
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
+        when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
+
+        addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+        addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+        addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+        addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
+        addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
+
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        mResourcesSpy = spy(mContextSpy.getResources());
+        when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+
+        MockContentResolver cr = new MockContentResolver(mContextSpy);
+        cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContextSpy.getContentResolver()).thenReturn(cr);
+
+        when(mContextSpy.getSystemService(DeviceStateManager.class))
+                .thenReturn(mDeviceStateManagerMock);
+
+        Settings.Global.putInt(mContextSpy.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+    }
+
+    private PowerManagerService createService() {
+        mService = new PowerManagerService(mContextSpy, new Injector() {
+            @Override
+            Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                    SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+                return mNotifierMock;
+            }
+
+            @Override
+            SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+                return super.createSuspendBlocker(service, name);
+            }
+
+            @Override
+            BatterySaverPolicy createBatterySaverPolicy(
+                    Object lock, Context context, BatterySavingStats batterySavingStats) {
+                return mBatterySaverPolicyMock;
+            }
+
+            @Override
+            BatterySaverController createBatterySaverController(
+                    Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
+                    BatterySavingStats batterySavingStats) {
+                return mBatterySaverControllerMock;
+            }
+
+            @Override
+            BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
+                    BatterySaverController batterySaverController) {
+                return mBatterySaverStateMachineMock;
+            }
+
+            @Override
+            NativeWrapper createNativeWrapper() {
+                return mNativeWrapperMock;
+            }
+
+            @Override
+            WirelessChargerDetector createWirelessChargerDetector(
+                    SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
+                return mWirelessChargerDetectorMock;
+            }
+
+            @Override
+            AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
+                return mAmbientDisplayConfigurationMock;
+            }
+
+            @Override
+            InattentiveSleepWarningController createInattentiveSleepWarningController() {
+                return mInattentiveSleepWarningControllerMock;
+            }
+
+            @Override
+            public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+                return mSystemPropertiesMock;
+            }
+
+            @Override
+            PowerManagerService.Clock createClock() {
+                return () -> mClock.now();
+            }
+
+            @Override
+            Handler createHandler(Looper looper, Handler.Callback callback) {
+                return new Handler(mTestLooper.getLooper(), callback);
+            }
+
+            @Override
+            void invalidateIsInteractiveCaches() {
+                // Avoids an SELinux failure.
+            }
+        });
+        return mService;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(LightsManager.class);
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(AttentionManagerInternal.class);
+        LocalServices.removeServiceForTest(DreamManagerInternal.class);
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    /**
+     * 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();
+    }
+
+    @Test
+    public void testUserActivityOnDeviceStateChange() {
+        createService();
+        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        final DisplayInfo info = new DisplayInfo();
+        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+        final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
+                ArgumentCaptor.forClass(DeviceStateCallback.class);
+        verify(mDeviceStateManagerMock).registerCallback(any(),
+                deviceStateCallbackCaptor.capture());
+
+        // Advance the time 10001 and verify that the device thinks it has been idle
+        // for just less than that.
+        mService.onUserActivity();
+        advanceTime(10001);
+        assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
+
+        // Send a display state change event and advance the clock 10.
+        final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
+        deviceStateCallback.onStateChanged(1);
+        final long timeToAdvance = 10;
+        advanceTime(timeToAdvance);
+
+        // Ensure that the device has been idle for only 10 (doesn't include the idle time
+        // before the display state event).
+        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
+        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
+
+        // Send the same state and ensure that does not trigger an update.
+        deviceStateCallback.onStateChanged(1);
+        advanceTime(timeToAdvance);
+        final long newTime = timeToAdvance * 2;
+
+        assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
+        assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
index 6a35481..234d70b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -22,6 +22,7 @@
 import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
 import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_VR;
 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+import static android.view.Display.DEFAULT_DISPLAY_GROUP;
 
 import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
 import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED;
@@ -60,6 +61,7 @@
                     POLICY_DIM,
                     POLICY_BRIGHT,
                     POLICY_VR);
+    private static final int OTHER_DISPLAY_GROUP = DEFAULT_DISPLAY_GROUP + 1;
 
     @ClassRule
     public static final TestableContext sContext = new TestableContext(
@@ -106,8 +108,8 @@
                 KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/);
 
         setup();
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
     }
@@ -116,8 +118,8 @@
     public void recordScreenPolicy_samePolicy_noop() {
         for (int policy : ALL_POLICIES) {
             setup();
-            mScreenUndimDetector.recordScreenPolicy(policy);
-            mScreenUndimDetector.recordScreenPolicy(policy);
+            mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, policy);
+            mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, policy);
 
             assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
         }
@@ -125,13 +127,24 @@
 
     @Test
     public void recordScreenPolicy_dimToBright_extends() {
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
     }
 
     @Test
+    public void recordScreenPolicy_dimToBright_ignoresOtherDisplayGroup() {
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+
+        mScreenUndimDetector.recordScreenPolicy(OTHER_DISPLAY_GROUP, POLICY_BRIGHT);
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
+    }
+
+    @Test
     public void recordScreenPolicy_otherTransitions_doesNotExtend() {
         for (int from : ALL_POLICIES) {
             for (int to : ALL_POLICIES) {
@@ -139,8 +152,8 @@
                     continue;
                 }
                 setup();
-                mScreenUndimDetector.recordScreenPolicy(from);
-                mScreenUndimDetector.recordScreenPolicy(to);
+                mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, from);
+                mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to);
 
                 assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
                 assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
@@ -155,28 +168,49 @@
                 Integer.toString(2), false /*makeDefault*/);
         mScreenUndimDetector.readValuesFromDeviceConfig();
 
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
 
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
     }
 
     @Test
+    public void recordScreenPolicy_dimToBright_twoUndimsNeeded_otherDisplayDoesNotExtend() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(OTHER_DISPLAY_GROUP, POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
+    }
+
+    @Test
     public void recordScreenPolicy_dimBrightDimOff_resetsCounter_doesNotExtend() {
         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_UNDIMS_REQUIRED,
                 Integer.toString(2), false /*makeDefault*/);
         mScreenUndimDetector.readValuesFromDeviceConfig();
 
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
         assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
@@ -189,10 +223,27 @@
                 Integer.toString(2), false /*makeDefault*/);
         mScreenUndimDetector.readValuesFromDeviceConfig();
 
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+    }
+
+    @Test
+    public void recordScreenPolicy_undimToOff_otherDisplayDoesNotResetCounter() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
         assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
@@ -206,15 +257,15 @@
         mScreenUndimDetector.readValuesFromDeviceConfig();
 
         // undim
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
         // off
-        mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF);
         // second undim
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
         assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1);
@@ -227,12 +278,12 @@
                 Integer.toString(2), false /*makeDefault*/);
         mScreenUndimDetector.readValuesFromDeviceConfig();
 
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         mClock.advanceTime(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS + 5);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
         assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
         assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1);
@@ -246,8 +297,8 @@
             mScreenUndimDetector.mUndimCounterStartedMillis = 123;
             mScreenUndimDetector.mWakeLock.acquire();
 
-            mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
-            mScreenUndimDetector.recordScreenPolicy(to);
+            mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+            mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to);
 
             assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
             assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0);
@@ -264,8 +315,8 @@
             mScreenUndimDetector.mUndimCounterStartedMillis = 123;
             mScreenUndimDetector.mWakeLock.acquire();
 
-            mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
-            mScreenUndimDetector.recordScreenPolicy(to);
+            mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+            mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to);
 
             assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
             assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0);
@@ -294,8 +345,8 @@
                 mScreenUndimDetector.mUndimCounterStartedMillis =
                         SystemClock.currentThreadTimeMillis();
 
-                mScreenUndimDetector.recordScreenPolicy(from);
-                mScreenUndimDetector.recordScreenPolicy(to);
+                mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, from);
+                mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to);
 
                 assertThat(mScreenUndimDetector.mUndimCounter).isNotEqualTo(0);
                 assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isNotEqualTo(0);
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 9666758..0b488b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -153,6 +153,9 @@
         sContext.getTestablePermissions().setPermission(
                 android.Manifest.permission.SET_WALLPAPER,
                 PackageManager.PERMISSION_GRANTED);
+        sContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT,
+                PackageManager.PERMISSION_GRANTED);
         doNothing().when(sContext).sendBroadcastAsUser(any(), any());
 
         //Wallpaper components
@@ -433,6 +436,15 @@
         assertTrue(timestamps[1] > timestamps[0]);
     }
 
+    @Test
+    public void testSetWallpaperDimAmount() throws RemoteException {
+        mService.switchUser(USER_SYSTEM, null);
+        float dimAmount = 0.7f;
+        mService.setWallpaperDimAmount(dimAmount);
+        assertEquals("Getting dim amount should match after setting the dim amount",
+                mService.getWallpaperDimAmount(), dimAmount, 0.0);
+    }
+
     // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
     // non-current user must not bind to wallpaper service.
     private void verifyNoConnectionBeforeLastUser(int lastUserId) {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index c3a364e..f24059c 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -25,6 +25,7 @@
         "test-apps/JobTestApp/src/**/*.java",
 
         "test-apps/SuspendTestApp/src/**/*.java",
+        ":service-bluetooth-tests-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready
     ],
     static_libs: [
         "frameworks-base-testutils",
@@ -181,18 +182,41 @@
 java_genrule {
     name: "FrameworksServicesTests_apks_as_resources",
     srcs: [
-        ":FrameworksCoreTests_install_complete_package_info",
+        ":FrameworksServicesTests_install",
+        ":FrameworksServicesTests_install_bad_dex",
+        ":FrameworksServicesTests_install_complete_package_info",
+        ":FrameworksServicesTests_install_decl_perm",
         ":FrameworksServicesTests_install_intent_filters",
+        ":FrameworksServicesTests_install_loc_auto",
+        ":FrameworksServicesTests_install_loc_internal",
+        ":FrameworksServicesTests_install_loc_sdcard",
+        ":FrameworksServicesTests_install_loc_unspecified",
         ":FrameworksServicesTests_install_split_base",
         ":FrameworksServicesTests_install_split_feature_a",
+        ":FrameworksServicesTests_install_use_perm_good",
+        ":FrameworksServicesTests_install_uses_feature",
         ":FrameworksServicesTests_install_uses_sdk_0",
         ":FrameworksServicesTests_install_uses_sdk_q0",
         ":FrameworksServicesTests_install_uses_sdk_q0_r0",
-        ":FrameworksServicesTests_install_uses_sdk_r_none",
         ":FrameworksServicesTests_install_uses_sdk_r0",
         ":FrameworksServicesTests_install_uses_sdk_r5",
+        ":FrameworksServicesTests_install_uses_sdk_r_none",
         ":FrameworksServicesTests_install_uses_sdk_r0_s0",
         ":FrameworksServicesTests_install_uses_sdk_r0_s5",
+        ":FrameworksServicesTests_keyset_permdef_sa_unone",
+        ":FrameworksServicesTests_keyset_permuse_sa_ua_ub",
+        ":FrameworksServicesTests_keyset_permuse_sb_ua_ub",
+        ":FrameworksServicesTests_keyset_sa_ua",
+        ":FrameworksServicesTests_keyset_sa_ua_ub",
+        ":FrameworksServicesTests_keyset_sa_uab",
+        ":FrameworksServicesTests_keyset_sa_ub",
+        ":FrameworksServicesTests_keyset_sa_unone",
+        ":FrameworksServicesTests_keyset_sab_ua",
+        ":FrameworksServicesTests_keyset_sau_ub",
+        ":FrameworksServicesTests_keyset_sb_ua",
+        ":FrameworksServicesTests_keyset_sb_ub",
+        ":FrameworksServicesTests_keyset_splat_api",
+        ":FrameworksServicesTests_keyset_splata_api",
     ],
     out: ["FrameworkServicesTests_apks_as_resources.res.zip"],
     tools: ["soong_zip"],
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index e756124..d9f73d9 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
     <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
     <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <uses-permission android:name="android.permission.STATUS_BAR"/>
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
@@ -94,9 +95,12 @@
     <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
     <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
     <uses-permission android:name="android.permission.KILL_UID"/>
+    <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
     <uses-permission
         android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
+    <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
+
     <queries>
         <package android:name="com.android.servicestests.apps.suspendtestapp" />
     </queries>
@@ -267,4 +271,11 @@
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
          android:targetPackage="com.android.frameworks.servicestests"
          android:label="Frameworks Services Tests"/>
+    <key-sets>
+        <key-set android:name="A" >
+            <public-key android:name="keyA"
+                android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMpNthdOxud7roPDZMMomOqXgJJdRfIWpkKEqmC61Mv+Nf6QY3TorEwJeghjSmqj7IbBKrtvfQq4E2XJO1HuspmQO4Ng2gvn+r+6EwNfKc9k55d6s+27SR867jKurBbHNtZMG+tjL1yH4r+tNzcuJCsgyAFqLmxFdcxEwzNvREyRpoYc5RDR0mmTwkMCUhJ6CId1EYEKiCEdNzxv+fWPEb21u+/MWpleGCILs8kglRVb2q/WOzAAvGr4FY5plfaE6N+lr7+UschQ+aMi1+uqewo2o0qPFVmZP5hnwj55K4UMzu/NhhDqQQsX4cSGES1KgHo5MTqRqZjN/I7emw5pFQIDAQAB"/>
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+    </key-sets>
 </manifest>
diff --git a/services/tests/servicestests/OWNERS b/services/tests/servicestests/OWNERS
index 0fb0c30..d07848e 100644
--- a/services/tests/servicestests/OWNERS
+++ b/services/tests/servicestests/OWNERS
@@ -1 +1,2 @@
 include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
+per-file GameManagerServiceSettingsTests.java = file:/GAME_MANAGER_OWNERS
diff --git a/services/tests/servicestests/apks/install/Android.bp b/services/tests/servicestests/apks/install/Android.bp
new file mode 100644
index 0000000..12175fd
--- /dev/null
+++ b/services/tests/servicestests/apks/install/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install/AndroidManifest.xml b/services/tests/servicestests/apks/install/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install/AndroidManifest.xml
rename to services/tests/servicestests/apks/install/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install/res/values/strings.xml b/services/tests/servicestests/apks/install/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install/res/values/strings.xml
rename to services/tests/servicestests/apks/install/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_bad_dex/Android.bp b/services/tests/servicestests/apks/install_bad_dex/Android.bp
new file mode 100644
index 0000000..ad75668
--- /dev/null
+++ b/services/tests/servicestests/apks/install_bad_dex/Android.bp
@@ -0,0 +1,32 @@
+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: "FrameworksServicesTests_install_bad_dex_",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["src/**/*.java"],
+}
+
+// Inject bad classes.dex file.
+java_genrule {
+    name: "FrameworksServicesTests_install_bad_dex",
+    tools: [
+        "soong_zip",
+        "merge_zips",
+    ],
+    srcs: [
+        ":FrameworksServicesTests_install_bad_dex_",
+        "classes.dex",
+    ],
+    out: ["FrameworksServicesTests_install_bad_dex.apk"],
+    cmd: "$(location soong_zip) -o $(genDir)/classes.dex.zip -j -f $(location classes.dex) && " +
+        "$(location merge_zips) -ignore-duplicates $(out) $(genDir)/classes.dex.zip " +
+        "$(location :FrameworksServicesTests_install_bad_dex_)",
+}
diff --git a/core/tests/coretests/apks/install_bad_dex/AndroidManifest.xml b/services/tests/servicestests/apks/install_bad_dex/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_bad_dex/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_bad_dex/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_bad_dex/classes.dex b/services/tests/servicestests/apks/install_bad_dex/classes.dex
similarity index 100%
rename from core/tests/coretests/apks/install_bad_dex/classes.dex
rename to services/tests/servicestests/apks/install_bad_dex/classes.dex
diff --git a/core/tests/coretests/apks/install_bad_dex/res/values/strings.xml b/services/tests/servicestests/apks/install_bad_dex/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_bad_dex/res/values/strings.xml
rename to services/tests/servicestests/apks/install_bad_dex/res/values/strings.xml
diff --git a/core/tests/coretests/apks/install_bad_dex/src/com/android/frameworks/coretests/TestActivity.java b/services/tests/servicestests/apks/install_bad_dex/src/com/android/frameworks/coretests/TestActivity.java
similarity index 100%
rename from core/tests/coretests/apks/install_bad_dex/src/com/android/frameworks/coretests/TestActivity.java
rename to services/tests/servicestests/apks/install_bad_dex/src/com/android/frameworks/coretests/TestActivity.java
diff --git a/services/tests/servicestests/apks/install_complete_package_info/Android.bp b/services/tests/servicestests/apks/install_complete_package_info/Android.bp
new file mode 100644
index 0000000..98aa750
--- /dev/null
+++ b/services/tests/servicestests/apks/install_complete_package_info/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_complete_package_info",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml b/services/tests/servicestests/apks/install_complete_package_info/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_complete_package_info/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java b/services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java
similarity index 100%
rename from core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java
rename to services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestActivity.java
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java b/services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java
similarity index 100%
rename from core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java
rename to services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestProvider.java
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java b/services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java
similarity index 100%
rename from core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java
rename to services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestReceiver.java
diff --git a/core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java b/services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java
similarity index 100%
rename from core/tests/coretests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java
rename to services/tests/servicestests/apks/install_complete_package_info/src/com/android/frameworks/coretests/TestService.java
diff --git a/services/tests/servicestests/apks/install_decl_perm/Android.bp b/services/tests/servicestests/apks/install_decl_perm/Android.bp
new file mode 100644
index 0000000..ef65f5d
--- /dev/null
+++ b/services/tests/servicestests/apks/install_decl_perm/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_decl_perm",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml b/services/tests/servicestests/apks/install_decl_perm/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_decl_perm/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_decl_perm/res/values/strings.xml b/services/tests/servicestests/apks/install_decl_perm/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_decl_perm/res/values/strings.xml
rename to services/tests/servicestests/apks/install_decl_perm/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_loc_auto/Android.bp b/services/tests/servicestests/apks/install_loc_auto/Android.bp
new file mode 100644
index 0000000..4e4ae52
--- /dev/null
+++ b/services/tests/servicestests/apks/install_loc_auto/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_loc_auto",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_auto/AndroidManifest.xml b/services/tests/servicestests/apks/install_loc_auto/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_auto/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_loc_auto/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_loc_auto/res/values/strings.xml b/services/tests/servicestests/apks/install_loc_auto/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_auto/res/values/strings.xml
rename to services/tests/servicestests/apks/install_loc_auto/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_loc_internal/Android.bp b/services/tests/servicestests/apks/install_loc_internal/Android.bp
new file mode 100644
index 0000000..39cdd51
--- /dev/null
+++ b/services/tests/servicestests/apks/install_loc_internal/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_loc_internal",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_internal/AndroidManifest.xml b/services/tests/servicestests/apks/install_loc_internal/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_internal/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_loc_internal/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_loc_internal/res/values/strings.xml b/services/tests/servicestests/apks/install_loc_internal/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_internal/res/values/strings.xml
rename to services/tests/servicestests/apks/install_loc_internal/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_loc_sdcard/Android.bp b/services/tests/servicestests/apks/install_loc_sdcard/Android.bp
new file mode 100644
index 0000000..ed82793
--- /dev/null
+++ b/services/tests/servicestests/apks/install_loc_sdcard/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_loc_sdcard",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_sdcard/AndroidManifest.xml b/services/tests/servicestests/apks/install_loc_sdcard/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_sdcard/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_loc_sdcard/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_loc_sdcard/res/values/strings.xml b/services/tests/servicestests/apks/install_loc_sdcard/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_sdcard/res/values/strings.xml
rename to services/tests/servicestests/apks/install_loc_sdcard/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_loc_unspecified/Android.bp b/services/tests/servicestests/apks/install_loc_unspecified/Android.bp
new file mode 100644
index 0000000..fd15cb8
--- /dev/null
+++ b/services/tests/servicestests/apks/install_loc_unspecified/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_loc_unspecified",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_unspecified/AndroidManifest.xml b/services/tests/servicestests/apks/install_loc_unspecified/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_unspecified/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_loc_unspecified/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_loc_unspecified/res/values/strings.xml b/services/tests/servicestests/apks/install_loc_unspecified/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_loc_unspecified/res/values/strings.xml
rename to services/tests/servicestests/apks/install_loc_unspecified/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_use_perm_good/Android.bp b/services/tests/servicestests/apks/install_use_perm_good/Android.bp
new file mode 100644
index 0000000..959ffbc
--- /dev/null
+++ b/services/tests/servicestests/apks/install_use_perm_good/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_use_perm_good",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_use_perm_good/AndroidManifest.xml b/services/tests/servicestests/apks/install_use_perm_good/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_use_perm_good/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_use_perm_good/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_use_perm_good/res/values/strings.xml b/services/tests/servicestests/apks/install_use_perm_good/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_use_perm_good/res/values/strings.xml
rename to services/tests/servicestests/apks/install_use_perm_good/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/install_uses_feature/Android.bp b/services/tests/servicestests/apks/install_uses_feature/Android.bp
new file mode 100644
index 0000000..fa25af4
--- /dev/null
+++ b/services/tests/servicestests/apks/install_uses_feature/Android.bp
@@ -0,0 +1,15 @@
+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: "FrameworksServicesTests_install_uses_feature",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_uses_feature/AndroidManifest.xml b/services/tests/servicestests/apks/install_uses_feature/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/install_uses_feature/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_uses_feature/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/install_uses_feature/res/values/strings.xml b/services/tests/servicestests/apks/install_uses_feature/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_uses_feature/res/values/strings.xml
rename to services/tests/servicestests/apks/install_uses_feature/res/values/strings.xml
diff --git a/services/tests/servicestests/apks/keyset/Android.bp b/services/tests/servicestests/apks/keyset/Android.bp
new file mode 100644
index 0000000..ce7919c
--- /dev/null
+++ b/services/tests/servicestests/apks/keyset/Android.bp
@@ -0,0 +1,129 @@
+//apks signed by keyset_A
+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: "FrameworksServicesTests_keyset_sa_unone",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "uNone/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sa_ua",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "uA/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sa_ub",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "uB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sa_uab",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "uAB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sa_ua_ub",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "uAuB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_permdef_sa_unone",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "permDef/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_permuse_sa_ua_ub",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    manifest: "permUse/AndroidManifest.xml",
+}
+
+//apks signed by keyset_B
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sb_ua",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_B_cert",
+    manifest: "uA/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sb_ub",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_B_cert",
+    manifest: "uB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_permuse_sb_ua_ub",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_B_cert",
+    manifest: "permUse/AndroidManifest.xml",
+}
+
+//apks signed by keyset_A and keyset_B
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sab_ua",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    additional_certificates: [":FrameworksServicesTests_keyset_B_cert"],
+    manifest: "uA/AndroidManifest.xml",
+}
+
+//apks signed by keyset_A and unit_test
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_sau_ub",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksServicesTests_keyset_A_cert",
+    additional_certificates: [":FrameworksServicesTests_keyset_B_cert"],
+    manifest: "uB/AndroidManifest.xml",
+}
+
+//apks signed by platform only
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_splat_api",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: "platform",
+    manifest: "api_test/AndroidManifest.xml",
+}
+
+//apks signed by platform and keyset_A
+android_test_helper_app {
+    name: "FrameworksServicesTests_keyset_splata_api",
+    defaults: ["FrameworksServicesTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: "platform",
+    additional_certificates: [":FrameworksServicesTests_keyset_A_cert"],
+    manifest: "api_test/AndroidManifest.xml",
+}
diff --git a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/api_test/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/api_test/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/permDef/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/permDef/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/permDef/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/permDef/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/permUse/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/permUse/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/permUse/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/permUse/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/res/values/strings.xml b/services/tests/servicestests/apks/keyset/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/res/values/strings.xml
rename to services/tests/servicestests/apks/keyset/res/values/strings.xml
diff --git a/core/tests/coretests/apks/keyset/uA/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/uA/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/uA/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/uA/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/uAB/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/uAB/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/uAB/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/uAB/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/uAuB/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/uAuB/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/uAuB/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/uAuB/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/uB/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/uB/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/uB/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/uB/AndroidManifest.xml
diff --git a/core/tests/coretests/apks/keyset/uNone/AndroidManifest.xml b/services/tests/servicestests/apks/keyset/uNone/AndroidManifest.xml
similarity index 100%
rename from core/tests/coretests/apks/keyset/uNone/AndroidManifest.xml
rename to services/tests/servicestests/apks/keyset/uNone/AndroidManifest.xml
diff --git a/services/tests/servicestests/certs/Android.bp b/services/tests/servicestests/certs/Android.bp
new file mode 100644
index 0000000..61367c0
--- /dev/null
+++ b/services/tests/servicestests/certs/Android.bp
@@ -0,0 +1,20 @@
+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
+    //   SPDX-license-identifier-MIT
+    //   SPDX-license-identifier-Unicode-DFS
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app_certificate {
+    name: "FrameworksServicesTests_keyset_A_cert",
+    certificate: "keyset_A",
+}
+
+android_app_certificate {
+    name: "FrameworksServicesTests_keyset_B_cert",
+    certificate: "keyset_B",
+}
diff --git a/services/tests/servicestests/certs/README b/services/tests/servicestests/certs/README
new file mode 100644
index 0000000..00917a1
--- /dev/null
+++ b/services/tests/servicestests/certs/README
@@ -0,0 +1,4 @@
+Generate with:
+
+development/tools/make_key unit_test         '/CN=unit_test'
+development/tools/make_key unit_test_diff    '/CN=unit_test_diff'
diff --git a/core/tests/coretests/certs/keyset_A.pk8 b/services/tests/servicestests/certs/keyset_A.pk8
similarity index 100%
rename from core/tests/coretests/certs/keyset_A.pk8
rename to services/tests/servicestests/certs/keyset_A.pk8
Binary files differ
diff --git a/core/tests/coretests/certs/keyset_A.x509.pem b/services/tests/servicestests/certs/keyset_A.x509.pem
similarity index 100%
rename from core/tests/coretests/certs/keyset_A.x509.pem
rename to services/tests/servicestests/certs/keyset_A.x509.pem
diff --git a/core/tests/coretests/certs/keyset_B.pk8 b/services/tests/servicestests/certs/keyset_B.pk8
similarity index 100%
rename from core/tests/coretests/certs/keyset_B.pk8
rename to services/tests/servicestests/certs/keyset_B.pk8
Binary files differ
diff --git a/core/tests/coretests/certs/keyset_B.x509.pem b/services/tests/servicestests/certs/keyset_B.x509.pem
similarity index 100%
rename from core/tests/coretests/certs/keyset_B.x509.pem
rename to services/tests/servicestests/certs/keyset_B.x509.pem
diff --git a/core/tests/coretests/res/raw/install_app1_cert1 b/services/tests/servicestests/res/raw/install_app1_cert1
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert1
rename to services/tests/servicestests/res/raw/install_app1_cert1
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert1_cert2 b/services/tests/servicestests/res/raw/install_app1_cert1_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert1_cert2
rename to services/tests/servicestests/res/raw/install_app1_cert1_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert2 b/services/tests/servicestests/res/raw/install_app1_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert2
rename to services/tests/servicestests/res/raw/install_app1_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert3 b/services/tests/servicestests/res/raw/install_app1_cert3
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert3
rename to services/tests/servicestests/res/raw/install_app1_cert3
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert3_cert4 b/services/tests/servicestests/res/raw/install_app1_cert3_cert4
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert3_cert4
rename to services/tests/servicestests/res/raw/install_app1_cert3_cert4
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert5 b/services/tests/servicestests/res/raw/install_app1_cert5
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert5
rename to services/tests/servicestests/res/raw/install_app1_cert5
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6 b/services/tests/servicestests/res/raw/install_app1_cert5_rotated_cert6
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6
rename to services/tests/servicestests/res/raw/install_app1_cert5_rotated_cert6
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_cert6 b/services/tests/servicestests/res/raw/install_app1_cert6
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_cert6
rename to services/tests/servicestests/res/raw/install_app1_cert6
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app1_unsigned b/services/tests/servicestests/res/raw/install_app1_unsigned
similarity index 100%
rename from core/tests/coretests/res/raw/install_app1_unsigned
rename to services/tests/servicestests/res/raw/install_app1_unsigned
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_cert1 b/services/tests/servicestests/res/raw/install_app2_cert1
similarity index 100%
rename from core/tests/coretests/res/raw/install_app2_cert1
rename to services/tests/servicestests/res/raw/install_app2_cert1
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_cert1_cert2 b/services/tests/servicestests/res/raw/install_app2_cert1_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_app2_cert1_cert2
rename to services/tests/servicestests/res/raw/install_app2_cert1_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_cert2 b/services/tests/servicestests/res/raw/install_app2_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_app2_cert2
rename to services/tests/servicestests/res/raw/install_app2_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_cert3 b/services/tests/servicestests/res/raw/install_app2_cert3
similarity index 100%
rename from core/tests/coretests/res/raw/install_app2_cert3
rename to services/tests/servicestests/res/raw/install_app2_cert3
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6 b/services/tests/servicestests/res/raw/install_app2_cert5_rotated_cert6
similarity index 100%
rename from core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6
rename to services/tests/servicestests/res/raw/install_app2_cert5_rotated_cert6
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_app2_unsigned b/services/tests/servicestests/res/raw/install_app2_unsigned
similarity index 100%
rename from core/tests/coretests/res/raw/install_app2_unsigned
rename to services/tests/servicestests/res/raw/install_app2_unsigned
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared1_cert1 b/services/tests/servicestests/res/raw/install_shared1_cert1
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared1_cert1
rename to services/tests/servicestests/res/raw/install_shared1_cert1
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared1_cert1_cert2 b/services/tests/servicestests/res/raw/install_shared1_cert1_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared1_cert1_cert2
rename to services/tests/servicestests/res/raw/install_shared1_cert1_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared1_cert2 b/services/tests/servicestests/res/raw/install_shared1_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared1_cert2
rename to services/tests/servicestests/res/raw/install_shared1_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared1_unsigned b/services/tests/servicestests/res/raw/install_shared1_unsigned
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared1_unsigned
rename to services/tests/servicestests/res/raw/install_shared1_unsigned
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared2_cert1 b/services/tests/servicestests/res/raw/install_shared2_cert1
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared2_cert1
rename to services/tests/servicestests/res/raw/install_shared2_cert1
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared2_cert1_cert2 b/services/tests/servicestests/res/raw/install_shared2_cert1_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared2_cert1_cert2
rename to services/tests/servicestests/res/raw/install_shared2_cert1_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared2_cert2 b/services/tests/servicestests/res/raw/install_shared2_cert2
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared2_cert2
rename to services/tests/servicestests/res/raw/install_shared2_cert2
Binary files differ
diff --git a/core/tests/coretests/res/raw/install_shared2_unsigned b/services/tests/servicestests/res/raw/install_shared2_unsigned
similarity index 100%
rename from core/tests/coretests/res/raw/install_shared2_unsigned
rename to services/tests/servicestests/res/raw/install_shared2_unsigned
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
deleted file mode 100644
index a1d4c20..0000000
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ /dev/null
@@ -1,123 +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.server;
-
-import static org.mockito.Mockito.*;
-
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.os.Looper;
-import android.provider.Settings;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothAirplaneModeListenerTest {
-    private Context mContext;
-    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
-    private BluetoothAdapter mBluetoothAdapter;
-    private BluetoothModeChangeHelper mHelper;
-
-    @Mock BluetoothManagerService mBluetoothManagerService;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-
-        mHelper = mock(BluetoothModeChangeHelper.class);
-        when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
-                .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
-        doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
-        doNothing().when(mHelper).showToastMessage();
-        doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class));
-
-        mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
-                    mBluetoothManagerService, Looper.getMainLooper(), mContext);
-        mBluetoothAirplaneModeListener.start(mHelper);
-    }
-
-    @Test
-    public void testIgnoreOnAirplanModeChange() {
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
-        when(mHelper.isBluetoothOn()).thenReturn(true);
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
-        when(mHelper.isMediaProfileConnected()).thenReturn(true);
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
-        when(mHelper.isAirplaneModeOn()).thenReturn(true);
-        Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-    }
-
-    @Test
-    public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() {
-        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-        verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService);
-    }
-
-    @Test
-    public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
-        when(mHelper.isBluetoothOn()).thenReturn(true);
-        when(mHelper.isMediaProfileConnected()).thenReturn(true);
-        when(mHelper.isAirplaneModeOn()).thenReturn(true);
-        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-
-        verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
-                BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
-        verify(mHelper, times(0)).showToastMessage();
-        verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
-    }
-
-    @Test
-    public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = 0;
-        when(mHelper.isBluetoothOn()).thenReturn(true);
-        when(mHelper.isMediaProfileConnected()).thenReturn(true);
-        when(mHelper.isAirplaneModeOn()).thenReturn(true);
-        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-
-        verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
-                BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
-        verify(mHelper).showToastMessage();
-        verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
-    }
-
-    @Test
-    public void testIsPopToast_PopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = 0;
-        Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast());
-        verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1);
-    }
-
-    @Test
-    public void testIsPopToast_NotPopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast());
-        verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java b/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java
new file mode 100644
index 0000000..f92f5ea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.junit.Assert.assertThrows;
+
+import android.test.AndroidTestCase;
+
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Tests for {@link SystemServiceManager}.
+ */
+public class SystemServiceManagerTest extends AndroidTestCase {
+
+    private static final String TAG = "SystemServiceManagerTest";
+
+    private final SystemServiceManager mSystemServiceManager =
+            new SystemServiceManager(getContext());
+
+    @Test
+    public void testSealStartedServices() throws Exception {
+        // must be effectively final, since it's changed from inner class below
+        AtomicBoolean serviceStarted = new AtomicBoolean(false);
+        SystemService service1 = new SystemService(getContext()) {
+            @Override
+            public void onStart() {
+                serviceStarted.set(true);
+            }
+        };
+        SystemService service2 = new SystemService(getContext()) {
+            @Override
+            public void onStart() {
+                throw new IllegalStateException("Second service must not be called");
+            }
+        };
+
+        // started services have their #onStart methods called
+        mSystemServiceManager.startService(service1);
+        assertTrue(serviceStarted.get());
+
+        // however, after locking started services, it is not possible to start a new service
+        mSystemServiceManager.sealStartedServices();
+        assertThrows(UnsupportedOperationException.class,
+                () -> mSystemServiceManager.startService(service2));
+    }
+
+    @Test
+    public void testDuplicateServices() throws Exception {
+        AtomicInteger counter = new AtomicInteger(0);
+        SystemService service = new SystemService(getContext()) {
+            @Override
+            public void onStart() {
+                counter.incrementAndGet();
+            }
+        };
+
+        mSystemServiceManager.startService(service);
+        assertEquals(1, counter.get());
+
+        // manager does not start the same service twice
+        mSystemServiceManager.startService(service);
+        assertEquals(1, counter.get());
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d18030f..97ebdd4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -595,6 +595,20 @@
     }
 
     @Test
+    public void getCurrentMagnificationRegion_returnRegion() {
+        final int displayId = 1;
+        final Region region = new Region(10, 20, 100, 200);
+        doAnswer((invocation) -> {
+            ((Region) invocation.getArguments()[1]).set(region);
+            return null;
+        }).when(mMockMagnificationProcessor).getCurrentMagnificationRegion(eq(displayId), any(),
+                anyBoolean());
+
+        final Region result = mServiceConnection.getCurrentMagnificationRegion(displayId);
+        assertEquals(result, region);
+    }
+
+    @Test
     public void getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero() {
         final int displayId = 1;
         final float centerX = 480.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 3ade9ff..953b536 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -343,6 +343,20 @@
                 eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), ArgumentMatchers.isNotNull());
     }
 
+    @Test
+    public void testFollowTypingEnabled_defaultEnabledAndThenDisable_propagateToController() {
+        final AccessibilityUserState userState = mA11yms.mUserStates.get(
+                mA11yms.getCurrentUserIdLocked());
+        Settings.Secure.putIntForUser(
+                mTestableContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+                0, mA11yms.getCurrentUserIdLocked());
+
+        mA11yms.readMagnificationFollowTypingLocked(userState);
+
+        verify(mMockMagnificationController).setMagnificationFollowTypingEnabled(false);
+    }
+
     @SmallTest
     @Test
     public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b9d94ed..27637c2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -172,6 +172,7 @@
                 mUserState.getMagnificationModeLocked(TEST_DISPLAY));
         assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
         assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
+        assertTrue(mUserState.isMagnificationFollowTypingEnabled());
     }
 
     @Test
@@ -374,6 +375,15 @@
     }
 
     @Test
+    public void setMagnificationFollowTypingEnabled_defaultTrueAndThenDisable_returnFalse() {
+        assertTrue(mUserState.isMagnificationFollowTypingEnabled());
+
+        mUserState.setMagnificationFollowTypingEnabled(false);
+
+        assertFalse(mUserState.isMagnificationFollowTypingEnabled());
+    }
+
+    @Test
     public void setFocusAppearanceData_returnExpectedFocusAppearanceData() {
         final int focusStrokeWidthValue = 100;
         final int focusColorValue = Color.BLUE;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 99d6c2a..c4040b4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -184,6 +184,38 @@
     }
 
     @Test
+    public void getCurrentMagnificationRegion_windowModeActivated_returnRegion() {
+        final Region region = new Region(10, 20, 100, 200);
+        setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
+        doAnswer((invocation) -> {
+            ((Region) invocation.getArguments()[1]).set(region);
+            return null;
+        }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+                any());
+
+        final Region result = new Region();
+        mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY,
+                result, /* canControlMagnification= */true);
+        assertEquals(region, result);
+    }
+
+    @Test
+    public void getCurrentMagnificationRegion_fullscreenModeActivated_returnRegion() {
+        final Region region = new Region(10, 20, 100, 200);
+        setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
+        doAnswer((invocation) -> {
+            ((Region) invocation.getArguments()[1]).set(region);
+            return null;
+        }).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
+                any());
+
+        final Region result = new Region();
+        mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY,
+                result, /* canControlMagnification= */true);
+        assertEquals(region, result);
+    }
+
+    @Test
     public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
         final MagnificationConfig config = new MagnificationConfig.Builder()
                 .setMode(MAGNIFICATION_MODE_FULLSCREEN)
@@ -222,7 +254,7 @@
     }
 
     @Test
-    public void reset_fullscreenMagnificationActivated() {
+    public void resetFullscreenMagnification_fullscreenMagnificationActivated() {
         setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
 
         mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false);
@@ -231,7 +263,7 @@
     }
 
     @Test
-    public void reset_windowMagnificationActivated() {
+    public void resetCurrentMagnification_windowMagnificationActivated() {
         setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
 
         mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 96af617..a9b7cfb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+
 import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
 
 import static org.junit.Assert.assertEquals;
@@ -23,10 +25,8 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
+import android.accessibilityservice.MagnificationConfig;
 import android.animation.ValueAnimator;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -105,6 +106,8 @@
     private final MagnificationScaleProvider mScaleProvider = mock(
             MagnificationScaleProvider.class);
 
+    private final ArgumentCaptor<MagnificationConfig> mConfigCaptor = ArgumentCaptor.forClass(
+            MagnificationConfig.class);
 
     ValueAnimator mMockValueAnimator;
     ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
@@ -142,13 +145,13 @@
         register(DISPLAY_1);
         register(INVALID_DISPLAY);
         verify(mMockContext).registerReceiver(
-                (BroadcastReceiver) anyObject(), (IntentFilter) anyObject());
+                any(BroadcastReceiver.class), any(IntentFilter.class));
         verify(mMockWindowManager).setMagnificationCallbacks(
-                eq(DISPLAY_0), (MagnificationCallbacks) anyObject());
+                eq(DISPLAY_0), any(MagnificationCallbacks.class));
         verify(mMockWindowManager).setMagnificationCallbacks(
-                eq(DISPLAY_1), (MagnificationCallbacks) anyObject());
+                eq(DISPLAY_1), any(MagnificationCallbacks.class));
         verify(mMockWindowManager).setMagnificationCallbacks(
-                eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject());
+                eq(INVALID_DISPLAY), any(MagnificationCallbacks.class));
         assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
         assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
         assertFalse(mFullScreenMagnificationController.isRegistered(INVALID_DISPLAY));
@@ -159,9 +162,9 @@
         register(DISPLAY_0);
         register(DISPLAY_1);
         mFullScreenMagnificationController.unregister(DISPLAY_0);
-        verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject());
+        verify(mMockContext, times(0)).unregisterReceiver(any(BroadcastReceiver.class));
         mFullScreenMagnificationController.unregister(DISPLAY_1);
-        verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject());
+        verify(mMockContext).unregisterReceiver(any(BroadcastReceiver.class));
         verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null));
         verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null));
         assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
@@ -343,6 +346,7 @@
         MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         float scale = 2.5f;
         PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        final MagnificationConfig config = buildConfig(scale, newCenter.x, newCenter.y);
         PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
         MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
 
@@ -353,8 +357,9 @@
         assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
         assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
         assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
-        verify(mMockAms).notifyMagnificationChanged(displayId,
-                INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
+        verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
+                mConfigCaptor.capture());
+        assertConfigEquals(config, mConfigCaptor.getValue());
         verify(mMockValueAnimator).start();
         verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1);
 
@@ -494,8 +499,11 @@
         MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         callbacks.onMagnificationRegionChanged(OTHER_REGION);
         mMessageCapturingHandler.sendAllMessages();
-        verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f,
-                OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY());
+        MagnificationConfig config = buildConfig(1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(),
+                OTHER_MAGNIFICATION_BOUNDS.centerY());
+        verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(OTHER_REGION),
+                mConfigCaptor.capture());
+        assertConfigEquals(config, mConfigCaptor.getValue());
     }
 
     @Test
@@ -650,7 +658,7 @@
         reset(mMockAms);
         assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
         verify(mMockAms).notifyMagnificationChanged(eq(displayId),
-                eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
+                eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class));
         assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
         assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
     }
@@ -668,8 +676,8 @@
         assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
         mMessageCapturingHandler.sendAllMessages();
 
-        verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId),
-                any(Region.class), anyFloat(), anyFloat(), anyFloat());
+        verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class),
+                any(MagnificationConfig.class));
         verify(mAnimationCallback).onResult(true);
     }
 
@@ -726,7 +734,7 @@
         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mMockContext).registerReceiver(
-                broadcastReceiverCaptor.capture(), (IntentFilter) anyObject());
+                broadcastReceiverCaptor.capture(), any(IntentFilter.class));
         BroadcastReceiver br = broadcastReceiverCaptor.getValue();
         zoomIn2xToMiddle(DISPLAY_0);
         zoomIn2xToMiddle(DISPLAY_1);
@@ -913,6 +921,22 @@
     }
 
     @Test
+    public void requestRectOnScreen_disabledByPrefSetting_doesNothing() {
+        register(DISPLAY_0);
+        zoomIn2xToMiddle(DISPLAY_0);
+        Mockito.reset(mMockWindowManager);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
+        MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
+        mFullScreenMagnificationController.setMagnificationFollowTypingEnabled(false);
+
+        mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1);
+
+        assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
+        verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0),
+                argThat(closeTo(expectedEndSpec)));
+    }
+
+    @Test
     public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
         for (int i = 0; i < DISPLAY_COUNT; i++) {
             requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i);
@@ -1031,6 +1055,7 @@
         MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         float scale = 2.5f;
         PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        final MagnificationConfig config = buildConfig(scale, firstCenter.x, firstCenter.y);
         MagnificationSpec firstEndSpec = getMagnificationSpec(
                 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
 
@@ -1047,8 +1072,9 @@
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
         verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
-        verify(mMockAms).notifyMagnificationChanged(displayId,
-                INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y);
+        verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
+                mConfigCaptor.capture());
+        assertConfigEquals(config, mConfigCaptor.getValue());
         Mockito.reset(mMockWindowManager);
 
         // Intermediate point
@@ -1062,6 +1088,7 @@
         Mockito.reset(mMockWindowManager);
 
         PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
+        final MagnificationConfig newConfig = buildConfig(scale, newCenter.x, newCenter.y);
         MagnificationSpec newEndSpec = getMagnificationSpec(
                 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
         assertTrue(mFullScreenMagnificationController.setCenter(displayId,
@@ -1070,8 +1097,9 @@
 
         // Animation should have been restarted
         verify(mMockValueAnimator, times(2)).start();
-        verify(mMockAms).notifyMagnificationChanged(displayId,
-                INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
+        verify(mMockAms, times(2)).notifyMagnificationChanged(eq(displayId),
+                eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
+        assertConfigEquals(newConfig, mConfigCaptor.getValue());
 
         // New starting point should be where we left off
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
@@ -1155,7 +1183,7 @@
             Region regionArg = (Region) args[1];
             regionArg.set(INITIAL_MAGNIFICATION_REGION);
             return null;
-        }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject());
+        }).when(mMockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
     }
 
     private void resetMockWindowManager() {
@@ -1201,6 +1229,19 @@
                 magnifiedBounds.centerY() - scale * center.y);
     }
 
+    private MagnificationConfig buildConfig(float scale, float centerX, float centerY) {
+        return new MagnificationConfig.Builder().setMode(
+                MAGNIFICATION_MODE_FULLSCREEN).setScale(scale).setCenterX(centerX).setCenterY(
+                centerY).build();
+    }
+
+    private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig result) {
+        assertEquals(expected.getMode(), result.getMode());
+        assertEquals(expected.getScale(), result.getScale(), 0f);
+        assertEquals(expected.getCenterX(), result.getCenterX(), 0f);
+        assertEquals(expected.getCenterY(), result.getCenterY(), 0f);
+    }
+
     private MagnificationSpec getInterpolatedMagSpec(MagnificationSpec start, MagnificationSpec end,
             float fraction) {
         MagnificationSpec interpolatedSpec = new MagnificationSpec();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 0054fc3..064b762 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -430,6 +430,21 @@
     }
 
     @Test
+    public void onSourceBoundsChanged_notifyMagnificationChanged() {
+        Rect rect = new Rect(0, 0, 100, 120);
+        Region region = new Region(rect);
+
+        mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, rect);
+
+        final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
+                MagnificationConfig.class);
+        verify(mService).notifyMagnificationChanged(eq(TEST_DISPLAY), eq(region),
+                configCaptor.capture());
+        assertEquals(rect.exactCenterX(), configCaptor.getValue().getCenterX(), 0);
+        assertEquals(rect.exactCenterY(), configCaptor.getValue().getCenterY(), 0);
+    }
+
+    @Test
     public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
@@ -464,6 +479,14 @@
     }
 
     @Test
+    public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
+        mMagnificationController.setMagnificationFollowTypingEnabled(false);
+
+        verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+        verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
+    }
+
+    @Test
     public void onRectangleOnScreenRequested_fullScreenIsActivated_fullScreenDispatchEvent() {
         mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
                 true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a62c0d5..8da513b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -274,6 +274,123 @@
     }
 
     @Test
+    public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnification()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        final Region outRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+
+        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+                eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                eq(0f), eq(0f), notNull());
+    }
+
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnification()
+            throws RemoteException {
+        final float distanceX = 10f;
+        final float distanceY = 10f;
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        final Region outRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+
+        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+                eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnification()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        final Region outRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.inset(-10, -10);
+
+        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+                eq(3f), eq(500f), eq(500f), eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnification()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        final Region outRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnification()
+            throws RemoteException {
+        final PointF initialPoint = new PointF(50f, 50f);
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f,
+                initialPoint.x, initialPoint.y);
+        mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+        mWindowMagnificationManager.onImeWindowVisibilityChanged(true);
+        final Region outRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY),
+                eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        final Region beforeRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+        final Rect requestedRect = beforeRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
+
+        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        final Region afterRegion = new Region();
+        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+        assertEquals(afterRegion, beforeRegion);
+    }
+
+    @Test
     public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index cffff66..02cf971 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -23,7 +23,14 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -105,6 +112,7 @@
     public void tearDown() throws Exception {
         mKeyStore.deleteKeyStore();
         setAllowedConnectionTime(mOriginalAllowedConnectionTime);
+        dropShellPermissionIdentity();
     }
 
     /**
@@ -813,6 +821,108 @@
         return hasAtLeastOneLetter;
     }
 
+    CountDownLatch mAdbActionLatch = new CountDownLatch(1);
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.i(TAG, "Received intent action=" + action);
+            if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
+                assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+                        context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                        PackageManager.PERMISSION_GRANTED);
+                Log.i(TAG, "action=" + action + " paired_device=" + intent.getSerializableExtra(
+                        AdbManager.WIRELESS_DEVICES_EXTRA).toString());
+                mAdbActionLatch.countDown();
+            } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
+                assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+                        context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                        PackageManager.PERMISSION_GRANTED);
+                int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_DISCONNECTED);
+                Log.i(TAG, "action=" + action + " status=" + status);
+                mAdbActionLatch.countDown();
+            } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+                assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+                        context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                        PackageManager.PERMISSION_GRANTED);
+                Integer res = intent.getIntExtra(
+                        AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_FAIL);
+                Log.i(TAG, "action=" + action + " result=" + res);
+
+                if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
+                    String pairingCode = intent.getStringExtra(
+                                AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
+                    Log.i(TAG, "pairingCode=" + pairingCode);
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+                    int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+                    Log.i(TAG, "port=" + port);
+                }
+                mAdbActionLatch.countDown();
+            }
+        }
+    };
+
+    private void adoptShellPermissionIdentity() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+            .adoptShellPermissionIdentity(android.Manifest.permission.MANAGE_DEBUGGING);
+    }
+
+    private void dropShellPermissionIdentity() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+            .dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testBroadcastReceiverWithPermissions() throws Exception {
+        adoptShellPermissionIdentity();
+        final IAdbManager mAdbManager = IAdbManager.Stub.asInterface(
+                ServiceManager.getService(Context.ADB_SERVICE));
+        IntentFilter intentFilter =
+                new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+        assertEquals("Context does not have MANAGE_DEBUGGING permission.",
+                mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                PackageManager.PERMISSION_GRANTED);
+        try {
+            mContext.registerReceiver(mReceiver, intentFilter);
+            mAdbManager.enablePairingByPairingCode();
+            if (!mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+                fail("Receiver did not receive adb intent action within the timeout duration");
+            }
+        } finally {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    @Test
+    public void testBroadcastReceiverWithoutPermissions() throws Exception {
+        adoptShellPermissionIdentity();
+        final IAdbManager mAdbManager = IAdbManager.Stub.asInterface(
+                ServiceManager.getService(Context.ADB_SERVICE));
+        IntentFilter intentFilter =
+                new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+        mAdbManager.enablePairingByPairingCode();
+
+        dropShellPermissionIdentity();
+        assertEquals("Context has MANAGE_DEBUGGING permission.",
+                mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                PackageManager.PERMISSION_DENIED);
+        try {
+            mContext.registerReceiver(mReceiver, intentFilter);
+
+            if (mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+                fail("Broadcast receiver received adb action intent without debug permissions");
+            }
+        } finally {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
     /**
      * Runs an adb test with the provided configuration.
      *
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 36c37c4..677f0f6 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -541,11 +541,14 @@
                     | ActivityManager.UID_OBSERVER_CAPABILITY
         };
         final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
+        doReturn(Process.myUid()).when(sPackageManagerInternal)
+                .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
         for (int i = 0; i < observers.length; ++i) {
             observers[i] = mock(IUidObserver.Stub.class);
             when(observers[i].asBinder()).thenReturn((IBinder) observers[i]);
             mAms.registerUidObserver(observers[i], changesToObserve[i] /* which */,
-                    ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */, null /* caller */);
+                    ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */,
+                    mContext.getOpPackageName());
 
             // When we invoke AMS.registerUidObserver, there are some interactions with observers[i]
             // mock in RemoteCallbackList class. We don't want to test those interactions and
@@ -674,10 +677,12 @@
         mockNoteOperation();
 
         final IUidObserver observer = mock(IUidObserver.Stub.class);
-
         when(observer.asBinder()).thenReturn((IBinder) observer);
+        doReturn(Process.myUid()).when(sPackageManagerInternal)
+                .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
         mAms.registerUidObserver(observer, ActivityManager.UID_OBSERVER_PROCSTATE /* which */,
-                ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */, null /* callingPackage */);
+                ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */,
+                mContext.getOpPackageName());
         // When we invoke AMS.registerUidObserver, there are some interactions with observer
         // mock in RemoteCallbackList class. We don't want to test those interactions and
         // at the same time, we don't want those to interfere with verifyNoMoreInteractions.
@@ -771,7 +776,9 @@
 
         final IUidObserver observer = mock(IUidObserver.Stub.class);
         when(observer.asBinder()).thenReturn((IBinder) observer);
-        mAms.registerUidObserver(observer, 0, 0, null);
+        doReturn(Process.myUid()).when(sPackageManagerInternal)
+                .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
+        mAms.registerUidObserver(observer, 0, 0, mContext.getOpPackageName());
         // Verify that when observers are registered, then validateUids is correctly updated.
         addPendingUidChanges(pendingItemsForUids);
         mAms.mUidObserverController.dispatchUidsChanged();
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 f1a63bc..6818d1f 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,13 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 
@@ -64,6 +71,7 @@
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
 import android.os.Binder;
@@ -617,6 +625,100 @@
         assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true);
     }
 
+    /** Tests handleIncomingUser() for a variety of permissions and situations. */
+    @Test
+    public void testHandleIncomingUser() throws Exception {
+        final UserInfo user1a = new UserInfo(111, "user1a", 0);
+        final UserInfo user1b = new UserInfo(112, "user1b", 0);
+        final UserInfo user2 = new UserInfo(113, "user2", 0);
+        // user1a and user2b are in the same profile group; user2 is in a different one.
+        user1a.profileGroupId = 5;
+        user1b.profileGroupId = 5;
+        user2.profileGroupId = 6;
+
+        final List<UserInfo> users = Arrays.asList(user1a, user1b, user2);
+        when(mInjector.mUserManagerMock.getUsers(false)).thenReturn(users);
+        mUserController.onSystemReady(); // To set the profileGroupIds in UserController.
+
+
+        // Has INTERACT_ACROSS_USERS_FULL.
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkPermissionForPreflight(
+                eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(false);
+
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, true);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, true);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+
+        // Has INTERACT_ACROSS_USERS.
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mInjector.checkPermissionForPreflight(
+                eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(false);
+
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user2.id,  ALLOW_NON_FULL_IN_PROFILE, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+
+        // Has INTERACT_ACROSS_PROFILES.
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkPermissionForPreflight(
+                eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(true);
+
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, false);
+
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+    }
+
+    private void checkHandleIncomingUser(int fromUser, int toUser, int allowMode, boolean pass) {
+        final int pid = 100;
+        final int uid = fromUser * UserHandle.PER_USER_RANGE + 34567 + fromUser;
+        final String name = "whatever";
+        final String pkg = "some.package";
+        final boolean allowAll = false;
+
+        if (pass) {
+            mUserController.handleIncomingUser(pid, uid, toUser, allowAll, allowMode, name, pkg);
+        } else {
+            assertThrows(SecurityException.class, () -> mUserController.handleIncomingUser(
+                    pid, uid, toUser, allowAll, allowMode, name, pkg));
+        }
+    }
+
     private void setUpAndStartUserInBackground(int userId) throws Exception {
         setUpUser(userId, 0);
         mUserController.startUser(userId, /* foreground= */ false);
@@ -784,6 +886,23 @@
         }
 
         @Override
+        int checkComponentPermission(String permission, int pid, int uid, int owner, boolean exp) {
+            Log.i(TAG, "checkComponentPermission " + permission);
+            return PERMISSION_GRANTED;
+        }
+
+        @Override
+        boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
+            Log.i(TAG, "checkPermissionForPreflight " + permission);
+            return true;
+        }
+
+        @Override
+        boolean isCallerRecents(int uid) {
+            return false;
+        }
+
+        @Override
         WindowManagerService getWindowManager() {
             return mWindowManagerMock;
         }
diff --git a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
index d5a28f6..d2ea9c4 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.autofill;
 
-import static com.android.server.autofill.AutofillManagerService.getWhitelistedCompatModePackages;
+import static com.android.server.autofill.AutofillManagerService.getAllowedCompatModePackages;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -29,54 +29,54 @@
 public class AutofillManagerServiceTest {
 
     @Test
-    public void testGetWhitelistedCompatModePackages_null() {
-        assertThat(getWhitelistedCompatModePackages(null)).isNull();
+    public void testGetAllowedCompatModePackages_null() {
+        assertThat(getAllowedCompatModePackages(null)).isNull();
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_empty() {
-        assertThat(getWhitelistedCompatModePackages("")).isNull();
+    public void testGetAllowedCompatModePackages_empty() {
+        assertThat(getAllowedCompatModePackages("")).isNull();
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_onePackageNoUrls() {
-        assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
+    public void testGetAllowedCompatModePackages_onePackageNoUrls() {
+        assertThat(getAllowedCompatModePackages("one_is_the_loniest_package"))
                 .containsExactly("one_is_the_loniest_package", null);
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_onePackageMissingEndDelimiter() {
-        assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package[")).isEmpty();
+    public void testGetAllowedCompatModePackages_onePackageMissingEndDelimiter() {
+        assertThat(getAllowedCompatModePackages("one_is_the_loniest_package[")).isEmpty();
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_onePackageOneUrl() {
+    public void testGetAllowedCompatModePackages_onePackageOneUrl() {
         final Map<String, String[]> result =
-                getWhitelistedCompatModePackages("one_is_the_loniest_package[url]");
+                getAllowedCompatModePackages("one_is_the_loniest_package[url]");
         assertThat(result).hasSize(1);
         assertThat(result.get("one_is_the_loniest_package")).asList().containsExactly("url");
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_onePackageMultipleUrls() {
+    public void testGetAllowedCompatModePackages_onePackageMultipleUrls() {
         final Map<String, String[]> result =
-                getWhitelistedCompatModePackages("one_is_the_loniest_package[4,5,8,15,16,23,42]");
+                getAllowedCompatModePackages("one_is_the_loniest_package[4,5,8,15,16,23,42]");
         assertThat(result).hasSize(1);
         assertThat(result.get("one_is_the_loniest_package")).asList()
             .containsExactly("4", "5", "8", "15", "16", "23", "42");
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_multiplePackagesOneInvalid() {
-        final Map<String, String[]> result = getWhitelistedCompatModePackages("one:two[");
+    public void testGetAllowedCompatModePackages_multiplePackagesOneInvalid() {
+        final Map<String, String[]> result = getAllowedCompatModePackages("one:two[");
         assertThat(result).hasSize(1);
         assertThat(result.get("one")).isNull();
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_multiplePackagesMultipleUrls() {
+    public void testGetAllowedCompatModePackages_multiplePackagesMultipleUrls() {
         final Map<String, String[]> result =
-                getWhitelistedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
+                getAllowedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
         assertThat(result).hasSize(3);
         assertThat(result.get("p1")).asList().containsExactly("p1u1");
         assertThat(result.get("p2")).isNull();
@@ -84,9 +84,9 @@
     }
 
     @Test
-    public void testGetWhitelistedCompatModePackages_threePackagesOneInvalid() {
+    public void testGetAllowedCompatModePackages_threePackagesOneInvalid() {
         final Map<String, String[]> result =
-                getWhitelistedCompatModePackages("p1[p1u1]:p2[:p3[p3u1,p3u2]");
+                getAllowedCompatModePackages("p1[p1u1]:p2[:p3[p3u1,p3u2]");
         assertThat(result).hasSize(2);
         assertThat(result.get("p1")).asList().containsExactly("p1u1");
         assertThat(result.get("p3")).asList().containsExactly("p3u1", "p3u2");
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
new file mode 100644
index 0000000..2b72fab
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -0,0 +1,255 @@
+/*
+ * 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.biometrics.log;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.input.InputSensorInfo;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class BiometricLoggerTest {
+
+    private static final int DEFAULT_MODALITY = BiometricsProtoEnums.MODALITY_FINGERPRINT;
+    private static final int DEFAULT_ACTION = BiometricsProtoEnums.ACTION_AUTHENTICATE;
+    private static final int DEFAULT_CLIENT = BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
+
+    @Rule
+    public TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext());
+    @Mock
+    private BiometricFrameworkStatsLogger mSink;
+    @Mock
+    private SensorManager mSensorManager;
+    @Mock
+    private BaseClientMonitor mClient;
+
+    private BiometricLogger mLogger;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext.addMockSystemService(SensorManager.class, mSensorManager);
+        when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
+                new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
+                        "", "", 0, 0, 0))
+        );
+    }
+
+    private BiometricLogger createLogger() {
+        return createLogger(DEFAULT_MODALITY, DEFAULT_ACTION, DEFAULT_CLIENT);
+    }
+
+    private BiometricLogger createLogger(int statsModality, int statsAction, int statsClient) {
+        return new BiometricLogger(statsModality, statsAction, statsClient, mSink, mSensorManager);
+    }
+
+    @Test
+    public void testAcquired() {
+        mLogger = createLogger();
+
+        final int acquiredInfo = 2;
+        final int vendorCode = 3;
+        final boolean isCrypto = true;
+        final int targetUserId = 9;
+
+        mLogger.logOnAcquired(mContext, acquiredInfo, vendorCode, isCrypto, targetUserId);
+
+        verify(mSink).acquired(
+                eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
+                eq(acquiredInfo), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+    }
+
+    @Test
+    public void testAuth() {
+        mLogger = createLogger();
+
+        final boolean authenticated = true;
+        final boolean requireConfirmation = false;
+        final boolean isCrypto = false;
+        final int targetUserId = 11;
+        final boolean isBiometricPrompt = true;
+
+        mLogger.logOnAuthenticated(mContext,
+                authenticated, requireConfirmation, isCrypto, targetUserId, isBiometricPrompt);
+
+        verify(mSink).authenticate(
+                eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
+                anyLong(), eq(authenticated), anyInt(), eq(requireConfirmation), eq(isCrypto),
+                eq(targetUserId), eq(isBiometricPrompt), anyFloat());
+    }
+
+    @Test
+    public void testEnroll() {
+        mLogger = createLogger();
+
+        final int targetUserId = 4;
+        final long latency = 44;
+        final boolean enrollSuccessful = true;
+
+        mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful);
+
+        verify(mSink).enroll(
+                eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
+                eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat());
+    }
+
+    @Test
+    public void testError() {
+        mLogger = createLogger();
+
+        final int error = 7;
+        final int vendorCode = 11;
+        final boolean isCrypto = false;
+        final int targetUserId = 9;
+
+        mLogger.logOnError(mContext, error, vendorCode, isCrypto, targetUserId);
+
+        verify(mSink).error(
+                eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
+                anyLong(), eq(error), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+    }
+
+    @Test
+    public void testBadModalityActsDisabled() {
+        mLogger = createLogger(
+                BiometricsProtoEnums.MODALITY_UNKNOWN, DEFAULT_ACTION, DEFAULT_CLIENT);
+        testDisabledMetrics(true /* isBadConfig */);
+    }
+
+    @Test
+    public void testBadActionActsDisabled() {
+        mLogger = createLogger(
+                DEFAULT_MODALITY, BiometricsProtoEnums.ACTION_UNKNOWN, DEFAULT_CLIENT);
+        testDisabledMetrics(true /* isBadConfig */);
+    }
+
+    @Test
+    public void testDisableLogger() {
+        mLogger = createLogger();
+        testDisabledMetrics(false /* isBadConfig */);
+    }
+
+    private void testDisabledMetrics(boolean isBadConfig) {
+        mLogger.disableMetrics();
+        mLogger.logOnAcquired(mContext,
+                0 /* acquiredInfo */,
+                1 /* vendorCode */,
+                true /* isCrypto */,
+                8 /* targetUserId */);
+        mLogger.logOnAuthenticated(mContext,
+                true /* authenticated */,
+                true /* requireConfirmation */,
+                false /* isCrypto */,
+                4 /* targetUserId */,
+                true/* isBiometricPrompt */);
+        mLogger.logOnEnrolled(2 /* targetUserId */,
+                10 /* latency */,
+                true /* enrollSuccessful */);
+        mLogger.logOnError(mContext,
+                4 /* error */,
+                0 /* vendorCode */,
+                false /* isCrypto */,
+                6 /* targetUserId */);
+
+        verify(mSink, never()).acquired(
+                anyInt(), anyInt(), anyInt(), anyBoolean(),
+                anyInt(), anyInt(), anyBoolean(), anyInt());
+        verify(mSink, never()).authenticate(
+                anyInt(), anyInt(), anyInt(), anyBoolean(),
+                anyLong(), anyBoolean(), anyInt(), anyBoolean(),
+                anyBoolean(), anyInt(), anyBoolean(), anyFloat());
+        verify(mSink, never()).enroll(
+                anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
+        verify(mSink, never()).error(
+                anyInt(), anyInt(), anyInt(), anyBoolean(),
+                anyLong(), anyInt(), anyInt(), anyBoolean(), anyInt());
+
+        mLogger.logUnknownEnrollmentInFramework();
+        mLogger.logUnknownEnrollmentInHal();
+
+        verify(mSink, times(isBadConfig ? 0 : 1))
+                .reportUnknownTemplateEnrolledHal(eq(DEFAULT_MODALITY));
+        verify(mSink, times(isBadConfig ? 0 : 1))
+                .reportUnknownTemplateEnrolledFramework(eq(DEFAULT_MODALITY));
+    }
+
+    @Test
+    public void systemHealthBadHalTemplate() {
+        mLogger = createLogger();
+        mLogger.logUnknownEnrollmentInHal();
+        verify(mSink).reportUnknownTemplateEnrolledHal(eq(DEFAULT_MODALITY));
+    }
+
+    @Test
+    public void systemHealthBadFrameworkTemplate() {
+        mLogger = createLogger();
+        mLogger.logUnknownEnrollmentInFramework();
+        verify(mSink).reportUnknownTemplateEnrolledFramework(eq(DEFAULT_MODALITY));
+    }
+
+    @Test
+    public void testALSCallback() {
+        mLogger = createLogger();
+        final CallbackWithProbe<Probe> callback =
+                mLogger.createALSCallback(true /* startWithClient */);
+
+        callback.onClientStarted(mClient);
+        verify(mSensorManager).registerListener(any(), any(), anyInt());
+
+        callback.onClientFinished(mClient, true /* success */);
+        verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+    }
+
+    @Test
+    public void testALSCallbackDoesNotStart() {
+        mLogger = createLogger();
+        final CallbackWithProbe<Probe> callback =
+                mLogger.createALSCallback(false /* startWithClient */);
+
+        callback.onClientStarted(mClient);
+        callback.onClientFinished(mClient, true /* success */);
+        verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index a06a782..fc55a9f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -52,7 +52,7 @@
     @Mock
     private ClientMonitorCallbackConverter mClientCallback;
     @Mock
-    private BaseClientMonitor.Callback mSchedulerCallback;
+    private ClientMonitorCallback mSchedulerCallback;
 
     @Before
     public void setUp() {
@@ -96,7 +96,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
             startHalOperation();
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
new file mode 100644
index 0000000..51d234d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class BaseClientMonitorTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private IBinder mToken;
+    private @Mock ClientMonitorCallbackConverter mListener;
+    @Mock
+    private BiometricLogger mLogger;
+    @Mock
+    private ClientMonitorCallback mCallback;
+
+    private TestClientMonitor mClientMonitor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mClientMonitor = new TestClientMonitor();
+    }
+
+    @Test
+    public void preparesForDeath() throws RemoteException {
+        verify(mToken).linkToDeath(eq(mClientMonitor), anyInt());
+
+        mClientMonitor.binderDied();
+
+        assertThat(mClientMonitor.mCanceled).isTrue();
+        assertThat(mClientMonitor.getListener()).isNull();
+    }
+
+    @Test
+    public void ignoresDeathWhenDone() {
+        mClientMonitor.markAlreadyDone();
+        mClientMonitor.binderDied();
+
+        assertThat(mClientMonitor.mCanceled).isFalse();
+    }
+
+    @Test
+    public void start() {
+        mClientMonitor.start(mCallback);
+
+        verify(mCallback).onClientStarted(eq(mClientMonitor));
+    }
+
+    @Test
+    public void destroy() {
+        mClientMonitor.destroy();
+        mClientMonitor.destroy();
+
+        assertThat(mClientMonitor.isAlreadyDone()).isTrue();
+        verify(mToken).unlinkToDeath(eq(mClientMonitor), anyInt());
+    }
+
+    @Test
+    public void hasRequestId() {
+        assertThat(mClientMonitor.hasRequestId()).isFalse();
+
+        final int id = 200;
+        mClientMonitor.setRequestId(id);
+        assertThat(mClientMonitor.hasRequestId()).isTrue();
+        assertThat(mClientMonitor.getRequestId()).isEqualTo(id);
+    }
+
+    private class TestClientMonitor extends BaseClientMonitor implements Interruptable {
+        public boolean mCanceled = false;
+
+        TestClientMonitor() {
+            super(mContext, mToken, mListener, 9 /* userId */, "foo" /* owner */, 2 /* cookie */,
+                    5 /* sensorId */, mLogger);
+        }
+
+        @Override
+        public int getProtoEnum() {
+            return 0;
+        }
+
+        @Override
+        public void cancel() {
+            mCanceled = true;
+        }
+
+        @Override
+        public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
+            mCanceled = true;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
new file mode 100644
index 0000000..8751cf3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static android.testing.TestableLooper.RunWithLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+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 static org.testng.Assert.assertThrows;
+
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+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;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class BiometricSchedulerOperationTest {
+
+    public interface FakeHal {}
+    public abstract static class InterruptableMonitor<T>
+            extends HalClientMonitor<T> implements  Interruptable {
+        public InterruptableMonitor() {
+            super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+        }
+    }
+
+    @Mock
+    private InterruptableMonitor<FakeHal> mClientMonitor;
+    @Mock
+    private ClientMonitorCallback mClientCallback;
+    @Mock
+    private FakeHal mHal;
+    @Captor
+    ArgumentCaptor<ClientMonitorCallback> mStartCallback;
+
+    private Handler mHandler;
+    private BiometricSchedulerOperation mOperation;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mHandler = new Handler(TestableLooper.get(this).getLooper());
+        mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
+    }
+
+    @Test
+    public void testStartWithCookie() {
+        final int cookie = 200;
+        when(mClientMonitor.getCookie()).thenReturn(cookie);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        assertThat(mOperation.isReadyToStart()).isEqualTo(cookie);
+        assertThat(mOperation.isStarted()).isFalse();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+
+        final boolean started = mOperation.startWithCookie(
+                mock(ClientMonitorCallback.class), cookie);
+
+        assertThat(started).isTrue();
+        verify(mClientMonitor).start(mStartCallback.capture());
+        mStartCallback.getValue().onClientStarted(mClientMonitor);
+        assertThat(mOperation.isStarted()).isTrue();
+    }
+
+    @Test
+    public void testNoStartWithoutCookie() {
+        final int goodCookie = 20;
+        final int badCookie = 22;
+        when(mClientMonitor.getCookie()).thenReturn(goodCookie);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
+        final boolean started = mOperation.startWithCookie(
+                mock(ClientMonitorCallback.class), badCookie);
+
+        assertThat(started).isFalse();
+        assertThat(mOperation.isStarted()).isFalse();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+    }
+
+    @Test
+    public void startsWhenReadyAndHalAvailable() {
+        when(mClientMonitor.getCookie()).thenReturn(0);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
+        mOperation.start(cb);
+        verify(mClientMonitor).start(mStartCallback.capture());
+        mStartCallback.getValue().onClientStarted(mClientMonitor);
+
+        assertThat(mOperation.isStarted()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+
+        verify(mClientCallback).onClientStarted(eq(mClientMonitor));
+        verify(cb).onClientStarted(eq(mClientMonitor));
+        verify(mClientCallback, never()).onClientFinished(any(), anyBoolean());
+        verify(cb, never()).onClientFinished(any(), anyBoolean());
+
+        mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+        assertThat(mOperation.isFinished()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        verify(mClientMonitor).destroy();
+        verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+    }
+
+    @Test
+    public void startFailsWhenReadyButHalNotAvailable() {
+        when(mClientMonitor.getCookie()).thenReturn(0);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(null);
+
+        final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
+        mOperation.start(cb);
+        verify(mClientMonitor, never()).start(any());
+
+        assertThat(mOperation.isStarted()).isFalse();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isTrue();
+
+        verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor));
+        verify(cb, never()).onClientStarted(eq(mClientMonitor));
+        verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false));
+        verify(cb).onClientFinished(eq(mClientMonitor), eq(false));
+    }
+
+    @Test
+    public void doesNotStartWithCookie() {
+        when(mClientMonitor.getCookie()).thenReturn(9);
+        assertThrows(IllegalStateException.class,
+                () -> mOperation.start(mock(ClientMonitorCallback.class)));
+    }
+
+    @Test
+    public void cannotRestart() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.start(mock(ClientMonitorCallback.class));
+
+        assertThrows(IllegalStateException.class,
+                () -> mOperation.start(mock(ClientMonitorCallback.class)));
+    }
+
+    @Test
+    public void abortsNotRunning() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.abort();
+
+        assertThat(mOperation.isFinished()).isTrue();
+        verify(mClientMonitor).unableToStart();
+        verify(mClientMonitor).destroy();
+        assertThrows(IllegalStateException.class,
+                () -> mOperation.start(mock(ClientMonitorCallback.class)));
+    }
+
+    @Test
+    public void cannotAbortRunning() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.start(mock(ClientMonitorCallback.class));
+
+        assertThrows(IllegalStateException.class, () -> mOperation.abort());
+    }
+
+    @Test
+    public void cancel() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        final ClientMonitorCallback startCb = mock(ClientMonitorCallback.class);
+        final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
+        mOperation.start(startCb);
+        verify(mClientMonitor).start(mStartCallback.capture());
+        mStartCallback.getValue().onClientStarted(mClientMonitor);
+        mOperation.cancel(mHandler, cancelCb);
+
+        assertThat(mOperation.isCanceling()).isTrue();
+        verify(mClientMonitor).cancel();
+        verify(mClientMonitor, never()).cancelWithoutStarting(any());
+        verify(mClientMonitor, never()).destroy();
+
+        mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+        assertThat(mOperation.isFinished()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        verify(mClientMonitor).destroy();
+
+        // should be unused since the operation was started
+        verify(cancelCb, never()).onClientStarted(any());
+        verify(cancelCb, never()).onClientFinished(any(), anyBoolean());
+    }
+
+    @Test
+    public void cancelWithoutStarting() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
+        mOperation.cancel(mHandler, cancelCb);
+
+        assertThat(mOperation.isCanceling()).isTrue();
+        ArgumentCaptor<ClientMonitorCallback> cbCaptor =
+                ArgumentCaptor.forClass(ClientMonitorCallback.class);
+        verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
+
+        cbCaptor.getValue().onClientFinished(mClientMonitor, true);
+        verify(cancelCb).onClientFinished(eq(mClientMonitor), eq(true));
+        verify(mClientMonitor, never()).start(any());
+        verify(mClientMonitor, never()).cancel();
+        verify(mClientMonitor).destroy();
+    }
+
+    @Test
+    public void markCanceling() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.markCanceling();
+
+        assertThat(mOperation.isMarkedCanceling()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+        verify(mClientMonitor, never()).start(any());
+        verify(mClientMonitor, never()).cancel();
+        verify(mClientMonitor, never()).cancelWithoutStarting(any());
+        verify(mClientMonitor, never()).unableToStart();
+        verify(mClientMonitor, never()).destroy();
+    }
+
+    @Test
+    public void cancelPendingWithCookie() {
+        markCancellingAndStart(2);
+    }
+
+    @Test
+    public void cancelPendingWithoutCookie() {
+        markCancellingAndStart(null);
+    }
+
+    private void markCancellingAndStart(Integer withCookie) {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+        if (withCookie != null) {
+            when(mClientMonitor.getCookie()).thenReturn(withCookie);
+        }
+
+        mOperation.markCanceling();
+        final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
+        if (withCookie != null) {
+            mOperation.startWithCookie(cb, withCookie);
+        } else {
+            mOperation.start(cb);
+        }
+
+        assertThat(mOperation.isFinished()).isTrue();
+        verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+        verify(mClientMonitor, never()).start(any());
+        verify(mClientMonitor, never()).cancel();
+        verify(mClientMonitor, never()).cancelWithoutStarting(any());
+        verify(mClientMonitor, never()).unableToStart();
+        verify(mClientMonitor).destroy();
+    }
+
+    @Test
+    public void cancelWatchdogWhenStarted() {
+        cancelWatchdog(true);
+    }
+
+    @Test
+    public void cancelWatchdogWithoutStarting() {
+        cancelWatchdog(false);
+    }
+
+    private void cancelWatchdog(boolean start) {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.start(mock(ClientMonitorCallback.class));
+        if (start) {
+            verify(mClientMonitor).start(mStartCallback.capture());
+            mStartCallback.getValue().onClientStarted(mClientMonitor);
+        }
+        mOperation.cancel(mHandler, mock(ClientMonitorCallback.class));
+
+        assertThat(mOperation.isCanceling()).isTrue();
+
+        // omit call to onClientFinished and trigger watchdog
+        mOperation.mCancelWatchdog.run();
+
+        assertThat(mOperation.isFinished()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        verify(mClientMonitor).destroy();
+    }
+}
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 d192697..c99d656 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
@@ -16,10 +16,14 @@
 
 package com.android.server.biometrics.sensors;
 
+import static android.testing.TestableLooper.RunWithLooper;
+
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,10 +38,13 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.IBiometricService;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
+import android.testing.TestableLooper;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -46,16 +53,18 @@
 
 import com.android.server.biometrics.nano.BiometricSchedulerProto;
 import com.android.server.biometrics.nano.BiometricsProto;
-import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
 
 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;
 
 @Presubmit
 @SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
 public class BiometricSchedulerTest {
 
     private static final String TAG = "BiometricSchedulerTest";
@@ -76,8 +85,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mToken = new Binder();
-        mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
-                null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
+        mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
+                BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
+                mBiometricService, LOG_NUM_RECENT_OPERATIONS,
                 CoexCoordinator.getInstance());
     }
 
@@ -86,9 +96,9 @@
         final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
 
         final HalClientMonitor<Object> client1 =
-                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+                new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
         final HalClientMonitor<Object> client2 =
-                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+                new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
         mScheduler.scheduleClientMonitor(client1);
         mScheduler.scheduleClientMonitor(client2);
 
@@ -99,21 +109,18 @@
     @Test
     public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
         // Even if second client has a non-null daemon, it needs to be canceled.
-        Object daemon2 = mock(Object.class);
+        final TestHalClientMonitor client1 = new TestHalClientMonitor(
+                mContext, mToken, () -> null);
+        final TestHalClientMonitor client2 = new TestHalClientMonitor(
+                mContext, mToken, () -> mock(Object.class));
 
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
-        final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1);
-        final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
-
-        final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
-        final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+        final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
 
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
-        mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
-                mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
+        mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
+                mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
         assertEquals(1, mScheduler.mPendingOperations.size());
@@ -122,11 +129,11 @@
         mScheduler.scheduleClientMonitor(client2, callback2);
         waitForIdle();
 
-        assertTrue(client1.wasUnableToStart());
+        assertTrue(client1.mUnableToStart);
         verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
         verify(callback1, never()).onClientStarted(any());
 
-        assertTrue(client2.wasUnableToStart());
+        assertTrue(client2.mUnableToStart);
         verify(callback2).onClientFinished(eq(client2), eq(false) /* success */);
         verify(callback2, never()).onClientStarted(any());
 
@@ -138,22 +145,20 @@
         // Second non-BiometricPrompt client has a valid daemon
         final Object daemon2 = mock(Object.class);
 
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
         final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
 
         final TestAuthenticationClient client1 =
-                new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
-        final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+                new TestAuthenticationClient(mContext, () -> null, mToken, listener1);
+        final TestHalClientMonitor client2 =
+                new TestHalClientMonitor(mContext, mToken, () -> daemon2);
 
-        final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
-        final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+        final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
 
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
-        mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
-                mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
+        mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
+                mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
         assertEquals(1, mScheduler.mPendingOperations.size());
@@ -172,8 +177,8 @@
         verify(callback1, never()).onClientStarted(any());
 
         // Client 2 was able to start
-        assertFalse(client2.wasUnableToStart());
-        assertTrue(client2.hasStarted());
+        assertFalse(client2.mUnableToStart);
+        assertTrue(client2.mStarted);
         verify(callback2).onClientStarted(eq(client2));
     }
 
@@ -182,21 +187,23 @@
         final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
                 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
-        final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
 
         // Schedule a BiometricPrompt authentication request
         mScheduler.scheduleClientMonitor(client1, callback1);
 
-        assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState);
-        assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor);
+        assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
+        assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
         assertEquals(0, mScheduler.mPendingOperations.size());
 
         // Request it to be canceled. The operation can be canceled immediately, and the scheduler
         // should go back to idle, since in this case the framework has not even requested the HAL
         // to authenticate yet.
         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+        waitForIdle();
         assertTrue(client1.isAlreadyDone());
         assertTrue(client1.mDestroyed);
+        assertFalse(client1.mStartedHal);
         assertNull(mScheduler.mCurrentOperation);
     }
 
@@ -210,8 +217,8 @@
         // assertEquals(0, bsp.recentOperations.length);
 
         // Pretend the scheduler is busy enrolling, and check the proto dump again.
-        final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+        final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
         mScheduler.scheduleClientMonitor(client);
         waitForIdle();
         bsp = getDump(true /* clearSchedulerBuffer */);
@@ -230,8 +237,8 @@
     @Test
     public void testProtoDump_fifo() throws Exception {
         // Add the first operation
-        final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+        final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
         mScheduler.scheduleClientMonitor(client);
         waitForIdle();
         BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
@@ -244,8 +251,8 @@
         client.getCallback().onClientFinished(client, true);
 
         // Add another operation
-        final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_REMOVE);
+        final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE);
         mScheduler.scheduleClientMonitor(client2);
         waitForIdle();
         bsp = getDump(false /* clearSchedulerBuffer */);
@@ -256,8 +263,8 @@
         client2.getCallback().onClientFinished(client2, true);
 
         // And another operation
-        final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
+        final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE);
         mScheduler.scheduleClientMonitor(client3);
         waitForIdle();
         bsp = getDump(false /* clearSchedulerBuffer */);
@@ -290,8 +297,7 @@
     @Test
     public void testCancelPendingAuth() throws RemoteException {
         final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-
-        final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon);
+        final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
                 mToken, callback);
@@ -302,14 +308,12 @@
         waitForIdle();
 
         assertEquals(mScheduler.getCurrentClient(), client1);
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
 
         // Request cancel before the authentication client has started
         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
         waitForIdle();
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
 
         // Finish the blocking client. The authentication client should send ERROR_CANCELED
         client1.getCallback().onClientFinished(client1, true /* success */);
@@ -326,67 +330,109 @@
 
     @Test
     public void testCancels_whenAuthRequestIdNotSet() {
-        testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+        testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */);
     }
 
     @Test
     public void testCancels_whenAuthRequestIdNotSet_notStarted() {
-        testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+        testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */);
     }
 
     @Test
     public void testCancels_whenAuthRequestIdMatches() {
-        testCancelsWhenRequestId(200L, 200, true /* started */);
+        testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */);
     }
 
     @Test
     public void testCancels_whenAuthRequestIdMatches_noStarted() {
-        testCancelsWhenRequestId(200L, 200, false /* started */);
+        testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */);
     }
 
     @Test
     public void testDoesNotCancel_whenAuthRequestIdMismatched() {
-        testCancelsWhenRequestId(10L, 20, true /* started */);
+        testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */);
     }
 
     @Test
     public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
-        testCancelsWhenRequestId(10L, 20, false /* started */);
+        testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */);
+    }
+
+    private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+            boolean started) {
+        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+        testCancelsWhenRequestId(requestId, cancelRequestId, started,
+                new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback));
+    }
+
+    @Test
+    public void testCancels_whenEnrollRequestIdNotSet() {
+        testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */);
+    }
+
+    @Test
+    public void testCancels_whenEnrollRequestIdMatches() {
+        testCancelsEnrollWhenRequestId(200L, 200, false /* started */);
+    }
+
+    @Test
+    public void testDoesNotCancel_whenEnrollRequestIdMismatched() {
+        testCancelsEnrollWhenRequestId(10L, 20, false /* started */);
+    }
+
+    private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+            boolean started) {
+        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+        testCancelsWhenRequestId(requestId, cancelRequestId, started,
+                new TestEnrollClient(mContext, lazyDaemon, mToken, callback));
     }
 
     private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
-            boolean started) {
+            boolean started, HalClientMonitor<?> client) {
         final boolean matches = requestId == null || requestId == cancelRequestId;
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
-        final TestAuthenticationClient client = new TestAuthenticationClient(
-                mContext, lazyDaemon, mToken, callback);
         if (requestId != null) {
             client.setRequestId(requestId);
         }
 
+        final boolean isAuth = client instanceof TestAuthenticationClient;
+        final boolean isEnroll = client instanceof TestEnrollClient;
+
         mScheduler.scheduleClientMonitor(client);
         if (started) {
             mScheduler.startPreparedClient(client.getCookie());
         }
         waitForIdle();
-        mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+        if (isAuth) {
+            mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+        } else if (isEnroll) {
+            mScheduler.cancelEnrollment(mToken, cancelRequestId);
+        } else {
+            fail("unexpected operation type");
+        }
         waitForIdle();
 
-        assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+        if (isAuth) {
+            // auth clients that were waiting for cookie when canceled should never invoke the hal
+            final TestAuthenticationClient authClient = (TestAuthenticationClient) client;
+            assertEquals(matches && started ? 1 : 0, authClient.mNumCancels);
+            assertEquals(started, authClient.mStartedHal);
+        } else if (isEnroll) {
+            final TestEnrollClient enrollClient = (TestEnrollClient) client;
+            assertEquals(matches ? 1 : 0, enrollClient.mNumCancels);
+            assertTrue(enrollClient.mStartedHal);
+        }
 
         if (matches) {
-            if (started) {
-                assertEquals(Operation.STATE_STARTED_CANCELING,
-                        mScheduler.mCurrentOperation.mState);
+            if (started || isEnroll) { // prep'd auth clients and enroll clients
+                assertTrue(mScheduler.mCurrentOperation.isCanceling());
             }
         } else {
-            if (started) {
-                assertEquals(Operation.STATE_STARTED,
-                        mScheduler.mCurrentOperation.mState);
+            if (started || isEnroll) { // prep'd auth clients and enroll clients
+                assertTrue(mScheduler.mCurrentOperation.isStarted());
             } else {
-                assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
-                        mScheduler.mCurrentOperation.mState);
+                assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
             }
         }
     }
@@ -411,18 +457,14 @@
         mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
         waitForIdle();
 
-        assertEquals(Operation.STATE_STARTED,
-                mScheduler.mCurrentOperation.mState);
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertTrue(mScheduler.mCurrentOperation.isStarted());
+        assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
 
         mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
         waitForIdle();
 
-        assertEquals(Operation.STATE_STARTED,
-                mScheduler.mCurrentOperation.mState);
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertTrue(mScheduler.mCurrentOperation.isStarted());
+        assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
     }
 
     @Test
@@ -459,12 +501,12 @@
     @Test
     public void testClientDestroyed_afterFinish() {
         final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
-        final TestClientMonitor client =
-                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+        final TestHalClientMonitor client =
+                new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
         mScheduler.scheduleClientMonitor(client);
         client.mCallback.onClientFinished(client, true /* success */);
         waitForIdle();
-        assertTrue(client.wasDestroyed());
+        assertTrue(client.mDestroyed);
     }
 
     private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
@@ -472,8 +514,10 @@
     }
 
     private static class TestAuthenticationClient extends AuthenticationClient<Object> {
-        int mNumCancels = 0;
+        boolean mStartedHal = false;
+        boolean mStoppedHal = false;
         boolean mDestroyed = false;
+        int mNumCancels = 0;
 
         public TestAuthenticationClient(@NonNull Context context,
                 @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -488,18 +532,16 @@
 
         @Override
         protected void stopHalOperation() {
-
+            mStoppedHal = true;
         }
 
         @Override
         protected void startHalOperation() {
-
+            mStartedHal = true;
         }
 
         @Override
-        protected void handleLifecycleAfterAuth(boolean authenticated) {
-
-        }
+        protected void handleLifecycleAfterAuth(boolean authenticated) {}
 
         @Override
         public boolean wasUserDetected() {
@@ -519,36 +561,59 @@
         }
     }
 
-    private static class TestClientMonitor2 extends TestClientMonitor {
-        private final int mProtoEnum;
+    private static class TestEnrollClient extends EnrollClient<Object> {
+        boolean mStartedHal = false;
+        boolean mStoppedHal = false;
+        int mNumCancels = 0;
 
-        public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
-                @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
-            super(context, token, lazyDaemon);
-            mProtoEnum = protoEnum;
+        TestEnrollClient(@NonNull Context context,
+                @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
+                @NonNull ClientMonitorCallbackConverter listener) {
+            super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
+                    "test" /* owner */, mock(BiometricUtils.class),
+                    5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
+                    true /* shouldVibrate */);
         }
 
         @Override
-        public int getProtoEnum() {
-            return mProtoEnum;
+        protected void stopHalOperation() {
+            mStoppedHal = true;
+        }
+
+        @Override
+        protected void startHalOperation() {
+            mStartedHal = true;
+        }
+
+        @Override
+        protected boolean hasReachedEnrollmentLimit() {
+            return false;
+        }
+
+        @Override
+        public void cancel() {
+            mNumCancels++;
+            super.cancel();
         }
     }
 
-    private static class TestClientMonitor extends HalClientMonitor<Object> {
+    private static class TestHalClientMonitor extends HalClientMonitor<Object> {
+        private final int mProtoEnum;
         private boolean mUnableToStart;
         private boolean mStarted;
         private boolean mDestroyed;
 
-        public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+        TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
                 @NonNull LazyDaemon<Object> lazyDaemon) {
-            this(context, token, lazyDaemon, 0 /* cookie */);
+            this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER);
         }
 
-        public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
-                @NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
+        TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
+                @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) {
             super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
                     TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
                     0 /* statsAction */, 0 /* statsClient */);
+            mProtoEnum = protoEnum;
         }
 
         @Override
@@ -559,13 +624,11 @@
 
         @Override
         public int getProtoEnum() {
-            // Anything other than CM_NONE, which is used to represent "idle". Tests that need
-            // real proto enums should use TestClientMonitor2
-            return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+            return mProtoEnum;
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
             assertFalse(mStarted);
             mStarted = true;
@@ -573,7 +636,7 @@
 
         @Override
         protected void startHalOperation() {
-
+            mStarted = true;
         }
 
         @Override
@@ -581,22 +644,9 @@
             super.destroy();
             mDestroyed = true;
         }
-
-        public boolean wasUnableToStart() {
-            return mUnableToStart;
-        }
-
-        public boolean hasStarted() {
-            return mStarted;
-        }
-
-        public boolean wasDestroyed() {
-            return mDestroyed;
-        }
-
     }
 
-    private static void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    private void waitForIdle() {
+        TestableLooper.get(this).processAllMessages();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
index 09b5c5c..587bb60 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -17,36 +17,62 @@
 package com.android.server.biometrics.sensors;
 
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
 
 @Presubmit
 @SmallTest
 public class CompositeCallbackTest {
 
+    @Mock
+    private BaseClientMonitor mClientMonitor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     @Test
-    public void testNullCallback() {
-        BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
-        BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
-        BaseClientMonitor.Callback callback3 = null;
+    public void testCallbacks() {
+        testCallbacks(mock(ClientMonitorCallback.class), mock(ClientMonitorCallback.class));
+    }
 
-        BaseClientMonitor.CompositeCallback callback = new BaseClientMonitor.CompositeCallback(
-                callback1, callback2, callback3);
+    @Test
+    public void testNullCallbacks() {
+        testCallbacks(null, mock(ClientMonitorCallback.class),
+                null, mock(ClientMonitorCallback.class));
+    }
 
-        BaseClientMonitor clientMonitor = mock(BaseClientMonitor.class);
+    private void testCallbacks(ClientMonitorCallback... callbacks) {
+        final ClientMonitorCallback[] expected = Arrays.stream(callbacks)
+                .filter(Objects::nonNull).toArray(ClientMonitorCallback[]::new);
 
-        callback.onClientStarted(clientMonitor);
-        verify(callback1).onClientStarted(eq(clientMonitor));
-        verify(callback2).onClientStarted(eq(clientMonitor));
+        ClientMonitorCompositeCallback callback = new ClientMonitorCompositeCallback(callbacks);
 
-        callback.onClientFinished(clientMonitor, true /* success */);
-        verify(callback1).onClientFinished(eq(clientMonitor), eq(true));
-        verify(callback2).onClientFinished(eq(clientMonitor), eq(true));
+        callback.onClientStarted(mClientMonitor);
+        final InOrder order = inOrder(expected);
+        for (ClientMonitorCallback cb : expected) {
+            order.verify(cb).onClientStarted(eq(mClientMonitor));
+        }
+
+        callback.onClientFinished(mClientMonitor, true /* success */);
+        Collections.reverse(Arrays.asList(expected));
+        for (ClientMonitorCallback cb : expected) {
+            order.verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java
new file mode 100644
index 0000000..a53e22e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class LockoutResetDispatcherTest {
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+    @Mock
+    private IBinder mBinder;
+    @Mock
+    private IBiometricServiceLockoutResetCallback mCallback;
+
+    private LockoutResetDispatcher mDispatcher;
+
+    @Before
+    public void setup() {
+        when(mCallback.asBinder()).thenReturn(mBinder);
+        mDispatcher = new LockoutResetDispatcher(mContext);
+    }
+
+    @Test
+    public void linksToDeath() throws Exception {
+        mDispatcher.addCallback(mCallback, "package");
+        verify(mBinder).linkToDeath(eq(mDispatcher), anyInt());
+    }
+
+    @Test
+    public void notifyLockoutReset() throws Exception {
+        final int sensorId = 24;
+
+        mDispatcher.addCallback(mCallback, "some.package");
+        mDispatcher.notifyLockoutResetCallbacks(sensorId);
+
+        final ArgumentCaptor<IRemoteCallback> captor =
+                ArgumentCaptor.forClass(IRemoteCallback.class);
+        verify(mCallback).onLockoutReset(eq(sensorId), captor.capture());
+        captor.getValue().sendResult(new Bundle());
+    }
+
+    @Test
+    public void releaseWakeLockOnDeath() {
+        mDispatcher.addCallback(mCallback, "a.b.cee");
+        mDispatcher.binderDied(mBinder);
+
+        // would be better to check the wake lock
+        // but this project lacks the extended mockito support to do it
+        assertThat(mDispatcher.mClientCallbacks).isEmpty();
+    }
+
+    @Test
+    public void releaseCorrectWakeLockOnDeath() {
+        mDispatcher.addCallback(mCallback, "a.b");
+        mDispatcher.binderDied(mock(IBinder.class));
+
+        assertThat(mDispatcher.mClientCallbacks).hasSize(1);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 7fccd49..a11709a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import static android.testing.TestableLooper.RunWithLooper;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
@@ -28,52 +30,53 @@
 import android.content.Context;
 import android.hardware.biometrics.IBiometricService;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
 @SmallTest
 public class UserAwareBiometricSchedulerTest {
 
-    private static final String TAG = "BiometricSchedulerTest";
+    private static final String TAG = "UserAwareBiometricSchedulerTest";
     private static final int TEST_SENSOR_ID = 0;
 
+    private Handler mHandler;
     private UserAwareBiometricScheduler mScheduler;
-    private IBinder mToken;
+    private IBinder mToken = new Binder();
 
     @Mock
     private Context mContext;
     @Mock
     private IBiometricService mBiometricService;
 
-    private TestUserStartedCallback mUserStartedCallback;
-    private TestUserStoppedCallback mUserStoppedCallback;
+    private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
+    private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
     private int mCurrentUserId = UserHandle.USER_NULL;
-    private boolean mStartOperationsFinish;
-    private int mStartUserClientCount;
+    private boolean mStartOperationsFinish = true;
+    private int mStartUserClientCount = 0;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mToken = new Binder();
-        mStartOperationsFinish = true;
-        mStartUserClientCount = 0;
-        mUserStartedCallback = new TestUserStartedCallback();
-        mUserStoppedCallback = new TestUserStoppedCallback();
-
+        mHandler = new Handler(TestableLooper.get(this).getLooper());
         mScheduler = new UserAwareBiometricScheduler(TAG,
+                mHandler,
                 BiometricScheduler.SENSOR_TYPE_UNKNOWN,
                 null /* gestureAvailabilityDispatcher */,
                 mBiometricService,
@@ -117,7 +120,7 @@
         mCurrentUserId = UserHandle.USER_NULL;
         mStartOperationsFinish = false;
 
-        final BaseClientMonitor[] nextClients = new BaseClientMonitor[] {
+        final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
                 mock(BaseClientMonitor.class),
                 mock(BaseClientMonitor.class),
                 mock(BaseClientMonitor.class)
@@ -147,12 +150,12 @@
         waitForIdle();
 
         final TestStartUserClient startUserClient =
-                (TestStartUserClient) mScheduler.mCurrentOperation.mClientMonitor;
+                (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
         mScheduler.reset();
         assertNull(mScheduler.mCurrentOperation);
 
-        final BiometricScheduler.Operation fakeOperation = new BiometricScheduler.Operation(
-                mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
+        final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
+                mock(BaseClientMonitor.class), new ClientMonitorCallback() {});
         mScheduler.mCurrentOperation = fakeOperation;
         startUserClient.mCallback.onClientFinished(startUserClient, true);
         assertSame(fakeOperation, mScheduler.mCurrentOperation);
@@ -194,8 +197,8 @@
         verify(nextClient).start(any());
     }
 
-    private static void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    private void waitForIdle() {
+        TestableLooper.get(this).processAllMessages();
     }
 
     private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback {
@@ -231,7 +234,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
             onUserStopped();
         }
@@ -245,7 +248,7 @@
     private static class TestStartUserClient extends StartUserClient<Object, Object> {
         private final boolean mShouldFinish;
 
-        Callback mCallback;
+        ClientMonitorCallback mCallback;
 
         public TestStartUserClient(@NonNull Context context,
                 @NonNull LazyDaemon<Object> lazyDaemon, @Nullable IBinder token, int userId,
@@ -260,7 +263,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
 
             mCallback = callback;
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 a13dff2..2718bf9 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
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.CoexCoordinator;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -79,10 +80,13 @@
         when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
 
         mScheduler = new UserAwareBiometricScheduler(TAG,
+                new Handler(mLooper.getLooper()),
                 BiometricScheduler.SENSOR_TYPE_FACE,
                 null /* gestureAvailabilityDispatcher */,
+                mBiometricService,
                 () -> USER_ID,
-                mUserSwitchCallback);
+                mUserSwitchCallback,
+                CoexCoordinator.getInstance());
         mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
                 TAG, mScheduler, SENSOR_ID,
                 USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 39c51d5..21a7a8a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -32,7 +32,9 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 
@@ -69,6 +71,7 @@
     @Mock
     private BiometricScheduler mScheduler;
 
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
     private LockoutResetDispatcher mLockoutResetDispatcher;
     private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
     private IBinder mBinder;
@@ -97,7 +100,7 @@
                 resetLockoutRequiresChallenge);
 
         Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
-        mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
+        mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
         mBinder = new Binder();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
index 55dc035..931fad1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -34,7 +34,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 
 import org.junit.Before;
@@ -61,7 +61,7 @@
     @Mock
     private IFaceServiceReceiver mOtherReceiver;
     @Mock
-    private BaseClientMonitor.Callback mMonitorCallback;
+    private ClientMonitorCallback mMonitorCallback;
 
     private FaceGenerateChallengeClient mClient;
 
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 0d520ca..d4609b5 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
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.CoexCoordinator;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -79,10 +80,13 @@
         when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
 
         mScheduler = new UserAwareBiometricScheduler(TAG,
+                new Handler(mLooper.getLooper()),
                 BiometricScheduler.SENSOR_TYPE_FP_OTHER,
                 null /* gestureAvailabilityDispatcher */,
+                mBiometricService,
                 () -> USER_ID,
-                mUserSwitchCallback);
+                mUserSwitchCallback,
+                CoexCoordinator.getInstance());
         mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
                 TAG, mScheduler, SENSOR_ID,
                 USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
new file mode 100644
index 0000000..53468c8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.input.InputManagerInternal;
+import android.os.Binder;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class InputControllerTest {
+
+    @Mock
+    private InputManagerInternal mInputManagerInternalMock;
+    @Mock
+    private InputController.NativeWrapper mNativeWrapperMock;
+
+    private InputController mInputController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+        LocalServices.removeServiceForTest(InputManagerInternal.class);
+        LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+
+        mInputController = new InputController(new Object(), mNativeWrapperMock);
+    }
+
+    @Test
+    public void unregisterInputDevice_allMiceUnregistered_unsetValues() {
+        final IBinder deviceToken = new Binder();
+        mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+                /* displayId= */ 1);
+        mInputController.unregisterInputDevice(deviceToken);
+        verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(
+                eq(Display.INVALID_DISPLAY));
+    }
+
+    @Test
+    public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride() {
+        final IBinder deviceToken = new Binder();
+        mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+                /* displayId= */ 1);
+        verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
+        mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+                /* displayId= */ 2);
+        verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
+        mInputController.unregisterInputDevice(deviceToken);
+        verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
+    }
+}
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 c7c0756..72100e44 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
@@ -18,18 +18,24 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
 import android.Manifest;
+import android.app.admin.DevicePolicyManager;
+import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.VirtualDeviceParams;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.graphics.Point;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
@@ -70,6 +76,14 @@
     private InputController.NativeWrapper mNativeWrapperMock;
     @Mock
     private DisplayManagerInternal mDisplayManagerInternalMock;
+    @Mock
+    private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
+    @Mock
+    private DevicePolicyManager mDevicePolicyManagerMock;
+    @Mock
+    private InputManagerInternal mInputManagerInternalMock;
+    @Mock
+    private IVirtualDeviceActivityListener mActivityListener;
 
     @Before
     public void setUp() {
@@ -78,13 +92,29 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
 
+        doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+        LocalServices.removeServiceForTest(InputManagerInternal.class);
+        LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+
         mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
         doNothing().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+        when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mDevicePolicyManagerMock);
+
         mInputController = new InputController(new Object(), mNativeWrapperMock);
         mDeviceImpl = new VirtualDeviceImpl(mContext,
                 /* association info */ null, new Binder(), /* uid */ 0, mInputController,
-                (int associationId) -> {});
+                (int associationId) -> {}, mPendingTrampolineCallback, mActivityListener,
+                new VirtualDeviceParams.Builder().build());
+    }
+
+    @Test
+    public void onVirtualDisplayRemovedLocked_doesNotThrowException() {
+        final int displayId = 2;
+        mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+        // This call should not throw any exceptions.
+        mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
     }
 
     @Test
@@ -150,7 +180,7 @@
         mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
                 BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches")
-                .that(mInputController.mInputDeviceFds).isNotEmpty();
+                .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
         verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
     }
 
@@ -160,7 +190,7 @@
         mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
                 BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches")
-                .that(mInputController.mInputDeviceFds).isNotEmpty();
+                .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
         verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
     }
 
@@ -170,7 +200,7 @@
         mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
                 BINDER, new Point(WIDTH, HEIGHT));
         assertWithMessage("Virtual keyboard should register fd when the display matches")
-                .that(mInputController.mInputDeviceFds).isNotEmpty();
+                .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
         verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
                 WIDTH);
     }
@@ -190,7 +220,9 @@
         final int fd = 1;
         final int keyCode = KeyEvent.KEYCODE_A;
         final int action = VirtualKeyEvent.ACTION_UP;
-        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1,
+                        /* displayId= */ 1));
         mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
                 .setAction(action).build());
         verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
@@ -213,7 +245,10 @@
         final int fd = 1;
         final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
         final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
-        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+                        /* displayId= */ 1));
+        mInputController.mActivePointerDisplayId = 1;
         mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
                 .setButtonCode(buttonCode)
                 .setAction(action).build());
@@ -221,6 +256,22 @@
     }
 
     @Test
+    public void sendButtonEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
+        final int fd = 1;
+        final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
+        final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+                        /* displayId= */ 1));
+        assertThrows(
+                IllegalStateException.class,
+                () ->
+                        mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
+                                .setButtonCode(buttonCode)
+                                .setAction(action).build()));
+    }
+
+    @Test
     public void sendRelativeEvent_noFd() {
         assertThrows(
                 IllegalArgumentException.class,
@@ -235,13 +286,32 @@
         final int fd = 1;
         final float x = -0.2f;
         final float y = 0.7f;
-        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+                        /* displayId= */ 1));
+        mInputController.mActivePointerDisplayId = 1;
         mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
                 .setRelativeX(x).setRelativeY(y).build());
         verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
     }
 
     @Test
+    public void sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
+        final int fd = 1;
+        final float x = -0.2f;
+        final float y = 0.7f;
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+                        /* displayId= */ 1));
+        assertThrows(
+                IllegalStateException.class,
+                () ->
+                        mDeviceImpl.sendRelativeEvent(BINDER,
+                                new VirtualMouseRelativeEvent.Builder()
+                                        .setRelativeX(x).setRelativeY(y).build()));
+    }
+
+    @Test
     public void sendScrollEvent_noFd() {
         assertThrows(
                 IllegalArgumentException.class,
@@ -257,7 +327,10 @@
         final int fd = 1;
         final float x = 0.5f;
         final float y = 1f;
-        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+                        /* displayId= */ 1));
+        mInputController.mActivePointerDisplayId = 1;
         mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
                 .setXAxisMovement(x)
                 .setYAxisMovement(y).build());
@@ -265,6 +338,22 @@
     }
 
     @Test
+    public void sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
+        final int fd = 1;
+        final float x = 0.5f;
+        final float y = 1f;
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+                        /* displayId= */ 1));
+        assertThrows(
+                IllegalStateException.class,
+                () ->
+                        mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
+                                .setXAxisMovement(x)
+                                .setYAxisMovement(y).build()));
+    }
+
+    @Test
     public void sendTouchEvent_noFd() {
         assertThrows(
                 IllegalArgumentException.class,
@@ -286,7 +375,9 @@
         final float x = 100.5f;
         final float y = 200.5f;
         final int action = VirtualTouchEvent.ACTION_UP;
-        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
+                        /* displayId= */ 1));
         mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
                 .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
         verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
@@ -303,7 +394,9 @@
         final int action = VirtualTouchEvent.ACTION_UP;
         final float pressure = 1.0f;
         final float majorAxisSize = 10.0f;
-        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mInputController.mInputDeviceDescriptors.put(BINDER,
+                new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
+                        /* displayId= */ 1));
         mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
                 .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
                 .setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
new file mode 100644
index 0000000..77f1e24
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.companion.virtual.VirtualDeviceParams;
+import android.os.Parcel;
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualDeviceParamsTest {
+
+    @Test
+    public void parcelable_shouldRecreateSuccessfully() {
+        VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder()
+                .setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED)
+                .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456)))
+                .build();
+        Parcel parcel = Parcel.obtain();
+        originalParams.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        VirtualDeviceParams params = VirtualDeviceParams.CREATOR.createFromParcel(parcel);
+        assertThat(params).isEqualTo(originalParams);
+        assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED);
+        assertThat(params.getUsersWithMatchingAccounts())
+                .containsExactly(UserHandle.of(123), UserHandle.of(456));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 3c809f9..842a438 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -18,7 +18,6 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_ACTIVATE_VPN;
-import static android.app.Notification.EXTRA_TEXT;
 import static android.app.Notification.EXTRA_TITLE;
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
 import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
@@ -34,10 +33,17 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
 import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -89,6 +95,7 @@
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.WifiSsidPolicy;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -100,7 +107,7 @@
 import android.content.pm.UserInfo;
 import android.graphics.Color;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
+import android.net.ProfileNetworkPreference;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
@@ -1110,6 +1117,10 @@
                 eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
                 eq(true), eq(UserHandle.SYSTEM));
 
+        verify(getServices().userManager, times(1)).setUserRestriction(
+                eq(UserManager.DISALLOW_ADD_CLONE_PROFILE),
+                eq(true), eq(UserHandle.SYSTEM));
+
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
                 MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -1391,6 +1402,10 @@
                 eq(false),
                 MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
 
+        verify(getServices().userManager)
+                .setUserRestriction(eq(UserManager.DISALLOW_ADD_CLONE_PROFILE), eq(false),
+                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(),
                 MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
@@ -4058,12 +4073,15 @@
         mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         dpms.handleStartUser(managedProfileUserId);
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                anyInt(),
-                any(),
-                any()
-        );
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                null, null);
     }
 
     @Test
@@ -4075,12 +4093,15 @@
         mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         dpms.handleStopUser(managedProfileUserId);
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
-                any(),
-                any()
-        );
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
     }
 
     @Test
@@ -4098,21 +4119,30 @@
 
         dpm.setPreferentialNetworkServiceEnabled(false);
         assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
-                any(),
-                any()
-        );
+
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
 
         dpm.setPreferentialNetworkServiceEnabled(true);
         assertThat(dpm.isPreferentialNetworkServiceEnabled()).isTrue();
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE),
-                any(),
-                any()
-        );
+
+        ProfileNetworkPreference preferenceDetails2 =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+                        .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+                        .build();
+        List<ProfileNetworkPreference> preferences2 = new ArrayList<>();
+        preferences2.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences2,
+                        null, null);
     }
 
     @Test
@@ -7193,8 +7223,7 @@
         verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
         // Now the user should see a warning notification.
         verify(getServices().notificationManager, times(1))
-                .notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
-                        EXTRA_TEXT, PROFILE_OFF_SUSPENSION_SOON_TEXT)));
+                .notify(anyInt(), any());
         // Apps shouldn't be suspended yet.
         verifyZeroInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
@@ -7208,8 +7237,7 @@
         verifyZeroInteractions(getServices().alarmManager);
         // Now the user should see a notification about suspended apps.
         verify(getServices().notificationManager, times(1))
-                .notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
-                        EXTRA_TEXT, PROFILE_OFF_SUSPENSION_TEXT)));
+                .notify(anyInt(), any());
         // Verify that the apps are suspended.
         verify(getServices().ipackageManager, times(1)).setPackagesSuspendedAsUser(
                 any(), eq(true), any(), any(), any(), any(), anyInt());
@@ -7812,6 +7840,128 @@
                 () -> dpm.getOrganizationNameForUser(UserHandle.USER_SYSTEM));
     }
 
+    @Test
+    public void testSetWifiMinimumSecurity_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+        assertThrows(SecurityException.class, () -> dpm.setMinimumRequiredWifiSecurityLevel(
+                DevicePolicyManager.WIFI_SECURITY_PERSONAL));
+    }
+
+    @Test
+    public void testSetWifiMinimumSecurity_asDeviceOwner() throws Exception {
+        setDeviceOwner();
+
+        final Set<Integer> allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL,
+                WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192);
+        for (int level : allowedLevels) {
+            dpm.setMinimumRequiredWifiSecurityLevel(level);
+            assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level);
+        }
+    }
+
+    @Test
+    public void testSetWifiMinimumSecurity_asPoOfOrgOwnedDevice() throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        final Set<Integer> allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL,
+                WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192);
+        for (int level : allowedLevels) {
+            dpm.setMinimumRequiredWifiSecurityLevel(level);
+            assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level);
+        }
+    }
+
+    @Test
+    public void testSetSsidAllowlist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+        final Set<String> ssids = Collections.singleton("ssid1");
+        WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+        assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
+    }
+
+    @Test
+    public void testSetSsidAllowlist_asDeviceOwner() throws Exception {
+        setDeviceOwner();
+
+        final Set<String> ssids = Collections.singleton("ssid1");
+        WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+        dpm.setWifiSsidPolicy(policy);
+        assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+        assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+                WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST);
+    }
+
+    @Test
+    public void testSetSsidAllowlist_asPoOfOrgOwnedDevice() throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
+        WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+        dpm.setWifiSsidPolicy(policy);
+        assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+        assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+                WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST);
+    }
+
+    @Test
+    public void testSetSsidAllowlist_emptyList() throws Exception {
+        setDeviceOwner();
+
+        final Set<String> ssids = new ArraySet<>();
+        assertThrows(IllegalArgumentException.class,
+                () -> WifiSsidPolicy.createAllowlistPolicy(ssids));
+    }
+
+    @Test
+    public void testSetSsidDenylist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+        final Set<String> ssids = Collections.singleton("ssid1");
+        WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+        assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
+    }
+
+    @Test
+    public void testSetSsidDenylist_asDeviceOwner() throws Exception {
+        setDeviceOwner();
+
+        final Set<String> ssids = Collections.singleton("ssid1");
+        WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+        dpm.setWifiSsidPolicy(policy);
+        assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+        assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+                WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST);
+    }
+
+    @Test
+    public void testSetSsidDenylist_asPoOfOrgOwnedDevice() throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
+        WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+        dpm.setWifiSsidPolicy(policy);
+        assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+        assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+                WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST);
+    }
+
+    @Test
+    public void testSetSsidDenylist_emptyList() throws Exception {
+        setDeviceOwner();
+
+        final Set<String> ssids = new ArraySet<>();
+        assertThrows(IllegalArgumentException.class,
+                () -> WifiSsidPolicy.createDenylistPolicy(ssids));
+    }
+
     private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
         final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
                 userVpnUid, List.of(new AppOpsManager.OpEntry(
@@ -7847,18 +7997,6 @@
         // To allow creation of Notification via Notification.Builder
         mContext.applicationInfo = mRealTestContext.getApplicationInfo();
 
-        // Setup resources to render notification titles and texts.
-        when(mServiceContext.resources
-                .getString(R.string.personal_apps_suspension_title))
-                .thenReturn(PROFILE_OFF_SUSPENSION_TITLE);
-        when(mServiceContext.resources
-                .getString(R.string.personal_apps_suspension_text))
-                .thenReturn(PROFILE_OFF_SUSPENSION_TEXT);
-        when(mServiceContext.resources
-                .getString(eq(R.string.personal_apps_suspension_soon_text),
-                        anyString(), anyString(), anyInt()))
-                .thenReturn(PROFILE_OFF_SUSPENSION_SOON_TEXT);
-
         // Make locale available for date formatting:
         when(mServiceContext.resources.getConfiguration())
                 .thenReturn(mRealTestContext.getResources().getConfiguration());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index d4b1165..6eb2085 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -232,6 +232,8 @@
                 return mMockSystemServices.crossProfileApps;
             case Context.VPN_MANAGEMENT_SERVICE:
                 return mMockSystemServices.vpnManager;
+            case Context.DEVICE_POLICY_SERVICE:
+                return mMockSystemServices.devicePolicyManager;
         }
         throw new UnsupportedOperationException();
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 8a2919d..597a165 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -31,6 +31,7 @@
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
 import android.app.backup.IBackupManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
@@ -125,6 +126,7 @@
     public final AppOpsManager appOpsManager;
     public final UsbManager usbManager;
     public final VpnManager vpnManager;
+    public final DevicePolicyManager devicePolicyManager;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
     public final BuildMock buildMock = new BuildMock();
@@ -172,6 +174,7 @@
         appOpsManager = mock(AppOpsManager.class);
         usbManager = mock(UsbManager.class);
         vpnManager = mock(VpnManager.class);
+        devicePolicyManager = mock(DevicePolicyManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
index e286cb2..d54524e 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
@@ -41,10 +41,10 @@
     @Test
     public void testConstruct() {
         final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
-                "CLOSED" /* name */, DeviceState.FLAG_CANCEL_STICKY_REQUESTS /* flags */);
+                "TEST_CLOSED" /* name */, DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS /* flags */);
         assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
-        assertEquals(state.getName(), "CLOSED");
-        assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS);
+        assertEquals(state.getName(), "TEST_CLOSED");
+        assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index c9cf2f0..b94fc43 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -213,6 +213,25 @@
         assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
     }
 
+    @Test
+    public void cancelOverrideRequestsTest() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                1 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                2 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.cancelOverrideRequests();
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+    }
+
     private static final class TestStatusChangeListener implements
             OverrideRequestController.StatusChangeListener {
         private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>();
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index abe7d89..54945e4 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyFloat;
@@ -32,18 +35,22 @@
 import android.hardware.SensorManager;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
+import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.testutils.OffsettableClock;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
@@ -59,7 +66,11 @@
     private static final float DOZE_SCALE_FACTOR = 0.0f;
     private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
-
+    private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000;
+    private static final int AMBIENT_LIGHT_HORIZON_LONG = 2000;
+    private static final float EPSILON = 0.001f;
+    private OffsettableClock mClock = new OffsettableClock();
+    private TestLooper mTestLooper;
     private Context mContext;
     private AutomaticBrightnessController mController;
 
@@ -89,21 +100,36 @@
         }
     }
 
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
     private AutomaticBrightnessController setupController(Sensor lightSensor) {
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+
         AutomaticBrightnessController controller = new AutomaticBrightnessController(
                 new AutomaticBrightnessController.Injector() {
                     @Override
                     public Handler getBackgroundThreadHandler() {
                         return mNoOpHandler;
                     }
-                },
-                () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
+
+                    @Override
+                    AutomaticBrightnessController.Clock createClock() {
+                        return mClock::now;
+                    }
+
+                }, // pass in test looper instead, pass in offsetable clock
+                () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
                 mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
-                mContext, mHbmController, mIdleBrightnessMappingStrategy
+                mContext, mHbmController, mIdleBrightnessMappingStrategy,
+                AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
         );
 
         when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -111,7 +137,7 @@
 
         // Configure the brightness controller and grab an instance of the sensor listener,
         // through which we can deliver fake (for test) sensor values.
-        controller.configure(true /* enable */, null /* configuration */,
+        controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
@@ -227,7 +253,7 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
 
         // User sets brightness to 100
-        mController.configure(true /* enable */, null /* configuration */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
@@ -250,7 +276,7 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
 
         // User sets brightness to 100
-        mController.configure(true /* enable */, null /* configuration */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
@@ -267,11 +293,102 @@
         verifyNoMoreInteractions(mBrightnessMappingStrategy);
 
         // User sets idle brightness to 0.5
-        mController.configure(true /* enable */, null /* configuration */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
         // Ensure we use the correct mapping strategy
         verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
     }
+
+    @Test
+    public void testAmbientLightHorizon() throws Exception {
+        // create abc
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        mController = setupController(lightSensor);
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        long increment = 500;
+        // set autobrightness to low
+        // t = 0
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+
+        // t = 500
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+
+        // t = 1000
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 1500
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 2000
+        // ensure that our reading is at 0.
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 2500
+        // first 10000 lux sensor event reading
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 3000
+        // lux reading should still not yet be 10000.
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 3500
+        mClock.fastForward(increment);
+        // lux has been high (10000) for 1000ms.
+        // lux reading should be 10000
+        // short horizon (ambient lux) is high, long horizon is still not high
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 4000
+        // stay high
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 4500
+        Mockito.clearInvocations(mBrightnessMappingStrategy);
+        mClock.fastForward(increment);
+        // short horizon is high, long horizon is high too
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1);
+        assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 5000
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 5500
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 6000
+        mClock.fastForward(increment);
+        // ambient lux goes to 0
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index aca8632..4bb5d74 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -20,10 +20,17 @@
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+import static com.android.server.display.AutomaticBrightnessController
+                                                      .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
+
 import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -47,6 +54,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
@@ -87,6 +95,7 @@
     private TestLooper mTestLooper;
     private Handler mHandler;
     private Binder mDisplayToken;
+    private String mDisplayUniqueId;
     private Context mContextSpy;
 
     @Rule
@@ -108,6 +117,7 @@
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
         mDisplayToken = null;
+        mDisplayUniqueId = "unique_id";
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -123,8 +133,8 @@
     public void testNoHbmData() {
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
-                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
-                DEFAULT_MAX, null, () -> {}, mContextSpy);
+                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
         assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
     }
@@ -133,9 +143,9 @@
     public void testNoHbmData_Enabled() {
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
-                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
-                DEFAULT_MAX, null, () -> {}, mContextSpy);
-        hbmc.setAutoBrightnessEnabled(true);
+                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
         assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
@@ -152,7 +162,7 @@
     public void testAutoBrightnessEnabled_NoLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
     }
 
@@ -160,7 +170,7 @@
     public void testAutoBrightnessEnabled_LowLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
     }
@@ -169,7 +179,7 @@
     public void testAutoBrightnessEnabled_HighLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
     }
@@ -178,9 +188,9 @@
     public void testAutoBrightnessEnabled_HighLux_ThenDisable() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.setAutoBrightnessEnabled(false);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_DISABLED);
 
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
     }
@@ -189,7 +199,7 @@
     public void testWithinHighRange_thenOverTime_thenEarnBackTime() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
 
@@ -221,7 +231,7 @@
     public void testInHBM_ThenLowLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
 
@@ -245,7 +255,7 @@
     public void testInHBM_TestMultipleEvents_DueToAutoBrightness() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
 
         hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
@@ -274,7 +284,7 @@
     public void testInHBM_TestMultipleEvents_DueToLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
 
         // Go into HBM for half the allowed window
@@ -316,7 +326,7 @@
         listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
 
         // Try to go into HBM mode but fail
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         advanceTime(10);
 
@@ -335,7 +345,7 @@
         listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT));
 
         // Try to go into HBM mode
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         advanceTime(1);
 
@@ -378,7 +388,7 @@
         final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
 
         // Turn on sunlight
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         advanceTime(0);
         assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
@@ -451,6 +461,155 @@
         assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
     }
 
+    @Test
+    public void testHbmStats_StateChange() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onBrightnessChanged(TRANSITION_POINT);
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+        advanceTime(0);
+        assertEquals(HIGH_BRIGHTNESS_MODE_HDR, hbmc.getHighBrightnessMode());
+
+        // Verify Stats HBM_ON_HDR
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 0 /*numberOfHdrLayers*/,
+                0, 0, 0 /*flags*/);
+        advanceTime(0);
+
+        // Verify Stats HBM_OFF
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+
+        // Verify Stats HBM_ON_SUNLIGHT
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.onAmbientLuxChange(1);
+        advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1);
+
+        // Verify Stats HBM_OFF
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
+    }
+
+    @Test
+    public void tetHbmStats_NbmHdrNoReport() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onBrightnessChanged(DEFAULT_MIN);
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+        advanceTime(0);
+        assertEquals(HIGH_BRIGHTNESS_MODE_HDR, hbmc.getHighBrightnessMode());
+
+        // Verify Stats HBM_ON_HDR not report
+        verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+            anyInt());
+    }
+
+    @Test
+    public void testHbmStats_ThermalOff() throws Exception {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        verify(mThermalServiceMock).registerThermalEventListenerWithType(
+                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+        final IThermalEventListener thermListener = mThermalEventListenerCaptor.getValue();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(1);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        thermListener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+        advanceTime(10);
+        assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+    }
+
+    @Test
+    public void testHbmStats_TimeOut() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(0);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        // Use up all the time in the window.
+        advanceTime(TIME_WINDOW_MILLIS + 1);
+
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
+    }
+
+    @Test
+    public void testHbmStats_DisplayOff() {
+        final HighBrightnessModeController hbmc = createDefaultHbm();
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(0);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
+    }
+
+    @Test
+    public void testHbmStats_HdrPlaying() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(0);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+        advanceTime(0);
+
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
+    }
+
     private void assertState(HighBrightnessModeController hbmc,
             float brightnessMin, float brightnessMax, int hbmMode) {
         assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON);
@@ -466,8 +625,8 @@
     private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
         initHandler(clock);
         return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
-                DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {},
-                mContextSpy);
+                DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
+                DEFAULT_HBM_DATA, () -> {}, mContextSpy);
     }
 
     private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index b588db6..18f2642 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -92,7 +92,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         mHdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index ff01cb1..e4c5ad67 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -109,7 +109,6 @@
                 hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
         hdmiControlService.setCecController(hdmiCecController);
         hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
-        hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
         hdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         hdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index a44a5cde..d73cdb5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -102,7 +102,6 @@
                 hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
         hdmiControlService.setCecController(hdmiCecController);
         hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
-        hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
         hdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         hdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 9c99240..5cec8ad 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -53,7 +53,7 @@
 
     @Before
     public void SetUp() {
-        mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+        mDeviceInfoForTests = HdmiDeviceInfo.hardwarePort(1001, 1234);
         HdmiControlService hdmiControlService =
                 new HdmiControlService(InstrumentationRegistry.getTargetContext(),
                         Collections.emptyList()) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 638b386..52a0b6c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -109,7 +109,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         mHdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index 41231e0..35432ed 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -128,10 +128,8 @@
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
         mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
                 mHdmiCecController, mHdmiMhlControllerStub);
         mHdmiControlService.setHdmiCecNetwork(mHdmiCecNetwork);
@@ -156,13 +154,13 @@
         mPlaybackLogicalAddress3 = mPlaybackLogicalAddress1 == ADDR_PLAYBACK_3
                 ? ADDR_PLAYBACK_1 : ADDR_PLAYBACK_3;
 
-        mReportPowerStatusOn = new HdmiCecMessage(
+        mReportPowerStatusOn = HdmiCecMessage.build(
                 mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
                 Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
-        mReportPowerStatusStandby = new HdmiCecMessage(
+        mReportPowerStatusStandby = HdmiCecMessage.build(
                 mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
                 Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
-        mReportPowerStatusTransientToOn = new HdmiCecMessage(
+        mReportPowerStatusTransientToOn = HdmiCecMessage.build(
                 mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
                 Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON);
         mSetStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
@@ -173,21 +171,36 @@
         mActiveSource = HdmiCecMessageBuilder.buildActiveSource(
                 mPlaybackLogicalAddress2, PHYSICAL_ADDRESS_PLAYBACK_2);
 
-        HdmiDeviceInfo infoPlayback1 = new HdmiDeviceInfo(
-                mPlaybackLogicalAddress1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1,
-                HdmiDeviceInfo.DEVICE_PLAYBACK,
-                0x1234, "Playback 1",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        HdmiDeviceInfo infoPlayback2 = new HdmiDeviceInfo(
-                mPlaybackLogicalAddress2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2,
-                HdmiDeviceInfo.DEVICE_PLAYBACK,
-                0x1234, "Playback 2",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        HdmiDeviceInfo infoPlayback3 = new HdmiDeviceInfo(
-                mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3, PORT_3,
-                HdmiDeviceInfo.DEVICE_PLAYBACK,
-                0x1234, "Playback 3",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+        HdmiDeviceInfo infoPlayback1 = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mPlaybackLogicalAddress1)
+                .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_1)
+                .setPortId(PORT_1)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 1")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
+        HdmiDeviceInfo infoPlayback2 = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mPlaybackLogicalAddress2)
+                .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_2)
+                .setPortId(PORT_2)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 2")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
+        HdmiDeviceInfo infoPlayback3 = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mPlaybackLogicalAddress3)
+                .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_3)
+                .setPortId(PORT_3)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 3")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
 
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback1);
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback2);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index dd74864..e77cd91 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -64,22 +64,34 @@
     private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON };
     private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY };
     private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON };
-    private static final HdmiCecMessage REPORT_POWER_STATUS_ON = new HdmiCecMessage(
+    private static final HdmiCecMessage REPORT_POWER_STATUS_ON = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
-    private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage(
+    private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
-    private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage(
+    private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON);
     private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath(
                         ADDR_TV, PHYSICAL_ADDRESS_PLAYBACK_1);
-    private static final HdmiDeviceInfo INFO_PLAYBACK_1 = new HdmiDeviceInfo(
-            ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1, HdmiDeviceInfo.DEVICE_PLAYBACK,
-            0x1234, "Playback 1",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-    private static final HdmiDeviceInfo INFO_PLAYBACK_2 = new HdmiDeviceInfo(
-            ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2, HdmiDeviceInfo.DEVICE_PLAYBACK,
-            0x1234, "Playback 2",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+    private static final HdmiDeviceInfo INFO_PLAYBACK_1 = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_PLAYBACK_1)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_1)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+            .setVendorId(0x1234)
+            .setDisplayName("Plyback 1")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
+    private static final HdmiDeviceInfo INFO_PLAYBACK_2 = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_PLAYBACK_2)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_2)
+            .setPortId(PORT_2)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+            .setVendorId(0x1234)
+            .setDisplayName("Playback 2")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
 
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
@@ -123,7 +135,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index d630ef6..559a2c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.Iterables;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -77,7 +78,8 @@
         if (body.length == 0) {
             return mPollAddressResponse[dstAddress];
         } else {
-            HdmiCecMessage message = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+            HdmiCecMessage message = HdmiCecMessage.build(srcAddress, dstAddress, body[0],
+                    Arrays.copyOfRange(body, 1, body.length));
             mResultMessages.add(message);
             return mMessageSendResult.getOrDefault(message.getOpcode(), SendMessageResult.SUCCESS);
         }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java
new file mode 100644
index 0000000..0b31db6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_VERSION_2_0;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class GiveFeaturesActionTest {
+    private HdmiControlService mHdmiControlServiceSpy;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDevicePlayback mPlaybackDevice;
+    private FakeNativeWrapper mNativeWrapper;
+    private FakePowerManagerWrapper mPowerManager;
+    private Looper mLooper;
+    private Context mContextSpy;
+    private TestLooper mTestLooper = new TestLooper();
+    private int mPhysicalAddress = 0x1100;
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mPlaybackLogicalAddress;
+
+    private TestCallback mTestCallback;
+    private GiveFeaturesAction mAction;
+
+    /**
+     * Setup: Local Playback device queries the features of a connected TV.
+     */
+    @Before
+    public void setUp() throws RemoteException {
+        mContextSpy = spy(new ContextWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext()));
+
+        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
+        doNothing().when(mHdmiControlServiceSpy)
+                .writeStringSystemProperty(anyString(), anyString());
+
+        mLooper = mTestLooper.getLooper();
+        mHdmiControlServiceSpy.setIoLooper(mLooper);
+        mHdmiControlServiceSpy.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+
+        mNativeWrapper = new FakeNativeWrapper();
+        mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+
+        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlServiceSpy, mNativeWrapper, mHdmiControlServiceSpy.getAtomWriter());
+        mHdmiControlServiceSpy.setCecController(mHdmiCecController);
+        mHdmiControlServiceSpy.setHdmiMhlController(
+                HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
+        mHdmiControlServiceSpy.initService();
+        mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+        mHdmiControlServiceSpy.setPowerManager(mPowerManager);
+
+        mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy);
+        mPlaybackDevice.init();
+        mLocalDevices.add(mPlaybackDevice);
+
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        mTestLooper.dispatchAll();
+
+        synchronized (mPlaybackDevice.mLock) {
+            mPlaybackLogicalAddress = mPlaybackDevice.getDeviceInfo().getLogicalAddress();
+        }
+
+        // Setup specific to these tests
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                Constants.ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV));
+        mTestLooper.dispatchAll();
+
+        mTestCallback = new TestCallback();
+        mAction = new GiveFeaturesAction(mPlaybackDevice, Constants.ADDR_TV, mTestCallback);
+    }
+
+    @Test
+    public void sendsGiveFeaturesMessage() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveFeatures = HdmiCecMessageBuilder.buildGiveFeatures(
+                mPlaybackLogicalAddress, Constants.ADDR_TV);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveFeatures);
+    }
+
+    @Test
+    public void noMatchingReportFeaturesReceived_actionFailsAndNetworkIsNotUpdated() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        // Wrong source
+        mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
+                Constants.ADDR_AUDIO_SYSTEM, HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Arrays.asList(DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_SOURCE,
+                Collections.emptyList(), DeviceFeatures.NO_FEATURES_SUPPORTED));
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
+        assertThat(mTestCallback.getResult()).isEqualTo(
+                HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+    }
+
+    @Test
+    public void matchingReportFeaturesReceived_actionSucceedsAndNetworkIsUpdated() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.onCecMessage(
+                ReportFeaturesMessage.build(
+                        Constants.ADDR_TV, HDMI_CEC_VERSION_2_0, Collections.emptyList(),
+                        Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                        DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)
+                                .build()
+                )
+        );
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    private static class TestCallback extends IHdmiControlCallback.Stub {
+        private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+        @Override
+        public void onComplete(int result) {
+            mCallbackResult.add(result);
+        }
+
+        private int getResult() {
+            assertThat(mCallbackResult.size()).isEqualTo(1);
+            return mCallbackResult.get(0);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index f30e97a..30bcc7e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -98,8 +98,6 @@
         doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
 
         mHdmiControlServiceSpy.setIoLooper(mLooper);
-        mHdmiControlServiceSpy.setMessageValidator(
-                new HdmiCecMessageValidator(mHdmiControlServiceSpy));
         mHdmiControlServiceSpy.setCecMessageBuffer(
                 new CecMessageBuffer(mHdmiControlServiceSpy));
 
@@ -226,7 +224,7 @@
 
     @Test
     public void testMessageReported_writesAtom_userControlPressed_noParams() {
-        HdmiCecMessage message = new HdmiCecMessage(
+        HdmiCecMessage message = HdmiCecMessage.build(
                 Constants.ADDR_TV,
                 Constants.ADDR_PLAYBACK_1,
                 Constants.MESSAGE_USER_CONTROL_PRESSED,
@@ -279,7 +277,7 @@
 
     @Test
     public void testMessageReported_writesAtom_featureAbort_noParams() {
-        HdmiCecMessage message = new HdmiCecMessage(
+        HdmiCecMessage message = HdmiCecMessage.build(
                 Constants.ADDR_TV,
                 Constants.ADDR_PLAYBACK_1,
                 Constants.MESSAGE_FEATURE_ABORT,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index a411392..70bc460 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -188,7 +188,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
         mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 2d13e69..6fc3354 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -59,10 +59,16 @@
             HotplugDetectionAction.POLLING_INTERVAL_MS_FOR_PLAYBACK;
 
     private static final int PORT_1 = 1;
-    private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
-            ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
-            0x1234, "TV",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+    private static final HdmiDeviceInfo INFO_TV = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_TV)
+            .setPhysicalAddress(0x0000)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_TV)
+            .setVendorId(0x1234)
+            .setDisplayName("TV")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
 
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
@@ -138,7 +144,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
@@ -1691,10 +1696,16 @@
     public void hotplugDetectionAction_removeDevice() {
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
-        HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo(
-                Constants.ADDR_PLAYBACK_2, 0x1234, PORT_1,
-                HdmiDeviceInfo.DEVICE_PLAYBACK, 0x1234, "Playback 2",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+        HdmiDeviceInfo infoPlayback = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_2)
+                .setPhysicalAddress(0x1234)
+                .setPortId(PORT_1)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 2")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback);
         // This logical address (ADDR_PLAYBACK_2) won't acknowledge the poll message sent by the
         // HotplugDetectionAction so it shall be removed.
@@ -1726,8 +1737,15 @@
 
     @Test
     public void getActiveSource_deviceInNetworkIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x3000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x3000)
+                .setPortId(0)
+                .setDeviceType(Constants.ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
+
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(externalDevice);
         mTestLooper.dispatchAll();
 
@@ -1739,8 +1757,14 @@
 
     @Test
     public void getActiveSource_unknownDeviceIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x3000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x3000)
+                .setPortId(0)
+                .setDeviceType(Constants.ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
 
         mHdmiControlService.setActiveSource(externalDevice.getLogicalAddress(),
                 externalDevice.getPhysicalAddress(), "HdmiControlServiceTest");
@@ -1933,7 +1957,7 @@
 
     @Test
     public void doesNotSupportRecordTvScreen() {
-        HdmiCecMessage recordTvScreen = new HdmiCecMessage(ADDR_TV, mPlaybackLogicalAddress,
+        HdmiCecMessage recordTvScreen = HdmiCecMessage.build(ADDR_TV, mPlaybackLogicalAddress,
                 Constants.MESSAGE_RECORD_TV_SCREEN, HdmiCecMessage.EMPTY_PARAM);
 
         mNativeWrapper.onCecMessage(recordTvScreen);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index f5af6df..fb8baa3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -109,11 +109,6 @@
         protected List<Integer> getRcFeatures() {
             return Collections.emptyList();
         }
-
-        @Override
-        protected List<Integer> getDeviceFeatures() {
-            return Collections.emptyList();
-        }
     }
 
     private MyHdmiCecLocalDevice mHdmiLocalDevice;
@@ -187,14 +182,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
-        mMessageValidator =
-                new HdmiCecMessageValidator(mHdmiControlService) {
-                    @Override
-                    int isValid(HdmiCecMessage message, boolean isMessageReceived) {
-                        return HdmiCecMessageValidator.OK;
-                    }
-                };
-        mHdmiControlService.setMessageValidator(mMessageValidator);
 
         mLocalDevices.add(mHdmiLocalDevice);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
@@ -230,7 +217,7 @@
     @Test
     public void dispatchMessage_logicalAddressDoesNotMatch() {
         HdmiCecMessage msg =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         ADDR_PLAYBACK_1,
                         Constants.MESSAGE_CEC_VERSION,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index a260a6d..b6c4bc2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -121,7 +121,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
@@ -172,8 +171,14 @@
 
     @Test
     public void getActiveSource_deviceInNetworkIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x1000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x3000)
+                .setPortId(0)
+                .setDeviceType(Constants.ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(externalDevice);
         mTestLooper.dispatchAll();
 
@@ -185,7 +190,7 @@
 
     @Test
     public void getActiveSource_unknownLogicalAddressInNetworkIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(0x1000, 1);
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.hardwarePort(0x1000, 1);
 
         mHdmiControlService.setActiveSource(Constants.ADDR_UNREGISTERED,
                 externalDevice.getPhysicalAddress(), "HdmiControlServiceTest");
@@ -197,8 +202,14 @@
 
     @Test
     public void getActiveSource_unknownDeviceIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x1000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x0000)
+                .setPortId(0)
+                .setDeviceType(ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
 
         mHdmiControlService.setActiveSource(externalDevice.getLogicalAddress(),
                 externalDevice.getPhysicalAddress(), "HdmiControlServiceTest");
@@ -240,7 +251,7 @@
                 HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
         mTestLooper.dispatchAll();
         mPowerManager.setInteractive(false);
-        HdmiCecMessage imageViewOn = new HdmiCecMessage(ADDR_PLAYBACK_1, mTvLogicalAddress,
+        HdmiCecMessage imageViewOn = HdmiCecMessage.build(ADDR_PLAYBACK_1, mTvLogicalAddress,
                 Constants.MESSAGE_IMAGE_VIEW_ON, HdmiCecMessage.EMPTY_PARAM);
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(imageViewOn)).isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
@@ -268,7 +279,7 @@
                 HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED);
         mTestLooper.dispatchAll();
         mPowerManager.setInteractive(false);
-        HdmiCecMessage imageViewOn = new HdmiCecMessage(ADDR_PLAYBACK_1, mTvLogicalAddress,
+        HdmiCecMessage imageViewOn = HdmiCecMessage.build(ADDR_PLAYBACK_1, mTvLogicalAddress,
                 Constants.MESSAGE_IMAGE_VIEW_ON, HdmiCecMessage.EMPTY_PARAM);
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(imageViewOn)).isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
@@ -478,7 +489,7 @@
 
     @Test
     public void supportsRecordTvScreen() {
-        HdmiCecMessage recordTvScreen = new HdmiCecMessage(ADDR_RECORDER_1, mTvLogicalAddress,
+        HdmiCecMessage recordTvScreen = HdmiCecMessage.build(ADDR_RECORDER_1, mTvLogicalAddress,
                 Constants.MESSAGE_RECORD_TV_SCREEN, HdmiCecMessage.EMPTY_PARAM);
 
         mNativeWrapper.onCecMessage(recordTvScreen);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 453303e..f869462f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -22,20 +22,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
-import com.google.android.collect.Lists;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.Collections;
-
 @SmallTest
 @Presubmit
 @RunWith(JUnit4.class)
@@ -100,89 +95,4 @@
 
         assertThat(message).isEqualTo(buildMessage("40:A5"));
     }
-
-    @Test
-    public void buildReportFeatures_basicTv_1_4() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:05:80:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_basicPlayback_1_4() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("4F:A6:05:10:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_basicPlaybackAudioSystem_1_4() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
-                        HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("4F:A6:05:18:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_basicTv_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_remoteControlTv_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:02:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_remoteControlPlayback_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
-                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
-                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_deviceFeaturesTv_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
-                Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN));
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:40"));
-    }
-
-    @Test
-    public void buildReportFeatures_deviceFeaturesPlayback_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
-                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
-                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
-                Lists.newArrayList(Constants.DEVICE_FEATURE_SUPPORTS_DECK_CONTROL));
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:10"));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
index cca5094..2984cfa 100755
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
@@ -41,12 +41,12 @@
 
         new EqualsTester()
                 .addEqualityGroup(
-                        new HdmiCecMessage(source, destination, opcode, params1),
-                        new HdmiCecMessage(source, destination, opcode, params1))
-                .addEqualityGroup(new HdmiCecMessage(source, destination, opcode, params2))
-                .addEqualityGroup(new HdmiCecMessage(source + 1, destination, opcode, params1))
-                .addEqualityGroup(new HdmiCecMessage(source, destination + 1, opcode, params1))
-                .addEqualityGroup(new HdmiCecMessage(source, destination, opcode + 1, params1))
+                        HdmiCecMessage.build(source, destination, opcode, params1),
+                        HdmiCecMessage.build(source, destination, opcode, params1))
+                .addEqualityGroup(HdmiCecMessage.build(source, destination, opcode, params2))
+                .addEqualityGroup(HdmiCecMessage.build(source + 1, destination, opcode, params1))
+                .addEqualityGroup(HdmiCecMessage.build(source, destination + 1, opcode, params1))
+                .addEqualityGroup(HdmiCecMessage.build(source, destination, opcode + 1, params1))
                 .testEquals();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 548a439..50c9f70 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -54,7 +54,6 @@
                 InstrumentationRegistry.getTargetContext(), Collections.emptyList());
 
         mHdmiControlService.setIoLooper(mTestLooper.getLooper());
-        mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
     }
 
     @Test
@@ -400,16 +399,6 @@
     }
 
     @Test
-    public void isValid_reportFeatures() {
-        assertMessageValidity("0F:A6:05:80:00:00").isEqualTo(OK);
-
-        assertMessageValidity("04:A6:05:80:00:00").isEqualTo(ERROR_DESTINATION);
-        assertMessageValidity("FF:A6:05:80:00:00").isEqualTo(ERROR_SOURCE);
-
-        assertMessageValidity("0F:A6").isEqualTo(ERROR_PARAMETER_SHORT);
-    }
-
-    @Test
     public void isValid_deckControl() {
         assertMessageValidity("40:42:01:6E").isEqualTo(OK);
         assertMessageValidity("40:42:04").isEqualTo(OK);
@@ -649,6 +638,6 @@
     }
 
     private IntegerSubject assertMessageValidity(String message) {
-        return assertThat(mHdmiCecMessageValidator.isValid(HdmiUtils.buildMessage(message), false));
+        return assertThat(HdmiUtils.buildMessage(message).getValidationResult());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index 1048eb5..42fa32c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -17,9 +17,12 @@
 package com.android.server.hdmi;
 
 
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -80,7 +83,6 @@
         mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
 
         mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
                 mHdmiCecController, mHdmiMhlControllerStub);
@@ -178,7 +180,7 @@
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
                 HdmiControlManager.POWER_STATUS_UNKNOWN);
 
@@ -216,7 +218,7 @@
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
                 HdmiControlManager.POWER_STATUS_UNKNOWN);
     }
@@ -258,7 +260,7 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
                 Constants.INVALID_PHYSICAL_ADDRESS);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
@@ -279,7 +281,7 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
                 Constants.INVALID_PHYSICAL_ADDRESS);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
                 HdmiControlManager.POWER_STATUS_UNKNOWN);
@@ -471,7 +473,7 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
                 Constants.INVALID_PHYSICAL_ADDRESS);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
@@ -514,12 +516,14 @@
         int logicalAddress = Constants.ADDR_PLAYBACK_1;
         int cecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
         mHdmiCecNetwork.handleCecMessage(
-                HdmiCecMessageBuilder.buildReportFeatures(logicalAddress,
+                ReportFeaturesMessage.build(logicalAddress,
                         cecVersion, Collections.emptyList(),
                         Constants.RC_PROFILE_SOURCE, Collections.emptyList(),
-                        Collections.emptyList()));
+                        DeviceFeatures.NO_FEATURES_SUPPORTED));
 
-        assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        synchronized (mHdmiCecNetwork.mLock) {
+            assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        }
 
         HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
         assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
@@ -531,12 +535,14 @@
         int logicalAddress = Constants.ADDR_PLAYBACK_1;
         int cecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
         mHdmiCecNetwork.handleCecMessage(
-                HdmiCecMessageBuilder.buildReportFeatures(logicalAddress,
+                ReportFeaturesMessage.build(logicalAddress,
                         cecVersion, Collections.emptyList(),
                         Constants.RC_PROFILE_SOURCE, Collections.emptyList(),
-                        Collections.emptyList()));
+                        DeviceFeatures.NO_FEATURES_SUPPORTED));
 
-        assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        synchronized (mHdmiCecNetwork.mLock) {
+            assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        }
 
         HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
         assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
@@ -544,10 +550,33 @@
     }
 
     @Test
-    public void getSafeCecDevicesLocked_addDevice_sizeOne() {
-        HdmiDeviceInfo cecDeviceInfo = new HdmiDeviceInfo();
+    public void cecDevices_tracking_reportFeatures_updatesDeviceFeatures() {
+        // Features should be set correctly with the initial <Report Features>
+        int logicalAddress = Constants.ADDR_PLAYBACK_1;
+        int cecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+        DeviceFeatures deviceFeatures = DeviceFeatures.NO_FEATURES_SUPPORTED;
+        mHdmiCecNetwork.handleCecMessage(
+                ReportFeaturesMessage.build(logicalAddress,
+                        cecVersion, Collections.emptyList(),
+                        Constants.RC_PROFILE_SOURCE, Collections.emptyList(), deviceFeatures));
 
-        mHdmiCecNetwork.addCecDevice(cecDeviceInfo);
+        HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+        assertThat(cecDeviceInfo.getDeviceFeatures()).isEqualTo(deviceFeatures);
+
+        // New information from <Report Features> should override old information
+        DeviceFeatures updatedFeatures = DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED).build();
+        mHdmiCecNetwork.handleCecMessage(
+                ReportFeaturesMessage.build(logicalAddress,
+                        cecVersion, Collections.emptyList(),
+                        Constants.RC_PROFILE_SOURCE, Collections.emptyList(), updatedFeatures));
+        cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+        assertThat(cecDeviceInfo.getDeviceFeatures()).isEqualTo(updatedFeatures);
+    }
+
+    @Test
+    public void getSafeCecDevicesLocked_addDevice_sizeOne() {
+        mHdmiCecNetwork.addCecDevice(HdmiDeviceInfo.INACTIVE_DEVICE);
 
         assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index bff1296..7a68285 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -98,7 +98,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index a44bd8e..7751ef5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -111,8 +111,6 @@
         mHdmiControlServiceSpy.setCecController(mHdmiCecController);
         mHdmiControlServiceSpy.setHdmiMhlController(HdmiMhlControllerStub.create(
                 mHdmiControlServiceSpy));
-        mHdmiControlServiceSpy.setMessageValidator(new HdmiCecMessageValidator(
-                mHdmiControlServiceSpy));
 
         mLocalDevices.add(mAudioSystemDeviceSpy);
         mLocalDevices.add(mPlaybackDeviceSpy);
@@ -487,7 +485,7 @@
                 Constants.ADDR_PLAYBACK_1));
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
                 Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
@@ -505,7 +503,7 @@
         mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
                 Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
@@ -522,7 +520,7 @@
         mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
                 Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 22ad956..561e6a5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -57,10 +57,16 @@
             new byte[]{HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON};
 
     private static final int PORT_1 = 1;
-    private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
-            ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
-            0x1234, "TV",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+    private static final HdmiDeviceInfo INFO_TV = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_TV)
+            .setPhysicalAddress(0x0000)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_TV)
+            .setVendorId(0x1234)
+            .setDisplayName("TV")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
 
     private Context mContextSpy;
     private HdmiControlService mHdmiControlService;
@@ -113,7 +119,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         mHdmiControlService.setPowerManager(mPowerManager);
@@ -165,7 +170,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -218,7 +223,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -271,7 +276,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusTransientToOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -284,7 +289,7 @@
         mNativeWrapper.clearResultMessages();
 
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -428,7 +433,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -482,7 +487,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 2f22bce..c878f99 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -99,7 +99,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mTvDevice = new HdmiCecLocalDeviceTv(mHdmiControlService);
         mTvDevice.init();
         mLocalDevices.add(mTvDevice);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ReportFeaturesMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ReportFeaturesMessageTest.java
new file mode 100644
index 0000000..22f1f43
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ReportFeaturesMessageTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+import static com.android.server.hdmi.HdmiUtils.buildMessage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ReportFeaturesMessageTest {
+    @Test
+    public void build_invalidMessages() {
+        assertThat(HdmiUtils.buildMessage("FF:A6:05:80:00:00")
+                .getValidationResult()).isEqualTo(ERROR_SOURCE);
+        assertThat(HdmiUtils.buildMessage("04:A6:05:80:00:00")
+                .getValidationResult()).isEqualTo(ERROR_DESTINATION);
+        assertThat(HdmiUtils.buildMessage("0F:A6")
+                .getValidationResult()).isEqualTo(ERROR_PARAMETER_SHORT);
+        assertThat(HdmiUtils.buildMessage("4F:A6:06:00:80:80:00")
+                .getValidationResult()).isEqualTo(ERROR_PARAMETER_SHORT);
+    }
+
+    @Test
+    public void build_longMessage() {
+        HdmiCecMessage longMessage = HdmiUtils.buildMessage("4F:A6:05:00:80:80:00:81:80:00");
+        assertThat(longMessage).isInstanceOf(ReportFeaturesMessage.class);
+        ReportFeaturesMessage longReportFeaturesMessage = (ReportFeaturesMessage) longMessage;
+
+        HdmiCecMessage shortMessage = HdmiUtils.buildMessage("4F:A6:05:00:00:01");
+        assertThat(shortMessage).isInstanceOf(ReportFeaturesMessage.class);
+        ReportFeaturesMessage shortReportFeaturesMessage = (ReportFeaturesMessage) shortMessage;
+
+        assertThat(longReportFeaturesMessage.getDeviceFeatures()).isEqualTo(
+                shortReportFeaturesMessage.getDeviceFeatures());
+        assertThat(longReportFeaturesMessage.getCecVersion()).isEqualTo(
+                shortReportFeaturesMessage.getCecVersion());
+    }
+
+    @Test
+    public void build_basicTv_1_4() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:05:80:00:00"));
+    }
+
+    @Test
+    public void build_basicPlayback_1_4() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_PLAYBACK_1,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("4F:A6:05:10:00:00"));
+    }
+
+    @Test
+    public void build_basicPlaybackAudioSystem_1_4() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_PLAYBACK_1,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
+                        HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("4F:A6:05:18:00:00"));
+    }
+
+    @Test
+    public void build_basicTv_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:00"));
+    }
+
+    @Test
+    public void build_remoteControlTv_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_ONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:02:00"));
+    }
+
+    @Test
+    public void build_remoteControlPlayback_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
+                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:00"));
+    }
+
+    @Test
+    public void build_deviceFeaturesTv_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                        .setRecordTvScreenSupport(DeviceFeatures.FEATURE_SUPPORTED)
+                        .build());
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:40"));
+    }
+
+    @Test
+    public void build_deviceFeaturesPlayback_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
+                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
+                DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                        .setDeckControlSupport(DeviceFeatures.FEATURE_SUPPORTED)
+                        .build());
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:10"));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 2d81fc9..6184c21 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -123,7 +123,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(context);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index 302c0ac..0587864 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -101,19 +101,29 @@
     private static final byte[] PLAYER_PARAM =
             new byte[]{(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF};
 
-    private static final HdmiDeviceInfo DEVICE_INFO_AVR =
-            new HdmiDeviceInfo(ADDR_AUDIO_SYSTEM, PHYSICAL_ADDRESS_AVR, PORT_1,
-                    HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, VENDOR_ID_AVR, "Audio");
-    private static final HdmiDeviceInfo DEVICE_INFO_PLAYER =
-            new HdmiDeviceInfo(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYER, PORT_1,
-                    HdmiDeviceInfo.DEVICE_PLAYBACK, VENDOR_ID_AVR, "Player");
-    private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = new HdmiCecMessage(
+    private static final HdmiDeviceInfo DEVICE_INFO_AVR = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_AUDIO_SYSTEM)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_AVR)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)
+            .setVendorId(VENDOR_ID_AVR)
+            .setDisplayName("Audio")
+            .build();
+    private static final HdmiDeviceInfo DEVICE_INFO_PLAYER = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_PLAYBACK_1)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYER)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+            .setVendorId(VENDOR_ID_AVR)
+            .setDisplayName("Player")
+            .build();
+    private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = HdmiCecMessage.build(
             ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM);
-    private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = new HdmiCecMessage(
+    private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = HdmiCecMessage.build(
             ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM);
-    private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = new HdmiCecMessage(
+    private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = HdmiCecMessage.build(
             ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM);
-    private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = new HdmiCecMessage(
+    private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM);
 
     private HdmiControlService mHdmiControlService;
@@ -169,7 +179,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
new file mode 100644
index 0000000..a34b55c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class SetAudioVolumeLevelDiscoveryActionTest {
+    private HdmiControlService mHdmiControlServiceSpy;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDevicePlayback mPlaybackDevice;
+    private FakeNativeWrapper mNativeWrapper;
+    private FakePowerManagerWrapper mPowerManager;
+    private Looper mLooper;
+    private Context mContextSpy;
+    private TestLooper mTestLooper = new TestLooper();
+    private int mPhysicalAddress = 0x1100;
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mPlaybackLogicalAddress;
+
+    private TestCallback mTestCallback;
+    private SetAudioVolumeLevelDiscoveryAction mAction;
+
+    /**
+     * Setup: Local Playback device attempts to determine whether a connected TV supports
+     * <Set Audio Volume Level>.
+     */
+    @Before
+    public void setUp() throws RemoteException {
+        mContextSpy = spy(new ContextWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext()));
+
+        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
+        doNothing().when(mHdmiControlServiceSpy)
+                .writeStringSystemProperty(anyString(), anyString());
+
+        mLooper = mTestLooper.getLooper();
+        mHdmiControlServiceSpy.setIoLooper(mLooper);
+        mHdmiControlServiceSpy.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+
+        mNativeWrapper = new FakeNativeWrapper();
+        mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+
+        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlServiceSpy, mNativeWrapper, mHdmiControlServiceSpy.getAtomWriter());
+        mHdmiControlServiceSpy.setCecController(mHdmiCecController);
+        mHdmiControlServiceSpy.setHdmiMhlController(
+                HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
+        mHdmiControlServiceSpy.initService();
+        mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+        mHdmiControlServiceSpy.setPowerManager(mPowerManager);
+
+        mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy);
+        mPlaybackDevice.init();
+        mLocalDevices.add(mPlaybackDevice);
+
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        mTestLooper.dispatchAll();
+
+        synchronized (mPlaybackDevice.mLock) {
+            mPlaybackLogicalAddress = mPlaybackDevice.getDeviceInfo().getLogicalAddress();
+        }
+
+        // Setup specific to these tests
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                Constants.ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV));
+        mTestLooper.dispatchAll();
+
+        mTestCallback = new TestCallback();
+        mAction = new SetAudioVolumeLevelDiscoveryAction(mPlaybackDevice,
+                Constants.ADDR_TV, mTestCallback);
+    }
+
+    @Test
+    public void sendsSetAudioVolumeLevel() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage setAudioVolumeLevel = SetAudioVolumeLevelMessage.build(
+                mPlaybackLogicalAddress, Constants.ADDR_TV,
+                Constants.AUDIO_VOLUME_STATUS_UNKNOWN);
+        assertThat(mNativeWrapper.getResultMessages()).contains(setAudioVolumeLevel);
+    }
+
+    @Test
+    public void noMatchingFeatureAbortReceived_actionSucceedsAndSetsFeatureSupported() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        // Wrong opcode
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                Constants.ADDR_TV,
+                mPlaybackLogicalAddress,
+                Constants.MESSAGE_GIVE_DECK_STATUS,
+                Constants.ABORT_UNRECOGNIZED_OPCODE));
+        // Wrong source
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                Constants.ADDR_AUDIO_SYSTEM,
+                mPlaybackLogicalAddress,
+                Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                Constants.ABORT_UNRECOGNIZED_OPCODE));
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    @Test
+    public void matchingFeatureAbortReceived_actionSucceedsAndSetsFeatureNotSupported() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                Constants.ADDR_TV,
+                mPlaybackLogicalAddress,
+                Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                Constants.ABORT_UNRECOGNIZED_OPCODE));
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    @Test
+    public void messageFailedToSend_actionFailsAndDoesNotUpdateFeatureSupport() {
+        mNativeWrapper.setMessageSendResult(Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                SendMessageResult.FAIL);
+        mTestLooper.dispatchAll();
+
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
+        assertThat(mTestCallback.getResult()).isEqualTo(
+                HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+    }
+
+    private static class TestCallback extends IHdmiControlCallback.Stub {
+        private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+        @Override
+        public void onComplete(int result) {
+            mCallbackResult.add(result);
+        }
+
+        private int getResult() {
+            assertThat(mCallbackResult.size()).isEqualTo(1);
+            return mCallbackResult.get(0);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelMessageTest.java
new file mode 100644
index 0000000..0201c68
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelMessageTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
+import static com.android.server.hdmi.HdmiUtils.buildMessage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class SetAudioVolumeLevelMessageTest {
+    @Test
+    public void build_maxVolume() {
+        HdmiCecMessage message = SetAudioVolumeLevelMessage.build(
+                Constants.ADDR_TV, Constants.ADDR_PLAYBACK_1, 100);
+        assertThat(message.getValidationResult()).isEqualTo(HdmiCecMessageValidator.OK);
+        assertThat(message).isEqualTo(buildMessage("04:73:64"));
+    }
+
+    @Test
+    public void build_noVolumeChange() {
+        HdmiCecMessage message = SetAudioVolumeLevelMessage.build(
+                Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM, 0x7F);
+        assertThat(message.getValidationResult()).isEqualTo(HdmiCecMessageValidator.OK);
+        assertThat(message).isEqualTo(buildMessage("05:73:7F"));
+    }
+
+    @Test
+    public void build_invalid() {
+        assertThat(SetAudioVolumeLevelMessage
+                .build(Constants.ADDR_UNREGISTERED, Constants.ADDR_AUDIO_SYSTEM, 50)
+                .getValidationResult())
+                .isEqualTo(ERROR_SOURCE);
+        assertThat(SetAudioVolumeLevelMessage
+                .build(Constants.ADDR_TV, Constants.ADDR_BROADCAST, 50)
+                .getValidationResult())
+                .isEqualTo(ERROR_DESTINATION);
+        assertThat(HdmiUtils.buildMessage("04:73")
+                .getValidationResult())
+                .isEqualTo(ERROR_PARAMETER_SHORT);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index b34b853..9d14341 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -100,7 +100,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index b40650e..095c69c7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -64,7 +64,7 @@
 
     @Before
     public void SetUp() {
-        mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+        mDeviceInfoForTests = HdmiDeviceInfo.hardwarePort(1001, 1234);
 
         Context context = InstrumentationRegistry.getTargetContext();
 
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index 70e78eb..c771000 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -17,9 +17,7 @@
 package com.android.server.locales;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
@@ -43,11 +41,10 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.SimpleClock;
-import android.util.AtomicFile;
+import android.util.SparseArray;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
@@ -57,6 +54,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.XmlUtils;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,10 +63,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.time.Clock;
@@ -95,9 +90,8 @@
             LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
     private static final Map<String, String> DEFAULT_PACKAGE_LOCALES_MAP = Map.of(
             DEFAULT_PACKAGE_NAME, DEFAULT_LOCALE_TAGS);
-    private static final File STAGED_LOCALES_DIR = new File(
-            Environment.getExternalStorageDirectory(), "lmsUnitTests");
-
+    private static final SparseArray<LocaleManagerBackupHelper.StagedData> STAGE_DATA =
+            new SparseArray<>();
 
     private LocaleManagerBackupHelper mBackupHelper;
     private long mCurrentTimeMillis;
@@ -138,14 +132,17 @@
         doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
 
         mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
-                mMockLocaleManagerService, mMockPackageManagerInternal,
-                new File(Environment.getExternalStorageDirectory(), "lmsUnitTests"), mClock));
+                mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA));
         doNothing().when(mBackupHelper).notifyBackupManager();
 
         mUserMonitor = mBackupHelper.getUserMonitor();
         mPackageMonitor = mBackupHelper.getPackageMonitor();
         setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
-        cleanStagedFiles();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        STAGE_DATA.clear();
     }
 
     @Test
@@ -203,25 +200,25 @@
     }
 
     @Test
-    public void testRestore_nullPayload_nothingRestoredAndNoStageFile() throws Exception {
+    public void testRestore_nullPayload_nothingRestoredAndNoStageData() throws Exception {
         mBackupHelper.stageAndApplyRestoredPayload(/* payload= */ null, DEFAULT_USER_ID);
 
         verifyNothingRestored();
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
-    public void testRestore_zeroLengthPayload_nothingRestoredAndNoStageFile() throws Exception {
+    public void testRestore_zeroLengthPayload_nothingRestoredAndNoStageData() throws Exception {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         mBackupHelper.stageAndApplyRestoredPayload(/* payload= */ out.toByteArray(),
                 DEFAULT_USER_ID);
 
         verifyNothingRestored();
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
-    public void testRestore_allAppsInstalled_noStageFileCreated() throws Exception {
+    public void testRestore_allAppsInstalled_noStageDataCreated() throws Exception {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
 
@@ -234,8 +231,7 @@
         verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
                 DEFAULT_USER_ID, DEFAULT_LOCALES);
 
-        // Stage file wasn't created.
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
@@ -248,8 +244,8 @@
         mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
 
         verifyNothingRestored();
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
-                getStageFileIfExists(DEFAULT_USER_ID), DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
     }
 
     @Test
@@ -257,8 +253,10 @@
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         HashMap<String, String> pkgLocalesMap = new HashMap<>();
 
-        String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB";
-        String langTagsA = "ru", langTagsB = "hi,fr";
+        String pkgNameA = "com.android.myAppA";
+        String pkgNameB = "com.android.myAppB";
+        String langTagsA = "ru";
+        String langTagsB = "hi,fr";
         pkgLocalesMap.put(pkgNameA, langTagsA);
         pkgLocalesMap.put(pkgNameB, langTagsB);
         writeTestPayload(out, pkgLocalesMap);
@@ -273,12 +271,12 @@
                 LocaleList.forLanguageTags(langTagsA));
 
         pkgLocalesMap.remove(pkgNameA);
-        verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(pkgLocalesMap,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
     }
 
     @Test
-    public void testRestore_appLocalesAlreadySet_nothingRestoredAndNoStageFile() throws Exception {
+    public void testRestore_appLocalesAlreadySet_nothingRestoredAndNoStageData() throws Exception {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
 
@@ -289,8 +287,7 @@
 
         // Since locales are already set, we should not restore anything for it.
         verifyNothingRestored();
-        // Stage file wasn't created
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
@@ -299,9 +296,12 @@
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         HashMap<String, String> pkgLocalesMap = new HashMap<>();
 
-        String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB", pkgNameC =
-                "com.android.myAppC";
-        String langTagsA = "ru", langTagsB = "hi,fr", langTagsC = "zh,es";
+        String pkgNameA = "com.android.myAppA";
+        String pkgNameB = "com.android.myAppB";
+        String pkgNameC = "com.android.myAppC";
+        String langTagsA = "ru";
+        String langTagsB = "hi,fr";
+        String langTagsC = "zh,es";
         pkgLocalesMap.put(pkgNameA, langTagsA);
         pkgLocalesMap.put(pkgNameB, langTagsB);
         pkgLocalesMap.put(pkgNameC, langTagsC);
@@ -328,8 +328,8 @@
         // App C is staged.
         pkgLocalesMap.remove(pkgNameA);
         pkgLocalesMap.remove(pkgNameB);
-        verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(pkgLocalesMap,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
     }
 
     @Test
@@ -341,15 +341,15 @@
 
         mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
 
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
 
         final long newCreationTime = DEFAULT_CREATION_TIME_MILLIS + 100;
         setCurrentTimeMillis(newCreationTime);
         mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
 
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
-                newCreationTime);
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+                newCreationTime, DEFAULT_USER_ID);
     }
 
     @Test
@@ -357,8 +357,10 @@
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         HashMap<String, String> pkgLocalesMap = new HashMap<>();
 
-        String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB";
-        String langTagsA = "ru", langTagsB = "hi,fr";
+        String pkgNameA = "com.android.myAppA";
+        String pkgNameB = "com.android.myAppB";
+        String langTagsA = "ru";
+        String langTagsB = "hi,fr";
         pkgLocalesMap.put(pkgNameA, langTagsA);
         pkgLocalesMap.put(pkgNameB, langTagsB);
         writeTestPayload(out, pkgLocalesMap);
@@ -380,8 +382,7 @@
                 LocaleList.forLanguageTags(langTagsA));
 
         pkgLocalesMap.remove(pkgNameA);
-        verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(pkgLocalesMap, DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
 
         setUpPackageInstalled(pkgNameB);
 
@@ -389,7 +390,7 @@
 
         verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
                 LocaleList.forLanguageTags(langTagsB));
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
@@ -404,8 +405,8 @@
         mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
 
         verifyNothingRestored();
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
 
         // App is installed later (post SUW).
         setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
@@ -415,11 +416,11 @@
 
         // Since locales are already set, we should not restore anything for it.
         verifyNothingRestored();
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
-    public void testStageFileDeletion_backupPassRunAfterRetentionPeriod_stageFileDeleted()
+    public void testStageDataDeletion_backupPassRunAfterRetentionPeriod_stageDataDeleted()
             throws Exception {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
@@ -429,8 +430,8 @@
         mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
 
         verifyNothingRestored();
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
 
         // Retention period has not elapsed.
         setCurrentTimeMillis(
@@ -439,32 +440,78 @@
                 .getInstalledApplications(anyLong(), anyInt(), anyInt());
         assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
 
-        // Stage file should NOT be deleted.
-        checkStageFileExists(DEFAULT_USER_ID);
+        checkStageDataExists(DEFAULT_USER_ID);
 
-        // Exactly RETENTION_PERIOD amount of time has passed so stage file should still not be
+        // Exactly RETENTION_PERIOD amount of time has passed so stage data should still not be
         // removed.
         setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.toMillis());
         doReturn(List.of()).when(mMockPackageManagerInternal)
                 .getInstalledApplications(anyLong(), anyInt(), anyInt());
         assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
 
-        // Stage file should NOT be deleted.
-        checkStageFileExists(DEFAULT_USER_ID);
+        checkStageDataExists(DEFAULT_USER_ID);
 
-        // Retention period has now expired, stage file should be deleted.
+        // Retention period has now expired, stage data should be deleted.
         setCurrentTimeMillis(
                 DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
         doReturn(List.of()).when(mMockPackageManagerInternal)
                 .getInstalledApplications(anyLong(), anyInt(), anyInt());
         assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
 
-        // Stage file should be deleted.
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
     }
 
     @Test
-    public void testUserRemoval_userRemoved_stageFileDeleted() throws Exception {
+    public void testStageDataDeletion_lazyRestoreAfterRetentionPeriod_stageDataDeleted()
+            throws Exception {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+        String pkgNameA = "com.android.myAppA";
+        String pkgNameB = "com.android.myAppB";
+        String langTagsA = "ru";
+        String langTagsB = "hi,fr";
+        pkgLocalesMap.put(pkgNameA, langTagsA);
+        pkgLocalesMap.put(pkgNameB, langTagsB);
+        writeTestPayload(out, pkgLocalesMap);
+
+        setUpPackageNotInstalled(pkgNameA);
+        setUpPackageNotInstalled(pkgNameB);
+        setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
+        setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
+
+        mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+        verifyNothingRestored();
+        verifyStageDataForUser(pkgLocalesMap, DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
+
+        // Retention period has not elapsed.
+        setCurrentTimeMillis(
+                DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.minusHours(1).toMillis());
+
+        setUpPackageInstalled(pkgNameA);
+        mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
+
+        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
+                LocaleList.forLanguageTags(langTagsA));
+
+        pkgLocalesMap.remove(pkgNameA);
+        verifyStageDataForUser(pkgLocalesMap, DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
+
+        // Retention period has now expired, stage data should be deleted.
+        setCurrentTimeMillis(
+                DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
+        setUpPackageInstalled(pkgNameB);
+        mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
+
+        verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameB), anyInt(),
+                any());
+
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
+    }
+
+    @Test
+    public void testUserRemoval_userRemoved_stageDataDeleted() throws Exception {
         final ByteArrayOutputStream outDefault = new ByteArrayOutputStream();
         writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_MAP);
 
@@ -485,119 +532,20 @@
 
         verifyNothingRestored();
 
-        // Verify stage file contents.
-        AtomicFile stageFileDefaultUser = getStageFileIfExists(DEFAULT_USER_ID);
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, stageFileDefaultUser,
-                DEFAULT_CREATION_TIME_MILLIS);
-
-        AtomicFile stageFileWorkProfile = getStageFileIfExists(WORK_PROFILE_USER_ID);
-        verifyStageFileContent(pkgLocalesMapWorkProfile, stageFileWorkProfile,
-                DEFAULT_CREATION_TIME_MILLIS);
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
+        verifyStageDataForUser(pkgLocalesMapWorkProfile,
+                DEFAULT_CREATION_TIME_MILLIS, WORK_PROFILE_USER_ID);
 
         Intent intent = new Intent();
         intent.setAction(Intent.ACTION_USER_REMOVED);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, DEFAULT_USER_ID);
         mUserMonitor.onReceive(mMockContext, intent);
 
-        // Stage file should be removed only for DEFAULT_USER_ID.
-        checkStageFileDoesNotExist(DEFAULT_USER_ID);
-        verifyStageFileContent(pkgLocalesMapWorkProfile, stageFileWorkProfile,
-                DEFAULT_CREATION_TIME_MILLIS);
-    }
-
-    @Test
-    public void testLoadStageFiles_invalidNameFormat_stageFileDeleted() throws Exception {
-        // Stage file name should be : staged_locales_<user_id_int>.xml
-        File stageFile = new File(STAGED_LOCALES_DIR, "xyz.xml");
-        assertTrue(stageFile.createNewFile());
-        assertTrue(stageFile.isFile());
-
-        // Putting valid xml data in file.
-        FileOutputStream out = new FileOutputStream(stageFile);
-        writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
-                true, /* creationTimeMillis= */ 0);
-        out.flush();
-        out.close();
-
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
-                new AtomicFile(stageFile), /* creationTimeMillis= */ 0);
-
-        mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
-                mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
-        assertFalse(stageFile.isFile());
-    }
-
-    @Test
-    public void testLoadStageFiles_userIdNotParseable_stageFileDeleted() throws Exception {
-        // Stage file name should be : staged_locales_<user_id_int>.xml
-        File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_abc.xml");
-        assertTrue(stageFile.createNewFile());
-        assertTrue(stageFile.isFile());
-
-        // Putting valid xml data in file.
-        FileOutputStream out = new FileOutputStream(stageFile);
-        writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
-                true, /* creationTimeMillis= */ 0);
-        out.flush();
-        out.close();
-
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
-                new AtomicFile(stageFile), /* creationTimeMillis= */ 0);
-
-        mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
-                mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
-        assertFalse(stageFile.isFile());
-    }
-
-    @Test
-    public void testLoadStageFiles_invalidContent_stageFileDeleted() throws Exception {
-        File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_0.xml");
-        assertTrue(stageFile.createNewFile());
-        assertTrue(stageFile.isFile());
-
-        FileOutputStream out = new FileOutputStream(stageFile);
-        out.write("some_non_xml_string".getBytes());
-        out.close();
-
-        mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
-                mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
-        assertFalse(stageFile.isFile());
-    }
-
-    @Test
-    public void testLoadStageFiles_validContent_doesLazyRestore() throws Exception {
-        File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_0.xml");
-        assertTrue(stageFile.createNewFile());
-        assertTrue(stageFile.isFile());
-
-        // Putting valid xml data in file.
-        FileOutputStream out = new FileOutputStream(stageFile);
-        writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
-                true, DEFAULT_CREATION_TIME_MILLIS);
-        out.flush();
-        out.close();
-
-        verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
-                new AtomicFile(stageFile), DEFAULT_CREATION_TIME_MILLIS);
-
-        mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
-                mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
-        mPackageMonitor = mBackupHelper.getPackageMonitor();
-
-        // Stage file still exists.
-        assertTrue(stageFile.isFile());
-
-        // App is installed later.
-        setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
-        setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
-
-        mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
-
-        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
-                DEFAULT_USER_ID, DEFAULT_LOCALES);
-
-        // Stage file gets deleted here because all staged locales have been applied.
-        assertFalse(stageFile.isFile());
+        // Stage data should be removed only for DEFAULT_USER_ID.
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
+        verifyStageDataForUser(pkgLocalesMapWorkProfile,
+                DEFAULT_CREATION_TIME_MILLIS, WORK_PROFILE_USER_ID);
     }
 
     private void setUpPackageInstalled(String packageName) throws Exception {
@@ -633,27 +581,15 @@
                 any());
     }
 
-
     private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
             byte[] payload)
             throws IOException, XmlPullParserException {
-        verifyPayloadForAppLocales(expectedPkgLocalesMap, payload, /* forStage= */ false, -1);
-    }
-
-    private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
-            byte[] payload, boolean forStage, long expectedCreationTime)
-            throws IOException, XmlPullParserException {
         final ByteArrayInputStream stream = new ByteArrayInputStream(payload);
         final TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(stream, StandardCharsets.UTF_8.name());
 
         Map<String, String> backupDataMap = new HashMap<>();
         XmlUtils.beginDocument(parser, TEST_LOCALES_XML_TAG);
-        if (forStage) {
-            long actualCreationTime = parser.getAttributeLong(/* namespace= */ null,
-                    "creationTimeMillis");
-            assertEquals(expectedCreationTime, actualCreationTime);
-        }
         int depth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, depth)) {
             if (parser.getName().equals("package")) {
@@ -668,13 +604,6 @@
 
     private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap)
             throws IOException {
-        writeTestPayload(stream, pkgLocalesMap, /* forStage= */ false, /* creationTimeMillis= */
-                -1);
-    }
-
-    private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap,
-            boolean forStage, long creationTimeMillis)
-            throws IOException {
         if (pkgLocalesMap.isEmpty()) {
             return;
         }
@@ -684,11 +613,6 @@
         out.startDocument(/* encoding= */ null, /* standalone= */ true);
         out.startTag(/* namespace= */ null, TEST_LOCALES_XML_TAG);
 
-        if (forStage) {
-            out.attribute(/* namespace= */ null, "creationTimeMillis",
-                    Long.toString(creationTimeMillis));
-        }
-
         for (String pkg : pkgLocalesMap.keySet()) {
             out.startTag(/* namespace= */ null, "package");
             out.attribute(/* namespace= */ null, "name", pkg);
@@ -700,41 +624,19 @@
         out.endDocument();
     }
 
-    private static void verifyStageFileContent(Map<String, String> expectedPkgLocalesMap,
-            AtomicFile stageFile,
-            long creationTimeMillis)
-            throws Exception {
-        assertNotNull(stageFile);
-        try (InputStream stagedDataInputStream = stageFile.openRead()) {
-            verifyPayloadForAppLocales(expectedPkgLocalesMap, stagedDataInputStream.readAllBytes(),
-                    /* forStage= */ true, creationTimeMillis);
-        } catch (IOException | XmlPullParserException e) {
-            throw e;
-        }
+    private void verifyStageDataForUser(Map<String, String> expectedPkgLocalesMap,
+            long expectedCreationTimeMillis, int userId) {
+        LocaleManagerBackupHelper.StagedData stagedDataForUser = STAGE_DATA.get(userId);
+        assertNotNull(stagedDataForUser);
+        assertEquals(expectedCreationTimeMillis, stagedDataForUser.mCreationTimeMillis);
+        assertEquals(expectedPkgLocalesMap, stagedDataForUser.mPackageStates);
     }
 
-    private static void checkStageFileDoesNotExist(int userId) {
-        assertNull(getStageFileIfExists(userId));
+    private static void checkStageDataExists(int userId) {
+        assertNotNull(STAGE_DATA.get(userId));
     }
 
-    private static void checkStageFileExists(int userId) {
-        assertNotNull(getStageFileIfExists(userId));
-    }
-
-    private static AtomicFile getStageFileIfExists(int userId) {
-        File file = new File(STAGED_LOCALES_DIR, String.format("staged_locales_%d.xml", userId));
-        if (file.isFile()) {
-            return new AtomicFile(file);
-        }
-        return null;
-    }
-
-    private static void cleanStagedFiles() {
-        File[] files = STAGED_LOCALES_DIR.listFiles();
-        if (files != null) {
-            for (File f : files) {
-                f.delete();
-            }
-        }
+    private static void checkStageDataDoesNotExist(int userId) {
+        assertNull(STAGE_DATA.get(userId));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index 658f8d5..ee2bb0a 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -30,6 +30,7 @@
 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.times;
 import static org.mockito.Mockito.verify;
 
@@ -58,6 +59,7 @@
 @RunWith(AndroidJUnit4.class)
 public class LocaleManagerServiceTest {
     private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
+    private static final String DEFAULT_INSTALLER_PACKAGE_NAME = "com.android.myapp.installer";
     private static final int DEFAULT_USER_ID = 0;
     private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
     private static final int INVALID_UID = -1;
@@ -66,7 +68,8 @@
             LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
     private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
             /* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
-            /* originatingPackageName = */ null, /* installingPackageName = */ null);
+            /* originatingPackageName = */ null,
+            /* installingPackageName = */ DEFAULT_INSTALLER_PACKAGE_NAME);
 
     private LocaleManagerService mLocaleManagerService;
     private LocaleManagerBackupHelper mMockBackupHelper;
@@ -89,7 +92,7 @@
         mMockActivityManager = mock(ActivityManagerInternal.class);
         mMockPackageManagerInternal = mock(PackageManagerInternal.class);
 
-        // For unit tests, set the default (null) installer info
+        // For unit tests, set the default installer info
         PackageManager mockPackageManager = mock(PackageManager.class);
         doReturn(DEFAULT_INSTALL_SOURCE_INFO).when(mockPackageManager)
                 .getInstallSourceInfo(anyString());
@@ -275,6 +278,23 @@
         assertEquals(DEFAULT_LOCALES, locales);
     }
 
+    @Test
+    public void testGetApplicationLocales_callerIsInstaller_returnsLocales()
+            throws Exception {
+        doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
+                .getPackageUid(eq(DEFAULT_PACKAGE_NAME), anyLong(), anyInt());
+        doReturn(Binder.getCallingUid()).when(mMockPackageManagerInternal)
+                .getPackageUid(eq(DEFAULT_INSTALLER_PACKAGE_NAME), anyLong(), anyInt());
+        doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
+                .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
+
+        LocaleList locales =
+                mLocaleManagerService.getApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+        verify(mMockContext, never()).enforceCallingOrSelfPermission(any(), any());
+        assertEquals(DEFAULT_LOCALES, locales);
+    }
+
     private static void assertNoLocalesStored(LocaleList locales) {
         assertNull(locales);
     }
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
index 93972c3..b0fc636 100644
--- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.util.SparseArray;
 
-import java.io.File;
 import java.time.Clock;
 
 /**
@@ -30,7 +30,8 @@
 public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper {
     ShadowLocaleManagerBackupHelper(Context context,
             LocaleManagerService localeManagerService,
-            PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
-        super(context, localeManagerService, pmInternal, stagedLocalesDir, clock);
+            PackageManagerInternal pmInternal, Clock clock,
+            SparseArray<LocaleManagerBackupHelper.StagedData> stagedData) {
+        super(context, localeManagerService, pmInternal, clock, stagedData);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
new file mode 100644
index 0000000..51ddcef
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
@@ -0,0 +1,186 @@
+/*
+ * 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.locksettings;
+
+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.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.PasswordMetrics;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** atest FrameworksServicesTests:WeakEscrowTokenTests */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WeakEscrowTokenTests extends BaseLockSettingsServiceTests{
+
+    @Test
+    public void testWeakTokenActivatedImmediatelyIfNoUserPassword()
+            throws RemoteException {
+        mockAutoHardware();
+        final byte[] token = "some-high-entropy-secure-token".getBytes();
+        IWeakEscrowTokenActivatedListener mockListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockListener);
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
+        verify(mockListener).onWeakEscrowTokenActivated(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokenActivatedLaterWithUserPassword()
+            throws RemoteException {
+        mockAutoHardware();
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        IWeakEscrowTokenActivatedListener mockListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        LockscreenCredential password = newPassword("password");
+        mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
+
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockListener);
+        // Token not activated immediately since user password exists
+        assertFalse(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        // Activate token (password gets migrated to SP at the same time)
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+        // Verify token is activated and valid
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
+        verify(mockListener).onWeakEscrowTokenActivated(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokensRemovedIfCredentialChanged() throws Exception {
+        mockAutoHardware();
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        IWeakEscrowTokenRemovedListener mockRemoveListener = mockAliveRemoveListener();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        LockscreenCredential password = newPassword("password");
+        LockscreenCredential pattern = newPattern("123654");
+        mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
+        mService.registerWeakEscrowTokenRemovedListener(mockRemoveListener);
+
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockActivateListener);
+
+        // Activate token
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+
+        // Verify token removed
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mLocalService.setLockCredentialWithToken(
+                pattern, handle, token, PRIMARY_USER_ID));
+        assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+        verify(mockRemoveListener).onWeakEscrowTokenRemoved(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokenRemovedListenerRegistered() throws Exception {
+        mockAutoHardware();
+        IWeakEscrowTokenRemovedListener mockRemoveListener = mockAliveRemoveListener();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockActivateListener);
+
+        mService.registerWeakEscrowTokenRemovedListener(mockRemoveListener);
+        mService.removeWeakEscrowToken(handle, PRIMARY_USER_ID);
+
+        verify(mockRemoveListener).onWeakEscrowTokenRemoved(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokenRemovedListenerUnregistered() throws Exception {
+        mockAutoHardware();
+        IWeakEscrowTokenRemovedListener mockRemoveListener = mockAliveRemoveListener();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        byte[] token0 = "some-high-entropy-secure-token-0".getBytes();
+        byte[] token1 = "some-high-entropy-secure-token-1".getBytes();
+        long handle0 = mService.addWeakEscrowToken(token0, PRIMARY_USER_ID, mockActivateListener);
+        long handle1 = mService.addWeakEscrowToken(token1, PRIMARY_USER_ID, mockActivateListener);
+
+        mService.registerWeakEscrowTokenRemovedListener(mockRemoveListener);
+        mService.removeWeakEscrowToken(handle0, PRIMARY_USER_ID);
+        verify(mockRemoveListener).onWeakEscrowTokenRemoved(handle0, PRIMARY_USER_ID);
+
+        mService.unregisterWeakEscrowTokenRemovedListener(mockRemoveListener);
+        mService.removeWeakEscrowToken(handle1, PRIMARY_USER_ID);
+        verify(mockRemoveListener, never()).onWeakEscrowTokenRemoved(handle1, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testUnlockUserWithToken_weakEscrowToken() throws Exception {
+        mockAutoHardware();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        LockscreenCredential password = newPassword("password");
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
+        // Disregard any reportPasswordChanged() invocations as part of credential setup.
+        flushHandlerTasks();
+        reset(mDevicePolicyManager);
+
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockActivateListener);
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
+
+        mService.onCleanupUser(PRIMARY_USER_ID);
+        assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
+
+        assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
+        assertEquals(PasswordMetrics.computeForCredential(password),
+                mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
+    }
+
+    private void mockAutoHardware() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
+    }
+
+    private IWeakEscrowTokenRemovedListener mockAliveRemoveListener() {
+        IWeakEscrowTokenRemovedListener mockListener =
+                mock(IWeakEscrowTokenRemovedListener.Stub.class);
+        IBinder mockIBinder = mock(IBinder.class);
+        when(mockIBinder.isBinderAlive()).thenReturn(true);
+        when(mockListener.asBinder()).thenReturn(mockIBinder);
+        return mockListener;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index b811e28..81c9871 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -49,12 +49,8 @@
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -75,6 +71,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
 import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
+import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -108,6 +105,8 @@
 import android.app.IUidObserver;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -125,8 +124,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
 import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.wifi.WifiInfo;
@@ -138,7 +135,6 @@
 import android.os.PowerSaveState;
 import android.os.RemoteException;
 import android.os.SimpleClock;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
@@ -263,12 +259,13 @@
     private @Mock CarrierConfigManager mCarrierConfigManager;
     private @Mock TelephonyManager mTelephonyManager;
     private @Mock UserManager mUserManager;
+    private @Mock NetworkStatsManager mStatsManager;
+    private TestDependencies mDeps;
 
     private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
 
     private ActivityManagerInternal mActivityManagerInternal;
-    private NetworkStatsManagerInternal mStatsService;
 
     private IUidObserver mUidObserver;
     private INetworkManagementEventObserver mNetworkObserver;
@@ -335,8 +332,47 @@
                 .setBatterySaverEnabled(false).build();
         final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
         when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
+    }
 
-        mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
+    private class TestDependencies extends NetworkPolicyManagerService.Dependencies {
+        private final SparseArray<NetworkStats.Bucket> mMockedStats = new SparseArray<>();
+
+        TestDependencies(Context context) {
+            super(context);
+        }
+
+        @Override
+        long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+            int total = 0;
+            for (int i = 0; i < mMockedStats.size(); i++) {
+                NetworkStats.Bucket bucket = mMockedStats.valueAt(i);
+                total += bucket.getRxBytes() + bucket.getTxBytes();
+            }
+            return total;
+        }
+
+        @Override
+        List<NetworkStats.Bucket> getNetworkUidBytes(NetworkTemplate template, long start,
+                long end) {
+            final List<NetworkStats.Bucket> ret = new ArrayList<>();
+            for (int i = 0; i < mMockedStats.size(); i++) {
+                ret.add(mMockedStats.valueAt(i));
+            }
+            return ret;
+        }
+
+        private void setMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+            final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
+            when(bucket.getUid()).thenReturn(uid);
+            when(bucket.getRxBytes()).thenReturn(rxBytes);
+            when(bucket.getTxBytes()).thenReturn(txBytes);
+            mMockedStats.set(uid, bucket);
+        }
+
+        private void increaseMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+            final NetworkStats.Bucket bucket = mMockedStats.get(uid);
+            setMockedTotalBytes(uid, bucket.getRxBytes() + rxBytes, bucket.getTxBytes() + txBytes);
+        }
     }
 
     @Before
@@ -376,6 +412,8 @@
                         return mConnManager;
                     case Context.USER_SERVICE:
                         return mUserManager;
+                    case Context.NETWORK_STATS_SERVICE:
+                        return mStatsManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -400,8 +438,9 @@
         }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
 
         mFutureIntent = newRestrictBackgroundChangedFuture();
+        mDeps = new TestDependencies(mServiceContext);
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
-                mNetworkManager, mIpm, mClock, mPolicyDir, true);
+                mNetworkManager, mIpm, mClock, mPolicyDir, true, mDeps);
         mService.bindConnectivityManager();
         mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
@@ -456,6 +495,9 @@
         verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
 
+        // Simulate NetworkStatsService broadcast stats updated to signal its readiness.
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_UPDATED));
+
         NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
         mDefaultWarningBytes = defaultPolicy.warningBytes;
         mDefaultLimitBytes = defaultPolicy.limitBytes;
@@ -479,7 +521,6 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.removeServiceForTest(AppStandbyInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
-        LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
     }
 
     @After
@@ -1108,10 +1149,7 @@
         when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
 
         // pretend that 512 bytes total have happened
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
-        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
-                .thenReturn(stats.getTotalBytes());
+        mDeps.setMockedTotalBytes(UID_A, 256L, 256L);
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
         setNetworkPolicies(new NetworkPolicy(
@@ -1124,26 +1162,6 @@
 
     @Test
     public void testNotificationWarningLimitSnooze() throws Exception {
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1161,9 +1179,7 @@
 
         // Normal usage means no notification
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1178,9 +1194,7 @@
 
         // Push over warning
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1196,9 +1210,7 @@
 
         // Push over warning, but with a config that isn't from an identified carrier
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1215,9 +1227,7 @@
 
         // Push over limit
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1810), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1248,26 +1258,6 @@
 
     @Test
     public void testNotificationRapid() throws Exception {
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1285,9 +1275,7 @@
 
         // Using 20% data in 20% time is normal
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1297,16 +1285,9 @@
         // Using 80% data in 20% time is alarming; but spread equally among
         // three UIDs means we get generic alert
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
-            stats.clear();
-            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_C, DataUnit.MEGABYTES.toBytes(480), 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1325,14 +1306,9 @@
         // Using 80% data in 20% time is alarming; but mostly done by one UID
         // means we get specific alert
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
-            stats.clear();
-            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(960), 0);
+            mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_C, 0, 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1362,13 +1338,10 @@
 
         // bring up wifi network with metered policy
         snapshots = List.of(buildWifi());
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
+        mDeps.setMockedTotalBytes(UID_A, 0L, 0L);
 
         {
             when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(
@@ -1647,18 +1620,6 @@
         final NetworkPolicyManagerInternal internal = LocalServices
                 .getService(NetworkPolicyManagerInternal.class);
 
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(invocation -> {
-                    final NetworkStatsHistory.Entry entry = history.getValues(
-                            invocation.getArgument(1), invocation.getArgument(2), null);
-                    return entry.rxBytes + entry.txBytes;
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1669,9 +1630,7 @@
         setCurrentTimeMillis(end);
 
         // Get some data usage in place
-        history.clear();
-        history.recordData(start, end,
-                new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+        mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
         // No data plan
         {
@@ -1786,22 +1745,11 @@
                 true);
     }
 
-    private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
-        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
-                rxBytes, 1, txBytes, 1, 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats.getTotalBytes());
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
-    }
-
     private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
-        npmi.onStatsProviderWarningOrLimitReached("TEST");
+        mService.notifyStatsProviderWarningOrLimitReached();
         // Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
         postMsgAndWaitForCompletion();
-        verify(mStatsService).forceUpdate();
+        verify(mStatsManager).forceUpdate();
         // Wait for processing of MSG_*_INTERFACE_QUOTAS.
         postMsgAndWaitForCompletion();
     }
@@ -1814,13 +1762,12 @@
     public void testStatsProviderWarningAndLimitReached() throws Exception {
         final int CYCLE_DAY = 15;
 
-        final NetworkStats stats = new NetworkStats(0L, 1);
-        increaseMockedTotalBytes(stats, 2999, 2000);
+        mDeps.setMockedTotalBytes(UID_A, 2999, 2000);
 
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
                 Long.MAX_VALUE);
 
         // Set warning to 7KB and limit to 10KB.
@@ -1830,32 +1777,32 @@
         postMsgAndWaitForCompletion();
 
         // Verifies that remaining quotas are set to providers.
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
+        reset(mStatsManager);
 
         // Increase the usage and simulates that limit reached fires earlier by provider,
         // but actually the quota is not yet reached. Verifies that the limit reached leads to
         // a force update and new quotas should be set.
-        increaseMockedTotalBytes(stats, 1000, 999);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000, 999);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+        reset(mStatsManager);
 
         // Increase the usage and simulate warning reached, the new warning should be unlimited
         // since service will disable warning quota to stop lower layer from keep triggering
         // warning reached event.
-        increaseMockedTotalBytes(stats, 1000L, 1000);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(
                 TEST_IFACE, Long.MAX_VALUE, 1002L);
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // Increase the usage that over the warning and limit, the new limit should set to 1 to
         // block the network traffic.
-        increaseMockedTotalBytes(stats, 1000L, 1000);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+        reset(mStatsManager);
     }
 
     private void enableRestrictedMode(boolean enable) throws Exception {
@@ -2099,7 +2046,7 @@
     private static NetworkStateSnapshot buildWifi() {
         WifiInfo mockWifiInfo = mock(WifiInfo.class);
         when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
-        when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
+        when(mockWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
@@ -2145,7 +2092,7 @@
     }
 
     private void verifyAdvisePersistThreshold() throws Exception {
-        verify(mStatsService).advisePersistThreshold(anyLong());
+        verify(mStatsManager).setDefaultGlobalAlert(anyLong());
     }
 
     private static class TestAbstractFuture<T> extends AbstractFuture<T> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 0f6dfda..13a8f69 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -33,12 +33,12 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityImpl;
-import android.content.pm.parsing.component.ParsedInstrumentationImpl;
-import android.content.pm.parsing.component.ParsedIntentInfoImpl;
-import android.content.pm.parsing.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
diff --git a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
index 54ab133..e137c37 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
@@ -29,8 +29,8 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 6b6d84a..d7e3825 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -17,7 +17,7 @@
 package com.android.server.pm;
 
 
-import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
+import static android.content.pm.parsing.FrameworkParsingPackageUtils.parsePublicKey;
 
 import android.content.pm.Signature;
 import android.platform.test.annotations.Presubmit;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 62a2b1b..59f2ca4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -187,7 +187,7 @@
                 /* isFailed */ false,
                 /* isApplied */false,
                 /* stagedSessionErrorCode */
-                PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
                 /* stagedSessionErrorMessage */ "some error");
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 9d67240..6c9a60a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -22,7 +22,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_MORE_DETAILS;
 import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
-import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
+import static android.content.pm.parsing.FrameworkParsingPackageUtils.parsePublicKey;
 import static android.content.res.Resources.ID_NULL;
 
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -43,6 +43,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.os.BaseBundle;
 import android.os.PersistableBundle;
 import android.os.Process;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
new file mode 100644
index 0000000..b621a44
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
@@ -0,0 +1,3021 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.system.OsConstants.S_IFDIR;
+import static android.system.OsConstants.S_IFMT;
+import static android.system.OsConstants.S_IRGRP;
+import static android.system.OsConstants.S_IROTH;
+import static android.system.OsConstants.S_IRWXU;
+import static android.system.OsConstants.S_ISDIR;
+import static android.system.OsConstants.S_IXGRP;
+import static android.system.OsConstants.S_IXOTH;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.KeySet;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionInfo;
+import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.content.PackageHelper;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+public class PackageManagerTests extends AndroidTestCase {
+    private static final boolean localLOGV = true;
+
+    public static final String TAG = "PackageManagerTests";
+
+    public static final long MAX_WAIT_TIME = 25 * 1000;
+
+    public static final long WAIT_TIME_INCR = 5 * 1000;
+
+    private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec";
+
+    private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO;
+
+    private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL;
+
+    private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL;
+
+    void failStr(String errMsg) {
+        Log.w(TAG, "errMsg=" + errMsg);
+        fail(errMsg);
+    }
+
+    void failStr(Exception e) {
+        failStr(e.getMessage());
+    }
+
+    private abstract static class GenericReceiver extends BroadcastReceiver {
+        private boolean doneFlag = false;
+
+        boolean received = false;
+
+        Intent intent;
+
+        IntentFilter filter;
+
+        abstract boolean notifyNow(Intent intent);
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (notifyNow(intent)) {
+                synchronized (this) {
+                    received = true;
+                    doneFlag = true;
+                    this.intent = intent;
+                    notifyAll();
+                }
+            }
+        }
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+
+        public void setFilter(IntentFilter filter) {
+            this.filter = filter;
+        }
+    }
+
+    private static class InstallReceiver extends GenericReceiver {
+        String pkgName;
+
+        InstallReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addDataScheme("package");
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                return false;
+            }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName.equals(installedPkg)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private static class LocalIntentReceiver {
+        private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                try {
+                    mResult.offer(intent, 5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            try {
+                return mResult.take();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private PackageManager getPm() {
+        return mContext.getPackageManager();
+    }
+
+    private PackageInstaller getPi() {
+        return getPm().getPackageInstaller();
+    }
+
+    private void writeSplitToInstallSession(PackageInstaller.Session session, String inPath,
+            String splitName) throws RemoteException {
+        long sizeBytes = 0;
+        final File file = new File(inPath);
+        if (file.isFile()) {
+            sizeBytes = file.length();
+        } else {
+            return;
+        }
+
+        InputStream in = null;
+        OutputStream out = null;
+        try {
+            in = new FileInputStream(inPath);
+            out = session.openWrite(splitName, 0, sizeBytes);
+
+            int total = 0;
+            byte[] buffer = new byte[65536];
+            int c;
+            while ((c = in.read(buffer)) != -1) {
+                total += c;
+                out.write(buffer, 0, c);
+            }
+            session.fsync(out);
+        } catch (IOException e) {
+            fail("Error: failed to write; " + e.getMessage());
+        } finally {
+            IoUtils.closeQuietly(out);
+            IoUtils.closeQuietly(in);
+            IoUtils.closeQuietly(session);
+        }
+    }
+
+    private void invokeInstallPackage(Uri packageUri, int flags, GenericReceiver receiver,
+            boolean shouldSucceed) {
+        mContext.registerReceiver(receiver, receiver.filter);
+        synchronized (receiver) {
+            final String inPath = packageUri.getPath();
+            PackageInstaller.Session session = null;
+            try {
+                final SessionParams sessionParams =
+                        new SessionParams(SessionParams.MODE_FULL_INSTALL);
+                sessionParams.installFlags = flags;
+                final int sessionId = getPi().createSession(sessionParams);
+                session = getPi().openSession(sessionId);
+                writeSplitToInstallSession(session, inPath, "base.apk");
+                final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
+                session.commit(localReceiver.getIntentSender());
+                final Intent result = localReceiver.getResult();
+                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PackageInstaller.STATUS_FAILURE);
+                if (shouldSucceed) {
+                    if (status != PackageInstaller.STATUS_SUCCESS) {
+                        fail("Installation should have succeeded, but got code " + status);
+                    }
+                } else {
+                    if (status == PackageInstaller.STATUS_SUCCESS) {
+                        fail("Installation should have failed");
+                    }
+                    // We'll never get a broadcast since the package failed to install
+                    return;
+                }
+                // Verify we received the broadcast
+                long waitTime = 0;
+                while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) {
+                    try {
+                        receiver.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
+                }
+                if (!receiver.isDone()) {
+                    fail("Timed out waiting for PACKAGE_ADDED notification");
+                }
+            } catch (IllegalArgumentException | IOException | RemoteException e) {
+                Log.w(TAG, "Failed to install package; path=" + inPath, e);
+                fail("Failed to install package; path=" + inPath + ", e=" + e);
+            } finally {
+                IoUtils.closeQuietly(session);
+                mContext.unregisterReceiver(receiver);
+            }
+        }
+    }
+
+    private void invokeInstallPackageFail(Uri packageUri, int flags, int expectedResult) {
+        final String inPath = packageUri.getPath();
+        PackageInstaller.Session session = null;
+        try {
+            final SessionParams sessionParams =
+                    new SessionParams(SessionParams.MODE_FULL_INSTALL);
+            sessionParams.installFlags = flags;
+            final int sessionId = getPi().createSession(sessionParams);
+            session = getPi().openSession(sessionId);
+            writeSplitToInstallSession(session, inPath, "base.apk");
+            final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
+            session.commit(localReceiver.getIntentSender());
+            final Intent result = localReceiver.getResult();
+            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_SUCCESS);
+            String statusMessage = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+            assertEquals(statusMessage, expectedResult, status);
+        } catch (IllegalArgumentException | IOException | RemoteException e) {
+            Log.w(TAG, "Failed to install package; path=" + inPath, e);
+            fail("Failed to install package; path=" + inPath + ", e=" + e);
+        } finally {
+            IoUtils.closeQuietly(session);
+        }
+    }
+
+    private Uri getInstallablePackage(int fileResId, File outFile) {
+        Resources res = mContext.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(fileResId);
+        } catch (NotFoundException e) {
+            failStr("Failed to load resource with id: " + fileResId);
+        }
+        FileUtils.setPermissions(outFile.getPath(),
+                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+                -1, -1);
+        assertTrue(FileUtils.copyToFile(is, outFile));
+        FileUtils.setPermissions(outFile.getPath(),
+                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+                -1, -1);
+        return Uri.fromFile(outFile);
+    }
+
+    private ParsingPackage parsePackage(Uri packageURI) {
+        final String archiveFilePath = packageURI.getPath();
+        ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
+                new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(),
+                false /*collectCertificates*/);
+        if (result.isError()) {
+            throw new IllegalStateException(result.getErrorMessage(), result.getException());
+        }
+        return result.getResult();
+    }
+
+    private boolean checkSd(long pkgLen) {
+        String status = Environment.getExternalStorageState();
+        if (!status.equals(Environment.MEDIA_MOUNTED)) {
+            return false;
+        }
+        long sdSize = -1;
+        StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
+        sdSize = (long) sdStats.getAvailableBlocks() * (long) sdStats.getBlockSize();
+        // TODO check for thresholds here
+        return pkgLen <= sdSize;
+
+    }
+
+    private boolean checkInt(long pkgLen) {
+        StatFs intStats = new StatFs(Environment.getDataDirectory().getPath());
+        long intSize = (long) intStats.getBlockCount() * (long) intStats.getBlockSize();
+        long iSize = (long) intStats.getAvailableBlocks() * (long) intStats.getBlockSize();
+        // TODO check for thresholds here?
+        return pkgLen <= iSize;
+    }
+
+    private static final int INSTALL_LOC_INT = 1;
+
+    private static final int INSTALL_LOC_SD = 2;
+
+    private static final int INSTALL_LOC_ERR = -1;
+
+    private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
+        // Flags explicitly over ride everything else.
+        if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+            return INSTALL_LOC_INT;
+        }
+        // Manifest option takes precedence next
+        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+            if (checkSd(pkgLen)) {
+                return INSTALL_LOC_SD;
+            }
+            if (checkInt(pkgLen)) {
+                return INSTALL_LOC_INT;
+            }
+            return INSTALL_LOC_ERR;
+        }
+        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+            if (checkInt(pkgLen)) {
+                return INSTALL_LOC_INT;
+            }
+            return INSTALL_LOC_ERR;
+        }
+        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+            // Check for free memory internally
+            if (checkInt(pkgLen)) {
+                return INSTALL_LOC_INT;
+            }
+            // Check for free memory externally
+            if (checkSd(pkgLen)) {
+                return INSTALL_LOC_SD;
+            }
+            return INSTALL_LOC_ERR;
+        }
+        // Check for settings preference.
+        boolean checkSd = false;
+        int userPref = getDefaultInstallLoc();
+        if (userPref == APP_INSTALL_DEVICE) {
+            if (checkInt(pkgLen)) {
+                return INSTALL_LOC_INT;
+            }
+            return INSTALL_LOC_ERR;
+        } else if (userPref == APP_INSTALL_SDCARD) {
+            if (checkSd(pkgLen)) {
+                return INSTALL_LOC_SD;
+            }
+            return INSTALL_LOC_ERR;
+        }
+        // Default system policy for apps with no manifest option specified.
+        // Check for free memory internally
+        if (checkInt(pkgLen)) {
+            return INSTALL_LOC_INT;
+        }
+        return INSTALL_LOC_ERR;
+    }
+
+    private void assertInstall(ParsingPackage pkg, int flags, int expInstallLocation) {
+        try {
+            String pkgName = pkg.getPackageName();
+            ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+            assertNotNull(info);
+            assertEquals(pkgName, info.packageName);
+            File dataDir = Environment.getDataDirectory();
+            String appInstallParent = new File(dataDir, "app").getPath();
+            File srcDir = new File(info.sourceDir);
+            String srcPathParent = srcDir.getParentFile().getParentFile().getParent();
+            File publicSrcDir = new File(info.publicSourceDir);
+            String publicSrcPath = publicSrcDir.getParentFile().getParentFile().getParent();
+            long pkgLen = new File(info.sourceDir).length();
+            String expectedLibPath = new File(new File(info.sourceDir).getParentFile(), "lib")
+                    .getPath();
+
+            int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
+            if (rLoc == INSTALL_LOC_INT) {
+                assertEquals(appInstallParent, srcPathParent);
+                assertEquals(appInstallParent, publicSrcPath);
+                assertStartsWith("Native library should point to shared lib directory",
+                        expectedLibPath, info.nativeLibraryDir);
+                assertDirOwnerGroupPermsIfExists(
+                        "Native library directory should be owned by system:system and 0755",
+                        Process.SYSTEM_UID, Process.SYSTEM_UID,
+                        S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
+                        info.nativeLibraryDir);
+                assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+
+                // Make sure the native library dir is not a symlink
+                final File nativeLibDir = new File(info.nativeLibraryDir);
+                if (nativeLibDir.exists()) {
+                    try {
+                        assertEquals("Native library dir should not be a symlink",
+                                info.nativeLibraryDir, nativeLibDir.getCanonicalPath());
+                    } catch (IOException e) {
+                        fail("Can't read " + nativeLibDir.getPath());
+                    }
+                }
+            } else if (rLoc == INSTALL_LOC_SD) {
+                assertTrue("Application flags (" + info.flags
+                        + ") should contain FLAG_EXTERNAL_STORAGE",
+                        (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+                // Might need to check:
+                // ((info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0)
+                assertStartsWith("The APK path should point to the ASEC",
+                        SECURE_CONTAINERS_PREFIX, srcPathParent);
+                assertStartsWith("The public APK path should point to the ASEC",
+                        SECURE_CONTAINERS_PREFIX, publicSrcPath);
+                assertStartsWith("The native library path should point to the ASEC",
+                        SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir);
+
+                // Make sure the native library in /data/data/<app>/lib is a
+                // symlink to the ASEC
+                final File nativeLibSymLink = new File(info.dataDir, "lib");
+                assertTrue("Native library symlink should exist at " + nativeLibSymLink.getPath(),
+                        nativeLibSymLink.exists());
+                try {
+                    assertEquals(nativeLibSymLink.getPath() + " should be a symlink to "
+                            + info.nativeLibraryDir, info.nativeLibraryDir,
+                            nativeLibSymLink.getCanonicalPath());
+                } catch (IOException e) {
+                    fail("Can't read " + nativeLibSymLink.getPath());
+                }
+            } else {
+                // TODO handle error. Install should have failed.
+                fail("Install should have failed");
+            }
+        } catch (NameNotFoundException e) {
+            failStr("failed with exception : " + e);
+        }
+    }
+
+    private void assertDirOwnerGroupPermsIfExists(String reason, int uid, int gid, int perms,
+            String path) {
+        if (!new File(path).exists()) {
+            return;
+        }
+
+        final StructStat stat;
+        try {
+            stat = Os.lstat(path);
+        } catch (ErrnoException e) {
+            throw new AssertionError(reason + "\n" + "Got: " + path + " does not exist");
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        if (!S_ISDIR(stat.st_mode)) {
+            sb.append("\nExpected type: ");
+            sb.append(S_IFDIR);
+            sb.append("\ngot type: ");
+            sb.append((stat.st_mode & S_IFMT));
+        }
+
+        if (stat.st_uid != uid) {
+            sb.append("\nExpected owner: ");
+            sb.append(uid);
+            sb.append("\nGot owner: ");
+            sb.append(stat.st_uid);
+        }
+
+        if (stat.st_gid != gid) {
+            sb.append("\nExpected group: ");
+            sb.append(gid);
+            sb.append("\nGot group: ");
+            sb.append(stat.st_gid);
+        }
+
+        if ((stat.st_mode & ~S_IFMT) != perms) {
+            sb.append("\nExpected permissions: ");
+            sb.append(Integer.toOctalString(perms));
+            sb.append("\nGot permissions: ");
+            sb.append(Integer.toOctalString(stat.st_mode & ~S_IFMT));
+        }
+
+        if (sb.length() > 0) {
+            throw new AssertionError(reason + sb.toString());
+        }
+    }
+
+    private static void assertStartsWith(String prefix, String actual) {
+        assertStartsWith("", prefix, actual);
+    }
+
+    private static void assertStartsWith(String description, String prefix, String actual) {
+        if (!actual.startsWith(prefix)) {
+            StringBuilder sb = new StringBuilder(description);
+            sb.append("\nExpected prefix: ");
+            sb.append(prefix);
+            sb.append("\n     got: ");
+            sb.append(actual);
+            sb.append('\n');
+            throw new AssertionError(sb.toString());
+        }
+    }
+
+    private void assertNotInstalled(String pkgName) {
+        try {
+            ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+            fail(pkgName + " shouldnt be installed");
+        } catch (NameNotFoundException e) {
+        }
+    }
+
+    class InstallParams {
+        Uri packageURI;
+
+        ParsingPackage pkg;
+
+        InstallParams(String outFileName, int rawResId) {
+            this.pkg = getParsedPackage(outFileName, rawResId);
+            this.packageURI = Uri.fromFile(new File(pkg.getPath()));
+        }
+
+        InstallParams(ParsingPackage pkg) {
+            this.packageURI = Uri.fromFile(new File(pkg.getPath()));
+            this.pkg = pkg;
+        }
+
+        long getApkSize() {
+            File file = new File(pkg.getPath());
+            return file.length();
+        }
+    }
+
+    private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp)
+            throws Exception {
+        return installFromRawResource("install.apk", R.raw.install, flags, cleanUp, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    static final String PERM_PACKAGE = "package";
+
+    static final String PERM_DEFINED = "defined";
+
+    static final String PERM_UNDEFINED = "undefined";
+
+    static final String PERM_USED = "used";
+
+    static final String PERM_NOTUSED = "notused";
+
+    private void assertPermissions(String[] cmds) {
+        final PackageManager pm = getPm();
+        String pkg = null;
+        PackageInfo pkgInfo = null;
+        String mode = PERM_DEFINED;
+        int i = 0;
+        while (i < cmds.length) {
+            String cmd = cmds[i++];
+            if (cmd == PERM_PACKAGE) {
+                pkg = cmds[i++];
+                try {
+                    pkgInfo = pm.getPackageInfo(pkg,
+                            PackageManager.GET_PERMISSIONS
+                            | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+                } catch (NameNotFoundException e) {
+                    pkgInfo = null;
+                }
+            } else if (cmd == PERM_DEFINED || cmd == PERM_UNDEFINED
+                    || cmd == PERM_USED || cmd == PERM_NOTUSED) {
+                mode = cmds[i++];
+            } else {
+                if (mode == PERM_DEFINED) {
+                    try {
+                        PermissionInfo pi = pm.getPermissionInfo(cmd, 0);
+                        assertNotNull(pi);
+                        assertEquals(pi.packageName, pkg);
+                        assertEquals(pi.name, cmd);
+                        assertNotNull(pkgInfo);
+                        boolean found = false;
+                        for (int j = 0; j < pkgInfo.permissions.length && !found; j++) {
+                            if (pkgInfo.permissions[j].name.equals(cmd)) {
+                                found = true;
+                            }
+                        }
+                        if (!found) {
+                            fail("Permission not found: " + cmd);
+                        }
+                    } catch (NameNotFoundException e) {
+                        throw new RuntimeException(e);
+                    }
+                } else if (mode == PERM_UNDEFINED) {
+                    try {
+                        pm.getPermissionInfo(cmd, 0);
+                        throw new RuntimeException("Permission exists: " + cmd);
+                    } catch (NameNotFoundException e) {
+                    }
+                    if (pkgInfo != null) {
+                        boolean found = false;
+                        for (int j = 0; j < pkgInfo.permissions.length && !found; j++) {
+                            if (pkgInfo.permissions[j].name.equals(cmd)) {
+                                found = true;
+                            }
+                        }
+                        if (found) {
+                            fail("Permission still exists: " + cmd);
+                        }
+                    }
+                } else if (mode == PERM_USED || mode == PERM_NOTUSED) {
+                    boolean found = false;
+                    for (int j = 0; j < pkgInfo.requestedPermissions.length && !found; j++) {
+                        if (pkgInfo.requestedPermissions[j].equals(cmd)) {
+                            found = true;
+                        }
+                    }
+                    if (!found) {
+                        fail("Permission not requested: " + cmd);
+                    }
+                    if (mode == PERM_USED) {
+                        if (pm.checkPermission(cmd, pkg) != PackageManager.PERMISSION_GRANTED) {
+                            fail("Permission not granted: " + cmd);
+                        }
+                    } else {
+                        if (pm.checkPermission(cmd, pkg) != PackageManager.PERMISSION_DENIED) {
+                            fail("Permission granted: " + cmd);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private ParsingPackage getParsedPackage(String outFileName, int rawResId) {
+        PackageManager pm = mContext.getPackageManager();
+        File filesDir = mContext.getFilesDir();
+        File outFile = new File(filesDir, outFileName);
+        Uri packageURI = getInstallablePackage(rawResId, outFile);
+        return parsePackage(packageURI);
+    }
+
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install it.
+     */
+    private void installFromRawResource(InstallParams ip, int flags, boolean cleanUp, boolean fail,
+            int result, int expInstallLocation) throws Exception {
+        PackageManager pm = mContext.getPackageManager();
+        ParsingPackage pkg = ip.pkg;
+        Uri packageURI = ip.packageURI;
+        if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
+            // Make sure the package doesn't exist
+            try {
+                ApplicationInfo appInfo = pm.getApplicationInfo(pkg.getPackageName(),
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES);
+                GenericReceiver receiver = new DeleteReceiver(pkg.getPackageName());
+                invokeDeletePackage(pkg.getPackageName(), 0, receiver);
+            } catch (IllegalArgumentException | NameNotFoundException e) {
+            }
+        }
+        try {
+            if (fail) {
+                invokeInstallPackageFail(packageURI, flags, result);
+                if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
+                    assertNotInstalled(pkg.getPackageName());
+                }
+            } else {
+                InstallReceiver receiver = new InstallReceiver(pkg.getPackageName());
+                invokeInstallPackage(packageURI, flags, receiver, true);
+                // Verify installed information
+                assertInstall(pkg, flags, expInstallLocation);
+            }
+        } finally {
+            if (cleanUp) {
+                cleanUpInstall(ip);
+            }
+        }
+    }
+
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install it.
+     */
+    private InstallParams installFromRawResource(String outFileName, int rawResId, int flags,
+            boolean cleanUp, boolean fail, int result, int expInstallLocation) throws Exception {
+        InstallParams ip = new InstallParams(outFileName, rawResId);
+        installFromRawResource(ip, flags, cleanUp, fail, result, expInstallLocation);
+        return ip;
+    }
+
+    @LargeTest
+    public void testInstallNormalInternal() throws Exception {
+        sampleInstallFromRawResource(0, true);
+    }
+
+    /* ------------------------- Test replacing packages -------------- */
+    class ReplaceReceiver extends GenericReceiver {
+        String pkgName;
+
+        final static int INVALID = -1;
+
+        final static int REMOVED = 1;
+
+        final static int ADDED = 2;
+
+        final static int REPLACED = 3;
+
+        int removed = INVALID;
+
+        // for updated system apps only
+        boolean update = false;
+
+        ReplaceReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            if (update) {
+                filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            }
+            filter.addDataScheme("package");
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName == null || !pkgName.equals(installedPkg)) {
+                return false;
+            }
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                removed = REMOVED;
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                if (removed != REMOVED) {
+                    return false;
+                }
+                boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                if (!replacing) {
+                    return false;
+                }
+                removed = ADDED;
+                if (!update) {
+                    return true;
+                }
+            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                if (removed != ADDED) {
+                    return false;
+                }
+                removed = REPLACED;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install first and then replace it
+     * again.
+     */
+    private void sampleReplaceFromRawResource(int flags) throws Exception {
+        InstallParams ip = sampleInstallFromRawResource(flags, false);
+        boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+        Log.i(TAG, "replace=" + replace);
+        GenericReceiver receiver;
+        if (replace) {
+            receiver = new ReplaceReceiver(ip.pkg.getPackageName());
+            Log.i(TAG, "Creating replaceReceiver");
+        } else {
+            receiver = new InstallReceiver(ip.pkg.getPackageName());
+        }
+        try {
+            invokeInstallPackage(ip.packageURI, flags, receiver, true);
+            if (replace) {
+                assertInstall(ip.pkg, flags, ip.pkg.getInstallLocation());
+            }
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @LargeTest
+    public void testReplaceFlagDoesNotNeedToBeSet() throws Exception {
+        sampleReplaceFromRawResource(0);
+    }
+
+    @LargeTest
+    public void testReplaceNormalInternal() throws Exception {
+        sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
+    }
+
+    /* -------------- Delete tests --- */
+    private static class DeleteObserver extends IPackageDeleteObserver.Stub {
+        private CountDownLatch mLatch = new CountDownLatch(1);
+
+        private int mReturnCode;
+
+        private final String mPackageName;
+
+        private String mObservedPackage;
+
+        public DeleteObserver(String packageName) {
+            mPackageName = packageName;
+        }
+
+        public boolean isSuccessful() {
+            return mReturnCode == PackageManager.DELETE_SUCCEEDED;
+        }
+
+        public void packageDeleted(String packageName, int returnCode) throws RemoteException {
+            mObservedPackage = packageName;
+
+            mReturnCode = returnCode;
+
+            mLatch.countDown();
+        }
+
+        public void waitForCompletion(long timeoutMillis) {
+            final long deadline = SystemClock.uptimeMillis() + timeoutMillis;
+
+            long waitTime = timeoutMillis;
+            while (waitTime > 0) {
+                try {
+                    boolean done = mLatch.await(waitTime, TimeUnit.MILLISECONDS);
+                    if (done) {
+                        assertEquals(mPackageName, mObservedPackage);
+                        return;
+                    }
+                } catch (InterruptedException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+                waitTime = deadline - SystemClock.uptimeMillis();
+            }
+
+            throw new AssertionError("Timeout waiting for package deletion");
+        }
+    }
+
+    class DeleteReceiver extends GenericReceiver {
+        String pkgName;
+
+        DeleteReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                return false;
+            }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName.equals(installedPkg)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public boolean invokeDeletePackage(final String pkgName, int flags, GenericReceiver receiver)
+            throws Exception {
+        ApplicationInfo info = getPm().getApplicationInfo(pkgName,
+                PackageManager.MATCH_UNINSTALLED_PACKAGES);
+
+        mContext.registerReceiver(receiver, receiver.filter);
+        try {
+            final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
+            getPi().uninstall(pkgName,
+                    flags | PackageManager.DELETE_ALL_USERS,
+                    localReceiver.getIntentSender());
+            localReceiver.getResult();
+
+            assertUninstalled(info);
+
+            // Verify we received the broadcast
+            // TODO replace this with a CountDownLatch
+            synchronized (receiver) {
+                long waitTime = 0;
+                while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) {
+                    receiver.wait(WAIT_TIME_INCR);
+                    waitTime += WAIT_TIME_INCR;
+                }
+                if (!receiver.isDone()) {
+                    throw new Exception("Timed out waiting for PACKAGE_REMOVED notification");
+                }
+            }
+            return receiver.received;
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    private static void assertUninstalled(ApplicationInfo info) throws Exception {
+        File nativeLibraryFile = new File(info.nativeLibraryDir);
+        assertFalse("Native library directory " + info.nativeLibraryDir
+                + " should be erased", nativeLibraryFile.exists());
+    }
+
+    public void deleteFromRawResource(int iFlags, int dFlags) throws Exception {
+        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
+        boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0);
+        GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
+        try {
+            assertTrue(invokeDeletePackage(ip.pkg.getPackageName(), dFlags, receiver));
+            ApplicationInfo info = null;
+            Log.i(TAG, "okay4");
+            try {
+                info = getPm().getApplicationInfo(ip.pkg.getPackageName(),
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES);
+            } catch (NameNotFoundException e) {
+                info = null;
+            }
+            if (retainData) {
+                assertNotNull(info);
+                assertEquals(info.packageName, ip.pkg.getPackageName());
+            } else {
+                assertNull(info);
+            }
+        } catch (Exception e) {
+            failStr(e);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @LargeTest
+    public void testDeleteNormalInternal() throws Exception {
+        deleteFromRawResource(0, 0);
+    }
+
+
+    @LargeTest
+    public void testDeleteNormalInternalRetainData() throws Exception {
+        deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
+    }
+
+    void cleanUpInstall(InstallParams ip) throws Exception {
+        if (ip == null) {
+            return;
+        }
+        Runtime.getRuntime().gc();
+        try {
+            cleanUpInstall(ip.pkg.getPackageName());
+        } finally {
+            File outFile = new File(ip.pkg.getPath());
+            if (outFile != null && outFile.exists()) {
+                outFile.delete();
+            }
+        }
+    }
+
+    private void cleanUpInstall(String pkgName) throws Exception {
+        if (pkgName == null) {
+            return;
+        }
+        Log.i(TAG, "Deleting package : " + pkgName);
+        try {
+            final ApplicationInfo info = getPm().getApplicationInfo(pkgName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES);
+            if (info != null) {
+                final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
+                getPi().uninstall(pkgName,
+                        PackageManager.DELETE_ALL_USERS,
+                        localReceiver.getIntentSender());
+                localReceiver.getResult();
+                assertUninstalled(info);
+            }
+        } catch (IllegalArgumentException | NameNotFoundException e) {
+        }
+    }
+
+    @LargeTest
+    public void testManifestInstallLocationInternal() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_internal,
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    @LargeTest
+    public void testManifestInstallLocationSdcard() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+    }
+
+    @LargeTest
+    public void testManifestInstallLocationAuto() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_auto,
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+    }
+
+    @LargeTest
+    public void testManifestInstallLocationUnspecified() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_unspecified,
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int iFlags = 0;
+        int iApk = R.raw.install_loc_internal;
+        int rFlags = 0;
+        int rApk = R.raw.install_loc_sdcard;
+        InstallParams ip = installFromRawResource("install.apk", iApk,
+                iFlags, false,
+                false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.getPackageName());
+        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
+        try {
+            InstallParams rp = installFromRawResource("install.apk", rApk,
+                    replaceFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+            assertInstall(rp.pkg, replaceFlags, rp.pkg.getInstallLocation());
+        } catch (Exception e) {
+            failStr("Failed with exception : " + e);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @LargeTest
+    public void testManifestInstallLocationReplaceSdcardInternal() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int iFlags = 0;
+        int iApk = R.raw.install_loc_sdcard;
+        int rFlags = 0;
+        int rApk = R.raw.install_loc_unspecified;
+        InstallParams ip = installFromRawResource("install.apk", iApk,
+                iFlags, false,
+                false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
+        try {
+            InstallParams rp = installFromRawResource("install.apk", rApk,
+                    replaceFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+            assertInstall(rp.pkg, replaceFlags, ip.pkg.getInstallLocation());
+        } catch (Exception e) {
+            failStr("Failed with exception : " + e);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    class MoveReceiver extends GenericReceiver {
+        String pkgName;
+
+        final static int INVALID = -1;
+
+        final static int REMOVED = 1;
+
+        final static int ADDED = 2;
+
+        int removed = INVALID;
+
+        MoveReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            Log.i(TAG, "MoveReceiver::" + action);
+            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (list != null) {
+                    for (String pkg : list) {
+                        if (pkg.equals(pkgName)) {
+                            removed = REMOVED;
+                            break;
+                        }
+                    }
+                }
+                removed = REMOVED;
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                if (removed != REMOVED) {
+                    return false;
+                }
+                String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (list != null) {
+                    for (String pkg : list) {
+                        if (pkg.equals(pkgName)) {
+                            removed = ADDED;
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    public boolean invokeMovePackage(String pkgName, int flags, GenericReceiver receiver)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    private boolean invokeMovePackageFail(String pkgName, int flags, int errCode) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    private int getDefaultInstallLoc() {
+        int origDefaultLoc = PackageInfo.INSTALL_LOCATION_AUTO;
+        try {
+            origDefaultLoc = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.DEFAULT_INSTALL_LOCATION);
+        } catch (SettingNotFoundException e1) {
+        }
+        return origDefaultLoc;
+    }
+
+    private void setInstallLoc(int loc) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
+    }
+
+    /*
+     * Tests for moving apps between internal and external storage
+     */
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install first and then replace it
+     * again.
+     */
+
+    private void moveFromRawResource(String outFileName, int rawResId, int installFlags,
+            int moveFlags, boolean cleanUp, boolean fail, int result) throws Exception {
+        int origDefaultLoc = getDefaultInstallLoc();
+        InstallParams ip = null;
+        try {
+            setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
+            // Install first
+            ip = installFromRawResource("install.apk", rawResId, installFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            ApplicationInfo oldAppInfo = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
+            if (fail) {
+                assertTrue(invokeMovePackageFail(ip.pkg.getPackageName(), moveFlags, result));
+                ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
+                assertNotNull(info);
+                assertEquals(oldAppInfo.flags, info.flags);
+            } else {
+                // Create receiver based on expRetCode
+                MoveReceiver receiver = new MoveReceiver(ip.pkg.getPackageName());
+                boolean retCode = invokeMovePackage(ip.pkg.getPackageName(), moveFlags, receiver);
+                assertTrue(retCode);
+                ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
+                assertNotNull("ApplicationInfo for recently installed application should exist",
+                        info);
+                if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) {
+                    assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should NOT be set",
+                            (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0);
+                    assertStartsWith("Native library dir should be in dataDir",
+                            info.dataDir, info.nativeLibraryDir);
+                } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0) {
+                    assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should be set",
+                            (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+                    assertStartsWith("Native library dir should point to ASEC",
+                            SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir);
+                }
+            }
+        } catch (NameNotFoundException e) {
+            failStr("Pkg hasnt been installed correctly");
+        } finally {
+            if (ip != null) {
+                cleanUpInstall(ip);
+            }
+            // Restore default install location
+            setInstallLoc(origDefaultLoc);
+        }
+    }
+
+    private void sampleMoveFromRawResource(int installFlags, int moveFlags, boolean fail,
+            int result) throws Exception {
+        moveFromRawResource("install.apk",
+                R.raw.install, installFlags, moveFlags, true,
+                fail, result);
+    }
+
+    @LargeTest
+    public void testMoveAppInternalToExternal() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int installFlags = PackageManager.INSTALL_INTERNAL;
+        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
+        boolean fail = false;
+        int result = PackageManager.MOVE_SUCCEEDED;
+        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
+    }
+
+    @Suppress
+    @LargeTest
+    public void testMoveAppInternalToInternal() throws Exception {
+        int installFlags = PackageManager.INSTALL_INTERNAL;
+        int moveFlags = PackageManager.MOVE_INTERNAL;
+        boolean fail = true;
+        int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
+        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
+    }
+
+    @LargeTest
+    public void testMoveAppFailInternalToExternalDelete() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int installFlags = 0;
+        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
+        boolean fail = true;
+        final int result = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+
+        int rawResId = R.raw.install;
+        int origDefaultLoc = getDefaultInstallLoc();
+        InstallParams ip = null;
+        try {
+            PackageManager pm = getPm();
+            setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
+            // Install first
+            ip = installFromRawResource("install.apk", R.raw.install, installFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            // Delete the package now retaining data.
+            GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
+            invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
+            assertTrue(invokeMovePackageFail(ip.pkg.getPackageName(), moveFlags, result));
+        } catch (Exception e) {
+            failStr(e);
+        } finally {
+            if (ip != null) {
+                cleanUpInstall(ip);
+            }
+            // Restore default install location
+            setInstallLoc(origDefaultLoc);
+        }
+    }
+
+    /*---------- Recommended install location tests ----*/
+    /*
+     * PrecedenceSuffixes:
+     * Flag : FlagI, FlagE, FlagF
+     * I - internal, E - external, F - forward locked, Flag suffix absent if not using any option.
+     * Manifest: ManifestI, ManifestE, ManifestA, Manifest suffix absent if not using any option.
+     * Existing: Existing suffix absent if not existing.
+     * User: UserI, UserE, UserA, User suffix absent if not existing.
+     *
+     */
+
+    /*
+     * Install an app on internal flash
+     */
+    @LargeTest
+    public void testFlagI() throws Exception {
+        sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, true);
+    }
+
+    /*
+     * Install an app with both internal and manifest option set.
+     * should install on internal.
+     */
+    @LargeTest
+    public void testFlagIManifestI() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_internal,
+                PackageManager.INSTALL_INTERNAL,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+    /*
+     * Install an app with both internal and manifest preference for
+     * preferExternal. Should install on internal.
+     */
+    @LargeTest
+    public void testFlagIManifestE() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+                PackageManager.INSTALL_INTERNAL,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+    /*
+     * Install an app with both internal and manifest preference for
+     * auto. should install internal.
+     */
+    @LargeTest
+    public void testFlagIManifestA() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_auto,
+                PackageManager.INSTALL_INTERNAL,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    /*
+     * The following test functions verify install location for existing apps.
+     * ie existing app can be installed internally or externally. If install
+     * flag is explicitly set it should override current location. If manifest location
+     * is set, that should over ride current location too. if not the existing install
+     * location should be honoured.
+     * testFlagI/E/F/ExistingI/E -
+     */
+    @LargeTest
+    public void testFlagIExistingI() throws Exception {
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
+        // First install.
+        installFromRawResource("install.apk", R.raw.install,
+                iFlags,
+                false,
+                false, -1,
+                -1);
+        // Replace now
+        installFromRawResource("install.apk", R.raw.install,
+                rFlags,
+                true,
+                false, -1,
+                -1);
+    }
+
+    /*
+     * The following set of tests verify the installation of apps with
+     * install location attribute set to internalOnly, preferExternal and auto.
+     * The manifest option should dictate the install location.
+     * public void testManifestI/E/A
+     * TODO out of memory fall back behaviour.
+     */
+    @LargeTest
+    public void testManifestI() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_internal,
+                0,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    @LargeTest
+    public void testManifestE() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+                0,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+    }
+
+    @LargeTest
+    public void testManifestA() throws Exception {
+        installFromRawResource("install.apk", R.raw.install_loc_auto,
+                0,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    /*
+     * The following set of tests verify the installation of apps
+     * with install location attribute set to internalOnly, preferExternal and auto
+     * for already existing apps. The manifest option should take precedence.
+     * TODO add out of memory fall back behaviour.
+     * testManifestI/E/AExistingI/E
+     */
+    @LargeTest
+    public void testManifestIExistingI() throws Exception {
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        // First install.
+        installFromRawResource("install.apk", R.raw.install,
+                iFlags,
+                false,
+                false, -1,
+                -1);
+        // Replace now
+        installFromRawResource("install.apk", R.raw.install_loc_internal,
+                rFlags,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    @LargeTest
+    public void testManifestEExistingI() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        // First install.
+        installFromRawResource("install.apk", R.raw.install,
+                iFlags,
+                false,
+                false, -1,
+                -1);
+        // Replace now
+        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+                rFlags,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+    }
+
+    @LargeTest
+    public void testManifestAExistingI() throws Exception {
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        // First install.
+        installFromRawResource("install.apk", R.raw.install,
+                iFlags,
+                false,
+                false, -1,
+                -1);
+        // Replace now
+        installFromRawResource("install.apk", R.raw.install_loc_auto,
+                rFlags,
+                true,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_AUTO);
+    }
+
+    /*
+     * The following set of tests check install location for existing
+     * application based on user setting.
+     */
+    private int getExpectedInstallLocation(int userSetting) {
+        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+        boolean enable = getUserSettingSetInstallLocation();
+        if (enable) {
+            if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
+                iloc = PackageInfo.INSTALL_LOCATION_AUTO;
+            } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
+                iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
+            } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
+                iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+            }
+        }
+        return iloc;
+    }
+
+    private void setExistingXUserX(int userSetting, int iFlags, int iloc) throws Exception {
+        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        // First install.
+        installFromRawResource("install.apk", R.raw.install,
+                iFlags,
+                false,
+                false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        int origSetting = getDefaultInstallLoc();
+        try {
+            // Set user setting
+            setInstallLoc(userSetting);
+            // Replace now
+            installFromRawResource("install.apk", R.raw.install,
+                    rFlags,
+                    true,
+                    false, -1,
+                    iloc);
+        } finally {
+            setInstallLoc(origSetting);
+        }
+    }
+    @LargeTest
+    public void testExistingIUserI() throws Exception {
+        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    @LargeTest
+    public void testExistingIUserE() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    @LargeTest
+    public void testExistingIUserA() throws Exception {
+        int userSetting = PackageHelper.APP_INSTALL_AUTO;
+        int iFlags = PackageManager.INSTALL_INTERNAL;
+        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+    }
+
+    /*
+     * The following set of tests verify that the user setting defines
+     * the install location.
+     *
+     */
+    private boolean getUserSettingSetInstallLocation() {
+        try {
+            return Settings.Global.getInt(
+                    mContext.getContentResolver(), Settings.Global.SET_INSTALL_LOCATION) != 0;
+        } catch (SettingNotFoundException e1) {
+        }
+        return false;
+    }
+
+    private void setUserSettingSetInstallLocation(boolean value) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.SET_INSTALL_LOCATION, value ? 1 : 0);
+    }
+
+    private void setUserX(boolean enable, int userSetting, int iloc) throws Exception {
+        boolean origUserSetting = getUserSettingSetInstallLocation();
+        int origSetting = getDefaultInstallLoc();
+        try {
+            setUserSettingSetInstallLocation(enable);
+            // Set user setting
+            setInstallLoc(userSetting);
+            // Replace now
+            installFromRawResource("install.apk", R.raw.install,
+                    0,
+                    true,
+                    false, -1,
+                    iloc);
+        } finally {
+            // Restore original setting
+            setUserSettingSetInstallLocation(origUserSetting);
+            setInstallLoc(origSetting);
+        }
+    }
+    @LargeTest
+    public void testUserI() throws Exception {
+        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+        int iloc = getExpectedInstallLocation(userSetting);
+        setUserX(true, userSetting, iloc);
+    }
+
+    @LargeTest
+    public void testUserE() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+        int iloc = getExpectedInstallLocation(userSetting);
+        setUserX(true, userSetting, iloc);
+    }
+
+    @LargeTest
+    public void testUserA() throws Exception {
+        int userSetting = PackageHelper.APP_INSTALL_AUTO;
+        int iloc = getExpectedInstallLocation(userSetting);
+        setUserX(true, userSetting, iloc);
+    }
+
+    /*
+     * The following set of tests turn on/off the basic
+     * user setting for turning on install location.
+     */
+    @LargeTest
+    public void testUserPrefOffUserI() throws Exception {
+        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+        setUserX(false, userSetting, iloc);
+    }
+
+    @LargeTest
+    public void testUserPrefOffUserE() throws Exception {
+        // Do not run on devices with emulated external storage.
+        if (Environment.isExternalStorageEmulated()) {
+            return;
+        }
+
+        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+        setUserX(false, userSetting, iloc);
+    }
+
+    @LargeTest
+    public void testUserPrefOffA() throws Exception {
+        int userSetting = PackageHelper.APP_INSTALL_AUTO;
+        int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+        setUserX(false, userSetting, iloc);
+    }
+
+    static final String BASE_PERMISSIONS_DEFINED[] = new String[] {
+        PERM_PACKAGE, "com.android.unit_tests.install_decl_perm",
+        PERM_DEFINED,
+        "com.android.frameworks.coretests.NORMAL",
+        "com.android.frameworks.coretests.DANGEROUS",
+        "com.android.frameworks.coretests.SIGNATURE",
+    };
+
+    static final String BASE_PERMISSIONS_UNDEFINED[] = new String[] {
+        PERM_PACKAGE, "com.android.frameworks.coretests.install_decl_perm",
+        PERM_UNDEFINED,
+        "com.android.frameworks.coretests.NORMAL",
+        "com.android.frameworks.coretests.DANGEROUS",
+        "com.android.frameworks.coretests.SIGNATURE",
+    };
+
+    static final String BASE_PERMISSIONS_USED[] = new String[] {
+        PERM_PACKAGE, "com.android.frameworks.coretests.install_use_perm_good",
+        PERM_USED,
+        "com.android.frameworks.coretests.NORMAL",
+        "com.android.frameworks.coretests.DANGEROUS",
+        "com.android.frameworks.coretests.SIGNATURE",
+    };
+
+    static final String BASE_PERMISSIONS_NOTUSED[] = new String[] {
+        PERM_PACKAGE, "com.android.frameworks.coretests.install_use_perm_good",
+        PERM_NOTUSED,
+        "com.android.frameworks.coretests.NORMAL",
+        "com.android.frameworks.coretests.DANGEROUS",
+        "com.android.frameworks.coretests.SIGNATURE",
+    };
+
+    static final String BASE_PERMISSIONS_SIGUSED[] = new String[] {
+        PERM_PACKAGE, "com.android.frameworks.coretests.install_use_perm_good",
+        PERM_USED,
+        "com.android.frameworks.coretests.SIGNATURE",
+        PERM_NOTUSED,
+        "com.android.frameworks.coretests.NORMAL",
+        "com.android.frameworks.coretests.DANGEROUS",
+    };
+
+    /*
+     * Ensure that permissions are properly declared.
+     */
+    @LargeTest
+    public void testInstallDeclaresPermissions() throws Exception {
+        InstallParams ip = null;
+        InstallParams ip2 = null;
+        try {
+            // **: Upon installing a package, are its declared permissions published?
+
+            int iFlags = PackageManager.INSTALL_INTERNAL;
+            int iApk = R.raw.install_decl_perm;
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+
+            // **: Upon installing package, are its permissions granted?
+
+            int i2Flags = PackageManager.INSTALL_INTERNAL;
+            int i2Apk = R.raw.install_use_perm_good;
+            ip2 = installFromRawResource("install2.apk", i2Apk,
+                    i2Flags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
+            assertPermissions(BASE_PERMISSIONS_USED);
+
+            // **: Upon removing but not deleting, are permissions retained?
+
+            GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
+
+            try {
+                invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
+            } catch (Exception e) {
+                failStr(e);
+            }
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            assertPermissions(BASE_PERMISSIONS_USED);
+
+            // **: Upon re-installing, are permissions retained?
+
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags | PackageManager.INSTALL_REPLACE_EXISTING, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            assertPermissions(BASE_PERMISSIONS_USED);
+
+            // **: Upon deleting package, are all permissions removed?
+
+            try {
+                invokeDeletePackage(ip.pkg.getPackageName(), 0, receiver);
+                ip = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+            assertPermissions(BASE_PERMISSIONS_UNDEFINED);
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+            // **: Delete package using permissions; nothing to check here.
+
+            GenericReceiver receiver2 = new DeleteReceiver(ip2.pkg.getPackageName());
+            try {
+                invokeDeletePackage(ip2.pkg.getPackageName(), 0, receiver);
+                ip2 = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+
+            // **: Re-install package using permissions; no permissions can be granted.
+
+            ip2 = installFromRawResource("install2.apk", i2Apk,
+                    i2Flags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+            // **: Upon installing declaring package, are sig permissions granted
+            // to other apps (but not other perms)?
+
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            assertPermissions(BASE_PERMISSIONS_SIGUSED);
+
+            // **: Re-install package using permissions; are all permissions granted?
+
+            ip2 = installFromRawResource("install2.apk", i2Apk,
+                    i2Flags | PackageManager.INSTALL_REPLACE_EXISTING, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+            // **: Upon deleting package, are all permissions removed?
+
+            try {
+                invokeDeletePackage(ip.pkg.getPackageName(), 0, receiver);
+                ip = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+            assertPermissions(BASE_PERMISSIONS_UNDEFINED);
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+            // **: Delete package using permissions; nothing to check here.
+
+            try {
+                invokeDeletePackage(ip2.pkg.getPackageName(), 0, receiver);
+                ip2 = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+
+        } finally {
+            if (ip2 != null) {
+                cleanUpInstall(ip2);
+            }
+            if (ip != null) {
+                cleanUpInstall(ip);
+            }
+        }
+    }
+
+    /*
+     * The following series of tests are related to upgrading apps with
+     * different certificates.
+     */
+    private static final int APP1_UNSIGNED = R.raw.install_app1_unsigned;
+
+    private static final int APP1_CERT1 = R.raw.install_app1_cert1;
+
+    private static final int APP1_CERT2 = R.raw.install_app1_cert2;
+
+    private static final int APP1_CERT1_CERT2 = R.raw.install_app1_cert1_cert2;
+
+    private static final int APP1_CERT3_CERT4 = R.raw.install_app1_cert3_cert4;
+
+    private static final int APP1_CERT3 = R.raw.install_app1_cert3;
+
+    private static final int APP1_CERT5 = R.raw.install_app1_cert5;
+
+    private static final int APP1_CERT5_ROTATED_CERT6 = R.raw.install_app1_cert5_rotated_cert6;
+
+    private static final int APP1_CERT6 = R.raw.install_app1_cert6;
+
+    private static final int APP2_UNSIGNED = R.raw.install_app2_unsigned;
+
+    private static final int APP2_CERT1 = R.raw.install_app2_cert1;
+
+    private static final int APP2_CERT2 = R.raw.install_app2_cert2;
+
+    private static final int APP2_CERT1_CERT2 = R.raw.install_app2_cert1_cert2;
+
+    private static final int APP2_CERT3 = R.raw.install_app2_cert3;
+
+    private static final int APP2_CERT5_ROTATED_CERT6 = R.raw.install_app2_cert5_rotated_cert6;
+
+    private InstallParams replaceCerts(int apk1, int apk2, boolean cleanUp, boolean fail,
+            int retCode) throws Exception {
+        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        String apk1Name = "install1.apk";
+        String apk2Name = "install2.apk";
+        ParsingPackage pkg1 = getParsedPackage(apk1Name, apk1);
+        try {
+            InstallParams ip = installFromRawResource(apk1Name, apk1, 0, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            installFromRawResource(apk2Name, apk2, rFlags, false,
+                    fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            return ip;
+        } catch (Exception e) {
+            failStr(e.getMessage());
+        } finally {
+            if (cleanUp) {
+                cleanUpInstall(pkg1.getPackageName());
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Test that an app signed with two certificates can be upgraded by the
+     * same app signed with two certificates.
+     */
+    @LargeTest
+    public void testReplaceMatchAllCerts() throws Exception {
+        replaceCerts(APP1_CERT1_CERT2, APP1_CERT1_CERT2, true, false, -1);
+    }
+
+    /*
+     * Test that an app signed with two certificates cannot be upgraded
+     * by an app signed with a different certificate.
+     */
+    @LargeTest
+    public void testReplaceMatchNoCerts1() throws Exception {
+        replaceCerts(APP1_CERT1_CERT2, APP1_CERT3, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Test that an app signed with two certificates cannot be upgraded
+     * by an app signed with a different certificate.
+     */
+    @LargeTest
+    public void testReplaceMatchNoCerts2() throws Exception {
+        replaceCerts(APP1_CERT1_CERT2, APP1_CERT3_CERT4, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Test that an app signed with two certificates cannot be upgraded by
+     * an app signed with a subset of initial certificates.
+     */
+    @LargeTest
+    public void testReplaceMatchSomeCerts1() throws Exception {
+        replaceCerts(APP1_CERT1_CERT2, APP1_CERT1, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Test that an app signed with two certificates cannot be upgraded by
+     * an app signed with the last certificate.
+     */
+    @LargeTest
+    public void testReplaceMatchSomeCerts2() throws Exception {
+        replaceCerts(APP1_CERT1_CERT2, APP1_CERT2, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Test that an app signed with a certificate can be upgraded by app
+     * signed with a superset of certificates.
+     */
+    @LargeTest
+    public void testReplaceMatchMoreCerts() throws Exception {
+        replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Test that an app signed with a certificate can be upgraded by app
+     * signed with a superset of certificates. Then verify that the an app
+     * signed with the original set of certs cannot upgrade the new one.
+     */
+    @LargeTest
+    public void testReplaceMatchMoreCertsReplaceSomeCerts() throws Exception {
+        InstallParams ip = replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, false, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+        try {
+            int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+            installFromRawResource("install.apk", APP1_CERT1, rFlags, false,
+                    false, -1,
+                    PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        } catch (Exception e) {
+            failStr(e.getMessage());
+        } finally {
+            if (ip != null) {
+                cleanUpInstall(ip);
+            }
+        }
+    }
+
+    /**
+     * The following tests are related to testing KeySets-based key rotation
+     */
+    /*
+     * Check if an apk which does not specify an upgrade-keyset may be upgraded
+     * by an apk which does
+     */
+    public void testNoKSToUpgradeKS() throws Exception {
+        replaceCerts(R.raw.keyset_sa_unone, R.raw.keyset_sa_ua, true, false, -1);
+    }
+
+    /*
+     * Check if an apk which does specify an upgrade-keyset may be downgraded to
+     * an apk which does not
+     */
+    public void testUpgradeKSToNoKS() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sa_unone, true, false, -1);
+    }
+
+    /*
+     * Check if an apk signed by a key other than the upgrade keyset can update
+     * an app
+     */
+    public void testUpgradeKSWithWrongKey() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sb_ua, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Check if an apk signed by its signing key, which is not an upgrade key,
+     * can upgrade an app.
+     */
+    public void testUpgradeKSWithWrongSigningKey() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ub, R.raw.keyset_sa_ub, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Check if an apk signed by its upgrade key, which is not its signing key,
+     * can upgrade an app.
+     */
+    public void testUpgradeKSWithUpgradeKey() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ub, R.raw.keyset_sb_ub, true, false, -1);
+    }
+    /*
+     * Check if an apk signed by its upgrade key, which is its signing key, can
+     * upgrade an app.
+     */
+    public void testUpgradeKSWithSigningUpgradeKey() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sa_ua, true, false, -1);
+    }
+
+    /*
+     * Check if an apk signed by multiple keys, one of which is its upgrade key,
+     * can upgrade an app.
+     */
+    public void testMultipleUpgradeKSWithUpgradeKey() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sab_ua, true, false, -1);
+    }
+
+    /*
+     * Check if an apk signed by multiple keys, one of which is its signing key,
+     * but none of which is an upgrade key, can upgrade an app.
+     */
+    public void testMultipleUpgradeKSWithSigningKey() throws Exception {
+        replaceCerts(R.raw.keyset_sau_ub, R.raw.keyset_sa_ua, true, true,
+                PackageInstaller.STATUS_FAILURE_CONFLICT);
+    }
+
+    /*
+     * Check if an apk which defines multiple (two) upgrade keysets is
+     * upgrade-able by either.
+     */
+    public void testUpgradeKSWithMultipleUpgradeKeySets() throws Exception {
+        replaceCerts(R.raw.keyset_sa_ua_ub, R.raw.keyset_sa_ua, true, false, -1);
+        replaceCerts(R.raw.keyset_sa_ua_ub, R.raw.keyset_sb_ub, true, false, -1);
+    }
+
+    /*
+     * Check if an apk's sigs are changed after upgrading with a non-signing
+     * key.
+     *
+     * TODO: consider checking against hard-coded Signatures in the Sig-tests
+     */
+    public void testSigChangeAfterUpgrade() throws Exception {
+        // install original apk and grab sigs
+        installFromRawResource("tmp.apk", R.raw.keyset_sa_ub,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        PackageManager pm = getPm();
+        String pkgName = "com.android.frameworks.coretests.keysets";
+        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should only have one signature, sig A",
+                pi.signatures.length == 1);
+        String sigBefore = pi.signatures[0].toCharsString();
+        // install apk signed by different upgrade KeySet
+        installFromRawResource("tmp2.apk", R.raw.keyset_sb_ub,
+                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should only have one signature, sig B",
+                pi.signatures.length == 1);
+        String sigAfter = pi.signatures[0].toCharsString();
+        assertFalse("Package signatures did not change after upgrade!",
+                sigBefore.equals(sigAfter));
+        cleanUpInstall(pkgName);
+    }
+
+    /*
+     * Check if an apk's sig is the same  after upgrading with a signing
+     * key.
+     */
+    public void testSigSameAfterUpgrade() throws Exception {
+        // install original apk and grab sigs
+        installFromRawResource("tmp.apk", R.raw.keyset_sa_ua,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        PackageManager pm = getPm();
+        String pkgName = "com.android.frameworks.coretests.keysets";
+        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should only have one signature, sig A",
+                pi.signatures.length == 1);
+        String sigBefore = pi.signatures[0].toCharsString();
+        // install apk signed by same upgrade KeySet
+        installFromRawResource("tmp2.apk", R.raw.keyset_sa_ua,
+                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should only have one signature, sig A",
+                pi.signatures.length == 1);
+        String sigAfter = pi.signatures[0].toCharsString();
+        assertTrue("Package signatures changed after upgrade!",
+                sigBefore.equals(sigAfter));
+        cleanUpInstall(pkgName);
+    }
+
+    /*
+     * Check if an apk's sigs are the same after upgrading with an app with
+     * a subset of the original signing keys.
+     */
+    public void testSigRemovedAfterUpgrade() throws Exception {
+        // install original apk and grab sigs
+        installFromRawResource("tmp.apk", R.raw.keyset_sab_ua,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        PackageManager pm = getPm();
+        String pkgName = "com.android.frameworks.coretests.keysets";
+        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should have two signatures, sig A and sig B",
+                pi.signatures.length == 2);
+        Set<String> sigsBefore = new HashSet<String>();
+        for (int i = 0; i < pi.signatures.length; i++) {
+            sigsBefore.add(pi.signatures[i].toCharsString());
+        }
+        // install apk signed subset upgrade KeySet
+        installFromRawResource("tmp2.apk", R.raw.keyset_sa_ua,
+                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should only have one signature, sig A",
+                pi.signatures.length == 1);
+        String sigAfter = pi.signatures[0].toCharsString();
+        assertTrue("Original package signatures did not contain new sig",
+                sigsBefore.contains(sigAfter));
+        cleanUpInstall(pkgName);
+    }
+
+    /*
+     * Check if an apk's sigs are added to after upgrading with an app with
+     * a superset of the original signing keys.
+     */
+    public void testSigAddedAfterUpgrade() throws Exception {
+        // install original apk and grab sigs
+        installFromRawResource("tmp.apk", R.raw.keyset_sa_ua,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        PackageManager pm = getPm();
+        String pkgName = "com.android.frameworks.coretests.keysets";
+        PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should only have one signature, sig A",
+                pi.signatures.length == 1);
+        String sigBefore = pi.signatures[0].toCharsString();
+        // install apk signed subset upgrade KeySet
+        installFromRawResource("tmp2.apk", R.raw.keyset_sab_ua,
+                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
+        assertTrue("Package should have two signatures, sig A and sig B",
+                pi.signatures.length == 2);
+        Set<String> sigsAfter = new HashSet<String>();
+        for (int i = 0; i < pi.signatures.length; i++) {
+            sigsAfter.add(pi.signatures[i].toCharsString());
+        }
+        assertTrue("Package signatures did not change after upgrade!",
+                sigsAfter.contains(sigBefore));
+        cleanUpInstall(pkgName);
+    }
+
+    /*
+     * Check if an apk gains signature-level permission after changing to the a
+     * new signature, for which a permission should be granted.
+     */
+    public void testUpgradeSigPermGained() throws Exception {
+        // install apk which defines permission
+        installFromRawResource("permDef.apk", R.raw.keyset_permdef_sa_unone,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        // install apk which uses permission but does not have sig
+        installFromRawResource("permUse.apk", R.raw.keyset_permuse_sb_ua_ub,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        // verify that package does not have perm before
+        PackageManager pm = getPm();
+        String permPkgName = "com.android.frameworks.coretests.keysets_permdef";
+        String pkgName = "com.android.frameworks.coretests.keysets";
+        String permName = "com.android.frameworks.coretests.keysets_permdef.keyset_perm";
+        assertFalse("keyset permission granted to app without same signature!",
+                    pm.checkPermission(permName, pkgName)
+                    == PackageManager.PERMISSION_GRANTED);
+        // upgrade to apk with perm signature
+        installFromRawResource("permUse2.apk", R.raw.keyset_permuse_sa_ua_ub,
+                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertTrue("keyset permission not granted to app after upgrade to same sig",
+                    pm.checkPermission(permName, pkgName)
+                    == PackageManager.PERMISSION_GRANTED);
+        cleanUpInstall(permPkgName);
+        cleanUpInstall(pkgName);
+    }
+
+    /*
+     * Check if an apk loses signature-level permission after changing to the a
+     * new signature, from one which a permission should be granted.
+     */
+    public void testUpgradeSigPermLost() throws Exception {
+        // install apk which defines permission
+        installFromRawResource("permDef.apk", R.raw.keyset_permdef_sa_unone,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        // install apk which uses permission, signed by same sig
+        installFromRawResource("permUse.apk", R.raw.keyset_permuse_sa_ua_ub,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        // verify that package does not have perm before
+        PackageManager pm = getPm();
+        String permPkgName = "com.android.frameworks.coretests.keysets_permdef";
+        String pkgName = "com.android.frameworks.coretests.keysets";
+        String permName = "com.android.frameworks.coretests.keysets_permdef.keyset_perm";
+        assertTrue("keyset permission not granted to app with same sig",
+                    pm.checkPermission(permName, pkgName)
+                    == PackageManager.PERMISSION_GRANTED);
+        // upgrade to apk without perm signature
+        installFromRawResource("permUse2.apk", R.raw.keyset_permuse_sb_ua_ub,
+                PackageManager.INSTALL_REPLACE_EXISTING, false, false, -1,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+
+        assertFalse("keyset permission not revoked from app which upgraded to a "
+                    + "different signature",
+                    pm.checkPermission(permName, pkgName)
+                    == PackageManager.PERMISSION_GRANTED);
+        cleanUpInstall(permPkgName);
+        cleanUpInstall(pkgName);
+    }
+
+    /**
+     * The following tests are related to testing KeySets-based API
+     */
+
+    /*
+     * testGetSigningKeySetNull - ensure getSigningKeySet() returns null on null
+     * input and when calling a package other than that which made the call.
+     */
+    public void testGetSigningKeySet() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet ks;
+        try {
+            ks = pm.getSigningKeySet(null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getSigningKeySet("keysets.test.bogus.package");
+            assertTrue(false); // should have thrown
+        } catch (IllegalArgumentException e) {
+        }
+        final InstallParams ip = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            ks = pm.getSigningKeySet(otherPkgName);
+            assertTrue(false); // should have thrown
+        } catch (SecurityException e) {
+        } finally {
+            cleanUpInstall(ip);
+        }
+        ks = pm.getSigningKeySet(mContext.getPackageName());
+        assertNotNull(ks);
+    }
+
+    /*
+     * testGetKeySetByAlias - same as getSigningKeySet, but for keysets defined
+     * by this package.
+     */
+    public void testGetKeySetByAlias() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet ks;
+        try {
+            ks = pm.getKeySetByAlias(null, null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias(null, "keysetBogus");
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias("keysets.test.bogus.package", null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias("keysets.test.bogus.package", "A");
+            assertTrue(false); // should have thrown
+        } catch(IllegalArgumentException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias(mPkgName, "keysetBogus");
+            assertTrue(false); // should have thrown
+        } catch(IllegalArgumentException e) {
+        }
+
+        // make sure we can get a KeySet from our pkg
+        ks = pm.getKeySetByAlias(mPkgName, "A");
+        assertNotNull(ks);
+
+        // and another
+        final InstallParams ip = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            ks = pm.getKeySetByAlias(otherPkgName, "A");
+            assertNotNull(ks);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    public void testIsSignedBy() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
+        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
+
+        try {
+            assertFalse(pm.isSignedBy(null, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(null, mSigningKS));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(mPkgName, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy("keysets.test.bogus.package", mDefinedKS));
+        } catch(IllegalArgumentException e) {
+        }
+        assertFalse(pm.isSignedBy(mPkgName, mDefinedKS));
+        assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder())));
+        assertTrue(pm.isSignedBy(mPkgName, mSigningKS));
+
+        final InstallParams ip1 = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS));
+            assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip1);
+        }
+
+        final InstallParams ip2 = installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS));
+            assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip2);
+        }
+    }
+
+    public void testIsSignedByExactly() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
+        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
+        try {
+            assertFalse(pm.isSignedBy(null, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(null, mSigningKS));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(mPkgName, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedByExactly("keysets.test.bogus.package", mDefinedKS));
+        } catch(IllegalArgumentException e) {
+        }
+        assertFalse(pm.isSignedByExactly(mPkgName, mDefinedKS));
+        assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder())));
+        assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS));
+
+        final InstallParams ip1 = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+            assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip1);
+        }
+
+        final InstallParams ip2 = installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+            assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip2);
+        }
+    }
+
+
+
+    /**
+     * The following tests are related to testing the checkSignatures api.
+     */
+    private void checkSignatures(int apk1, int apk2, int expMatchResult) throws Exception {
+        checkSharedSignatures(apk1, apk2, true, false, -1, expMatchResult);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesAllMatch() throws Exception {
+        int apk1 = APP1_CERT1_CERT2;
+        int apk2 = APP2_CERT1_CERT2;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesNoMatch() throws Exception {
+        int apk1 = APP1_CERT1;
+        int apk2 = APP2_CERT2;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesSomeMatch1() throws Exception {
+        int apk1 = APP1_CERT1_CERT2;
+        int apk2 = APP2_CERT1;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesSomeMatch2() throws Exception {
+        int apk1 = APP1_CERT1_CERT2;
+        int apk2 = APP2_CERT2;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesMoreMatch() throws Exception {
+        int apk1 = APP1_CERT1;
+        int apk2 = APP2_CERT1_CERT2;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesUnknown() throws Exception {
+        int apk1 = APP1_CERT1_CERT2;
+        int apk2 = APP2_CERT1_CERT2;
+        String apk1Name = "install1.apk";
+        String apk2Name = "install2.apk";
+
+        final InstallParams ip = installFromRawResource(apk1Name, apk1, 0, false,
+                false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            PackageManager pm = mContext.getPackageManager();
+            // Delete app2
+            File filesDir = mContext.getFilesDir();
+            File outFile = new File(filesDir, apk2Name);
+            int rawResId = apk2;
+            Uri packageURI = getInstallablePackage(rawResId, outFile);
+            ParsingPackage pkg = parsePackage(packageURI);
+            try {
+                getPi().uninstall(pkg.getPackageName(),
+                        PackageManager.DELETE_ALL_USERS,
+                        null /*statusReceiver*/);
+            } catch (IllegalArgumentException ignore) {
+            }
+            // Check signatures now
+            int match = mContext.getPackageManager().checkSignatures(
+                    ip.pkg.getPackageName(), pkg.getPackageName());
+            assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @LargeTest
+    public void testCheckSignaturesRotatedAgainstOriginal() throws Exception {
+        // checkSignatures should be backwards compatible with pre-rotation behavior; this test
+        // verifies that an app signed with a rotated key results in a signature match with an app
+        // signed with the original key in the lineage.
+        int apk1 = APP1_CERT5;
+        int apk2 = APP2_CERT5_ROTATED_CERT6;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesRotatedAgainstRotated() throws Exception {
+        // checkSignatures should be successful when both apps have been signed with the same
+        // rotated key since the initial signature comparison between the two apps should
+        // return a match.
+        int apk1 = APP1_CERT5_ROTATED_CERT6;
+        int apk2 = APP2_CERT5_ROTATED_CERT6;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+    }
+
+    @LargeTest
+    public void testInstallNoCertificates() throws Exception {
+        int apk1 = APP1_UNSIGNED;
+        String apk1Name = "install1.apk";
+
+        installFromRawResource(apk1Name, apk1, 0, false,
+                true, PackageInstaller.STATUS_FAILURE_INVALID,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    /*
+     * The following tests are related to apps using shared uids signed with
+     * different certs.
+     */
+    private int SHARED1_UNSIGNED = R.raw.install_shared1_unsigned;
+
+    private int SHARED1_CERT1 = R.raw.install_shared1_cert1;
+
+    private int SHARED1_CERT2 = R.raw.install_shared1_cert2;
+
+    private int SHARED1_CERT1_CERT2 = R.raw.install_shared1_cert1_cert2;
+
+    private int SHARED2_UNSIGNED = R.raw.install_shared2_unsigned;
+
+    private int SHARED2_CERT1 = R.raw.install_shared2_cert1;
+
+    private int SHARED2_CERT2 = R.raw.install_shared2_cert2;
+
+    private int SHARED2_CERT1_CERT2 = R.raw.install_shared2_cert1_cert2;
+
+    private void checkSharedSignatures(int apk1, int apk2, boolean cleanUp, boolean fail,
+            int retCode, int expMatchResult) throws Exception {
+        String apk1Name = "install1.apk";
+        String apk2Name = "install2.apk";
+        ParsingPackage pkg1 = getParsedPackage(apk1Name, apk1);
+        ParsingPackage pkg2 = getParsedPackage(apk2Name, apk2);
+
+        try {
+            // Clean up before testing first.
+            cleanUpInstall(pkg1.getPackageName());
+            cleanUpInstall(pkg2.getPackageName());
+            installFromRawResource(apk1Name, apk1, 0, false, false, -1,
+                    PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            if (fail) {
+                installFromRawResource(apk2Name, apk2, 0, false, true, retCode,
+                        PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            } else {
+                installFromRawResource(apk2Name, apk2, 0, false, false, -1,
+                        PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+                // TODO: All checkSignatures tests should return the same result regardless of
+                // querying by package name or uid; however if there are any edge cases where
+                // individual packages within a shareduid are compared with signatures that do not
+                // match the full lineage of the shareduid this method should be overloaded to
+                // accept the expected response for the uid query.
+                PackageManager pm = getPm();
+                int matchByName = pm.checkSignatures(pkg1.getPackageName(), pkg2.getPackageName());
+                int pkg1Uid = pm.getApplicationInfo(pkg1.getPackageName(), 0).uid;
+                int pkg2Uid = pm.getApplicationInfo(pkg2.getPackageName(), 0).uid;
+                int matchByUid = pm.checkSignatures(pkg1Uid, pkg2Uid);
+                assertEquals(expMatchResult, matchByName);
+                assertEquals(expMatchResult, matchByUid);
+            }
+        } finally {
+            if (cleanUp) {
+                cleanUpInstall(pkg1.getPackageName());
+                cleanUpInstall(pkg2.getPackageName());
+            }
+        }
+    }
+
+    @LargeTest
+    public void testCheckSignaturesSharedAllMatch() throws Exception {
+        int apk1 = SHARED1_CERT1_CERT2;
+        int apk2 = SHARED2_CERT1_CERT2;
+        boolean fail = false;
+        int retCode = -1;
+        int expMatchResult = PackageManager.SIGNATURE_MATCH;
+        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesSharedNoMatch() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT2;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        int expMatchResult = -1;
+        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
+    }
+
+    /*
+     * Test that an app signed with cert1 and cert2 cannot be replaced when
+     * signed with cert1 alone.
+     */
+    @LargeTest
+    public void testCheckSignaturesSharedSomeMatch1() throws Exception {
+        int apk1 = SHARED1_CERT1_CERT2;
+        int apk2 = SHARED2_CERT1;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        int expMatchResult = -1;
+        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
+    }
+
+    /*
+     * Test that an app signed with cert1 and cert2 cannot be replaced when
+     * signed with cert2 alone.
+     */
+    @LargeTest
+    public void testCheckSignaturesSharedSomeMatch2() throws Exception {
+        int apk1 = SHARED1_CERT1_CERT2;
+        int apk2 = SHARED2_CERT2;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        int expMatchResult = -1;
+        checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult);
+    }
+
+    @LargeTest
+    public void testCheckSignaturesSharedUnknown() throws Exception {
+        int apk1 = SHARED1_CERT1_CERT2;
+        int apk2 = SHARED2_CERT1_CERT2;
+        String apk1Name = "install1.apk";
+        String apk2Name = "install2.apk";
+        InstallParams ip1 = null;
+
+        try {
+            ip1 = installFromRawResource(apk1Name, apk1, 0, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+            PackageManager pm = mContext.getPackageManager();
+            // Delete app2
+            ParsingPackage pkg = getParsedPackage(apk2Name, apk2);
+            try {
+                getPi().uninstall(pkg.getPackageName(), PackageManager.DELETE_ALL_USERS,
+                        null /*statusReceiver*/);
+            } catch (IllegalArgumentException ignore) {
+            }
+            // Check signatures now
+            int match = mContext.getPackageManager().checkSignatures(
+                    ip1.pkg.getPackageName(), pkg.getPackageName());
+            assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
+        } finally {
+            if (ip1 != null) {
+                cleanUpInstall(ip1);
+            }
+        }
+    }
+
+    @LargeTest
+    public void testReplaceFirstSharedMatchAllCerts() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT1;
+        int rapk1 = SHARED1_CERT1;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+        replaceCerts(apk1, rapk1, true, false, -1);
+    }
+
+    @LargeTest
+    public void testReplaceSecondSharedMatchAllCerts() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT1;
+        int rapk2 = SHARED2_CERT1;
+        checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH);
+        replaceCerts(apk2, rapk2, true, false, -1);
+    }
+
+    @LargeTest
+    public void testReplaceFirstSharedMatchSomeCerts() throws Exception {
+        int apk1 = SHARED1_CERT1_CERT2;
+        int apk2 = SHARED2_CERT1_CERT2;
+        int rapk1 = SHARED1_CERT1;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
+        installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true,
+                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testReplaceSecondSharedMatchSomeCerts() throws Exception {
+        int apk1 = SHARED1_CERT1_CERT2;
+        int apk2 = SHARED2_CERT1_CERT2;
+        int rapk2 = SHARED2_CERT1;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
+        installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true,
+                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testReplaceFirstSharedMatchNoCerts() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT1;
+        int rapk1 = SHARED1_CERT2;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
+        installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true,
+                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testReplaceSecondSharedMatchNoCerts() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT1;
+        int rapk2 = SHARED2_CERT2;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
+        installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true,
+                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testReplaceFirstSharedMatchMoreCerts() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT1;
+        int rapk1 = SHARED1_CERT1_CERT2;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
+        installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true,
+                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testReplaceSecondSharedMatchMoreCerts() throws Exception {
+        int apk1 = SHARED1_CERT1;
+        int apk2 = SHARED2_CERT1;
+        int rapk2 = SHARED2_CERT1_CERT2;
+        boolean fail = true;
+        int retCode = PackageInstaller.STATUS_FAILURE_CONFLICT;
+        checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH);
+        installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true,
+                fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    /**
+     * Unknown features should be allowed to install. This prevents older phones
+     * from rejecting new packages that specify features that didn't exist when
+     * an older phone existed. All older phones are assumed to have those
+     * features.
+     * <p>
+     * Right now we allow all packages to be installed regardless of their
+     * features.
+     */
+    @LargeTest
+    public void testUsesFeatureUnknownFeature() throws Exception {
+        int retCode = PackageManager.INSTALL_SUCCEEDED;
+        installFromRawResource("install.apk", R.raw.install_uses_feature, 0, true, false, retCode,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    @LargeTest
+    public void testInstallNonexistentFile() throws Exception {
+        int retCode = PackageInstaller.STATUS_FAILURE_INVALID;
+        File invalidFile = new File("/nonexistent-file.apk");
+        invokeInstallPackageFail(Uri.fromFile(invalidFile), 0, retCode);
+    }
+
+    @SmallTest
+    public void testGetVerifierDeviceIdentity() throws Exception {
+        PackageManager pm = getPm();
+        VerifierDeviceIdentity id = pm.getVerifierDeviceIdentity();
+
+        assertNotNull("Verifier device identity should not be null", id);
+    }
+
+    public void testGetInstalledPackages() throws Exception {
+        List<PackageInfo> packages = getPm().getInstalledPackages(0);
+        assertNotNull("installed packages cannot be null", packages);
+        assertTrue("installed packages cannot be empty", packages.size() > 0);
+    }
+
+    public void testGetUnInstalledPackages() throws Exception {
+        List<PackageInfo> packages = getPm().getInstalledPackages(
+                PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        assertNotNull("installed packages cannot be null", packages);
+        assertTrue("installed packages cannot be empty", packages.size() > 0);
+    }
+
+    /**
+     * Test that getInstalledPackages returns all the data specified in flags.
+     */
+    public void testGetInstalledPackagesAll() throws Exception {
+        final int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
+                | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
+                | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
+                | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
+                | PackageManager.GET_SIGNATURES | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+        final InstallParams ip =
+                installFromRawResource("install.apk", R.raw.install_complete_package_info,
+                        0 /*flags*/, false /*cleanUp*/, false /*fail*/, -1 /*result*/,
+                        PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+        try {
+            final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
+            assertNotNull("installed packages cannot be null", packages);
+            assertTrue("installed packages cannot be empty", packages.size() > 0);
+
+            PackageInfo packageInfo = null;
+
+            // Find the package with all components specified in the AndroidManifest
+            // to ensure no null values
+            for (PackageInfo pi : packages) {
+                if ("com.android.frameworks.coretests.install_complete_package_info"
+                        .equals(pi.packageName)) {
+                    packageInfo = pi;
+                    break;
+                }
+            }
+            assertNotNull("activities should not be null", packageInfo.activities);
+            assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
+            assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
+            assertNotNull("permissions should not be null", packageInfo.permissions);
+            assertNotNull("providers should not be null", packageInfo.providers);
+            assertNotNull("receivers should not be null", packageInfo.receivers);
+            assertNotNull("services should not be null", packageInfo.services);
+            assertNotNull("signatures should not be null", packageInfo.signatures);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    /**
+     * Test that getInstalledPackages returns all the data specified in
+     * flags when the GET_UNINSTALLED_PACKAGES flag is set.
+     */
+    public void testGetUnInstalledPackagesAll() throws Exception {
+        final int flags = PackageManager.MATCH_UNINSTALLED_PACKAGES
+                | PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
+                | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
+                | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
+                | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
+                | PackageManager.GET_SIGNATURES;
+
+        // first, install the package
+        final InstallParams ip =
+                installFromRawResource("install.apk", R.raw.install_complete_package_info,
+                        0 /*flags*/, false /*cleanUp*/, false /*fail*/, -1 /*result*/,
+                        PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+        try {
+            // then, remove it, keeping it's data around
+            final GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
+            invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
+
+            final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
+            assertNotNull("installed packages cannot be null", packages);
+            assertTrue("installed packages cannot be empty", packages.size() > 0);
+
+            PackageInfo packageInfo = null;
+
+            // Find the package with all components specified in the AndroidManifest
+            // to ensure no null values
+            for (PackageInfo pi : packages) {
+                if ("com.android.frameworks.coretests.install_complete_package_info"
+                        .equals(pi.packageName)) {
+                    packageInfo = pi;
+                    break;
+                }
+            }
+            assertNotNull("activities should not be null", packageInfo.activities);
+            assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
+            assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
+            assertNotNull("permissions should not be null", packageInfo.permissions);
+            assertNotNull("providers should not be null", packageInfo.providers);
+            assertNotNull("receivers should not be null", packageInfo.receivers);
+            assertNotNull("services should not be null", packageInfo.services);
+            assertNotNull("signatures should not be null", packageInfo.signatures);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @Suppress
+    public void testInstall_BadDex_CleanUp() throws Exception {
+        int retCode = PackageInstaller.STATUS_FAILURE_INVALID;
+        installFromRawResource("install.apk", R.raw.install_bad_dex, 0, true, true, retCode,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+    }
+
+    private static class TestDexModuleRegisterCallback
+            extends PackageManager.DexModuleRegisterCallback {
+        private String mDexModulePath = null;
+        private boolean mSuccess = false;
+        private String mMessage = null;
+        CountDownLatch doneSignal = new CountDownLatch(1);
+
+        @Override
+        public void onDexModuleRegistered(String dexModulePath, boolean success, String message) {
+            mDexModulePath = dexModulePath;
+            mSuccess = success;
+            mMessage = message;
+            doneSignal.countDown();
+        }
+
+        boolean waitTillDone() {
+            long startTime = System.currentTimeMillis();
+            while (System.currentTimeMillis() - startTime < MAX_WAIT_TIME) {
+                try {
+                    return doneSignal.await(MAX_WAIT_TIME, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                    Log.i(TAG, "Interrupted during sleep", e);
+                }
+            }
+            return false;
+        }
+
+    }
+
+    // Verify that the base code path cannot be registered.
+    public void testRegisterDexModuleBaseCode() throws Exception {
+        PackageManager pm = getPm();
+        ApplicationInfo info = getContext().getApplicationInfo();
+        TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
+        pm.registerDexModule(info.getBaseCodePath(), callback);
+        assertTrue(callback.waitTillDone());
+        assertEquals(info.getBaseCodePath(), callback.mDexModulePath);
+        assertFalse("BaseCodePath should not be registered", callback.mSuccess);
+    }
+
+    // Verify that modules which are not own by the calling package are not registered.
+    public void testRegisterDexModuleNotOwningModule() throws Exception {
+        TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
+        String moduleBelongingToOtherPackage = "/data/user/0/com.google.android.gms/module.apk";
+        getPm().registerDexModule(moduleBelongingToOtherPackage, callback);
+        assertTrue(callback.waitTillDone());
+        assertEquals(moduleBelongingToOtherPackage, callback.mDexModulePath);
+        assertTrue(callback.waitTillDone());
+        assertFalse("Only modules belonging to the calling package can be registered",
+                callback.mSuccess);
+    }
+
+    // Verify that modules owned by the package are successfully registered.
+    public void testRegisterDexModuleSuccessfully() throws Exception {
+        ApplicationInfo info = getContext().getApplicationInfo();
+        // Copy the main apk into the data folder and use it as a "module".
+        File dexModuleDir = new File(info.dataDir, "module-dir");
+        File dexModule = new File(dexModuleDir, "module.apk");
+        try {
+            assertNotNull(FileUtils.createDir(
+                    dexModuleDir.getParentFile(), dexModuleDir.getName()));
+            Files.copy(Paths.get(info.getBaseCodePath()), dexModule.toPath(),
+                    StandardCopyOption.REPLACE_EXISTING);
+            TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
+            getPm().registerDexModule(dexModule.toString(), callback);
+            assertTrue(callback.waitTillDone());
+            assertEquals(dexModule.toString(), callback.mDexModulePath);
+            assertTrue(callback.waitTillDone());
+            assertTrue(callback.mMessage, callback.mSuccess);
+
+            // NOTE:
+            // This actually verifies internal behaviour which might change. It's not
+            // ideal but it's the best we can do since there's no other place we can currently
+            // write a better test.
+            for(String isa : getAppDexInstructionSets(info)) {
+                Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.odex"));
+                Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.vdex"));
+            }
+        } finally {
+            FileUtils.deleteContentsAndDir(dexModuleDir);
+        }
+    }
+
+    // If the module does not exist on disk we should get a failure.
+    public void testRegisterDexModuleNotExists() throws Exception {
+        ApplicationInfo info = getContext().getApplicationInfo();
+        String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString();
+        TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
+        getPm().registerDexModule(nonExistentApk, callback);
+        assertTrue(callback.waitTillDone());
+        assertEquals(nonExistentApk, callback.mDexModulePath);
+        assertTrue(callback.waitTillDone());
+        assertFalse("DexModule registration should fail", callback.mSuccess);
+    }
+
+    // If the module does not exist on disk we should get a failure.
+    public void testRegisterDexModuleNotExistsNoCallback() throws Exception {
+        ApplicationInfo info = getContext().getApplicationInfo();
+        String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString();
+        getPm().registerDexModule(nonExistentApk, null);
+    }
+
+    // Copied from com.android.server.pm.InstructionSets because we don't have access to it here.
+    private static String[] getAppDexInstructionSets(ApplicationInfo info) {
+        if (info.primaryCpuAbi != null) {
+            if (info.secondaryCpuAbi != null) {
+                return new String[] {
+                        VMRuntime.getInstructionSet(info.primaryCpuAbi),
+                        VMRuntime.getInstructionSet(info.secondaryCpuAbi) };
+            } else {
+                return new String[] {
+                        VMRuntime.getInstructionSet(info.primaryCpuAbi) };
+            }
+        }
+
+        return new String[] { VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]) };
+    }
+
+    /*---------- Recommended install location tests ----*/
+    /*
+     * TODO's
+     * check version numbers for upgrades
+     * check permissions of installed packages
+     * how to do tests on updated system apps?
+     * verify updates to system apps cannot be installed on the sdcard.
+     */
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index c888524..d8ecf20 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.pm;
 
-import static android.content.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+import static com.android.server.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -43,27 +43,6 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityImpl;
-import android.content.pm.parsing.component.ParsedApexSystemService;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedInstrumentationImpl;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedIntentInfoImpl;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.component.ParsedPermissionGroupImpl;
-import android.content.pm.parsing.component.ParsedPermissionImpl;
-import android.content.pm.parsing.component.ParsedPermissionUtils;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedProviderImpl;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.component.ParsedServiceImpl;
-import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
-import android.content.pm.permission.CompatibilityPermissionInfo;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -85,7 +64,28 @@
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.CompatibilityPermissionInfo;
 import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
+import com.android.server.pm.pkg.component.ParsedPermissionImpl;
+import com.android.server.pm.pkg.component.ParsedPermissionUtils;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.component.ParsedServiceImpl;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
+import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 28f24f2..7ff8eec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -43,8 +43,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
 import android.content.res.TypedArray;
 import android.os.Environment;
 import android.os.UserHandle;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 0708be2..78bcf0c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -147,6 +147,11 @@
         // Verifies pushDynamicShortcuts further persists shortcuts into AppSearch without
         // removing previous shortcuts when max number of shortcuts is reached.
         mManager.pushDynamicShortcut(makeShortcut("s6"));
+        // Increasing the max number of shortcuts since number of results per page in AppSearch
+        // is set to match the former.
+        mService.updateConfigurationLocked(
+                ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=10,"
+                        + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
         shortcuts = getAllPersistedShortcuts();
         assertNotNull(shortcuts);
         assertEquals(6, shortcuts.size());
@@ -281,7 +286,7 @@
 
     private List<ShortcutInfo> getAllPersistedShortcuts() {
         try {
-            SystemClock.sleep(500);
+            SystemClock.sleep(5000);
             final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
             getPersistedShortcut(future);
             return future.get(10, TimeUnit.SECONDS);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 408d2c5..99edecf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -16,6 +16,7 @@
 package com.android.server.pm;
 
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
@@ -257,6 +258,10 @@
                 .setLongLived(true)
                 .setExtras(pb)
                 .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
+                .addCapabilityBinding("action.intent.START_EXERCISE",
+                        "exercise.type", list("running", "jogging"))
+                .addCapabilityBinding("action.intent.START_EXERCISE",
+                        "exercise.duration", list("10m"))
                 .build();
         si.addFlags(ShortcutInfo.FLAG_PINNED);
         si.setBitmapPath("abc");
@@ -294,6 +299,13 @@
         assertEquals(null, si.getDisabledMessageResName());
         assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
                 si.getStartingThemeResName());
+        assertTrue(si.hasCapability("action.intent.START_EXERCISE"));
+        assertFalse(si.hasCapability(""));
+        assertFalse(si.hasCapability("random"));
+        assertEquals(list("running", "jogging"), si.getCapabilityParameterValues(
+                "action.intent.START_EXERCISE", "exercise.type"));
+        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", ""));
+        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random"));
     }
 
     public void testShortcutInfoParcel_resId() {
@@ -947,6 +959,10 @@
                 .setRank(123)
                 .setExtras(pb)
                 .setLocusId(new LocusId("1.2.3.4.5"))
+                .addCapabilityBinding("action.intent.START_EXERCISE",
+                        "exercise.type", list("running", "jogging"))
+                .addCapabilityBinding("action.intent.START_EXERCISE",
+                        "exercise.duration", list("10m"))
                 .build();
         sorig.setTimestamp(mInjectedCurrentTimeMillis);
 
@@ -1008,6 +1024,14 @@
         assertNull(si.getIconUri());
         assertTrue(si.getLastChangedTimestamp() < now);
 
+        assertTrue(si.hasCapability("action.intent.START_EXERCISE"));
+        assertFalse(si.hasCapability(""));
+        assertFalse(si.hasCapability("random"));
+        assertEquals(list("running", "jogging"), si.getCapabilityParameterValues(
+                "action.intent.START_EXERCISE", "exercise.type"));
+        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", ""));
+        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random"));
+
         // Make sure ranks are saved too.  Because of the auto-adjusting, we need two shortcuts
         // to test it.
         si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index cf6165f..7e5fe04 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -315,8 +315,8 @@
         mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
                 asHandle(currentUser));
         try {
-            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
-                    /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+            assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                    /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
                     asHandle(currentUser));
@@ -335,8 +335,8 @@
                 asHandle(currentUser));
         try {
             synchronized (mUserRemoveLock) {
-                assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
-                        /* evenWhenDisallowed= */ true))
+                assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                        /* overrideDevicePolicy= */ true))
                                 .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
                 waitForUserRemovalLocked(user1.id);
             }
@@ -352,8 +352,8 @@
     @MediumTest
     @Test
     public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
-        assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM,
-                /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+        assertThat(mUserManager.removeUserWhenPossible(UserHandle.SYSTEM,
+                /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
 
         assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
     }
@@ -362,8 +362,8 @@
     @Test
     public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
         assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
-        assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE,
-                /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+        assertThat(mUserManager.removeUserWhenPossible(UserHandle.of(Integer.MAX_VALUE),
+                /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
     }
 
     @MediumTest
@@ -374,8 +374,8 @@
         // Switch to the user just created.
         switchUser(user1.id, null, /* ignoreHandle= */ true);
 
-        assertThat(mUserManager.removeUserOrSetEphemeral(user1.id, /* evenWhenDisallowed= */ false))
-                .isEqualTo(UserManager.REMOVE_RESULT_SET_EPHEMERAL);
+        assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_DEFERRED);
 
         assertThat(hasUser(user1.id)).isTrue();
         assertThat(getUser(user1.id).isEphemeral()).isTrue();
@@ -395,8 +395,9 @@
     public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         synchronized (mUserRemoveLock) {
-            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
-                    /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
+            assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                    /* overrideDevicePolicy= */ false))
+                            .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
             waitForUserRemovalLocked(user1.id);
         }
 
@@ -663,6 +664,24 @@
         }
     }
 
+    // Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE.
+    @MediumTest
+    @Test
+    public void testCreateUser_disallowAddClonedUserProfile() throws Exception {
+        final int primaryUserId = ActivityManager.getCurrentUser();
+        final UserHandle primaryUserHandle = asHandle(primaryUserId);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+                true, primaryUserHandle);
+        try {
+            UserInfo cloneProfileUserInfo = createProfileForUser("Clone",
+                    UserManager.USER_TYPE_PROFILE_CLONE, primaryUserId);
+            assertThat(cloneProfileUserInfo).isNull();
+        } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
+                    primaryUserHandle);
+        }
+    }
+
     // Make sure createProfile would fail if we have DISALLOW_ADD_MANAGED_PROFILE.
     @MediumTest
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 1dcb0b7..7c8bbec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.fail;
 
 import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
deleted file mode 100644
index 4059a49..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.parsing
-
-import android.Manifest
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageParser
-import android.platform.test.annotations.Postsubmit
-import com.android.internal.util.ArrayUtils
-import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo
-import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo
-import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-/**
- * Verifies that missing/adding [PackageManager] flags adds/remove the appropriate fields from the
- * [PackageInfo] or [ApplicationInfo] results.
- *
- * This test has to be updated manually whenever the info generation behavior changes, since
- * there's no single place where flag -> field is defined besides this test.
- */
-@Postsubmit
-@RunWith(Parameterized::class)
-class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() {
-
-    companion object {
-
-        data class Param<T> constructor(
-            val flag: Int,
-            val logTag: String,
-            val oldPkgFunction: (pkg: PackageParser.Package, flags: Int) -> T?,
-            val newPkgFunction: (pkg: AndroidPackage, flags: Int) -> T?,
-            val fieldFunction: (T) -> List<Any?>
-        ) {
-            companion object {
-                fun pkgInfo(flag: Int, fieldFunction: (PackageInfo) -> List<Any?>) = Param(
-                        flag, PackageInfo::class.java.simpleName,
-                        ::oldPackageInfo, ::newPackageInfo, fieldFunction
-                )
-
-                fun appInfo(flag: Int, fieldFunction: (ApplicationInfo) -> List<Any?>) = Param(
-                        flag, ApplicationInfo::class.java.simpleName,
-                        { pkg, flags -> oldAppInfo(pkg, flags) },
-                        { pkg, flags -> newAppInfo(pkg, flags) },
-                        fieldFunction
-                )
-            }
-
-            override fun toString(): String {
-                val hex = Integer.toHexString(flag)
-                val fromRight = Integer.toBinaryString(flag).reversed().indexOf('1')
-                return "$logTag $hex | 1 shl $fromRight"
-            }
-        }
-
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun parameters() = arrayOf(
-                pkgInfo(PackageManager.GET_ACTIVITIES) { listOf(it.activities) },
-                pkgInfo(PackageManager.GET_GIDS) { listOf(it.gids) },
-                pkgInfo(PackageManager.GET_INSTRUMENTATION) { listOf(it.instrumentation) },
-                pkgInfo(PackageManager.GET_META_DATA) { listOf(it.applicationInfo.metaData) },
-                pkgInfo(PackageManager.GET_PROVIDERS) { listOf(it.providers) },
-                pkgInfo(PackageManager.GET_RECEIVERS) { listOf(it.receivers) },
-                pkgInfo(PackageManager.GET_SERVICES) { listOf(it.services) },
-                pkgInfo(PackageManager.GET_SIGNATURES) { listOf(it.signatures) },
-                pkgInfo(PackageManager.GET_SIGNING_CERTIFICATES) { listOf(it.signingInfo) },
-                pkgInfo(PackageManager.GET_SHARED_LIBRARY_FILES) {
-                    it.applicationInfo.run { listOf(sharedLibraryFiles, sharedLibraryFiles) }
-                },
-                pkgInfo(PackageManager.GET_CONFIGURATIONS) {
-                    listOf(it.configPreferences, it.reqFeatures, it.featureGroups)
-                },
-                pkgInfo(PackageManager.GET_PERMISSIONS) {
-                    listOf(
-                        it.permissions,
-                        // Strip compatibility permission added in T
-                        it.requestedPermissions?.filter { x ->
-                            x != Manifest.permission.POST_NOTIFICATIONS
-                        }?.ifEmpty { null }?.toTypedArray(),
-                        // Strip the flag from compatibility permission added in T
-                        it.requestedPermissionsFlags?.filterIndexed { index, _ ->
-                            index != ArrayUtils.indexOf(it.requestedPermissions,
-                                                        Manifest.permission.POST_NOTIFICATIONS)
-                        }?.ifEmpty { null }?.toTypedArray())
-                },
-                appInfo(PackageManager.GET_META_DATA) { listOf(it.metaData) },
-                appInfo(PackageManager.GET_SHARED_LIBRARY_FILES) {
-                    listOf(it.sharedLibraryFiles, it.sharedLibraryFiles)
-                }
-        )
-    }
-
-    @Parameterized.Parameter(0)
-    lateinit var param: Param<Any>
-
-    @Test
-    fun fieldPresence() {
-        oldPackages.asSequence().zip(newPackages.asSequence())
-                .forEach { (old, new) ->
-                    val oldWithFlag = param.oldPkgFunction(old, param.flag)
-                    val newWithFlag = param.newPkgFunction(new, param.flag)
-                    val oldFieldList = oldWithFlag?.let(param.fieldFunction).orEmpty()
-                    val newFieldList = newWithFlag?.let(param.fieldFunction).orEmpty()
-
-                    oldFieldList.zip(newFieldList).forEach {
-                        assertWithMessage(new.packageName).that(it.second).apply {
-                            // Assert same null-ness as old logic
-                            if (it.first == null) {
-                                isNull()
-                            } else {
-                                isNotNull()
-                            }
-                        }
-                    }
-                }
-    }
-
-    @Test
-    fun fieldAbsence() {
-        newPackages.forEach {
-            val newWithoutFlag = param.newPkgFunction(it, 0)
-            val newFieldListWithoutFlag = newWithoutFlag?.let(param.fieldFunction).orEmpty()
-            assertThat(newFieldListWithoutFlag.filterNotNull()).isEmpty()
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
deleted file mode 100644
index 574921c..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.parsing
-
-import android.content.pm.PackageManager
-import android.platform.test.annotations.Postsubmit
-import androidx.test.filters.LargeTest
-import com.google.common.truth.Expect
-import org.junit.Rule
-import org.junit.Test
-
-/**
- * Collects APKs from the device and verifies that the new parsing behavior outputs
- * the same exposed Info object as the old parsing logic.
- */
-@Postsubmit
-class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
-
-    @get:Rule
-    val expect = Expect.create()
-
-    @Test
-    fun applicationInfoEquality() {
-        val flags = PackageManager.GET_META_DATA or PackageManager.GET_SHARED_LIBRARY_FILES
-        val oldAppInfo = oldPackages.asSequence().map { oldAppInfo(it, flags) }
-        val newAppInfo = newPackages.asSequence().map { newAppInfo(it, flags) }
-        oldAppInfo.zip(newAppInfo).forEach {
-            val firstName = it.first?.packageName
-            val secondName = it.second?.packageName
-            val packageName = if (firstName == secondName) {
-                "$firstName"
-            } else {
-                "$firstName | $secondName"
-            }
-            expect.withMessage("${it.first?.sourceDir} $packageName")
-                    .that(it.first?.dumpToString())
-                    .isEqualTo(it.second?.dumpToString())
-        }
-    }
-
-    @LargeTest
-    @Test
-    fun packageInfoEquality() {
-        val flags = PackageManager.GET_ACTIVITIES or
-                PackageManager.GET_CONFIGURATIONS or
-                PackageManager.GET_GIDS or
-                PackageManager.GET_INSTRUMENTATION or
-                PackageManager.GET_META_DATA or
-                PackageManager.GET_PERMISSIONS or
-                PackageManager.GET_PROVIDERS or
-                PackageManager.GET_RECEIVERS or
-                PackageManager.GET_SERVICES or
-                PackageManager.GET_SHARED_LIBRARY_FILES or
-                PackageManager.GET_SIGNATURES or
-                PackageManager.GET_SIGNING_CERTIFICATES or
-                PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
-                PackageManager.MATCH_DIRECT_BOOT_AWARE
-        val oldPackageInfo = oldPackages.asSequence().map { oldPackageInfo(it, flags) }
-        val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) }
-
-        oldPackageInfo.zip(newPackageInfo).forEach {
-            val firstName = it.first?.packageName
-            val secondName = it.second?.packageName
-            val packageName = if (firstName == secondName) {
-                "$firstName"
-            } else {
-                "$firstName | $secondName"
-            }
-
-            // Main components are asserted independently to separate the failures. Otherwise the
-            // comparison would include every component in one massive string.
-
-            val prefix = "${it.first?.applicationInfo?.sourceDir} $packageName"
-
-            expect.withMessage("$prefix PackageInfo")
-                    .that(it.second?.dumpToString())
-                    .isEqualTo(it.first?.dumpToString())
-
-            expect.withMessage("$prefix ApplicationInfo")
-                    .that(it.second?.applicationInfo?.dumpToString())
-                    .isEqualTo(it.first?.applicationInfo?.dumpToString())
-
-            val firstActivityNames = it.first?.activities?.map { it.name } ?: emptyList()
-            val secondActivityNames = it.second?.activities?.map { it.name } ?: emptyList()
-            expect.withMessage("$prefix activities")
-                    .that(secondActivityNames)
-                    .containsExactlyElementsIn(firstActivityNames)
-                    .inOrder()
-
-            if (!it.first?.activities.isNullOrEmpty() && !it.second?.activities.isNullOrEmpty()) {
-                it.first?.activities?.zip(it.second?.activities!!)?.forEach {
-                    expect.withMessage("$prefix ${it.first.name}")
-                            .that(it.second.dumpToString())
-                            .isEqualTo(it.first.dumpToString())
-                }
-            }
-
-            val firstReceiverNames = it.first?.receivers?.map { it.name } ?: emptyList()
-            val secondReceiverNames = it.second?.receivers?.map { it.name } ?: emptyList()
-            expect.withMessage("$prefix receivers")
-                    .that(secondReceiverNames)
-                    .containsExactlyElementsIn(firstReceiverNames)
-                    .inOrder()
-
-            if (!it.first?.receivers.isNullOrEmpty() && !it.second?.receivers.isNullOrEmpty()) {
-                it.first?.receivers?.zip(it.second?.receivers!!)?.forEach {
-                    expect.withMessage("$prefix ${it.first.name}")
-                            .that(it.second.dumpToString())
-                            .isEqualTo(it.first.dumpToString())
-                }
-            }
-
-            val firstProviderNames = it.first?.providers?.map { it.name } ?: emptyList()
-            val secondProviderNames = it.second?.providers?.map { it.name } ?: emptyList()
-            expect.withMessage("$prefix providers")
-                    .that(secondProviderNames)
-                    .containsExactlyElementsIn(firstProviderNames)
-                    .inOrder()
-
-            if (!it.first?.providers.isNullOrEmpty() && !it.second?.providers.isNullOrEmpty()) {
-                it.first?.providers?.zip(it.second?.providers!!)?.forEach {
-                    expect.withMessage("$prefix ${it.first.name}")
-                            .that(it.second.dumpToString())
-                            .isEqualTo(it.first.dumpToString())
-                }
-            }
-
-            val firstServiceNames = it.first?.services?.map { it.name } ?: emptyList()
-            val secondServiceNames = it.second?.services?.map { it.name } ?: emptyList()
-            expect.withMessage("$prefix services")
-                    .that(secondServiceNames)
-                    .containsExactlyElementsIn(firstServiceNames)
-                    .inOrder()
-
-            if (!it.first?.services.isNullOrEmpty() && !it.second?.services.isNullOrEmpty()) {
-                it.first?.services?.zip(it.second?.services!!)?.forEach {
-                    expect.withMessage("$prefix ${it.first.name}")
-                            .that(it.second.dumpToString())
-                            .isEqualTo(it.first.dumpToString())
-                }
-            }
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
deleted file mode 100644
index 122661e..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.parsing
-
-import android.Manifest
-import android.content.Context
-import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
-import android.content.pm.ConfigurationInfo
-import android.content.pm.FeatureInfo
-import android.content.pm.InstrumentationInfo
-import android.content.pm.PackageInfo
-import android.content.pm.PackageParser
-import android.content.pm.PermissionInfo
-import android.content.pm.ProviderInfo
-import android.content.pm.ServiceInfo
-import android.content.pm.parsing.ParsingPackageUtils
-import android.os.Bundle
-import android.os.Debug
-import android.os.Environment
-import android.os.Process
-import android.util.SparseArray
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.internal.util.ArrayUtils
-import com.android.server.pm.PackageManagerService
-import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.pm.pkg.PackageStateUnserialized
-import com.android.server.pm.pkg.PackageUserStateImpl
-import com.android.server.testutils.mockThrowOnUnmocked
-import com.android.server.testutils.whenever
-import org.junit.BeforeClass
-import org.mockito.Mockito.anyInt
-import java.io.File
-
-open class AndroidPackageParsingTestBase {
-
-    companion object {
-
-        private const val VERIFY_ALL_APKS = true
-
-        // For auditing memory usage differences to /sdcard/AndroidPackageParsingTestBase.hprof
-        private const val DUMP_HPROF_TO_EXTERNAL = false
-
-        val context: Context = InstrumentationRegistry.getInstrumentation().getContext()
-        protected val packageParser = PackageParser().apply {
-            setOnlyCoreApps(false)
-            setDisplayMetrics(context.resources.displayMetrics)
-            setCallback { false /* hasFeature */ }
-        }
-
-        protected val packageParser2 = PackageParser2.forParsingFileWithDefaults()
-
-        /**
-         * It would be difficult to mock all possibilities, so just use the APKs on device.
-         * Unfortunately, this means the device must be bootable to verify potentially
-         * boot-breaking behavior.
-         */
-        private val apks = mutableListOf(File(Environment.getRootDirectory(), "framework"))
-                .apply {
-                    @Suppress("ConstantConditionIf")
-                    if (VERIFY_ALL_APKS) {
-                        this += (PackageManagerService.SYSTEM_PARTITIONS)
-                                .flatMap {
-                                    listOfNotNull(it.privAppFolder, it.appFolder, it.overlayFolder)
-                                }
-                    }
-                }
-                .flatMap {
-                    it.walkTopDown()
-                            .filter { file -> file.name.endsWith(".apk") }
-                            .toList()
-                }
-                .distinct()
-
-        private val dummyUserState =
-            PackageUserStateImpl()
-
-        val oldPackages = mutableListOf<PackageParser.Package>()
-
-        val newPackages = mutableListOf<AndroidPackage>()
-
-        @Suppress("ConstantConditionIf")
-        @JvmStatic
-        @BeforeClass
-        fun setUpPackages() {
-            var uid = Process.FIRST_APPLICATION_UID
-            apks.mapNotNull {
-                try {
-                    packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) to
-                            packageParser2.parsePackage(it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR,
-                                    false)
-                } catch (ignored: Exception) {
-                    // It is intentional that a failure of either call here will result in failing
-                    // both. Having null on one side would mean nothing to compare. Due to the
-                    // nature of presubmit, this may not be caused by the change being tested, so
-                    // it's unhelpful to consider it a failure. Actual parsing issues will be
-                    // reported by SystemPartitionParseTest in postsubmit.
-                    null
-                }
-            }.forEach { (old, new) ->
-                // Assign an arbitrary UID. This is normally done after parsing completes, inside
-                // PackageManagerService, but since that code isn't run here, need to mock it. This
-                // is equivalent to what the system would assign.
-                old.applicationInfo.uid = uid
-                new.uid = uid
-                uid++
-
-                oldPackages += old
-                newPackages += new.hideAsFinal()
-            }
-
-            if (DUMP_HPROF_TO_EXTERNAL) {
-                System.gc()
-                Environment.getExternalStorageDirectory()
-                        .resolve(
-                                "${AndroidPackageParsingTestBase::class.java.simpleName}.hprof")
-                        .absolutePath
-                        .run(Debug::dumpHprofData)
-            }
-        }
-
-        fun oldAppInfo(
-            pkg: PackageParser.Package,
-            flags: Int = 0,
-            userId: Int = 0
-        ): ApplicationInfo? {
-            return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, userId)
-        }
-
-        fun newAppInfo(
-            pkg: AndroidPackage,
-            flags: Int = 0,
-            userId: Int = 0
-        ): ApplicationInfo? {
-            return PackageInfoUtils.generateApplicationInfo(pkg, flags.toLong(), dummyUserState,
-                userId, mockPkgSetting(pkg))
-        }
-
-        fun newAppInfoWithoutState(
-            pkg: AndroidPackage,
-            flags: Int = 0,
-            userId: Int = 0
-        ): ApplicationInfo? {
-            return PackageInfoUtils.generateApplicationInfo(pkg, flags.toLong(), dummyUserState,
-                userId, mockPkgSetting(pkg))
-        }
-
-        fun oldPackageInfo(pkg: PackageParser.Package, flags: Int = 0): PackageInfo? {
-            return PackageParser.generatePackageInfo(pkg, intArrayOf(), flags, 5, 6, emptySet(),
-                    dummyUserState)
-        }
-
-        fun newPackageInfo(pkg: AndroidPackage, flags: Int = 0): PackageInfo? {
-            return PackageInfoUtils.generate(pkg, intArrayOf(), flags.toLong(), 5, 6, emptySet(),
-                    dummyUserState, 0, mockPkgSetting(pkg))
-        }
-
-        private fun mockPkgSetting(aPkg: AndroidPackage) =
-            mockThrowOnUnmocked<PackageStateInternal> {
-                whenever(pkg) { aPkg }
-                whenever(appId) { aPkg.uid }
-                whenever(transientState) { PackageStateUnserialized() }
-                whenever(getUserStateOrDefault(anyInt())) { dummyUserState }
-                whenever(categoryOverride) { ApplicationInfo.CATEGORY_UNDEFINED }
-                whenever(primaryCpuAbi) { null }
-                whenever(secondaryCpuAbi) { null }
-            }
-    }
-
-    // The following methods dump an exact set of fields from the object to compare, because
-    // 1. comprehensive equals/toStrings do not exist on all of the Info objects, and
-    // 2. the test must only verify fields that [PackageParser.Package] can actually fill, as
-    // no new functionality will be added to it.
-
-    // The following methods prepend "this." because @hide APIs can cause an IDE to auto-import
-    // the R.attr constant instead of referencing the field in an attempt to fix the error.
-
-    // It's difficult to comment out a line in a triple quoted string, so this is used instead
-    // to ignore specific fields. A comment is required to explain why a field was ignored.
-    private fun Any?.ignored(comment: String): String = "IGNORED"
-
-    protected fun ApplicationInfo.dumpToString() = """
-            appComponentFactory=${this.appComponentFactory}
-            backupAgentName=${this.backupAgentName}
-            banner=${this.banner}
-            category=${this.category}
-            classLoaderName=${this.classLoaderName}
-            className=${this.className}
-            compatibleWidthLimitDp=${this.compatibleWidthLimitDp}
-            compileSdkVersion=${this.compileSdkVersion}
-            compileSdkVersionCodename=${this.compileSdkVersionCodename}
-            credentialProtectedDataDir=${this.credentialProtectedDataDir
-            .ignored("Deferred pre-R, but assigned immediately in R")}
-            crossProfile=${this.crossProfile.ignored("Added in R")}
-            dataDir=${this.dataDir.ignored("Deferred pre-R, but assigned immediately in R")}
-            descriptionRes=${this.descriptionRes}
-            deviceProtectedDataDir=${this.deviceProtectedDataDir
-            .ignored("Deferred pre-R, but assigned immediately in R")}
-            enabled=${this.enabled}
-            enabledSetting=${this.enabledSetting}
-            flags=${Integer.toBinaryString(this.flags)}
-            fullBackupContent=${this.fullBackupContent}
-            gwpAsanMode=${this.gwpAsanMode.ignored("Added in R")}
-            hiddenUntilInstalled=${this.hiddenUntilInstalled}
-            icon=${this.icon}
-            iconRes=${this.iconRes}
-            installLocation=${this.installLocation}
-            labelRes=${this.labelRes}
-            largestWidthLimitDp=${this.largestWidthLimitDp}
-            logo=${this.logo}
-            longVersionCode=${this.longVersionCode}
-            ${"".ignored("mHiddenApiPolicy is a private field")}
-            manageSpaceActivityName=${this.manageSpaceActivityName}
-            maxAspectRatio=${this.maxAspectRatio}
-            metaData=${this.metaData.dumpToString()}
-            minAspectRatio=${this.minAspectRatio}
-            minSdkVersion=${this.minSdkVersion}
-            name=${this.name}
-            nativeLibraryDir=${this.nativeLibraryDir}
-            nativeLibraryRootDir=${this.nativeLibraryRootDir}
-            nativeLibraryRootRequiresIsa=${this.nativeLibraryRootRequiresIsa}
-            networkSecurityConfigRes=${this.networkSecurityConfigRes}
-            nonLocalizedLabel=${
-                // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
-                // comparison, trim both so they can be matched.
-                this.nonLocalizedLabel?.trim()
-            }
-            packageName=${this.packageName}
-            permission=${this.permission}
-            primaryCpuAbi=${this.primaryCpuAbi}
-            privateFlags=${Integer.toBinaryString(this.privateFlags)}
-            processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
-            publicSourceDir=${this.publicSourceDir
-            .ignored("Deferred pre-R, but assigned immediately in R")}
-            requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
-            resourceDirs=${this.resourceDirs?.contentToString()}
-            overlayPaths=${this.overlayPaths?.contentToString()}
-            roundIconRes=${this.roundIconRes}
-            scanPublicSourceDir=${this.scanPublicSourceDir
-            .ignored("Deferred pre-R, but assigned immediately in R")}
-            scanSourceDir=${this.scanSourceDir
-            .ignored("Deferred pre-R, but assigned immediately in R")}
-            seInfo=${this.seInfo}
-            seInfoUser=${this.seInfoUser}
-            secondaryCpuAbi=${this.secondaryCpuAbi}
-            secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
-            sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()}
-            sharedLibraryInfos=${this.sharedLibraryInfos}
-            showUserIcon=${this.showUserIcon}
-            sourceDir=${this.sourceDir
-            .ignored("Deferred pre-R, but assigned immediately in R")}
-            splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()}
-            splitDependencies=${this.splitDependencies.dumpToString()}
-            splitNames=${this.splitNames?.contentToString()}
-            splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
-            splitSourceDirs=${this.splitSourceDirs?.contentToString()}
-            storageUuid=${this.storageUuid}
-            targetSandboxVersion=${this.targetSandboxVersion}
-            targetSdkVersion=${this.targetSdkVersion}
-            taskAffinity=${this.taskAffinity}
-            theme=${this.theme}
-            uiOptions=${this.uiOptions}
-            uid=${this.uid}
-            versionCode=${this.versionCode}
-            volumeUuid=${this.volumeUuid}
-            zygotePreloadName=${this.zygotePreloadName}
-            """.trimIndent()
-
-    protected fun FeatureInfo.dumpToString() = """
-            flags=${Integer.toBinaryString(this.flags)}
-            name=${this.name}
-            reqGlEsVersion=${this.reqGlEsVersion}
-            version=${this.version}
-            """.trimIndent()
-
-    protected fun InstrumentationInfo.dumpToString() = """
-            banner=${this.banner}
-            credentialProtectedDataDir=${this.credentialProtectedDataDir}
-            dataDir=${this.dataDir}
-            deviceProtectedDataDir=${this.deviceProtectedDataDir}
-            functionalTest=${this.functionalTest}
-            handleProfiling=${this.handleProfiling}
-            icon=${this.icon}
-            labelRes=${this.labelRes}
-            logo=${this.logo}
-            metaData=${this.metaData}
-            name=${this.name}
-            nativeLibraryDir=${this.nativeLibraryDir}
-            nonLocalizedLabel=${
-                // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
-                // comparison, trim both so they can be matched.
-                this.nonLocalizedLabel?.trim()
-            }
-            packageName=${this.packageName}
-            primaryCpuAbi=${this.primaryCpuAbi}
-            publicSourceDir=${this.publicSourceDir}
-            secondaryCpuAbi=${this.secondaryCpuAbi}
-            secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
-            showUserIcon=${this.showUserIcon}
-            sourceDir=${this.sourceDir}
-            splitDependencies=${this.splitDependencies.dumpToString()}
-            splitNames=${this.splitNames?.contentToString()}
-            splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
-            splitSourceDirs=${this.splitSourceDirs?.contentToString()}
-            targetPackage=${this.targetPackage}
-            targetProcesses=${this.targetProcesses}
-            """.trimIndent()
-
-    protected fun ActivityInfo.dumpToString() = """
-            banner=${this.banner}
-            colorMode=${this.colorMode}
-            configChanges=${this.configChanges}
-            descriptionRes=${this.descriptionRes}
-            directBootAware=${this.directBootAware}
-            documentLaunchMode=${this.documentLaunchMode
-            .ignored("Update for fixing b/128526493 and the testing is no longer valid")}
-            enabled=${this.enabled}
-            exported=${this.exported}
-            flags=${Integer.toBinaryString(
-                // Strip flag added in T
-                this.flags and (ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES.inv()))
-            }
-            icon=${this.icon}
-            labelRes=${this.labelRes}
-            launchMode=${this.launchMode}
-            launchToken=${this.launchToken}
-            lockTaskLaunchMode=${this.lockTaskLaunchMode}
-            logo=${this.logo}
-            maxRecents=${this.maxRecents}
-            metaData=${this.metaData.dumpToString()}
-            name=${this.name}
-            nonLocalizedLabel=${
-                // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
-                // comparison, trim both so they can be matched.
-                this.nonLocalizedLabel?.trim()
-            }
-            packageName=${this.packageName}
-            parentActivityName=${this.parentActivityName}
-            permission=${this.permission}
-            persistableMode=${this.persistableMode.ignored("Could be dropped pre-R, fixed in R")}
-            privateFlags=${
-                // Strip flag added in S
-                this.privateFlags and (ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND.inv())
-            }
-            processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
-            requestedVrComponent=${this.requestedVrComponent}
-            resizeMode=${this.resizeMode}
-            rotationAnimation=${this.rotationAnimation}
-            screenOrientation=${this.screenOrientation}
-            showUserIcon=${this.showUserIcon}
-            softInputMode=${this.softInputMode}
-            splitName=${this.splitName}
-            targetActivity=${this.targetActivity}
-            taskAffinity=${this.taskAffinity}
-            theme=${this.theme}
-            uiOptions=${this.uiOptions}
-            windowLayout=${this.windowLayout?.dumpToString()}
-            """.trimIndent()
-
-    protected fun ActivityInfo.WindowLayout.dumpToString() = """
-            gravity=${this.gravity}
-            height=${this.height}
-            heightFraction=${this.heightFraction}
-            minHeight=${this.minHeight}
-            minWidth=${this.minWidth}
-            width=${this.width}
-            widthFraction=${this.widthFraction}
-            """.trimIndent()
-
-    protected fun PermissionInfo.dumpToString() = """
-            backgroundPermission=${this.backgroundPermission}
-            banner=${this.banner}
-            descriptionRes=${this.descriptionRes}
-            flags=${Integer.toBinaryString(this.flags)}
-            group=${this.group}
-            icon=${this.icon}
-            labelRes=${this.labelRes}
-            logo=${this.logo}
-            metaData=${this.metaData.dumpToString()}
-            name=${this.name}
-            nonLocalizedDescription=${this.nonLocalizedDescription}
-            nonLocalizedLabel=${
-                // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
-                // comparison, trim both so they can be matched.
-                this.nonLocalizedLabel?.trim()
-            }
-            packageName=${this.packageName}
-            protectionLevel=${this.protectionLevel}
-            requestRes=${this.requestRes}
-            showUserIcon=${this.showUserIcon}
-            """.trimIndent()
-
-    protected fun ProviderInfo.dumpToString() = """
-            applicationInfo=${this.applicationInfo.ignored("Already checked")}
-            authority=${this.authority}
-            banner=${this.banner}
-            descriptionRes=${this.descriptionRes}
-            directBootAware=${this.directBootAware}
-            enabled=${this.enabled}
-            exported=${this.exported}
-            flags=${Integer.toBinaryString(this.flags)}
-            forceUriPermissions=${this.forceUriPermissions}
-            grantUriPermissions=${this.grantUriPermissions}
-            icon=${this.icon}
-            initOrder=${this.initOrder}
-            isSyncable=${this.isSyncable}
-            labelRes=${this.labelRes}
-            logo=${this.logo}
-            metaData=${this.metaData.dumpToString()}
-            multiprocess=${this.multiprocess}
-            name=${this.name}
-            nonLocalizedLabel=${
-                // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
-                // comparison, trim both so they can be matched.
-                this.nonLocalizedLabel?.trim()
-            }
-            packageName=${this.packageName}
-            pathPermissions=${this.pathPermissions?.joinToString {
-        "readPermission=${it.readPermission}\nwritePermission=${it.writePermission}"
-    }}
-            processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
-            readPermission=${this.readPermission}
-            showUserIcon=${this.showUserIcon}
-            splitName=${this.splitName}
-            uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()}
-            writePermission=${this.writePermission}
-            """.trimIndent()
-
-    protected fun ServiceInfo.dumpToString() = """
-            applicationInfo=${this.applicationInfo.ignored("Already checked")}
-            banner=${this.banner}
-            descriptionRes=${this.descriptionRes}
-            directBootAware=${this.directBootAware}
-            enabled=${this.enabled}
-            exported=${this.exported}
-            flags=${Integer.toBinaryString(this.flags)}
-            icon=${this.icon}
-            labelRes=${this.labelRes}
-            logo=${this.logo}
-            mForegroundServiceType"${this.mForegroundServiceType}
-            metaData=${this.metaData.dumpToString()}
-            name=${this.name}
-            nonLocalizedLabel=${
-                // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
-                // comparison, trim both so they can be matched.
-                this.nonLocalizedLabel?.trim()
-            }
-            packageName=${this.packageName}
-            permission=${this.permission}
-            processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
-            showUserIcon=${this.showUserIcon}
-            splitName=${this.splitName}
-            """.trimIndent()
-
-    protected fun ConfigurationInfo.dumpToString() = """
-            reqGlEsVersion=${this.reqGlEsVersion}
-            reqInputFeatures=${this.reqInputFeatures}
-            reqKeyboardType=${this.reqKeyboardType}
-            reqNavigation=${this.reqNavigation}
-            reqTouchScreen=${this.reqTouchScreen}
-            """.trimIndent()
-
-    protected fun PackageInfo.dumpToString() = """
-            activities=${this.activities?.joinToString { it.dumpToString() }
-            .ignored("Checked separately in test")}
-            applicationInfo=${this.applicationInfo.dumpToString()
-            .ignored("Checked separately in test")}
-            baseRevisionCode=${this.baseRevisionCode}
-            compileSdkVersion=${this.compileSdkVersion}
-            compileSdkVersionCodename=${this.compileSdkVersionCodename}
-            configPreferences=${this.configPreferences?.joinToString { it.dumpToString() }}
-            coreApp=${this.coreApp}
-            featureGroups=${this.featureGroups?.joinToString {
-        it.features?.joinToString { featureInfo -> featureInfo.dumpToString() }.orEmpty()
-    }}
-            firstInstallTime=${this.firstInstallTime}
-            gids=${gids?.contentToString()}
-            installLocation=${this.installLocation}
-            instrumentation=${instrumentation?.joinToString { it.dumpToString() }}
-            isApex=${this.isApex}
-            isStub=${this.isStub}
-            lastUpdateTime=${this.lastUpdateTime}
-            mOverlayIsStatic=${this.mOverlayIsStatic}
-            overlayCategory=${this.overlayCategory}
-            overlayPriority=${this.overlayPriority}
-            overlayTarget=${this.overlayTarget}
-            packageName=${this.packageName}
-            permissions=${this.permissions?.joinToString { it.dumpToString() }}
-            providers=${this.providers?.joinToString { it.dumpToString() }
-            .ignored("Checked separately in test")}
-            receivers=${this.receivers?.joinToString { it.dumpToString() }
-            .ignored("Checked separately in test")}
-            reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }}
-            requestedPermissions=${
-                // Strip compatibility permission added in T
-                this.requestedPermissions?.filter { x ->
-                    x != Manifest.permission.POST_NOTIFICATIONS
-                }?.ifEmpty { null }?.joinToString()
-            }
-            requestedPermissionsFlags=${
-                // Strip the flag from compatibility permission added in T
-                this.requestedPermissionsFlags?.filterIndexed { index, _ ->
-                    index != ArrayUtils.indexOf(requestedPermissions,
-                                                Manifest.permission.POST_NOTIFICATIONS)
-                }?.map {
-                    // Newer flags are stripped
-                    it and (PackageInfo.REQUESTED_PERMISSION_REQUIRED
-                            or PackageInfo.REQUESTED_PERMISSION_GRANTED)
-                }?.ifEmpty { null }?.joinToString()
-            }
-            requiredAccountType=${this.requiredAccountType}
-            requiredForAllUsers=${this.requiredForAllUsers}
-            restrictedAccountType=${this.restrictedAccountType}
-            services=${this.services?.joinToString { it.dumpToString() }
-            .ignored("Checked separately in test")}
-            sharedUserId=${this.sharedUserId}
-            sharedUserLabel=${this.sharedUserLabel}
-            signatures=${this.signatures?.joinToString { it.toCharsString() }}
-            signingInfo=${this.signingInfo?.signingCertificateHistory
-            ?.joinToString { it.toCharsString() }.orEmpty()}
-            splitNames=${this.splitNames?.contentToString()}
-            splitRevisionCodes=${this.splitRevisionCodes?.contentToString()}
-            targetOverlayableName=${this.targetOverlayableName}
-            versionCode=${this.versionCode}
-            versionCodeMajor=${this.versionCodeMajor}
-            versionName=${this.versionName}
-            """.trimIndent()
-
-    private fun Bundle?.dumpToString() = this?.keySet()?.associateWith { get(it) }?.toString()
-
-    private fun <T> SparseArray<T>?.dumpToString(): String {
-        if (this == null) {
-            return "EMPTY"
-        }
-
-        val list = mutableListOf<Pair<Int, T>>()
-        for (index in (0 until size())) {
-            list += keyAt(index) to valueAt(index)
-        }
-        return list.toString()
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt
deleted file mode 100644
index 67b5d68..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.parsing
-
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageParser
-import android.os.Environment
-import android.os.UserHandle
-import android.platform.test.annotations.Presubmit
-import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Test
-
-/**
- * As a performance optimization, the new parsing code builds the user data directories manually
- * using string concatenation. This tries to mirror the logic that [Environment] uses, but it is
- * still fragile to changes and potentially different device configurations.
- *
- * This compares the resultant values against the old [PackageParser] outputs as well as
- * [ApplicationInfo]'s own [ApplicationInfo.initForUser].
- */
-@Presubmit
-class PackageInfoUserFieldsTest : AndroidPackageParsingTestBase() {
-
-    @Test
-    fun userEnvironmentValues() {
-        // Specifically use a large user ID to test assumptions about single character IDs
-        val userId = 110
-
-        oldPackages.zip(newPackages)
-                .map { (old, new) ->
-                    (old to oldAppInfo(pkg = old, userId = userId)!!) to
-                            (new to newAppInfo(pkg = new, userId = userId)!!)
-                }
-                .forEach { (oldPair, newPair) ->
-                    val (oldPkg, oldInfo) = oldPair
-                    val (newPkg, newInfo) = newPair
-
-                    val oldValuesActual = extractActual(oldInfo)
-                    val newValuesActual = extractActual(newInfo)
-                    val oldValuesExpected: Values
-                    val newValuesExpected: Values
-
-                    val packageName = oldPkg.packageName
-                    if (packageName == "android") {
-                        val systemDataDir = Environment.getDataSystemDirectory().absolutePath
-                        oldValuesExpected = Values(
-                                uid = UserHandle.getUid(userId,
-                                        UserHandle.getAppId(oldPkg.applicationInfo.uid)),
-                                userDe = null,
-                                userCe = null,
-                                dataDir = systemDataDir
-                        )
-                        newValuesExpected = Values(
-                                uid = UserHandle.getUid(userId, UserHandle.getAppId(newPkg.uid)),
-                                userDe = null,
-                                userCe = null,
-                                dataDir = systemDataDir
-                        )
-                    } else {
-                        oldValuesExpected = extractExpected(oldInfo, oldInfo.uid, userId)
-                        newValuesExpected = extractExpected(newInfo, newPkg.uid, userId)
-                    }
-
-                    // Calls the internal ApplicationInfo logic to compare against. This must be
-                    // done after saving the original values, since this will overwrite them.
-                    oldInfo.initForUser(userId)
-                    newInfo.initForUser(userId)
-
-                    val oldInitValues = extractActual(oldInfo)
-                    val newInitValues = extractActual(newInfo)
-
-                    // The optimization is also done for the no state API that isn't used by the
-                    // system. This API is still exposed publicly, so for this test we should
-                    // verify it.
-                    val newNoStateValues = extractActual(
-                            newAppInfoWithoutState(newPkg, 0, userId)!!)
-
-                    assertAllEquals(packageName,
-                            oldValuesActual, oldValuesExpected, oldInitValues,
-                            newValuesActual, newValuesExpected, newInitValues, newNoStateValues)
-                }
-    }
-
-    private fun assertAllEquals(packageName: String, vararg values: Values) {
-        // Local function to avoid accidentally calling wrong type
-        fun assertAllEquals(message: String, vararg values: Any?) {
-            values.forEachIndexed { index, value ->
-                if (index == 0) return@forEachIndexed
-                assertWithMessage("$message $index").that(values[0]).isEqualTo(value)
-            }
-        }
-
-        assertAllEquals("$packageName mismatched uid", values.map { it.uid })
-        assertAllEquals("$packageName mismatched userDe", values.map { it.userDe })
-        assertAllEquals("$packageName mismatched userCe", values.map { it.userCe })
-        assertAllEquals("$packageName mismatched dataDir", values.map { it.dataDir })
-    }
-
-    private fun extractActual(appInfo: ApplicationInfo) = Values(
-            uid = appInfo.uid,
-            userDe = appInfo.deviceProtectedDataDir,
-            userCe = appInfo.credentialProtectedDataDir,
-            dataDir = appInfo.dataDir
-    )
-
-    private fun extractExpected(appInfo: ApplicationInfo, appIdUid: Int, userId: Int): Values {
-        val userDe = Environment.getDataUserDePackageDirectory(appInfo.volumeUuid, userId,
-                appInfo.packageName).absolutePath
-        val userCe = Environment.getDataUserCePackageDirectory(appInfo.volumeUuid, userId,
-                appInfo.packageName).absolutePath
-        val dataDir = if (appInfo.isDefaultToDeviceProtectedStorage) {
-            appInfo.deviceProtectedDataDir
-        } else {
-            appInfo.credentialProtectedDataDir
-        }
-
-        return Values(
-                uid = UserHandle.getUid(userId, UserHandle.getAppId(appIdUid)),
-                userDe = userDe,
-                userCe = userCe,
-                dataDir = dataDir
-        )
-    }
-
-    data class Values(
-        val uid: Int,
-        val userDe: String?,
-        val userCe: String?,
-        val dataDir: String?
-    )
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index c990342..004d7bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -18,7 +18,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.apex.ApexInfo;
@@ -27,16 +26,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
 import android.content.pm.SigningDetails;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionUtils;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Build;
@@ -55,6 +47,14 @@
 import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.component.ParsedActivityUtils;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionUtils;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import com.google.common.truth.Expect;
 
@@ -105,20 +105,19 @@
 
     private void verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename,
             boolean isPlatformReleased, int expectedMinSdk) {
-        final String[] outError = new String[1];
-        final int result = PackageParser.computeMinSdkVersion(
+        final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat();
+        final ParseResult<Integer> result = FrameworkParsingPackageUtils.computeMinSdkVersion(
                 minSdkVersion,
                 minSdkCodename,
                 PLATFORM_VERSION,
                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
-                outError);
-
-        assertEquals("Error msg: " + outError[0], expectedMinSdk, result);
+                input);
 
         if (expectedMinSdk == -1) {
-            assertNotNull(outError[0]);
+            assertTrue(result.isError());
         } else {
-            assertNull(outError[0]);
+            assertTrue(result.isSuccess());
+            assertEquals(expectedMinSdk, (int) result.getResult());
         }
     }
 
@@ -201,19 +200,18 @@
 
     private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
             boolean isPlatformReleased, int expectedTargetSdk) {
-        final String[] outError = new String[1];
-        final int result = PackageParser.computeTargetSdkVersion(
+        final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat();
+        final ParseResult<Integer> result = FrameworkParsingPackageUtils.computeTargetSdkVersion(
                 targetSdkVersion,
                 targetSdkCodename,
                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
-                outError);
-
-        assertEquals(result, expectedTargetSdk);
+                input);
 
         if (expectedTargetSdk == -1) {
-            assertNotNull(outError[0]);
+            assertTrue(result.isError());
         } else {
-            assertNull(outError[0]);
+            assertTrue(result.isSuccess());
+            assertEquals(expectedTargetSdk, (int) result.getResult());
         }
     }
 
@@ -306,34 +304,34 @@
         // Not set in either configChanges or recreateOnConfigChanges.
         int configChanges = 0x0000; // 00000000.
         int recreateOnConfigChanges = 0x0000; // 00000000.
-        int finalConfigChanges =
-                PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
+        int finalConfigChanges = ParsedActivityUtils.getActivityConfigChanges(configChanges,
+                recreateOnConfigChanges);
         assertEquals(0x0003, finalConfigChanges); // Should be 00000011.
 
         // Not set in configChanges, but set in recreateOnConfigChanges.
         configChanges = 0x0000; // 00000000.
         recreateOnConfigChanges = 0x0003; // 00000011.
-        finalConfigChanges =
-                PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
+        finalConfigChanges = ParsedActivityUtils.getActivityConfigChanges(configChanges,
+                recreateOnConfigChanges);
         assertEquals(0x0000, finalConfigChanges); // Should be 00000000.
 
         // Set in configChanges.
         configChanges = 0x0003; // 00000011.
         recreateOnConfigChanges = 0X0000; // 00000000.
-        finalConfigChanges =
-                PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
+        finalConfigChanges = ParsedActivityUtils.getActivityConfigChanges(configChanges,
+                recreateOnConfigChanges);
         assertEquals(0x0003, finalConfigChanges); // Should be 00000011.
 
         recreateOnConfigChanges = 0x0003; // 00000011.
-        finalConfigChanges =
-                PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
+        finalConfigChanges = ParsedActivityUtils.getActivityConfigChanges(configChanges,
+                recreateOnConfigChanges);
         assertEquals(0x0003, finalConfigChanges); // Should still be 00000011.
 
         // Other bit set in configChanges.
         configChanges = 0x0080; // 10000000, orientation.
         recreateOnConfigChanges = 0x0000; // 00000000.
-        finalConfigChanges =
-                PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
+        finalConfigChanges = ParsedActivityUtils.getActivityConfigChanges(configChanges,
+                recreateOnConfigChanges);
         assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index f530421..bb094ba 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -18,8 +18,8 @@
 
 import android.annotation.RawRes
 import android.content.Context
-import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.ParsingPackageUtils
+import com.android.server.pm.pkg.parsing.ParsingPackage
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import android.content.pm.parsing.result.ParseResult
 import android.platform.test.annotations.Presubmit
 import androidx.test.InstrumentationRegistry
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index ffa1957..1f57b6c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.parsing
 
 import android.content.pm.PackageManager
-import android.content.pm.parsing.ParsingPackageUtils
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import android.platform.test.annotations.Postsubmit
 import com.android.server.pm.PackageManagerException
 import com.android.server.pm.PackageManagerService
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index 5bcd0f6..b28446b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -23,7 +23,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.content.pm.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 761cea7..90b19a4 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -160,12 +160,12 @@
     }
 
     @Test
-    public void create_stateWithCancelStickyRequestFlag() {
+    public void create_stateWithCancelOverrideRequestFlag() {
         String configString = "<device-state-config>\n"
                 + "    <device-state>\n"
                 + "        <identifier>1</identifier>\n"
                 + "        <flags>\n"
-                + "            <flag>FLAG_CANCEL_STICKY_REQUESTS</flag>\n"
+                + "            <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
                 + "        </flags>\n"
                 + "        <conditions/>\n"
                 + "    </device-state>\n"
@@ -183,7 +183,7 @@
 
         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         final DeviceState[] expectedStates = new DeviceState[]{
-                new DeviceState(1, "", DeviceState.FLAG_CANCEL_STICKY_REQUESTS),
+                new DeviceState(1, "", DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS),
                 new DeviceState(2, "", 0 /* flags */) };
         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
     }
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index e47a07c..c832a3e 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -73,7 +73,7 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -138,7 +138,6 @@
     private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
 
     private PowerManagerService mService;
-    private PowerSaveState mPowerSaveState;
     private DisplayPowerRequest mDisplayPowerRequest;
     private ContextWrapper mContextSpy;
     private BatteryReceiver mBatteryReceiver;
@@ -147,7 +146,7 @@
     private OffsettableClock mClock;
     private TestLooper mTestLooper;
 
-    private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
+    private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
         private final IntentFilter mFilter;
 
         IntentFilterMatcher(IntentFilter filter) {
@@ -173,13 +172,13 @@
         MockitoAnnotations.initMocks(this);
         FakeSettingsProvider.clearSettingsProvider();
 
-        mPowerSaveState = new PowerSaveState.Builder()
+        PowerSaveState powerSaveState = new PowerSaveState.Builder()
                 .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
                 .setBrightnessFactor(BRIGHTNESS_FACTOR)
                 .build();
         when(mBatterySaverPolicyMock.getBatterySaverPolicy(
                 eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
-                .thenReturn(mPowerSaveState);
+                .thenReturn(powerSaveState);
         when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
         when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
@@ -195,7 +194,7 @@
         addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
         addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
 
-        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
 
@@ -304,8 +303,8 @@
         LocalServices.addService(clazz, mock);
     }
 
-    private void startSystem() throws Exception {
-        mService.systemReady(null);
+    private void startSystem() {
+        mService.systemReady();
 
         // Grab the BatteryReceiver
         ArgumentCaptor<BatteryReceiver> batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class);
@@ -403,9 +402,9 @@
     }
 
     @Test
-    public void testGetDesiredScreenPolicy_WithVR() throws Exception {
+    public void testGetDesiredScreenPolicy_WithVR() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         // Brighten up the screen
         mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
                 null, null);
@@ -436,13 +435,13 @@
     }
 
     @Test
-    public void testWakefulnessAwake_InitialValue() throws Exception {
+    public void testWakefulnessAwake_InitialValue() {
         createService();
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
     }
 
     @Test
-    public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+    public void testWakefulnessSleep_NoDozeSleepFlag() {
         createService();
         // Start with AWAKE state
         startSystem();
@@ -455,7 +454,7 @@
     }
 
     @Test
-    public void testWakefulnessAwake_AcquireCausesWakeup() throws Exception {
+    public void testWakefulnessAwake_AcquireCausesWakeup() {
         createService();
         startSystem();
         forceSleep();
@@ -487,7 +486,7 @@
     }
 
     @Test
-    public void testWakefulnessAwake_IPowerManagerWakeUp() throws Exception {
+    public void testWakefulnessAwake_IPowerManagerWakeUp() {
         createService();
         startSystem();
         forceSleep();
@@ -501,9 +500,7 @@
      * or docked.
      */
     @Test
-    public void testWakefulnessAwake_ShouldWakeUpWhenPluggedIn() throws Exception {
-        boolean powerState;
-
+    public void testWakefulnessAwake_ShouldWakeUpWhenPluggedIn() {
         createService();
         startSystem();
         forceSleep();
@@ -579,7 +576,7 @@
     }
 
     @Test
-    public void testWakefulnessDoze_goToSleep() throws Exception {
+    public void testWakefulnessDoze_goToSleep() {
         createService();
         // Start with AWAKE state
         startSystem();
@@ -595,7 +592,7 @@
     public void testWasDeviceIdleFor_true() {
         int interval = 1000;
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mService.onUserActivity();
         advanceTime(interval + 1 /* just a little more */);
@@ -606,7 +603,7 @@
     public void testWasDeviceIdleFor_false() {
         int interval = 1000;
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
@@ -615,7 +612,7 @@
     @Test
     public void testForceSuspend_putsDeviceToSleep() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Verify that we start awake
@@ -636,7 +633,7 @@
     }
 
     @Test
-    public void testForceSuspend_pakeLocksDisabled() {
+    public void testForceSuspend_wakeLocksDisabled() {
         final String tag = "TestWakelockTag_098213";
         final int flags = PowerManager.PARTIAL_WAKE_LOCK;
         final String pkg = mContextSpy.getOpPackageName();
@@ -661,7 +658,7 @@
         //
         // TEST STARTS HERE
         //
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Verify that we start awake
@@ -686,7 +683,7 @@
     }
 
     @Test
-    public void testForceSuspend_forceSuspendFailurePropagated() throws Exception {
+    public void testForceSuspend_forceSuspendFailurePropagated() {
         createService();
         startSystem();
         when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false);
@@ -694,7 +691,7 @@
     }
 
     @Test
-    public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() throws Exception {
+    public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() {
         final String suspendBlockerName = "PowerManagerService.Display";
         final String tag = "acq_causes_wakeup";
         final String packageName = "pkg.name";
@@ -741,7 +738,7 @@
     }
 
     @Test
-    public void testSuspendBlockerHeldDuringBoot() throws Exception {
+    public void testSuspendBlockerHeldDuringBoot() {
         final String suspendBlockerName = "PowerManagerService.Booting";
 
         final boolean[] isAcquired = new boolean[1];
@@ -760,7 +757,7 @@
         createService();
         assertTrue(isAcquired[0]);
 
-        mService.systemReady(null);
+        mService.systemReady();
         assertTrue(isAcquired[0]);
 
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -768,7 +765,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception {
+    public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(120);
         setAttentiveTimeout(100);
@@ -788,7 +785,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_hideWarningIfInattentiveSleepIsDisabled() throws Exception {
+    public void testInattentiveSleep_hideWarningIfInattentiveSleepIsDisabled() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(120);
         setAttentiveTimeout(100);
@@ -807,7 +804,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_userActivityDismissesWarning() throws Exception {
+    public void testInattentiveSleep_userActivityDismissesWarning() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -833,7 +830,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_warningHiddenAfterWakingUp() throws Exception {
+    public void testInattentiveSleep_warningHiddenAfterWakingUp() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(70);
         setAttentiveTimeout(100);
@@ -851,7 +848,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_noWarningShownIfInattentiveSleepDisabled() throws Exception {
+    public void testInattentiveSleep_noWarningShownIfInattentiveSleepDisabled() {
         setAttentiveTimeout(-1);
         createService();
         startSystem();
@@ -859,7 +856,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_goesToSleepAfterTimeout() throws Exception {
+    public void testInattentiveSleep_goesToSleepAfterTimeout() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveTimeout(5);
         createService();
@@ -871,7 +868,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception {
+    public void testInattentiveSleep_goesToSleepWithWakeLock() {
         final String pkg = mContextSpy.getOpPackageName();
         final Binder token = new Binder();
         final String tag = "testInattentiveSleep_goesToSleepWithWakeLock";
@@ -893,8 +890,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_wakeLockOnAfterRelease_inattentiveSleepTimeoutNotAffected()
-            throws Exception {
+    public void testInattentiveSleep_wakeLockOnAfterRelease_inattentiveSleepTimeoutNotAffected() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -922,8 +918,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_userActivityNoChangeLights_inattentiveSleepTimeoutNotAffected()
-            throws Exception {
+    public void testInattentiveSleep_userActivityNoChangeLights_inattentiveSleepTimeoutNotAffected() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -945,8 +940,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_userActivity_inattentiveSleepTimeoutExtended()
-            throws Exception {
+    public void testInattentiveSleep_userActivity_inattentiveSleepTimeoutExtended() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -965,7 +959,7 @@
     }
 
     @Test
-    public void testWakeLock_affectsProperDisplayGroup() throws Exception {
+    public void testWakeLock_affectsProperDisplayGroup() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1005,7 +999,7 @@
     }
 
     @Test
-    public void testInvalidDisplayGroupWakeLock_affectsAllDisplayGroups() throws Exception {
+    public void testInvalidDisplayGroupWakeLock_affectsAllDisplayGroups() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1045,7 +1039,7 @@
     }
 
     @Test
-    public void testRemovedDisplayGroupWakeLock_affectsNoDisplayGroups() throws Exception {
+    public void testRemovedDisplayGroupWakeLock_affectsNoDisplayGroups() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
@@ -1086,7 +1080,7 @@
     }
 
     @Test
-    public void testBoot_ShouldBeAwake() throws Exception {
+    public void testBoot_ShouldBeAwake() {
         createService();
         startSystem();
 
@@ -1095,7 +1089,7 @@
     }
 
     @Test
-    public void testBoot_DesiredScreenPolicyShouldBeBright() throws Exception {
+    public void testBoot_DesiredScreenPolicyShouldBeBright() {
         createService();
         startSystem();
 
@@ -1104,7 +1098,7 @@
     }
 
     @Test
-    public void testQuiescentBoot_ShouldBeAsleep() throws Exception {
+    public void testQuiescentBoot_ShouldBeAsleep() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
         startSystem();
@@ -1115,7 +1109,7 @@
     }
 
     @Test
-    public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception {
+    public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
         startSystem();
@@ -1124,7 +1118,7 @@
     }
 
     @Test
-    public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() throws Exception {
+    public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
         startSystem();
@@ -1134,11 +1128,10 @@
     }
 
     @Test
-    public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted()
-            throws Exception {
+    public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
@@ -1150,7 +1143,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplayAvailable_available() throws Exception {
+    public void testIsAmbientDisplayAvailable_available() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
 
@@ -1158,7 +1151,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplayAvailable_unavailable() throws Exception {
+    public void testIsAmbientDisplayAvailable_unavailable() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
 
@@ -1166,14 +1159,14 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_default_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_default_notSuppressed() {
         createService();
 
         assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse();
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_suppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
 
@@ -1181,7 +1174,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
 
@@ -1189,7 +1182,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_multipleTokens_suppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_multipleTokens_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true);
@@ -1198,7 +1191,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_multipleTokens_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_multipleTokens_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
@@ -1207,7 +1200,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_default_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_default_notSuppressed() {
         createService();
 
         assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test"))
@@ -1215,7 +1208,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_suppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
 
@@ -1224,7 +1217,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
 
@@ -1233,8 +1226,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_suppressed()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true);
@@ -1246,8 +1238,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_notSuppressed()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
@@ -1259,8 +1250,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_ambientDisplayUnavailable()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_ambientDisplayUnavailable() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
 
@@ -1270,8 +1260,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_default()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_default() {
         createService();
 
         BinderService service = mService.getBinderServiceInstance();
@@ -1280,8 +1269,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_suppressedByCallingApp()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_suppressedByCallingApp() {
         createService();
         BinderService service = mService.getBinderServiceInstance();
         service.suppressAmbientDisplay("test", true);
@@ -1294,8 +1282,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_notSuppressedByCallingApp()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_notSuppressedByCallingApp() {
         createService();
         BinderService service = mService.getBinderServiceInstance();
         service.suppressAmbientDisplay("test", false);
@@ -1308,8 +1295,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_multipleTokensSuppressedByCallingApp()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_multipleTokensSuppressedByCallingApp() {
         createService();
         BinderService service = mService.getBinderServiceInstance();
         service.suppressAmbientDisplay("test1", true);
@@ -1358,7 +1344,7 @@
     @Test
     public void testSetPowerBoost_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         mService.getBinderServiceInstance().setPowerBoost(Boost.INTERACTION, 1234);
 
@@ -1368,7 +1354,7 @@
     @Test
     public void testSetPowerMode_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         // Enabled launch boost in BatterySaverController to allow setting launch mode.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(false);
@@ -1384,7 +1370,7 @@
     @Test
     public void testSetPowerMode_withLaunchBoostDisabledAndModeLaunch_ignoresCallToEnable() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1400,7 +1386,7 @@
     @Test
     public void testSetPowerModeChecked_returnsNativeCallResult() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1419,7 +1405,7 @@
     }
 
     @Test
-    public void testMultiDisplay_wakefulnessUpdates() throws Exception {
+    public void testMultiDisplay_wakefulnessUpdates() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1448,7 +1434,7 @@
     }
 
     @Test
-    public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception {
+    public void testMultiDisplay_addDisplayGroup_preservesWakefulness() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1472,7 +1458,7 @@
     }
 
     @Test
-    public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception {
+    public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1502,7 +1488,7 @@
     @Test
     public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
                 .thenReturn(mockReturnConfig);
@@ -1517,7 +1503,7 @@
     @Test
     public void testSetFullPowerSavePolicy_callsStateMachine() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         BatterySaverPolicyConfig mockSetPolicyConfig =
                 new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 26b34fd..304fe5a 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -30,6 +30,7 @@
 import android.hardware.power.stats.State;
 import android.hardware.power.stats.StateResidency;
 import android.hardware.power.stats.StateResidencyResult;
+import android.os.Looper;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -145,12 +146,12 @@
         }
 
         @Override
-        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
-                String meterFilename, String meterCacheFilename,
+        PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+                File dataStoragePath, String meterFilename, String meterCacheFilename,
                 String modelFilename, String modelCacheFilename,
                 String residencyFilename, String residencyCacheFilename,
                 IPowerStatsHALWrapper powerStatsHALWrapper) {
-            mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath,
+            mPowerStatsLogger = new PowerStatsLogger(context, looper, dataStoragePath,
                 meterFilename, meterCacheFilename,
                 modelFilename, modelCacheFilename,
                 residencyFilename, residencyCacheFilename,
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index c293b5e..0e98b5e 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
@@ -28,6 +29,7 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,6 +39,7 @@
 import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.om.IOverlayManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -95,6 +98,10 @@
     private ApplicationInfo mApplicationInfo;
     @Mock
     private IStatusBar.Stub mMockStatusBar;
+    @Mock
+    private IOverlayManager mOverlayManager;
+    @Mock
+    private PackageManager mPackageManager;
     @Captor
     private ArgumentCaptor<IAddTileResultCallback> mAddTileResultCallbackCaptor;
 
@@ -129,6 +136,7 @@
                 mStatusBarManagerService);
 
         mStatusBarManagerService.registerStatusBar(mMockStatusBar);
+        mStatusBarManagerService.registerOverlayManager(mOverlayManager);
 
         mIcon = Icon.createWithResource(mContext, android.R.drawable.btn_plus);
     }
@@ -575,6 +583,59 @@
         }
     }
 
+    @Test
+    public void testSetNavBarModeOverride_setsOverrideModeKids() throws RemoteException {
+        int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+        mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+        assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNavBarModeOverride_setsOverrideModeNone() throws RemoteException {
+        int navBarModeOverrideNone = StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
+
+        mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideNone);
+
+        assertEquals(navBarModeOverrideNone, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNavBarModeOverride_invalidInputThrowsError() throws RemoteException {
+        int navBarModeOverrideInvalid = -1;
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideInvalid));
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNavBarModeOverride_noOverlayManagerDoesNotEnable() throws RemoteException {
+        mOverlayManager = null;
+        int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+        mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+        assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNavBarModeOverride_noPackageDoesNotEnable() throws Exception {
+        mContext.setMockPackageManager(mPackageManager);
+        when(mPackageManager.getPackageInfo(anyString(),
+                any(PackageManager.PackageInfoFlags.class))).thenReturn(null);
+        int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+        mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+        assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
     private void mockUidCheck() {
         mockUidCheck(TEST_PACKAGE);
     }
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index add4cda..eeaf781 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -357,13 +357,13 @@
 
     /**
      * Tests that readPermissions works correctly for a library using the new
-     * {@code updatable-library} tag.
+     * {@code apex-library} tag.
      */
     @Test
     public void readPermissions_allowLibs_parsesUpdatableLibrary() throws IOException {
         String contents =
                 "<permissions>\n"
-                        + "    <updatable-library \n"
+                        + "    <apex-library \n"
                         + "        name=\"foo\"\n"
                         + "        file=\"" + mFooJar + "\"\n"
                         + "        on-bootclasspath-before=\"10\"\n"
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 88f368e..06726b0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -22,16 +22,20 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static android.app.usage.UsageStatsManager.standbyBucketToString;
 
 import android.os.FileUtils;
 import android.test.AndroidTestCase;
 
 import java.io.File;
+import java.util.Map;
 
 public class AppIdleHistoryTests extends AndroidTestCase {
 
@@ -65,7 +69,7 @@
         // Screen On time file should be written right away
         assertTrue(aih.getScreenOnTimeFile().exists());
 
-        aih.writeAppIdleTimes(USER_ID);
+        aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 2000);
         // stats file should be written now
         assertTrue(new File(new File(mStorageDir, "users/" + USER_ID),
                 AppIdleHistory.APP_IDLE_FILENAME).exists());
@@ -128,7 +132,7 @@
 
         // Check persistence
         aih.writeAppIdleDurations();
-        aih.writeAppIdleTimes(USER_ID);
+        aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 3000);
         aih = new AppIdleHistory(mStorageDir, 4000);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
@@ -165,7 +169,7 @@
                 aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
                 REASON_MAIN_TIMEOUT);
-        aih.writeAppIdleTimes(USER_ID);
+        aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 4000);
 
         aih = new AppIdleHistory(mStorageDir, 5000);
         assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
@@ -180,11 +184,63 @@
         aih.reportUsage(null, USER_ID, STANDBY_BUCKET_ACTIVE,
                 REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
         // Persist data
-        aih.writeAppIdleTimes(USER_ID);
+        aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 2000);
         // Recover data from disk
         aih = new AppIdleHistory(mStorageDir, 5000);
         // Verify data is intact
         assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
                 aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
     }
+
+    public void testBucketExpiryTimes() throws Exception {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000 /* elapsedRealtime */);
+        aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_SUB_USAGE_SLICE_PINNED,
+                2000 /* elapsedRealtime */, 6000 /* expiryRealtime */);
+        assertEquals(5000 /* expectedExpiryTimeMs */, aih.getBucketExpiryTimeMs(PACKAGE_1, USER_ID,
+                STANDBY_BUCKET_WORKING_SET, 2000 /* elapsedRealtime */));
+        aih.reportUsage(PACKAGE_2, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_SUB_USAGE_NOTIFICATION_SEEN,
+                2000 /* elapsedRealtime */, 3000 /* expiryRealtime */);
+        assertEquals(2000 /* expectedExpiryTimeMs */, aih.getBucketExpiryTimeMs(PACKAGE_2, USER_ID,
+                STANDBY_BUCKET_FREQUENT, 2000 /* elapsedRealtime */));
+        aih.writeAppIdleTimes(USER_ID, 4000 /* elapsedRealtime */);
+
+        // Persist data
+        aih = new AppIdleHistory(mStorageDir, 5000 /* elapsedRealtime */);
+        final Map<Integer, Long> expectedExpiryTimes1 = Map.of(
+                STANDBY_BUCKET_ACTIVE, 0L,
+                STANDBY_BUCKET_WORKING_SET, 5000L,
+                STANDBY_BUCKET_FREQUENT, 0L,
+                STANDBY_BUCKET_RARE, 0L,
+                STANDBY_BUCKET_RESTRICTED, 0L
+        );
+        // For PACKAGE_1, only WORKING_SET bucket should have an expiry time.
+        verifyBucketExpiryTimes(aih, PACKAGE_1, USER_ID, 5000 /* elapsedRealtime */,
+                expectedExpiryTimes1);
+        final Map<Integer, Long> expectedExpiryTimes2 = Map.of(
+                STANDBY_BUCKET_ACTIVE, 0L,
+                STANDBY_BUCKET_WORKING_SET, 0L,
+                STANDBY_BUCKET_FREQUENT, 0L,
+                STANDBY_BUCKET_RARE, 0L,
+                STANDBY_BUCKET_RESTRICTED, 0L
+        );
+        // For PACKAGE_2, there shouldn't be any expiry time since the one set earlier would have
+        // elapsed by the time the data was persisted to disk
+        verifyBucketExpiryTimes(aih, PACKAGE_2, USER_ID, 5000 /* elapsedRealtime */,
+                expectedExpiryTimes2);
+    }
+
+    private void verifyBucketExpiryTimes(AppIdleHistory aih, String packageName, int userId,
+            long elapsedRealtimeMs, Map<Integer, Long> expectedExpiryTimesMs) throws Exception {
+        for (Map.Entry<Integer, Long> entry : expectedExpiryTimesMs.entrySet()) {
+            final int bucket = entry.getKey();
+            final long expectedExpiryTimeMs = entry.getValue();
+            final long actualExpiryTimeMs = aih.getBucketExpiryTimeMs(packageName, userId, bucket,
+                    elapsedRealtimeMs);
+            assertEquals("Unexpected expiry time for pkg=" + packageName + ", userId=" + userId
+                            + ", bucket=" + standbyBucketToString(bucket),
+                    expectedExpiryTimeMs, actualExpiryTimeMs);
+        }
+    }
 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 949ee01..18d3f3d 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -63,7 +63,6 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -828,7 +827,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 185169504)
     public void testNotificationEvent() throws Exception {
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
@@ -842,6 +840,22 @@
     }
 
     @Test
+    public void testNotificationEvent_changePromotedBucket() throws Exception {
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+        // TODO: Avoid hardcoding these string constants.
+        mInjector.mSettingsBuilder.setInt("notification_seen_promoted_bucket",
+                STANDBY_BUCKET_FREQUENT);
+        mInjector.mPropertiesChangedListener.onPropertiesChanged(
+                mInjector.getDeviceConfigProperties());
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+    }
+
+    @Test
     @FlakyTest(bugId = 185169504)
     public void testSlicePinnedEvent() throws Exception {
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
index b934ecb..739b3b1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
@@ -54,11 +54,10 @@
             /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
 
     private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
-            new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
+            new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
     private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
-            new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY,
-                    TEST_RESONANT_FREQUENCY, TEST_FREQUENCY_RESOLUTION,
-                    /* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP);
+            new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
+                    TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
 
     private DeviceVibrationEffectAdapter mAdapter;
 
@@ -87,14 +86,14 @@
     @Test
     public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() {
         VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100),
+                        /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
-                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 1000)),
+                        /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 1, /* duration= */ 1000)),
                 /* repeatIndex= */ 3);
 
         VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect,
@@ -110,23 +109,23 @@
     @Test
     public void testStepAndRampSegments_withPwleCapability_convertsStepsToRamps() {
         VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 175, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 60),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+                        /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
+                        /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)),
                 /* repeatIndex= */ 2);
 
         VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
-                        /* startFrequency= */ 175, /* endFrequency= */ 175, /* duration= */ 10),
+                        /* startFrequencyHz= */ 175, /* endFrequencyHz= */ 175, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 100),
+                        /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 60),
                 new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
-                        /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
+                        /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
-                        /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
                 /* repeatIndex= */ 2);
 
         VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
@@ -135,28 +134,28 @@
     }
 
     @Test
-    public void testStepAndRampSegments_withEmptyFreqMapping_returnsSameAmplitudesAndZeroFreq() {
+    public void testStepAndRampSegments_withEmptyFreqMapping_returnsAmplitudesWithResonantFreq() {
         VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 175, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 50),
+                        /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.7f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
+                        /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)),
                 /* repeatIndex= */ 2);
 
         VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
+                        /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
                         /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
+                        /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
                         /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1,
-                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
+                        /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
                         /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.7f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
+                        /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
                         /* duration= */ 20)),
                 /* repeatIndex= */ 2);
 
@@ -168,25 +167,25 @@
     @Test
     public void testStepAndRampSegments_withValidFreqMapping_returnsClippedValues() {
         VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ -1, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 125, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+                        /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
+                        /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)),
                 /* repeatIndex= */ 2);
 
         VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ 150, /* endFrequency= */ 150,
+                        /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150,
                         /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
-                        /* startFrequency= */ 125, /* endFrequency= */ 125,
+                        /* startFrequencyHz= */ 125, /* endFrequencyHz= */ 125,
                         /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
-                        /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
+                        /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
-                        /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
                 /* repeatIndex= */ 2);
 
         VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
index e2a348e..4556a4a 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
@@ -17,6 +17,7 @@
 package com.android.server.vibrator;
 
 import android.annotation.NonNull;
+import android.content.Context;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -24,37 +25,8 @@
 /** Fake implementation of {@link Vibrator} for service tests. */
 final class FakeVibrator extends Vibrator {
 
-    private int mDefaultHapticFeedbackIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
-    private int mDefaultNotificationIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
-    private int mDefaultRingIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
-
-    @Override
-    public int getDefaultHapticFeedbackIntensity() {
-        return mDefaultHapticFeedbackIntensity;
-    }
-
-    @Override
-    public int getDefaultNotificationVibrationIntensity() {
-        return mDefaultNotificationIntensity;
-    }
-
-    @Override
-    public int getDefaultRingVibrationIntensity() {
-        return mDefaultRingIntensity;
-    }
-
-    public void setDefaultHapticFeedbackIntensity(
-            @VibrationIntensity int defaultHapticFeedbackIntensity) {
-        mDefaultHapticFeedbackIntensity = defaultHapticFeedbackIntensity;
-    }
-
-    public void setDefaultNotificationVibrationIntensity(
-            @VibrationIntensity int defaultNotificationIntensity) {
-        mDefaultNotificationIntensity = defaultNotificationIntensity;
-    }
-
-    public void setDefaultRingVibrationIntensity(@VibrationIntensity int defaultRingIntensity) {
-        mDefaultRingIntensity = defaultRingIntensity;
+    FakeVibrator(Context context) {
+        super(context);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 777e3f4..2ad0e93 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -87,7 +87,7 @@
         @Override
         public long on(long milliseconds, long vibrationId) {
             mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
-                    /* frequency= */ 0, (int) milliseconds));
+                    /* frequencyHz= */ 0, (int) milliseconds));
             applyLatency();
             scheduleListener(milliseconds, vibrationId);
             return milliseconds;
@@ -158,7 +158,7 @@
         }
 
         @Override
-        public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+        public boolean getInfo(VibratorInfo.Builder infoBuilder) {
             infoBuilder.setCapabilities(mCapabilities);
             infoBuilder.setSupportedBraking(mSupportedBraking);
             infoBuilder.setPwleSizeMax(mPwleSizeMax);
@@ -170,9 +170,8 @@
             }
             infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
             infoBuilder.setQFactor(mQFactor);
-            infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
-                    mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
-                    mMaxAmplitudes));
+            infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+                    mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
             return mIsInfoLoadSuccessful;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java
index 4c3312c..a3edf23 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java
@@ -70,9 +70,9 @@
     @Test
     public void testRampAndStepSegments_withNoOffSegment_keepsListUnchanged() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
@@ -86,12 +86,12 @@
         mAdapter = new RampDownAdapter(/* rampDownDuration= */ 0, TEST_STEP_DURATION);
 
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 50)));
+                        /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 50)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
@@ -102,12 +102,12 @@
     @Test
     public void testStepSegments_withShortZeroSegment_replaceWithStepsDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10)));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5));
 
         assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
         assertEquals(expectedSegments, segments);
@@ -116,17 +116,17 @@
     @Test
     public void testStepSegments_withLongZeroSegment_replaceWithStepsDownWithRemainingOffSegment() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 50),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
+                        /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 50),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100));
 
         assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
         assertEquals(expectedSegments, segments);
@@ -135,16 +135,16 @@
     @Test
     public void testStepSegments_withZeroSegmentBeforeRepeat_fixesRepeat() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 50),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100));
 
         // Repeat index fixed after intermediate steps added
         assertEquals(5, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
@@ -154,14 +154,14 @@
     @Test
     public void testStepSegments_withZeroSegmentAfterRepeat_preservesRepeat() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100));
 
         assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
         assertEquals(expectedSegments, segments);
@@ -170,22 +170,22 @@
     @Test
     public void testStepSegments_withZeroSegmentAtRepeat_fixesRepeatAndAppendOriginalToListEnd() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100)));
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 50),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 100)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 100),
                 // Original zero segment appended to the end of new looping vibration,
                 // then converted to ramp down as well.
-                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35));
+                new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35));
 
         // Repeat index fixed after intermediate steps added
         assertEquals(5, mAdapter.apply(segments, 1, TEST_VIBRATOR_INFO));
@@ -195,8 +195,8 @@
     @Test
     public void testStepSegments_withRepeatToNonZeroSegment_keepsOriginalSteps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100)));
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         assertEquals(0, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -208,14 +208,14 @@
     public void testStepSegments_withRepeatToShortZeroSegment_skipAndAppendRampDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30)));
+                        /* startfrequencyHz= */ 0, /* endfrequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
+                        /* startfrequencyHz= */ 0, /* endfrequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5));
 
         // Shift repeat index to the right to use append instead of zero segment.
         assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -226,17 +226,17 @@
     @Test
     public void testStepSegments_withRepeatToLongZeroSegment_splitAndAppendRampDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 120),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30)));
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 120),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 // Split long zero segment to skip part of it.
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 20),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 100),
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30),
-                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 20),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30),
+                new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5));
 
         // Shift repeat index to the right to use append with part of the zero segment.
         assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -248,18 +248,20 @@
     public void testRampSegments_withShortZeroSegment_replaceWithRampDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
+                        /* duration= */ 30)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
+                        /* duration= */ 30));
 
         assertEquals(2, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
 
@@ -269,20 +271,23 @@
     @Test
     public void testRampSegments_withLongZeroSegment_splitAndAddRampDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 150),
+                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 150, /* duration= */ 150),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
+                        /* duration= */ 30)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 130),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100,
+                        /* duration= */ 130),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
+                        /* duration= */ 30));
 
         // Repeat index fixed after intermediate steps added
         assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
@@ -294,9 +299,10 @@
     public void testRampSegments_withRepeatToNonZeroSegment_keepsOriginalSteps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
+                        /* duration= */ 30)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         assertEquals(0, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -307,15 +313,15 @@
     @Test
     public void testRampSegments_withRepeatToShortZeroSegment_skipAndAppendRampDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 1,
-                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 20)));
+                        /* startFrequencyHz= */ 40, /* endFrequencyHz= */ 80, /* duration= */ 20)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 20),
+                        /* startFrequencyHz= */ 40, /* endFrequencyHz= */ 80, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20));
+                        /* startFrequencyHz= */ 80, /* endFrequencyHz= */ 80, /* duration= */ 20));
 
         // Shift repeat index to the right to use append instead of zero segment.
         assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -327,19 +333,19 @@
     public void testRampSegments_withRepeatToLongZeroSegment_splitAndAppendRampDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 70),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 70),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 30)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 // Split long zero segment to skip part of it.
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 50),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 30),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20));
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 20));
 
         // Shift repeat index to the right to use append with part of the zero segment.
         assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
index 95c3bd93..22db917 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
@@ -45,6 +45,12 @@
 @Presubmit
 public class RampToStepAdapterTest {
     private static final int TEST_STEP_DURATION = 5;
+    private static final float[] TEST_AMPLITUDE_MAP = new float[]{
+            /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
+    private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
+            new VibratorInfo.FrequencyMapping(
+                    /* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
+                    /* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
 
     private RampToStepAdapter mAdapter;
 
@@ -56,7 +62,7 @@
     @Test
     public void testStepAndPrebakedAndPrimitiveSegments_keepsListUnchanged() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
                 new PrebakedSegment(
                         VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
                 new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
@@ -71,9 +77,9 @@
     @Test
     public void testRampSegments_withPwleCapability_keepsListUnchanged() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 1, /* duration= */ 20)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -86,27 +92,28 @@
     @Test
     public void testRampSegments_withoutPwleCapability_convertsRampsToSteps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 10, /* duration= */ 100),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 0, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ -3, /* endFrequency= */ 0, /* duration= */ 11),
+                        /* startFrequencyHz= */ 30, /* endFrequencyHz= */ 60, /* duration= */ 11),
                 new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
-                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200)));
+                        /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 1, /* duration= */ 200)));
 
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 10, /* duration= */ 100),
                 // 10ms ramp becomes 2 steps
-                new StepSegment(/* amplitude= */ 1, /* frequency= */ -4, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 2, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 10, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 150, /* duration= */ 5),
                 // 11ms ramp becomes 3 steps
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -3, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.6f, /* frequency= */ -2, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 0, /* duration= */ 1),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 30, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.6f, /* frequencyHz= */ 40, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 60, /* duration= */ 1),
                 // 200ms ramp with same amplitude becomes a single step
-                new StepSegment(/* amplitude= */ 0.65f, /* frequency= */ 0, /* duration= */ 200));
+                new StepSegment(/* amplitude= */ 0.65f, /* frequencyHz= */ 150,
+                        /* duration= */ 200));
 
         // Repeat index fixed after intermediate steps added
         assertEquals(4, mAdapter.apply(segments, 3, createVibratorInfo()));
@@ -117,6 +124,7 @@
     private static VibratorInfo createVibratorInfo(int... capabilities) {
         return new VibratorInfo.Builder(0)
                 .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
+                .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
                 .build();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
index 128cd2f..18ff953 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -44,6 +44,13 @@
  */
 @Presubmit
 public class StepToRampAdapterTest {
+    private static final float[] TEST_AMPLITUDE_MAP = new float[]{
+            /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
+    private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
+            new VibratorInfo.FrequencyMapping(
+                    /* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
+                    /* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
+
     private StepToRampAdapter mAdapter;
 
     @Before
@@ -55,7 +62,7 @@
     public void testRampAndPrebakedAndPrimitiveSegments_returnsOriginalSegments() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
+                        /* startFrequencyHz= */ 40f, /* endFrequencyHz= */ 20f, /* duration= */ 10),
                 new PrebakedSegment(
                         VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
                 new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
@@ -71,27 +78,28 @@
     public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
-                        /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25),
+                        /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 50, /* duration= */ 25),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
-                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)));
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
-                        /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8),
+                        /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 118f, /* duration= */ 8),
                 new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
-                        /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f,
+                        /* startFrequencyHz= */ 118f, /* endFrequencyHz= */ 86f,
                         /* duration= */ 8),
                 new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9),
+                        /* startFrequencyHz= */ 86f, /* endFrequencyHz= */ 50f, /* duration= */ 9),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
-                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5));
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5));
 
         VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
                 .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
                 .setPwlePrimitiveDurationMax(10)
+                .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
                 .build();
 
         // Update repeat index to skip the ramp splits.
@@ -102,9 +110,9 @@
     @Test
     public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         assertEquals(-1, mAdapter.apply(segments, -1, createVibratorInfo()));
@@ -116,13 +124,13 @@
     @Test
     public void testStepAndRampSegments_withPwleCapabilityAndNoFrequency_keepsOriginalSteps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
                 new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+                        /* startFrequencyHz= */ 40, /* endFrequencyHz= */ 200, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 1, /* duration= */ 20)));
         List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
 
         VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -135,25 +143,25 @@
     @Test
     public void testStepAndRampSegments_withPwleCapabilityAndStepNextToRamp_convertsStepsToRamps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 60),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -1, /* duration= */ 60)));
+                        /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 10, /* duration= */ 60)));
 
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100),
+                        /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 60),
                 new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+                        /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 50),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
-                        /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
+                        /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20),
                 new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 60));
+                        /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 60));
 
         VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
         assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
@@ -165,13 +173,13 @@
     @Test
     public void testStepSegments_withPwleCapabilityAndFrequency_convertsStepsToRamps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ -1, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 1, /* duration= */ 100)));
+                new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 100, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 6)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
-                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 100));
+                        /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 6));
 
         VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
         assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
@@ -183,6 +191,7 @@
     private static VibratorInfo createVibratorInfo(int... capabilities) {
         return new VibratorInfo.Builder(0)
                 .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
+                .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
                 .build();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index 59c0b0e..8167710 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -16,6 +16,14 @@
 
 package com.android.server.vibrator;
 
+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.Vibrator.VIBRATION_INTENSITY_HIGH;
+import static android.os.Vibrator.VIBRATION_INTENSITY_LOW;
+import static android.os.Vibrator.VIBRATION_INTENSITY_MEDIUM;
+import static android.os.Vibrator.VIBRATION_INTENSITY_OFF;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
@@ -24,7 +32,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.ContextWrapper;
 import android.os.Handler;
 import android.os.IExternalVibratorService;
@@ -37,6 +44,7 @@
 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.Presubmit;
 import android.provider.Settings;
@@ -68,29 +76,31 @@
     @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Mock private PowerManagerInternal mPowerManagerInternalMock;
+    @Mock private VibrationConfig mVibrationConfigMock;
 
     private TestLooper mTestLooper;
     private ContextWrapper mContextSpy;
-    private FakeVibrator mFakeVibrator;
     private VibrationSettings mVibrationSettings;
     private VibrationScaler mVibrationScaler;
 
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
-        mFakeVibrator = new FakeVibrator();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-        when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
 
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
 
+        Settings.System.putInt(contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
+        Settings.System.putInt(contentResolver, Settings.System.VIBRATE_WHEN_RINGING, 1);
+
         mVibrationSettings = new VibrationSettings(
-                mContextSpy, new Handler(mTestLooper.getLooper()));
+                mContextSpy, new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
         mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings);
+
         mVibrationSettings.onSystemReady();
     }
 
@@ -101,173 +111,157 @@
 
     @Test
     public void testGetExternalVibrationScale() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
         assertEquals(IExternalVibratorService.SCALE_VERY_HIGH,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
         assertEquals(IExternalVibratorService.SCALE_HIGH,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
         assertEquals(IExternalVibratorService.SCALE_NONE,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
         assertEquals(IExternalVibratorService.SCALE_LOW,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
         assertEquals(IExternalVibratorService.SCALE_VERY_LOW,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
-        // Unexpected vibration intensity will be treated as SCALE_NONE.
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+        // Vibration setting being bypassed will use default setting and not scale.
         assertEquals(IExternalVibratorService.SCALE_NONE,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
     }
 
     @Test
     public void scale_withPrebakedSegment_setsEffectStrengthBasedOnSettings() {
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         PrebakedSegment effect = new PrebakedSegment(VibrationEffect.EFFECT_CLICK,
                 /* shouldFallback= */ false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
-        PrebakedSegment scaled = mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION);
+        PrebakedSegment scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
+                VIBRATION_INTENSITY_MEDIUM);
+        scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
-        scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
-        // Unexpected intensity setting will be mapped to STRONG.
-        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
+        // Vibration setting being bypassed will use default setting.
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
     }
 
     @Test
     public void scale_withPrebakedEffect_setsEffectStrengthBasedOnSettings() {
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
 
-        PrebakedSegment scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+        PrebakedSegment scaled =
+                getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+                VIBRATION_INTENSITY_MEDIUM);
+        scaled = getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        scaled = getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
-        scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
-        // Unexpected intensity setting will be mapped to STRONG.
-        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        scaled = getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
+        // Vibration setting being bypassed will use default setting.
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
     }
 
     @Test
     public void scale_withOneShotAndWaveform_resolvesAmplitude() {
         // No scale, default amplitude still resolved
-        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
 
         StepSegment resolved = getFirstSegment(mVibrationScaler.scale(
                 VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE),
-                VibrationAttributes.USAGE_RINGTONE));
+                USAGE_RINGTONE));
         assertTrue(resolved.getAmplitude() > 0);
 
         resolved = getFirstSegment(mVibrationScaler.scale(
                 VibrationEffect.createWaveform(new long[]{10},
                         new int[]{VibrationEffect.DEFAULT_AMPLITUDE}, -1),
-                VibrationAttributes.USAGE_RINGTONE));
+                USAGE_RINGTONE));
         assertTrue(resolved.getAmplitude() > 0);
     }
 
     @Test
     public void scale_withOneShotAndWaveform_scalesAmplitude() {
-        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
 
         StepSegment scaled = getFirstSegment(mVibrationScaler.scale(
-                VibrationEffect.createOneShot(128, 128), VibrationAttributes.USAGE_RINGTONE));
+                VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE));
         // Ringtone scales up.
         assertTrue(scaled.getAmplitude() > 0.5);
 
         scaled = getFirstSegment(mVibrationScaler.scale(
                 VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1),
-                VibrationAttributes.USAGE_NOTIFICATION));
+                USAGE_NOTIFICATION));
         // Notification scales down.
         assertTrue(scaled.getAmplitude() < 0.5);
 
         scaled = getFirstSegment(mVibrationScaler.scale(VibrationEffect.createOneShot(128, 128),
-                VibrationAttributes.USAGE_TOUCH));
+                USAGE_TOUCH));
         // Haptic feedback does not scale.
         assertEquals(128f / 255, scaled.getAmplitude(), 1e-5);
     }
 
     @Test
     public void scale_withComposed_scalesPrimitives() {
-        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
 
         VibrationEffect composed = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f).compose();
 
-        PrimitiveSegment scaled = getFirstSegment(mVibrationScaler.scale(composed,
-                VibrationAttributes.USAGE_RINGTONE));
+        PrimitiveSegment scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_RINGTONE));
         // Ringtone scales up.
         assertTrue(scaled.getScale() > 0.5f);
 
-        scaled = getFirstSegment(mVibrationScaler.scale(composed,
-                VibrationAttributes.USAGE_NOTIFICATION));
+        scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_NOTIFICATION));
         // Notification scales down.
         assertTrue(scaled.getScale() < 0.5f);
 
-        scaled = getFirstSegment(mVibrationScaler.scale(composed, VibrationAttributes.USAGE_TOUCH));
+        scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_TOUCH));
         // Haptic feedback does not scale.
         assertEquals(0.5, scaled.getScale(), 1e-5);
     }
 
+    private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
+            @Vibrator.VibrationIntensity int intensity) {
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
+    }
+
     private <T extends VibrationEffectSegment> T getFirstSegment(VibrationEffect.Composed effect) {
         return (T) effect.getSegments().get(0);
     }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index ab9fbb5..5d4ffbb 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -16,9 +16,11 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
 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_PHYSICAL_EMULATION;
 import static android.os.VibrationAttributes.USAGE_RINGTONE;
@@ -35,6 +37,7 @@
 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.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
@@ -45,7 +48,6 @@
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.media.AudioManager;
@@ -55,7 +57,9 @@
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.os.test.TestLooper;
+import android.os.vibrator.VibrationConfig;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 
@@ -87,6 +91,19 @@
     private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
             .setBatterySaverEnabled(true).build();
 
+    private static final int[] ALL_USAGES = new int[] {
+            USAGE_UNKNOWN,
+            USAGE_ACCESSIBILITY,
+            USAGE_ALARM,
+            USAGE_COMMUNICATION_REQUEST,
+            USAGE_HARDWARE_FEEDBACK,
+            USAGE_MEDIA,
+            USAGE_NOTIFICATION,
+            USAGE_PHYSICAL_EMULATION,
+            USAGE_RINGTONE,
+            USAGE_TOUCH,
+    };
+
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
@@ -96,23 +113,23 @@
     private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
     @Mock
     private PowerManagerInternal mPowerManagerInternalMock;
+    @Mock
+    private VibrationConfig mVibrationConfigMock;
 
     private TestLooper mTestLooper;
     private ContextWrapper mContextSpy;
     private AudioManager mAudioManager;
-    private FakeVibrator mFakeVibrator;
     private VibrationSettings mVibrationSettings;
     private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
 
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
-        mFakeVibrator = new FakeVibrator();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-        when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
+
         doAnswer(invocation -> {
             mRegisteredPowerModeListener = invocation.getArgument(0);
             return null;
@@ -121,16 +138,18 @@
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
 
+        setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
         mAudioManager = mContextSpy.getSystemService(AudioManager.class);
         mVibrationSettings = new VibrationSettings(mContextSpy,
-                new Handler(mTestLooper.getLooper()));
-        mVibrationSettings.onSystemReady();
+                new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
 
         // Simulate System defaults.
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
         setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+        mVibrationSettings.onSystemReady();
     }
 
     @After
@@ -145,12 +164,15 @@
         setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        verify(mListenerMock, times(7)).onChange();
+        verify(mListenerMock, times(10)).onChange();
     }
 
     @Test
@@ -192,9 +214,7 @@
                 UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
 
         for (int usage : expectedAllowedVibrations) {
-            assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationNotIgnoredForUsage(usage);
         }
     }
 
@@ -209,10 +229,7 @@
                 UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
 
         for (int usage : expectedIgnoredVibrations) {
-            assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
-                    Vibration.Status.IGNORED_BACKGROUND,
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND);
         }
     }
 
@@ -221,10 +238,9 @@
         mVibrationSettings.mUidObserver.onUidStateChanged(
                 UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
@@ -238,9 +254,7 @@
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
 
         for (int usage : expectedAllowedVibrations) {
-            assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationNotIgnoredForUsage(usage);
         }
     }
 
@@ -257,10 +271,7 @@
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
 
         for (int usage : expectedIgnoredVibrations) {
-            assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
-                    Vibration.Status.IGNORED_FOR_POWER,
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER);
         }
     }
 
@@ -268,130 +279,168 @@
     public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() {
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() {
         // Vibrating settings on are overruled by ringer mode.
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         setRingerMode(AudioManager.RINGER_MODE_SILENT);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_RINGTONE || usage == USAGE_TOUCH) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() {
-        // Vibrating settings off are overruled by ringer mode.
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOff_ignoresRingtoneOnly() {
-        // Vibrating settings off are respected for normal ringer mode.
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+    public void shouldIgnoreVibration_withRingerModeNormal_allowsAllVibrations() {
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+    }
+
+
+    @Test
+    public void shouldIgnoreVibration_vibrateOnDisabled_ignoresUsagesNotAccessibility() {
+        setUserSetting(Settings.System.VIBRATE_ON, 0);
+
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_ACCESSIBILITY) {
+                assertVibrationNotIgnoredForUsage(usage);
+            } else {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOn_allowsAllVibrations() {
+    public void shouldIgnoreVibration_vibrateOnEnabledOrUnset_allowsAnyUsage() {
+        deleteUserSetting(Settings.System.VIBRATE_ON);
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+
+        setUserSetting(Settings.System.VIBRATE_ON, 1);
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+    }
+    @Test
+    public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() {
+        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_RINGTONE) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
+    }
+
+    @Test
+    public void shouldIgnoreVibration_withRingSettingsOn_allowsAllVibrations() {
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
-        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingerModeNormalAndRampingRingerOn_allowsAllVibrations() {
+    public void shouldIgnoreVibration_withRampingRingerOn_allowsAllVibrations() {
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
-        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+    }
+
+    @Test
+    public void shouldIgnoreVibration_withHapticFeedbackDisabled_ignoresTouchVibration() {
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0);
+
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_TOUCH) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withHapticFeedbackSettingsOff_ignoresTouchVibration() {
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_TOUCH) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withHardwareFeedbackSettingsOff_ignoresHardwareVibrations() {
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withNotificationSettingsOff_ignoresNotificationVibrations() {
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_NOTIFICATION) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
     }
 
     @Test
@@ -402,15 +451,15 @@
         setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_RINGTONE) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+            assertVibrationNotIgnoredForUsageAndFlags(usage,
+                    VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+        }
     }
 
     @Test
@@ -423,90 +472,40 @@
     }
 
     @Test
-    public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_HIGH);
-
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+    public void getDefaultIntensity_returnsIntensityFromVibratorConfig() {
+        setDefaultIntensity(VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
-
-        VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
-                new Handler(mTestLooper.getLooper()));
-
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                vibrationSettings.getDefaultIntensity(USAGE_ALARM));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_TOUCH));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
-    }
-
-    @Test
-    public void getDefaultIntensity_returnsIntensityFromVibratorService() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_MEDIUM);
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_LOW);
-
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_ALARM));
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_TOUCH));
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
+        for (int usage : ALL_USAGES) {
+            assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getDefaultIntensity(usage));
+        }
     }
 
     @Test
     public void getCurrentIntensity_returnsIntensityFromSettings() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_OFF);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_OFF);
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
-
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
 
-        assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_ALARM));
-        assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getCurrentIntensity(USAGE_NOTIFICATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getCurrentIntensity(USAGE_UNKNOWN));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+        for (int usage : ALL_USAGES) {
+            assertEquals(errorMessageForUsage(usage),
+                    VIBRATION_INTENSITY_LOW,
+                    mVibrationSettings.getCurrentIntensity(usage));
+        }
     }
 
     @Test
     public void getCurrentIntensity_updateTriggeredAfterUserSwitched() {
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
@@ -524,8 +523,9 @@
 
     @Test
     public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+        mVibrationSettings.updateSettings();
         assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
         // If haptic feedback is off, fallback to default value.
         assertEquals(VIBRATION_INTENSITY_MEDIUM,
@@ -533,15 +533,11 @@
         assertEquals(VIBRATION_INTENSITY_MEDIUM,
                 mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
 
-        // Switching user is not working with FakeSettingsProvider.
-        // Testing the broadcast flow manually.
-        Settings.System.putIntForUser(mContextSpy.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH,
-                UserHandle.USER_CURRENT);
-        mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
-                new Intent(Intent.ACTION_USER_SWITCHED));
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        mVibrationSettings.updateSettings();
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+        // If haptic feedback is on, fallback to that value.
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
         assertEquals(VIBRATION_INTENSITY_HIGH,
@@ -557,10 +553,52 @@
         assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_DOUBLE_CLICK));
     }
 
+    private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
+            Vibration.Status expectedStatus) {
+        assertEquals(errorMessageForUsage(usage),
+                expectedStatus,
+                mVibrationSettings.shouldIgnoreVibration(UID,
+                        VibrationAttributes.createForUsage(usage)));
+    }
+
+    private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) {
+        assertVibrationNotIgnoredForUsageAndFlags(usage, /* flags= */ 0);
+    }
+
+    private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
+            @VibrationAttributes.Flag int flags) {
+        assertNull(errorMessageForUsage(usage),
+                mVibrationSettings.shouldIgnoreVibration(UID,
+                        new VibrationAttributes.Builder()
+                                .setUsage(usage)
+                                .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
+                                .build()));
+    }
+
+    private String errorMessageForUsage(int usage) {
+        return "Error for usage " + VibrationAttributes.usageToString(usage);
+    }
+
+    private void setDefaultIntensity(@Vibrator.VibrationIntensity int intensity) {
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())).thenReturn(intensity);
+    }
+
+    private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
+            @Vibrator.VibrationIntensity int intensity) {
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
+    }
+
+    private void deleteUserSetting(String settingName) {
+        Settings.System.putStringForUser(
+                mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
+        // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
+        mVibrationSettings.updateSettings();
+    }
+
     private void setUserSetting(String settingName, int value) {
         Settings.System.putIntForUser(
                 mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
-        // FakeSettingsProvider don't support testing triggering ContentObserver yet.
+        // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
         mVibrationSettings.updateSettings();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 4cc4d55..5dd44ff 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -44,11 +44,13 @@
 import android.os.SystemClock;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.os.test.TestLooper;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
 import android.platform.test.annotations.LargeTest;
 import android.platform.test.annotations.Presubmit;
@@ -101,6 +103,8 @@
     private IBinder mVibrationToken;
     @Mock
     private IBatteryStats mIBatteryStatsMock;
+    @Mock
+    private VibrationConfig mVibrationConfigMock;
 
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
     private VibrationSettings mVibrationSettings;
@@ -113,9 +117,13 @@
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
 
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt()))
+                .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        when(mVibrationConfigMock.getRampStepDurationMs()).thenReturn(TEST_RAMP_STEP_DURATION);
+
         Context context = InstrumentationRegistry.getContext();
         mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
-                /* rampDownDuration= */ 0, TEST_RAMP_STEP_DURATION);
+                mVibrationConfigMock);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mWakeLock = context.getSystemService(
                 PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
@@ -553,8 +561,8 @@
         VibrationEffect effect = VibrationEffect.startWaveform()
                 .addStep(1, 10)
                 .addRamp(0, 20)
-                .addStep(0.8f, 1, 30)
-                .addRamp(0.6f, -1, 40)
+                .addStep(0.8f, 100, 30)
+                .addRamp(0.6f, 200, 40)
                 .build();
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
@@ -565,12 +573,13 @@
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
-                expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10),
-                expectedRamp(/* StartAmplitude= */ 1, /* endAmplitude= */ 0,
-                        /* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 20),
-                expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
-                expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
-                        /* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
+                expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
+                expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
+                        /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 20),
+                expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 30),
+                expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f,
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200,
+                        /* duration= */ 40)),
                 fakeVibrator.getEffectSegments());
         assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking());
     }
@@ -589,8 +598,8 @@
         VibrationEffect effect = VibrationEffect.startWaveform()
                 .addStep(1, 10)
                 .addRamp(0, 20)
-                .addStep(0.8f, 1, 30)
-                .addRamp(0.6f, -1, 40)
+                .addStep(0.8f, 10, 30)
+                .addRamp(0.6f, 100, 40)
                 .build();
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
@@ -1110,9 +1119,7 @@
 
     @Test
     public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
@@ -1138,9 +1145,7 @@
 
     @Test
     public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() {
-        int rampDownDuration = 10_000;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(10_000);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
@@ -1173,9 +1178,7 @@
     @Test
     public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled()
             throws Exception {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
@@ -1201,9 +1204,7 @@
 
     @Test
     public void vibrate_predefinedWithRampDown_doesNotAddRampDown() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
@@ -1223,9 +1224,7 @@
 
     @Test
     public void vibrate_composedWithRampDown_doesNotAddRampDown() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
                 IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1250,9 +1249,7 @@
 
     @Test
     public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
         fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
@@ -1345,7 +1342,8 @@
     }
 
     private VibrationEffectSegment expectedOneShot(long millis) {
-        return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequency= */ 0, (int) millis);
+        return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
+                /* frequencyHz= */ 0, (int) millis);
     }
 
     private VibrationEffectSegment expectedPrebaked(int effectId) {
@@ -1356,13 +1354,13 @@
         return new PrimitiveSegment(primitiveId, scale, delay);
     }
 
-    private VibrationEffectSegment expectedRamp(float amplitude, float frequency, int duration) {
-        return expectedRamp(amplitude, amplitude, frequency, frequency, duration);
+    private VibrationEffectSegment expectedRamp(float amplitude, float frequencyHz, int duration) {
+        return expectedRamp(amplitude, amplitude, frequencyHz, frequencyHz, duration);
     }
 
     private VibrationEffectSegment expectedRamp(float startAmplitude, float endAmplitude,
-            float startFrequency, float endFrequency, int duration) {
-        return new RampSegment(startAmplitude, endAmplitude, startFrequency, endFrequency,
+            float startFrequencyHz, float endFrequencyHz, int duration) {
+        return new RampSegment(startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz,
                 duration);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 9fb8b38..cb4982b 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertTrue;
 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.eq;
@@ -236,7 +235,7 @@
 
         RampSegment[] primitives = new RampSegment[]{
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
-                        /* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 10)
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 10)
         };
         assertEquals(15L, controller.on(primitives, 12));
         assertTrue(controller.isVibrating());
@@ -312,10 +311,10 @@
 
     private void mockVibratorCapabilities(int capabilities) {
         VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
-                Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
-        when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
+                Float.NaN, Float.NaN, Float.NaN, null);
+        when(mNativeWrapperMock.getInfo(any(VibratorInfo.Builder.class)))
                 .then(invocation -> {
-                    ((VibratorInfo.Builder) invocation.getArgument(1))
+                    ((VibratorInfo.Builder) invocation.getArgument(0))
                             .setCapabilities(capabilities)
                             .setFrequencyMapping(frequencyMapping);
                     return true;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index c0f7596..ab86e29 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -155,12 +156,13 @@
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
-        mVibrator = new FakeVibrator();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+
+        mVibrator = new FakeVibrator(mContextSpy);
         when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibrator);
         when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
         when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
@@ -175,8 +177,13 @@
         }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
 
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
@@ -437,7 +444,7 @@
                 UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
 
         PrebakedSegment expected = new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
+                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
         // Only vibrators 1 and 3 have always-on capabilities.
         assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expected);
@@ -461,10 +468,10 @@
                 UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
 
         PrebakedSegment expectedClick = new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
+                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
         PrebakedSegment expectedTick = new PrebakedSegment(
-                VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
+                VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
         // Enables click on vibrator 1 and tick on vibrator 2 only.
         assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expectedClick);
@@ -539,7 +546,6 @@
     public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
-        mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
         fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK,
                 VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
 
@@ -932,55 +938,67 @@
 
     @Test
     public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
-        mVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
+        int defaultNotificationIntensity =
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+                defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH
+                        ? defaultNotificationIntensity + 1
+                        : defaultNotificationIntensity);
+
+        int defaultTouchIntensity =
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
+                defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
+                        ? defaultTouchIntensity - 1
+                        : defaultTouchIntensity);
+
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
 
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
         fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
                 IVibrator.CAP_COMPOSE_EFFECTS);
-        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         VibratorManagerService service = createSystemReadyService();
 
         vibrate(service, CombinedVibration.startSequential()
-                .addNext(1, VibrationEffect.createOneShot(20, 100))
+                .addNext(1, VibrationEffect.createOneShot(100, 125))
                 .combine(), NOTIFICATION_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
                 service, TEST_TIMEOUT_MILLIS));
 
         vibrate(service, VibrationEffect.startComposition()
-                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
                 .compose(), HAPTIC_FEEDBACK_ATTRS);
+        assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
+                service, TEST_TIMEOUT_MILLIS));
+
+        vibrate(service, VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+                .compose(), ALARM_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
                 service, TEST_TIMEOUT_MILLIS));
 
-        vibrate(service, CombinedVibration.startParallel()
-                .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
-                .combine(), ALARM_ATTRS);
-        assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 4,
+        vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
+        assertFalse(waitUntil(s -> fakeVibrator.getEffectSegments().size() > 3,
                 service, TEST_TIMEOUT_MILLIS));
 
-        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+        assertEquals(3, fakeVibrator.getEffectSegments().size());
 
-        assertEquals(4, fakeVibrator.getEffectSegments().size());
+        // Notification vibrations will be scaled with SCALE_HIGH or none if default is high.
+        assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH,
+                0.6 < fakeVibrator.getAmplitudes().get(0));
 
-        // Notification vibrations will be scaled with SCALE_VERY_HIGH.
-        assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0));
+        // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
+        assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
+                0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale());
 
-        // Haptic feedback vibrations will be scaled with SCALE_LOW.
-        assertTrue(0.5 < ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale());
-        assertTrue(0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale());
-
-        // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
-        PrebakedSegment expected = new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
-        assertEquals(expected, fakeVibrator.getEffectSegments().get(3));
+        // Alarm vibration will be scaled with SCALE_NONE.
+        assertEquals(1f,
+                ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale(), 1e-5);
 
         // Ring vibrations have intensity OFF and are not played.
     }
@@ -1100,7 +1118,7 @@
         int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
         mExternalVibratorService.onExternalVibrationStop(externalVibration);
 
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
         assertEquals(Arrays.asList(false, true, false),
                 mVibratorProviders.get(1).getExternalControlStates());
     }
@@ -1127,8 +1145,8 @@
                 ringtoneAudioAttrs, secondController);
         int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
 
-        assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
-        assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale);
         verify(firstController).mute();
         verify(secondController, never()).mute();
         // Set external control called only once.
@@ -1151,7 +1169,7 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
                 mock(IExternalVibrationController.class));
         int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
 
         // Vibration is cancelled.
         assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1163,7 +1181,6 @@
     public void onExternalVibration_withRingtone_usesRingerModeSettings() {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
-        mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
         AudioAttributes audioAttrs = new AudioAttributes.Builder()
                 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
                 .build();
@@ -1181,13 +1198,40 @@
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
 
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+    }
+
+    @Test
+    public void onExternalVibration_withBypassMuteAudioFlag_ignoresUserSettings() {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_OFF);
+        AudioAttributes audioAttrs = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_ALARM)
+                .build();
+        AudioAttributes flaggedAudioAttrs = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_ALARM)
+                .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
+                .build();
+        createSystemReadyService();
+
+        int scale = mExternalVibratorService.onExternalVibrationStart(
+                new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
+                        mock(IExternalVibrationController.class)));
+        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+
+        createSystemReadyService();
+        scale = mExternalVibratorService.onExternalVibrationStart(
+                new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs,
+                        mock(IExternalVibrationController.class)));
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
     }
 
     private VibrationEffectSegment expectedPrebaked(int effectId) {
@@ -1235,10 +1279,6 @@
                 mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
     }
 
-    private void setGlobalSetting(String settingName, int value) {
-        Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
-    }
-
     private void vibrate(VibratorManagerService service, VibrationEffect effect,
             VibrationAttributes attrs) {
         vibrate(service, CombinedVibration.createParallel(effect), attrs);
diff --git a/services/tests/servicestests/src/com/android/server/wm/OWNERS b/services/tests/servicestests/src/com/android/server/wm/OWNERS
new file mode 100644
index 0000000..361760d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index f21991d..a12bc3b 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -16,14 +16,20 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE;
 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
 import static android.app.UiModeManager.MODE_NIGHT_NO;
 import static android.app.UiModeManager.MODE_NIGHT_YES;
 import static android.app.UiModeManager.PROJECTION_TYPE_ALL;
 import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
 import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
@@ -194,7 +200,7 @@
 
     @Ignore // b/152719290 - Fails on stage-aosp-master
     @Test
-    public void setNightMoveActivated_overridesFunctionCorrectly() throws RemoteException {
+    public void setNightModeActivated_overridesFunctionCorrectly() throws RemoteException {
         // set up
         when(mPowerManager.isInteractive()).thenReturn(false);
         mService.setNightMode(MODE_NIGHT_NO);
@@ -225,6 +231,29 @@
     }
 
     @Test
+    public void setNightModeActivated_true_withCustomModeBedtime_shouldOverrideNightModeCorrectly()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+
+        mService.setNightModeActivated(true);
+
+        assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isTrue();
+    }
+
+    @Test
+    public void setNightModeActivated_false_withCustomModeBedtime_shouldOverrideNightModeCorrectly()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+
+        mService.setNightModeActivated(true);
+        mService.setNightModeActivated(false);
+
+        assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isFalse();
+    }
+
+    @Test
     public void setAutoMode_screenOffRegistered() throws RemoteException {
         try {
             mService.setNightMode(MODE_NIGHT_NO);
@@ -247,7 +276,44 @@
     }
 
     @Test
-    public void setNightModeActivated_fromNoToYesAndBAck() throws RemoteException {
+    public void setNightModeCustomType_bedtime_shouldNotActivateNightMode() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void setNightModeCustomType_noPermission_shouldThrow() throws RemoteException {
+        when(mContext.checkCallingOrSelfPermission(eq(MODIFY_DAY_NIGHT_MODE)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        assertThrows(SecurityException.class,
+                () -> mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME));
+    }
+
+    @Test
+    public void setNightModeCustomType_bedtime_shouldHaveNoScreenOffRegistered()
+            throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        ArgumentCaptor<IntentFilter> intentFiltersCaptor = ArgumentCaptor.forClass(
+                IntentFilter.class);
+        verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class),
+                intentFiltersCaptor.capture());
+
+        List<IntentFilter> intentFilters = intentFiltersCaptor.getAllValues();
+        for (IntentFilter intentFilter : intentFilters) {
+            assertThat(intentFilter.hasAction(Intent.ACTION_SCREEN_OFF)).isFalse();
+        }
+    }
+
+    @Test
+    public void setNightModeActivated_fromNoToYesAndBack() throws RemoteException {
         mService.setNightMode(MODE_NIGHT_NO);
         mService.setNightModeActivated(true);
         assertTrue(isNightModeActivated());
@@ -256,7 +322,7 @@
     }
 
     @Test
-    public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException {
+    public void setNightModeActivated_permissionToChangeOtherUsers() throws RemoteException {
         SystemService.TargetUser user = mock(SystemService.TargetUser.class);
         doReturn(9).when(user).getUserIdentifier();
         mUiManagerService.onUserSwitching(user, user);
@@ -267,6 +333,89 @@
     }
 
     @Test
+    public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_shouldActivate()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOffAndBedtime_shouldDeactivate()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndSchedule_shouldNotActivate()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, true /* active */);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_shouldNotActivate()
+            throws RemoteException {
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_thenCustomTypeBedtime_shouldActivate()
+            throws RemoteException {
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeSchedule_shouldKeepNightModeActivate()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        LocalTime now = LocalTime.now();
+        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeScheduleAndScreenOff_shouldDeactivateNightMode()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        LocalTime now = LocalTime.now();
+        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
     public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
         mService.setNightMode(MODE_NIGHT_NO);
         when(mTwilightState.isNight()).thenReturn(false);
@@ -283,6 +432,191 @@
     }
 
     @Test
+    public void nightModeCustomBedtime_batterySaverOn_notInBedtime_shouldActivateNightMode()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+        mPowerSaveConsumer.accept(
+                new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeCustomBedtime_batterySaverOn_afterBedtime_shouldKeepNightModeActivated()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mPowerSaveConsumer.accept(
+                new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeBedtime_duringBedtime_batterySaverOnThenOff_shouldKeepNightModeActivated()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mPowerSaveConsumer.accept(
+                new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+        mPowerSaveConsumer.accept(
+                new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeCustomBedtime_duringBedtime_batterySaverOnThenOff_finallyAfterBedtime_shouldDeactivateNightMode()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+        mPowerSaveConsumer.accept(
+                new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+        mPowerSaveConsumer.accept(
+                new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
+
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void nightModeCustomBedtime_duringBedtime_changeModeToNo_shouldDeactivateNightMode()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mService.setNightMode(MODE_NIGHT_NO);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void nightModeCustomBedtime_duringBedtime_changeModeToNoAndThenExitBedtime_shouldKeepNightModeDeactivated()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+        mService.setNightMode(MODE_NIGHT_NO);
+
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void nightModeCustomBedtime_duringBedtime_changeModeToYes_shouldKeepNightModeActivated()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mService.setNightMode(MODE_NIGHT_YES);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeCustomBedtime_duringBedtime_changeModeToYesAndThenExitBedtime_shouldKeepNightModeActivated()
+            throws RemoteException {
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeNo_duringBedtime_shouldKeepNightModeDeactivated()
+            throws RemoteException {
+        mService.setNightMode(MODE_NIGHT_NO);
+
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void nightModeNo_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()
+            throws RemoteException {
+        mService.setNightMode(MODE_NIGHT_NO);
+
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeYes_thenChangeToCustomTypeBedtime_shouldDeactivateNightMode()
+            throws RemoteException {
+        mService.setNightMode(MODE_NIGHT_YES);
+
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void nightModeYes_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()
+            throws RemoteException {
+        mService.setNightMode(MODE_NIGHT_YES);
+
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
+    public void nightModeAuto_thenChangeToCustomTypeBedtime_notInBedtime_shouldDeactivateNightMode()
+            throws RemoteException {
+        // set mode to auto
+        mService.setNightMode(MODE_NIGHT_AUTO);
+        mService.setNightModeActivated(true);
+        // now it is night time
+        doReturn(true).when(mTwilightState).isNight();
+        mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+        assertThat(isNightModeActivated()).isFalse();
+    }
+
+    @Test
+    public void nightModeAuto_thenChangeToCustomTypeBedtime_duringBedtime_shouldActivateNightMode()
+            throws RemoteException {
+        // set mode to auto
+        mService.setNightMode(MODE_NIGHT_AUTO);
+        mService.setNightModeActivated(true);
+        // now it is night time
+        doReturn(true).when(mTwilightState).isNight();
+        mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+        mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        mService.setNightModeActivatedForCustomMode(
+                MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+        assertThat(isNightModeActivated()).isTrue();
+    }
+
+    @Test
     public void setAutoMode_clearCache() throws RemoteException {
         try {
             mService.setNightMode(MODE_NIGHT_AUTO);
@@ -327,6 +661,62 @@
     }
 
     @Test
+    public void getNightModeCustomType_nightModeNo_shouldReturnUnknown() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+        assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void getNightModeCustomType_nightModeYes_shouldReturnUnknown() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_YES);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+        assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void getNightModeCustomType_nightModeAuto_shouldReturnUnknown() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_AUTO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+        assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void getNightModeCustomType_nightModeCustom_shouldReturnSchedule()
+            throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_CUSTOM);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+        assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
+    }
+
+    @Test
+    public void getNightModeCustomType_nightModeCustomBedtime_shouldReturnBedtime()
+            throws RemoteException {
+        try {
+            mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+        assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+    }
+
+    @Test
+    public void getNightModeCustomType_permissionNotGranted_shouldThrow()
+            throws RemoteException {
+        when(mContext.checkCallingOrSelfPermission(eq(MODIFY_DAY_NIGHT_MODE)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        assertThrows(SecurityException.class, () -> mService.getNightModeCustomType());
+    }
+
+    @Test
     public void isNightModeActive_nightModeYes() throws RemoteException {
         try {
             mService.setNightMode(MODE_NIGHT_YES);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index 182848b4..bd7186e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -108,7 +108,7 @@
 
     @Test
     public void testDeletionReceiver() {
-        verify(mContext, times(1)).registerReceiver(any(), any());
+        verify(mContext, times(1)).registerReceiver(any(), any(), anyInt());
     }
 
     @Test
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 a834e2b6..12cd834 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -394,7 +394,7 @@
                 "disabledMessage", 0, "disabledMessageResName",
                 null, null, 0, null, 0, 0,
                 0, "iconResName", "bitmapPath", null, 0,
-                null, null, null);
+                null, null, null, null);
         return si;
     }
 
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 419dda5..a192bf8 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -22,7 +22,10 @@
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
 import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_CAN_COLORIZE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
@@ -201,6 +204,7 @@
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
 import com.android.server.pm.PackageManagerService;
+import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.quota.MultiRateLimiter;
@@ -231,6 +235,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
 
 
@@ -239,6 +244,8 @@
 @RunWithLooper
 public class NotificationManagerServiceTest extends UiServiceTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
+    private static final String PKG_NO_CHANNELS = "com.example.no.channels";
+    private static final int TEST_TASK_ID = 1;
     private static final int UID_HEADLESS = 1000000;
 
     private final int mUid = Binder.getCallingUid();
@@ -253,6 +260,8 @@
     @Mock
     private PackageManagerInternal mPackageManagerInternal;
     @Mock
+    private PermissionPolicyInternal mPermissionPolicyInternal;
+    @Mock
     private WindowManagerInternal mWindowManagerInternal;
     @Mock
     private PermissionHelper mPermissionHelper;
@@ -381,6 +390,8 @@
         LocalServices.addService(ActivityManagerInternal.class, mAmi);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+        LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
+        LocalServices.addService(PermissionPolicyInternal.class, mPermissionPolicyInternal);
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -411,8 +422,18 @@
         when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
         when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
         when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
+        when(mPermissionPolicyInternal.canShowPermissionPromptForTask(
+                any(ActivityManager.RecentTaskInfo.class))).thenReturn(false);
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
 
+        ActivityManager.AppTask task = mock(ActivityManager.AppTask.class);
+        List<ActivityManager.AppTask> taskList = new ArrayList<>();
+        ActivityManager.RecentTaskInfo taskInfo = new ActivityManager.RecentTaskInfo();
+        taskInfo.taskId = TEST_TASK_ID;
+        when(task.getTaskInfo()).thenReturn(taskInfo);
+        taskList.add(task);
+        when(mAtm.getAppTasks(anyString(), anyInt())).thenReturn(taskList);
+
         // write to a test file; the system file isn't readable from tests
         mFile = new File(mContext.getCacheDir(), "test.xml");
         mFile.createNewFile();
@@ -466,6 +487,9 @@
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
+        verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
+        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
+        verify(mHistoryManager).onBootPhaseAppsCanStart();
 
         mService.setAudioManager(mAudioManager);
 
@@ -934,6 +958,51 @@
     }
 
     @Test
+    public void testCreateNotificationChannels_FirstChannelWithFgndTaskStartsPermDialog()
+            throws Exception {
+        when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
+                ActivityManager.RecentTaskInfo.class))).thenReturn(true);
+        final NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
+        mBinderService.createNotificationChannels(PKG_NO_CHANNELS,
+                new ParceledListSlice(Arrays.asList(channel)));
+        verify(mWorkerHandler).post(eq(new NotificationManagerService
+                .ShowNotificationPermissionPromptRunnable(PKG_NO_CHANNELS,
+                UserHandle.getUserId(mUid), TEST_TASK_ID, mPermissionPolicyInternal)));
+    }
+
+    @Test
+    public void testCreateNotificationChannels_SecondChannelWithFgndTaskDoesntStartPermDialog()
+            throws Exception {
+        when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
+                ActivityManager.RecentTaskInfo.class))).thenReturn(true);
+        assertTrue(mBinderService.getNumNotificationChannelsForPackage(PKG, mUid, true) > 0);
+
+        final NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
+        mBinderService.createNotificationChannels(PKG,
+                new ParceledListSlice(Arrays.asList(channel)));
+        verify(mWorkerHandler, never()).post(any(
+                NotificationManagerService.ShowNotificationPermissionPromptRunnable.class));
+    }
+
+    @Test
+    public void testCreateNotificationChannels_FirstChannelWithBgndTaskDoesntStartPermDialog()
+            throws Exception {
+        reset(mPermissionPolicyInternal);
+        when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
+                ActivityManager.RecentTaskInfo.class))).thenReturn(false);
+
+        final NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
+        mBinderService.createNotificationChannels(PKG,
+                new ParceledListSlice(Arrays.asList(channel)));
+
+        verify(mWorkerHandler, never()).post(any(
+                NotificationManagerService.ShowNotificationPermissionPromptRunnable.class));
+    }
+
+    @Test
     public void testCreateNotificationChannels_TwoChannels() throws Exception {
         final NotificationChannel channel1 =
                 new NotificationChannel("id1", "name", IMPORTANCE_DEFAULT);
@@ -1437,6 +1506,62 @@
     }
 
     @Test
+    public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
+                "true",
+                false);
+        Thread.sleep(300);
+
+        final String tag = "testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed";
+
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, tag, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        waitForIdle();
+
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(PKG);
+        assertThat(notifs[0].getNotification().flags).isEqualTo(
+                FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR);
+    }
+
+    @Test
+    public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
+                "false",
+                false);
+        Thread.sleep(300);
+
+        final String tag = "testEnqueueNotificationWithTag_FGSaddsNoClear";
+
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        waitForIdle();
+
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(PKG);
+        assertThat(notifs[0].getNotification().flags).isEqualTo(
+                FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR | FLAG_ONGOING_EVENT);
+    }
+
+    @Test
     public void testCancelNonexistentNotification() throws Exception {
         mBinderService.cancelNotificationWithTag(PKG, PKG,
                 "testCancelNonexistentNotification", 0, 0);
@@ -1757,51 +1882,6 @@
     }
 
     @Test
-    public void testCancelAllCancelNotificationsFromListener_NoClearFlag() throws Exception {
-        final NotificationRecord parent = generateNotificationRecord(
-                mTestNotificationChannel, 1, "group", true);
-        final NotificationRecord child = generateNotificationRecord(
-                mTestNotificationChannel, 2, "group", false);
-        final NotificationRecord child2 = generateNotificationRecord(
-                mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        final NotificationRecord newGroup = generateNotificationRecord(
-                mTestNotificationChannel, 4, "group2", false);
-        mService.addNotification(parent);
-        mService.addNotification(child);
-        mService.addNotification(child2);
-        mService.addNotification(newGroup);
-        mService.getBinderService().cancelNotificationsFromListener(null, null);
-        waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
-        assertEquals(1, notifs.length);
-    }
-
-    @Test
-    public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
-        final NotificationRecord parent = generateNotificationRecord(
-                mTestNotificationChannel, 1, "group", true);
-        final NotificationRecord child = generateNotificationRecord(
-                mTestNotificationChannel, 2, "group", false);
-        final NotificationRecord child2 = generateNotificationRecord(
-                mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        final NotificationRecord newGroup = generateNotificationRecord(
-                mTestNotificationChannel, 4, "group2", false);
-        mService.addNotification(parent);
-        mService.addNotification(child);
-        mService.addNotification(child2);
-        mService.addNotification(newGroup);
-        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
-                parent.getUserId());
-        waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
-        assertEquals(1, notifs.length);
-    }
-
-    @Test
     public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
         Notification n =
                 new Notification.Builder(mContext, mTestNotificationChannel.getId())
@@ -1838,7 +1918,268 @@
     }
 
     @Test
-    public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlag()
+    public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.getBinderService().cancelNotificationWithTag(
+                parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+                parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationWithTag_fromApp_cannotCancelFgsParent()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.getBinderService().cancelNotificationWithTag(
+                parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+                parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(3, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearChild()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.getBinderService().cancelNotificationWithTag(
+                parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+                parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearParent()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.getBinderService().cancelNotificationWithTag(
+                parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+                parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelAllNotificationsFromApp_cannotCancelFgsChild()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelAllNotifications(
+                parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelAllNotifications_fromApp_cannotCancelFgsParent()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelAllNotifications(
+                parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearChild()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelAllNotifications(
+                parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearParent()
+            throws Exception {
+        mService.isSystemUid = false;
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelAllNotifications(
+                parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_GroupWithOngoingParent()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_ONGOING_EVENT;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_GroupWithOngoingChild()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_GroupWithFgsParent()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_GroupWithFgsChild()
             throws Exception {
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -1861,7 +2202,171 @@
     }
 
     @Test
-    public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlagWithParameter()
+    public void testCancelNotificationsFromListener_clearAll_GroupWithNoClearParent()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_NO_CLEAR;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_GroupWithNoClearChild()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_NO_CLEAR;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_Ongoing()
+            throws Exception {
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+        mService.addNotification(child2);
+        String[] keys = {child2.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_NoClear()
+            throws Exception {
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        child2.getNotification().flags |= FLAG_NO_CLEAR;
+        mService.addNotification(child2);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_clearAll_Fgs()
+            throws Exception {
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mService.addNotification(child2);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_GroupWithOngoingParent()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_ONGOING_EVENT;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_GroupWithOngoingChild()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_GroupWithFgsParent()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_GroupWithFgsChild()
             throws Exception {
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -1882,26 +2387,28 @@
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
-        assertEquals(1, notifs.length);
+        assertEquals(0, notifs.length);
     }
 
     @Test
-    public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+    public void testCancelNotificationsFromListener_byKey_GroupWithNoClearParent()
+            throws Exception {
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
+        parent.getNotification().flags |= FLAG_NO_CLEAR;
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         final NotificationRecord newGroup = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group2", false);
         mService.addNotification(parent);
         mService.addNotification(child);
         mService.addNotification(child2);
         mService.addNotification(newGroup);
-        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
-                parent.getUserId());
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
@@ -1909,6 +2416,76 @@
     }
 
     @Test
+    public void testCancelNotificationsFromListener_byKey_GroupWithNoClearChild()
+            throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_NO_CLEAR;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_Ongoing()
+            throws Exception {
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+        mService.addNotification(child2);
+        String[] keys = {child2.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_NoClear()
+            throws Exception {
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        child2.getNotification().flags |= FLAG_NO_CLEAR;
+        mService.addNotification(child2);
+        String[] keys = {child2.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_byKey_Fgs()
+            throws Exception {
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mService.addNotification(child2);
+        String[] keys = {child2.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
     public void testGroupInstanceIds() throws Exception {
         final NotificationRecord group1 = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group1", true);
@@ -1973,7 +2550,7 @@
     }
 
     @Test
-    public void testCancelAllNotifications_CancelsNoClearFlagOnGoing() throws Exception {
+    public void testCancelAllNotificationsInt_CancelsNoClearFlagOnGoing() throws Exception {
         final NotificationRecord notif = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
@@ -1987,32 +2564,7 @@
     }
 
     @Test
-    public void testCancelAllCancelNotificationsFromListener_NoClearFlagWithParameter()
-            throws Exception {
-        final NotificationRecord parent = generateNotificationRecord(
-                mTestNotificationChannel, 1, "group", true);
-        final NotificationRecord child = generateNotificationRecord(
-                mTestNotificationChannel, 2, "group", false);
-        final NotificationRecord child2 = generateNotificationRecord(
-                mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        final NotificationRecord newGroup = generateNotificationRecord(
-                mTestNotificationChannel, 4, "group2", false);
-        mService.addNotification(parent);
-        mService.addNotification(child);
-        mService.addNotification(child2);
-        mService.addNotification(newGroup);
-        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
-                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
-        mService.getBinderService().cancelNotificationsFromListener(null, keys);
-        waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
-        assertEquals(0, notifs.length);
-    }
-
-    @Test
-    public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+    public void testAppInitiatedCancelAllNotifications_CancelsOngoingFlag() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -2026,7 +2578,7 @@
     }
 
     @Test
-    public void testCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+    public void testCancelAllNotificationsInt_CancelsOngoingFlag() throws Exception {
         final NotificationRecord notif = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
@@ -2040,69 +2592,7 @@
     }
 
     @Test
-    public void testUserInitiatedCancelAllOnClearAll_OnGoingFlag() throws Exception {
-        final NotificationRecord notif = generateNotificationRecord(
-                mTestNotificationChannel, 1, "group", true);
-        notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
-        mService.addNotification(notif);
-
-        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
-                notif.getUserId());
-        waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
-        assertEquals(1, notifs.length);
-    }
-
-    @Test
-    public void testCancelAllCancelNotificationsFromListener_OnGoingFlag() throws Exception {
-        final NotificationRecord parent = generateNotificationRecord(
-                mTestNotificationChannel, 1, "group", true);
-        final NotificationRecord child = generateNotificationRecord(
-                mTestNotificationChannel, 2, "group", false);
-        final NotificationRecord child2 = generateNotificationRecord(
-                mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
-        final NotificationRecord newGroup = generateNotificationRecord(
-                mTestNotificationChannel, 4, "group2", false);
-        mService.addNotification(parent);
-        mService.addNotification(child);
-        mService.addNotification(child2);
-        mService.addNotification(newGroup);
-        mService.getBinderService().cancelNotificationsFromListener(null, null);
-        waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
-        assertEquals(1, notifs.length);
-    }
-
-    @Test
-    public void testCancelAllCancelNotificationsFromListener_OnGoingFlagWithParameter()
-            throws Exception {
-        final NotificationRecord parent = generateNotificationRecord(
-                mTestNotificationChannel, 1, "group", true);
-        final NotificationRecord child = generateNotificationRecord(
-                mTestNotificationChannel, 2, "group", false);
-        final NotificationRecord child2 = generateNotificationRecord(
-                mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
-        final NotificationRecord newGroup = generateNotificationRecord(
-                mTestNotificationChannel, 4, "group2", false);
-        mService.addNotification(parent);
-        mService.addNotification(child);
-        mService.addNotification(child2);
-        mService.addNotification(newGroup);
-        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
-                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
-        mService.getBinderService().cancelNotificationsFromListener(null, keys);
-        waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
-        assertEquals(0, notifs.length);
-    }
-
-    @Test
-    public void testUserInitiatedCancelAllWithGroup_OnGoingFlag() throws Exception {
+    public void testUserInitiatedCancelAllWithGroup_OngoingFlag() throws Exception {
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -2125,6 +2615,52 @@
     }
 
     @Test
+    public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+                parent.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+        final NotificationRecord child2 = generateNotificationRecord(
+                mTestNotificationChannel, 3, "group", false);
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        final NotificationRecord newGroup = generateNotificationRecord(
+                mTestNotificationChannel, 4, "group2", false);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+                parent.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
     public void testTvExtenderChannelOverride_onTv() throws Exception {
         mService.setIsTelevision(true);
         mService.setPreferencesHelper(mPreferencesHelper);
@@ -2165,6 +2701,8 @@
 
         // should trigger a broadcast
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+        Thread.sleep(500);
+        waitForIdle();
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
 
@@ -2192,6 +2730,8 @@
 
         // should trigger a broadcast
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+        Thread.sleep(500);
+        waitForIdle();
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
 
@@ -2418,7 +2958,8 @@
                 .thenReturn(singletonList(mock(AssociationInfo.class)));
         NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
         mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
+        when(mPreferencesHelper.getNotificationChannelGroupWithChannels(
+                eq(PKG), anyInt(), eq(ncg.getId()), anyBoolean()))
                 .thenReturn(ncg);
         reset(mListeners);
         mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId());
@@ -2428,6 +2969,54 @@
     }
 
     @Test
+    public void testDeleteChannelGroupChecksForFgses() throws Exception {
+        when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+                .thenReturn(singletonList(mock(AssociationInfo.class)));
+        CountDownLatch latch = new CountDownLatch(2);
+        mService.createNotificationChannelGroup(
+                PKG, mUid, new NotificationChannelGroup("group", "group"), true, false);
+        new Thread(() -> {
+            NotificationChannel notificationChannel = new NotificationChannel("id", "id",
+                    NotificationManager.IMPORTANCE_HIGH);
+            notificationChannel.setGroup("group");
+            ParceledListSlice<NotificationChannel> pls =
+                    new ParceledListSlice(ImmutableList.of(notificationChannel));
+            try {
+                mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+            latch.countDown();
+        }).start();
+        new Thread(() -> {
+            try {
+                synchronized (this) {
+                    wait(5000);
+                }
+                mService.createNotificationChannelGroup(PKG, mUid,
+                        new NotificationChannelGroup("new", "new group"), true, false);
+                NotificationChannel notificationChannel =
+                        new NotificationChannel("id", "id", NotificationManager.IMPORTANCE_HIGH);
+                notificationChannel.setGroup("new");
+                ParceledListSlice<NotificationChannel> pls =
+                        new ParceledListSlice(ImmutableList.of(notificationChannel));
+                try {
+                mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls);
+                mBinderService.deleteNotificationChannelGroup(PKG, "group");
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            latch.countDown();
+        }).start();
+
+        latch.await();
+        verify(mAmi).hasForegroundServiceNotification(anyString(), anyInt(), anyString());
+    }
+
+    @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
@@ -3396,7 +3985,7 @@
                 mTestNotificationChannel.getId())
                 .setContentTitle("foo")
                 .setColorized(true).setColor(Color.WHITE)
-                .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
                 "testNoFakeColorizedPermission", mUid, 0,
@@ -6588,6 +7177,14 @@
     }
 
     @Test
+    public void testAreNotificationsEnabledForPackage_viaInternalService() throws Exception {
+        assertEquals(mInternalService.areNotificationsEnabledForPackage(
+                mContext.getPackageName(), mUid),
+                mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid));
+        verify(mPermissionHelper, never()).hasPermission(anyInt());
+    }
+
+    @Test
     public void testAreBubblesAllowedForPackage_crossUser() throws Exception {
         try {
             mBinderService.getBubblePreferenceForPackage(mContext.getPackageName(),
@@ -7469,17 +8066,6 @@
     }
 
     @Test
-    public void testOnBootPhase() {
-        mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
-
-        verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
-
-        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
-        verify(mHistoryManager, times(1)).onBootPhaseAppsCanStart();
-    }
-
-    @Test
     public void testHandleOnPackageChanged() {
         String[] pkgs = new String[] {PKG, PKG_N_MR1};
         int[] uids = new int[] {mUid, UserHandle.PER_USER_RANGE + 1};
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 0c8fe35..f6400b6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -536,6 +536,12 @@
     }
 
     @Test
+    public void testAreNotificationsEnabledForPackage_viaInternalService() {
+        mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid);
+        verify(mPermissionHelper).hasPermission(mUid);
+    }
+
+    @Test
     public void testGetPackageImportance() throws Exception {
         when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
         assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
@@ -595,6 +601,8 @@
         when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);
 
         mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+        Thread.sleep(500);
+        waitForIdle();
 
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -610,6 +618,8 @@
         when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
 
         mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+        Thread.sleep(500);
+        waitForIdle();
 
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index bd3ba04..fa294dd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -16,6 +16,7 @@
 package com.android.server.notification;
 
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -224,7 +225,30 @@
         verify(mPermManager).grantRuntimePermission(
                 "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10);
+                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED,
+                FLAG_PERMISSION_USER_SET, true, 10);
+    }
+
+    @Test
+    public void testSetNotificationPermission_grantReviewRequired() throws Exception {
+        mPermissionHelper.setNotificationPermission("pkg", 10, true, false, true);
+
+        verify(mPermManager).grantRuntimePermission(
+                "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
+        verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
+                FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10);
+    }
+
+    @Test
+    public void testSetNotificationPermission_pkgPerm_grantReviewRequired() throws Exception {
+        PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission(
+                "pkg", 10, true, false);
+        mPermissionHelper.setNotificationPermission(pkgPerm);
+
+        verify(mPermManager).grantRuntimePermission(
+                "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
+        verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
+                FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10);
     }
 
     @Test
@@ -234,7 +258,8 @@
         verify(mPermManager).revokeRuntimePermission(
                 eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10);
+                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED,
+                FLAG_PERMISSION_USER_SET, true, 10);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d49cf67..736fbd9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -46,6 +46,7 @@
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.UID_FIELD_NUMBER;
 import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
+import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT;
 import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -542,6 +543,7 @@
         mHelper.setInvalidMessageSent(PKG_P, UID_P);
         mHelper.setValidMessageSent(PKG_P, UID_P);
         mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
+        mHelper.setValidBubbleSent(PKG_P, UID_P);
 
         mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
 
@@ -561,6 +563,7 @@
         assertFalse(mHelper.hasSentInvalidMsg(PKG_N_MR1, UID_N_MR1));
         assertTrue(mHelper.hasSentValidMsg(PKG_P, UID_P));
         assertTrue(mHelper.didUserEverDemoteInvalidMsgApp(PKG_P, UID_P));
+        assertTrue(mHelper.hasSentValidBubble(PKG_P, UID_P));
         assertEquals(channel1,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         compareChannels(channel2,
@@ -3174,6 +3177,19 @@
     }
 
     @Test
+    public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        group.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+
+        NotificationChannelGroup group3 = group.clone();
+        group3.setBlocked(false);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true);
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+    }
+
+    @Test
     public void testIsGroup_appCannotResetBlock() throws Exception {
         NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
@@ -4235,6 +4251,52 @@
     }
 
     @Test
+    public void testTooManyGroups() {
+        for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) {
+            NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i),
+                    String.valueOf(i));
+            mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+        }
+        try {
+            NotificationChannelGroup group = new NotificationChannelGroup(
+                    String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT),
+                    String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT));
+            mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+            fail("Allowed to create too many notification channel groups");
+        } catch (IllegalStateException e) {
+            // great
+        }
+    }
+
+    @Test
+    public void testTooManyGroups_xml() throws Exception {
+        String extraGroup = "EXTRA";
+        String extraGroup1 = "EXTRA1";
+
+        // create first... many... directly so we don't need a big xml blob in this test
+        for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) {
+            NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i),
+                    String.valueOf(i));
+            mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+        }
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channelGroup id=\"" + extraGroup + "\" name=\"hi\"/>"
+                + "<channelGroup id=\"" + extraGroup1 + "\" name=\"hi2\"/>"
+                + "</package>"
+                + "</ranking>";
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNull(mHelper.getNotificationChannelGroup(extraGroup, PKG_O, UID_O));
+        assertNull(mHelper.getNotificationChannelGroup(extraGroup1, PKG_O, UID_O));
+    }
+
+    @Test
     public void testRestoreMultiUser() throws Exception {
         String pkg = "restore_pkg";
         String channelId = "channelId";
@@ -4706,7 +4768,7 @@
     public void testGetConversations_noDisabledGroups() {
         NotificationChannelGroup group = new NotificationChannelGroup("a", "a");
         group.setBlocked(true);
-        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false);
         NotificationChannel parent = new NotificationChannel("parent", "p", 1);
         mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
 
@@ -4927,6 +4989,18 @@
     }
 
     @Test
+    public void testValidBubbleSent() {
+        // create package preferences
+        mHelper.canShowBadge(PKG_P, UID_P);
+        // false by default
+        assertFalse(mHelper.hasSentValidBubble(PKG_P, UID_P));
+
+        // set something valid was sent
+        mHelper.setValidBubbleSent(PKG_P, UID_P);
+        assertTrue(mHelper.hasSentValidBubble(PKG_P, UID_P));
+    }
+
+    @Test
     public void testPullPackageChannelPreferencesStats() {
         String channelId = "parent";
         String name = "messages";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
index c77a474..f135d16 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
@@ -40,7 +40,10 @@
 @RunWith(AndroidJUnit4.class)
 public class VibratorHelperTest extends UiServiceTestCase {
 
+    // OFF/ON vibration pattern
     private static final long[] CUSTOM_PATTERN = new long[] { 100, 200, 300, 400 };
+    // (amplitude, frequency, duration) triples list
+    private static final float[] PWLE_PATTERN = new float[] { 1, 120, 100 };
 
     @Mock private Vibrator mVibrator;
 
@@ -58,12 +61,16 @@
     public void createWaveformVibration_insistent_createsRepeatingVibration() {
         assertRepeatingVibration(
                 VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ true));
+        assertRepeatingVibration(
+                VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ true));
     }
 
     @Test
     public void createWaveformVibration_nonInsistent_createsSingleShotVibration() {
         assertSingleVibration(
                 VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ false));
+        assertSingleVibration(
+                VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ false));
     }
 
     @Test
@@ -71,6 +78,11 @@
         assertNull(VibratorHelper.createWaveformVibration(null, false));
         assertNull(VibratorHelper.createWaveformVibration(new long[0], false));
         assertNull(VibratorHelper.createWaveformVibration(new long[] { 0, 0 }, false));
+
+        assertNull(VibratorHelper.createPwleWaveformVibration(null, false));
+        assertNull(VibratorHelper.createPwleWaveformVibration(new float[0], false));
+        assertNull(VibratorHelper.createPwleWaveformVibration(new float[] { 0 }, false));
+        assertNull(VibratorHelper.createPwleWaveformVibration(new float[] { 0, 0, 0 }, false));
     }
 
     @Test
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 8a057f7..774e5b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -24,7 +24,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -103,6 +102,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 
@@ -136,6 +136,7 @@
 import android.view.IWindowSession;
 import android.view.InsetsSource;
 import android.view.InsetsState;
+import android.view.InsetsVisibilities;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.Surface;
@@ -152,6 +153,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
 
 import java.util.ArrayList;
@@ -568,7 +570,7 @@
         final ActivityRecord activity = createActivityWith2LevelTask();
         final Task task = activity.getTask();
         final Task rootTask = activity.getRootTask();
-        rootTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         final Rect stableRect = new Rect();
         rootTask.mDisplayContent.getStableRect(stableRect);
 
@@ -613,7 +615,7 @@
         spyOn(tda);
         doReturn(true).when(tda).supportsNonResizableMultiWindow();
         final Task rootTask = mDisplayContent.getDefaultTaskDisplayArea().createRootTask(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         rootTask.setBounds(0, 0, 1000, 500);
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setParentTask(rootTask)
@@ -3065,6 +3067,50 @@
         assertEquals(state.getSource(ITYPE_IME).getFrame(), imeSource.getFrame());
     }
 
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
+    @Test
+    public void testImeInsetsFrozenFlag_noDispatchVisibleInsetsWhenAppNotRequest()
+            throws RemoteException {
+        final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
+        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+                mImeWindow, null, null);
+        mImeWindow.getControllableInsetProvider().setServerVisible(true);
+
+        // Simulate app2 is closing and let app1 is visible to be IME targets.
+        makeWindowVisibleAndDrawn(app1, mImeWindow);
+        mDisplayContent.setImeLayeringTarget(app1);
+        mDisplayContent.updateImeInputAndControlTarget(app1);
+        app2.mActivityRecord.commitVisibility(false, false);
+
+        // app1 requests IME visible.
+        final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+        requestedVisibilities.setVisibility(ITYPE_IME, true);
+        app1.setRequestedVisibilities(requestedVisibilities);
+        mDisplayContent.getInsetsStateController().onInsetsModified(app1);
+
+        // Verify app1's IME insets is visible and app2's IME insets frozen flag set.
+        assertTrue(app1.getInsetsState().peekSource(ITYPE_IME).isVisible());
+        assertTrue(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Simulate switching to app2 to make it visible to be IME targets.
+        makeWindowVisibleAndDrawn(app2);
+        spyOn(app2);
+        spyOn(app2.mClient);
+        ArgumentCaptor<InsetsState> insetsStateCaptor = ArgumentCaptor.forClass(InsetsState.class);
+        doReturn(true).when(app2).isReadyToDispatchInsetsState();
+        mDisplayContent.setImeLayeringTarget(app2);
+        mDisplayContent.updateImeInputAndControlTarget(app2);
+
+        // 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);
+        verify(app2.mClient, atLeastOnce()).insetsChanged(insetsStateCaptor.capture(), anyBoolean(),
+                anyBoolean());
+        assertFalse(insetsStateCaptor.getAllValues().get(0).peekSource(ITYPE_IME).isVisible());
+    }
+
     @Test
     public void testInClosingAnimation_doNotHideSurface() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
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 e2f0658f..fdc8982 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -31,7 +31,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -51,7 +50,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -457,7 +455,7 @@
         final ActivityRecord splitSecondReusableActivity = activities.second;
         final ActivityRecord splitSecondTopActivity = new ActivityBuilder(mAtm).setCreateTask(true)
                 .setParentTask(splitSecondReusableActivity.getRootTask()).build();
-        assertTrue(splitSecondTopActivity.inSplitScreenSecondaryWindowingMode());
+        assertTrue(splitSecondTopActivity.inMultiWindowMode());
 
         // Let primary stack has focus.
         splitPrimaryFocusActivity.moveFocusableActivityToTop("testSplitScreenTaskToFront");
@@ -476,13 +474,16 @@
         final TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm);
         // The fullscreen windowing mode activity will be moved to split-secondary by
         // TestSplitOrganizer when a split-primary task appears.
-        final ActivityRecord splitSecondActivity =
-                new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor)
                 .setParentTaskFragment(splitOrg.mPrimary)
                 .setCreateActivity(true)
                 .build()
                 .getTopMostActivity();
+        final ActivityRecord splitSecondActivity = new TaskBuilder(mSupervisor)
+                .setParentTaskFragment(splitOrg.mSecondary)
+                .setCreateActivity(true)
+                .build()
+                .getTopMostActivity();
         splitPrimaryActivity.mVisibleRequested = splitSecondActivity.mVisibleRequested = true;
 
         assertEquals(splitOrg.mPrimary, splitPrimaryActivity.getRootTask());
@@ -1090,7 +1091,7 @@
         starter.setActivityOptions(options.toBundle())
                 .setReason("testWindowingModeOptionsLaunchAdjacent")
                 .setOutActivity(outActivity).execute();
-        assertThat(outActivity[0].inSplitScreenSecondaryWindowingMode()).isTrue();
+        assertThat(outActivity[0].inMultiWindowMode()).isTrue();
     }
 
     @Test
@@ -1108,50 +1109,6 @@
     }
 
     @Test
-    public void testStartActivityInner_allSplitScreenPrimaryActivitiesVisible() {
-        // Given
-        final ActivityStarter starter = prepareStarter(0, false);
-
-        starter.setReason("testAllSplitScreenPrimaryActivitiesAreResumed");
-
-        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
-        targetRecord.setFocusable(false);
-        targetRecord.setVisibility(false);
-        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).build();
-
-        final Task stack = spy(
-                mRootWindowContainer.getDefaultTaskDisplayArea()
-                        .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                                /* onTop */true));
-
-        stack.addChild(targetRecord);
-
-        doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(), any(),
-                anyBoolean(), any(), anyInt(), anyInt(), anyInt());
-
-        starter.mStartActivity = new ActivityBuilder(mAtm).build();
-
-        // When
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ null,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
-
-        // Then
-        verify(stack).ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-        verify(targetRecord).makeVisibleIfNeeded(null, true);
-        assertTrue(targetRecord.mVisibleRequested);
-    }
-
-    @Test
     public void testStartActivityInner_inTaskFragment() {
         final ActivityStarter starter = prepareStarter(0, false);
         final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index a2b04c2..7c340ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -77,6 +77,7 @@
 import org.mockito.MockitoSession;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -188,6 +189,9 @@
 
             @Override
             public void onFixedRotationFinished(int displayId) {}
+
+            @Override
+            public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
         };
         int[] displayIds = mAtm.mWindowManager.registerDisplayWindowListener(listener);
         for (int i = 0; i < displayIds.length; i++) {
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 5062706..0d67946 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -678,6 +678,66 @@
                         opening, closing, false /* visible */));
     }
 
+    @Test
+    public void testGetAnimationTargets_embeddedTask() {
+        // [DisplayContent] -+- [Task1] -            [ActivityRecord1] (opening, invisible)
+        //                   +- [Task2] (embedded) - [ActivityRecord2] (opening, invisible)
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final Task task2 = createTask(mDisplayContent);
+        task2.mRemoveWithTaskOrganizer = true;
+        final ActivityRecord activity2 = createActivityRecord(task2);
+        activity2.setVisible(false);
+        activity2.mVisibleRequested = true;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        opening.add(activity2);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+
+        // No animation on the embedded task.
+        assertEquals(
+                new ArraySet<>(new WindowContainer[]{activity1.getTask()}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(
+                new ArraySet<>(),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+
+    @Test
+    public void testGetAnimationTargets_activityInEmbeddedTask() {
+        // [DisplayContent] - [Task] (embedded)-+- [ActivityRecord1] (opening, invisible)
+        //                                      +- [ActivityRecord2] (closing, visible)
+        final Task task = createTask(mDisplayContent);
+        task.mRemoveWithTaskOrganizer = true;
+
+        final ActivityRecord activity1 = createActivityRecord(task);
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+        final ActivityRecord activity2 = createActivityRecord(task);
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Even though embedded task itself doesn't animate, activities in an embedded task
+        // animate.
+        assertEquals(
+                new ArraySet<>(new WindowContainer[]{activity1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(
+                new ArraySet<>(new WindowContainer[]{activity2}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
     static class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         @Override
         public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
new file mode 100644
index 0000000..687779d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.window.BackNavigationInfo.typeToString;
+
+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.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.hardware.HardwareBuffer;
+import android.platform.test.annotations.Presubmit;
+import android.window.BackNavigationInfo;
+import android.window.TaskSnapshot;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class BackNavigationControllerTests extends WindowTestsBase {
+
+    private BackNavigationController mBackNavigationController;
+
+    @Before
+    public void setUp() throws Exception {
+        mBackNavigationController = new BackNavigationController();
+    }
+
+    @Test
+    public void backTypeHomeWhenBackToLauncher() {
+        Task task = createTopTaskWithActivity();
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
+    }
+
+    @Test
+    public void backTypeCrossTaskWhenBackToPreviousTask() {
+        Task taskA = createTask(mDefaultDisplay);
+        createActivityRecord(taskA);
+        Task task = createTopTaskWithActivity();
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK));
+    }
+
+    @Test
+    public void backTypeCrossActivityWhenBackToPreviousActivity() {
+        Task task = createTopTaskWithActivity();
+        mAtm.setFocusedTask(task.mTaskId, createActivityRecord(task));
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
+    }
+
+    /**
+     * Checks that we are able to fill all the field of the {@link BackNavigationInfo} object.
+     */
+    @Test
+    public void backNavInfoFullyPopulated() {
+        Task task = createTopTaskWithActivity();
+        createActivityRecord(task);
+
+        // We need a mock screenshot so
+        TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
+
+        mBackNavigationController.setTaskSnapshotController(taskSnapshotController);
+
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(backNavigationInfo.getDepartingWindowContainer()).isNotNull();
+        assertThat(backNavigationInfo.getScreenshotSurface()).isNotNull();
+        assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNotNull();
+        assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
+    }
+
+    @NonNull
+    private TaskSnapshotController createMockTaskSnapshotController() {
+        TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class);
+        TaskSnapshot taskSnapshot = mock(TaskSnapshot.class);
+        when(taskSnapshot.getHardwareBuffer()).thenReturn(mock(HardwareBuffer.class));
+        when(taskSnapshotController.getSnapshot(anyInt(), anyInt(), anyBoolean(), anyBoolean()))
+                .thenReturn(taskSnapshot);
+        return taskSnapshotController;
+    }
+
+    @NonNull
+    private Task createTopTaskWithActivity() {
+        Task task = createTask(mDefaultDisplay);
+        ActivityRecord record = createActivityRecord(task);
+        when(record.mSurfaceControl.isValid()).thenReturn(true);
+        mAtm.setFocusedTask(task.mTaskId, record);
+        return task;
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 525888d..22e411e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -30,7 +31,6 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
@@ -49,12 +49,14 @@
 
 import static java.util.stream.Collectors.toList;
 
+import android.app.ActivityOptions;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
 
 import com.google.android.collect.Lists;
 
@@ -81,6 +83,8 @@
  */
 @Presubmit
 public class DisplayAreaPolicyBuilderTest {
+    private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID =
+            "android.test.launchTaskDisplayAreaFeatureId";
 
     @Rule
     public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
@@ -104,6 +108,7 @@
         mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer");
         mDisplayContent = mock(DisplayContent.class);
         doReturn(true).when(mDisplayContent).isTrusted();
+        doReturn(DEFAULT_DISPLAY).when(mDisplayContent).getDisplayId();
         mDisplayContent.isDefaultDisplay = true;
         mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks",
                 FEATURE_DEFAULT_TASK_CONTAINER);
@@ -197,25 +202,6 @@
     }
 
     @Test
-    public void testBuilder_defaultPolicy_hasOneHandedBackgroundFeature() {
-        final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
-                resourcesWithProvider(""));
-        final DisplayAreaPolicyBuilder.Result defaultPolicy =
-                (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
-                        mRoot, mImeContainer);
-        if (mDisplayContent.isDefaultDisplay) {
-            final List<Feature> features = defaultPolicy.getFeatures();
-            boolean hasOneHandedBackgroundFeature = false;
-            for (Feature feature : features) {
-                hasOneHandedBackgroundFeature |=
-                        feature.getId() == FEATURE_ONE_HANDED_BACKGROUND_PANEL;
-            }
-
-            assertThat(hasOneHandedBackgroundFeature).isTrue();
-        }
-    }
-
-    @Test
     public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() {
         final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
                 resourcesWithProvider(""));
@@ -731,6 +717,208 @@
                 .build();
     }
 
+    @Test
+    public void testGetTaskDisplayArea_DefaultFunction_NullOptions_ReturnsDefaultTda() {
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .setRootHierarchy(hierarchy0)
+                .addDisplayAreaGroupHierarchy(hierarchy1)
+                .addDisplayAreaGroupHierarchy(hierarchy2)
+                .build(mWms);
+
+        final TaskDisplayArea tda = policy.getTaskDisplayArea(null /* options */);
+
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
+    }
+
+    @Test
+    public void testGetTaskDisplayArea_DefaultFunction_NotContainsLunchedTda_ReturnsDefaultTda() {
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .setRootHierarchy(hierarchy0)
+                .addDisplayAreaGroupHierarchy(hierarchy1)
+                .addDisplayAreaGroupHierarchy(hierarchy2)
+                .build(mWms);
+
+        final TaskDisplayArea tda = policy.getTaskDisplayArea(new Bundle());
+
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
+    }
+
+    @Test
+    public void testGetTaskDisplayArea_DefaultFunction_InvalidTdaToken_ReturnsDefaultTda() {
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .setRootHierarchy(hierarchy0)
+                .addDisplayAreaGroupHierarchy(hierarchy1)
+                .addDisplayAreaGroupHierarchy(hierarchy2)
+                .build(mWms);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        final WindowContainerToken fakeToken = mRoot.mRemoteToken.toWindowContainerToken();
+        options.setLaunchTaskDisplayArea(fakeToken);
+
+        final TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());
+
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetTaskDisplayArea_DefaultFunction_TdaOnDifferentDisplay_ThrowException() {
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .setRootHierarchy(hierarchy0)
+                .addDisplayAreaGroupHierarchy(hierarchy1)
+                .addDisplayAreaGroupHierarchy(hierarchy2)
+                .build(mWms);
+        final TaskDisplayArea tdaOnSecondaryDisplay = mock(TaskDisplayArea.class);
+        doReturn(DEFAULT_DISPLAY + 1).when(tdaOnSecondaryDisplay).getDisplayId();
+        doReturn(tdaOnSecondaryDisplay).when(tdaOnSecondaryDisplay).asTaskDisplayArea();
+        tdaOnSecondaryDisplay.mRemoteToken = new WindowContainer.RemoteToken(tdaOnSecondaryDisplay);
+
+        final WindowContainerToken tdaToken = tdaOnSecondaryDisplay.mRemoteToken
+                .toWindowContainerToken();
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskDisplayArea(tdaToken);
+        final TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());
+
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
+    }
+
+    @Test
+    public void testGetTaskDisplayArea_DefaultFunction_ContainsTdaToken_ReturnsTda() {
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .setRootHierarchy(hierarchy0)
+                .addDisplayAreaGroupHierarchy(hierarchy1)
+                .addDisplayAreaGroupHierarchy(hierarchy2)
+                .build(mWms);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+
+        final WindowContainerToken defaultTdaToken = mDefaultTaskDisplayArea.mRemoteToken
+                .toWindowContainerToken();
+        options.setLaunchTaskDisplayArea(defaultTdaToken);
+        TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());
+
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
+
+        final WindowContainerToken tda1Token = mTda1.mRemoteToken.toWindowContainerToken();
+        options.setLaunchTaskDisplayArea(tda1Token);
+        tda = policy.getTaskDisplayArea(options.toBundle());
+
+        assertThat(tda).isEqualTo(mTda1);
+
+        final WindowContainerToken tda2Token = mTda2.mRemoteToken.toWindowContainerToken();
+        options.setLaunchTaskDisplayArea(tda2Token);
+        tda = policy.getTaskDisplayArea(options.toBundle());
+
+        assertThat(tda).isEqualTo(mTda2);
+    }
+
+    @Test
+    public void testBuilder_getTaskDisplayArea_setSelectTaskDisplayAreaFunc() {
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .setRootHierarchy(hierarchy0)
+                .setSelectTaskDisplayAreaFunc((options) -> {
+                    if (options == null) {
+                        return mDefaultTaskDisplayArea;
+                    }
+                    final int tdaFeatureId =
+                            options.getInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID);
+                    if (tdaFeatureId == mTda1.mFeatureId) {
+                        return mTda1;
+                    }
+                    if (tdaFeatureId == mTda2.mFeatureId) {
+                        return mTda2;
+                    }
+                    return mDefaultTaskDisplayArea;
+                })
+                .addDisplayAreaGroupHierarchy(hierarchy1)
+                .addDisplayAreaGroupHierarchy(hierarchy2)
+                .build(mWms);
+
+        TaskDisplayArea tda = policy.getTaskDisplayArea(null /* options */);
+
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
+
+        final Bundle options = new Bundle();
+        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, -1);
+        tda = policy.getTaskDisplayArea(options);
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+
+        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mDefaultTaskDisplayArea.mFeatureId);
+        tda = policy.getTaskDisplayArea(options);
+        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
+
+        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mTda1.mFeatureId);
+        tda = policy.getTaskDisplayArea(options);
+        assertThat(tda).isEqualTo(mTda1);
+
+        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mTda2.mFeatureId);
+        tda = policy.getTaskDisplayArea(options);
+        assertThat(tda).isEqualTo(mTda2);
+    }
+
     private static Resources resourcesWithProvider(String provider) {
         Resources mock = mock(Resources.class);
         when(mock.getString(
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 2ef59f6..8d58ec0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -119,6 +119,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
@@ -1101,7 +1102,7 @@
         final DisplayContent dc = createNewDisplay();
         dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app"));
         dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode(
-                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+                WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
         assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
     }
 
@@ -1152,7 +1153,7 @@
         final DisplayContent dc = createNewDisplay();
         dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
         dc.getImeTarget(IME_TARGET_INPUT).getWindow().setWindowingMode(
-                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+                WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
         dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
         dc.setRemoteInsetsController(createDisplayWindowInsetsController());
         assertNotEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(),
@@ -1718,6 +1719,7 @@
     @Test
     public void testShellTransitRotation() {
         DisplayContent dc = createNewDisplay();
+        dc.setLastHasContent();
 
         final TestTransitionPlayer testPlayer = registerTestTransitionPlayer();
         final DisplayRotation dr = dc.getDisplayRotation();
@@ -1981,6 +1983,7 @@
         // Test step 1: appWin1 is the current IME target and soft-keyboard is visible.
         mDisplayContent.computeImeTarget(true);
         assertEquals(appWin1, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
+        mDisplayContent.setImeInputTarget(appWin1);
         spyOn(mDisplayContent.mInputMethodWindow);
         doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
         mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
@@ -1997,7 +2000,6 @@
         // be shown at this time.
         final Transaction t = mDisplayContent.getPendingTransaction();
         spyOn(t);
-        mDisplayContent.setImeInputTarget(appWin2);
         mDisplayContent.computeImeTarget(true);
         assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
         assertTrue(mDisplayContent.shouldImeAttachedToApp());
@@ -2435,6 +2437,31 @@
         mockSession.finishMocking();
     }
 
+    @Test
+    public void testKeepClearAreasMultipleWindows() {
+        final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
+        final Rect rect1 = new Rect(0, 0, 10, 10);
+        w1.setKeepClearAreas(Arrays.asList(rect1));
+        final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
+        final Rect rect2 = new Rect(10, 10, 20, 20);
+        w2.setKeepClearAreas(Arrays.asList(rect2));
+
+        // No keep clear areas on display, because the windows are not visible
+        assertEquals(Arrays.asList(), mDisplayContent.getKeepClearAreas());
+
+        makeWindowVisible(w1);
+
+        // The returned keep-clear areas contain the areas just from the visible window
+        assertEquals(new ArraySet(Arrays.asList(rect1)),
+                     new ArraySet(mDisplayContent.getKeepClearAreas()));
+
+        makeWindowVisible(w1, w2);
+
+        // The returned keep-clear areas contain the areas from all visible windows
+        assertEquals(new ArraySet(Arrays.asList(rect1, rect2)),
+                     new ArraySet(mDisplayContent.getKeepClearAreas()));
+    }
+
     private class TestToken extends Binder {
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index acf6dc5..497ae1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -31,10 +31,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
-import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
@@ -44,7 +42,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
-import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.InsetsState;
 import android.view.PrivacyIndicatorBounds;
@@ -210,24 +207,6 @@
         expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
     }
 
-    @Test
-    public void layoutHint_appWindow() {
-        mWindow.mAttrs.setFitInsetsTypes(0);
-
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-        final InsetsState outState = new InsetsState();
-
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
-                true /* localClient */);
-
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-        assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
-                is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
-        assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
-                is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
-    }
-
     /**
      * Verify that {@link DisplayPolicy#simulateLayoutDisplay} outputs the same display frames as
      * the real one.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 6970005..2b131e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -322,4 +322,26 @@
         assertFalse(navBarSource.getFrame().isEmpty());
         assertTrue(imeSource.getFrame().contains(navBarSource.getFrame()));
     }
+
+    @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
+    @Test
+    public void testInsetsGivenContentFrame() {
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 1000;
+        displayInfo.logicalHeight = 2000;
+        displayInfo.rotation = ROTATION_0;
+
+        WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
+        displayPolicy.addWindowLw(mNavBarWindow, attrs);
+        mNavBarWindow.setRequestedSize(attrs.width, attrs.height);
+        mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
+
+        mNavBarWindow.mGivenContentInsets.set(0, 10, 0, 0);
+
+        displayPolicy.layoutWindowLw(mNavBarWindow, null, mDisplayContent.mDisplayFrames);
+        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        final InsetsSource navBarSource = state.peekSource(ITYPE_NAVIGATION_BAR);
+        assertEquals(attrs.height - 10, navBarSource.getFrame().height());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
new file mode 100644
index 0000000..6e11d8c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.view.Display;
+import android.window.DisplayWindowPolicyController;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Tests for the {@link DisplayWindowPolicyControllerHelper} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:DisplayWindowPolicyControllerHelperTests
+ */
+@RunWith(WindowTestRunner.class)
+public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase {
+    private static final int TEST_USER_0_ID = 0;
+    private static final int TEST_USER_1_ID = 10;
+
+    private TestDisplayWindowPolicyController mDwpc = new TestDisplayWindowPolicyController();
+    private DisplayContent mSecondaryDisplay;
+
+    @Before
+    public void setUp() {
+        doReturn(mDwpc).when(mWm.mDisplayManagerInternal)
+                .getDisplayWindowPolicyController(anyInt());
+        mSecondaryDisplay = createNewDisplay();
+        assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
+        assertTrue(mSecondaryDisplay.mDwpcHelper.hasController());
+    }
+
+    @Test
+    public void testOnRunningActivityChanged() {
+        final ActivityRecord activity1 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_0_ID);
+        verifyTopActivityAndRunningUid(activity1,
+                true /* expectedUid0 */, false /* expectedUid1 */);
+        final ActivityRecord activity2 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_1_ID);
+        verifyTopActivityAndRunningUid(activity2,
+                true /* expectedUid0 */, true /* expectedUid1 */);
+        final ActivityRecord activity3 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_0_ID);
+        verifyTopActivityAndRunningUid(activity3,
+                true /* expectedUid0 */, true /* expectedUid1 */);
+
+        activity3.finishing = true;
+        verifyTopActivityAndRunningUid(activity2,
+                true /* expectedUid0 */, true /* expectedUid1 */);
+
+        activity2.finishing = true;
+        verifyTopActivityAndRunningUid(activity1,
+                true /* expectedUid0 */, false /* expectedUid1 */);
+
+        activity1.finishing = true;
+        verifyTopActivityAndRunningUid(null /* expectedTopActivity */,
+                false /* expectedUid0 */, false /* expectedUid1 */);
+    }
+
+    private void verifyTopActivityAndRunningUid(ActivityRecord expectedTopActivity,
+            boolean expectedUid0, boolean expectedUid1) {
+        mSecondaryDisplay.onRunningActivityChanged();
+        int uidAmount = (expectedUid0 && expectedUid1) ? 2 : (expectedUid0 || expectedUid1) ? 1 : 0;
+        assertEquals(expectedTopActivity == null ? null :
+                expectedTopActivity.info.getComponentName(), mDwpc.mTopActivity);
+        assertEquals(expectedTopActivity == null ? UserHandle.USER_NULL :
+                expectedTopActivity.info.applicationInfo.uid, mDwpc.mTopActivityUid);
+        assertEquals(uidAmount, mDwpc.mRunningUids.size());
+        assertTrue(mDwpc.mRunningUids.contains(TEST_USER_0_ID) == expectedUid0);
+        assertTrue(mDwpc.mRunningUids.contains(TEST_USER_1_ID) == expectedUid1);
+
+    }
+
+    private ActivityRecord launchActivityOnDisplay(DisplayContent display, int uid) {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(display)
+                .setUserId(uid)
+                .build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .setUid(uid)
+                .setOnTop(true)
+                .build();
+        return activity;
+    }
+
+    private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController {
+
+        ComponentName mTopActivity = null;
+        int mTopActivityUid = UserHandle.USER_NULL;
+        ArraySet<Integer> mRunningUids = new ArraySet<>();
+
+        @Override
+        public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+            return false;
+        }
+
+        @Override
+        public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
+                int systemWindowFlags) {
+            return false;
+        }
+
+        @Override
+        public void onTopActivityChanged(ComponentName topActivity, int uid) {
+            super.onTopActivityChanged(topActivity, uid);
+            mTopActivity = topActivity;
+            mTopActivityUid = uid;
+        }
+
+        @Override
+        public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
+            super.onRunningAppsChanged(runningUids);
+            mRunningUids.clear();
+            mRunningUids.addAll(runningUids);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 9e4cd16..365e749d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -51,6 +51,7 @@
 
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -447,6 +448,21 @@
         assertEquals(456, config.densityDpi);
     }
 
+    @Test
+    public void testDisplayRotationSettingsAppliedOnCreation() {
+        // Create new displays with different rotation settings
+        final SettingsEntry settingsEntry1 = new SettingsEntry();
+        settingsEntry1.mIgnoreOrientationRequest = false;
+        final DisplayContent dcDontIgnoreOrientation = createMockSimulatedDisplay(settingsEntry1);
+        final SettingsEntry settingsEntry2 = new SettingsEntry();
+        settingsEntry2.mIgnoreOrientationRequest = true;
+        final DisplayContent dcIgnoreOrientation = createMockSimulatedDisplay(settingsEntry2);
+
+        // Verify that newly created displays are created with correct rotation settings
+        assertFalse(dcDontIgnoreOrientation.getIgnoreOrientationRequest());
+        assertTrue(dcIgnoreOrientation.getIgnoreOrientationRequest());
+    }
+
     public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider {
         Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index 407f9cf..d64bf12 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -26,11 +26,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
 
 import android.app.ActivityThread;
 import android.content.Context;
@@ -43,6 +46,7 @@
 import android.view.IWindowManager;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
 
 import com.android.server.inputmethod.InputMethodManagerService;
 import com.android.server.inputmethod.InputMethodMenuController;
@@ -130,15 +134,31 @@
     @Test
     public void testGetSettingsContextOnDualDisplayContent() {
         final Context context = mController.getSettingsContext(mSecondaryDisplay.getDisplayId());
+        final WindowTokenClient tokenClient = (WindowTokenClient) context.getWindowContextToken();
+        assertNotNull(tokenClient);
+        spyOn(tokenClient);
 
         final DisplayArea.Tokens imeContainer = mSecondaryDisplay.getImeContainer();
+        spyOn(imeContainer);
         assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay);
 
         mSecondaryDisplay.mFirstRoot.placeImeContainer(imeContainer);
+
+        verify(imeContainer, atLeastOnce()).onConfigurationChanged(
+                eq(mSecondaryDisplay.mFirstRoot.getConfiguration()));
+        verify(tokenClient, atLeastOnce()).onConfigurationChanged(
+                eq(mSecondaryDisplay.mFirstRoot.getConfiguration()),
+                eq(mSecondaryDisplay.mDisplayId));
         assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mFirstRoot);
         assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay);
 
         mSecondaryDisplay.mSecondRoot.placeImeContainer(imeContainer);
+
+        verify(imeContainer, atLeastOnce()).onConfigurationChanged(
+                eq(mSecondaryDisplay.mSecondRoot.getConfiguration()));
+        verify(tokenClient, atLeastOnce()).onConfigurationChanged(
+                eq(mSecondaryDisplay.mSecondRoot.getConfiguration()),
+                eq(mSecondaryDisplay.mDisplayId));
         assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mSecondRoot);
         assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 07d467b..d3282b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -82,19 +81,6 @@
     }
 
     @Test
-    public void testControlsForDispatch_dockedTaskVisible() {
-        addWindow(TYPE_STATUS_BAR, "statusBar");
-        addWindow(TYPE_NAVIGATION_BAR, "navBar");
-
-        final WindowState win = createWindow(null, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
-        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
-
-        // The app must not control any system bars.
-        assertNull(controls);
-    }
-
-    @Test
     public void testControlsForDispatch_multiWindowTaskVisible() {
         addWindow(TYPE_STATUS_BAR, "statusBar");
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -291,7 +277,8 @@
         assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
                 .getSource(ITYPE_NAVIGATION_BAR).isVisible());
 
-        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+                true /* isGestureOnSystemBar */);
         waitUntilWindowAnimatorIdle();
         final InsetsSourceControl[] controls =
                 mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -319,7 +306,8 @@
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
         doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
-        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+                true /* isGestureOnSystemBar */);
         waitUntilWindowAnimatorIdle();
         final InsetsSourceControl[] controls =
                 mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -351,7 +339,8 @@
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
         doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
-        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+                true /* isGestureOnSystemBar */);
         waitUntilWindowAnimatorIdle();
         InsetsSourceControl[] controls =
                 mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -402,7 +391,8 @@
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
         doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
-        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+        policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+                true /* isGestureOnSystemBar */);
         final InsetsSourceControl[] controls =
                 mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
         policy.updateBarControlTarget(app2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index f8c84df..2eece4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
@@ -246,7 +245,7 @@
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
         app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
         child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-        child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
 
         mDisplayContent.computeImeTarget(true);
         mDisplayContent.setLayoutNeeded();
@@ -332,7 +331,8 @@
         assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
 
         provider.getSource().setVisible(false);
-        mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR });
+        mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR },
+                true /* isGestureOnSystemBar */);
 
         assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR));
         assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index fc298b0..0c2de5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -20,7 +20,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -334,7 +333,7 @@
         params.mWindowingMode = windowingMode;
         final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
         final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setCreateParentTask(true).build();
-        task.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
 
         mController.registerModifier(positioner);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index b4c449a..632a59d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -117,8 +117,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
                 mFinishedCallback);
 
-        // Remove the app window so that the animation target can not be created
-        activity.removeImmediately();
+        // The activity doesn't contain window so the animation target cannot be created.
         mController.startAnimation();
 
         // Verify that the finish callback to reparent the leash is called
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 bb49cd2..65b5cf5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -87,6 +88,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -240,7 +242,7 @@
         final WindowConfiguration windowConfiguration =
                 task.getResolvedOverrideConfiguration().windowConfiguration;
         spyOn(windowConfiguration);
-        doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+        doReturn(WINDOWING_MODE_MULTI_WINDOW)
                 .when(windowConfiguration).getWindowingMode();
 
         // Prevent adjust task dimensions
@@ -323,72 +325,12 @@
     }
 
     @Test
-    public void testPrimarySplitScreenMoveToBack() {
-        TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
-        // We're testing an edge case here where we have primary + fullscreen rather than secondary.
-        organizer.setMoveToSecondaryOnEnter(false);
-
-        // Create primary splitscreen root task.
-        final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTaskFragment(organizer.mPrimary)
-                .setOnTop(true)
-                .build();
-
-        // Assert windowing mode.
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode());
-
-        // Move primary to back.
-        primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
-                null /* task */);
-
-        // Assert that root task is at the bottom.
-        assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primarySplitScreen));
-
-        // Ensure no longer in splitscreen.
-        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
-
-        // Ensure that the override mode is restored to undefined
-        assertEquals(WINDOWING_MODE_UNDEFINED,
-                primarySplitScreen.getRequestedOverrideWindowingMode());
-    }
-
-    @Test
-    public void testMoveToPrimarySplitScreenThenMoveToBack() {
-        TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
-        // This time, start with a fullscreen activity root task.
-        final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        primarySplitScreen.reparent(organizer.mPrimary, POSITION_TOP,
-                false /*moveParents*/, "test");
-
-        // Assert windowing mode.
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode());
-
-        // Move primary to back.
-        primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
-                null /* task */);
-
-        // Assert that root task is at the bottom.
-        assertEquals(primarySplitScreen, organizer.mSecondary.getChildAt(0));
-
-        // Ensure that the override mode is restored to what it was (fullscreen)
-        assertEquals(WINDOWING_MODE_UNDEFINED,
-                primarySplitScreen.getRequestedOverrideWindowingMode());
-    }
-
-    @Test
     public void testSplitScreenMoveToBack() {
         TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
-        // Explicitly reparent task to primary split root to enter split mode, in which implies
-        // primary on top and secondary containing the home task below another root task.
-        final Task primaryTask = mDefaultTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final Task primaryTask = organizer.createTaskToPrimary(true /* onTop */);
+        final Task secondaryTask = organizer.createTaskToSecondary(true /* onTop */);
         final Task homeRoot = mDefaultTaskDisplayArea.getRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-        primaryTask.reparent(organizer.mPrimary, POSITION_TOP);
         mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
                 false /* includingParents */);
 
@@ -397,21 +339,26 @@
 
         // Assert that the primaryTask is now below home in its parent but primary is left alone.
         assertEquals(0, organizer.mPrimary.getChildCount());
-        assertEquals(primaryTask, organizer.mSecondary.getChildAt(0));
+        // Assert that root task is at the bottom.
+        assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primaryTask));
         assertEquals(1, organizer.mPrimary.compareTo(organizer.mSecondary));
         assertEquals(1, homeRoot.compareTo(primaryTask));
         assertEquals(homeRoot.getParent(), primaryTask.getParent());
 
         // Make sure windowing modes are correct
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, organizer.mPrimary.getWindowingMode());
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, primaryTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, organizer.mPrimary.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, secondaryTask.getWindowingMode());
+        // Ensure no longer in splitscreen.
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primaryTask.getWindowingMode());
+        // Ensure that the override mode is restored to undefined
+        assertEquals(WINDOWING_MODE_UNDEFINED, primaryTask.getRequestedOverrideWindowingMode());
 
         // Move secondary to back via parent (should be equivalent)
         organizer.mSecondary.moveToBack("test", secondaryTask);
 
-        // Assert that it is now in back but still in secondary split
+        // Assert that it is now in back and left in secondary split
+        assertEquals(0, organizer.mSecondary.getChildCount());
         assertEquals(1, homeRoot.compareTo(primaryTask));
-        assertEquals(secondaryTask, organizer.mSecondary.getChildAt(0));
         assertEquals(1, primaryTask.compareTo(secondaryTask));
         assertEquals(homeRoot.getParent(), secondaryTask.getParent());
     }
@@ -423,7 +370,7 @@
                 .setTask(rootHomeTask)
                 .build();
         final Task secondaryRootTask = mAtm.mTaskOrganizerController.createRootTask(
-                rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+                rootHomeTask.getDisplayContent(), WINDOWING_MODE_MULTI_WINDOW, null);
 
         rootHomeTask.reparent(secondaryRootTask, POSITION_TOP);
         assertEquals(secondaryRootTask, rootHomeTask.getParent());
@@ -581,7 +528,9 @@
         assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
     }
 
+    // TODO(b/199236198): check this is unnecessary or need to migrate after remove legacy split.
     @Test
+    @Ignore
     public void testShouldBeVisible_SplitScreen() {
         // task not supporting split should be fullscreen for this test.
         final Task notSupportingSplitTask = createTaskForShouldBeVisibleTest(
@@ -700,30 +649,23 @@
 
     @Test
     public void testGetVisibility_MultiLevel() {
-        final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
+        TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
         final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
-        // Creating as two-level tasks so home task can be reparented to split-secondary root task.
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
         final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */,
-                true /* twoLevelTask */);
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
 
-        doReturn(false).when(homeRootTask).isTranslucent(any());
         doReturn(false).when(splitPrimary).isTranslucent(any());
         doReturn(false).when(splitSecondary).isTranslucent(any());
 
-        // Re-parent home to split secondary.
-        homeRootTask.reparent(splitSecondary, POSITION_TOP);
-        // Current tasks should be visible.
+        // Re-parent tasks to split.
+        organizer.putTaskToPrimary(splitPrimary, true /* onTop */);
+        organizer.putTaskToSecondary(splitSecondary, true /* onTop */);
+        // Reparented tasks should be visible.
         assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 splitPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 splitSecondary.getVisibility(null /* starting */));
-        // Home task should still be visible even though it is a child of another visible task.
-        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
-                homeRootTask.getVisibility(null /* starting */));
-
 
         // Add fullscreen translucent task that partially occludes split tasks
         final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
@@ -736,19 +678,12 @@
                 splitPrimary.getVisibility(null /* starting */));
         assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitSecondary.getVisibility(null /* starting */));
-        // Home task should be visible behind translucent since its parent is visible behind
-        // translucent.
-        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-                homeRootTask.getVisibility(null /* starting */));
-
 
         // Hide split-secondary
-        splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
+        organizer.mSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
         // Home split secondary and home task should be invisible.
         assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 splitSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
-                homeRootTask.getVisibility(null /* starting */));
     }
 
     @Test
@@ -1094,36 +1029,6 @@
         assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
     }
 
-    @Test
-    public void testSplitScreenMoveToFront() {
-        final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
-
-        doReturn(false).when(splitScreenPrimary).isTranslucent(any());
-        doReturn(false).when(splitScreenSecondary).isTranslucent(any());
-        doReturn(false).when(assistantRootTask).isTranslucent(any());
-
-        assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
-
-        splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
-
-        if (isAssistantOnTop()) {
-            assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
-            assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-            assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
-        } else {
-            assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-            assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-            assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
-        }
-    }
-
     private Task createStandardRootTaskForVisibilityTest(int windowingMode,
             boolean translucent) {
         final Task rootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
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 4069f0f..f4abf88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -21,8 +21,8 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.TYPE_VIRTUAL;
@@ -458,7 +458,7 @@
         final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
                 .getDefaultTaskDisplayArea();
         final Task task = defaultTaskDisplayArea.createRootTask(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         // Created tasks are focusable by default.
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 645d804..b815c38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -77,6 +77,7 @@
 import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
@@ -937,8 +938,8 @@
         mTask.reparent(organizer.mPrimary, POSITION_TOP,
                 false /*moveParents*/, "test");
         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mTask.getWindowingMode());
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, activity.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
 
         // Resizable activity is sandboxed due to config being enabled.
         assertActivityMaxBoundsSandboxed(activity);
@@ -1828,8 +1829,8 @@
         mTask.reparent(organizer.mPrimary, POSITION_TOP,
                 false /*moveParents*/, "test");
         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mTask.getWindowingMode());
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mActivity.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // Non-resizable activity in size compat mode
         assertScaled();
@@ -1868,8 +1869,8 @@
         mTask.reparent(organizer.mPrimary, POSITION_TOP,
                 false /*moveParents*/, "test");
         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mTask.getWindowingMode());
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mActivity.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // Non-resizable activity in size compat mode
         assertScaled();
@@ -2182,6 +2183,29 @@
                 .computeAspectRatio(sizeCompatAppBounds), delta);
     }
 
+    @Test
+    public void testClearSizeCompat_resetOverrideConfig() {
+        final int origDensity = 480;
+        final int newDensity = 520;
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 600, 800)
+                .setDensityDpi(origDensity)
+                .build();
+        setUpApp(display);
+        prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity should enter size compat with old density after display density change.
+        display.setForcedDensity(newDensity, UserHandle.USER_CURRENT);
+
+        assertScaled();
+        assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
+
+        // Activity should exit size compat with new density.
+        mActivity.clearSizeCompatMode();
+
+        assertFitted();
+        assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
+    }
+
     private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
             float letterboxHorizontalPositionMultiplier) {
         // Set up a display in landscape and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index cdf6b59..80f6bce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -25,8 +25,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
@@ -355,14 +353,10 @@
                 true /* reuseCandidate */);
         assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask,
-                true /* reuseCandidate */);
         assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask,
                 true /* reuseCandidate */);
         assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
                 true /* reuseCandidate */);
-        assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask,
-                false /* reuseCandidate */);
         assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask,
                 true /* reuseCandidate */);
 
@@ -388,7 +382,7 @@
 
         final Task primarySplitTask = new TaskBuilder(mSupervisor)
                 .setTaskDisplayArea(defaultTaskDisplayArea)
-                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+                .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
                 .setOnTop(true)
                 .setCreateActivity(true)
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 9ad8c5b..0debdfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,14 +21,17 @@
 import static com.android.server.wm.testing.Assert.assertThrows;
 
 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.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
@@ -471,6 +474,7 @@
         final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
                 .setOrganizer(mOrganizer)
+                .setFragmentToken(mFragmentToken)
                 .build();
 
         // Mock the task to invisible
@@ -485,4 +489,38 @@
         // Verifies that event was not sent
         verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
     }
+
+    /**
+     * Tests that a task fragment info changed event is still sent if the task is invisible only
+     * when the info changed event is because of the last activity in a task finishing.
+     */
+    @Test
+    public void testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent() {
+        // Create a TaskFragment with an activity, all within a parent task
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(mFragmentToken)
+                .setCreateParentTask()
+                .createActivityCount(1)
+                .build();
+        final Task parentTask = taskFragment.getTask();
+        final ActivityRecord activity = taskFragment.getTopNonFinishingActivity();
+        assertTrue(parentTask.shouldBeVisible(null));
+
+        // Dispatch pending info changed event from creating the activity
+        mController.registerOrganizer(mIOrganizer);
+        taskFragment.mTaskFragmentAppearedSent = true;
+        mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+        mController.dispatchPendingEvents();
+
+        // Finish the activity and verify that the task is invisible
+        activity.finishing = true;
+        assertFalse(parentTask.shouldBeVisible(null));
+
+        // Verify the info changed callback still occurred despite the task being invisible
+        reset(mOrganizer);
+        mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+        mController.dispatchPendingEvents();
+        verify(mOrganizer).onTaskFragmentInfoChanged(any());
+    }
 }
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 0a8b2e7..fe41734 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -44,6 +44,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.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -66,6 +67,7 @@
 import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -1424,6 +1426,29 @@
         verify(task2).moveToFrontInner(anyString(), isNull());
     }
 
+    @Test
+    public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment tfBehind = createTaskFragmentWithParentTask(
+                task, false /* createEmbeddedTask */);
+        final TaskFragment tfFront = createTaskFragmentWithParentTask(
+                task, false /* createEmbeddedTask */);
+        spyOn(tfFront);
+        doReturn(true).when(tfFront).isTranslucent(any());
+
+        // TaskFragment behind another translucent TaskFragment should not be resumed.
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+                tfBehind.getVisibility(null /* starting */));
+        assertTrue(tfBehind.isFocusable());
+        assertFalse(tfBehind.canBeResumed(null /* starting */));
+
+        spyOn(tfBehind);
+        task.resumeTopActivityUncheckedLocked(null /* prev */, ActivityOptions.makeBasic(),
+                false /* deferPause */);
+
+        verify(tfBehind, never()).resumeTopActivity(any(), any(), anyBoolean());
+    }
+
     private Task getTestTask() {
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         return task.getBottomMostTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 3065e7d..8b0716c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -30,6 +30,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 
+import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -38,6 +39,8 @@
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
+import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
+
 class TestDisplayContent extends DisplayContent {
 
     public static final int DEFAULT_LOGICAL_DISPLAY_DENSITY = 300;
@@ -81,6 +84,7 @@
         protected final ActivityTaskManagerService mService;
         private boolean mSystemDecorations = false;
         private int mStatusBarHeight = 0;
+        private SettingsEntry mOverrideSettings;
 
         Builder(ActivityTaskManagerService service, int width, int height) {
             mService = service;
@@ -104,6 +108,10 @@
         private String generateUniqueId() {
             return "TEST_DISPLAY_CONTENT_" + System.currentTimeMillis();
         }
+        Builder setOverrideSettings(@Nullable SettingsEntry overrideSettings) {
+            mOverrideSettings = overrideSettings;
+            return this;
+        }
         Builder setSystemDecorations(boolean yes) {
             mSystemDecorations = yes;
             return this;
@@ -151,6 +159,11 @@
         TestDisplayContent build() {
             SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
 
+            if (mOverrideSettings != null) {
+                mService.mWindowManager.mDisplayWindowSettingsProvider
+                        .updateOverrideSettings(mInfo, mOverrideSettings);
+            }
+
             final int displayId = SystemServicesTestRule.sNextDisplayId++;
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
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 ed3888c..141588a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -488,6 +488,7 @@
         final TestTransitionPlayer player = registerTestTransitionPlayer();
 
         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+        mDisplayContent.setLastHasContent();
         mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */,
                 null /* displayChange */);
         final FadeRotationAnimationController fadeController =
@@ -536,6 +537,7 @@
                 null /* remoteTransition */, null /* displayChange */);
         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
         final int anyChanges = 1;
+        mDisplayContent.setLastHasContent();
         mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
         transition.setKnownConfigChanges(mDisplayContent, anyChanges);
         final FadeRotationAnimationController fadeController =
@@ -550,9 +552,11 @@
         assertTrue(app.getTask().inTransition());
 
         final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
+        doReturn(true).when(leash).isValid();
         player.onTransactionReady(startTransaction);
         // The leash should be unrotated.
-        verify(startTransaction).setMatrix(eq(statusBar.mToken.getAnimationLeash()), any(), any());
+        verify(startTransaction).setMatrix(eq(leash), any(), any());
 
         // The redrawn window will be faded in when the transition finishes. And because this test
         // only use one non-activity window, the fade rotation controller should also be cleared.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index bec53d7..8b14e98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
@@ -77,6 +78,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
@@ -271,6 +273,22 @@
     }
 
     @Test
+    public void testRemoveImmediatelyClearsLeash() {
+        final AnimationAdapter animAdapter = mock(AnimationAdapter.class);
+        final WindowToken token = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+        final SurfaceControl.Transaction t = token.getPendingTransaction();
+        token.startAnimation(t, animAdapter, false /* hidden */,
+                SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
+        final ArgumentCaptor<SurfaceControl> leashCaptor =
+                ArgumentCaptor.forClass(SurfaceControl.class);
+        verify(animAdapter).startAnimation(leashCaptor.capture(), eq(t), anyInt(), any());
+        assertTrue(token.mSurfaceAnimator.hasLeash());
+        token.removeImmediately();
+        assertFalse(token.mSurfaceAnimator.hasLeash());
+        verify(t).remove(eq(leashCaptor.getValue()));
+    }
+
+    @Test
     public void testAddChildByIndex() {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index f138475..64959f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -17,8 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -50,11 +49,8 @@
     @Test
     public void testDockedDividerPosition() {
         final WindowState splitScreenWindow = createWindow(null,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
                 mDisplayContent, "splitScreenWindow");
-        final WindowState splitScreenSecondaryWindow = createWindow(null,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
-                TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
 
         mDisplayContent.setImeLayeringTarget(splitScreenWindow);
 
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 75a87ba..4d5fb6d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -24,8 +24,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -520,16 +518,16 @@
         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
 
         Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                dc, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+                dc, WINDOWING_MODE_FULLSCREEN, null);
         RunningTaskInfo info1 = task1.getTaskInfo();
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+        assertEquals(WINDOWING_MODE_FULLSCREEN,
                 info1.configuration.windowConfiguration.getWindowingMode());
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
 
         Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                dc, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+                dc, WINDOWING_MODE_MULTI_WINDOW, null);
         RunningTaskInfo info2 = task2.getTaskInfo();
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW,
                 info2.configuration.windowConfiguration.getWindowingMode());
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
 
@@ -539,7 +537,7 @@
         assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
         infos = getTasksCreatedByOrganizer(dc);
         assertEquals(1, infos.size());
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode());
     }
 
     @Test
@@ -577,7 +575,7 @@
         final StubOrganizer listener = new StubOrganizer();
         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
         Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
         RunningTaskInfo info1 = task.getTaskInfo();
 
         final Task rootTask = createTask(
@@ -626,7 +624,7 @@
         };
         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
         Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
         RunningTaskInfo info1 = task.getTaskInfo();
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -684,10 +682,10 @@
         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
 
         Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
         RunningTaskInfo info1 = task1.getTaskInfo();
         Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
         RunningTaskInfo info2 = task2.getTaskInfo();
         // Ensure events dispatch to organizer.
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -1056,7 +1054,7 @@
     public void testReparentToOrganizedTask() {
         final ITaskOrganizer organizer = registerMockOrganizer();
         Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
         final Task task1 = createRootTask();
         final Task task2 = createTask(rootTask, false /* fakeDraw */);
         WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1223,7 +1221,7 @@
         final Task rootTask = activity.getRootTask();
         rootTask.setResizeMode(activity.info.resizeMode);
         final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
                 splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
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 ca2b4ae..80192f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -20,7 +20,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -83,6 +82,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 import android.view.Gravity;
 import android.view.InputWindowHandle;
 import android.view.InsetsSource;
@@ -252,6 +252,11 @@
         assertFalse(appWindow.canBeImeTarget());
         appWindow.mActivityRecord.setWindowingMode(initialMode);
 
+        // Verify that app window can still be IME target as long as it is visible (even if
+        // it is going to become invisible).
+        appWindow.mActivityRecord.mVisibleRequested = false;
+        assertTrue(appWindow.canBeImeTarget());
+
         // Make windows invisible
         appWindow.hide(false /* doAnimation */, false /* requestAnim */);
         imeWindow.hide(false /* doAnimation */, false /* requestAnim */);
@@ -260,22 +265,19 @@
         assertFalse(appWindow.canBeImeTarget());
         assertFalse(imeWindow.canBeImeTarget());
 
-        // Simulate the window is in split screen primary root task and the current state is
-        // minimized and home root task is resizable, so that we should ignore input for the
-        // root task.
+        // Simulate the window is in split screen root task.
         final DockedTaskDividerController controller =
                 mDisplayContent.getDockedDividerController();
         final Task rootTask = createTask(mDisplayContent,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         spyOn(appWindow);
         spyOn(controller);
         spyOn(rootTask);
         rootTask.setFocusable(false);
         doReturn(rootTask).when(appWindow).getRootTask();
 
-        // Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
+        // Make sure canBeImeTarget is false;
         assertFalse(appWindow.canBeImeTarget());
-        assertTrue(rootTask.shouldIgnoreInput());
     }
 
     @Test
@@ -722,8 +724,9 @@
     @Test
     public void testCantReceiveTouchWhenNotFocusable() {
         final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
-        win0.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        win0.mActivityRecord.getRootTask().setFocusable(false);
+        final Task rootTask = win0.mActivityRecord.getRootTask();
+        spyOn(rootTask);
+        when(rootTask.shouldIgnoreInput()).thenReturn(true);
         assertFalse(win0.canReceiveTouchInput());
     }
 
@@ -923,8 +926,8 @@
         mDisplayContent.setImeLayeringTarget(mAppWindow);
 
         // Simulate entering multi-window mode and verify if the IME control target is remote.
-        app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, app.getWindowingMode());
+        app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode());
         assertEquals(mDisplayContent.mRemoteInsetsControlTarget,
                 mDisplayContent.computeImeControlTarget());
 
@@ -939,14 +942,13 @@
 
     @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
     @Test
-    public void testNotificationShadeHasImeInsetsWhenSplitscreenActivated() {
+    public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
         WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
                 mAppWindow.mToken, "app");
 
-        // Simulate entering multi-window mode and verify if the split-screen is activated.
-        app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, app.getWindowingMode());
-        assertTrue(mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated());
+        // Simulate entering multi-window mode and windowing mode is multi-window.
+        app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode());
 
         // Simulate notificationShade is shown and being IME layering target.
         mNotificationShadeWindow.setHasSurface(true);
@@ -960,7 +962,7 @@
         mDisplayContent.getInsetsStateController().getRawInsetsState()
                 .setSourceVisible(ITYPE_IME, true);
 
-        // Verify notificationShade can still get IME insets even the split-screen is activated.
+        // Verify notificationShade can still get IME insets even windowing mode is multi-window.
         InsetsState state = mDisplayContent.getInsetsStateController().getInsetsForWindow(
                 mNotificationShadeWindow);
         assertNotNull(state.peekSource(ITYPE_IME));
@@ -981,4 +983,40 @@
         assertFalse(app.isVisible());
         assertTrue(app.isVisibleRequested());
     }
+
+    @Test
+    public void testKeepClearAreas() {
+        final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+        makeWindowVisible(window);
+
+        final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
+        final Rect keepClearArea2 = new Rect(5, 10, 15, 20);
+        final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2);
+        window.setKeepClearAreas(keepClearAreas);
+
+        // Test that the keep-clear rects are stored and returned
+        assertEquals(new ArraySet(keepClearAreas), new ArraySet(window.getKeepClearAreas()));
+
+        // Test that keep-clear rects are overwritten
+        window.setKeepClearAreas(Arrays.asList());
+        assertEquals(0, window.getKeepClearAreas().size());
+
+        // Move the window position
+        final SurfaceControl.Transaction t = spy(StubTransaction.class);
+        window.mSurfaceControl = mock(SurfaceControl.class);
+        final Rect frame = window.getFrame();
+        frame.set(10, 20, 60, 80);
+        window.updateSurfacePosition(t);
+        assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition);
+
+        // Test that the returned keep-clear rects are translated to display space
+        window.setKeepClearAreas(keepClearAreas);
+        Rect expectedArea1 = new Rect(keepClearArea1);
+        expectedArea1.offset(frame.left, frame.top);
+        Rect expectedArea2 = new Rect(keepClearArea2);
+        expectedArea2.offset(frame.left, frame.top);
+
+        assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
+                     new ArraySet(window.getKeepClearAreas()));
+    }
 }
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 34038c5..62c1067 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -17,14 +17,10 @@
 package com.android.server.wm;
 
 import static android.app.AppOpsManager.OP_NONE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -66,15 +62,14 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
-import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
@@ -106,6 +101,7 @@
 
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
 import org.junit.After;
 import org.junit.Before;
@@ -720,18 +716,21 @@
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
     private DisplayContent createNewDisplayWithImeSupport(@DisplayImePolicy int imePolicy) {
-        return createNewDisplay(mDisplayInfo, imePolicy);
+        return createNewDisplay(mDisplayInfo, imePolicy, /* overrideSettings */ null);
     }
 
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
     DisplayContent createNewDisplay(DisplayInfo info) {
-        return createNewDisplay(info, DISPLAY_IME_POLICY_LOCAL);
+        return createNewDisplay(info, DISPLAY_IME_POLICY_LOCAL, /* overrideSettings */ null);
     }
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
-    private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy) {
+    private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy,
+            @Nullable SettingsEntry overrideSettings) {
         final DisplayContent display =
-                new TestDisplayContent.Builder(mAtm, info).build();
+                new TestDisplayContent.Builder(mAtm, info)
+                        .setOverrideSettings(overrideSettings)
+                        .build();
         final DisplayContent dc = display.mDisplayContent;
         // this display can show IME.
         dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy);
@@ -749,7 +748,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
         displayInfo.state = displayState;
-        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_LOCAL);
+        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_LOCAL, /* overrideSettings */ null);
     }
 
     /** Creates a {@link TestWindowState} */
@@ -761,11 +760,15 @@
 
     /** Creates a {@link DisplayContent} as parts of simulate display info for test. */
     DisplayContent createMockSimulatedDisplay() {
+        return createMockSimulatedDisplay(/* overrideSettings */ null);
+    }
+
+    DisplayContent createMockSimulatedDisplay(@Nullable SettingsEntry overrideSettings) {
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
         displayInfo.type = Display.TYPE_VIRTUAL;
         displayInfo.ownerUid = SYSTEM_UID;
-        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_FALLBACK_DISPLAY);
+        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_FALLBACK_DISPLAY, overrideSettings);
     }
 
     IDisplayWindowInsetsController createDisplayWindowInsetsController() {
@@ -1507,64 +1510,55 @@
 
     static class TestSplitOrganizer extends WindowOrganizerTests.StubOrganizer {
         final ActivityTaskManagerService mService;
+        final TaskDisplayArea mDefaultTDA;
         Task mPrimary;
         Task mSecondary;
-        boolean mInSplit = false;
-        // moves everything to secondary. Most tests expect this since sysui usually does it.
-        boolean mMoveToSecondaryOnEnter = true;
         int mDisplayId;
-        private static final int[] CONTROLLED_ACTIVITY_TYPES = {
-                ACTIVITY_TYPE_STANDARD,
-                ACTIVITY_TYPE_HOME,
-                ACTIVITY_TYPE_RECENTS,
-                ACTIVITY_TYPE_UNDEFINED
-        };
-        private static final int[] CONTROLLED_WINDOWING_MODES = {
-                WINDOWING_MODE_FULLSCREEN,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                WINDOWING_MODE_UNDEFINED
-        };
+
         TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) {
             mService = service;
+            mDefaultTDA = display.getDefaultTaskDisplayArea();
             mDisplayId = display.mDisplayId;
             mService.mTaskOrganizerController.registerTaskOrganizer(this);
             mPrimary = mService.mTaskOrganizerController.createRootTask(
-                    display, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+                    display, WINDOWING_MODE_MULTI_WINDOW, null);
             mSecondary = mService.mTaskOrganizerController.createRootTask(
-                    display, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);;
+                    display, WINDOWING_MODE_MULTI_WINDOW, null);
+
+            mPrimary.setAdjacentTaskFragment(mSecondary, true);
+            display.getDefaultTaskDisplayArea().setLaunchAdjacentFlagRootTask(mSecondary);
+
+            final Rect primaryBounds = new Rect();
+            final Rect secondaryBounds = new Rect();
+            display.getBounds().splitVertically(primaryBounds, secondaryBounds);
+            mPrimary.setBounds(primaryBounds);
+            mSecondary.setBounds(secondaryBounds);
         }
+
         TestSplitOrganizer(ActivityTaskManagerService service) {
             this(service, service.mTaskSupervisor.mRootWindowContainer.getDefaultDisplay());
         }
-        public void setMoveToSecondaryOnEnter(boolean move) {
-            mMoveToSecondaryOnEnter = move;
+
+        public Task createTaskToPrimary(boolean onTop) {
+            final Task primaryTask = mDefaultTDA.createRootTask(
+                    WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, onTop);
+            putTaskToPrimary(primaryTask, onTop);
+            return primaryTask;
         }
 
-        @Override
-        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
-            if (mInSplit) {
-                return;
-            }
-            if (info.topActivityType == ACTIVITY_TYPE_UNDEFINED) {
-                // Not populated
-                return;
-            }
-            if (info.configuration.windowConfiguration.getWindowingMode()
-                    != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                return;
-            }
-            mInSplit = true;
-            if (!mMoveToSecondaryOnEnter) {
-                return;
-            }
-            DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
-            dc.getDefaultTaskDisplayArea().setLaunchRootTask(
-                    mSecondary, CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES);
-            dc.forAllRootTasks(rootTask -> {
-                if (!WindowConfiguration.isSplitScreenWindowingMode(rootTask.getWindowingMode())) {
-                    rootTask.reparent(mSecondary, POSITION_BOTTOM);
-                }
-            });
+        public Task createTaskToSecondary(boolean onTop) {
+            final Task secondaryTask = mDefaultTDA.createRootTask(
+                    WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, onTop);
+            putTaskToSecondary(secondaryTask, onTop);
+            return secondaryTask;
+        }
+
+        public void putTaskToPrimary(Task task, boolean onTop) {
+            task.reparent(mPrimary, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        }
+
+        public void putTaskToSecondary(Task task, boolean onTop) {
+            task.reparent(mSecondary, onTop ? POSITION_TOP : POSITION_BOTTOM);
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 049966c..0f223ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -22,7 +22,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -363,7 +362,7 @@
                 ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
                 "pinnedStackWindow");
         final WindowState dockedStackWindow = createWindow(null,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
                 mDisplayContent, "dockedStackWindow");
         final WindowState assistantStackWindow = createWindow(null,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
@@ -428,6 +427,25 @@
         assertWindowHigher(mImeWindow, imeAppTarget);
     }
 
+    @Test
+    public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() {
+        final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
+                mAppWindow.mActivityRecord, "imeAppTarget");
+        mDisplayContent.setImeInputTarget(imeAppTarget);
+        mDisplayContent.setImeLayeringTarget(imeAppTarget);
+        mDisplayContent.setImeControlTarget(imeAppTarget);
+
+        // Set a popup IME layering target and keeps the original IME control target behinds it.
+        final WindowState popupImeTargetWin = createWindow(imeAppTarget,
+                TYPE_APPLICATION_SUB_PANEL, mAppWindow.mActivityRecord, "popupImeTargetWin");
+        mDisplayContent.setImeLayeringTarget(popupImeTargetWin);
+        mDisplayContent.updateImeParent();
+
+        // Ime should on top of the popup IME layering target window.
+        mDisplayContent.assignChildLayers(mTransaction);
+        assertWindowHigher(mImeWindow, popupImeTargetWin);
+    }
+
 
     @Test
     public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() {
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 88725a6..0d88a0d 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -61,6 +61,7 @@
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -70,6 +71,7 @@
 import android.util.Slog;
 import android.util.SparseLongArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
@@ -128,6 +130,12 @@
     private final CopyOnWriteArrayList<Pair<String, StorageStatsAugmenter>>
             mStorageStatsAugmenters = new CopyOnWriteArrayList<>();
 
+    @GuardedBy("mLock")
+    private int
+            mStorageThresholdPercentHigh = StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH;
+
+    private final Object mLock = new Object();
+
     public StorageStatsService(Context context) {
         mContext = Preconditions.checkNotNull(context);
         mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class));
@@ -173,6 +181,19 @@
                 }
             }
         }, prFilter);
+
+        updateConfig();
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                mContext.getMainExecutor(), properties -> updateConfig());
+    }
+
+    private void updateConfig() {
+        synchronized (mLock) {
+            mStorageThresholdPercentHigh = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                    StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY,
+                    StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+        }
     }
 
     private void invalidateMounts() {
@@ -554,7 +575,7 @@
          * By only triggering a re-calculation after the storage has changed sizes, we can avoid
          * recalculating quotas too often. Minimum change delta high and low define the
          * percentage of change we need to see before we recalculate quotas when the device has
-         * enough storage space (more than StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total
+         * enough storage space (more than mStorageThresholdPercentHigh of total
          * free) and in low storage condition respectively.
          */
         private static final long MINIMUM_CHANGE_DELTA_PERCENT_HIGH = 5;
@@ -588,11 +609,15 @@
                     mStats.restat(Environment.getDataDirectory().getAbsolutePath());
                     long bytesDelta = Math.abs(mPreviousBytes - mStats.getAvailableBytes());
                     long bytesDeltaThreshold;
-                    if (mStats.getAvailableBytes() >  mTotalBytes
-                            * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
-                        bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
-                    } else {
-                        bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+                    synchronized (mLock) {
+                        if (mStats.getAvailableBytes() >  mTotalBytes
+                                * mStorageThresholdPercentHigh / 100) {
+                            bytesDeltaThreshold = mTotalBytes
+                                    * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
+                        } else {
+                            bytesDeltaThreshold = mTotalBytes
+                                    * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+                        }
                     }
                     if (bytesDelta > bytesDeltaThreshold) {
                         mPreviousBytes = mStats.getAvailableBytes();
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 01feacd..3b50fa4 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -29,6 +29,7 @@
         "android.hardware.usb-V1.1-java",
         "android.hardware.usb-V1.2-java",
         "android.hardware.usb-V1.3-java",
+	"android.hardware.usb-V1-java",
         "android.hardware.usb.gadget-V1.0-java",
         "android.hardware.usb.gadget-V1.1-java",
         "android.hardware.usb.gadget-V1.2-java",
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 9d4db00..85b1de5 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -41,6 +41,7 @@
 
     private final boolean mIsInputHeadset;
     private final boolean mIsOutputHeadset;
+    private final boolean mIsDock;
 
     private boolean mSelected = false;
     private int mOutputState;
@@ -53,7 +54,7 @@
 
     public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
             boolean hasOutput, boolean hasInput,
-            boolean isInputHeadset, boolean isOutputHeadset) {
+            boolean isInputHeadset, boolean isOutputHeadset, boolean isDock) {
         mAudioService = audioService;
         mCardNum = card;
         mDeviceNum = device;
@@ -62,31 +63,32 @@
         mHasInput = hasInput;
         mIsInputHeadset = isInputHeadset;
         mIsOutputHeadset = isOutputHeadset;
+        mIsDock = isDock;
     }
 
     /**
-     * @returns the ALSA card number associated with this peripheral.
+     * @return the ALSA card number associated with this peripheral.
      */
     public int getCardNum() {
         return mCardNum;
     }
 
     /**
-     * @returns the ALSA device number associated with this peripheral.
+     * @return the ALSA device number associated with this peripheral.
      */
     public int getDeviceNum() {
         return mDeviceNum;
     }
 
     /**
-     * @returns the USB device device address associated with this peripheral.
+     * @return the USB device device address associated with this peripheral.
      */
     public String getDeviceAddress() {
         return mDeviceAddress;
     }
 
     /**
-     * @returns the ALSA card/device address string.
+     * @return the ALSA card/device address string.
      */
     public String getAlsaCardDeviceString() {
         if (mCardNum < 0 || mDeviceNum < 0) {
@@ -98,35 +100,42 @@
     }
 
     /**
-     * @returns true if the device supports output.
+     * @return true if the device supports output.
      */
     public boolean hasOutput() {
         return mHasOutput;
     }
 
     /**
-     * @returns true if the device supports input (recording).
+     * @return true if the device supports input (recording).
      */
     public boolean hasInput() {
         return mHasInput;
     }
 
     /**
-     * @returns true if the device is a headset for purposes of input.
+     * @return true if the device is a headset for purposes of input.
      */
     public boolean isInputHeadset() {
         return mIsInputHeadset;
     }
 
     /**
-     * @returns true if the device is a headset for purposes of output.
+     * @return true if the device is a headset for purposes of output.
      */
     public boolean isOutputHeadset() {
         return mIsOutputHeadset;
     }
 
     /**
-     * @returns true if input jack is detected or jack detection is not supported.
+     * @return true if the device is a USB dock.
+     */
+    public boolean isDock() {
+        return mIsDock;
+    }
+
+    /**
+     * @return true if input jack is detected or jack detection is not supported.
      */
     private synchronized boolean isInputJackConnected() {
         if (mJackDetector == null) {
@@ -136,7 +145,7 @@
     }
 
     /**
-     * @returns true if input jack is detected or jack detection is not supported.
+     * @return true if input jack is detected or jack detection is not supported.
      */
     private synchronized boolean isOutputJackConnected() {
         if (mJackDetector == null) {
@@ -190,9 +199,10 @@
         try {
             // Output Device
             if (mHasOutput) {
-                int device = mIsOutputHeadset
-                        ? AudioSystem.DEVICE_OUT_USB_HEADSET
-                        : AudioSystem.DEVICE_OUT_USB_DEVICE;
+                int device = mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET
+                        : (mIsOutputHeadset
+                            ? AudioSystem.DEVICE_OUT_USB_HEADSET
+                            : AudioSystem.DEVICE_OUT_USB_DEVICE);
                 if (DEBUG) {
                     Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
                             + " addr:" + alsaCardDeviceString
@@ -231,7 +241,7 @@
 
     /**
      * @Override
-     * @returns a string representation of the object.
+     * @return a string representation of the object.
      */
     public synchronized String toString() {
         return "UsbAlsaDevice: [card: " + mCardNum
@@ -273,7 +283,7 @@
 
     /**
      * @Override
-     * @returns true if the objects are equivalent.
+     * @return true if the objects are equivalent.
      */
     public boolean equals(Object obj) {
         if (!(obj instanceof UsbAlsaDevice)) {
@@ -285,12 +295,13 @@
                 && mHasOutput == other.mHasOutput
                 && mHasInput == other.mHasInput
                 && mIsInputHeadset == other.mIsInputHeadset
-                && mIsOutputHeadset == other.mIsOutputHeadset);
+                && mIsOutputHeadset == other.mIsOutputHeadset
+                && mIsDock == other.mIsDock);
     }
 
     /**
      * @Override
-     * @returns a hash code generated from the object contents.
+     * @return a hash code generated from the object contents.
      */
     public int hashCode() {
         final int prime = 31;
@@ -301,6 +312,7 @@
         result = prime * result + (mHasInput ? 0 : 1);
         result = prime * result + (mIsInputHeadset ? 0 : 1);
         result = prime * result + (mIsOutputHeadset ? 0 : 1);
+        result = prime * result + (mIsDock ? 0 : 1);
 
         return result;
     }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 0aa62c5..fd9b995 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -237,6 +237,7 @@
         if (hasInput || hasOutput) {
             boolean isInputHeadset = parser.isInputHeadset();
             boolean isOutputHeadset = parser.isOutputHeadset();
+            boolean isDock = parser.isDock();
 
             if (mAudioService == null) {
                 Slog.e(TAG, "no AudioService");
@@ -246,7 +247,7 @@
             UsbAlsaDevice alsaDevice =
                     new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
                                       deviceAddress, hasOutput, hasInput,
-                                      isInputHeadset, isOutputHeadset);
+                                      isInputHeadset, isOutputHeadset, isDock);
             if (alsaDevice != null) {
                 alsaDevice.setDeviceNameAndDescription(
                           cardRec.getCardName(), cardRec.getCardDescription());
@@ -257,10 +258,11 @@
 
         // look for MIDI devices
         boolean hasMidi = parser.hasMIDIInterface();
-        int midiNumInputs = parser.calculateNumMidiInputs();
-        int midiNumOutputs = parser.calculateNumMidiOutputs();
+        int midiNumInputs = parser.calculateNumLegacyMidiInputs();
+        int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
         if (DEBUG) {
             Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
+            Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
         }
         if (hasMidi && mHasMidiFeature) {
             int device = 0;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index f33001c..94cc826 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
 import android.os.Bundle;
@@ -46,6 +47,8 @@
 import com.android.server.usb.descriptors.report.TextReportCanvas;
 import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
 
+import libcore.io.IoUtils;
+
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.HashMap;
@@ -90,6 +93,13 @@
     private ConnectionRecord mLastConnect;
     private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
 
+    /**
+     * List of connected MIDI devices
+     */
+    private final HashMap<String, UsbUniversalMidiDevice>
+            mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+    private final boolean mHasMidiFeature;
+
     /*
      * ConnectionRecord
      * Stores connection/disconnection data.
@@ -155,7 +165,7 @@
                 pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID())
                         + " product:" + Integer.toHexString(deviceDescriptor.getProductID()));
                 pw.println("isHeadset[in: " + parser.isInputHeadset()
-                        + " , out: " + parser.isOutputHeadset() + "]");
+                        + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
             } else {
                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
             }
@@ -169,9 +179,8 @@
                 UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
                 descriptorTree.parse(parser);
                 descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
-
                 stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
-                        + " , out: " + parser.isOutputHeadset() + "]");
+                        + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
                 pw.println(stringBuilder.toString());
             } else {
                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
@@ -188,9 +197,8 @@
                     descriptor.report(canvas);
                 }
                 pw.println(stringBuilder.toString());
-
                 pw.println("isHeadset[in: " + parser.isInputHeadset()
-                        + " , out: " + parser.isOutputHeadset() + "]");
+                        + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
             } else {
                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
             }
@@ -245,6 +253,7 @@
             setUsbDeviceConnectionHandler(ComponentName.unflattenFromString(
                     deviceConnectionHandler));
         }
+        mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
     }
 
     public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
@@ -413,6 +422,18 @@
 
                 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
 
+                if (mHasMidiFeature) {
+                    if (parser.containsUniversalMidiDeviceEndpoint()) {
+                        UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
+                                newDevice, parser);
+                        if (midiDevice != null) {
+                            mMidiDevices.put(deviceAddress, midiDevice);
+                        } else {
+                            Slog.e(TAG, "Universal Midi Device is null.");
+                        }
+                    }
+                }
+
                 // Tracking
                 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
                         parser.getRawDescriptors());
@@ -446,6 +467,14 @@
                 Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
                 mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
                 mPermissionManager.usbDeviceRemoved(device);
+
+                // MIDI
+                UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
+                if (midiDevice != null) {
+                    Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
+                    IoUtils.closeQuietly(midiDevice);
+                }
+
                 getCurrentUserSettings().usbDeviceRemoved(device);
                 ConnectionRecord current = mConnected.get(deviceAddress);
                 // Tracking
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index b61e93b..275f217 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -303,7 +303,8 @@
         }
 
         mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs,
-                null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
+                null, null, properties, MidiDeviceInfo.TYPE_USB,
+                MidiDeviceInfo.PROTOCOL_UNKNOWN, mCallback);
         if (mServer == null) {
             return false;
         }
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ec28040..65b79bf 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
@@ -25,6 +27,12 @@
 import static android.hardware.usb.UsbPortStatus.MODE_UFP;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SINK;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_HOST;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_DEVICE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_DFP;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_UFP;
 
 import static com.android.internal.usb.DumpUtils.writePort;
 import static com.android.internal.usb.DumpUtils.writePortStatus;
@@ -38,6 +46,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.hardware.usb.IUsbOperationInternal;
 import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
@@ -74,9 +83,13 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.dump.DualDumpOutputStream;
 import com.android.server.FgThread;
+import com.android.server.usb.hal.port.RawPortInfo;
+import com.android.server.usb.hal.port.UsbPortHal;
+import com.android.server.usb.hal.port.UsbPortHalInstance;
 
 import java.util.ArrayList;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 /**
  * Allows trusted components to control the properties of physical USB ports
@@ -109,16 +122,9 @@
     // The system context.
     private final Context mContext;
 
-    // Proxy object for the usb hal daemon.
-    @GuardedBy("mLock")
-    private IUsb mProxy = null;
-
     // Callback when the UsbPort status is changed by the kernel.
     // Mostly due a command sent by the remote Usb device.
-    private HALCallback mHALCallback = new HALCallback(null, this);
-
-    // Cookie sent for usb hal death notification.
-    private static final int USB_HAL_DEATH_COOKIE = 1000;
+    //private HALCallback mHALCallback = new HALCallback(null, this);
 
     // Used as the key while sending the bundle to Main thread.
     private static final String PORT_INFO = "port_info";
@@ -156,36 +162,23 @@
      */
     private int mIsPortContaminatedNotificationId;
 
-    private boolean mEnableUsbDataSignaling;
-    protected int mCurrentUsbHalVersion;
+    private UsbPortHal mUsbPortHal;
+
+    private long mTransactionId;
 
     public UsbPortManager(Context context) {
         mContext = context;
-        try {
-            ServiceNotification serviceNotification = new ServiceNotification();
-
-            boolean ret = IServiceManager.getService()
-                    .registerForNotifications("[email protected]::IUsb",
-                            "", serviceNotification);
-            if (!ret) {
-                logAndPrint(Log.ERROR, null,
-                        "Failed to register service start notification");
-            }
-        } catch (RemoteException e) {
-            logAndPrintException(null,
-                    "Failed to register service start notification", e);
-            return;
-        }
-        connectToProxy(null);
+        mUsbPortHal = UsbPortHalInstance.getInstance(this, null);
+        logAndPrint(Log.DEBUG, null, "getInstance done");
     }
 
     public void systemReady() {
-	mSystemReady = true;
-        if (mProxy != null) {
+        mSystemReady = true;
+        if (mUsbPortHal != null) {
+            mUsbPortHal.systemReady();
             try {
-                mProxy.queryPortStatus();
-                mEnableUsbDataSignaling = true;
-            } catch (RemoteException e) {
+                mUsbPortHal.queryPortStatus(++mTransactionId);
+            } catch (Exception e) {
                 logAndPrintException(null,
                         "ServiceStart: Failed to query port status", e);
             }
@@ -233,6 +226,7 @@
             intent.setComponent(ComponentName.unflattenFromString(r.getString(
                     com.android.internal.R.string.config_usbContaminantActivity)));
             intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
+            intent.putExtra(UsbManager.EXTRA_PORT_STATUS, currentPortInfo.mUsbPortStatus);
 
             // Simple notification clicks are immutable
             PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
@@ -340,13 +334,92 @@
         }
 
         try {
-            // Oneway call into the hal. Use the castFrom method from HIDL.
-            android.hardware.usb.V1_2.IUsb proxy = android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
-            proxy.enableContaminantPresenceDetection(portId, enable);
-        } catch (RemoteException e) {
+            mUsbPortHal.enableContaminantPresenceDetection(portId, enable, ++mTransactionId);
+        } catch (Exception e) {
             logAndPrintException(pw, "Failed to set contaminant detection", e);
-        } catch (ClassCastException e) {
-            logAndPrintException(pw, "Method only applicable to V1.2 or above implementation", e);
+        }
+    }
+
+    /**
+     * Limits power transfer in/out of USB-C port.
+     *
+     * @param portId port identifier.
+     * @param limit limit power transfer when true.
+     */
+    public void enableLimitPowerTransfer(@NonNull String portId, boolean limit, long transactionId,
+            IUsbOperationInternal callback, IndentingPrintWriter pw) {
+        Objects.requireNonNull(portId);
+        final PortInfo portInfo = mPorts.get(portId);
+        if (portInfo == null) {
+            logAndPrint(Log.ERROR, pw, "enableLimitPowerTransfer: No such port: " + portId
+                    + " opId:" + transactionId);
+            try {
+                if (callback != null) {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(pw,
+                        "enableLimitPowerTransfer: Failed to call OperationComplete. opId:"
+                        + transactionId, e);
+            }
+            return;
+        }
+
+        try {
+            try {
+                mUsbPortHal.enableLimitPowerTransfer(portId, limit, transactionId, callback);
+            } catch (Exception e) {
+                logAndPrintException(pw,
+                    "enableLimitPowerTransfer: Failed to limit power transfer. opId:"
+                    + transactionId , e);
+                if (callback != null) {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                }
+            }
+        } catch (RemoteException e) {
+            logAndPrintException(pw,
+                    "enableLimitPowerTransfer:Failed to call onOperationComplete. opId:"
+                    + transactionId, e);
+        }
+    }
+
+    /**
+     * Enables USB data when disabled due to {@link UsbPortStatus#USB_DATA_STATUS_DISABLED_DOCK}
+     */
+    public void enableUsbDataWhileDocked(@NonNull String portId, long transactionId,
+            IUsbOperationInternal callback, IndentingPrintWriter pw) {
+        Objects.requireNonNull(portId);
+        final PortInfo portInfo = mPorts.get(portId);
+        if (portInfo == null) {
+            logAndPrint(Log.ERROR, pw, "enableUsbDataWhileDocked: No such port: " + portId
+                    + " opId:" + transactionId);
+            try {
+                if (callback != null) {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(pw,
+                        "enableUsbDataWhileDocked: Failed to call OperationComplete. opId:"
+                        + transactionId, e);
+            }
+            return;
+        }
+
+        try {
+            try {
+                mUsbPortHal.enableUsbDataWhileDocked(portId, transactionId, callback);
+            } catch (Exception e) {
+                logAndPrintException(pw,
+                    "enableUsbDataWhileDocked: Failed to limit power transfer. opId:"
+                    + transactionId , e);
+                if (callback != null) {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                }
+            }
+        } catch (RemoteException e) {
+            logAndPrintException(pw,
+                    "enableUsbDataWhileDocked:Failed to call onOperationComplete. opId:"
+                    + transactionId, e);
         }
     }
 
@@ -355,46 +428,79 @@
      *
      * @param enable enable or disable USB data signaling
      */
-    public boolean enableUsbDataSignal(boolean enable) {
-        try {
-            mEnableUsbDataSignaling = enable;
-            // Call into the hal. Use the castFrom method from HIDL.
-            android.hardware.usb.V1_3.IUsb proxy = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
-            return proxy.enableUsbDataSignal(enable);
-        } catch (RemoteException e) {
-            logAndPrintException(null, "Failed to set USB data signaling", e);
-            return false;
-        } catch (ClassCastException e) {
-            logAndPrintException(null, "Method only applicable to V1.3 or above implementation", e);
+    public boolean enableUsbData(@NonNull String portId, boolean enable, int transactionId,
+            @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) {
+        Objects.requireNonNull(callback);
+        Objects.requireNonNull(portId);
+        final PortInfo portInfo = mPorts.get(portId);
+        if (portInfo == null) {
+            logAndPrint(Log.ERROR, pw, "enableUsbData: No such port: " + portId
+                    + " opId:" + transactionId);
+            try {
+                callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+            } catch (RemoteException e) {
+                logAndPrintException(pw,
+                        "enableUsbData: Failed to call OperationComplete. opId:"
+                        + transactionId, e);
+            }
             return false;
         }
+
+        try {
+            try {
+                return mUsbPortHal.enableUsbData(portId, enable, transactionId, callback);
+            } catch (Exception e) {
+                logAndPrintException(pw,
+                    "enableUsbData: Failed to invoke enableUsbData. opId:"
+                    + transactionId , e);
+                callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+            }
+        } catch (RemoteException e) {
+            logAndPrintException(pw,
+                    "enableUsbData: Failed to call onOperationComplete. opId:"
+                    + transactionId, e);
+        }
+
+        return false;
     }
 
     /**
      * Get USB HAL version
      *
      * @param none
+     * @return {@link UsbManager#USB_HAL_RETRY} returned when hal version
+     *         is yet to be determined.
      */
     public int getUsbHalVersion() {
-        return mCurrentUsbHalVersion;
+        if (mUsbPortHal != null) {
+            try {
+                return mUsbPortHal.getUsbHalVersion();
+            } catch (RemoteException e) {
+                return UsbManager.USB_HAL_RETRY;
+            }
+        }
+        return UsbManager.USB_HAL_RETRY;
     }
 
-    /**
-     * update USB HAL version
-     *
-     * @param none
-     */
-    private void updateUsbHalVersion() {
-        if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
-            mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_3;
-        } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
-            mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_2;
-        } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
-            mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_1;
-        } else {
-            mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0;
-        }
-        logAndPrint(Log.INFO, null, "USB HAL version: " + mCurrentUsbHalVersion);
+    private int toHalUsbDataRole(int usbDataRole) {
+        if (usbDataRole == DATA_ROLE_DEVICE)
+            return HAL_DATA_ROLE_DEVICE;
+        else
+            return HAL_DATA_ROLE_HOST;
+    }
+
+    private int toHalUsbPowerRole(int usbPowerRole) {
+        if (usbPowerRole == POWER_ROLE_SINK)
+            return HAL_POWER_ROLE_SINK;
+        else
+            return HAL_POWER_ROLE_SOURCE;
+    }
+
+    private int toHalUsbMode(int usbMode) {
+        if (usbMode == MODE_UFP)
+            return HAL_MODE_UFP;
+        else
+            return HAL_MODE_DFP;
     }
 
     public void setPortRoles(String portId, int newPowerRole, int newDataRole,
@@ -473,7 +579,7 @@
                 sim.currentPowerRole = newPowerRole;
                 sim.currentDataRole = newDataRole;
                 updatePortsLocked(pw, null);
-            } else if (mProxy != null) {
+            } else if (mUsbPortHal != null) {
                 if (currentMode != newMode) {
                     // Changing the mode will have the side-effect of also changing
                     // the power and data roles but it might take some time to apply
@@ -485,44 +591,37 @@
                     logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: "
                             + "portId=" + portId
                             + ", newMode=" + UsbPort.modeToString(newMode));
-                    PortRole newRole = new PortRole();
-                    newRole.type = PortRoleType.MODE;
-                    newRole.role = newMode;
                     try {
-                        mProxy.switchRole(portId, newRole);
-                    } catch (RemoteException e) {
+                        mUsbPortHal.switchMode(portId, toHalUsbMode(newMode), ++mTransactionId);
+                    } catch (Exception e) {
                         logAndPrintException(pw, "Failed to set the USB port mode: "
                                 + "portId=" + portId
-                                + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+                                + ", newMode=" + UsbPort.modeToString(newMode), e);
                     }
                 } else {
                     // Change power and data role independently as needed.
                     if (currentPowerRole != newPowerRole) {
-                        PortRole newRole = new PortRole();
-                        newRole.type = PortRoleType.POWER_ROLE;
-                        newRole.role = newPowerRole;
                         try {
-                            mProxy.switchRole(portId, newRole);
-                        } catch (RemoteException e) {
+                            mUsbPortHal.switchPowerRole(portId, toHalUsbPowerRole(newPowerRole),
+                                    ++mTransactionId);
+                        } catch (Exception e) {
                             logAndPrintException(pw, "Failed to set the USB port power role: "
                                             + "portId=" + portId
                                             + ", newPowerRole=" + UsbPort.powerRoleToString
-                                            (newRole.role),
+                                            (newPowerRole),
                                     e);
                             return;
                         }
                     }
                     if (currentDataRole != newDataRole) {
-                        PortRole newRole = new PortRole();
-                        newRole.type = PortRoleType.DATA_ROLE;
-                        newRole.role = newDataRole;
                         try {
-                            mProxy.switchRole(portId, newRole);
-                        } catch (RemoteException e) {
+                            mUsbPortHal.switchDataRole(portId, toHalUsbDataRole(newDataRole),
+                                    ++mTransactionId);
+                        } catch (Exception e) {
                             logAndPrintException(pw, "Failed to set the USB port data role: "
                                             + "portId=" + portId
-                                            + ", newDataRole=" + UsbPort.dataRoleToString(newRole
-                                            .role),
+                                            + ", newDataRole=" + UsbPort.dataRoleToString
+                                            (newDataRole),
                                     e);
                         }
                     }
@@ -531,6 +630,15 @@
         }
     }
 
+    public void updatePorts(ArrayList<RawPortInfo> newPortInfo) {
+        Message message = mHandler.obtainMessage();
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
+        message.what = MSG_UPDATE_PORTS;
+        message.setData(bundle);
+        mHandler.sendMessage(message);
+    }
+
     public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
         synchronized (mLock) {
             if (mSimulatedPorts.containsKey(portId)) {
@@ -662,191 +770,12 @@
                 portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS);
             }
 
-            dump.write("enable_usb_data_signaling", UsbPortManagerProto.ENABLE_USB_DATA_SIGNALING,
-                    mEnableUsbDataSignaling);
+            dump.write("usb_hal_version", UsbPortManagerProto.HAL_VERSION, getUsbHalVersion());
         }
 
         dump.end(token);
     }
 
-    private static class HALCallback extends IUsbCallback.Stub {
-        public IndentingPrintWriter pw;
-        public UsbPortManager portManager;
-
-        HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) {
-            this.pw = pw;
-            this.portManager = portManager;
-        }
-
-        public void notifyPortStatusChange(
-                ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
-            if (!portManager.mSystemReady) {
-                return;
-            }
-
-            if (retval != Status.SUCCESS) {
-                logAndPrint(Log.ERROR, pw, "port status enquiry failed");
-                return;
-            }
-
-            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
-            for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
-                RawPortInfo temp = new RawPortInfo(current.portName,
-                        current.supportedModes, CONTAMINANT_PROTECTION_NONE,
-                        current.currentMode,
-                        current.canChangeMode, current.currentPowerRole,
-                        current.canChangePowerRole,
-                        current.currentDataRole, current.canChangeDataRole,
-                        false, CONTAMINANT_PROTECTION_NONE,
-                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
-                newPortInfo.add(temp);
-                logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName);
-            }
-
-            Message message = portManager.mHandler.obtainMessage();
-            Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
-            message.what = MSG_UPDATE_PORTS;
-            message.setData(bundle);
-            portManager.mHandler.sendMessage(message);
-        }
-
-
-        public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
-                int retval) {
-            if (!portManager.mSystemReady) {
-                return;
-            }
-
-            if (retval != Status.SUCCESS) {
-                logAndPrint(Log.ERROR, pw, "port status enquiry failed");
-                return;
-            }
-
-            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
-            int numStatus = currentPortStatus.size();
-            for (int i = 0; i < numStatus; i++) {
-                PortStatus_1_1 current = currentPortStatus.get(i);
-                RawPortInfo temp = new RawPortInfo(current.status.portName,
-                        current.supportedModes, CONTAMINANT_PROTECTION_NONE,
-                        current.currentMode,
-                        current.status.canChangeMode, current.status.currentPowerRole,
-                        current.status.canChangePowerRole,
-                        current.status.currentDataRole, current.status.canChangeDataRole,
-                        false, CONTAMINANT_PROTECTION_NONE,
-                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
-                newPortInfo.add(temp);
-                logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName);
-            }
-
-            Message message = portManager.mHandler.obtainMessage();
-            Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
-            message.what = MSG_UPDATE_PORTS;
-            message.setData(bundle);
-            portManager.mHandler.sendMessage(message);
-        }
-
-        public void notifyPortStatusChange_1_2(
-                ArrayList<PortStatus> currentPortStatus, int retval) {
-            if (!portManager.mSystemReady) {
-                return;
-            }
-
-            if (retval != Status.SUCCESS) {
-                logAndPrint(Log.ERROR, pw, "port status enquiry failed");
-                return;
-            }
-
-            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
-            int numStatus = currentPortStatus.size();
-            for (int i = 0; i < numStatus; i++) {
-                PortStatus current = currentPortStatus.get(i);
-                RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
-                        current.status_1_1.supportedModes,
-                        current.supportedContaminantProtectionModes,
-                        current.status_1_1.currentMode,
-                        current.status_1_1.status.canChangeMode,
-                        current.status_1_1.status.currentPowerRole,
-                        current.status_1_1.status.canChangePowerRole,
-                        current.status_1_1.status.currentDataRole,
-                        current.status_1_1.status.canChangeDataRole,
-                        current.supportsEnableContaminantPresenceProtection,
-                        current.contaminantProtectionStatus,
-                        current.supportsEnableContaminantPresenceDetection,
-                        current.contaminantDetectionStatus);
-                newPortInfo.add(temp);
-                logAndPrint(Log.INFO, pw, "ClientCallback V1_2: "
-                        + current.status_1_1.status.portName);
-            }
-
-            Message message = portManager.mHandler.obtainMessage();
-            Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
-            message.what = MSG_UPDATE_PORTS;
-            message.setData(bundle);
-            portManager.mHandler.sendMessage(message);
-        }
-
-        public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
-            if (retval == Status.SUCCESS) {
-                logAndPrint(Log.INFO, pw, portName + " role switch successful");
-            } else {
-                logAndPrint(Log.ERROR, pw, portName + " role switch failed");
-            }
-        }
-    }
-
-    final class DeathRecipient implements HwBinder.DeathRecipient {
-        public IndentingPrintWriter pw;
-
-        DeathRecipient(IndentingPrintWriter pw) {
-            this.pw = pw;
-        }
-
-        @Override
-        public void serviceDied(long cookie) {
-            if (cookie == USB_HAL_DEATH_COOKIE) {
-                logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
-                synchronized (mLock) {
-                    mProxy = null;
-                }
-            }
-        }
-    }
-
-    final class ServiceNotification extends IServiceNotification.Stub {
-        @Override
-        public void onRegistration(String fqName, String name, boolean preexisting) {
-            logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
-            connectToProxy(null);
-        }
-    }
-
-    private void connectToProxy(IndentingPrintWriter pw) {
-        synchronized (mLock) {
-            if (mProxy != null) {
-                return;
-            }
-
-            try {
-                mProxy = IUsb.getService();
-                mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
-                mProxy.setCallback(mHALCallback);
-                mProxy.queryPortStatus();
-                updateUsbHalVersion();
-            } catch (NoSuchElementException e) {
-                logAndPrintException(pw, "connectToProxy: usb hal service not found."
-                        + " Did the service fail to start?", e);
-            } catch (RemoteException e) {
-                logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
-            }
-        }
-    }
-
     /**
      * Simulated ports directly add the new roles to mSimulatedPorts before calling.
      * USB hal callback populates and sends the newPortInfo.
@@ -869,7 +798,10 @@
                         portInfo.supportsEnableContaminantPresenceProtection,
                         portInfo.contaminantProtectionStatus,
                         portInfo.supportsEnableContaminantPresenceDetection,
-                        portInfo.contaminantDetectionStatus, pw);
+                        portInfo.contaminantDetectionStatus,
+                        portInfo.usbDataStatus,
+                        portInfo.powerTransferLimited,
+                        portInfo.powerBrickStatus, pw);
             }
         } else {
             for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -881,7 +813,10 @@
                         currentPortInfo.supportsEnableContaminantPresenceProtection,
                         currentPortInfo.contaminantProtectionStatus,
                         currentPortInfo.supportsEnableContaminantPresenceDetection,
-                        currentPortInfo.contaminantDetectionStatus, pw);
+                        currentPortInfo.contaminantDetectionStatus,
+                        currentPortInfo.usbDataStatus,
+                        currentPortInfo.powerTransferLimited,
+                        currentPortInfo.powerBrickStatus, pw);
             }
         }
 
@@ -917,6 +852,9 @@
             int contaminantProtectionStatus,
             boolean supportsEnableContaminantPresenceDetection,
             int contaminantDetectionStatus,
+            int[] usbDataStatus,
+            boolean powerTransferLimited,
+            int powerBrickStatus,
             IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
@@ -975,7 +913,8 @@
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
-                    contaminantDetectionStatus);
+                    contaminantDetectionStatus, usbDataStatus,
+                    powerTransferLimited, powerBrickStatus);
             mPorts.put(portId, portInfo);
         } else {
             // Validate that ports aren't changing definition out from under us.
@@ -1012,7 +951,8 @@
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
-                    contaminantDetectionStatus)) {
+                    contaminantDetectionStatus, usbDataStatus,
+                    powerTransferLimited, powerBrickStatus)) {
                 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
             } else {
                 portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1034,6 +974,7 @@
     private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
         logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
         enableContaminantDetectionIfNeeded(portInfo, pw);
+        disableLimitPowerTransferIfNeeded(portInfo, pw);
         handlePortLocked(portInfo, pw);
     }
 
@@ -1090,6 +1031,19 @@
         }
     }
 
+    private void disableLimitPowerTransferIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) {
+        if (!mConnected.containsKey(portInfo.mUsbPort.getId())) {
+            return;
+        }
+
+        if (mConnected.get(portInfo.mUsbPort.getId())
+                && !portInfo.mUsbPortStatus.isConnected()
+                && portInfo.mUsbPortStatus.isPowerTransferLimited()) {
+            // Relax enableLimitPowerTransfer upon unplug.
+            enableLimitPowerTransfer(portInfo.mUsbPort.getId(), false, ++mTransactionId, null, pw);
+        }
+    }
+
     private void logToStatsd(PortInfo portInfo, IndentingPrintWriter pw) {
         // Port is removed
         if (portInfo.mUsbPortStatus == null) {
@@ -1141,14 +1095,14 @@
         }
     }
 
-    private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
+    public static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
         Slog.println(priority, TAG, msg);
         if (pw != null) {
             pw.println(msg);
         }
     }
 
-    private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
+    public static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
         Slog.e(TAG, msg, e);
         if (pw != null) {
             pw.println(msg + e);
@@ -1179,7 +1133,7 @@
     /**
      * Describes a USB port.
      */
-    private static final class PortInfo {
+    public static final class PortInfo {
         public static final int DISPOSITION_ADDED = 0;
         public static final int DISPOSITION_CHANGED = 1;
         public static final int DISPOSITION_READY = 2;
@@ -1224,7 +1178,9 @@
                     != supportedRoleCombinations) {
                 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
                         supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
-                        UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED);
+                        UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED,
+                        new int[]{UsbPortStatus.USB_DATA_STATUS_UNKNOWN}, false,
+                        UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN);
                 dispositionChanged = true;
             }
 
@@ -1239,11 +1195,31 @@
             return dispositionChanged;
         }
 
+        private boolean dataStatusEquals(int[] dataStatusL, int[] dataStatusR) {
+            if (dataStatusL == null && dataStatusR == null) {
+                return true;
+            }
+            if ((dataStatusL == null && dataStatusR != null)
+                || (dataStatusL != null && dataStatusR == null)) {
+                return false;
+            }
+            if (dataStatusL.length != dataStatusR.length) {
+                return false;
+            }
+            for (int i = 0; i < dataStatusL.length; i++) {
+                if (dataStatusL[i] != dataStatusR[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
         public boolean setStatus(int currentMode, boolean canChangeMode,
                 int currentPowerRole, boolean canChangePowerRole,
                 int currentDataRole, boolean canChangeDataRole,
                 int supportedRoleCombinations, int contaminantProtectionStatus,
-                int contaminantDetectionStatus) {
+                int contaminantDetectionStatus, int[] usbDataStatus,
+                boolean powerTransferLimited, int powerBrickStatus) {
             boolean dispositionChanged = false;
 
             mCanChangeMode = canChangeMode;
@@ -1258,10 +1234,16 @@
                     || mUsbPortStatus.getContaminantProtectionStatus()
                     != contaminantProtectionStatus
                     || mUsbPortStatus.getContaminantDetectionStatus()
-                    != contaminantDetectionStatus) {
+                    != contaminantDetectionStatus
+                    || !dataStatusEquals(mUsbPortStatus.getUsbDataStatus(), usbDataStatus)
+                    || mUsbPortStatus.isPowerTransferLimited()
+                    != powerTransferLimited
+                    || mUsbPortStatus.getPowerBrickStatus()
+                    != powerBrickStatus) {
                 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
                         supportedRoleCombinations, contaminantProtectionStatus,
-                        contaminantDetectionStatus);
+                        contaminantDetectionStatus, usbDataStatus,
+                        powerTransferLimited, powerBrickStatus);
                 dispositionChanged = true;
             }
 
@@ -1290,7 +1272,6 @@
                     UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
             dump.write("last_connect_duration_millis",
                     UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
-
             dump.end(token);
         }
 
@@ -1304,115 +1285,4 @@
                     + ", lastConnectDurationMillis=" + mLastConnectDurationMillis;
         }
     }
-
-    /**
-     * Used for storing the raw data from the kernel
-     * Values of the member variables mocked directly incase of emulation.
-     */
-    private static final class RawPortInfo implements Parcelable {
-        public final String portId;
-        public final int supportedModes;
-        public final int supportedContaminantProtectionModes;
-        public int currentMode;
-        public boolean canChangeMode;
-        public int currentPowerRole;
-        public boolean canChangePowerRole;
-        public int currentDataRole;
-        public boolean canChangeDataRole;
-        public boolean supportsEnableContaminantPresenceProtection;
-        public int contaminantProtectionStatus;
-        public boolean supportsEnableContaminantPresenceDetection;
-        public int contaminantDetectionStatus;
-
-        RawPortInfo(String portId, int supportedModes) {
-            this.portId = portId;
-            this.supportedModes = supportedModes;
-            this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
-            this.supportsEnableContaminantPresenceProtection = false;
-            this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
-            this.supportsEnableContaminantPresenceDetection = false;
-            this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
-        }
-
-        RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
-                int currentMode, boolean canChangeMode,
-                int currentPowerRole, boolean canChangePowerRole,
-                int currentDataRole, boolean canChangeDataRole,
-                boolean supportsEnableContaminantPresenceProtection,
-                int contaminantProtectionStatus,
-                boolean supportsEnableContaminantPresenceDetection,
-                int contaminantDetectionStatus) {
-            this.portId = portId;
-            this.supportedModes = supportedModes;
-            this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
-            this.currentMode = currentMode;
-            this.canChangeMode = canChangeMode;
-            this.currentPowerRole = currentPowerRole;
-            this.canChangePowerRole = canChangePowerRole;
-            this.currentDataRole = currentDataRole;
-            this.canChangeDataRole = canChangeDataRole;
-            this.supportsEnableContaminantPresenceProtection =
-                    supportsEnableContaminantPresenceProtection;
-            this.contaminantProtectionStatus = contaminantProtectionStatus;
-            this.supportsEnableContaminantPresenceDetection =
-                    supportsEnableContaminantPresenceDetection;
-            this.contaminantDetectionStatus = contaminantDetectionStatus;
-        }
-
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(portId);
-            dest.writeInt(supportedModes);
-            dest.writeInt(supportedContaminantProtectionModes);
-            dest.writeInt(currentMode);
-            dest.writeByte((byte) (canChangeMode ? 1 : 0));
-            dest.writeInt(currentPowerRole);
-            dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
-            dest.writeInt(currentDataRole);
-            dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
-            dest.writeBoolean(supportsEnableContaminantPresenceProtection);
-            dest.writeInt(contaminantProtectionStatus);
-            dest.writeBoolean(supportsEnableContaminantPresenceDetection);
-            dest.writeInt(contaminantDetectionStatus);
-        }
-
-        public static final Parcelable.Creator<RawPortInfo> CREATOR =
-                new Parcelable.Creator<RawPortInfo>() {
-            @Override
-            public RawPortInfo createFromParcel(Parcel in) {
-                String id = in.readString();
-                int supportedModes = in.readInt();
-                int supportedContaminantProtectionModes = in.readInt();
-                int currentMode = in.readInt();
-                boolean canChangeMode = in.readByte() != 0;
-                int currentPowerRole = in.readInt();
-                boolean canChangePowerRole = in.readByte() != 0;
-                int currentDataRole = in.readInt();
-                boolean canChangeDataRole = in.readByte() != 0;
-                boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
-                int contaminantProtectionStatus = in.readInt();
-                boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
-                int contaminantDetectionStatus = in.readInt();
-                return new RawPortInfo(id, supportedModes,
-                        supportedContaminantProtectionModes, currentMode, canChangeMode,
-                        currentPowerRole, canChangePowerRole,
-                        currentDataRole, canChangeDataRole,
-                        supportsEnableContaminantPresenceProtection,
-                        contaminantProtectionStatus,
-                        supportsEnableContaminantPresenceDetection,
-                        contaminantDetectionStatus);
-            }
-
-            @Override
-            public RawPortInfo[] newArray(int size) {
-                return new RawPortInfo[size];
-            }
-        };
-    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 3d3538d..88ffc7d61 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
 import static android.hardware.usb.UsbPortStatus.MODE_DFP;
@@ -35,6 +36,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.hardware.usb.IUsbManager;
+import android.hardware.usb.IUsbOperationInternal;
 import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
@@ -44,6 +46,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.usb.UsbServiceDumpProto;
@@ -731,6 +734,28 @@
     }
 
     @Override
+    public void enableLimitPowerTransfer(String portId, boolean limit, int operationId,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(portId, "portId must not be null. opID:" + operationId);
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (mPortManager != null) {
+                mPortManager.enableLimitPowerTransfer(portId, limit, operationId, callback, null);
+            } else {
+                try {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "enableLimitPowerTransfer: Failed to call onOperationComplete", e);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public void enableContaminantDetection(String portId, boolean enable) {
         Objects.requireNonNull(portId, "portId must not be null");
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -762,15 +787,52 @@
     }
 
     @Override
-    public boolean enableUsbDataSignal(boolean enable) {
+    public boolean enableUsbData(String portId, boolean enable, int operationId,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(portId, "enableUsbData: portId must not be null. opId:"
+                + operationId);
+        Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+                + operationId);
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-
         final long ident = Binder.clearCallingIdentity();
+        boolean wait;
         try {
             if (mPortManager != null) {
-                return mPortManager.enableUsbDataSignal(enable);
+                wait = mPortManager.enableUsbData(portId, enable, operationId, callback, null);
             } else {
-                return false;
+                wait = false;
+                try {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return wait;
+    }
+
+    @Override
+    public void enableUsbDataWhileDocked(String portId, int operationId,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(portId, "enableUsbDataWhileDocked: portId must not be null. opId:"
+                + operationId);
+        Objects.requireNonNull(callback,
+                "enableUsbDataWhileDocked: callback must not be null. opId:"
+                + operationId);
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        final long ident = Binder.clearCallingIdentity();
+        boolean wait;
+        try {
+            if (mPortManager != null) {
+                mPortManager.enableUsbDataWhileDocked(portId, operationId, callback, null);
+            } else {
+                try {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
new file mode 100644
index 0000000..db0c80f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbManager;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.midi.MidiEventScheduler;
+import com.android.internal.midi.MidiEventScheduler.MidiEvent;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.android.server.usb.descriptors.UsbEndpointDescriptor;
+import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
+import com.android.server.usb.descriptors.UsbMidiBlockParser;
+
+import libcore.io.IoUtils;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ */
+public final class UsbUniversalMidiDevice implements Closeable {
+    private static final String TAG = "UsbUniversalMidiDevice";
+    private static final boolean DEBUG = false;
+
+    private Context mContext;
+    private UsbDevice mUsbDevice;
+    private UsbDescriptorParser mParser;
+    private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+
+    // USB outputs are MIDI inputs
+    private final InputReceiverProxy[] mMidiInputPortReceivers;
+    private final int mNumInputs;
+    private final int mNumOutputs;
+
+    private MidiDeviceServer mServer;
+
+    // event schedulers for each input port of the physical device
+    private MidiEventScheduler[] mEventSchedulers;
+
+    private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
+    private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
+    private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
+
+    private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser();
+    private int mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
+
+    private final Object mLock = new Object();
+    private boolean mIsOpen;
+
+    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
+
+        @Override
+        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+            MidiDeviceInfo deviceInfo = status.getDeviceInfo();
+            int numInputPorts = deviceInfo.getInputPortCount();
+            int numOutputPorts = deviceInfo.getOutputPortCount();
+            boolean hasOpenPorts = false;
+
+            for (int i = 0; i < numInputPorts; i++) {
+                if (status.isInputPortOpen(i)) {
+                    hasOpenPorts = true;
+                    break;
+                }
+            }
+
+            if (!hasOpenPorts) {
+                for (int i = 0; i < numOutputPorts; i++) {
+                    if (status.getOutputPortOpenCount(i) > 0) {
+                        hasOpenPorts = true;
+                        break;
+                    }
+                }
+            }
+
+            synchronized (mLock) {
+                if (hasOpenPorts && !mIsOpen) {
+                    openLocked();
+                } else if (!hasOpenPorts && mIsOpen) {
+                    closeLocked();
+                }
+            }
+        }
+
+        @Override
+        public void onClose() {
+        }
+    };
+
+    // This class acts as a proxy for our MidiEventScheduler receivers, which do not exist
+    // until the device has active clients
+    private static final class InputReceiverProxy extends MidiReceiver {
+        private MidiReceiver mReceiver;
+
+        @Override
+        public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+            MidiReceiver receiver = mReceiver;
+            if (receiver != null) {
+                receiver.send(msg, offset, count, timestamp);
+            }
+        }
+
+        public void setReceiver(MidiReceiver receiver) {
+            mReceiver = receiver;
+        }
+
+        @Override
+        public void onFlush() throws IOException {
+            MidiReceiver receiver = mReceiver;
+            if (receiver != null) {
+                receiver.flush();
+            }
+        }
+    }
+
+    /**
+     * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+     * will be created individually as some devices don't have the same number of
+     * inputs and outputs.
+     */
+    public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
+            UsbDescriptorParser parser) {
+        UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+        if (!midiDevice.register(context)) {
+            IoUtils.closeQuietly(midiDevice);
+            Log.e(TAG, "createDeviceServer failed");
+            return null;
+        }
+        return midiDevice;
+    }
+
+    private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+        mUsbDevice = usbDevice;
+        mParser = parser;
+
+        mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+
+        int numInputs = 0;
+        int numOutputs = 0;
+
+        for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
+            UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
+            for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
+                    endpointIndex++) {
+                UsbEndpointDescriptor endpoint =
+                        interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+                // 0 is output, 1 << 7 is input.
+                if (endpoint.getDirection() == 0) {
+                    numOutputs++;
+                } else {
+                    numInputs++;
+                }
+            }
+        }
+
+        mNumInputs = numInputs;
+        mNumOutputs = numOutputs;
+
+        Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
+                + numOutputs + " outputs");
+
+        // Create MIDI port receivers based on the number of output ports. The
+        // output of USB is the input of MIDI.
+        mMidiInputPortReceivers = new InputReceiverProxy[numOutputs];
+        for (int port = 0; port < numOutputs; port++) {
+            mMidiInputPortReceivers[port] = new InputReceiverProxy();
+        }
+    }
+
+    private int calculateDefaultMidiProtocol() {
+        UsbManager manager = mContext.getSystemService(UsbManager.class);
+
+        for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
+            UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
+            boolean doesInterfaceContainInput = false;
+            boolean doesInterfaceContainOutput = false;
+            for (int endpointIndex = 0; (endpointIndex < interfaceDescriptor.getNumEndpoints())
+                    && !(doesInterfaceContainInput && doesInterfaceContainOutput);
+                    endpointIndex++) {
+                UsbEndpointDescriptor endpoint =
+                        interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+                // 0 is output, 1 << 7 is input.
+                if (endpoint.getDirection() == 0) {
+                    doesInterfaceContainOutput = true;
+                } else {
+                    doesInterfaceContainInput = true;
+                }
+            }
+
+            // Intentionally open the device connection to query the default MIDI type for
+            // a connection with both the input and output set.
+            if (doesInterfaceContainInput
+                    && doesInterfaceContainOutput) {
+                UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
+                if (!connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true)) {
+                    Log.d(TAG, "Can't claim control interface");
+                    continue;
+                }
+                int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
+                        interfaceDescriptor.getInterfaceNumber(),
+                        interfaceDescriptor.getAlternateSetting());
+
+                connection.close();
+                return defaultMidiProtocol;
+            }
+        }
+
+        Log.d(TAG, "Cannot find interface with both input and output endpoints");
+        return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
+    }
+
+    private boolean openLocked() {
+        UsbManager manager = mContext.getSystemService(UsbManager.class);
+
+        mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
+        mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
+        mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
+
+        for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
+            ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>();
+            ArrayList<UsbEndpoint> outputEndpoints = new ArrayList<UsbEndpoint>();
+            UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
+            for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
+                    endpointIndex++) {
+                UsbEndpointDescriptor endpoint =
+                        interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+                // 0 is output, 1 << 7 is input.
+                if (endpoint.getDirection() == 0) {
+                    outputEndpoints.add(endpoint.toAndroid(mParser));
+                } else {
+                    inputEndpoints.add(endpoint.toAndroid(mParser));
+                }
+            }
+            if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
+                UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
+                connection.setInterface(interfaceDescriptor.toAndroid(mParser));
+                connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+                mUsbDeviceConnections.add(connection);
+                mInputUsbEndpoints.add(inputEndpoints);
+                mOutputUsbEndpoints.add(outputEndpoints);
+            }
+        }
+
+        mEventSchedulers = new MidiEventScheduler[mNumOutputs];
+
+        for (int i = 0; i < mNumOutputs; i++) {
+            MidiEventScheduler scheduler = new MidiEventScheduler();
+            mEventSchedulers[i] = scheduler;
+            mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver());
+        }
+
+        final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
+
+        // Create input thread for each input port of the physical device
+        int portNumber = 0;
+        for (int connectionIndex = 0; connectionIndex < mInputUsbEndpoints.size();
+                connectionIndex++) {
+            for (int endpointIndex = 0;
+                    endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
+                    endpointIndex++) {
+                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+                final int portF = portNumber;
+
+                new Thread("UsbUniversalMidiDevice input thread " + portF) {
+                    @Override
+                    public void run() {
+                        byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+                        try {
+                            while (true) {
+                                // Record time of event immediately after waking.
+                                long timestamp = System.nanoTime();
+                                synchronized (mLock) {
+                                    if (!mIsOpen) break;
+
+                                    int nRead = connectionF.bulkTransfer(epF, inputBuffer,
+                                            inputBuffer.length, 0);
+
+                                    // For USB, each 32 bit word of a UMP is
+                                    // sent with the least significant byte first.
+                                    swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+
+                                    if (nRead > 0) {
+                                        if (DEBUG) {
+                                            logByteArray("Input ", inputBuffer, 0,
+                                                    nRead);
+                                        }
+                                        outputReceivers[portF].send(inputBuffer, 0, nRead,
+                                                timestamp);
+                                    }
+                                }
+                            }
+                        } catch (IOException e) {
+                            Log.d(TAG, "reader thread exiting");
+                        }
+                        Log.d(TAG, "input thread exit");
+                    }
+                }.start();
+
+                portNumber++;
+            }
+        }
+
+        // Create output thread for each output port of the physical device
+        portNumber = 0;
+        for (int connectionIndex = 0; connectionIndex < mOutputUsbEndpoints.size();
+                connectionIndex++) {
+            for (int endpointIndex = 0;
+                    endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
+                    endpointIndex++) {
+                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint epF =
+                        mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+                final int portF = portNumber;
+                final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+
+                new Thread("UsbUniversalMidiDevice output thread " + portF) {
+                    @Override
+                    public void run() {
+                        while (true) {
+                            MidiEvent event;
+                            try {
+                                event = (MidiEvent) eventSchedulerF.waitNextEvent();
+                            } catch (InterruptedException e) {
+                                // try again
+                                continue;
+                            }
+                            if (event == null) {
+                                break;
+                            }
+
+                            // For USB, each 32 bit word of a UMP is
+                            // sent with the least significant byte first.
+                            swapEndiannessPerWord(event.data, event.count);
+
+                            if (DEBUG) {
+                                logByteArray("Output ", event.data, 0,
+                                        event.count);
+                            }
+                            connectionF.bulkTransfer(epF, event.data, event.count, 0);
+                            eventSchedulerF.addEventToPool(event);
+                        }
+                        Log.d(TAG, "output thread exit");
+                    }
+                }.start();
+
+                portNumber++;
+            }
+        }
+
+        mIsOpen = true;
+        return true;
+    }
+
+    private boolean register(Context context) {
+        mContext = context;
+        MidiManager midiManager = context.getSystemService(MidiManager.class);
+        if (midiManager == null) {
+            Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+            return false;
+        }
+
+        mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+
+        Bundle properties = new Bundle();
+        String manufacturer = mUsbDevice.getManufacturerName();
+        String product = mUsbDevice.getProductName();
+        String version = mUsbDevice.getVersion();
+        String name;
+        if (manufacturer == null || manufacturer.isEmpty()) {
+            name = product;
+        } else if (product == null || product.isEmpty()) {
+            name = manufacturer;
+        } else {
+            name = manufacturer + " " + product + " MIDI 2.0";
+        }
+        properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
+        properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
+        properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
+        properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
+        properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+                mUsbDevice.getSerialNumber());
+        properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, mUsbDevice);
+
+        mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs,
+                null, null, properties, MidiDeviceInfo.TYPE_USB, mDefaultMidiProtocol, mCallback);
+        if (mServer == null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void close() throws IOException {
+        synchronized (mLock) {
+            if (mIsOpen) {
+                closeLocked();
+            }
+        }
+
+        if (mServer != null) {
+            IoUtils.closeQuietly(mServer);
+        }
+    }
+
+    private void closeLocked() {
+        for (int i = 0; i < mEventSchedulers.length; i++) {
+            mMidiInputPortReceivers[i].setReceiver(null);
+            mEventSchedulers[i].close();
+        }
+        for (UsbDeviceConnection connection : mUsbDeviceConnections) {
+            connection.close();
+        }
+        mUsbDeviceConnections = null;
+        mInputUsbEndpoints = null;
+        mOutputUsbEndpoints = null;
+
+        mIsOpen = false;
+    }
+
+    private void swapEndiannessPerWord(byte[] array, int size) {
+        for (int i = 0; i + 3 < size; i += 4) {
+            byte tmp = array[i];
+            array[i] = array[i + 3];
+            array[i + 3] = tmp;
+            tmp = array[i + 1];
+            array[i + 1] = array[i + 2];
+            array[i + 2] = tmp;
+        }
+    }
+
+    private static void logByteArray(String prefix, byte[] value, int offset, int count) {
+        StringBuilder builder = new StringBuilder(prefix);
+        for (int i = offset; i < offset + count; i++) {
+            builder.append(String.format("0x%02X", value[i]));
+            if (i != value.length - 1) {
+                builder.append(", ");
+            }
+        }
+        Log.d(TAG, builder.toString());
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 0c65cc4..286cff9 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -688,6 +688,8 @@
             String packageName,
             PendingIntent pi,
             int uid) {
+        boolean throwException = false;
+
         // compare uid with packageName to foil apps pretending to be someone else
         try {
             ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
@@ -695,11 +697,13 @@
                 Slog.w(TAG, "package " + packageName
                         + " does not match caller's uid " + uid);
                 EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, "");
-                throw new IllegalArgumentException("package " + packageName
-                        + " not found");
+                throwException = true;
             }
         } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalArgumentException("package " + packageName + " not found");
+            throwException = true;
+        } finally {
+            if (throwException)
+                throw new IllegalArgumentException("package " + packageName + " not found");
         }
 
         requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
index 409e605c..bfcf621 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
@@ -38,8 +38,8 @@
     static final byte ATTRIBSMASK_SYNC  = 0x0C;
     static final byte ATTRIBMASK_TRANS  = 0x03;
 
-    public UsbACAudioControlEndpoint(int length, byte type, int subclass) {
-        super(length, type, subclass);
+    public UsbACAudioControlEndpoint(int length, byte type, int subclass, byte subtype) {
+        super(length, type, subclass, subtype);
     }
 
     public byte getAddress() {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
index e63bb74..ae9ca0d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
@@ -24,8 +24,8 @@
     private static final String TAG = "UsbACAudioStreamEndpoint";
 
     //TODO data fields...
-    public UsbACAudioStreamEndpoint(int length, byte type, int subclass) {
-        super(length, type, subclass);
+    public UsbACAudioStreamEndpoint(int length, byte type, int subclass, byte subtype) {
+        super(length, type, subclass, subtype);
     }
 
     @Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index ff7f393..b7f9ac3 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -25,13 +25,17 @@
 abstract class UsbACEndpoint extends UsbDescriptor {
     private static final String TAG = "UsbACEndpoint";
 
+    public static final byte MS_GENERAL = 1;
+    public static final byte MS_GENERAL_2_0 = 2;
+
     protected final int mSubclass; // from the mSubclass member of the "enclosing"
                                    // Interface Descriptor, not the stream.
-    protected byte mSubtype;       // 2:1 HEADER descriptor subtype
+    protected final byte mSubtype;       // 2:1 HEADER descriptor subtype
 
-    UsbACEndpoint(int length, byte type, int subclass) {
+    UsbACEndpoint(int length, byte type, int subclass, byte subtype) {
         super(length, type);
         mSubclass = subclass;
+        mSubtype = subtype;
     }
 
     public int getSubclass() {
@@ -44,33 +48,39 @@
 
     @Override
     public int parseRawDescriptors(ByteStream stream) {
-        mSubtype = stream.getByte();
         return mLength;
     }
 
     public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
-                                                int length, byte type) {
+                                                int length, byte type, byte subType) {
         UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
         int subClass = interfaceDesc.getUsbSubclass();
-        // TODO shouldn't this switch on subtype?
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
                 if (UsbDescriptorParser.DEBUG) {
                     Log.d(TAG, "---> AUDIO_AUDIOCONTROL");
                 }
-                return new UsbACAudioControlEndpoint(length, type, subClass);
+                return new UsbACAudioControlEndpoint(length, type, subClass, subType);
 
             case AUDIO_AUDIOSTREAMING:
                 if (UsbDescriptorParser.DEBUG) {
                     Log.d(TAG, "---> AUDIO_AUDIOSTREAMING");
                 }
-                return new UsbACAudioStreamEndpoint(length, type, subClass);
+                return new UsbACAudioStreamEndpoint(length, type, subClass, subType);
 
             case AUDIO_MIDISTREAMING:
                 if (UsbDescriptorParser.DEBUG) {
                     Log.d(TAG, "---> AUDIO_MIDISTREAMING");
                 }
-                return new UsbACMidiEndpoint(length, type, subClass);
+                switch (subType) {
+                    case MS_GENERAL:
+                        return new UsbACMidi10Endpoint(length, type, subClass, subType);
+                    case MS_GENERAL_2_0:
+                        return new UsbACMidi20Endpoint(length, type, subClass, subType);
+                    default:
+                        Log.w(TAG, "Unknown Midi Endpoint id:0x" + Integer.toHexString(subType));
+                        return null;
+                }
 
             default:
                 Log.w(TAG, "Unknown Audio Class Endpoint id:0x" + Integer.toHexString(subClass));
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
new file mode 100644
index 0000000..49b9d7b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An audio class-specific Midi Endpoint.
+ * see midi10.pdf section 6.2.2
+ */
+public final class UsbACMidi10Endpoint extends UsbACEndpoint {
+    private static final String TAG = "UsbACMidi10Endpoint";
+
+    private byte mNumJacks;
+    private byte[] mJackIds = new byte[0];
+
+    public UsbACMidi10Endpoint(int length, byte type, int subclass, byte subtype) {
+        super(length, type, subclass, subtype);
+    }
+
+    public byte getNumJacks() {
+        return mNumJacks;
+    }
+
+    public byte[] getJackIds() {
+        return mJackIds;
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        super.parseRawDescriptors(stream);
+
+        mNumJacks = stream.getByte();
+        if (mNumJacks > 0) {
+            mJackIds = new byte[mNumJacks];
+            for (int jack = 0; jack < mNumJacks; jack++) {
+                mJackIds[jack] = stream.getByte();
+            }
+        }
+        return mLength;
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+
+        canvas.writeHeader(3, "ACMidi10Endpoint: " + ReportCanvas.getHexString(getType())
+                + " Length: " + getLength());
+        canvas.openList();
+        canvas.writeListItem("" + getNumJacks() + " Jacks.");
+        canvas.closeList();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
new file mode 100644
index 0000000..1024a5b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An audio class-specific Midi Endpoint.
+ * see midi10.pdf section 6.2.2
+ */
+public final class UsbACMidi20Endpoint extends UsbACEndpoint {
+    private static final String TAG = "UsbACMidi20Endpoint";
+
+    private byte mNumGroupTerminals;
+    private byte[] mBlockIds = new byte[0];
+
+    public UsbACMidi20Endpoint(int length, byte type, int subclass, byte subtype) {
+        super(length, type, subclass, subtype);
+    }
+
+    public byte getNumGroupTerminals() {
+        return mNumGroupTerminals;
+    }
+
+    public byte[] getBlockIds() {
+        return mBlockIds;
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        super.parseRawDescriptors(stream);
+
+        mNumGroupTerminals = stream.getByte();
+        if (mNumGroupTerminals > 0) {
+            mBlockIds = new byte[mNumGroupTerminals];
+            for (int block = 0; block < mNumGroupTerminals; block++) {
+                mBlockIds[block] = stream.getByte();
+            }
+        }
+        return mLength;
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+
+        canvas.writeHeader(3, "AC Midi20 Endpoint: " + ReportCanvas.getHexString(getType())
+                + " Length: " + getLength());
+        canvas.openList();
+        canvas.writeListItem("" + getNumGroupTerminals() + " Group Terminals.");
+        canvas.closeList();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
deleted file mode 100644
index 42ee889..0000000
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.usb.descriptors;
-
-import com.android.server.usb.descriptors.report.ReportCanvas;
-
-/**
- * @hide
- * An audio class-specific Midi Endpoint.
- * see midi10.pdf section 6.2.2
- */
-public final class UsbACMidiEndpoint extends UsbACEndpoint {
-    private static final String TAG = "UsbACMidiEndpoint";
-
-    private byte mNumJacks;
-    private byte[] mJackIds;
-
-    public UsbACMidiEndpoint(int length, byte type, int subclass) {
-        super(length, type, subclass);
-    }
-
-    public byte getNumJacks() {
-        return mNumJacks;
-    }
-
-    public byte[] getJackIds() {
-        return mJackIds;
-    }
-
-    @Override
-    public int parseRawDescriptors(ByteStream stream) {
-        super.parseRawDescriptors(stream);
-
-        mNumJacks = stream.getByte();
-        mJackIds = new byte[mNumJacks];
-        for (int jack = 0; jack < mNumJacks; jack++) {
-            mJackIds[jack] = stream.getByte();
-        }
-        return mLength;
-    }
-
-    @Override
-    public void report(ReportCanvas canvas) {
-        super.report(canvas);
-
-        canvas.writeHeader(3, "AC Midi Endpoint: " + ReportCanvas.getHexString(getType())
-                + " Length: " + getLength());
-        canvas.openList();
-        canvas.writeListItem("" + getNumJacks() + " Jacks.");
-        canvas.closeList();
-    }
-}
\ No newline at end of file
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 7250a07..6e68a91 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -30,6 +30,9 @@
 
     private final String mDeviceAddr;
 
+    private static final int MS_MIDI_1_0 = 0x0100;
+    private static final int MS_MIDI_2_0 = 0x0200;
+
     // Descriptor Objects
     private static final int DESCRIPTORS_ALLOC_SIZE = 128;
     private final ArrayList<UsbDescriptor> mDescriptors;
@@ -215,6 +218,7 @@
                             Log.w(TAG, "  Unparsed Class-specific");
                             break;
                     }
+                    mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
                 }
                 break;
 
@@ -222,17 +226,25 @@
                 if (mCurInterfaceDescriptor != null) {
                     int subClass = mCurInterfaceDescriptor.getUsbClass();
                     switch (subClass) {
-                        case UsbDescriptor.CLASSID_AUDIO:
-                            descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+                        case UsbDescriptor.CLASSID_AUDIO: {
+                            Byte subType = stream.getByte();
+                            if (DEBUG) {
+                                Log.d(TAG, "UsbDescriptor.CLASSID_AUDIO type:0x"
+                                        + Integer.toHexString(type));
+                            }
+                            descriptor = UsbACEndpoint.allocDescriptor(this, length, type,
+                                    subType);
+                        }
                             break;
 
                         case UsbDescriptor.CLASSID_VIDEO: {
-                            Byte subtype = stream.getByte();
+                            Byte subType = stream.getByte();
                             if (DEBUG) {
                                 Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x"
                                         + Integer.toHexString(type));
                             }
-                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, subtype);
+                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type,
+                                    subType);
                         }
                             break;
 
@@ -644,8 +656,8 @@
         for (UsbDescriptor descriptor : descriptors) {
             // enusure that this isn't an unrecognized interface descriptor
             if (descriptor instanceof UsbInterfaceDescriptor) {
-                UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
-                if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+                UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
+                if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
                     return true;
                 }
             } else {
@@ -656,17 +668,90 @@
         return false;
     }
 
-    private int calculateNumMidiPorts(boolean isOutput) {
+    /**
+     * @hide
+     */
+    public boolean containsUniversalMidiDeviceEndpoint() {
+        ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+                findUniversalMidiInterfaceDescriptors();
+        int outputCount = 0;
+        int inputCount = 0;
+        for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
+                interfaceIndex++) {
+            UsbInterfaceDescriptor interfaceDescriptor = interfaceDescriptors.get(interfaceIndex);
+            for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
+                    endpointIndex++) {
+                UsbEndpointDescriptor endpoint =
+                        interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+                // 0 is output, 1 << 7 is input.
+                if (endpoint.getDirection() == 0) {
+                    outputCount++;
+                } else {
+                    inputCount++;
+                }
+            }
+        }
+        return (outputCount > 0) || (inputCount > 0);
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+        int count = 0;
+        ArrayList<UsbDescriptor> descriptors =
+                getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+        ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+                new ArrayList<UsbInterfaceDescriptor>();
+
+        for (UsbDescriptor descriptor : descriptors) {
+            // ensure that this isn't an unrecognized interface descriptor
+            if (descriptor instanceof UsbInterfaceDescriptor) {
+                UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
+                if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+                    UsbDescriptor classSpecificDescriptor =
+                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
+                    if (classSpecificDescriptor != null) {
+                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                            UsbMSMidiHeader midiHeader =
+                                    (UsbMSMidiHeader) classSpecificDescriptor;
+                            if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
+                                universalMidiInterfaces.add(interfaceDescriptor);
+                            }
+                        }
+                    }
+                }
+            } else {
+                Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength()
+                        + " t:0x" + Integer.toHexString(descriptor.getType()));
+            }
+        }
+        return universalMidiInterfaces;
+    }
+
+    private int calculateNumLegacyMidiPorts(boolean isOutput) {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
         for (UsbDescriptor descriptor : descriptors) {
             // ensure that this isn't an unrecognized interface descriptor
             if (descriptor instanceof UsbInterfaceDescriptor) {
-                UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
-                if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    for (int i = 0; i < interfaceDescr.getNumEndpoints(); i++) {
-                        UsbEndpointDescriptor endpoint = interfaceDescr.getEndpointDescriptor(i);
+                UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
+                if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+                    UsbDescriptor classSpecificDescriptor =
+                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
+                    if (classSpecificDescriptor != null) {
+                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                            UsbMSMidiHeader midiHeader =
+                                    (UsbMSMidiHeader) classSpecificDescriptor;
+                            if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
+                                continue;
+                            }
+                        }
+                    }
+                    for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
+                        UsbEndpointDescriptor endpoint =
+                                interfaceDescriptor.getEndpointDescriptor(i);
                         // 0 is output, 1 << 7 is input.
                         if ((endpoint.getDirection() == 0) == isOutput) {
                             count++;
@@ -684,15 +769,15 @@
     /**
      * @hide
      */
-    public int calculateNumMidiInputs() {
-        return calculateNumMidiPorts(false /*isOutput*/);
+    public int calculateNumLegacyMidiInputs() {
+        return calculateNumLegacyMidiPorts(false /*isOutput*/);
     }
 
     /**
      * @hide
      */
-    public int calculateNumMidiOutputs() {
-        return calculateNumMidiPorts(true /*isOutput*/);
+    public int calculateNumLegacyMidiOutputs() {
+        return calculateNumLegacyMidiPorts(true /*isOutput*/);
     }
 
     /**
@@ -785,4 +870,35 @@
         return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
     }
 
+    /**
+     * isDock() indicates if the connected USB output peripheral is a docking station with
+     * audio output.
+     * A valid audio dock must declare only one audio output control terminal of type
+     * TERMINAL_EXTERN_DIGITAL.
+     */
+    public boolean isDock() {
+        if (hasMIDIInterface() || hasHIDInterface()) {
+            return false;
+        }
+
+        ArrayList<UsbDescriptor> acDescriptors =
+                getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+                        UsbACInterface.AUDIO_AUDIOCONTROL);
+
+        if (acDescriptors.size() != 1) {
+            return false;
+        }
+
+        if (acDescriptors.get(0) instanceof UsbACTerminal) {
+            UsbACTerminal outDescr = (UsbACTerminal) acDescriptors.get(0);
+            if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_DIGITAL) {
+                return true;
+            }
+        } else {
+            Log.w(TAG, "Undefined Audio Output terminal l: " + acDescriptors.get(0).getLength()
+                    + " t:0x" + Integer.toHexString(acDescriptors.get(0).getType()));
+        }
+        return false;
+    }
+
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 4d0cfea..ab07ce7 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -112,7 +112,10 @@
         return mEndpointAddress & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION;
     }
 
-    /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
+    /**
+    * Returns a UsbEndpoint that this UsbEndpointDescriptor is describing.
+    */
+    public UsbEndpoint toAndroid(UsbDescriptorParser parser) {
         if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() type:"
                     + Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 64dbd97..ca4613b 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,6 +42,8 @@
     private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
             new ArrayList<UsbEndpointDescriptor>();
 
+    private UsbDescriptor mClassSpecificInterfaceDescriptor;
+
     UsbInterfaceDescriptor(int length, byte type) {
         super(length, type);
         mHierarchyLevel = 3;
@@ -105,7 +107,18 @@
         mEndpointDescriptors.add(endpoint);
     }
 
-    UsbInterface toAndroid(UsbDescriptorParser parser) {
+    public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
+        mClassSpecificInterfaceDescriptor = descriptor;
+    }
+
+    public UsbDescriptor getClassSpecificInterfaceDescriptor() {
+        return mClassSpecificInterfaceDescriptor;
+    }
+
+    /**
+    * Returns a UsbInterface that this UsbInterfaceDescriptor is describing.
+    */
+    public UsbInterface toAndroid(UsbDescriptorParser parser) {
         if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
                     + " subclass:" + Integer.toHexString(mUsbSubclass)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
index d0ca6db..7653561 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
@@ -25,13 +25,19 @@
 public final class UsbMSMidiHeader extends UsbACInterface {
     private static final String TAG = "UsbMSMidiHeader";
 
+    private int mMidiStreamingClass;  // MSC Specification Release (BCD).
+
     public UsbMSMidiHeader(int length, byte type, byte subtype, int subclass) {
         super(length, type, subtype, subclass);
     }
 
+    public int getMidiStreamingClass() {
+        return mMidiStreamingClass;
+    }
+
     @Override
     public int parseRawDescriptors(ByteStream stream) {
-        // TODO - read data memebers
+        mMidiStreamingClass = stream.unpackUsbShort();
         stream.advance(mLength - stream.getReadCount());
         return mLength;
     }
@@ -42,6 +48,7 @@
 
         canvas.writeHeader(3, "MS Midi Header: " + ReportCanvas.getHexString(getType())
                 + " SubType: " + ReportCanvas.getHexString(getSubclass())
-                + " Length: " + getLength());
+                + " Length: " + getLength()
+                + " MidiStreamingClass :" + getMidiStreamingClass());
     }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
new file mode 100644
index 0000000..37bd0f8f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ * A class to parse Block Descriptors
+ * see midi20.pdf section 5.4
+ */
+public class UsbMidiBlockParser {
+    private static final String TAG = "UsbMidiBlockParser";
+
+    // Block header size
+    public static final int MIDI_BLOCK_HEADER_SIZE = 5;
+    public static final int MIDI_BLOCK_SIZE = 13;
+    public static final int REQ_GET_DESCRIPTOR = 0x06;
+    public static final int CS_GR_TRM_BLOCK = 0x26;     // Class-specific GR_TRM_BLK
+    public static final int GR_TRM_BLOCK_HEADER = 0x01; // Group block header
+    public static final int REQ_TIMEOUT_MS = 2000;      // 2 second timeout
+    public static final int DEFAULT_MIDI_TYPE = 1;      // Default MIDI type
+
+    protected int mHeaderLength;            // 0:1 Size of header descriptor
+    protected int mHeaderDescriptorType;    // 1:1 Descriptor Type
+    protected int mHeaderDescriptorSubtype; // 2:1 Descriptor Subtype
+    protected int mTotalLength;             // 3:2 Total Length of header and blocks
+
+    static class GroupTerminalBlock {
+        protected int mLength;                  // 0:1 Size of descriptor
+        protected int mDescriptorType;          // 1:1 Descriptor Type
+        protected int mDescriptorSubtype;       // 2:1 Descriptor Subtype
+        protected int mGroupBlockId;            // 3:1 Id of Group Terminal Block
+        protected int mGroupTerminalBlockType;  // 4:1 bi-directional, IN, or OUT
+        protected int mGroupTerminal;           // 5:1 Group Terminal Number
+        protected int mNumGroupTerminals;       // 6:1 Number of Group Terminals
+        protected int mBlockItem;               // 7:1 ID of STRING descriptor of Block item
+        protected int mMidiProtocol;            // 8:1 MIDI protocol
+        protected int mMaxInputBandwidth;       // 9:2 Max Input Bandwidth
+        protected int mMaxOutputBandwidth;      // 11:2 Max Output Bandwidth
+
+        public int parseRawDescriptors(ByteStream stream) {
+            mLength = stream.getUnsignedByte();
+            mDescriptorType = stream.getUnsignedByte();
+            mDescriptorSubtype = stream.getUnsignedByte();
+            mGroupBlockId = stream.getUnsignedByte();
+            mGroupTerminalBlockType = stream.getUnsignedByte();
+            mGroupTerminal = stream.getUnsignedByte();
+            mNumGroupTerminals = stream.getUnsignedByte();
+            mBlockItem = stream.getUnsignedByte();
+            mMidiProtocol = stream.getUnsignedByte();
+            mMaxInputBandwidth = stream.unpackUsbShort();
+            mMaxOutputBandwidth = stream.unpackUsbShort();
+            return mLength;
+        }
+    }
+
+    private ArrayList<GroupTerminalBlock> mGroupTerminalBlocks =
+            new ArrayList<GroupTerminalBlock>();
+
+    public UsbMidiBlockParser() {
+    }
+
+    /**
+     * Parses a raw ByteStream into a block terminal descriptor.
+     * The header is parsed before each block is parsed.
+     * @param   stream  ByteStream to parse
+     * @return          The total length that has been parsed.
+     */
+    public int parseRawDescriptors(ByteStream stream) {
+        mHeaderLength = stream.getUnsignedByte();
+        mHeaderDescriptorType = stream.getUnsignedByte();
+        mHeaderDescriptorSubtype = stream.getUnsignedByte();
+        mTotalLength = stream.unpackUsbShort();
+
+        while (stream.available() >= MIDI_BLOCK_SIZE) {
+            GroupTerminalBlock block = new GroupTerminalBlock();
+            block.parseRawDescriptors(stream);
+            mGroupTerminalBlocks.add(block);
+        }
+
+        return mTotalLength;
+    }
+
+    /**
+     * Calculates the MIDI type through querying the device twice, once for the size
+     * of the block descriptor and once for the block descriptor. This descriptor is
+     * then parsed to return the MIDI type.
+     * See the MIDI 2.0 USB doc for more info.
+     * @param  connection               UsbDeviceConnection to send the request
+     * @param  interfaceNumber          The interface number to query
+     * @param  alternateInterfaceNumber The alternate interface of the interface
+     * @return                          The MIDI type as an int.
+     */
+    public int calculateMidiType(UsbDeviceConnection connection, int interfaceNumber,
+            int alternateInterfaceNumber) {
+        byte[] byteArray = new byte[MIDI_BLOCK_HEADER_SIZE];
+        try {
+            // This first request is simply to get the full size of the descriptor.
+            // This info is stored in the last two bytes of the header.
+            int rdo = connection.controlTransfer(
+                    UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD
+                            | UsbConstants.USB_CLASS_AUDIO,
+                    REQ_GET_DESCRIPTOR,
+                    (CS_GR_TRM_BLOCK << 8) + alternateInterfaceNumber,
+                    interfaceNumber,
+                    byteArray,
+                    MIDI_BLOCK_HEADER_SIZE,
+                    REQ_TIMEOUT_MS);
+            if (rdo > 0) {
+                if (byteArray[1] != CS_GR_TRM_BLOCK) {
+                    Log.e(TAG, "Incorrect descriptor type: " + byteArray[1]);
+                    return DEFAULT_MIDI_TYPE;
+                }
+                if (byteArray[2] != GR_TRM_BLOCK_HEADER) {
+                    Log.e(TAG, "Incorrect descriptor subtype: " + byteArray[2]);
+                    return DEFAULT_MIDI_TYPE;
+                }
+                int newSize = (((int) byteArray[3]) & (0xff))
+                        + ((((int) byteArray[4]) & (0xff)) << 8);
+                if (newSize <= 0) {
+                    Log.e(TAG, "Parsed a non-positive block terminal size: " + newSize);
+                    return DEFAULT_MIDI_TYPE;
+                }
+                byteArray = new byte[newSize];
+                rdo = connection.controlTransfer(
+                        UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD
+                                | UsbConstants.USB_CLASS_AUDIO,
+                        REQ_GET_DESCRIPTOR,
+                        (CS_GR_TRM_BLOCK << 8) + alternateInterfaceNumber,
+                        interfaceNumber,
+                        byteArray,
+                        newSize,
+                        REQ_TIMEOUT_MS);
+                if (rdo > 0) {
+                    ByteStream stream = new ByteStream(byteArray);
+                    parseRawDescriptors(stream);
+                    if (mGroupTerminalBlocks.isEmpty()) {
+                        Log.e(TAG, "Group Terminal Blocks failed parsing: " + DEFAULT_MIDI_TYPE);
+                        return DEFAULT_MIDI_TYPE;
+                    } else {
+                        Log.d(TAG, "MIDI protocol: " + mGroupTerminalBlocks.get(0).mMidiProtocol);
+                        return mGroupTerminalBlocks.get(0).mMidiProtocol;
+                    }
+                } else {
+                    Log.e(TAG, "second transfer failed: " + rdo);
+                }
+            } else {
+                Log.e(TAG, "first transfer failed: " + rdo);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Can not communicate with USB device", e);
+        }
+        return DEFAULT_MIDI_TYPE;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
new file mode 100644
index 0000000..dd25620
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import android.hardware.usb.UsbPortStatus;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used for storing the raw data from the HAL.
+ * Values of the member variables mocked directly in case of emulation.
+ */
+public final class RawPortInfo implements Parcelable {
+    public final String portId;
+    public final int supportedModes;
+    public final int supportedContaminantProtectionModes;
+    public int currentMode;
+    public boolean canChangeMode;
+    public int currentPowerRole;
+    public boolean canChangePowerRole;
+    public int currentDataRole;
+    public boolean canChangeDataRole;
+    public boolean supportsEnableContaminantPresenceProtection;
+    public int contaminantProtectionStatus;
+    public boolean supportsEnableContaminantPresenceDetection;
+    public int contaminantDetectionStatus;
+    public int[] usbDataStatus;
+    public boolean powerTransferLimited;
+    public int powerBrickStatus;
+
+    public RawPortInfo(String portId, int supportedModes) {
+        this.portId = portId;
+        this.supportedModes = supportedModes;
+        this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+        this.supportsEnableContaminantPresenceProtection = false;
+        this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+        this.supportsEnableContaminantPresenceDetection = false;
+        this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+        this.usbDataStatus[0] = UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+        this.powerTransferLimited = false;
+        this.powerBrickStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+    }
+
+    public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
+            int currentMode, boolean canChangeMode,
+            int currentPowerRole, boolean canChangePowerRole,
+            int currentDataRole, boolean canChangeDataRole,
+            boolean supportsEnableContaminantPresenceProtection,
+            int contaminantProtectionStatus,
+            boolean supportsEnableContaminantPresenceDetection,
+            int contaminantDetectionStatus,
+            int[] usbDataStatus,
+            boolean powerTransferLimited,
+            int powerBrickStatus) {
+        this.portId = portId;
+        this.supportedModes = supportedModes;
+        this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
+        this.currentMode = currentMode;
+        this.canChangeMode = canChangeMode;
+        this.currentPowerRole = currentPowerRole;
+        this.canChangePowerRole = canChangePowerRole;
+        this.currentDataRole = currentDataRole;
+        this.canChangeDataRole = canChangeDataRole;
+        this.supportsEnableContaminantPresenceProtection =
+                supportsEnableContaminantPresenceProtection;
+        this.contaminantProtectionStatus = contaminantProtectionStatus;
+        this.supportsEnableContaminantPresenceDetection =
+                supportsEnableContaminantPresenceDetection;
+        this.contaminantDetectionStatus = contaminantDetectionStatus;
+        this.usbDataStatus = usbDataStatus;
+        this.powerTransferLimited = powerTransferLimited;
+        this.powerBrickStatus = powerBrickStatus;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(portId);
+        dest.writeInt(supportedModes);
+        dest.writeInt(supportedContaminantProtectionModes);
+        dest.writeInt(currentMode);
+        dest.writeByte((byte) (canChangeMode ? 1 : 0));
+        dest.writeInt(currentPowerRole);
+        dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
+        dest.writeInt(currentDataRole);
+        dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
+        dest.writeBoolean(supportsEnableContaminantPresenceProtection);
+        dest.writeInt(contaminantProtectionStatus);
+        dest.writeBoolean(supportsEnableContaminantPresenceDetection);
+        dest.writeInt(contaminantDetectionStatus);
+        dest.writeInt(usbDataStatus.length);
+        dest.writeIntArray(usbDataStatus);
+        dest.writeBoolean(powerTransferLimited);
+        dest.writeInt(powerBrickStatus);
+    }
+
+    public static final Parcelable.Creator<RawPortInfo> CREATOR =
+            new Parcelable.Creator<RawPortInfo>() {
+        @Override
+        public RawPortInfo createFromParcel(Parcel in) {
+            String id = in.readString();
+            int supportedModes = in.readInt();
+            int supportedContaminantProtectionModes = in.readInt();
+            int currentMode = in.readInt();
+            boolean canChangeMode = in.readByte() != 0;
+            int currentPowerRole = in.readInt();
+            boolean canChangePowerRole = in.readByte() != 0;
+            int currentDataRole = in.readInt();
+            boolean canChangeDataRole = in.readByte() != 0;
+            boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+            int contaminantProtectionStatus = in.readInt();
+            boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+            int contaminantDetectionStatus = in.readInt();
+            int[] usbDataStatus = new int[in.readInt()];
+            in.readIntArray(usbDataStatus);
+            boolean powerTransferLimited = in.readBoolean();
+            int powerBrickStatus = in.readInt();
+            return new RawPortInfo(id, supportedModes,
+                    supportedContaminantProtectionModes, currentMode, canChangeMode,
+                    currentPowerRole, canChangePowerRole,
+                    currentDataRole, canChangeDataRole,
+                    supportsEnableContaminantPresenceProtection,
+                    contaminantProtectionStatus,
+                    supportsEnableContaminantPresenceDetection,
+                    contaminantDetectionStatus, usbDataStatus,
+                    powerTransferLimited, powerBrickStatus);
+        }
+
+        @Override
+        public RawPortInfo[] newArray(int size) {
+            return new RawPortInfo[size];
+        }
+    };
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
new file mode 100644
index 0000000..5582600
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_V2_0;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.ContaminantProtectionStatus;
+import android.hardware.usb.IUsb;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.PortMode;
+import android.hardware.usb.Status;
+import android.hardware.usb.IUsbCallback;
+import android.hardware.usb.PortRole;
+import android.hardware.usb.PortStatus;
+import android.os.ServiceManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+/**
+ * Implements the methods to interact with AIDL USB HAL.
+ */
+public final class UsbPortAidl implements UsbPortHal {
+    private static final String TAG = UsbPortAidl.class.getSimpleName();
+    private static final String USB_AIDL_SERVICE =
+            "android.hardware.usb.IUsb/default";
+    private static final LongSparseArray<IUsbOperationInternal>
+                sCallbacks = new LongSparseArray<>();
+    // Proxy object for the usb hal daemon.
+    @GuardedBy("mLock")
+    private IUsb mProxy;
+    private UsbPortManager mPortManager;
+    public IndentingPrintWriter mPw;
+    // Mutex for all mutable shared state.
+    private final Object mLock = new Object();
+    // Callback when the UsbPort status is changed by the kernel.
+    private HALCallback mHALCallback;
+    private IBinder mBinder;
+    private boolean mSystemReady;
+    private long mTransactionId;
+
+    public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                throw new RemoteException("IUsb not initialized yet");
+            }
+        }
+        logAndPrint(Log.INFO, null, "USB HAL AIDL version: USB_HAL_V2_0");
+        return USB_HAL_V2_0;
+    }
+
+    @Override
+    public void systemReady() {
+        mSystemReady = true;
+    }
+
+    public void serviceDied() {
+        logAndPrint(Log.ERROR, mPw, "Usb AIDL hal service died");
+        synchronized (mLock) {
+            mProxy = null;
+        }
+        connectToProxy(null);
+    }
+
+    private void connectToProxy(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            if (mProxy != null) {
+                return;
+            }
+
+            try {
+                mBinder = ServiceManager.waitForService(USB_AIDL_SERVICE);
+                mProxy = IUsb.Stub.asInterface(mBinder);
+                mBinder.linkToDeath(this::serviceDied, 0);
+                mProxy.setCallback(mHALCallback);
+                mProxy.queryPortStatus(++mTransactionId);
+            } catch (NoSuchElementException e) {
+                logAndPrintException(pw, "connectToProxy: usb hal service not found."
+                        + " Did the service fail to start?", e);
+            } catch (RemoteException e) {
+                logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+            }
+        }
+    }
+
+    static boolean isServicePresent(IndentingPrintWriter pw) {
+        try {
+            return ServiceManager.isDeclared(USB_AIDL_SERVICE);
+        } catch (NoSuchElementException e) {
+            logAndPrintException(pw, "connectToProxy: usb Aidl hal service not found.", e);
+        }
+
+        return false;
+    }
+
+    public UsbPortAidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+        mPortManager = Objects.requireNonNull(portManager);
+        mPw = pw;
+        mHALCallback = new HALCallback(null, mPortManager, this);
+        connectToProxy(mPw);
+    }
+
+    @Override
+    public void enableContaminantPresenceDetection(String portName, boolean enable,
+            long operationID) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID: "
+                        + operationID);
+                return;
+            }
+
+            try {
+                // Oneway call into the hal. Use the castFrom method from HIDL.
+                mProxy.enableContaminantPresenceDetection(portName, enable, operationID);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set contaminant detection. opID:"
+                        + operationID, e);
+            }
+        }
+    }
+
+    @Override
+    public void queryPortStatus(long operationID) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+                        + operationID);
+                return;
+            }
+
+            try {
+                mProxy.queryPortStatus(operationID);
+            } catch (RemoteException e) {
+                logAndPrintException(null, "ServiceStart: Failed to query port status. opID:"
+                        + operationID, e);
+            }
+       }
+    }
+
+    @Override
+    public void switchMode(String portId, @HalUsbPortMode int newMode, long operationID) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+                        + operationID);
+                return;
+            }
+
+            PortRole newRole = new PortRole();
+            newRole.setMode((byte)newMode);
+            try {
+                mProxy.switchRole(portId, newRole, operationID);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set the USB port mode: "
+                        + "portId=" + portId
+                        + ", newMode=" + UsbPort.modeToString(newMode)
+                        + "opID:" + operationID, e);
+            }
+        }
+    }
+
+    @Override
+    public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+            long operationID) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+                        + operationID);
+                return;
+            }
+
+            PortRole newRole = new PortRole();
+            newRole.setPowerRole((byte)newPowerRole);
+            try {
+                mProxy.switchRole(portId, newRole, operationID);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+                        + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+                        + "opID:" + operationID, e);
+            }
+        }
+    }
+
+    @Override
+    public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long operationID) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+                        + operationID);
+                return;
+            }
+
+            PortRole newRole = new PortRole();
+            newRole.setDataRole((byte)newDataRole);
+            try {
+                mProxy.switchRole(portId, newRole, operationID);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+                        + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)
+                        + "opID:" + operationID, e);
+            }
+        }
+    }
+
+    @Override
+    public boolean enableUsbData(String portName, boolean enable, long operationID,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(portName);
+        Objects.requireNonNull(callback);
+        long key = operationID;
+        synchronized (mLock) {
+            try {
+                if (mProxy == null) {
+                    logAndPrint(Log.ERROR, mPw,
+                            "enableUsbData: Proxy is null. Retry !opID:"
+                            + operationID);
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                    return false;
+                }
+                while (sCallbacks.get(key) != null) {
+                    key = ThreadLocalRandom.current().nextInt();
+                }
+                if (key != operationID) {
+                    logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+                            + operationID + " key:" + key);
+                }
+                try {
+                    sCallbacks.put(key, callback);
+                    mProxy.enableUsbData(portName, enable, key);
+                } catch (RemoteException e) {
+                    logAndPrintException(mPw,
+                            "enableUsbData: Failed to invoke enableUsbData: portID="
+                            + portName + "opID:" + operationID, e);
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                    sCallbacks.remove(key);
+                    return false;
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(mPw,
+                        "enableUsbData: Failed to call onOperationComplete portID="
+                        + portName + "opID:" + operationID, e);
+                sCallbacks.remove(key);
+                return false;
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public void enableLimitPowerTransfer(String portName, boolean limit, long operationID,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(portName);
+        long key = operationID;
+        synchronized (mLock) {
+            try {
+                if (mProxy == null) {
+                    logAndPrint(Log.ERROR, mPw,
+                            "enableLimitPowerTransfer: Proxy is null. Retry !opID:"
+                            + operationID);
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                    return;
+                }
+                while (sCallbacks.get(key) != null) {
+                    key = ThreadLocalRandom.current().nextInt();
+                }
+                if (key != operationID) {
+                    logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+                            + operationID + " key:" + key);
+                }
+                try {
+                    sCallbacks.put(key, callback);
+                    mProxy.limitPowerTransfer(portName, limit, key);
+                } catch (RemoteException e) {
+                    logAndPrintException(mPw,
+                            "enableLimitPowerTransfer: Failed while invoking AIDL HAL"
+                            + " portID=" + portName + " opID:" + operationID, e);
+                    if (callback != null) {
+                        callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                    }
+                    sCallbacks.remove(key);
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(mPw,
+                        "enableLimitPowerTransfer: Failed to call onOperationComplete portID="
+                        + portName + " opID:" + operationID, e);
+            }
+        }
+    }
+
+    @Override
+    public void enableUsbDataWhileDocked(String portName, long operationID,
+            IUsbOperationInternal callback) {
+        Objects.requireNonNull(portName);
+        long key = operationID;
+        synchronized (mLock) {
+            try {
+                if (mProxy == null) {
+                    logAndPrint(Log.ERROR, mPw,
+                            "enableUsbDataWhileDocked: Proxy is null. Retry !opID:"
+                            + operationID);
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                    return;
+                }
+                while (sCallbacks.get(key) != null) {
+                    key = ThreadLocalRandom.current().nextInt();
+                }
+                if (key != operationID) {
+                    logAndPrint(Log.INFO, mPw,
+                            "enableUsbDataWhileDocked: operationID exists ! opID:"
+                            + operationID + " key:" + key);
+                }
+                try {
+                    sCallbacks.put(key, callback);
+                    mProxy.enableUsbDataWhileDocked(portName, key);
+                } catch (RemoteException e) {
+                    logAndPrintException(mPw,
+                            "enableUsbDataWhileDocked: error while invoking hal"
+                            + "portID=" + portName + " opID:" + operationID, e);
+                    if (callback != null) {
+                        callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                    }
+                    sCallbacks.remove(key);
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(mPw,
+                        "enableUsbDataWhileDocked: Failed to call onOperationComplete portID="
+                        + portName + " opID:" + operationID, e);
+            }
+        }
+    }
+
+    private static class HALCallback extends IUsbCallback.Stub {
+        public IndentingPrintWriter mPw;
+        public UsbPortManager mPortManager;
+        public UsbPortAidl mUsbPortAidl;
+
+        HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortAidl usbPortAidl) {
+            this.mPw = pw;
+            this.mPortManager = portManager;
+            this.mUsbPortAidl = usbPortAidl;
+        }
+
+        /**
+         * Converts from AIDL defined mode constants to UsbPortStatus constants.
+         * AIDL does not gracefully support bitfield when combined with enums.
+         */
+        private int toPortMode(byte aidlPortMode) {
+            switch (aidlPortMode) {
+                case PortMode.NONE:
+                    return UsbPortStatus.MODE_NONE;
+                case PortMode.UFP:
+                    return UsbPortStatus.MODE_UFP;
+                case PortMode.DFP:
+                    return UsbPortStatus.MODE_DFP;
+                case PortMode.DRP:
+                    return UsbPortStatus.MODE_DUAL;
+                case PortMode.AUDIO_ACCESSORY:
+                    return UsbPortStatus.MODE_AUDIO_ACCESSORY;
+                case PortMode.DEBUG_ACCESSORY:
+                    return UsbPortStatus.MODE_DEBUG_ACCESSORY;
+                default:
+                    UsbPortManager.logAndPrint(Log.ERROR, mPw, "Unrecognized aidlPortMode:"
+                            + aidlPortMode);
+                    return UsbPortStatus.MODE_NONE;
+            }
+        }
+
+        private int toSupportedModes(byte[] aidlPortModes) {
+            int supportedModes = UsbPortStatus.MODE_NONE;
+
+            for (byte aidlPortMode : aidlPortModes) {
+                supportedModes |= toPortMode(aidlPortMode);
+            }
+
+            return supportedModes;
+        }
+
+        /**
+         * Converts from AIDL defined contaminant protection constants to UsbPortStatus constants.
+         * AIDL does not gracefully support bitfield when combined with enums.
+         * Common to both ContaminantProtectionMode and ContaminantProtectionStatus.
+         */
+        private int toContaminantProtectionStatus(byte aidlContaminantProtection) {
+            switch (aidlContaminantProtection) {
+                case ContaminantProtectionStatus.NONE:
+                    return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+                case ContaminantProtectionStatus.FORCE_SINK:
+                    return UsbPortStatus.CONTAMINANT_PROTECTION_SINK;
+                case ContaminantProtectionStatus.FORCE_SOURCE:
+                    return UsbPortStatus.CONTAMINANT_PROTECTION_SOURCE;
+                case ContaminantProtectionStatus.FORCE_DISABLE:
+                    return UsbPortStatus.CONTAMINANT_PROTECTION_FORCE_DISABLE;
+                case ContaminantProtectionStatus.DISABLED:
+                    return UsbPortStatus.CONTAMINANT_PROTECTION_DISABLED;
+                default:
+                    UsbPortManager.logAndPrint(Log.ERROR, mPw,
+                            "Unrecognized aidlContaminantProtection:"
+                            + aidlContaminantProtection);
+                    return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+            }
+        }
+
+        private int toSupportedContaminantProtectionModes(byte[] aidlModes) {
+            int supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+
+            for (byte aidlMode : aidlModes) {
+                supportedContaminantProtectionModes |= toContaminantProtectionStatus(aidlMode);
+            }
+
+            return supportedContaminantProtectionModes;
+        }
+
+        private int[] toIntArray(byte[] input) {
+            int[] output = new int[input.length];
+            for (int i = 0; i < input.length; i++) {
+                output[i] = input[i];
+            }
+            return output;
+        }
+
+        @Override
+        public void notifyPortStatusChange(
+               android.hardware.usb.PortStatus[] currentPortStatus, int retval) {
+            if (!mUsbPortAidl.mSystemReady) {
+                return;
+            }
+
+            if (retval != Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+                return;
+            }
+
+            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+            int numStatus = currentPortStatus.length;
+            for (int i = 0; i < numStatus; i++) {
+                PortStatus current = currentPortStatus[i];
+                RawPortInfo temp = new RawPortInfo(current.portName,
+                        toSupportedModes(current.supportedModes),
+                        toSupportedContaminantProtectionModes(current
+                                .supportedContaminantProtectionModes),
+                        toPortMode(current.currentMode),
+                        current.canChangeMode,
+                        current.currentPowerRole,
+                        current.canChangePowerRole,
+                        current.currentDataRole,
+                        current.canChangeDataRole,
+                        current.supportsEnableContaminantPresenceProtection,
+                        toContaminantProtectionStatus(current.contaminantProtectionStatus),
+                        current.supportsEnableContaminantPresenceDetection,
+                        current.contaminantDetectionStatus,
+                        toIntArray(current.usbDataStatus),
+                        current.powerTransferLimited,
+                        current.powerBrickStatus);
+                newPortInfo.add(temp);
+                UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: "
+                        + current.portName);
+            }
+            mPortManager.updatePorts(newPortInfo);
+        }
+
+        @Override
+        public void notifyRoleSwitchStatus(String portName, PortRole role, int retval,
+                long operationID) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, portName
+                        + " role switch successful. opID:"
+                        + operationID);
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed. err:"
+                        + retval
+                        + "opID:" + operationID);
+            }
+        }
+
+        @Override
+        public void notifyQueryPortStatus(String portName, int retval, long operationID) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+                        + operationID + " successful");
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + ": opID:"
+                        + operationID + " failed. err:" + retval);
+            }
+        }
+
+        @Override
+        public void notifyEnableUsbDataStatus(String portName, boolean enable, int retval,
+                long operationID) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyEnableUsbDataStatus:"
+                        + portName + ": opID:"
+                        + operationID + " enable:" + enable);
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+                        + "notifyEnableUsbDataStatus: opID:"
+                        + operationID + " failed. err:" + retval);
+            }
+            try {
+                sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+                        ? USB_OPERATION_SUCCESS
+                        : USB_OPERATION_ERROR_INTERNAL);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw,
+                        "notifyEnableUsbDataStatus: Failed to call onOperationComplete",
+                        e);
+            }
+        }
+
+        @Override
+        public void notifyContaminantEnabledStatus(String portName, boolean enable, int retval,
+                long operationID) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyContaminantEnabledStatus:"
+                        + portName + ": opID:"
+                        + operationID + " enable:" + enable);
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+                        + "notifyContaminantEnabledStatus: opID:"
+                        + operationID + " failed. err:" + retval);
+            }
+        }
+
+        @Override
+        public void notifyLimitPowerTransferStatus(String portName, boolean limit, int retval,
+                long operationID) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+                        + operationID + " successful");
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+                        + "notifyLimitPowerTransferStatus: opID:"
+                        + operationID + " failed. err:" + retval);
+            }
+            try {
+                IUsbOperationInternal callback = sCallbacks.get(operationID);
+                if (callback != null) {
+                    sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+                            ? USB_OPERATION_SUCCESS
+                            : USB_OPERATION_ERROR_INTERNAL);
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(mPw,
+                        "enableLimitPowerTransfer: Failed to call onOperationComplete",
+                        e);
+            }
+        }
+
+        @Override
+        public void notifyEnableUsbDataWhileDockedStatus(String portName, int retval,
+                long operationID) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+                        + operationID + " successful");
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+                        + "notifyEnableUsbDataWhileDockedStatus: opID:"
+                        + operationID + " failed. err:" + retval);
+            }
+            try {
+                IUsbOperationInternal callback = sCallbacks.get(operationID);
+                if (callback != null) {
+                    sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+                            ? USB_OPERATION_SUCCESS
+                            : USB_OPERATION_ERROR_INTERNAL);
+                }
+            } catch (RemoteException e) {
+                logAndPrintException(mPw,
+                        "notifyEnableUsbDataWhileDockedStatus: Failed to call onOperationComplete",
+                        e);
+            }
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
new file mode 100644
index 0000000..abfdd6f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.String;
+
+/**
+ * @hide
+ */
+public interface UsbPortHal {
+    /**
+     * Power role: This USB port can act as a source (provide power).
+     * @hide
+     */
+    public static final int HAL_POWER_ROLE_SOURCE = 1;
+
+    /**
+     * Power role: This USB port can act as a sink (receive power).
+     * @hide
+     */
+    public static final int HAL_POWER_ROLE_SINK = 2;
+
+    @IntDef(prefix = { "HAL_POWER_ROLE_" }, value = {
+            HAL_POWER_ROLE_SOURCE,
+            HAL_POWER_ROLE_SINK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface HalUsbPowerRole{}
+
+    /**
+     * Data role: This USB port can act as a host (access data services).
+     * @hide
+     */
+    public static final int HAL_DATA_ROLE_HOST = 1;
+
+    /**
+     * Data role: This USB port can act as a device (offer data services).
+     * @hide
+     */
+    public static final int HAL_DATA_ROLE_DEVICE = 2;
+
+    @IntDef(prefix = { "HAL_DATA_ROLE_" }, value = {
+            HAL_DATA_ROLE_HOST,
+            HAL_DATA_ROLE_DEVICE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface HalUsbDataRole{}
+
+    /**
+     * This USB port can act as a downstream facing port (host).
+     *
+     * @hide
+     */
+    public static final int HAL_MODE_DFP = 1;
+
+    /**
+     * This USB port can act as an upstream facing port (device).
+     *
+     * @hide
+     */
+    public static final int HAL_MODE_UFP = 2;
+    @IntDef(prefix = { "HAL_MODE_" }, value = {
+            HAL_MODE_DFP,
+            HAL_MODE_UFP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface HalUsbPortMode{}
+
+    /**
+     * UsbPortManager would call this when the system is done booting.
+     */
+    public void systemReady();
+
+    /**
+     * Invoked to enable/disable contaminant presence detection on the USB port.
+     *
+     * @param portName Port Identifier.
+     * @param enable Enable contaminant presence detection when true.
+     *               Disable when false.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     */
+    public void enableContaminantPresenceDetection(String portName, boolean enable,
+            long transactionId);
+
+    /**
+     * Invoked to query port status of all the ports.
+     *
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     */
+    public void queryPortStatus(long transactionId);
+
+    /**
+     * Invoked to switch USB port mode.
+     *
+     * @param portName Port Identifier.
+     * @param mode New mode that the port is switching into.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     */
+    public void switchMode(String portName, @HalUsbPortMode int mode, long transactionId);
+
+    /**
+     * Invoked to switch USB port power role.
+     *
+     * @param portName Port Identifier.
+     * @param powerRole New power role that the port is switching into.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     */
+    public void switchPowerRole(String portName, @HalUsbPowerRole int powerRole,
+            long transactionId);
+
+    /**
+     * Invoked to switch USB port data role.
+     *
+     * @param portName Port Identifier.
+     * @param dataRole New data role that the port is switching into.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     */
+    public void switchDataRole(String portName, @HalUsbDataRole int dataRole, long transactionId);
+
+    /**
+     * Invoked to query the version of current hal implementation.
+     */
+    public @UsbHalVersion int getUsbHalVersion() throws RemoteException;
+
+    /**
+     * Invoked to enable/disable UsbData on the specified port.
+     *
+     * @param portName Port Identifier.
+     * @param enable Enable USB data when true.
+     *               Disable when false.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     * @param callback callback object to be invoked to invoke the status of the operation upon
+     *                 completion.
+     * @param callback callback object to be invoked when the operation is complete.
+     * @return True when the operation is asynchronous. The caller of
+     *         {@link UsbOperationInternal} must therefore call
+     *         {@link UsbOperationInternal#waitForOperationComplete} for processing
+     *         the result.
+     *         False when the operation is synchronous. Caller can proceed reading the result
+     *         through {@link UsbOperationInternal#getStatus}
+     */
+    public boolean enableUsbData(String portName, boolean enable, long transactionId,
+            IUsbOperationInternal callback);
+
+    /**
+     * Invoked to enable  UsbData when disabled due to docking event.
+     *
+     * @param portName Port Identifier.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     * @param callback callback object to be invoked to invoke the status of the operation upon
+     *                 completion.
+     */
+    public void enableUsbDataWhileDocked(String portName, long transactionId,
+            IUsbOperationInternal callback);
+
+    /**
+     * Invoked to enableLimitPowerTransfer on the specified port.
+     *
+     * @param portName Port Identifier.
+     * @param limit limit power transfer when true. Port wouldn't charge or power USB accessoried
+     *              when set.
+     *              Lift power transfer restrictions when false.
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
+     * @param callback callback object to be invoked to invoke the status of the operation upon
+     *                 completion.
+     */
+    public void enableLimitPowerTransfer(String portName, boolean limit, long transactionId,
+            IUsbOperationInternal callback);
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
new file mode 100644
index 0000000..41f9fae
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.hal.port.UsbPortHidl;
+import com.android.server.usb.hal.port.UsbPortAidl;
+import com.android.server.usb.UsbPortManager;
+
+import android.util.Log;
+/**
+ * Helper class that queries the underlying hal layer to populate UsbPortHal instance.
+ */
+public final class UsbPortHalInstance {
+
+    public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
+
+        logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
+        if (UsbPortHidl.isServicePresent(null)) {
+            logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+            return new UsbPortHidl(portManager, pw);
+        }
+        if (UsbPortAidl.isServicePresent(null)) {
+            logAndPrint(Log.INFO, null, "USB HAL AIDL present");
+            return new UsbPortAidl(portManager, pw);
+        }
+
+        return null;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
new file mode 100644
index 0000000..c1d7635
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_NOT_SUPPORTED;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_0;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_1;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_2;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_3;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.V1_0.IUsb;
+import android.hardware.usb.V1_0.PortRoleType;
+import android.hardware.usb.V1_0.Status;
+import android.hardware.usb.V1_1.PortStatus_1_1;
+import android.hardware.usb.V1_2.IUsbCallback;
+import android.hardware.usb.V1_0.PortRole;
+import android.hardware.usb.V1_2.PortStatus;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+/**
+ *
+ */
+public final class UsbPortHidl implements UsbPortHal {
+    private static final String TAG = UsbPortHidl.class.getSimpleName();
+    // Cookie sent for usb hal death notification.
+    private static final int USB_HAL_DEATH_COOKIE = 1000;
+    // Proxy object for the usb hal daemon.
+    @GuardedBy("mLock")
+    private IUsb mProxy;
+    private UsbPortManager mPortManager;
+    public IndentingPrintWriter mPw;
+    // Mutex for all mutable shared state.
+    private final Object mLock = new Object();
+    // Callback when the UsbPort status is changed by the kernel.
+    private HALCallback mHALCallback;
+    private boolean mSystemReady;
+    // Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus;
+    private static int sUsbDataStatus = USB_DATA_STATUS_UNKNOWN;
+
+    public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+        int version;
+        synchronized(mLock) {
+            if (mProxy == null) {
+                throw new RemoteException("IUsb not initialized yet");
+            }
+            if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
+                version = USB_HAL_V1_3;
+            } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
+                version = USB_HAL_V1_2;
+            } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
+                version = USB_HAL_V1_1;
+            } else {
+                version = USB_HAL_V1_0;
+            }
+            logAndPrint(Log.INFO, null, "USB HAL HIDL version: " + version);
+            return version;
+        }
+    }
+
+    final class DeathRecipient implements IHwBinder.DeathRecipient {
+        public IndentingPrintWriter pw;
+
+        DeathRecipient(IndentingPrintWriter pw) {
+            this.pw = pw;
+        }
+
+        @Override
+        public void serviceDied(long cookie) {
+            if (cookie == USB_HAL_DEATH_COOKIE) {
+                logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
+                synchronized (mLock) {
+                    mProxy = null;
+                }
+            }
+        }
+    }
+
+    final class ServiceNotification extends IServiceNotification.Stub {
+        @Override
+        public void onRegistration(String fqName, String name, boolean preexisting) {
+            logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
+            connectToProxy(null);
+        }
+    }
+
+    private void connectToProxy(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            if (mProxy != null) {
+                return;
+            }
+
+            try {
+                mProxy = IUsb.getService();
+                mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
+                mProxy.setCallback(mHALCallback);
+                mProxy.queryPortStatus();
+                //updateUsbHalVersion();
+            } catch (NoSuchElementException e) {
+                logAndPrintException(pw, "connectToProxy: usb hal service not found."
+                        + " Did the service fail to start?", e);
+            } catch (RemoteException e) {
+                logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+            }
+        }
+    }
+
+    @Override
+    public void systemReady() {
+        mSystemReady = true;
+    }
+
+    static boolean isServicePresent(IndentingPrintWriter pw) {
+        try {
+            IUsb.getService(true);
+        } catch (NoSuchElementException e) {
+            logAndPrintException(pw, "connectToProxy: usb hidl hal service not found.", e);
+            return false;
+        } catch (RemoteException e) {
+            logAndPrintException(pw, "IUSB hal service present but failed to get service", e);
+        }
+
+        return true;
+    }
+
+    public UsbPortHidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+        mPortManager = Objects.requireNonNull(portManager);
+        mPw = pw;
+        mHALCallback = new HALCallback(null, mPortManager, this);
+        try {
+            ServiceNotification serviceNotification = new ServiceNotification();
+
+            boolean ret = IServiceManager.getService()
+                    .registerForNotifications("[email protected]::IUsb",
+                            "", serviceNotification);
+            if (!ret) {
+                logAndPrint(Log.ERROR, null,
+                        "Failed to register service start notification");
+            }
+        } catch (RemoteException e) {
+            logAndPrintException(null,
+                    "Failed to register service start notification", e);
+            return;
+        }
+        connectToProxy(mPw);
+    }
+
+    @Override
+    public void enableContaminantPresenceDetection(String portName, boolean enable,
+            long transactionId) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+                return;
+            }
+
+            try {
+                // Oneway call into the hal. Use the castFrom method from HIDL.
+                android.hardware.usb.V1_2.IUsb proxy =
+                        android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
+                proxy.enableContaminantPresenceDetection(portName, enable);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set contaminant detection", e);
+            } catch (ClassCastException e)  {
+                logAndPrintException(mPw, "Method only applicable to V1.2 or above implementation",
+                    e);
+            }
+        }
+    }
+
+    @Override
+    public void queryPortStatus(long transactionId) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+                return;
+            }
+
+            try {
+                mProxy.queryPortStatus();
+            } catch (RemoteException e) {
+                logAndPrintException(null, "ServiceStart: Failed to query port status", e);
+            }
+       }
+    }
+
+    @Override
+    public void switchMode(String portId, @HalUsbPortMode int newMode, long transactionId) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+                return;
+            }
+
+            PortRole newRole = new PortRole();
+            newRole.type = PortRoleType.MODE;
+            newRole.role = newMode;
+            try {
+                mProxy.switchRole(portId, newRole);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set the USB port mode: "
+                    + "portId=" + portId
+                    + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+            }
+        }
+    }
+
+    @Override
+    public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+            long transactionId) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+                return;
+            }
+
+            PortRole newRole = new PortRole();
+            newRole.type = PortRoleType.POWER_ROLE;
+            newRole.role = newPowerRole;
+            try {
+                mProxy.switchRole(portId, newRole);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+                    + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role), e);
+            }
+        }
+    }
+
+    @Override
+    public void enableLimitPowerTransfer(String portName, boolean limit, long transactionId,
+            IUsbOperationInternal callback) {
+        /* Not supported in HIDL hals*/
+        try {
+            callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+        } catch (RemoteException e) {
+            logAndPrintException(mPw, "Failed to call onOperationComplete", e);
+        }
+    }
+
+    @Override
+    public void enableUsbDataWhileDocked(String portName, long transactionId,
+            IUsbOperationInternal callback) {
+        /* Not supported in HIDL hals*/
+        try {
+            callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+        } catch (RemoteException e) {
+            logAndPrintException(mPw, "Failed to call onOperationComplete", e);
+        }
+    }
+
+    @Override
+    public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long transactionId) {
+        synchronized (mLock) {
+            if (mProxy == null) {
+                logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+                return;
+            }
+
+            PortRole newRole = new PortRole();
+            newRole.type = PortRoleType.DATA_ROLE;
+            newRole.role = newDataRole;
+            try {
+                mProxy.switchRole(portId, newRole);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+                    + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role), e);
+            }
+        }
+    }
+
+    @Override
+    public boolean enableUsbData(String portName, boolean enable, long transactionId,
+            IUsbOperationInternal callback) {
+        int halVersion;
+
+        try {
+            halVersion = getUsbHalVersion();
+        } catch (RemoteException e) {
+            logAndPrintException(mPw, "Failed to query USB HAL version. opID:"
+                    + transactionId
+                    + " portId:" + portName, e);
+            return false;
+        }
+
+        if (halVersion != USB_HAL_V1_3) {
+            try {
+                callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+            } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+                        + transactionId
+                        + " portId:" + portName, e);
+            }
+            return false;
+        }
+
+        boolean success;
+        synchronized(mLock) {
+            try {
+                android.hardware.usb.V1_3.IUsb proxy
+                        = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
+                success = proxy.enableUsbDataSignal(enable);
+           } catch (RemoteException e) {
+                logAndPrintException(mPw, "Failed enableUsbData: opId:" + transactionId
+                        + " portId=" + portName , e);
+                try {
+                    callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+                } catch (RemoteException r) {
+                    logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+                            + transactionId
+                            + " portId:" + portName, r);
+                }
+                return false;
+            }
+        }
+        if (success) {
+            sUsbDataStatus = enable ? USB_DATA_STATUS_UNKNOWN : USB_DATA_STATUS_DISABLED_FORCE;
+        }
+        try {
+            callback.onOperationComplete(success
+                    ? USB_OPERATION_SUCCESS
+                    : USB_OPERATION_ERROR_INTERNAL);
+        } catch (RemoteException r) {
+            logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+                + transactionId
+                + " portId:" + portName, r);
+        }
+        return false;
+    }
+
+    private static class HALCallback extends IUsbCallback.Stub {
+        public IndentingPrintWriter mPw;
+        public UsbPortManager mPortManager;
+        public UsbPortHidl mUsbPortHidl;
+
+        HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortHidl usbPortHidl) {
+            this.mPw = pw;
+            this.mPortManager = portManager;
+            this.mUsbPortHidl = usbPortHidl;
+        }
+
+        public void notifyPortStatusChange(
+                ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
+            if (!mUsbPortHidl.mSystemReady) {
+                return;
+            }
+
+            if (retval != Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+                return;
+            }
+
+            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+            for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
+                RawPortInfo temp = new RawPortInfo(current.portName,
+                        current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+                        current.currentMode,
+                        current.canChangeMode, current.currentPowerRole,
+                        current.canChangePowerRole,
+                        current.currentDataRole, current.canChangeDataRole,
+                        false, CONTAMINANT_PROTECTION_NONE,
+                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
+                        false, POWER_BRICK_STATUS_UNKNOWN);
+                newPortInfo.add(temp);
+                UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: "
+                        + current.portName);
+            }
+
+            mPortManager.updatePorts(newPortInfo);
+        }
+
+
+        public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
+                int retval) {
+            if (!mUsbPortHidl.mSystemReady) {
+                return;
+            }
+
+            if (retval != Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+                return;
+            }
+
+            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+            int numStatus = currentPortStatus.size();
+            for (int i = 0; i < numStatus; i++) {
+                PortStatus_1_1 current = currentPortStatus.get(i);
+                RawPortInfo temp = new RawPortInfo(current.status.portName,
+                        current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+                        current.currentMode,
+                        current.status.canChangeMode, current.status.currentPowerRole,
+                        current.status.canChangePowerRole,
+                        current.status.currentDataRole, current.status.canChangeDataRole,
+                        false, CONTAMINANT_PROTECTION_NONE,
+                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
+                        false, POWER_BRICK_STATUS_UNKNOWN);
+                newPortInfo.add(temp);
+                UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: "
+                        + current.status.portName);
+            }
+            mPortManager.updatePorts(newPortInfo);
+        }
+
+        public void notifyPortStatusChange_1_2(
+                ArrayList<PortStatus> currentPortStatus, int retval) {
+            if (!mUsbPortHidl.mSystemReady) {
+                return;
+            }
+
+            if (retval != Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+                return;
+            }
+
+            ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+            int numStatus = currentPortStatus.size();
+            for (int i = 0; i < numStatus; i++) {
+                PortStatus current = currentPortStatus.get(i);
+                RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
+                        current.status_1_1.supportedModes,
+                        current.supportedContaminantProtectionModes,
+                        current.status_1_1.currentMode,
+                        current.status_1_1.status.canChangeMode,
+                        current.status_1_1.status.currentPowerRole,
+                        current.status_1_1.status.canChangePowerRole,
+                        current.status_1_1.status.currentDataRole,
+                        current.status_1_1.status.canChangeDataRole,
+                        current.supportsEnableContaminantPresenceProtection,
+                        current.contaminantProtectionStatus,
+                        current.supportsEnableContaminantPresenceDetection,
+                        current.contaminantDetectionStatus,
+                        new int[sUsbDataStatus],
+                        false, POWER_BRICK_STATUS_UNKNOWN);
+                newPortInfo.add(temp);
+                UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: "
+                        + current.status_1_1.status.portName);
+            }
+            mPortManager.updatePorts(newPortInfo);
+        }
+
+        public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
+            if (retval == Status.SUCCESS) {
+                UsbPortManager.logAndPrint(Log.INFO, mPw, portName + " role switch successful");
+            } else {
+                UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed");
+            }
+        }
+    }
+}
diff --git a/services/wallpapereffectsgeneration/OWNERS b/services/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 0000000..d2d3e2c0
--- /dev/null
+++ b/services/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d94fafc..ce9530c 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -701,8 +701,15 @@
          */
         public static final int PROPERTY_CROSS_SIM = 0x00004000;
 
+        /**
+         * Connection is a tethered external call.
+         * Indicates that the {@link Connection} is fixed on this device but the audio streams are
+         * re-routed to another device.
+         */
+        public static final int PROPERTY_TETHERED_CALL = 0x00008000;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00004000
+        // Next PROPERTY value: 0x00010000
         //******************************************************************************************
 
         private final @CallState int mState;
@@ -899,6 +906,9 @@
             if (hasProperty(properties, PROPERTY_CROSS_SIM)) {
                 builder.append(" PROPERTY_CROSS_SIM");
             }
+            if (hasProperty(properties, PROPERTY_TETHERED_CALL)) {
+                builder.append(" PROPERTY_TETHERED_CALL");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index fccdf76..389df80 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -27,7 +27,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -42,7 +41,8 @@
 public final class CallAudioState implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
+    @IntDef(value = {ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER,
+            ROUTE_EXTERNAL},
             flag=true)
     public @interface CallAudioRoute {}
 
@@ -58,6 +58,9 @@
     /** Direct the audio stream through the device's speakerphone. */
     public static final int ROUTE_SPEAKER       = 0x00000008;
 
+    /** Direct the audio stream through another device. */
+    public static final int ROUTE_EXTERNAL       = 0x00000010;
+
     /**
      * Direct the audio stream through the device's earpiece or wired headset if one is
      * connected.
@@ -70,7 +73,7 @@
      * @hide
      **/
     public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
-            ROUTE_SPEAKER;
+            ROUTE_SPEAKER | ROUTE_EXTERNAL;
 
     private final boolean isMuted;
     private final int route;
@@ -189,7 +192,11 @@
      */
     @CallAudioRoute
     public int getSupportedRouteMask() {
-        return supportedRouteMask;
+        if (route == ROUTE_EXTERNAL) {
+            return ROUTE_EXTERNAL;
+        } else {
+            return supportedRouteMask;
+        }
     }
 
     /**
@@ -233,6 +240,10 @@
             listAppend(buffer, "SPEAKER");
         }
 
+        if ((route & ROUTE_EXTERNAL) == ROUTE_EXTERNAL) {
+            listAppend(buffer, "EXTERNAL");
+        }
+
         return buffer.toString();
     }
 
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7861b11..37b4e65 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -632,8 +632,9 @@
      * post-dial digits are passed.
      * <p>
      * Calls with a {@link Call.Details#getHandlePresentation()} of
-     * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN}
-     * or {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
+     * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN},
+     * {@link TelecomManager#PRESENTATION_UNAVAILABLE} or
+     * {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
      * {@link CallScreeningService}.
      *
      * @param callDetails Information about a new call, see {@link Call.Details}.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 21a1804..30d4959 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -561,6 +561,15 @@
      */
     public static final int PROPERTY_CROSS_SIM = 1 << 13;
 
+    /**
+     * Connection is a tethered external call.
+     * <p>
+     * Indicates that the {@link Connection} is fixed on this device but the audio streams are
+     * re-routed to another device.
+     * <p>
+     */
+    public static final int PROPERTY_TETHERED_CALL = 1 << 14;
+
     //**********************************************************************************************
     // Next PROPERTY value: 1<<14
     //**********************************************************************************************
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 0dc899e..6279bf8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -990,6 +990,11 @@
      */
     public static final int PRESENTATION_PAYPHONE = 4;
 
+    /**
+     * Indicates that the address or number of a call is unavailable.
+     */
+    public static final int PRESENTATION_UNAVAILABLE = 5;
+
 
     /*
      * Values for the adb property "persist.radio.videocall.audio.output"
@@ -1006,7 +1011,7 @@
     @IntDef(
             prefix = { "PRESENTATION_" },
             value = {PRESENTATION_ALLOWED, PRESENTATION_RESTRICTED, PRESENTATION_UNKNOWN,
-            PRESENTATION_PAYPHONE})
+            PRESENTATION_PAYPHONE, PRESENTATION_UNAVAILABLE})
     public @interface Presentation {}
 
 
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index a75f79c..b9936ce 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -342,6 +342,8 @@
 
     void cleanupStuckCalls();
 
+    int cleanupOrphanPhoneAccounts();
+
     void resetCarMode();
 
     void setTestDefaultCallRedirectionApp(String packageName);
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index fabe612..184e154 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
@@ -261,6 +262,14 @@
     public static final String EXTRA_RESOLUTION_PORT_INDEX =
             "android.service.euicc.extra.RESOLUTION_PORT_INDEX";
 
+    /**
+     * Intent extra set for resolution requests containing a bool indicating whether to use the
+     * given port index. For example, if {@link #switchToSubscription(int, PendingIntent)} is
+     * called, then no portIndex has been provided by the caller, and this extra will be false.
+     */
+    public static final String EXTRA_RESOLUTION_USE_PORT_INDEX =
+            "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "RESULT_" }, value = {
@@ -852,14 +861,19 @@
         }
         @Override
         public void switchToSubscription(int slotId, int portIndex, String iccid,
-                boolean forceDeactivateSim, ISwitchToSubscriptionCallback callback) {
+                boolean forceDeactivateSim, ISwitchToSubscriptionCallback callback,
+                boolean usePortIndex) {
             mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    // TODO(b/207392528: use portIndex API once implemented)
-                    int result =
-                            EuiccService.this.onSwitchToSubscription(
-                                    slotId, iccid, forceDeactivateSim);
+                    int result = 0;
+                    if (usePortIndex) {
+                        result = EuiccService.this.onSwitchToSubscriptionWithPort(
+                                slotId, portIndex, iccid, forceDeactivateSim);
+                    } else {
+                        result = EuiccService.this.onSwitchToSubscription(
+                                slotId, iccid, forceDeactivateSim);
+                    }
                     try {
                         callback.onComplete(result);
                     } catch (RemoteException e) {
diff --git a/telephony/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
index aa30c9e8..030e11a 100644
--- a/telephony/java/android/service/euicc/IEuiccService.aidl
+++ b/telephony/java/android/service/euicc/IEuiccService.aidl
@@ -49,7 +49,7 @@
     void getEuiccInfo(int slotId, in IGetEuiccInfoCallback callback);
     void deleteSubscription(int slotId, String iccid, in IDeleteSubscriptionCallback callback);
     void switchToSubscription(int slotId, int portIndex, String iccid, boolean forceDeactivateSim,
-            in ISwitchToSubscriptionCallback callback);
+            in ISwitchToSubscriptionCallback callback, boolean useLegacyApi);
     void updateSubscriptionNickname(int slotId, String iccid, String nickname,
             in IUpdateSubscriptionNicknameCallback callback);
     void eraseSubscriptions(int slotId, in IEraseSubscriptionsCallback callback);
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index fa70c33..d77bf67 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -81,6 +81,13 @@
     private boolean mRtpInactivityDetected;
     private boolean mRxSilenceDetected;
     private boolean mTxSilenceDetected;
+    private int mNumVoiceFrames;
+    private int mNumNoDataFrames;
+    private int mNumDroppedRtpPackets;
+    private long mMinPlayoutDelayMillis;
+    private long mMaxPlayoutDelayMillis;
+    private int mNumRtpSidPacketsRx;
+    private int mNumRtpDuplicatePackets;
 
     /** @hide **/
     public CallQuality(Parcel in) {
@@ -98,6 +105,13 @@
         mRtpInactivityDetected = in.readBoolean();
         mRxSilenceDetected = in.readBoolean();
         mTxSilenceDetected = in.readBoolean();
+        mNumVoiceFrames = in.readInt();
+        mNumNoDataFrames = in.readInt();
+        mNumDroppedRtpPackets = in.readInt();
+        mMinPlayoutDelayMillis = in.readLong();
+        mMaxPlayoutDelayMillis = in.readLong();
+        mNumRtpSidPacketsRx = in.readInt();
+        mNumRtpDuplicatePackets = in.readInt();
     }
 
     /** @hide **/
@@ -298,6 +312,59 @@
     }
 
     /**
+     * Returns the number of Voice frames sent by jitter buffer to audio
+     */
+    public int getNumVoiceFrames() {
+        return mNumVoiceFrames;
+    }
+
+    /**
+     * Returns the number of no-data frames sent by jitter buffer to audio
+     */
+    public int getNumNoDataFrames() {
+        return mNumNoDataFrames;
+    }
+
+    /**
+     * Returns the number of RTP voice packets dropped by jitter buffer
+     */
+    public int getNumDroppedRtpPackets() {
+        return mNumDroppedRtpPackets;
+    }
+
+    /**
+     * Returns the minimum playout delay in the reporting interval
+     * in milliseconds.
+     */
+    public long getMinPlayoutDelayMillis() {
+        return mMinPlayoutDelayMillis;
+    }
+
+    /**
+     * Returns the maximum playout delay in the reporting interval
+     * in milliseconds.
+     */
+    public long getMaxPlayoutDelayMillis() {
+        return mMaxPlayoutDelayMillis;
+    }
+
+    /**
+     * Returns the total number of RTP SID (Silence Insertion Descriptor) packets
+     * received by this device for an ongoing call
+     */
+    public int getNumRtpSidPacketsRx() {
+        return mNumRtpSidPacketsRx;
+    }
+
+    /**
+     * Returns the total number of RTP duplicate packets received by this device
+     * for an ongoing call
+     */
+    public int getNumRtpDuplicatePackets() {
+        return mNumRtpDuplicatePackets;
+    }
+
+    /**
      * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
      * {@link ImsStreamMediaProfile}.
      *
@@ -345,6 +412,13 @@
                 + " rtpInactivityDetected=" + mRtpInactivityDetected
                 + " txSilenceDetected=" + mTxSilenceDetected
                 + " rxSilenceDetected=" + mRxSilenceDetected
+                + " numVoiceFrames=" + mNumVoiceFrames
+                + " numNoDataFrames=" + mNumNoDataFrames
+                + " numDroppedRtpPackets=" + mNumDroppedRtpPackets
+                + " minPlayoutDelayMillis=" + mMinPlayoutDelayMillis
+                + " maxPlayoutDelayMillis=" + mMaxPlayoutDelayMillis
+                + " numRtpSidPacketsRx=" + mNumRtpSidPacketsRx
+                + " numRtpDuplicatePackets=" + mNumRtpDuplicatePackets
                 + "}";
     }
 
@@ -364,7 +438,14 @@
                 mCodecType,
                 mRtpInactivityDetected,
                 mRxSilenceDetected,
-                mTxSilenceDetected);
+                mTxSilenceDetected,
+                mNumVoiceFrames,
+                mNumNoDataFrames,
+                mNumDroppedRtpPackets,
+                mMinPlayoutDelayMillis,
+                mMaxPlayoutDelayMillis,
+                mNumRtpSidPacketsRx,
+                mNumRtpDuplicatePackets);
     }
 
     @Override
@@ -392,7 +473,14 @@
                 && mCodecType == s.mCodecType
                 && mRtpInactivityDetected == s.mRtpInactivityDetected
                 && mRxSilenceDetected == s.mRxSilenceDetected
-                && mTxSilenceDetected == s.mTxSilenceDetected);
+                && mTxSilenceDetected == s.mTxSilenceDetected
+                && mNumVoiceFrames == s.mNumVoiceFrames
+                && mNumNoDataFrames == s.mNumNoDataFrames
+                && mNumDroppedRtpPackets == s.mNumDroppedRtpPackets
+                && mMinPlayoutDelayMillis == s.mMinPlayoutDelayMillis
+                && mMaxPlayoutDelayMillis == s.mMaxPlayoutDelayMillis
+                && mNumRtpSidPacketsRx == s.mNumRtpSidPacketsRx
+                && mNumRtpDuplicatePackets == s.mNumRtpDuplicatePackets);
     }
 
     /**
@@ -420,6 +508,13 @@
         dest.writeBoolean(mRtpInactivityDetected);
         dest.writeBoolean(mRxSilenceDetected);
         dest.writeBoolean(mTxSilenceDetected);
+        dest.writeInt(mNumVoiceFrames);
+        dest.writeInt(mNumNoDataFrames);
+        dest.writeInt(mNumDroppedRtpPackets);
+        dest.writeLong(mMinPlayoutDelayMillis);
+        dest.writeLong(mMaxPlayoutDelayMillis);
+        dest.writeInt(mNumRtpSidPacketsRx);
+        dest.writeInt(mNumRtpDuplicatePackets);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
@@ -431,4 +526,322 @@
             return new CallQuality[size];
         }
     };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link CallQuality} when creating a new
+     * instance.
+     *
+     * <p>The example below shows how you might create a new {@code CallQuality}:
+     *
+     * <pre><code>
+     *
+     * CallQuality callQuality = new CallQuality.Builder()
+     *     .setNumRtpPacketsTransmitted(150)
+     *     .setNumRtpPacketsReceived(200)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+
+        private int mDownlinkCallQualityLevel;
+        private int mUplinkCallQualityLevel;
+        private int mCallDuration;
+        private int mNumRtpPacketsTransmitted;
+        private int mNumRtpPacketsReceived;
+        private int mNumRtpPacketsTransmittedLost;
+        private int mNumRtpPacketsNotReceived;
+        private int mAverageRelativeJitter;
+        private int mMaxRelativeJitter;
+        private int mAverageRoundTripTime;
+        private int mCodecType;
+        private boolean mRtpInactivityDetected;
+        private boolean mRxSilenceDetected;
+        private boolean mTxSilenceDetected;
+        private int mNumVoiceFrames;
+        private int mNumNoDataFrames;
+        private int mNumDroppedRtpPackets;
+        private long mMinPlayoutDelayMillis;
+        private long mMaxPlayoutDelayMillis;
+        private int mNumRtpSidPacketsRx;
+        private int mNumRtpDuplicatePackets;
+
+        /**
+         * Set the downlink call quality level for ongoing call.
+         *
+         * @param downlinkCallQualityLevel the Downlink call quality level
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDownlinkCallQualityLevel(
+                @CallQualityLevel int downlinkCallQualityLevel) {
+            mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+            return this;
+        }
+
+        /**
+         * Set the uplink call quality level for ongoing call.
+         *
+         * @param uplinkCallQualityLevel the Uplink call quality level
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setUplinkCallQualityLevel(
+                @CallQualityLevel int uplinkCallQualityLevel) {
+            mUplinkCallQualityLevel = uplinkCallQualityLevel;
+            return this;
+        }
+
+        /**
+         * Set the call duration in milliseconds.
+         *
+         * @param callDuration the call duration in milliseconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCallDuration(int callDuration) {
+            mCallDuration = callDuration;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets sent for ongoing call.
+         *
+         * @param numRtpPacketsTransmitted RTP packets sent to network
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsTransmitted(int numRtpPacketsTransmitted) {
+            mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets received for ongoing call.
+         *
+         * @param numRtpPacketsReceived RTP packets received from network
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsReceived(int numRtpPacketsReceived) {
+            mNumRtpPacketsReceived = numRtpPacketsReceived;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets which were lost in network and never
+         * transmitted.
+         *
+         * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+         * transmitted
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsTransmittedLost(int numRtpPacketsTransmittedLost) {
+            mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets which were lost in network and never received.
+         *
+         * @param numRtpPacketsNotReceived RTP packets which were lost in network and
+         * never received
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsNotReceived(int numRtpPacketsNotReceived) {
+            mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+            return this;
+        }
+
+        /**
+         * Set the average relative jitter in milliseconds.
+         *
+         * @param averageRelativeJitter average relative jitter in milliseconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setAverageRelativeJitter(int averageRelativeJitter) {
+            mAverageRelativeJitter = averageRelativeJitter;
+            return this;
+        }
+
+        /**
+         * Set the maximum relative jitter in milliseconds.
+         *
+         * @param maxRelativeJitter maximum relative jitter in milliseconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMaxRelativeJitter(int maxRelativeJitter) {
+            mMaxRelativeJitter = maxRelativeJitter;
+            return this;
+        }
+
+        /**
+         * Set the average round trip delay in milliseconds.
+         *
+         * @param averageRoundTripTime average round trip delay in milliseconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setAverageRoundTripTime(int averageRoundTripTime) {
+            mAverageRoundTripTime = averageRoundTripTime;
+            return this;
+        }
+
+        /**
+         * Set the codec type used in the ongoing call.
+         *
+         * @param codecType the codec type.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCodecType(int codecType) {
+            mCodecType = codecType;
+            return this;
+        }
+
+        /**
+         * Set to be True if no incoming RTP is received for a continuous
+         * duration of 4 seconds.
+         *
+         * @param rtpInactivityDetected True if no incoming RTP is received for
+         * a continuous duration of 4 seconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRtpInactivityDetected(boolean rtpInactivityDetected) {
+            mRtpInactivityDetected = rtpInactivityDetected;
+            return this;
+        }
+
+        /**
+         * Set to be True if only silence RTP packets are received for 20 seconds
+         * immediately after call is connected.
+         *
+         * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+         * immediately after call is connected
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setIncomingSilenceDetectedAtCallSetup(boolean rxSilenceDetected) {
+            mRxSilenceDetected = rxSilenceDetected;
+            return this;
+        }
+
+        /**
+         * Set to be True if only silence RTP packets are sent for 20 seconds immediately
+         * after call is connected.
+         *
+         * @param txSilenceDetected True if only silence RTP packets are sent for
+         * 20 seconds immediately after call is connected.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setOutgoingSilenceDetectedAtCallSetup(boolean txSilenceDetected) {
+            mTxSilenceDetected = txSilenceDetected;
+            return this;
+        }
+
+        /**
+         * Set the number of voice frames sent by jitter buffer to audio.
+         *
+         * @param numVoiceFrames Voice frames sent by jitter buffer to audio.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumVoiceFrames(int numVoiceFrames) {
+            mNumVoiceFrames = numVoiceFrames;
+            return this;
+        }
+
+        /**
+         * Set the number of no-data frames sent by jitter buffer to audio.
+         *
+         * @param numNoDataFrames no-data frames sent by jitter buffer to audio
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumNoDataFrames(int numNoDataFrames) {
+            mNumNoDataFrames = numNoDataFrames;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP Voice packets dropped by jitter buffer.
+         *
+         * @param numDroppedRtpPackets number of RTP Voice packets dropped by jitter buffer
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumDroppedRtpPackets(int numDroppedRtpPackets) {
+            mNumDroppedRtpPackets = numDroppedRtpPackets;
+            return this;
+        }
+
+        /**
+         * Set the minimum playout delay in the reporting interval in milliseconds.
+         *
+         * @param minPlayoutDelayMillis minimum playout delay in the reporting interval
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMinPlayoutDelayMillis(long minPlayoutDelayMillis) {
+            mMinPlayoutDelayMillis = minPlayoutDelayMillis;
+            return this;
+        }
+
+        /**
+         * Set the maximum Playout delay in the reporting interval in milliseconds.
+         *
+         * @param maxPlayoutDelayMillis maximum Playout delay in the reporting interval
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMaxPlayoutDelayMillis(long maxPlayoutDelayMillis) {
+            mMaxPlayoutDelayMillis = maxPlayoutDelayMillis;
+            return this;
+        }
+
+        /**
+         * Set the total number of RTP SID (Silence Insertion Descriptor)
+         * packets received by this device for an ongoing call.
+         *
+         * @param numRtpSidPacketsRx the total number of RTP SID packets received
+         * by this device for an ongoing call.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpSidPacketsRx(int numRtpSidPacketsRx) {
+            mNumRtpSidPacketsRx = numRtpSidPacketsRx;
+            return this;
+        }
+
+        /**
+         * Set the total number of RTP duplicate packets received by this device
+         * for an ongoing call.
+         *
+         * @param numRtpDuplicatePackets the total number of RTP duplicate packets
+         * received by this device for an ongoing call
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpDuplicatePackets(int numRtpDuplicatePackets) {
+            mNumRtpDuplicatePackets = numRtpDuplicatePackets;
+            return this;
+        }
+
+        /**
+         * Build the CallQuality.
+         *
+         * @return the CallQuality object.
+         */
+        public @NonNull CallQuality build() {
+
+            CallQuality callQuality = new CallQuality();
+            callQuality.mDownlinkCallQualityLevel = mDownlinkCallQualityLevel;
+            callQuality.mUplinkCallQualityLevel = mUplinkCallQualityLevel;
+            callQuality.mCallDuration = mCallDuration;
+            callQuality.mNumRtpPacketsTransmitted = mNumRtpPacketsTransmitted;
+            callQuality.mNumRtpPacketsReceived = mNumRtpPacketsReceived;
+            callQuality.mNumRtpPacketsTransmittedLost = mNumRtpPacketsTransmittedLost;
+            callQuality.mNumRtpPacketsNotReceived = mNumRtpPacketsNotReceived;
+            callQuality.mAverageRelativeJitter = mAverageRelativeJitter;
+            callQuality.mMaxRelativeJitter = mMaxRelativeJitter;
+            callQuality.mAverageRoundTripTime = mAverageRoundTripTime;
+            callQuality.mCodecType = mCodecType;
+            callQuality.mRtpInactivityDetected = mRtpInactivityDetected;
+            callQuality.mTxSilenceDetected = mTxSilenceDetected;
+            callQuality.mRxSilenceDetected = mRxSilenceDetected;
+            callQuality.mNumVoiceFrames = mNumVoiceFrames;
+            callQuality.mNumNoDataFrames = mNumNoDataFrames;
+            callQuality.mNumDroppedRtpPackets = mNumDroppedRtpPackets;
+            callQuality.mMinPlayoutDelayMillis = mMinPlayoutDelayMillis;
+            callQuality.mMaxPlayoutDelayMillis = mMaxPlayoutDelayMillis;
+            callQuality.mNumRtpSidPacketsRx = mNumRtpSidPacketsRx;
+            callQuality.mNumRtpDuplicatePackets = mNumRtpDuplicatePackets;
+
+            return callQuality;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 82113f2..d5c846d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -37,6 +37,8 @@
 import android.os.RemoteException;
 import android.service.carrier.CarrierService;
 import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 import android.telephony.gba.TlsParams;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
@@ -173,7 +175,10 @@
     /**
      * This flag specifies whether VoLTE availability is based on provisioning. By default this is
      * false.
+     * Used for UCE to determine if EAB provisioning checks should be based on provisioning.
+     * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead.
      */
+    @Deprecated
     public static final String
             KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
 
@@ -798,6 +803,14 @@
             "carrier_cross_sim_ims_available_bool";
 
     /**
+     * Flag specifying whether cross sim calling on opportunistic data is supported for carrier.
+     * When {@code false} the carrier does not support cross sim calling on opportunistic data.
+     * When {@code true} the carrier does support cross sim calling on opportunistic data.
+     */
+    public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL =
+            "enable_cross_sim_calling_on_opportunistic_data_bool";
+
+    /**
      * Specifies a map from dialstrings to replacements for roaming network service numbers which
      * cannot be replaced on the carrier side.
      * <p>
@@ -869,7 +882,12 @@
     /**
      * Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
      * Calling.
+
+     * Combines VoLTE, VT, VoWiFI calling provisioning into one parameter.
+     * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+     * finer-grained control.
      */
+    @Deprecated
     public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
             = "carrier_volte_provisioning_required_bool";
 
@@ -883,7 +901,11 @@
      * and enable the UT over IMS capability for the subscription when the subscription is loaded.
      *
      * The default value for this key is {@code false}.
+     *
+     * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+     * determining if UT requires provisioning.
      */
+    @Deprecated
     public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL =
             "carrier_ut_provisioning_required_bool";
 
@@ -2265,6 +2287,7 @@
      * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
      * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
      * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+     * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE
      *
      * <p>
      * 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml
@@ -2475,6 +2498,10 @@
      *
      * Note: If {@code *} is specified for the original code, any ImsReasonInfo with the matching
      * {@code MESSAGE} will be remapped to {@code NEW_CODE}.
+     * If {@code *} is specified for the message, any ImsReasonInfo with the matching
+     * {@code ORIGINAL_CODE} will be remapped to {@code NEW_CODE}.
+     * The wildcard for {@code ORIGINAL_CODE} takes precedence to the wildcard for {@code MESSAGE}.
+     * A mapping with both wildcards has no effect.
      *
      * Example: "501|call completion elsewhere|1014"
      * When the {@link ImsReasonInfo#getCode()} is {@link ImsReasonInfo#CODE_USER_TERMINATED} and
@@ -2924,19 +2951,6 @@
             "signal_strength_nr_nsa_use_lte_as_primary_bool";
 
     /**
-     * String array of TCP buffer sizes per network type.
-     * The entries should be of the following form, with values in bytes:
-     * "network_name:read_min,read_default,read_max,write_min,write_default,write_max".
-     * For NR (5G), the following network names should be used:
-     * - NR_NSA: NR NSA, sub-6 frequencies
-     * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
-     * - NR_SA: NR SA, sub-6 frequencies
-     * - NR_SA_MMWAVE: NR SA, mmwave frequencies
-     * @hide
-     */
-    public static final String KEY_TCP_BUFFERS_STRING_ARRAY = "tcp_buffers_string_array";
-
-    /**
      * String array of default bandwidth values per network type.
      * The entries should be of form: "network_name:downlink,uplink", with values in Kbps.
      * For NR (5G), the following network names should be used:
@@ -4523,7 +4537,7 @@
             "carrier_auto_cancel_cs_notification";
 
     /**
-     * Passing this value as {@link KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
+     * Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
      * subscription from a group instead of adding it to a group.
      *
      * TODO: Expose in a future release.
@@ -4781,6 +4795,485 @@
         public static final String KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG =
                 KEY_PREFIX + "rcs_request_retry_interval_millis_long";
 
+        /** SIP timer T1 as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_T1_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_t1_millis_int";
+
+        /** SIP timer T2 as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_T2_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_t2_millis_int";
+
+        /** SIP timer T4 as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_T4_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_t4_millis_int";
+
+        /** SIP timer B as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_B_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_b_millis_int";
+
+        /** SIP timer C as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_C_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_c_millis_int";
+
+        /** SIP timer D as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_D_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_d_millis_int";
+
+        /** SIP timer F as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_F_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_f_millis_int";
+
+        /** SIP timer H as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_H_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_h_millis_int";
+
+        /** SIP timer J as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_J_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_j_millis_int";
+
+        /** Specifies the SIP Server default port. */
+        public static final String KEY_SIP_SERVER_PORT_NUMBER_INT  =
+                KEY_PREFIX + "sip_server_port_number_int";
+
+        /**
+         * Specify the “phone-context” parameter as defined in
+         * section 7.2A.10 in 3GPP TS 24.229.
+         */
+        public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING =
+                KEY_PREFIX + "phone_context_domain_name_string";
+
+        /** @hide */
+        @IntDef({REQUEST_URI_FORMAT_TEL, REQUEST_URI_FORMAT_SIP})
+
+        public @interface RequestUriFormatType {}
+
+        /**
+         *  Request URI is of type TEL URI.
+         */
+        public static final int REQUEST_URI_FORMAT_TEL = 0;
+
+        /**
+         *  Request URI is of type SIP URI.
+         */
+        public static final int REQUEST_URI_FORMAT_SIP = 1;
+
+        /**
+         * Specify whether the request URI is SIP URI
+         * {@link #REQUEST_URI_FORMAT_SIP} or
+         * TEL URI {@link #REQUEST_URI_FORMAT_TEL}.
+         */
+        public static final String KEY_REQUEST_URI_TYPE_INT =
+                KEY_PREFIX + "request_uri_type_int";
+
+        /**
+         * Flag indicating whether Globally Routable User agent (GRUU)
+         * in supported HEADER is included or not.
+         *
+         * <p> Reference: RFC 5627.
+         */
+        public static final String KEY_GRUU_ENABLED_BOOL =
+                KEY_PREFIX + "gruu_enabled_bool";
+
+        /**
+         * Flag indicating whether to keep/release IMS PDN in case of
+         * moving to non VOPS area.
+         *
+         * <p>if {@code True}, keep IMS PDN in case of moving to non VOPS area.
+         * if {@code false}, otherwise.
+         */
+        public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL =
+                KEY_PREFIX + "keep_pdn_up_in_no_vops_bool";
+
+        /** @hide */
+        @IntDef({
+            PREFERRED_TRANSPORT_UDP,
+            PREFERRED_TRANSPORT_TCP,
+            PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP,
+            PREFERRED_TRANSPORT_TLS
+        })
+
+        public @interface PreferredTransportType {}
+
+        /** Preferred Transport is always UDP. */
+        public static final int PREFERRED_TRANSPORT_UDP = 0;
+
+        /** Preferred Transport is always TCP. */
+        public static final int PREFERRED_TRANSPORT_TCP = 1;
+
+        /**
+         *  Preferred Transport is both UDP and TCP and selected based
+         *  on MTU size specified in {@link #KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT}
+         *  and {@link #KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT}.
+         *
+         *  <p>Default transport is UDP. If message size is larger
+         *  than MTU, then TCP shall be used.
+         */
+        public static final int PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP = 2;
+
+        /** Preferred Transport is TLS. */
+        public static final int PREFERRED_TRANSPORT_TLS = 3;
+
+        /**
+         * Specify the preferred transport protocol for SIP messages.
+         *
+         * <p>Possible values are,
+         * {@link #PREFERRED_TRANSPORT_UDP},
+         * {@link #PREFERRED_TRANSPORT_TCP},
+         * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP}
+         */
+        public static final String KEY_SIP_PREFERRED_TRANSPORT_INT =
+                KEY_PREFIX + "sip_preferred_transport_int";
+
+        /**
+         * Specify the maximum IPV4 MTU size of SIP message on Cellular.
+         *
+         * <p>If {@link #KEY_SIP_PREFERRED_TRANSPORT_INT} is
+         * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} and SIP message MTU size
+         * is more than this value, then SIP transport will be TCP, else the
+         * SIP transport is UDP.
+         */
+        public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT =
+                KEY_PREFIX + "ipv4_sip_mtu_size_cellular_int";
+
+        /**
+         * Specify the maximum IPV6 MTU size of SIP message on Cellular.
+         *
+         * <p>If {@link #KEY_SIP_PREFERRED_TRANSPORT_INT} is
+         * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} and SIP message MTU size
+         * is more than this value, then SIP transport will be TCP, else the
+         * SIP transport is UDP.
+         */
+        public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT =
+                KEY_PREFIX + "ipv6_sip_mtu_size_cellular_int";
+
+        /**
+         * This config determines whether IMS PDN needs to be enabled
+         * when VOPS support is not available in both home and roaming scenarios.
+         *
+         * <p>This is applicable before IMS PDN is up, to decide whether
+         * IMS PDN needs to be enabled based on VOPS support in home/roaming.
+         *
+         * <p>Possible values are,
+         * {@link #NETWORK_TYPE_HOME},
+         * {@link #NETWORK_TYPE_ROAMING}
+         * An empty array indicates IMS PDN depends on VOPS on both home
+         * and roaming scenarios.
+         */
+        public static final String KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY =
+                KEY_PREFIX + "ims_pdn_enabled_in_no_vops_support_int_array";
+
+        /**
+         * Flag indicating whether IPSec enabled for SIP messages.
+         *
+         * <p> Reference: 3GPP TS 33.203 and RFC 3329.
+         */
+        public static final String KEY_SIP_OVER_IPSEC_ENABLED_BOOL =
+                KEY_PREFIX + "sip_over_ipsec_enabled_bool";
+
+        /** @hide */
+        @IntDef({IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5, IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1})
+
+        public @interface IpsecAuthenticationAlgorithmType {}
+
+        /** IPSec Authentication algorithm is HMAC-MD5. see Annex H of TS 33.203 */
+        public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5 = 0;
+
+        /** IPSec Authentication algorithm is HMAC-SHA1. see Annex H of TS 33.203 */
+        public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1 = 1;
+
+        /**
+         * List of supported IPSEC Authentication algorithms.
+         *
+         * <p>Possible values are,
+         * {@link #IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5},
+         * {@link #IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1}
+         */
+        public static final String KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "ipsec_authentication_algorithms_int_array";
+
+        /** @hide */
+        @IntDef({
+            IPSEC_ENCRYPTION_ALGORITHM_NULL,
+            IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC,
+            IPSEC_ENCRYPTION_ALGORITHM_AES_CBC
+        })
+
+        public @interface IpsecEncryptionAlgorithmType {}
+
+        /** IPSec Encryption algorithm is NULL. see Annex H of TS 33.203 */
+        public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0;
+
+        /** IPSec Encryption algorithm is DES_EDE3_CBC. see Annex H of TS 33.203 */
+        public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1;
+
+        /** IPSec Encryption algorithm is AES_CBC. see Annex H of TS 33.203 */
+        public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2;
+
+        /**
+         * List of supported IPSEC encryption algorithms.
+         *
+         * <p>Possible values are,
+         * {@link #IPSEC_ENCRYPTION_ALGORITHM_NULL},
+         * {@link #IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC},
+         * {@link #IPSEC_ENCRYPTION_ALGORITHM_AES_CBC}
+         */
+        public static final String KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "ipsec_encryption_algorithms_int_array";
+
+        /**
+         * Expiry timer for IMS Registration in seconds.
+         * <p>Reference: RFC 3261 Section 20.19.
+         */
+        public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT =
+                KEY_PREFIX + "registration_expiry_timer_sec_int";
+
+        /** Registration Retry Base-time as per RFC 5626 Section 4.5. */
+        public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT =
+                KEY_PREFIX + "registration_retry_base_timer_millis_int";
+
+        /** Registration Retry max-time as per RFC 5626 Section 4.5. */
+        public static final String KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT =
+                KEY_PREFIX + "registration_retry_max_timer_millis_int";
+
+        /**
+         * Flag indicating whether subscription to registration event package
+         * is supported or not.
+         */
+        public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL =
+                KEY_PREFIX + "registration_event_package_supported_bool";
+
+        /**
+         * Expiry timer for SUBSCRIBE in seconds.
+         * <p>Reference: RFC 3261 Section 20.19.
+         */
+        public static final String KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT =
+                KEY_PREFIX + "registration_subscribe_expiry_timer_sec_int";
+
+        /** @hide */
+        @IntDef({
+            GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI,
+            GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI,
+            GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR,
+            GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR
+        })
+
+        public @interface GeolocationPidfAllowedType {}
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for
+         * normal/non-emergency call scenario on WiFi
+         *
+         * <p>Geolocation for normal/non-emergency call should only include
+         * country code.
+         */
+        public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI = 1;
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for emergency
+         * call scenario on WiFi
+         */
+        public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI = 2;
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for normal/non-emergency
+         * call scenario on Cellular
+         *
+         * <p>Geolocation for normal/non-emergency call should only include
+         * country code.
+         */
+        public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR = 3;
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for emergency
+         * call scenario on Cellular
+         */
+        public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR = 4;
+
+        /**
+         * List of cases where geolocation PIDF XML needs to be included in the
+         * SIP REGISTER over WiFi and Cellular.
+         *
+         * <p>Possible values are,
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR}
+         *
+         * <p>An empty array indicates geolocation PIDF XML should not be included in
+         * the SIP REGISTER over WiFi and Cellular.
+         */
+        public static final String KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY =
+                KEY_PREFIX + "geolocation_pidf_in_sip_register_support_int_array";
+
+        /**
+         * List of cases where geolocation PIDF XML needs to be included in the
+         * SIP INVITE over WiFi and Cellular.
+         *
+         * <p>Possible values are,
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR}
+         *
+         * <p>An empty array indicates geolocation PIDF XML should not be included
+         * in the SIP INVITE over WiFi and Cellular.
+         */
+        public static final String KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY =
+                KEY_PREFIX + "geolocation_pidf_in_sip_invite_support_int_array";
+
+        /**
+         * Specifies the IMS User Agent in template format.
+         *
+         * <p>Example: #MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#".
+         * IMS Stack should internally substitute the tokens with the
+         * values from the respective android properties.
+         *
+         * <p>List of allowed tokens and the corresponding android properties are,
+         * <UL>
+         *   <LI>MANUFACTURER : ro.product.manufacturer</LI>
+         *   <LI>MODEL :  ro.product.model</LI>
+         *   <LI>AV : ro.build.version.release"</LI>
+         *   <LI>BUILD : ro.build.id</LI>
+         * </UL>
+         * <p> Vendor IMS Stack should strip any whitespace characters present
+         * in the android properties values before replacing the token.
+         *
+         * <p> An empty string is invalid as per IR92 section 2.6. This key is
+         * considered invalid if the format is violated. If the key is invalid or
+         * not configured, IMS stack should use internal default values.
+         */
+        public static final String KEY_IMS_USER_AGENT_STRING =
+                KEY_PREFIX + "ims_user_agent_string";
+
+        /** @hide */
+        @IntDef({
+            NETWORK_TYPE_HOME,
+            NETWORK_TYPE_ROAMING
+        })
+
+        public @interface NetworkType {}
+
+        /** Indicates HOME Network. */
+        public static final int NETWORK_TYPE_HOME = 0;
+
+        /** Indicates Roaming Network. */
+        public static final int NETWORK_TYPE_ROAMING = 1;
+
+        /** @hide */
+        @IntDef({
+            RTCP_INACTIVITY_ON_HOLD,
+            RTCP_INACTIVITY_ON_CONNECTED,
+            RTP_INACTIVITY_ON_CONNECTED,
+            E911_RTCP_INACTIVITY_ON_CONNECTED,
+            E911_RTP_INACTIVITY_ON_CONNECTED
+        })
+
+        public @interface MediaInactivityReason {}
+
+        /**  RTCP inactivity occurred when call is on HOLD. */
+        public static final int RTCP_INACTIVITY_ON_HOLD = 0;
+
+        /**  RTCP inactivity occurred when call is connected. */
+        public static final int RTCP_INACTIVITY_ON_CONNECTED = 1;
+
+        /**  RTP inactivity occurred when call is connected. */
+        public static final int RTP_INACTIVITY_ON_CONNECTED = 2;
+
+        /**  E911 RTCP inactivity occurred when call is connected. */
+        public static final int E911_RTCP_INACTIVITY_ON_CONNECTED = 3;
+
+        /**  E911 RTP inactivity occurred when call is connected. */
+        public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4;
+
+        /**
+         * A bundle which specifies the MMTEL capability and registration technology
+         * that requires provisioning. If a tuple is not present, the
+         * framework will not require that the tuple requires provisioning before
+         * enabling the capability.
+         * <p> Possible keys in this bundle are
+         * <ul>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_UT_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_SMS_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY}</li>
+         * </ul>
+         * <p> The values are defined in
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+         */
+        public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE =
+                KEY_PREFIX + "mmtel_requires_provisioning_bundle";
+
+        /**
+         * This MmTelFeature supports Voice calling (IR.92)
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
+         */
+        public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_voice_int_array";
+
+        /**
+         * This MmTelFeature supports Video (IR.94)
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
+         */
+        public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_video_int_array";
+
+        /**
+         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
+         */
+        public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_ut_int_array";
+
+        /**
+         * This MmTelFeature supports SMS (IR.92)
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
+         */
+        public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_sms_int_array";
+
+        /**
+         * This MmTelFeature supports Call Composer (section 2.4 of RCC.20)
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
+         */
+        public static final String KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_call_composer_int_array";
+
+        /**
+         * A bundle which specifies the RCS capability and registration technology
+         * that requires provisioning. If a tuple is not present, the
+         * framework will not require that the tuple requires provisioning before
+         * enabling the capability.
+         * <p> Possible keys in this bundle are
+         * <ul>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li>
+         * </ul>
+         * <p> The values are defined in
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+         */
+        public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE =
+                KEY_PREFIX + "rcs_requires_provisioning_bundle";
+
+        /**
+         * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+         * If not set, this RcsFeature should not service capability requests.
+         * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE
+         */
+        public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_options_uce_int_array";
+
+        /**
+         * This carrier supports User Capability Exchange using a presence server as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using a presence
+         * server. If not set, this RcsFeature should not publish capabilities or service capability
+         * requests using presence.
+         * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE
+         */
+        public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY =
+                KEY_PREFIX + "key_capability_type_presence_uce_int_array";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
@@ -4817,6 +5310,2173 @@
                     "+g.gsma.rcs.botversion=\"#=1,#=2\"",
                     "+g.gsma.rcs.cpimext"});
 
+            /**
+             * @see #KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
+             */
+            PersistableBundle mmtel_requires_provisioning_int_array = new PersistableBundle();
+            defaults.putPersistableBundle(
+                    KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE, mmtel_requires_provisioning_int_array);
+
+            /**
+             * @see #KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+             */
+            PersistableBundle rcs_requires_provisioning_int_array = new PersistableBundle();
+            defaults.putPersistableBundle(
+                    KEY_RCS_REQUIRES_PROVISIONING_BUNDLE, rcs_requires_provisioning_int_array);
+
+            defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, true);
+            defaults.putBoolean(KEY_SIP_OVER_IPSEC_ENABLED_BOOL, true);
+            defaults.putBoolean(KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false);
+            defaults.putBoolean(KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_SIP_TIMER_T1_MILLIS_INT, 2000);
+            defaults.putInt(KEY_SIP_TIMER_T2_MILLIS_INT, 16000);
+            defaults.putInt(KEY_SIP_TIMER_T4_MILLIS_INT, 17000);
+            defaults.putInt(KEY_SIP_TIMER_B_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_TIMER_C_MILLIS_INT, 210000);
+            defaults.putInt(KEY_SIP_TIMER_D_MILLIS_INT, 130000);
+            defaults.putInt(KEY_SIP_TIMER_F_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_TIMER_H_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_TIMER_J_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_SERVER_PORT_NUMBER_INT, 5060);
+            defaults.putInt(KEY_REQUEST_URI_TYPE_INT, REQUEST_URI_FORMAT_SIP);
+            defaults.putInt(KEY_SIP_PREFERRED_TRANSPORT_INT, PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP);
+            defaults.putInt(KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT, 1500);
+            defaults.putInt(KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT, 1500);
+            defaults.putInt(KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT, 600000);
+            defaults.putInt(KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT, 30000);
+            defaults.putInt(KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT, 1800000);
+            defaults.putInt(KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT, 600000);
+
+            defaults.putIntArray(
+                    KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY,
+                    new int[] {
+                        IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5,
+                        IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1
+                    });
+            defaults.putIntArray(
+                    KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY,
+                    new int[] {
+                        IPSEC_ENCRYPTION_ALGORITHM_NULL,
+                        IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC,
+                        IPSEC_ENCRYPTION_ALGORITHM_AES_CBC
+                    });
+            defaults.putIntArray(
+                    KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY,
+                    new int[] {
+                    });
+            defaults.putIntArray(
+                    KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY,
+                    new int[] {
+                        GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI
+                    });
+            defaults.putIntArray(
+                    KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY,
+                    new int[] {
+                        GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI
+                    });
+
+            defaults.putString(KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING, "");
+            defaults.putString(KEY_IMS_USER_AGENT_STRING,
+                               "#MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#");
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS Voice configs. This groups the configs required for IMS Voice - VoNR/VoLTE
+     *
+     * <p>Reference: IR.92
+     */
+    public static final class ImsVoice {
+        private ImsVoice() {}
+
+        /** Prefix of all imsvoice.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsvoice.";
+
+        /**
+         * Flag specifying whether VoLTE should be available when on
+         * roaming network.
+         *
+         * <p>If {@code false}: hard disabled.
+         * If {@code true}: then depends on availability, etc.
+         */
+        public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL  =
+                KEY_PREFIX + "carrier_volte_roaming_available_bool";
+
+        /**
+         * Flag specifying whether to send vertical caller id service codes
+         * (*67 and *82) in the dialed string in the SIP:INVITE.
+         *
+         * <p>If {@code true}, vertical caller id service codes *67 and *82
+         * will be sent in the dialed string in the SIP:INVITE.
+         * If {@code false}, *67 and *82 will be removed.
+         */
+        public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL  =
+                KEY_PREFIX + "include_caller_id_service_codes_in_sip_invite_bool";
+
+        /**
+         * Flag indicating whether Multi-end point setting is enabled or not.
+         */
+        public static final String KEY_MULTIENDPOINT_SUPPORTED_BOOL =
+                KEY_PREFIX + "multiendpoint_supported_bool";
+
+        /**
+         * Flag indicating whether Supported header field with the option tag
+         *  'timer' is enabled or not.
+         *
+         * <p>If {@code true}, session timer support is available.{@code false} otherwise.
+         *
+         * Reference: RFC 4028 Section 3
+         */
+        public static final String KEY_SESSION_TIMER_SUPPORTED_BOOL =
+                KEY_PREFIX + "session_timer_supported_bool";
+
+        /**
+         * Session-expires header field expressed in seconds as per
+         * RFC 4028 Section 3.
+         *
+         * <p>This establishes the upper bound for the session refresh interval.
+         */
+        public static final String KEY_SESSION_EXPIRES_TIMER_SEC_INT =
+                KEY_PREFIX + "session_expires_timer_sec_int";
+
+        /**
+         * Indicates the minimum value for the session interval in seconds.
+         * Represented as min-SE header field as per RFC 4028 Section 3.
+         *
+         * <p>This establishes the lower bound for the session refresh interval.
+         */
+        public static final String KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT =
+                KEY_PREFIX + "minimum_session_expires_timer_sec_int";
+
+        /** @hide */
+        @IntDef({
+            SESSION_REFRESHER_TYPE_UNKNOWN,
+            SESSION_REFRESHER_TYPE_UAC,
+            SESSION_REFRESHER_TYPE_UAS
+        })
+
+        public @interface SessionRefresherType {}
+
+        /**
+         * Session Refresher entity is unknown. This means UE does not include the
+         * "refresher" parameter in the Session-Expires header field of
+         * the SIP INVITE request.
+         */
+        public static final int SESSION_REFRESHER_TYPE_UNKNOWN = 0;
+
+        /**
+         * Session Refresher entity is User Agent Client (UAC).
+         *
+         * <p>Type of "refresher" parameter in the Session-Expires header field
+         * of the SIP INVITE request is UAC.
+         */
+        public static final int SESSION_REFRESHER_TYPE_UAC = 1;
+
+        /**
+         * Session Refresher entity is User Agent Server (UAS).
+         *
+         * <p>Type of "refresher" parameter in the Session-Expires header field
+         * of the SIP INVITE request is UAS.
+         */
+        public static final int SESSION_REFRESHER_TYPE_UAS = 2;
+
+        /**
+         * Session Refresher entity as per RFC 4028 and IR.92 Section 2.2.8.
+         *
+         * <p>This determines,
+         * a) whether to include the "refresher" parameter
+         * b) Type of refresher" parameter
+         * in the Session-Expires header field of the SIP INVITE request.
+         *
+         * <p>Possible values are,
+         * {@link #SESSION_REFRESHER_TYPE_UNKNOWN},
+         * {@link #SESSION_REFRESHER_TYPE_UAC},
+         * {@link #SESSION_REFRESHER_TYPE_UAS}
+         */
+        public static final String KEY_SESSION_REFRESHER_TYPE_INT =
+                KEY_PREFIX + "session_refresher_type_int";
+
+        /**
+         * Flag indicating whether PRACK must be enabled for all 18x messages.
+         *
+         * <p>If {@code false}, only 18x responses with SDP are sent reliably.
+         * If {@code true},  SIP 18x responses (other than SIP 183 response)
+         * are sent reliably.
+         */
+        public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL  =
+                KEY_PREFIX + "prack_supported_for_18x_bool";
+
+        /** @hide */
+        @IntDef({
+            CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG,
+            CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG
+        })
+
+        public @interface ConferenceSubscribeType {}
+
+        /**
+         * The SIP SUBSCRIBE to conference state events is sent in the
+         * SIP INVITE dialog between the UE and the conference server.
+         *
+         * <p>Reference: IR.92 Section 2.3.3.
+         */
+        public static final int CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG = 0;
+
+        /**
+         * The SIP SUBSCRIBE to conference state events is sent out of
+         * the SIP INVITE dialog between the UE and the conference server.
+         *
+         * <p>Reference: IR.92 Section 2.3.3.
+         */
+        public static final int CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG = 1;
+
+        /**
+         * This is used to specify whether the SIP SUBSCRIBE to conference state events,
+         * is sent in or out of the  SIP INVITE dialog between the UE and the
+         * conference server.
+         *
+         * <p>Reference: IR.92 Section 2.3.3.
+         *
+         * <p>Possible values are,
+         * {@link #CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG},
+         * {@link #CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG}
+         *
+         * An empty array indicates SUBSCRIBE to conference event package
+         * is not required.
+         */
+        public static final String KEY_CONFERENCE_SUBSCRIBE_TYPE_INT =
+                KEY_PREFIX + "conference_subscribe_type_int";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported during call setup.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported during call setup and
+         * 'precondition' tag is included in the SIP INVITE header and precondition
+         * parameters are sent in SDP as required.
+         * <p>If {@code false}: QoS Preconditions are not supported during call setup.
+         *
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL  =
+                KEY_PREFIX + "voice_qos_precondition_supported_bool";
+
+        /**
+         * Flag specifying whether voice is allowed on default bearer.
+         *
+         * <p>If {@code true}: voice packets can be sent on default bearer. {@code false} otherwise.
+         */
+        public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL  =
+                KEY_PREFIX + "voice_on_default_bearer_supported_bool";
+
+        /**
+         * Specifies the dedicated bearer wait time during call establishment.
+         *
+         * <p>If dedicated bearer is not established within this time and if
+         * {@link #KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL} is false, then call setup would fail.
+         * <p>If dedicated bearer is not established within this time and if
+         * {@link #KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL} is true, then the media is allowed
+         * on default bearer.
+         */
+        public static final String KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT =
+                KEY_PREFIX + "dedicated_bearer_wait_timer_millis_int";
+
+        /** @hide */
+        @IntDef({
+            BASIC_SRVCC_SUPPORT,
+            ALERTING_SRVCC_SUPPORT,
+            PREALERTING_SRVCC_SUPPORT,
+            MIDCALL_SRVCC_SUPPORT
+        })
+
+        public @interface SrvccType {}
+
+        /**
+         * Indicates support for basic SRVCC, typically 1 active call
+         * as detailed in IR.92 Section A.3.
+         */
+        public static final int BASIC_SRVCC_SUPPORT = 0;
+
+        /**
+         * SRVCC access transfer for calls in alerting phase as per 3GPP 24.237
+         * and IR.64 Section 4.4.
+         * Media feature tag used: g.3gpp.srvcc-alerting.
+         */
+        public static final int ALERTING_SRVCC_SUPPORT = 1;
+
+        /**
+         * SRVCC access transfer for calls in pre-alerting phase as per 3GPP 24.237.
+         * Media feature tag used: g.3gpp.ps2cs-srvcc-orig-pre-alerting.
+         */
+        public static final int PREALERTING_SRVCC_SUPPORT = 2;
+
+        /**
+         * SRVCC access transfer for calls in mid-call phase as per 3GPP 24.237.
+         * and IR.64 Section 4.4.
+         * <p>This means UE supports the MSC server assisted mid-call feature.
+         * Media feature tag used: g.3gpp.mid-call.
+         */
+        public static final int MIDCALL_SRVCC_SUPPORT = 3;
+
+        /**
+         * List of different SRVCC types supported as defined in 3GPP 24.237.
+         *
+         * <p> Possible values are,
+         * {@link #BASIC_SRVCC_SUPPORT},
+         * {@link #ALERTING_SRVCC_SUPPORT},
+         * {@link #PREALERTING_SRVCC_SUPPORT},
+         * {@link #MIDCALL_SRVCC_SUPPORT}
+         *
+         * <p> Reference: IR.64, 3GPP 24.237, 3GPP 23.216
+         */
+        public static final String KEY_SRVCC_TYPE_INT_ARRAY =
+                KEY_PREFIX + "srvcc_type_int_array";
+
+        /**
+         * Specifies the ringing timer for Mobile terminated calls.
+         *
+         * <p>Ringing timer starts when the device sends SIP 180 Ringing in
+         * response to a received SIP INVITE. If Ringing timer expires,
+         * the device sends SIP 486 response.
+         */
+        public static final String KEY_RINGING_TIMER_MILLIS_INT =
+                KEY_PREFIX + "ringing_timer_millis_int";
+
+        /**
+         * Specifies the ringback timer for Mobile originated calls.
+         *
+         * <p>Ringback timer starts when the device receives SIP 180 Ringing
+         * in response to its SIP INVITE. If Ringback timer expires,
+         * the device sends SIP CANCEL.
+         */
+        public static final String KEY_RINGBACK_TIMER_MILLIS_INT =
+                KEY_PREFIX + "ringback_timer_millis_int";
+
+        /**
+         * Specifies the timeout value for RTP inactivity for audio media.
+         * <p>On timer expiry, call will end.
+         * See {@link #KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY} for more
+         * details.
+         * <p> Value of 0 means this timer is not enabled.
+         */
+        public static final String KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "audio_rtp_inactivity_timer_millis_int";
+
+        /**
+         * Specifies the timeout value for RTCP inactivity for audio media.
+         * <p>On timer expiry, call will end.
+         * See {@link #KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY} for more
+         * details.
+         * <p> Value of 0 means this timer is not enabled.
+         */
+        public static final String KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "audio_rtcp_inactivity_timer_millis_int";
+
+        /**
+         * Used to specify the conference factory URI.
+         *
+         * <p>If this is empty, then conference URI is generated from MCC/MNC as
+         * specified in clause 13.10 of 3GPP 23.003.
+         */
+        public static final String KEY_CONFERENCE_FACTORY_URI_STRING =
+                KEY_PREFIX + "conference_factory_uri_string";
+
+        /** @hide */
+        @IntDef({
+            SESSION_REFRESH_METHOD_INVITE,
+            SESSION_REFRESH_METHOD_UPDATE_PREFERRED
+        })
+
+        public @interface SessionRefreshMethod {}
+
+        /**
+         * SIP INVITE is used for Session Refresh
+         */
+        public static final int SESSION_REFRESH_METHOD_INVITE = 0;
+
+        /**
+         * Both SIP INVITE and UPDATE are used for session refresh.
+         *
+         * <p>SIP UPDATE will be used if UPDATE is in 'Allow' header.
+         * If UPDATE is not in 'Allow' header, then INVITE will be used.
+         */
+        public static final int SESSION_REFRESH_METHOD_UPDATE_PREFERRED = 1;
+
+        /**
+         * This is used to specify the method used for session refresh.
+         *
+         * <p>Possible values are,
+         * {@link #SESSION_REFRESH_METHOD_INVITE},
+         * {@link #SESSION_REFRESH_METHOD_UPDATE_PREFERRED}
+         */
+        public static final String KEY_SESSION_REFRESH_METHOD_INT =
+                KEY_PREFIX + "session_refresh_method_int";
+
+        /**
+         * Flag specifying whether the 'From' header field is used for determination of
+         * the originating party identity in Originating Identification Presentation(OIP)
+         * service.
+         *
+         * <p>If {@code true}: Indicates that the 'From' header field is used for
+         * determination of the originating party identity in OIP.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL  =
+                KEY_PREFIX + "oip_source_from_header_bool";
+
+        /**
+         * Specifies the timer value for INVITE to the first 1xx response
+         * (including 100 trying). If no response is received at timer expiry,
+         * call is redialed over CS.
+         *
+         * <p> Reference: 24.173 Table L.1
+         */
+        public static final String KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT =
+                KEY_PREFIX + "mo_call_request_timeout_millis_int";
+
+        /**
+         * List of various reasons of media inactivity for which
+         * voice/emergency call will end.
+         *
+         * <p>Possible values are,
+         * {@link Ims#RTCP_INACTIVITY_ON_HOLD},
+         * {@link Ims#RTCP_INACTIVITY_ON_CONNECTED},
+         * {@link Ims#RTP_INACTIVITY_ON_CONNECTED}
+         * {@link Ims#E911_RTCP_INACTIVITY_ON_CONNECTED},
+         * {@link Ims#E911_RTP_INACTIVITY_ON_CONNECTED}
+         */
+        public static final String KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY =
+                KEY_PREFIX + "audio_inactivity_call_end_reasons_int_array";
+
+        /**
+         * Specifies the AS (Application Specific) SDP modifier for audio media.
+         *
+         * <p>This value is expressed in kilobits per second.
+         * Reference: RFC 3556 Section 2.
+         */
+        public static final String KEY_AUDIO_AS_BANDWIDTH_KBPS_INT =
+                KEY_PREFIX + "audio_as_bandwidth_kbps_int";
+
+        /**
+         * Specifies the RS SDP modifier for audio media. This indicates the RTCP
+         * bandwidth allocated to active data senders for audio media.
+         *
+         * <p>This value is expressed in bits per second.
+         * Reference: RFC 3556 Section 2.
+         */
+        public static final String KEY_AUDIO_RS_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "audio_rs_bandwidth_bps_int";
+
+        /**
+         * Specifies the RR SDP modifier for audio media. This indicates the RTCP
+         * bandwidth allocated to receivers for audio media.
+         *
+         * <p>This value is expressed in bits per second.
+         * Reference: RFC 3556 Section 2.
+         */
+        public static final String KEY_AUDIO_RR_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "audio_rr_bandwidth_bps_int";
+
+        /**
+         * Specifies the Audio Codec capability. This contains a list of payload types
+         * representing different audio codec instances.
+         *
+         * <p> The priority of the codecs is EVS, AMRWB, AMRNB,  DTMF WB, DTMF NB
+         * from highest to lowest. In each individual codec, the priority is determined
+         * by the order of the payload types from highest to lowest.
+         *
+         * <p>Possible keys in this bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_EVS_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         * </UL>
+         * <p>To specify payload descriptions for each of the audio payload types, see
+         * <UL>
+         *     <LI>{@link #KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         *     <LI>{@link #KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         *     <LI>{@link #KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         * </UL>
+         */
+        public static final String KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE =
+                KEY_PREFIX + "audio_codec_capability_payload_types_bundle";
+
+        /**
+         * A list of integers representing the different payload types
+         * in EVS codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY  =
+                KEY_PREFIX + "evs_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in AMR-WB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY  =
+                KEY_PREFIX + "amrwb_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in AMR-NB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY  =
+                KEY_PREFIX + "amrnb_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in DTMF WB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY  =
+                KEY_PREFIX + "dtmfwb_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in DTMF NB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY  =
+                KEY_PREFIX + "dtmfnb_payload_type_int_array";
+
+        /** @hide */
+        @IntDef({
+            BANDWIDTH_EFFICIENT,
+            OCTET_ALIGNED
+        })
+
+        public @interface AmrPayloadFormat {}
+
+        /** AMR NB/WB Payload format is bandwidth-efficient. */
+        public static final int BANDWIDTH_EFFICIENT = 0;
+
+        /** AMR NB/WB Payload format is octet-aligned. */
+        public static final int OCTET_ALIGNED = 1;
+
+        /**
+         * Specifies the payload format of the AMR-NB/AMR-WB codec.
+         *
+         * <p>Possible values are,
+         * {@link #BANDWIDTH_EFFICIENT},
+         * {@link #OCTET_ALIGNED}
+
+         * <p>If value is not specified, payload format is
+         * {@link #BANDWIDTH_EFFICIENT}.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT  =
+                KEY_PREFIX + "amr_codec_attribute_payload_format_int";
+
+        /**
+         * Restricts the active mode set to a subset of all modes in the codec.
+         *
+         * <p>This attribute is optional. If value is set, then session mode
+         * set is restricted to the modes specified in this list. If this value
+         * is not specified, then all available modes in the codec are allowed.
+         * This attribute is applicable for AMR-WB, AMR-NB,
+         * and EVS codec (operating in AMR-WB IO Mode).
+         *
+         * <p>Possible values are subset of,
+         * [0,1,2,3,4,5,6,7,8] - AMRWB with the modes representing nine speech codec modes
+         * with bit rates of 6.6, 8.85, 12.65, 14.25,  15.85, 18.25, 19.85, 23.05, 23.85 kbps.
+         * [0,1,2,3,4,5,6,7] - AMRNB  with the modes representing eight speech codec modes
+         * with bit rates of 4.75, 5.15, 5.90, 6.70, 7.40, 7.95, 10.2, 12.2 kbps.
+         *
+         * <p>If value is not specified, then it means device supports all
+         * modes in the codec but not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1, 3GPP 26.445 A.3.1
+         */
+        public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY  =
+                KEY_PREFIX + "amr_codec_attribute_modeset_int_array";
+
+        /**
+         * Specifies the codec attributes of different payload types in
+         * the AMR NarrowBand (AMR-NB) codec.
+         *
+         * <p> The keys in this bundle are payload types specified
+         * in {@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of AMR-NB codec bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT}</LI>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY}</LI>
+         * </UL>
+         *
+         * <p> If this bundle is not configured and AMRNB payload type is added
+         * in {@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}, then default
+         * values as in the individual codec attribute to be used
+         * for that payload type.
+         */
+        public static final String KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "amrnb_payload_description_bundle";
+
+        /**
+         * Specifies the codec attributes of different payload types in
+         * the AMR WideBand (AMR-WB) codec.
+         *
+         * <p> The keys in this bundle are payload types specified
+         * in {@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of AMR-NB codec bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT}</LI>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY}</LI>
+         * </UL>
+         *
+         * <p> If this bundle is not configured and AMRWB payload type is added
+         * in {@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}, then default
+         * values as in the individual codec attribute to be used
+         * for that payload type.
+         */
+        public static final String KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "amrwb_payload_description_bundle";
+
+        /** @hide */
+        @IntDef({
+            EVS_OPERATIONAL_MODE_PRIMARY,
+            EVS_OPERATIONAL_MODE_AMRWB_IO
+        })
+
+        public @interface EvsOperationalMode {}
+
+        /**  Indicates the EVS primary mode. 3GPP 26.445 Section 3.1 */
+        public static final int EVS_OPERATIONAL_MODE_PRIMARY = 0;
+
+        /** Indicates the EVS AMR-WB IO mode. 3GPP 26.445 Section 3.1 */
+        public static final int EVS_OPERATIONAL_MODE_AMRWB_IO = 1;
+
+        /**
+         * Specifies if the EVS mode used is EVS primary mode
+         * or EVS AMR-WB IO mode.
+         *
+         * <p>Possible values are,
+         * {@link #EVS_OPERATIONAL_MODE_PRIMARY},
+         * {@link #EVS_OPERATIONAL_MODE_AMRWB_IO}
+         *
+         * <p>If this is not present, then {@link #EVS_OPERATIONAL_MODE_PRIMARY} is used.
+         * <p>Reference: 3GPP 26.445 Section 3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT =
+                KEY_PREFIX + "evs_codec_attribute_mode_switch_int";
+
+        /** @hide */
+        @IntDef({
+            EVS_ENCODED_BW_TYPE_NB,
+            EVS_ENCODED_BW_TYPE_WB,
+            EVS_ENCODED_BW_TYPE_SWB,
+            EVS_ENCODED_BW_TYPE_FB,
+            EVS_ENCODED_BW_TYPE_NB_WB,
+            EVS_ENCODED_BW_TYPE_NB_WB_SWB,
+            EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB,
+            EVS_ENCODED_BW_TYPE_WB_SWB,
+            EVS_ENCODED_BW_TYPE_WB_SWB_FB
+        })
+
+        public @interface EvsEncodedBwType {}
+
+        /**
+         * EVS encoded Bandwidth is Narrow Band (NB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB = 0;
+
+        /**
+         * EVS encoded Bandwidth is Wide Band (WB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_WB = 1;
+
+        /**
+         * EVS encoded Bandwidth is Super WideBand (SWB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_SWB = 2;
+
+        /**
+         * EVS encoded Bandwidth is Full Band (FB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_FB = 3;
+
+        /**
+         * EVS encoded Bandwidth is in the range NB,WB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB_WB = 4;
+
+        /**
+         * EVS encoded Bandwidth is in the range NB,WB,SWB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB = 5;
+
+        /**
+         * EVS encoded Bandwidth is in the range NB,WB,SWB,FB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB = 6;
+
+        /**
+         * EVS encoded Bandwidth is in the range WB,SWB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_WB_SWB = 7;
+
+        /**
+         * EVS encoded Bandwidth is in the range WB,SWB,FB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_WB_SWB_FB = 8;
+
+        /**
+         * Specifies the EVS codec encoding bandwidth options.
+         *
+         * Possible values are,
+         * {@link #EVS_ENCODED_BW_TYPE_NB},
+         * {@link #EVS_ENCODED_BW_TYPE_WB},
+         * {@link #EVS_ENCODED_BW_TYPE_SWB},
+         * {@link #EVS_ENCODED_BW_TYPE_FB},
+         * {@link #EVS_ENCODED_BW_TYPE_NB_WB},
+         * {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB},
+         * {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB},
+         * {@link #EVS_ENCODED_BW_TYPE_WB_SWB},
+         * {@link #EVS_ENCODED_BW_TYPE_WB_SWB_FB}
+         *
+         * If this key is not specified, then the behavior is same as
+         * value {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB}
+         *
+         * <p>Reference: 3GPP 26.441 Table 1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT  =
+                KEY_PREFIX + "evs_codec_attribute_bandwidth_int";
+
+        /** @hide */
+        @IntDef({
+            EVS_PRIMARY_MODE_BITRATE_5_9_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_7_2_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_8_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_9_6_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_13_2_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_16_4_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_24_4_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_32_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_48_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_64_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_96_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_128_0_KBPS
+        })
+
+        public @interface EvsPrimaryModeBitRate {}
+
+        /** EVS primary mode with bitrate 5.9 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_5_9_KBPS = 0;
+
+        /** EVS primary mode with bitrate 7.2 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_7_2_KBPS = 1;
+
+        /** EVS primary mode with bitrate 8.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_8_0_KBPS = 2;
+
+        /** EVS primary mode with bitrate 9.6 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_9_6_KBPS = 3;
+
+        /** EVS primary mode with bitrate 13.2 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_13_2_KBPS = 4;
+
+        /** EVS primary mode with bitrate 16.4 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_16_4_KBPS = 5;
+
+        /** EVS primary mode with bitrate 24.4 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_24_4_KBPS = 6;
+
+        /** EVS primary mode with bitrate 32.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_32_0_KBPS = 7;
+
+        /** EVS primary mode with bitrate 48.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_48_0_KBPS = 8;
+
+        /** EVS primary mode with bitrate 64.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_64_0_KBPS = 9;
+
+        /** EVS primary mode with bitrate 96.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_96_0_KBPS = 10;
+
+        /** EVS primary mode with bitrate 128.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_128_0_KBPS = 11;
+
+        /**
+         * Specifies the range of source codec bit-rate for EVS Primary mode
+         * in the session. This is expressed in kilobits per second and
+         * applicable for both the send and the receive directions.
+         *
+         * <p>The range is specified as integer aray of size 2,
+         * represented as [low, high], where low <= high
+         *
+         * <p>Possible values for low and high are,
+         * {@link #EVS_PRIMARY_MODE_BITRATE_5_9_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_7_2_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_8_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_9_6_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_13_2_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_16_4_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_24_4_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_32_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_48_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_64_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_96_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_128_0_KBPS}
+         *
+         * If this key is not specified, then the behavior is same as
+         * value {@link #EVS_PRIMARY_MODE_BITRATE_24_4_KBPS}
+         *
+         * <p>Reference: 3GPP 26.445 Section A.3.1
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY  =
+                KEY_PREFIX + "evs_codec_attribute_bitrate_int_array";
+
+        /**
+         * Specifies the Channel aware mode (ch-aw-recv) for the receive direction.
+         * This is applicable for EVS codec.
+         *
+         * <p>Permissible values  are -1, 0, 2, 3, 5, and 7.
+         * If this key is not specified, then the behavior is same as value 0
+         * (channel aware mode disabled).
+         * <p> If this key is configured, then device is expected to send
+         * this parameter in the SDP offer.
+         *
+         * <p>Reference: 3GPP TS 26.445 section 4.4.5, 3GPP 26.445 Section A.3.1
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT  =
+                KEY_PREFIX + "evs_codec_attribute_ch_aw_recv_int";
+
+        /**
+         * Specifies whether to limit the session to header-full format.
+         * This applies to both directions in the session. This attribute
+         * is applicable for EVS codec.
+         *
+         * <p>Permissible values are 0, 1
+         * If hf-only is 1, only Header-Full format is used and hf-only is
+         * included in the SDP.
+         * <p>If hf-only is 0, both Compact and Header-Full formats are used
+         * and hf-only is included in the SDP.
+         * <p>If this key is not present, then both Compact
+         * and Header-Full formats are used and hf-only is not included in
+         * the SDP.
+         * <p> If this key is configured, then device is expected to send
+         * this parameter in the SDP offer if operator required it.
+         *
+         * <p>Reference: 3GPP 26.445 Section A.3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT  =
+                KEY_PREFIX + "evs_codec_attribute_hf_only_int";
+
+        /**
+         * Specifies whether DTX (Discontinuous transmission) is enabled
+         * or not. This applies to both directions in the session.
+         * This attribute is applicable for EVS codec and can be used
+         * in both EVS Primary mode and EVS AMR-WB IO mode.
+         *
+         * <p>If {@code true}: Indicates DTX is enabled.
+         * If {@code false}: Indicates DTX is disabled.
+         *
+         * <p>If this is not present, then default value of {@code true}
+         * will apply.
+         * <p>Reference: 3GPP TS 26.445 Section A.3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL  =
+                KEY_PREFIX + "evs_codec_attribute_dtx_bool";
+
+        /**
+         * This is used if further restriction is required on DTX in the
+         * receive direction. This attribute is applicable for EVS codec
+         * and can be used in both EVS Primary mode and EVS AMR-WB IO mode.
+         *
+         * <p> If this value is true or not present, then DTX setting is
+         * dependent on {@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL}.
+         *
+         * <p> If this is not present, then default value of {@code true}
+         * will apply.
+         *
+         * <p>Reference: 3GPP TS 26.445 Section A.3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL =
+                KEY_PREFIX + "evs_codec_attribute_dtx_recv_bool";
+
+        /**
+         * Specifies the number of audio channels.
+         * If this is not present, then default value of 1 will apply.
+         *
+         * <p>Reference: RFC 3551
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT  =
+                KEY_PREFIX + "evs_codec_attribute_channels_int";
+
+        /**
+         * Indicates whether the Codec Mode Request (CMR) is supported
+         * for the session.
+         * This attribute is applicable for EVS codec in Primary Mode only.
+         *
+         * <p>Possible values are -1, 0, 1. If this key is not present,
+         * then behavior as per value 0 is applicable.
+         *
+         * <p>Reference: 3GPP 26.445 Section A.3.1, 3GPP 26.114 Table 6.2a
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT  =
+                KEY_PREFIX + "codec_attribute_cmr_int";
+
+        /**
+         * Specifies the number of frame-blocks. This indicates the frame-block period
+         * at which codec mode changes are allowed for the sender. This attribute is
+         * applicable for EVS codec in AMR-WB IO mode and AMR-WB.
+         *
+         * <p>Possible values are 1, 2.
+         * If the key is not present, behavior as per value 1 is applicable and this
+         * parameter is not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT  =
+                KEY_PREFIX + "codec_attribute_mode_change_period_int";
+
+        /**
+         * Specifies if the client is capable to transmit with a restricted mode
+         * change period. This attribute is applicable for EVS codec in
+         * AMR-WB IO mode and AMR-WB.
+         *
+         * <p>Possible values are 1, 2. If this key is not present,
+         * then behavior as per value 1 is applicable and this
+         * parameter is not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT  =
+                KEY_PREFIX + "codec_attribute_mode_change_capability_int";
+
+        /**
+         * Specifies the allowed mode changes for the sender in the active mode set.
+         * This attribute is applicable for EVS codec in AMR-WB IO mode
+         * and AMR-WB.
+         *
+         * <p>Possible values are 0, 1.  If value is 1, then the sender should only
+         * perform mode changes to the neighboring modes in the active codec mode set.
+         * If value is 0, then mode changes between any two modes
+         * in the active codec mode set is allowed.
+         * If the key is not present, behavior as per value 0 is applicable and this
+         * parameter is not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT  =
+                KEY_PREFIX + "codec_attribute_mode_change_neighbor_int";
+
+        /**
+         * Specifies the codec attributes of different payload types in
+         * the EVS codec.
+         *
+         * <p> The keys in this bundle are payload types specified
+         * in {@link #KEY_EVS_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of EVS codec are,
+         * <UL>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CMR_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT}</LI>
+         *     <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT}</LI>
+         *     <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT}</LI>
+         *     <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT}</LI>
+         * </UL>
+         */
+        public static final String KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "evs_payload_description_bundle";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL, true);
+            defaults.putBoolean(KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL, false);
+            defaults.putBoolean(KEY_MULTIENDPOINT_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_SESSION_TIMER_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_OIP_SOURCE_FROM_HEADER_BOOL, false);
+            defaults.putBoolean(KEY_PRACK_SUPPORTED_FOR_18X_BOOL, true);
+            defaults.putBoolean(KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
+
+            defaults.putInt(KEY_SESSION_REFRESHER_TYPE_INT, SESSION_REFRESHER_TYPE_UNKNOWN);
+            defaults.putInt(KEY_SESSION_REFRESH_METHOD_INT,
+                            SESSION_REFRESH_METHOD_UPDATE_PREFERRED);
+            defaults.putInt(KEY_CONFERENCE_SUBSCRIBE_TYPE_INT,
+                            CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG);
+            defaults.putInt(KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT, 20000);
+            defaults.putInt(KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT, 20000);
+            defaults.putInt(KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT, 8000);
+            defaults.putInt(KEY_RINGING_TIMER_MILLIS_INT, 90000);
+            defaults.putInt(KEY_RINGBACK_TIMER_MILLIS_INT, 90000);
+            defaults.putInt(KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT, 5000);
+            defaults.putInt(KEY_SESSION_EXPIRES_TIMER_SEC_INT, 1800);
+            defaults.putInt(KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT, 90);
+            defaults.putInt(KEY_AUDIO_AS_BANDWIDTH_KBPS_INT, 41);
+            defaults.putInt(KEY_AUDIO_RS_BANDWIDTH_BPS_INT, 600);
+            defaults.putInt(KEY_AUDIO_RR_BANDWIDTH_BPS_INT, 2000);
+
+            defaults.putIntArray(
+                    KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY,
+                    new int[] {
+                        Ims.RTCP_INACTIVITY_ON_CONNECTED,
+                        Ims.RTP_INACTIVITY_ON_CONNECTED
+                    });
+
+            defaults.putIntArray(
+                    KEY_SRVCC_TYPE_INT_ARRAY,
+                    new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                        ALERTING_SRVCC_SUPPORT,
+                        PREALERTING_SRVCC_SUPPORT,
+                        MIDCALL_SRVCC_SUPPORT
+                    });
+
+            defaults.putString(KEY_CONFERENCE_FACTORY_URI_STRING, "");
+
+            PersistableBundle audio_codec_capability_payload_types = new PersistableBundle();
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 97, 98 });
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 99, 100 });
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 101 });
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 102 });
+
+            defaults.putPersistableBundle(
+                    KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE,
+                    audio_codec_capability_payload_types);
+
+            /* Setting defaults for AMRWB */
+            PersistableBundle all_amrwb_payload_bundles = new PersistableBundle();
+            PersistableBundle amrwb_bundle_instance1 = new PersistableBundle();
+
+            all_amrwb_payload_bundles.putPersistableBundle(
+                    "97", /* Same value of payload type as in KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrwb_bundle_instance1);
+
+            PersistableBundle amrwb_bundle_instance2 = new PersistableBundle();
+
+            amrwb_bundle_instance2.putInt(KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT,
+                                          OCTET_ALIGNED);
+
+            all_amrwb_payload_bundles.putPersistableBundle(
+                    "98", /* Same value of payload type as in KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrwb_bundle_instance2);
+
+            defaults.putPersistableBundle(
+                    KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE,
+                    all_amrwb_payload_bundles);
+
+            /* Setting defaults for AMRNB */
+            PersistableBundle all_amrnb_payload_bundles = new PersistableBundle();
+            PersistableBundle amrnb_bundle_instance1 = new PersistableBundle();
+
+            all_amrnb_payload_bundles.putPersistableBundle(
+                    "99", /* Same value of payload type as in KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrnb_bundle_instance1);
+
+            PersistableBundle amrnb_bundle_instance2 = new PersistableBundle();
+
+            amrnb_bundle_instance2.putInt(KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT,
+                                          OCTET_ALIGNED);
+
+            all_amrnb_payload_bundles.putPersistableBundle(
+                    "100", /* Same value of payload type as in KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrnb_bundle_instance2);
+
+            defaults.putPersistableBundle(
+                    KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE,
+                    all_amrnb_payload_bundles);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS SMS configs. This groups the configs specific for SMS over IMS
+     */
+    public static final class ImsSms {
+        private ImsSms() {}
+
+        /** Prefix of all imssms.KEY_* constants. */
+        public static final String KEY_PREFIX = "imssms.";
+
+        /**
+         * Flag specifying if SMS over IMS support is available or not.
+         *
+         * <p>If {@code true}: SMS over IMS support available.
+         * {@code false}: otherwise.
+         */
+        public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL  =
+                KEY_PREFIX + "sms_over_ims_supported_bool";
+
+        /**
+         * Flag specifying whether to allow SMS CSFB in case of
+         * SMS over PS failure.
+         *
+         * <p>If {@code true}: allow SMS CSFB in case of SMS over PS failure.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL  =
+                KEY_PREFIX + "sms_csfb_retry_on_failure_bool";
+
+        /** @hide */
+        @IntDef({
+            SMS_FORMAT_3GPP,
+            SMS_FORMAT_3GPP2
+        })
+
+        public @interface SmsFormat {}
+
+        /** SMS format is 3GPP. */
+        public static final int SMS_FORMAT_3GPP = 0;
+
+        /** SMS format is 3GPP2. */
+        public static final int SMS_FORMAT_3GPP2 = 1;
+
+        /**
+         * Specifies the SMS over IMS format.
+         *
+         * <p>Possible values are,
+         * {@link #SMS_FORMAT_3GPP},
+         * {@link #SMS_FORMAT_3GPP2}
+         */
+        public static final String KEY_SMS_OVER_IMS_FORMAT_INT =
+                KEY_PREFIX + "sms_over_ims_format_int";
+
+        /**
+         * List of different RAT technologies on which SMS over IMS
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         */
+        public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "sms_over_ims_supported_rats_int_array";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_SMS_OVER_IMS_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL, true);
+
+            defaults.putInt(KEY_SMS_OVER_IMS_FORMAT_INT, SMS_FORMAT_3GPP);
+
+            defaults.putIntArray(
+                    KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN
+                    });
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS RTT configs. This groups the configs specific for text media,
+     * RTT (Real Time Text).
+     */
+    public static final class ImsRtt {
+        private ImsRtt() {}
+
+        /** Prefix of all imsrtt.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsrtt.";
+
+        /**
+         * Flag specifying whether text media is allowed on default bearer.
+         *
+         * <p>If {@code true}: text media can be sent on default bearer.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL  =
+                KEY_PREFIX + "text_on_default_bearer_supported_bool";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported for text.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported.
+         * {@code false} otherwise.
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL  =
+                KEY_PREFIX + "text_qos_precondition_supported_bool";
+
+        /**
+         * Specifies the AS (Application Specific) SDP modifier for text media.
+         *
+         * <p>Expressed in kilobits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_TEXT_AS_BANDWIDTH_KBPS_INT =
+                KEY_PREFIX + "text_as_bandwidth_kbps_int";
+
+        /**
+         * Specifies the RS (RTCP bandwidth-Sender) SDP modifier for text media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to active data senders
+         * for text media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_TEXT_RS_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "text_rs_bandwidth_bps_int";
+
+        /**
+         * Specifies the RR (RTCP bandwidth-Receiver) SDP modifier for
+         * text media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to receivers
+         * for text media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_TEXT_RR_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "text_rr_bandwidth_bps_int";
+
+        /**
+         * List of various reasons for RTT call to end due to
+         * media inactivity.
+         *
+         * <p>Possible values are,
+         * <UL>
+         *     <LI>{@link Ims#RTCP_INACTIVITY_ON_HOLD}</LI>
+         *     <LI>{@link Ims#RTCP_INACTIVITY_ON_CONNECTED}</LI>
+         *     <LI>{@link Ims#RTP_INACTIVITY_ON_CONNECTED}</LI>
+         *     <LI>{@link Ims#E911_RTCP_INACTIVITY_ON_CONNECTED}</LI>
+         *     <LI>{@link Ims#E911_RTP_INACTIVITY_ON_CONNECTED}</LI>
+         * </UL>
+         */
+        public static final String KEY_TEXT_INACTIVITY_CALL_END_REASONS_INT_ARRAY =
+                KEY_PREFIX + "text_inactivity_call_end_reasons_int_array";
+
+        /**
+         * Specifies the Text Codec capability.
+         *
+         * <p>Possible keys in this bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_T140_PAYLOAD_TYPE_INT}</LI>
+         *     <LI>{@link #KEY_RED_PAYLOAD_TYPE_INT}</LI>
+         * </UL>
+         */
+        public static final String KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE =
+                KEY_PREFIX + "text_codec_capability_payload_types_bundle";
+
+        /** Integer representing payload type for T140 codec.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_T140_PAYLOAD_TYPE_INT  =
+                KEY_PREFIX + "t140_payload_type_int";
+
+        /** Integer representing payload type for RED/redundancy codec.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_RED_PAYLOAD_TYPE_INT  =
+                KEY_PREFIX + "red_payload_type_int";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_TEXT_AS_BANDWIDTH_KBPS_INT, 4);
+            defaults.putInt(KEY_TEXT_RS_BANDWIDTH_BPS_INT, 100);
+            defaults.putInt(KEY_TEXT_RR_BANDWIDTH_BPS_INT, 300);
+
+            defaults.putIntArray(
+                    KEY_TEXT_INACTIVITY_CALL_END_REASONS_INT_ARRAY,
+                    new int[] {
+                        Ims.RTCP_INACTIVITY_ON_CONNECTED,
+                        Ims.RTP_INACTIVITY_ON_CONNECTED
+                    });
+
+            PersistableBundle text_codec_capability_payload_types = new PersistableBundle();
+
+            text_codec_capability_payload_types.putInt(
+                    KEY_RED_PAYLOAD_TYPE_INT,
+                    112);
+
+            text_codec_capability_payload_types.putInt(
+                    KEY_T140_PAYLOAD_TYPE_INT,
+                    111);
+
+            defaults.putPersistableBundle(
+                    KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE,
+                    text_codec_capability_payload_types);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * Emergency Call/E911. This groups the configs specific for emergency call
+     * over IMS.
+     *
+     * <p> Reference: 3GPP 24.229, 3GPP 23.167 Annex H, 3GPP 24.301.
+     */
+    public static final class ImsEmergency {
+        private ImsEmergency() {}
+
+        /** Prefix of all imsemergency.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsemergency.";
+
+        /**
+         * Flag specifying whether UE would retry E911 call on
+         * IMS PDN if emergency PDN setup failed.
+         *
+         * <p>If {@code true}: Allow UE to retry emergency call on
+         * IMS PDN if emergency PDN setup failed.{@code false} otherwise.
+         */
+        public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL  =
+                KEY_PREFIX + "retry_emergency_on_ims_pdn_bool";
+
+        /**
+         * Flag specifying whether UE should enter Emergency CallBack Mode(ECBM)
+         * after E911 call is ended.
+         *
+         * <p>If {@code true}: Enter ECBM mode after E911 call is ended.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL  =
+                KEY_PREFIX + "emergency_callback_mode_supported_bool";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported for emergency
+         * call setup.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported.
+         * {@code false} otherwise.
+         *
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL  =
+                KEY_PREFIX + "emergency_qos_precondition_supported_bool";
+
+        /**
+         * List of different RAT technologies on which emergency call using IMS
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         */
+        public static final String KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "emergency_over_ims_supported_rats_int_array";
+
+        /**
+         * Specifies the maximum time from deciding that an emergency service is to
+         * be established until completion of the emergency registration procedure.
+         * Upon timer expiry, the UE considers the emergency REGISTER request or
+         * the emergency call attempt as failed.
+         */
+        public static final String KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT =
+                KEY_PREFIX + "emergency_registration_timer_millis_int";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL, false);
+            defaults.putBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+
+            defaults.putIntArray(
+                    KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN
+                    });
+
+            defaults.putInt(KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT, 20000);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS Video Telephony configs. This groups the configs that are specific for video call.
+     */
+    public static final class ImsVt {
+        private ImsVt() {}
+
+        /** Prefix of all imsvt.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsvt.";
+
+        /**
+         * Flag specifying whether video media is allowed on default bearer.
+         *
+         * <p>If {@code true}: video media can be sent on default bearer.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL  =
+                KEY_PREFIX + "video_on_default_bearer_supported_bool";
+
+        /**
+         * Specifies the timeout value for no video RTP packets received.
+         * <p>On timer expiry, VT call can downgrade to voice call or end
+         * or continue depending on the operator requirement.
+         */
+        public static final String KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "video_rtp_inactivity_timer_millis_int";
+
+        /**
+         * Specifies the timeout value for no video RTCP packets received.
+         * <p>On timer expiry, VT call can downgrade to voice call or end
+         * or continue depending on the operator requirement.
+         */
+        public static final String KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "video_rtcp_inactivity_timer_millis_int";
+
+        /**
+         * Specifies the AS (Application Specific) SDP modifier for video media.
+         *
+         * <p>Expressed in kilobits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_VIDEO_AS_BANDWIDTH_KBPS_INT =
+                KEY_PREFIX + "video_as_bandwidth_kbps_int";
+
+        /**
+         * Specifies the RS (RTCP bandwidth-Sender) SDP modifier for video media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to active data senders
+         * for video media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_VIDEO_RS_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "video_rs_bandwidth_bps_int";
+
+        /**
+         * Specifies the RR (RTCP bandwidth-Receiver) SDP modifier
+         * for video media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to receivers
+         * for video media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_VIDEO_RR_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "video_rr_bandwidth_bps_int";
+
+        /**
+         * Specifies the differentiated services code point (DSCP) value
+         * for Video RTP.
+         *
+         * <p>Reference: RFC 4594 Section 1.4.4
+         */
+        public static final String KEY_VIDEO_RTP_DSCP_INT =
+                KEY_PREFIX + "video_rtp_dscp_int";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported for Video.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported.
+         * {@code false} otherwise.
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL  =
+                KEY_PREFIX + "video_qos_precondition_supported_bool";
+
+        /**
+         * Specifies the Video Codec capability. This contains a list of
+         * payload types representing different Video codec instances.
+
+         * <p>Possible key(s) in this bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         * </UL>
+         * <p>To specify payload descriptions for each of the payload types, see
+         * <UL>
+         *     <LI>{@link #KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         * </UL>
+         */
+        public static final String KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE =
+                KEY_PREFIX + "video_codec_capability_payload_types_bundle";
+
+        /**
+         * A list of integers representing the different payload types
+         * in H264 video codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY  =
+                KEY_PREFIX + "h264_payload_type_int_array";
+
+        /**
+         * Specifies the codec attributes of different payload types
+         * representing H264 video codec instances.
+         *
+         * <p> The allowed payload types of the video codecs are specified in,
+         * {@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of H264 codec bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING}</LI>
+         *     <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT}</LI>
+         *     <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT}</LI>
+         *     <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY}</LI>
+         * </UL>
+         *
+         * <p>If this bundle is not configured and
+         * {@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY} is not empty,
+         * then default values as in the individual codec attributes to
+         * be used for that payload type.
+         * <p>If the codec attributes in a particular codec instance bundle
+         * is not valid together, then that codec instance should not be used.
+         */
+        public static final String KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "h264_payload_description_bundle";
+
+        /**
+         * Specifies the packetization mode of the video codec.
+         *
+         * <p>Permissible values are 0 (Single NAL unit mode),
+         * 1(Non-interleaved mode).
+         *
+         * <p>If this key is not specified or invalid, then the following
+         * default value to be used.
+         * <UL>
+         *   <LI>For H264: 1(Non-interleaved mode)</LI>
+         * <UL>
+         *
+         * <p>Reference: RFC 6184 Section 5.4
+         */
+        public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT  =
+                KEY_PREFIX + "video_codec_attribute_packetization_mode_int";
+
+        /**
+         * Specifies the maximum frame rate the offerer wishes to receive.
+         * This gives the maximum video frame rate in frames/sec.
+         *
+         * <p>If this key is not specified or invalid, then the following
+         * default value to be used.
+         * <UL>
+         *   <LI>For H264: 15 </LI>
+         * <UL>
+         * <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2
+         */
+        public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT  =
+                KEY_PREFIX + "video_codec_attribute_frame_rate_int";
+
+        /**
+         * Specifies the maximum resolution allowed for the video codec
+         * instance.
+         *
+         * <p>This is specified as an array of two integers, with
+         * index 0 : Width,
+         * index 1 : Height
+         *
+         * <p>If this key is not specified or invalid as per the video codec,
+         * then the following default value to be used.
+         * <UL>
+         *   <LI>For H264: 240 (WIDTH) x 320 (HEIGHT) </LI>
+         * <UL>
+         * <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2
+         *
+         */
+        public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY  =
+                KEY_PREFIX + "video_codec_attribute_resolution_int_array";
+
+        /**
+         * Specifies the profile level id of the H264 video codec.
+         * This value is represented as "profile-level-id" in the SDP offer
+         * as per RFC 6184 Section 8.1.
+         *
+         * <p>If this key is not specified or invalid as per the video codec,
+         * then default value of 42C00C to be used.
+         *
+         * <p>Reference: RFC 6184 Section 8.1, ITU-T Recommendation H.264
+         */
+        public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING  =
+                KEY_PREFIX + "h264_video_codec_attribute_profile_level_id_string";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT, 0);
+            defaults.putInt(KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT, 0);
+
+            defaults.putInt(KEY_VIDEO_AS_BANDWIDTH_KBPS_INT, 960);
+            defaults.putInt(KEY_VIDEO_RS_BANDWIDTH_BPS_INT, 8000);
+            defaults.putInt(KEY_VIDEO_RR_BANDWIDTH_BPS_INT, 6000);
+            defaults.putInt(KEY_VIDEO_RTP_DSCP_INT, 40);
+
+            PersistableBundle video_codec_capability_payload_types = new PersistableBundle();
+
+            video_codec_capability_payload_types.putIntArray(
+                    KEY_H264_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 99, 100 });
+
+            defaults.putPersistableBundle(
+                    KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE,
+                    video_codec_capability_payload_types);
+
+            PersistableBundle all_h264_payload_bundles = new PersistableBundle();
+
+            /* Setting default codec attributes for individual H264 profiles*/
+
+            /* For H264 profile-level-id: 42C00C, frame rate:15, Resolution: 240x320 */
+            PersistableBundle h264_bundle_instance1 = new PersistableBundle();
+            all_h264_payload_bundles.putPersistableBundle(
+                    "99", /* Same value of payload type as in KEY_H264_PAYLOAD_TYPE_INT_ARRAY */
+                    h264_bundle_instance1);
+
+            /* For H264 profile-level-id: 42C00C, packetisation mode:0, frame rate:15,
+             * Resolution: 240x320 */
+            PersistableBundle h264_bundle_instance2 = new PersistableBundle();
+            h264_bundle_instance2.putInt(
+                    KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT,
+                    0);
+
+            all_h264_payload_bundles.putPersistableBundle(
+                    "100", /* Same value of payload type as in KEY_H264_PAYLOAD_TYPE_INT_ARRAY */
+                    h264_bundle_instance2);
+
+            defaults.putPersistableBundle(
+                    KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE,
+                    all_h264_payload_bundles);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * WiFi Calling. This groups the configs specific for Voice over WiFi/WFC call.
+     */
+    public static final class ImsWfc {
+        private ImsWfc() {}
+
+        /** Prefix of all imswfc.KEY_* constants. */
+        public static final String KEY_PREFIX = "imswfc.";
+
+        /**
+         *  List of MDNs for which Geo-location PIDF XML with country info
+         *  needs to included for normal calls involving short code.
+         */
+        public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY  =
+                KEY_PREFIX + "pidf_short_code_string_array";
+
+        /**
+         * Flag specifying whether emergency call over VoWiFi is requested over
+         * emergency PDN or IMS PDN.
+         *
+         * <p>If {@code false}: E911 call uses IMS PDN for E911 call over VoWiFi.
+         * If {@code true}: E911 call uses Emergency PDN for E911 call over VoWiFi.
+         */
+        public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL  =
+                KEY_PREFIX + "emergency_call_over_emergency_pdn_bool";
+
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+
+            defaults.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, false);
+            defaults.putStringArray(KEY_PIDF_SHORT_CODE_STRING_ARRAY, new String[] {});
+
+            return defaults;
+        }
+    }
+
+    /**
+     *   IMS supplementary services configs. This groups the configs required for
+     *   supplementary services (SS) like XCAP over UT,
+     *   Unstructured Supplementary Service Data(USSD).
+     */
+    public static final class ImsSs {
+        private ImsSs() {}
+
+        /** Prefix of all imsss.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsss.";
+
+        /**
+         * Flag that controls whether XCAP over UT status need to be
+         * dependent on IMS registration.
+         *
+         * <p>If {@code true}: XCAP over UT status need to be
+         * dependent on IMS registration.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL =
+                KEY_PREFIX + "ut_requires_ims_registration_bool";
+
+        /**
+         * Flag that controls whether Circuit Switched Fallback (CSFB)
+         * option is available when XCAP over UT fails.
+         *
+         * <p>If {@code false}:  XCAP over UT only with no CSFB option.
+         * If XCAP over UT fails, return error.
+         * if {@code true}, Use CSFB if XCAP over UT fails.
+         */
+        public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL  =
+                KEY_PREFIX + "use_csfb_on_xcap_over_ut_failure_bool";
+
+        /**
+         * Flag that controls whether XCAP over UT is enabled or not
+         * when PS data is turned off.
+         *
+         * <p>If {@code true}: XCAP over UT is enabled when PS data is off.
+         * {@code false}: Otherwise.
+         *
+         * Reference: IR.92 Section 5.5.1
+         */
+        public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL  =
+                KEY_PREFIX + "ut_supported_when_ps_data_off_bool";
+
+        /**
+         * Flag that controls whether network initiated USSD over IMS is
+         * supported by the UE.
+         *
+         * <p>If {@code true}:  Support Available.{@code false}: Otherwise.
+         * Reference: 3GPP 24.390.
+         */
+        public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL  =
+                KEY_PREFIX + "network_initiated_ussd_over_ims_supported_bool";
+
+        /**
+         * Specifies the 'XCAP over UT' IP Type when device is
+         * on Home Network.
+         *
+         * <p>Possible values are,
+         * {@link ApnSetting#PROTOCOL_IPV4V6},
+         * {@link ApnSetting#PROTOCOL_IP},
+         * {@link ApnSetting#PROTOCOL_IPV6}
+         *
+         * If key is invalid or not configured, the default value
+         * {@link ApnSetting#PROTOCOL_IPV4V6} will apply.
+         */
+        public static final String KEY_UT_IPTYPE_HOME_INT =
+                KEY_PREFIX + "ut_iptype_home_int";
+
+        /**
+         * Specifies the 'XCAP over UT' IP Type when device is roaming.
+         *
+         * <p>Possible values are,
+         * {@link ApnSetting#PROTOCOL_IPV4V6},
+         * {@link ApnSetting#PROTOCOL_IP},
+         * {@link ApnSetting#PROTOCOL_IPV6}
+
+         * If key is invalid or not configured, the default value
+         * {@link ApnSetting#PROTOCOL_IPV4V6} will apply.
+         */
+        public static final String KEY_UT_IPTYPE_ROAMING_INT =
+                KEY_PREFIX + "ut_iptype_roaming_int";
+
+        /**
+         * Specifies the XCAP Application Server fully qualified domain name (FQDN).
+         * <p> Reference: 24.623 Section 5.2.3.
+         */
+        public static final String KEY_UT_AS_SERVER_FQDN_STRING =
+                KEY_PREFIX + "ut_as_server_fqdn_string";
+
+        /**
+         * Specifies the XCAP Application Server Remote port.
+         * As XCAP is a usage of HTTP, the default value is same as HTTP, i.e. 80.
+         */
+        public static final String KEY_UT_AS_SERVER_PORT_INT =
+                KEY_PREFIX + "ut_as_server_port_int";
+
+        /**
+         * Specifies the preferred transport to be used for XCAP over UT.
+         *
+         * <p>Possible values are,
+         * {@link Ims#PREFERRED_TRANSPORT_TCP},
+         * {@link Ims#PREFERRED_TRANSPORT_TLS}
+         *
+         * <p>If key is invalid or not configured, the default value
+         * {@link Ims#PREFERRED_TRANSPORT_TCP} will apply.
+         */
+        public static final String KEY_UT_TRANSPORT_TYPE_INT =
+                KEY_PREFIX + "ut_transport_type_int";
+
+        /** @hide */
+        @IntDef({
+            SUPPLEMENTARY_SERVICE_CW,
+            SUPPLEMENTARY_SERVICE_CF_ALL,
+            SUPPLEMENTARY_SERVICE_CF_CFU,
+            SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING,
+            SUPPLEMENTARY_SERVICE_CF_CFB,
+            SUPPLEMENTARY_SERVICE_CF_CFNRY,
+            SUPPLEMENTARY_SERVICE_CF_CFNRC,
+            SUPPLEMENTARY_SERVICE_CF_CFNL,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR,
+            SUPPLEMENTARY_SERVICE_CB_ALL,
+            SUPPLEMENTARY_SERVICE_CB_OBS,
+            SUPPLEMENTARY_SERVICE_CB_BAOC,
+            SUPPLEMENTARY_SERVICE_CB_BOIC,
+            SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC,
+            SUPPLEMENTARY_SERVICE_CB_IBS,
+            SUPPLEMENTARY_SERVICE_CB_BAIC,
+            SUPPLEMENTARY_SERVICE_CB_BIC_ROAM,
+            SUPPLEMENTARY_SERVICE_CB_ACR,
+            SUPPLEMENTARY_SERVICE_CB_BIL
+        })
+
+        public @interface SsType {}
+
+        /** Communication Waiting (CW) support as per 3GPP 24.615. */
+        public static final int SUPPLEMENTARY_SERVICE_CW = 0;
+
+        /**
+         * Call Diversion - All call forwarding support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 002
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_ALL = 1;
+
+        /**
+         * Call Diversion - All Unconditional call forwarding support (CFU) as
+         * per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 21
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFU = 2;
+
+        /**
+         * Call Diversion - All conditional call forwarding support as
+         * per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 004
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING = 3;
+
+        /**
+         * Call Diversion - Call forwarding on mobile subscriber busy (CFB)
+         * support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 67
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFB = 4;
+
+        /**
+         * Call Diversion - Call forwarding on no reply (CFNRY)
+         * support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 61
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFNRY = 5;
+
+        /**
+         * Call Diversion - Call forwarding on mobile subscriber not reachable
+         * (CFNRC) support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 62
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFNRC = 6;
+
+        /**
+         * Communication Forwarding on Not Logged-in (CFNL).
+         * support as per 3GPP 24.604 Section 4.2.1.7
+         *
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFNL = 7;
+
+        /**
+         * Originating Identification Presentation (OIP) support
+         * as per 3GPP 24.607.
+         *
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP = 8;
+
+        /**
+         * Terminating Identification Presentation (TIP) support
+         * as per 3GPP 24.608.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP = 9;
+
+        /**
+         * Originating Identification Restriction (OIR) support
+         * as per 3GPP 24.607.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR = 10;
+
+        /**
+         * Terminating Identification Restriction (TIR) support
+         * as per 3GPP 24.608.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR = 11;
+
+        /**
+         * Call Barring - All barring services,
+         * This value is associated with MMI support service code 330
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_ALL = 12;
+
+        /**
+         * Call Barring - Outgoing barring services,
+         * This value is associated with MMI support service code 333
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_OBS = 13;
+
+        /**
+         * Call Barring - Barring of all outgoing calls (BAOC)
+         * support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 33
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BAOC = 14;
+
+        /**
+         * Call Barring - Barring of outgoing international calls
+         * (BOIC) support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 331
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BOIC = 15;
+
+        /**
+         * Call Barring - Barring of outgoing international calls
+         * except those directed to the home PLMN country (BOIC-EXHC) support
+         * as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 332
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC = 16;
+
+        /**
+         * Call Barring - Incoming barring services,
+         * This value is associated with MMI support service code 353
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_IBS = 17;
+
+        /**
+         * Call Barring - Barring of all incoming calls (BAIC)
+         * support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 35
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BAIC = 18;
+
+        /**
+         * Call Barring - Barring of incoming calls when roaming outside
+         * the home PLMN country (BIC-ROAM) support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 351
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BIC_ROAM = 19;
+
+        /**
+         * Call Barring - Anonymous Call Rejection/Barring of all anonymous
+         * incoming number support as per 3GPP TS 24.611.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_ACR = 20;
+
+        /**
+         * Call Barring - Barring list of incoming numbers support.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BIL = 21;
+
+        /**
+         * List of UT services that are Server based.
+         *
+         * <p>Possible values are,
+         * <UL>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CW}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_ALL}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFU}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFB}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNRY}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNRC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNL}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_ALL}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_OBS}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_IBS}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BAOC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BOIC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BAIC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BIC_ROAM}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_ACR}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BIL}</LI>
+         * </UL>
+         */
+        public static final String KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY =
+                KEY_PREFIX + "ut_server_based_services_int_array";
+
+        /**
+         * List of UT services that are terminal based.
+         *
+         * By default, all services are server based and defined in
+         * {@link #KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY}.
+         * Adding here will override that service setting to terminal based.
+         *
+         * <p>Possible values are,
+         * <UL>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CW}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR}</LI>
+         * </UL>
+         */
+        public static final String KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY =
+                KEY_PREFIX + "ut_terminal_based_services_int_array";
+
+        /**
+         * List of different RAT technologies on which XCAP over UT
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         */
+        public static final String KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "xcap_over_ut_supported_rats_int_array";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL, true);
+            defaults.putBoolean(KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL, true);
+            defaults.putBoolean(KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL, true);
+            defaults.putBoolean(KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_UT_IPTYPE_HOME_INT, ApnSetting.PROTOCOL_IPV4V6);
+            defaults.putInt(KEY_UT_IPTYPE_ROAMING_INT, ApnSetting.PROTOCOL_IPV4V6);
+            defaults.putInt(KEY_UT_AS_SERVER_PORT_INT, 80);
+            defaults.putInt(KEY_UT_TRANSPORT_TYPE_INT, Ims.PREFERRED_TRANSPORT_TCP);
+
+            defaults.putIntArray(
+                    KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY,
+                    new int[] {
+                        SUPPLEMENTARY_SERVICE_CW,
+                        SUPPLEMENTARY_SERVICE_CF_ALL,
+                        SUPPLEMENTARY_SERVICE_CF_CFU,
+                        SUPPLEMENTARY_SERVICE_CF_CFNRC,
+                        SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING,
+                        SUPPLEMENTARY_SERVICE_CF_CFB,
+                        SUPPLEMENTARY_SERVICE_CF_CFNRY,
+                        SUPPLEMENTARY_SERVICE_CF_CFNL,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR,
+                        SUPPLEMENTARY_SERVICE_CB_ALL,
+                        SUPPLEMENTARY_SERVICE_CB_OBS,
+                        SUPPLEMENTARY_SERVICE_CB_IBS,
+                        SUPPLEMENTARY_SERVICE_CB_BAOC,
+                        SUPPLEMENTARY_SERVICE_CB_BOIC,
+                        SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC,
+                        SUPPLEMENTARY_SERVICE_CB_BAIC,
+                        SUPPLEMENTARY_SERVICE_CB_BIC_ROAM,
+                        SUPPLEMENTARY_SERVICE_CB_ACR,
+                        SUPPLEMENTARY_SERVICE_CB_BIL
+                    });
+            defaults.putIntArray(
+                    KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
+                    new int[] {});
+
+            defaults.putIntArray(
+                    KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN
+                    });
+            defaults.putString(KEY_UT_AS_SERVER_FQDN_STRING, "");
+
+            return defaults;
+        }
+    }
+
+    /**
+     * This groups the BSF (BootStrapping Function) related configs.
+     * Reference: 3GPP TS 24.109.
+     */
+    public static final class Bsf {
+        private Bsf() {}
+
+        /** Prefix of all bsf.KEY_* constants. */
+        public static final String KEY_PREFIX = "bsf.";
+
+        /** Specifies the fully qualified domain name (FQDN) of BSF Server
+         * as per 3GPP 24.109.
+         */
+        public static final String KEY_BSF_SERVER_FQDN_STRING =
+                KEY_PREFIX + "bsf_server_fqdn_string";
+
+        /**
+         * Specifies the port number of the BSF server as per 3GPP 24.109.
+         * This is usually default port number of HTTP, i.e. 80.
+         */
+        public static final String KEY_BSF_SERVER_PORT_INT =
+                KEY_PREFIX + "bsf_server_port_int";
+
+        /**
+         * Specifies the transport type used in communication with
+         * BSF server.
+         *
+         * <p>Possible values are,
+         * {@link Ims#PREFERRED_TRANSPORT_TCP},
+         * {@link Ims#PREFERRED_TRANSPORT_TLS}
+         *
+         * <p>If key is invalid or not configured, the default value
+         * {@link Ims#PREFERRED_TRANSPORT_TCP} will apply.
+         */
+        public static final String KEY_BSF_TRANSPORT_TYPE_INT =
+                KEY_PREFIX + "bsf_transport type_int";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+
+            defaults.putInt(KEY_BSF_SERVER_PORT_INT, 80);
+            defaults.putInt(KEY_BSF_TRANSPORT_TYPE_INT, Ims.PREFERRED_TRANSPORT_TCP);
+            defaults.putString(KEY_BSF_SERVER_FQDN_STRING, "");
+
             return defaults;
         }
     }
@@ -4872,7 +7532,7 @@
         /**
          * A priority list of ePDG addresses to be used. Possible values are {@link
          * #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, {@link #EPDG_ADDRESS_PCO}, {@link
-         * #EPDG_ADDRESS_CELLULAR_LOC}
+         * #EPDG_ADDRESS_CELLULAR_LOC}, {@link #EPDG_ADDRESS_VISITED_COUNTRY}
          */
         public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY =
                 KEY_PREFIX + "epdg_address_priority_int_array";
@@ -5027,6 +7687,10 @@
         /** Specifies the PCO id for IPv4 Epdg server address */
         public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int";
 
+        /** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */
+        public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL =
+                KEY_PREFIX + "supports_eap_aka_fast_reauth_bool";
+
         /** @hide */
         @IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
         public @interface AuthenticationMethodType {}
@@ -5047,7 +7711,8 @@
             EPDG_ADDRESS_STATIC,
             EPDG_ADDRESS_PLMN,
             EPDG_ADDRESS_PCO,
-            EPDG_ADDRESS_CELLULAR_LOC
+            EPDG_ADDRESS_CELLULAR_LOC,
+            EPDG_ADDRESS_VISITED_COUNTRY
         })
         public @interface EpdgAddressType {}
 
@@ -5061,6 +7726,8 @@
         public static final int EPDG_ADDRESS_PCO = 2;
         /** Use cellular location to chose epdg server */
         public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
+        /* Use Visited Country FQDN rule*/
+        public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4;
 
         /** @hide */
         @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
@@ -5170,6 +7837,7 @@
             defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
             defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
             defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
+            defaults.putBoolean(KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL, false);
 
             return defaults;
         }
@@ -5587,6 +8255,7 @@
         sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
@@ -6010,28 +8679,6 @@
                 "iDEN:14,14", "LTE:30000,15000", "HSPA+:13000,3400", "GSM:24,24",
                 "TD_SCDMA:115,115", "LTE_CA:30000,15000", "NR_NSA:47000,18000",
                 "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000", "NR_SA_MMWAVE:145000,60000"});
-        sDefaults.putStringArray(KEY_TCP_BUFFERS_STRING_ARRAY, new String[]{
-                "GPRS:4092,8760,48000,4096,8760,48000", "EDGE:4093,26280,70800,4096,16384,70800",
-                "UMTS:58254,349525,1048576,58254,349525,1048576",
-                "CDMA:4094,87380,262144,4096,16384,262144",
-                "1xRTT:16384,32768,131072,4096,16384,102400",
-                "EvDo_0:4094,87380,262144,4096,16384,262144",
-                "EvDo_A:4094,87380,262144,4096,16384,262144",
-                "HSDPA:61167,367002,1101005,8738,52429,262114",
-                "HSUPA:40778,244668,734003,16777,100663,301990",
-                "HSPA:40778,244668,734003,16777,100663,301990",
-                "EvDo_B:4094,87380,262144,4096,16384,262144",
-                "eHRPD:131072,262144,1048576,4096,16384,524288",
-                "iDEN:4094,87380,262144,4096,16384,262144",
-                "LTE:524288,1048576,2097152,262144,524288,1048576",
-                "HSPA+:122334,734003,2202010,32040,192239,576717",
-                "GSM:4092,8760,48000,4096,8760,48000",
-                "TD_SCDMA:58254,349525,1048576,58254,349525,1048576",
-                "LTE_CA:4096,6291456,12582912,4096,1048576,2097152",
-                "NR_NSA:2097152,6291456,16777216,512000,2097152,8388608",
-                "NR_NSA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608",
-                "NR_SA:2097152,6291456,16777216,512000,2097152,8388608",
-                "NR_SA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608"});
         sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL, false);
         sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
         sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
@@ -6118,10 +8765,10 @@
         /* Default value is 2 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
         sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
-        sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
-        sDefaults.putInt(
+        sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L);
+        sDefaults.putLong(
                 KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
-                120000);
+                120000L);
         sDefaults.putAll(ImsServiceEntitlement.getDefaults());
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
@@ -6147,6 +8794,14 @@
                 });
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putAll(Ims.getDefaults());
+        sDefaults.putAll(ImsVoice.getDefaults());
+        sDefaults.putAll(ImsSms.getDefaults());
+        sDefaults.putAll(ImsRtt.getDefaults());
+        sDefaults.putAll(ImsEmergency.getDefaults());
+        sDefaults.putAll(ImsVt.getDefaults());
+        sDefaults.putAll(ImsWfc.getDefaults());
+        sDefaults.putAll(ImsSs.getDefaults());
+        sDefaults.putAll(Bsf.getDefaults());
         sDefaults.putAll(Iwlan.getDefaults());
         sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
          sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 2758e12..b0ff949 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -163,6 +163,26 @@
         return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
     }
 
+
+    /**
+     * Create an instance of {@link ProvisioningManager} for the subscription id specified.
+     * <p>
+     * Provides a ProvisioningManager instance to carrier apps to update carrier provisioning
+     * information, as well as provides a callback so that apps can listen for changes
+     * in MMTEL/RCS provisioning
+     * @param subscriptionId The ID of the subscription that this ProvisioningManager will use.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @return a ProvisioningManager instance with the specific subscription ID.
+     */
+    @NonNull
+    public ProvisioningManager getProvisioningManager(int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+        }
+
+        return new ProvisioningManager(subscriptionId);
+    }
+
     private static IImsRcsController getIImsRcsControllerInterface() {
         return IImsRcsController.Stub.asInterface(
                 TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index c8b8ffb..326f417 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -221,7 +221,8 @@
 
     private NetworkScanRequest(Parcel in) {
         mScanType = in.readInt();
-        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader());
+        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader(),
+                RadioAccessSpecifier.class);
         if (tempSpecifiers != null) {
             mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
             for (int i = 0; i < tempSpecifiers.length; i++) {
diff --git a/telephony/java/android/telephony/PcoData.java b/telephony/java/android/telephony/PcoData.java
index bcfbcf8b..39e4f2f 100644
--- a/telephony/java/android/telephony/PcoData.java
+++ b/telephony/java/android/telephony/PcoData.java
@@ -19,6 +19,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Contains Carrier-specific (and opaque) Protocol configuration Option
  * Data.  In general this is only passed on to carrier-specific applications
@@ -84,4 +87,22 @@
         return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
                 contents.length + "])";
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PcoData pcoData = (PcoData) o;
+        return cid == pcoData.cid
+                && pcoId == pcoData.pcoId
+                && Objects.equals(bearerProto, pcoData.bearerProto)
+                && Arrays.equals(contents, pcoData.contents);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(cid, bearerProto, pcoId);
+        result = 31 * result + Arrays.hashCode(contents);
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 54fb65ce..cbd03c7 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2548,6 +2548,11 @@
      */
     public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123;
 
+    /**
+     * A RIL error occurred during the SMS send.
+     */
+    public static final int RESULT_RIL_GENERIC_ERROR = 124;
+
     // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION}
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2295ed7..250e55c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -194,7 +194,7 @@
         }
 
         @Override
-        protected T recompute(Void aVoid) {
+        public T recompute(Void aVoid) {
             T result = mDefaultValue;
 
             try {
@@ -228,7 +228,7 @@
         }
 
         @Override
-        protected T recompute(Integer query) {
+        public T recompute(Integer query) {
             T result = mDefaultValue;
 
             try {
@@ -3892,6 +3892,11 @@
      * {@link #PHONE_NUMBER_SOURCE_UICC UICC} and decide if the previously set phone number
      * of source {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} should be updated.
      *
+     * <p>The API provides no guarantees of what format the number is in: the format can vary
+     * depending on the {@code source} and the network etc. Programmatic parsing should be done
+     * cautiously, for example, after formatting the number to a consistent format with
+     * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+     *
      * <p>Note the assumption is that one subscription (which usually means one SIM) has
      * only one phone number. The multiple sources backup each other so hopefully at least one
      * is availavle. For example, for a carrier that doesn't typically set phone numbers
@@ -3950,6 +3955,11 @@
      * from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER}
      * > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}.
      *
+     * <p>The API provides no guarantees of what format the number is in: the format can vary
+     * depending on the underlying source and the network etc. Programmatic parsing should be done
+     * cautiously, for example, after formatting the number to a consistent format with
+     * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+     *
      * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
      *                       for the default one.
      * @return the phone number, or an empty string if not available.
@@ -3988,6 +3998,9 @@
      * <p>The API is suitable for carrier apps to provide a phone number, for example when
      * it's not possible to update {@link #PHONE_NUMBER_SOURCE_UICC UICC} directly.
      *
+     * <p>It's recommended that the phone number is formatted to well-known formats,
+     * for example, by {@link PhoneNumberUtils} {@code formatNumber*} methods.
+     *
      * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
      *                       for the default one.
      * @param number the phone number, or an empty string to remove the previously set number.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f5505e6..536517c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -142,6 +142,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
@@ -6807,6 +6808,24 @@
     }
 
     /**
+     * Get the first active portIndex from the corresponding physical slot index.
+     * @param physicalSlotIndex physical slot index
+     * @return first active port index or INVALID_PORT_INDEX if no port is active
+     */
+    private int getFirstActivePortIndex(int physicalSlotIndex) {
+        UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
+        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+                && slotInfos[physicalSlotIndex] != null) {
+            Optional<UiccPortInfo> result =  slotInfos[physicalSlotIndex].getPorts().stream()
+                    .filter(portInfo -> portInfo.isActive()).findFirst();
+            if (result.isPresent()) {
+                return result.get().getPortIndex();
+            }
+        }
+        return INVALID_PORT_INDEX;
+    }
+
+    /**
      * Opens a logical channel to the ICC card.
      *
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
@@ -6852,7 +6871,8 @@
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
      * @hide
-     * @deprecated instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -6866,6 +6886,7 @@
             if (telephony != null) {
                 IccLogicalChannelRequest request = new IccLogicalChannelRequest();
                 request.slotIndex = slotIndex;
+                request.portIndex = getFirstActivePortIndex(slotIndex);
                 request.aid = aid;
                 request.p2 = p2;
                 request.callingPackage = getOpPackageName();
@@ -7021,7 +7042,8 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @hide
-     * @deprecated instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -7033,6 +7055,7 @@
             if (telephony != null) {
                 IccLogicalChannelRequest request = new IccLogicalChannelRequest();
                 request.slotIndex = slotIndex;
+                request.portIndex = getFirstActivePortIndex(slotIndex);
                 request.channel = channel;
                 return telephony.iccCloseLogicalChannel(request);
             }
@@ -7153,7 +7176,8 @@
      * @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.
      * @hide
-     * @deprecated instead use
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use
      * {@link #iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, String)}
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -7166,8 +7190,9 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.iccTransmitApduLogicalChannelByPort(slotIndex, DEFAULT_PORT_INDEX,
-                         channel, cla, instruction, p1, p2, p3, data);
+                return telephony.iccTransmitApduLogicalChannelByPort(slotIndex,
+                        getFirstActivePortIndex(slotIndex), channel, cla, instruction,
+                        p1, p2, p3, data);
             }
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -7305,7 +7330,8 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
-     * @deprecated instead use
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use
      * {@link #iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, String)}
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -7318,8 +7344,9 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.iccTransmitApduBasicChannelByPort(slotIndex, DEFAULT_PORT_INDEX,
-                         getOpPackageName(), cla, instruction, p1, p2, p3, data);
+                return telephony.iccTransmitApduBasicChannelByPort(slotIndex,
+                        getFirstActivePortIndex(slotIndex), getOpPackageName(),
+                        cla, instruction, p1, p2, p3, data);
             }
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -12588,12 +12615,15 @@
         if (carriers == null || !SubscriptionManager.isValidPhoneId(slotIndex)) {
             return -1;
         }
-        // Execute the method setCarrierRestrictionRules with an empty excluded list and
-        // indicating priority for the allowed list.
+        // Execute the method setCarrierRestrictionRules with an empty excluded list.
+        // If the allowed list is empty, it means that all carriers are allowed (default allowed),
+        // otherwise it means that only specified carriers are allowed (default not allowed).
         CarrierRestrictionRules carrierRestrictionRules = CarrierRestrictionRules.newBuilder()
                 .setAllowedCarriers(carriers)
                 .setDefaultCarrierRestriction(
-                    CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)
+                    carriers.isEmpty()
+                        ? CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED
+                        : CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)
                 .build();
 
         int result = setCarrierRestrictionRules(carrierRestrictionRules);
@@ -15108,6 +15138,15 @@
     public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
 
     /**
+     * Indicates the call waiting status could not be set or queried because the Fixed Dialing
+     * Numbers (FDN) feature is enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
@@ -15115,6 +15154,7 @@
             CALL_WAITING_STATUS_DISABLED,
             CALL_WAITING_STATUS_UNKNOWN_ERROR,
             CALL_WAITING_STATUS_NOT_SUPPORTED,
+            CALL_WAITING_STATUS_FDN_CHECK_FAILURE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallWaitingStatus {
@@ -15135,6 +15175,7 @@
      *                          <li>{@link #CALL_WAITING_STATUS_DISABLED}}</li>
      *                          <li>{@link #CALL_WAITING_STATUS_UNKNOWN_ERROR}}</li>
      *                          <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
+     *                          <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li>
      *                       </ul>
      * @hide
      */
@@ -15184,7 +15225,8 @@
      *                       {@link #CALL_WAITING_STATUS_ENABLED} or
      *                       {@link #CALL_WAITING_STATUS_DISABLED} if the operation succeeded and
      *                       {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
-     *                       {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} if it failed.
+     *                       {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or
+     *                       {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 74f9c87..30ca162 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -156,11 +156,9 @@
     @Nullable
     @Deprecated
     public String getIccId() {
-        // Temporarily bypassing exception
-        // TODO: add exception once refactoring completed.
-        //if (mIccIdAccessRestricted) {
-        //    throw new UnsupportedOperationException("getIccId from UiccPortInfo");
-        //}
+        if (mIccIdAccessRestricted) {
+            throw new UnsupportedOperationException("getIccId from UiccPortInfo");
+        }
         //always return ICCID from first port.
         return getPorts().stream().findFirst().get().getIccId();
     }
@@ -259,8 +257,6 @@
                 + mCardId
                 + ", mEid="
                 + mEid
-                + ", mIccId="
-                + SubscriptionInfo.givePrintableIccid(getIccId())
                 + ", mPhysicalSlotIndex="
                 + mPhysicalSlotIndex
                 + ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index a8668e7..17f34db 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -159,11 +159,9 @@
      */
     @Deprecated
     public boolean getIsActive() {
-        // Temporarily bypassing exception
-        // TODO: add exception once refactoring completed.
-        //if (mLogicalSlotAccessRestricted) {
-        //    throw new UnsupportedOperationException("get port status from UiccPortInfo");
-        //}
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get port status from UiccPortInfo");
+        }
         //always return status from first port.
         return getPorts().stream().findFirst().get().isActive();
     }
@@ -198,11 +196,9 @@
      */
     @Deprecated
     public int getLogicalSlotIdx() {
-        // Temporarily bypassing exception
-        // TODO: add exception once refactoring completed.
-        //if (mLogicalSlotAccessRestricted) {
-        //    throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
-        //}
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
+        }
         //always return logical slot index from first port.
         //portList always have at least one element.
         return getPorts().stream().findFirst().get().getLogicalSlotIndex();
@@ -275,16 +271,13 @@
     @NonNull
     @Override
     public String toString() {
-        return "UiccSlotInfo (mIsActive="
-                + mIsActive
+        return "UiccSlotInfo ("
                 + ", mIsEuicc="
                 + mIsEuicc
                 + ", mCardId="
                 + mCardId
                 + ", cardState="
                 + mCardStateInfo
-                + ", phoneId="
-                + mLogicalSlotIdx
                 + ", mIsExtendedApduSupported="
                 + mIsExtendedApduSupported
                 + ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotMapping.java b/telephony/java/android/telephony/UiccSlotMapping.java
index 87e7acd..08de7fd 100644
--- a/telephony/java/android/telephony/UiccSlotMapping.java
+++ b/telephony/java/android/telephony/UiccSlotMapping.java
@@ -94,7 +94,6 @@
      * @param physicalSlotIndex is unique index referring to a physical SIM slot.
      * @param logicalSlotIndex is unique index referring to a logical SIM slot.
      *
-     * @hide
      */
     public UiccSlotMapping(int portIndex, int physicalSlotIndex, int logicalSlotIndex) {
         this.mPortIndex = portIndex;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 4ff59b5..acbd64b 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -118,11 +118,9 @@
     public static final int TYPE_VSIM = 1 << 12;  // TODO: Refer to ApnTypes.VSIM
     /** APN type for BIP. */
     public static final int TYPE_BIP = 1 << 13;   // TODO: Refer to ApnTypes.BIP
-    /**
-     * APN type for ENTERPRISE.
-     * @hide
-     */
-    public static final int TYPE_ENTERPRISE = TYPE_BIP << 1;
+    /** APN type for ENTERPRISE. */
+    public static final int TYPE_ENTERPRISE = 1 << 14; //TODO: In future should be referenced from
+    // hardware.interfaces.radio.data.ApnTypes
 
     /** @hide */
     @IntDef(flag = true, prefix = {"TYPE_"}, value = {
@@ -355,6 +353,7 @@
      * modem components or carriers. Non-system apps should use the integer variants instead.
      * @hide
      */
+    @SystemApi
     public static final String TYPE_ENTERPRISE_STRING = "enterprise";
 
 
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 051d6c5..1ff6ec1 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -253,8 +253,10 @@
                 return "RESULT_ERROR_BUSY";
             case RESULT_ERROR_ILLEGAL_STATE:
                 return "RESULT_ERROR_ILLEGAL_STATE";
+            case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
+                return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
             default:
-                return "Missing case for result code=" + resultCode;
+                return "Unknown(" + resultCode + ")";
         }
     }
 
diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
index 5a7189f..0ab7b61 100644
--- a/telephony/java/android/telephony/data/QosBearerFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.LinkAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,11 +36,10 @@
  * @hide
  */
 public final class QosBearerFilter implements Parcelable {
-
-    private List<LinkAddress> localAddresses;
-    private List<LinkAddress> remoteAddresses;
-    private PortRange localPort;
-    private PortRange remotePort;
+    private @NonNull List<LinkAddress> localAddresses;
+    private @NonNull List<LinkAddress> remoteAddresses;
+    private @Nullable PortRange localPort;
+    private @Nullable PortRange remotePort;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -56,13 +56,12 @@
     public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH;
     public static final int QOS_MIN_PORT = android.hardware.radio.V1_6.QosPortRange.MIN;
     /**
-     * Hardcoded inplace of android.hardware.radio.V1_6.QosPortRange.MAX as it
+     * Hardcoded in place of android.hardware.radio.V1_6.QosPortRange.MAX as it
      * returns -1 due to uint16_t to int conversion in java. (TODO: Fix the HAL)
      */
     public static final int QOS_MAX_PORT = 65535; // android.hardware.radio.V1_6.QosPortRange.MIN;
 
-    @QosProtocol
-    private int protocol;
+    private @QosProtocol int protocol;
 
     private int typeOfServiceMask;
 
@@ -85,8 +84,7 @@
     public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
             android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
 
-    @QosBearerFilterDirection
-    private int filterDirection;
+    private @QosBearerFilterDirection int filterDirection;
 
     /**
      * Specified the order in which the filter needs to be matched.
@@ -94,9 +92,10 @@
      */
     private int precedence;
 
-    public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
-            PortRange localPort, PortRange remotePort, int protocol, int tos,
-            long flowLabel, long spi, int direction, int precedence) {
+    public QosBearerFilter(@NonNull List<LinkAddress> localAddresses,
+            @NonNull List<LinkAddress> remoteAddresses, @Nullable PortRange localPort,
+            @Nullable PortRange remotePort, @QosProtocol int protocol, int tos, long flowLabel,
+            long spi, @QosBearerFilterDirection int direction, int precedence) {
         this.localAddresses = new ArrayList<>();
         this.localAddresses.addAll(localAddresses);
         this.remoteAddresses = new ArrayList<>();
@@ -111,19 +110,19 @@
         this.precedence = precedence;
     }
 
-    public List<LinkAddress> getLocalAddresses() {
+    public @NonNull List<LinkAddress> getLocalAddresses() {
         return localAddresses;
     }
 
-    public List<LinkAddress> getRemoteAddresses() {
+    public @NonNull List<LinkAddress> getRemoteAddresses() {
         return remoteAddresses;
     }
 
-    public PortRange getLocalPortRange() {
+    public @Nullable PortRange getLocalPortRange() {
         return localPort;
     }
 
-    public PortRange getRemotePortRange() {
+    public @Nullable PortRange getRemotePortRange() {
         return remotePort;
     }
 
@@ -245,8 +244,8 @@
                 && localAddresses.containsAll(other.localAddresses)
                 && remoteAddresses.size() == other.remoteAddresses.size()
                 && remoteAddresses.containsAll(other.remoteAddresses)
-                && localPort.equals(other.localPort)
-                && remotePort.equals(other.remotePort)
+                && Objects.equals(localPort, other.localPort)
+                && Objects.equals(remotePort, other.remotePort)
                 && protocol == other.protocol
                 && typeOfServiceMask == other.typeOfServiceMask
                 && flowLabel == other.flowLabel
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 389cc6d..a49a61b5 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -16,7 +16,6 @@
 package android.telephony.euicc;
 
 import android.Manifest;
-import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,7 +29,6 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -38,13 +36,11 @@
 import android.telephony.euicc.EuiccCardManager.ResetOption;
 
 import com.android.internal.telephony.euicc.IEuiccController;
-import com.android.internal.telephony.euicc.IResultCallback;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
 
 /**
@@ -221,20 +217,6 @@
             "android.telephony.euicc.action.START_EUICC_ACTIVATION";
 
     /**
-     * Result codes passed to the ResultListener by
-     * {@link #switchToSubscription(int, int, Executor, ResultListener)}
-     *
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"EMBEDDED_SUBSCRIPTION_RESULT_"}, value = {
-            EMBEDDED_SUBSCRIPTION_RESULT_OK,
-            EMBEDDED_SUBSCRIPTION_RESULT_ERROR,
-            EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR
-    })
-    public @interface ResultCode{}
-
-    /**
      * Result code for an operation indicating that the operation succeeded.
      */
     public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
@@ -823,6 +805,13 @@
      */
     public static final int ERROR_OPERATION_BUSY = 10016;
 
+    /**
+     * Failure due to target port is not supported.
+     * @see #switchToSubscription(int, int, PendingIntent)
+     */
+    public static final int ERROR_INVALID_PORT = 10017;
+
+
     private final Context mContext;
     private int mCardId;
 
@@ -1138,6 +1127,15 @@
      * intent to prompt the user to accept the download. The caller should also be authorized to
      * manage the subscription to be enabled.
      *
+     * <p> From Android T, devices might support MEP(Multiple Enabled Profile), the subscription
+     * can be installed on different port from the eUICC. Calling apps with carrier privilege
+     * (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently active subscriptions
+     * can use {@link #switchToSubscription(int, int, PendingIntent)} to specify which port to
+     * enable the subscription. Otherwise, use this API to enable the subscription on the eUICC
+     * and the platform will internally resolve a port. If there is no available port,
+     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} might be returned in the callback
+     * intent to prompt the user to disable an already-active subscription.
+     *
      * @param subscriptionId the ID of the subscription to enable. May be
      *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
      *     current profile without activating another profile to replace it. If it's a disable
@@ -1145,12 +1143,7 @@
      *     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.
-     *
-     * @deprecated From T, callers should use
-     * {@link #switchToSubscription(int, int, Executor, ResultListener)} instead to specify a port
-     * index on the card to switch to.
      */
-    @Deprecated
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -1168,20 +1161,19 @@
     /**
      * Switch to (enable) the given subscription.
      *
-     * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
-     * or the calling app must be authorized to manage both the currently-active subscription and
-     * the subscription to be enabled according to the subscription metadata. Without the former,
-     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
-     * intent to prompt the user to accept the download.
+     * <p> Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * or the caller must be having both the carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}) over any currently active subscriptions
+     * and the subscription to be enabled according to the subscription metadata.
+     * Without the former permissions, an SecurityException is thrown.
      *
-     * <p>On a multi-active SIM device, requires the
-     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
-     * only if the targeted eUICC does not currently have an active subscription or the calling app
-     * is authorized to manage the active subscription on the target eUICC, and the calling app is
-     * authorized to manage any active subscription on any SIM. Without it, an
-     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
-     * intent to prompt the user to accept the download. The caller should also be authorized to
-     * manage the subscription to be enabled.
+     * <p> If the caller is passing invalid port index,
+     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR} with detailed error code
+     * {@link #ERROR_INVALID_PORT} will be returned.
+     *
+     * <p> Depending on the target port and permission check,
+     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} might be returned to the callback
+     * intent to prompt the user to authorize before the switch.
      *
      * @param subscriptionId the ID of the subscription to enable. May be
      *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
@@ -1190,47 +1182,24 @@
      *     permission, or the calling app must be authorized to manage the active subscription on
      *     the target eUICC.
      * @param portIndex the index of the port to target for the enabled subscription
-     * @param executor an Executor on which to run the callback
-     * @param callback a {@link ResultListener} which will run when the operation completes
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, int portIndex,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull ResultListener callback) {
+            @NonNull PendingIntent callbackIntent) {
         if (!isEnabled()) {
-            sendUnavailableErrorToCallback(executor, callback);
+            sendUnavailableError(callbackIntent);
             return;
         }
         try {
-            IResultCallback internalCallback = new IResultCallback.Stub() {
-                @Override
-                public void onComplete(int result, Intent resultIntent) {
-                    executor.execute(() -> Binder.withCleanCallingIdentity(
-                            () -> callback.onComplete(result, resultIntent)));
-                }
-            };
-            getIEuiccController().switchToSubscriptionWithPort(mCardId, portIndex,
-                    subscriptionId, mContext.getOpPackageName(), internalCallback);
+            getIEuiccController().switchToSubscriptionWithPort(mCardId,
+                    subscriptionId, portIndex, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Callback to receive the result of an EuiccManager API.
-     */
-    public interface ResultListener {
-        /**
-         * Called on completion of some operation.
-         * @param resultCode representing success or specific failure of the operation
-         *                   (See {@link ResultCode})
-         * @param resultIntent an intent used to start a resolution activity when an error
-         *                     occurs that can be resolved by the user
-         */
-        void onComplete(@ResultCode int resultCode, @Nullable Intent resultIntent);
-    }
-
-    /**
      * Update the nickname for the given subscription.
      *
      * <p>Requires that the calling app has carrier privileges according to the metadata of the
@@ -1501,13 +1470,6 @@
         }
     }
 
-    private static void sendUnavailableErrorToCallback(@NonNull Executor executor,
-            ResultListener callback) {
-        Integer result = EMBEDDED_SUBSCRIPTION_RESULT_ERROR;
-        executor.execute(() ->
-                Binder.withCleanCallingIdentity(() -> callback.onComplete(result, null)));
-    }
-
     private static IEuiccController getIEuiccController() {
         return IEuiccController.Stub.asInterface(
                 TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 93e1058..e6d7df3 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -317,6 +317,10 @@
      * Payphone presentation for Originating Identity.
      */
     public static final int OIR_PRESENTATION_PAYPHONE = 4;
+    /**
+     * Unavailable presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_UNAVAILABLE = 5;
 
     //Values for EXTRA_DIALSTRING
     /**
@@ -847,7 +851,8 @@
         mHasKnownUserIntentEmergency = in.readBoolean();
         mRestrictCause = in.readInt();
         mCallerNumberVerificationStatus = in.readInt();
-        Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader());
+        Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader(),
+                RtpHeaderExtensionType.class);
         mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted)
                 .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
     }
@@ -989,6 +994,8 @@
                 return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
             case PhoneConstants.PRESENTATION_UNKNOWN:
                 return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
+            case PhoneConstants.PRESENTATION_UNAVAILABLE:
+                return ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE;
             default:
                 return ImsCallProfile.OIR_DEFAULT;
         }
@@ -1017,6 +1024,8 @@
                 return PhoneConstants.PRESENTATION_ALLOWED;
             case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
                 return PhoneConstants.PRESENTATION_PAYPHONE;
+            case ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE:
+                return PhoneConstants.PRESENTATION_UNAVAILABLE;
             case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
                 return PhoneConstants.PRESENTATION_UNKNOWN;
             default:
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 6569de6..d65286f 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -1212,50 +1212,56 @@
          */
         @Override
         public void callSessionInitiating(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionInitiating(
-                        ImsCallSession.this, profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionInitiating(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionProgressing(ImsStreamMediaProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionProgressing(
-                        ImsCallSession.this, profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionProgressing(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionInitiated(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStarted(
-                        ImsCallSession.this, profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionStarted(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStartFailed(
-                        ImsCallSession.this, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStartFailed(
-                        ImsCallSession.this, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionTerminated(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionTerminated(
-                        ImsCallSession.this, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1263,51 +1269,56 @@
          */
         @Override
         public void callSessionHeld(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHeld(
-                        ImsCallSession.this, profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHeld(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHoldFailed(
-                        ImsCallSession.this, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionHoldReceived(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHoldReceived(
-                        ImsCallSession.this, profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionResumed(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionResumed(
-                        ImsCallSession.this, profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionResumed(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionResumeFailed(
-                        ImsCallSession.this, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionResumeReceived(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionResumeReceived(ImsCallSession.this, profile),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1330,8 +1341,8 @@
          */
         @Override
         public void callSessionMergeComplete(IImsCallSession newSession) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
                     if (newSession != null) {
                         // New session created after conference
                         mListener.callSessionMergeComplete(new ImsCallSession(newSession));
@@ -1339,8 +1350,8 @@
                         // Session already exists. Hence no need to pass
                         mListener.callSessionMergeComplete(null);
                     }
-                }, mListenerExecutor);
-            }
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1350,11 +1361,11 @@
          */
         @Override
         public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1362,29 +1373,29 @@
          */
         @Override
         public void callSessionUpdated(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionUpdated(ImsCallSession.this, profile),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUpdated(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionUpdateReceived(ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionUpdateReceived(ImsCallSession.this, profile),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1393,30 +1404,33 @@
         @Override
         public void callSessionConferenceExtended(IImsCallSession newSession,
                 ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionConferenceExtended(ImsCallSession.this,
-                        new ImsCallSession(newSession), profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceExtended(ImsCallSession.this,
+                            new ImsCallSession(newSession), profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionConferenceExtendFailed(
-                        ImsCallSession.this, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceExtendFailed(
+                            ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionConferenceExtendReceived(IImsCallSession newSession,
                 ImsCallProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
-                        new ImsCallSession(newSession), profile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+                            new ImsCallSession(newSession), profile);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1425,38 +1439,41 @@
          */
         @Override
         public void callSessionInviteParticipantsRequestDelivered() {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionInviteParticipantsRequestDelivered(
-                        ImsCallSession.this), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionInviteParticipantsRequestDelivered(
+                            ImsCallSession.this);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
-                        reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+                            reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionRemoveParticipantsRequestDelivered() {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRemoveParticipantsRequestDelivered(
-                        ImsCallSession.this), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
-                        reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+                            reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1464,11 +1481,11 @@
          */
         @Override
         public void callSessionConferenceStateUpdated(ImsConferenceState state) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1476,11 +1493,12 @@
          */
         @Override
         public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
-                        ussdMessage), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
+                            ussdMessage);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1496,11 +1514,12 @@
          */
         @Override
         public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
-                        targetNetworkType), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+                            targetNetworkType);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1509,11 +1528,12 @@
         @Override
         public void callSessionHandover(int srcNetworkType, int targetNetworkType,
                 ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
-                        targetNetworkType, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+                            targetNetworkType, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1522,11 +1542,12 @@
         @Override
         public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
                 ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
-                        targetNetworkType, reasonInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+                            targetNetworkType, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1534,11 +1555,11 @@
          */
         @Override
         public void callSessionTtyModeReceived(int mode) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionTtyModeReceived(ImsCallSession.this, mode),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1548,20 +1569,22 @@
          *      otherwise.
          */
         public void callSessionMultipartyStateChanged(boolean isMultiParty) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
-                        isMultiParty), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
+                            isMultiParty);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionSuppServiceReceived(ImsCallSession.this,
-                        suppServiceInfo), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionSuppServiceReceived(ImsCallSession.this,
+                            suppServiceInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1569,11 +1592,12 @@
          */
         @Override
         public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
-                        callProfile), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
+                            callProfile);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1581,11 +1605,11 @@
          */
         @Override
         public void callSessionRttModifyResponseReceived(int status) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRttModifyResponseReceived(status),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttModifyResponseReceived(status);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1593,10 +1617,11 @@
          */
         @Override
         public void callSessionRttMessageReceived(String rttMessage) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRttMessageReceived(rttMessage), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttMessageReceived(rttMessage);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1604,28 +1629,29 @@
          */
         @Override
         public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRttAudioIndicatorChanged(profile),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttAudioIndicatorChanged(profile);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionTransferred() {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionTransferred(ImsCallSession.this), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTransferred(ImsCallSession.this);
+                }
+            }, mListenerExecutor);
         }
 
         @Override
         public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo),
-                        mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1634,10 +1660,11 @@
          */
         @Override
         public void callSessionDtmfReceived(char dtmf) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionDtmfReceived(
-                        dtmf), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionDtmfReceived(dtmf);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1645,10 +1672,11 @@
          */
         @Override
         public void callQualityChanged(CallQuality callQuality) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callQualityChanged(
-                        callQuality), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callQualityChanged(callQuality);
+                }
+            }, mListenerExecutor);
         }
 
         /**
@@ -1658,11 +1686,12 @@
         @Override
         public void callSessionRtpHeaderExtensionsReceived(
                 @NonNull List<RtpHeaderExtension> extensions) {
-            if (mListener != null) {
-                TelephonyUtils.runWithCleanCallingIdentity(()->
-                        mListener.callSessionRtpHeaderExtensionsReceived(
-                        new ArraySet<RtpHeaderExtension>(extensions)), mListenerExecutor);
-            }
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRtpHeaderExtensionsReceived(
+                            new ArraySet<RtpHeaderExtension>(extensions));
+                }
+            }, mListenerExecutor);
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 53dff54..be233b8 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -42,6 +42,7 @@
 import android.telephony.ims.stub.SipTransportImplBase;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.internal.annotations.VisibleForTesting;
@@ -180,6 +181,12 @@
     // call ImsFeature#onFeatureRemoved.
     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
 
+    // A map of slot id -> boolean array, where each entry in the boolean array corresponds to an
+    // ImsFeature that was created for a slot id and not a sub id for backwards compatibility
+    // purposes.
+    private final SparseArray<SparseBooleanArray> mCreateImsFeatureWithSlotIdFlagMap =
+            new SparseArray<>();
+
     private IImsServiceControllerListener mListener;
     private Executor mExecutor;
 
@@ -222,15 +229,36 @@
         }
 
         @Override
-        public IImsMmTelFeature createMmTelFeature(int slotId) {
-            return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId),
-                "createMmTelFeature");
+        public IImsMmTelFeature createMmTelFeature(int slotId, int subId) {
+            MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
+            if (f == null) {
+                return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId, subId),
+                        "createMmTelFeature");
+            } else {
+                return f.getBinder();
+            }
         }
 
         @Override
-        public IImsRcsFeature createRcsFeature(int slotId) {
-            return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId),
-                "createRcsFeature");
+        public IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
+            MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
+            if (f == null) {
+                return executeMethodAsyncForResult(() -> createEmergencyOnlyMmTelFeatureInternal(
+                        slotId), "createEmergencyOnlyMmTelFeature");
+            } else {
+                return f.getBinder();
+            }
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, int subId) {
+            RcsFeature f  = (RcsFeature) getImsFeature(slotId, ImsFeature.FEATURE_RCS);
+            if (f == null) {
+                return executeMethodAsyncForResult(() ->
+                        createRcsFeatureInternal(slotId, subId), "createRcsFeature");
+            } else {
+                return f.getBinder();
+            }
         }
 
         @Override
@@ -248,9 +276,14 @@
         }
 
         @Override
-        public void removeImsFeature(int slotId, int featureType) {
+        public void removeImsFeature(int slotId, int featureType, boolean changeSubId) {
+            if (changeSubId && isImsFeatureCreatedForSlot(slotId, featureType)) {
+                Log.w(LOG_TAG, "Do not remove Ims feature for compatibility");
+                return;
+            }
             executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
                     "removeImsFeature");
+            setImsFeatureCreatedForSlot(slotId, featureType, false);
         }
 
         @Override
@@ -279,9 +312,10 @@
         }
 
         @Override
-        public IImsConfig getConfig(int slotId) {
+        public IImsConfig getConfig(int slotId, int subId) {
             return executeMethodAsyncForResult(() -> {
-                ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+                ImsConfigImplBase c =
+                        ImsService.this.getConfigForSubscription(slotId, subId);
                 if (c != null) {
                     c.setDefaultExecutor(mExecutor);
                     return c.getIImsConfig();
@@ -292,9 +326,10 @@
         }
 
         @Override
-        public IImsRegistration getRegistration(int slotId) {
+        public IImsRegistration getRegistration(int slotId, int subId) {
             return executeMethodAsyncForResult(() -> {
-                ImsRegistrationImplBase r =  ImsService.this.getRegistration(slotId);
+                ImsRegistrationImplBase r =
+                        ImsService.this.getRegistrationForSubscription(slotId, subId);
                 if (r != null) {
                     r.setDefaultExecutor(mExecutor);
                     return r.getBinder();
@@ -318,13 +353,15 @@
         }
 
         @Override
-        public void enableIms(int slotId) {
-            executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms");
+        public void enableIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.enableImsForSubscription(slotId, subId), "enableIms");
         }
 
         @Override
-        public void disableIms(int slotId) {
-            executeMethodAsync(() -> ImsService.this.disableIms(slotId), "disableIms");
+        public void disableIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.disableImsForSubscription(slotId, subId), "disableIms");
         }
 
         // Call the methods with a clean calling identity on the executor and wait indefinitely for
@@ -364,16 +401,8 @@
         return null;
     }
 
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    public SparseArray<ImsFeature> getFeatures(int slotId) {
-        return mFeaturesBySlot.get(slotId);
-    }
-
-    private IImsMmTelFeature createMmTelFeatureInternal(int slotId) {
-        MmTelFeature f = createMmTelFeature(slotId);
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) {
+        MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId);
         if (f != null) {
             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
             f.setDefaultExecutor(mExecutor);
@@ -384,8 +413,20 @@
         }
     }
 
-    private IImsRcsFeature createRcsFeatureInternal(int slotId) {
-        RcsFeature f = createRcsFeature(slotId);
+    private IImsMmTelFeature createEmergencyOnlyMmTelFeatureInternal(int slotId) {
+        MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+            f.setDefaultExecutor(mExecutor);
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId, int subI) {
+        RcsFeature f = createRcsFeatureForSubscription(slotId, subI);
         if (f != null) {
             f.setDefaultExecutor(mExecutor);
             setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
@@ -466,6 +507,49 @@
             f.onFeatureRemoved();
             features.remove(featureType);
         }
+
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsFeature getImsFeature(int slotId, int featureType) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                return null;
+            }
+            return features.get(featureType);
+        }
+    }
+
+    private void setImsFeatureCreatedForSlot(int slotId,
+            @ImsFeature.FeatureType int featureType, boolean createdForSlot) {
+        synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
+            getImsFeatureCreatedForSlot(slotId).put(featureType, createdForSlot);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean isImsFeatureCreatedForSlot(int slotId,
+            @ImsFeature.FeatureType int featureType) {
+        synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
+            return getImsFeatureCreatedForSlot(slotId).get(featureType);
+        }
+    }
+
+    private SparseBooleanArray getImsFeatureCreatedForSlot(int slotId) {
+        SparseBooleanArray createFlag = mCreateImsFeatureWithSlotIdFlagMap.get(slotId);
+        if (createFlag == null) {
+            createFlag = new SparseBooleanArray();
+            mCreateImsFeatureWithSlotIdFlagMap.put(slotId, createFlag);
+        }
+        return createFlag;
     }
 
     /**
@@ -524,27 +608,95 @@
     }
 
     /**
+     * The framework has enabled IMS for the subscription specified, the ImsService should register
+     * for IMS and perform all appropriate initialization to bring up all ImsFeatures.
+     *
+     * @param slotId The slot ID that IMS will be enabled for.
+     * @param subscriptionId The subscription ID that IMS will be enabled for.
+     */
+    public void enableImsForSubscription(int slotId, int subscriptionId) {
+        enableIms(slotId);
+    }
+
+    /**
+     * The framework has disabled IMS for the subscription specified. The ImsService must deregister
+     * for IMS and set capability status to false for all ImsFeatures.
+     * @param slotId The slot ID that IMS will be disabled for.
+     * @param subscriptionId The subscription ID that IMS will be disabled for.
+     */
+    public void disableImsForSubscription(int slotId, int subscriptionId) {
+        disableIms(slotId);
+    }
+
+    /**
      * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
      * and perform all appropriate initialization to bring up all ImsFeatures.
+     * @deprecated Use {@link #enableImsForSubscription} instead.
      */
+    @Deprecated
     public void enableIms(int slotId) {
     }
 
     /**
      * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
      * and set capability status to false for all ImsFeatures.
+     * @deprecated Use {@link #disableImsForSubscription} instead.
      */
+    @Deprecated
     public void disableIms(int slotId) {
     }
 
     /**
      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified subscription.
+     *
+     * @param subscriptionId The subscription ID that the MMTEL Feature is being created for.
+     * @return The newly created {@link MmTelFeature} associated with the subscription or null if
+     * the feature is not supported.
+     */
+    public @Nullable MmTelFeature createMmTelFeatureForSubscription(int slotId,
+            int subscriptionId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
+        return createMmTelFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified subscription.
+     *
+     * @param subscriptionId The subscription ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the subscription or null if the
+     * feature is not supported.
+     */
+    public @Nullable RcsFeature createRcsFeatureForSubscription(int slotId, int subscriptionId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_RCS, true);
+        return createRcsFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new emergency-only {@link MmTelFeature} is
+     * created for the specified slot. For emergency calls, there is no known Subscription Id.
+     *
+     * @param slotId The slot ID that the MMTEL Feature is being created for.
+     * @return An MmTelFeature instance to be used for the slot ID when there is not
+     * subscription inserted. Only requested when there is no subscription active on
+     * the specified slot.
+     */
+    public @Nullable MmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
+        return createMmTelFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
      * specified slot.
+     * @deprecated Use {@link #createMmTelFeatureForSubscription} instead
      *
      * @param slotId The slot ID that the MMTEL Feature is being created for.
      * @return The newly created {@link MmTelFeature} associated with the slot or null if the
      * feature is not supported.
      */
+    @Deprecated
     public MmTelFeature createMmTelFeature(int slotId) {
         return null;
     }
@@ -552,32 +704,62 @@
     /**
      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
      * specified slot.
+     * @deprecated Use {@link #createRcsFeatureForSubscription} instead
      *
      * @param slotId The slot ID that the RCS Feature is being created for.
      * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
      * is not supported.
      */
+    @Deprecated
     public RcsFeature createRcsFeature(int slotId) {
         return null;
     }
 
     /**
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided
+     * subscription. This will be used by the platform to get/set specific IMS related
+     * configurations.
+     *
+     * @param subscriptionId The subscription ID that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified subscription.
+     */
+    public @NonNull ImsConfigImplBase getConfigForSubscription(int slotId, int subscriptionId) {
+        return getConfig(slotId);
+    }
+
+    /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided
+     * subscription.
+     *
+     * @param subscriptionId The subscription ID that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the subscription.
+     */
+    public @NonNull ImsRegistrationImplBase getRegistrationForSubscription(int slotId,
+            int subscriptionId) {
+        return getRegistration(slotId);
+    }
+
+    /**
      * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
      * will be used by the platform to get/set specific IMS related configurations.
+     * @deprecated use {@link #getConfigForSubscription} instead.
      *
      * @param slotId The slot that the IMS configuration is associated with.
      * @return ImsConfig implementation that is associated with the specified slot.
      */
+    @Deprecated
     public ImsConfigImplBase getConfig(int slotId) {
         return new ImsConfigImplBase();
     }
 
     /**
      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+     * @deprecated use  {@link #getRegistrationForSubscription} instead.
      *
      * @param slotId The slot that is associated with the IMS Registration.
      * @return the ImsRegistration implementation associated with the slot.
      */
+    @Deprecated
     public ImsRegistrationImplBase getRegistration(int slotId) {
         return new ImsRegistrationImplBase();
     }
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index dbf4c99..677c1a9 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -34,6 +34,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
 import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.aidl.IRcsConfigCallback;
 import android.telephony.ims.feature.MmTelFeature;
@@ -54,18 +55,12 @@
  * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
  * applications and may vary. It is up to the carrier and OEM applications to ensure that the
  * correct provisioning keys are being used when integrating with a vendor's ImsService.
- *
- * Note: For compatibility purposes, the integer values [0 - 99] used in
- * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
- * previously defined in the Android framework. Please do not redefine new provisioning keys in this
- * range or it may generate collisions with existing keys. Some common constants have also been
- * defined in this class to make integrating with other system apps easier.
- * @hide
  */
-@SystemApi
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
 public class ProvisioningManager {
 
+    private static final String TAG = "ProvisioningManager";
+
     /**@hide*/
     @StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
             STRING_QUERY_RESULT_ERROR_GENERIC,
@@ -76,14 +71,18 @@
 
     /**
      * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+     * @hide
      */
+    @SystemApi
     public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
             "STRING_QUERY_RESULT_ERROR_GENERIC";
 
     /**
      * The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
      * ImsService implementation was not ready for provisioning queries.
+     * @hide
      */
+    @SystemApi
     public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
             "STRING_QUERY_RESULT_ERROR_NOT_READY";
 
@@ -95,12 +94,16 @@
 
     /**
      * The integer result of provisioning for the queried key is disabled.
+     * @hide
      */
+    @SystemApi
     public static final int PROVISIONING_VALUE_DISABLED = 0;
 
     /**
      * The integer result of provisioning for the queried key is enabled.
+     * @hide
      */
+    @SystemApi
     public static final int PROVISIONING_VALUE_ENABLED = 1;
 
 
@@ -445,27 +448,31 @@
 
     /**
      * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
-     * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
-     * the subscription for WiFi Calling.
+     * {@link android.telephony.SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI},
+     * for the purposes of provisioning the subscription for WiFi Calling.
      *
-     * @see #getProvisioningIntValue(int)
      * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
      */
+    @SystemApi
     public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
 
     /**
      * Override the user-defined WiFi mode for this subscription, defined in
-     * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning
-     * this subscription for WiFi Calling.
+     * {@link android.telephony.SubscriptionManager#WFC_MODE_CONTENT_URI},
+     * for the purposes of provisioning this subscription for WiFi Calling.
      *
      * Valid values for this key are:
      * {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
      * {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
      * {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
      *
-     * @see #getProvisioningIntValue(int)
      * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
      */
+    @SystemApi
     public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
 
     /**
@@ -864,7 +871,9 @@
      * <p>Value is in String format.
      * @see #setProvisioningStringValue(int, String)
      * @see #getProvisioningStringValue(int)
+     * @hide
      */
+    @SystemApi
     public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67;
 
     /**
@@ -886,7 +895,9 @@
 
     /**
      * Callback for IMS provisioning changes.
+     * @hide
      */
+    @SystemApi
     public static class Callback {
 
         private static class CallbackBinder extends IImsConfigCallback.Stub {
@@ -956,11 +967,105 @@
         }
     }
 
+    /**
+     * Callback for IMS provisioning feature changes.
+     */
+    public static class FeatureProvisioningCallback {
+
+        private static class CallbackBinder extends IFeatureProvisioningCallback.Stub {
+
+            private final FeatureProvisioningCallback mFeatureProvisioningCallback;
+            private Executor mExecutor;
+
+            private CallbackBinder(FeatureProvisioningCallback featureProvisioningCallback) {
+                mFeatureProvisioningCallback = featureProvisioningCallback;
+            }
+
+            @Override
+            public final void onFeatureProvisioningChanged(
+                    int capability, int tech, boolean isProvisioned) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mFeatureProvisioningCallback.onFeatureProvisioningChanged(
+                                    capability, tech, isProvisioned));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public final void onRcsFeatureProvisioningChanged(
+                    int capability, int tech, boolean isProvisioned) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
+                                    capability, tech, isProvisioned));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CallbackBinder mBinder = new CallbackBinder(this);
+
+        /**
+         * The IMS MMTEL provisioning has changed for a specific capability and IMS
+         * registration technology.
+         * @param capability The MMTEL capability that provisioning has changed for.
+         * @param tech The IMS registration technology associated with the MMTEL capability that
+         * provisioning has changed for.
+         * @param isProvisioned {@code true} if the capability is provisioned for the technology
+         * specified, or {@code false} if the capability is not provisioned for the technology
+         * specified.
+         */
+        public void onFeatureProvisioningChanged(
+                @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+                boolean isProvisioned) {
+            // Base Implementation
+        }
+
+        /**
+         * The IMS RCS provisioning has changed for a specific capability and IMS
+         * registration technology.
+         * @param capability The RCS capability that provisioning has changed for.
+         * @param tech The IMS registration technology associated with the RCS capability that
+         * provisioning has changed for.
+         * @param isProvisioned {@code true} if the capability is provisioned for the technology
+         * specified, or {@code false} if the capability is not provisioned for the technology
+         * specified.
+         */
+        public void onRcsFeatureProvisioningChanged(
+                @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+                boolean isProvisioned) {
+            // Base Implementation
+        }
+
+        /**@hide*/
+        public final IFeatureProvisioningCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
     private int mSubId;
 
     /**
      * The callback for RCS provisioning changes.
+     * @hide
      */
+    @SystemApi
     public static class RcsProvisioningCallback {
         private static class CallbackBinder extends IRcsConfigCallback.Stub {
 
@@ -1098,7 +1203,9 @@
      * @param subId The ID of the subscription that this ProvisioningManager will use.
      * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
      * @throws IllegalArgumentException if the subscription is invalid.
+     * @hide
      */
+    @SystemApi
     public static @NonNull ProvisioningManager createForSubscriptionId(int subId) {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid subscription ID");
@@ -1107,7 +1214,9 @@
         return new ProvisioningManager(subId);
     }
 
-    private ProvisioningManager(int subId) {
+    /**@hide*/
+    //@SystemApi
+    public ProvisioningManager(int subId) {
         mSubId = subId;
     }
 
@@ -1116,6 +1225,12 @@
      *
      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
      * etc...), this callback will automatically be removed.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+     * </ul>
+     *
      * @param executor The {@link Executor} to call the callback methods on
      * @param callback The provisioning callbackto be registered.
      * @see #unregisterProvisioningChangedCallback(Callback)
@@ -1126,7 +1241,9 @@
      * 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.
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerProvisioningChangedCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull Callback callback) throws ImsException {
@@ -1144,12 +1261,20 @@
      * Unregister an existing {@link Callback}. When the subscription associated with this
      * callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be
      * removed. If this method is called for an inactive subscription, it will result in a no-op.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+     * </ul>
+     *
      * @param callback The existing {@link Callback} to be removed.
      * @see #registerProvisioningChangedCallback(Executor, Callback)
      *
      * @throws IllegalArgumentException if the subscription associated with this callback is
      * invalid.
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
         try {
@@ -1160,6 +1285,62 @@
     }
 
     /**
+     * Register a new {@link FeatureProvisioningCallback}, which is used to listen for
+     * IMS feature provisioning updates.
+     * <p>
+     * When the subscription associated with this callback is removed (SIM removed,
+     * ESIM swap,etc...), this callback will automatically be removed.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @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
+     * {@link ImsException#getCode()} for a more detailed reason.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void registerFeatureProvisioningChangedCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull FeatureProvisioningCallback callback) throws ImsException {
+        callback.setExecutor(executor);
+        try {
+            getITelephony().registerFeatureProvisioningChangedCallback(mSubId,
+                    callback.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregisters a previously registered {@link FeatureProvisioningCallback}
+     * instance.  When the subscription associated with this
+     * callback is removed (SIM removed, ESIM swap, etc...), this callback will
+     * automatically be removed. If this method is called for an inactive
+     * subscription, it will result in a no-op.
+     *
+     * @param callback The existing {@link FeatureProvisioningCallback} to be removed.
+     * @see #registerFeatureProvisioningChangedCallback(Executor, FeatureProvisioningCallback)
+     */
+    public void unregisterFeatureProvisioningChangedCallback(
+            @NonNull FeatureProvisioningCallback callback) {
+        try {
+            getITelephony().unregisterFeatureProvisioningChangedCallback(mSubId,
+                    callback.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Query for the integer value associated with the provided key.
      *
      * This operation is blocking and should not be performed on the UI thread.
@@ -1168,7 +1349,9 @@
      * @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.
+     * @hide
      */
+    @SystemApi
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public int getProvisioningIntValue(int key) {
@@ -1188,7 +1371,9 @@
      * @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.
+     * @hide
      */
+    @SystemApi
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @Nullable @StringResultError String getProvisioningStringValue(int key) {
@@ -1209,7 +1394,15 @@
      * @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.
+     * @hide
+     *
+     * Note: For compatibility purposes, the integer values [0 - 99] used in
+     * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+     * previously defined in the Android framework. Please do not redefine new provisioning keys
+     * in this range or it may generate collisions with existing keys. Some common constants have
+     * also been defined in this class to make integrating with other system apps easier.
      */
+    @SystemApi
     @WorkerThread
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
@@ -1229,7 +1422,9 @@
      *     should be appropriately namespaced to avoid collision.
      * @param value a String value for the provided key.
      * @return the result of setting the configuration value.
+     * @hide
      */
+    @SystemApi
     @WorkerThread
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
@@ -1249,8 +1444,14 @@
      * does not support the capability/technology combination specified, this operation will be a
      * no-op.
      *
-     * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
-     * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * <p>Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>or that the calling app has carrier privileges (see</li>
+     *     <li>{@link TelephonyManager#hasCarrierPrivileges}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
      * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
      */
     @WorkerThread
@@ -1258,9 +1459,10 @@
     public void setProvisioningStatusForCapability(
             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int tech,  boolean isProvisioned) {
+
         try {
             getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
-                    isProvisioned);
+                        isProvisioned);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -1274,14 +1476,21 @@
      * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
      * always return {@code true}.
      *
-     * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
-     * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
      * @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.
      */
     @WorkerThread
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public boolean getProvisioningStatusForCapability(
             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
@@ -1299,17 +1508,93 @@
      * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
      * {@code true}.
      *
-     * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
      * @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.
+     * @deprecated Use {@link #getRcsProvisioningStatusForCapability(int, int)} instead,
+     * as this only retrieves provisioning information for
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+     * @hide
      */
+    @Deprecated
+    @SystemApi
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean getRcsProvisioningStatusForCapability(
             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
         try {
-            return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability);
+            return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability,
+            ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the provisioning status for the IMS RCS capability specified.
+     *
+     * If provisioning is not required for the queried
+     * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method
+     * will always return {@code true}.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+     * @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.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean getRcsProvisioningStatusForCapability(
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the provisioning status for the IMS RCS capability using the specified subscription.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE}</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+
+     * Provisioning may or may not be required, depending on the carrier configuration. If
+     * provisioning is not required for the carrier associated with this subscription or the device
+     * does not support the capability/technology combination specified, this operation will be a
+     * no-op.
+     *
+     * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+     *                      false otherwise.
+     * @deprecated Use {@link #setRcsProvisioningStatusForCapability(int, int, boolean)} instead,
+     * as this method only sets provisioning information for
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRcsProvisioningStatusForCapability(
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            boolean isProvisioned) {
+        try {
+            getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, isProvisioned);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -1323,7 +1608,14 @@
      * does not support the capability/technology combination specified, this operation will be a
      * no-op.
      *
-     * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
      * @param isProvisioned true if the device is provisioned for the RCS capability specified,
      *                      false otherwise.
      */
@@ -1331,31 +1623,92 @@
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRcsProvisioningStatusForCapability(
             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
-            boolean isProvisioned) {
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
         try {
             getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
-                    isProvisioned);
+                    tech, isProvisioned);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
+     * Indicates whether provisioning for the MMTEL capability and IMS registration technology
+     * specified is required or not
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li> or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @return true if provisioning is required for the MMTEL capability and IMS
+     * registration technology specified, false if it is not required.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean isProvisioningRequiredForCapability(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().isProvisioningRequiredForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+
+    /**
+     * Indicates whether provisioning for the RCS capability and IMS registration technology
+     * specified is required or not
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li> or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @return true if provisioning is required for the RCS capability and IMS
+     * registration technology specified, false if it is not required.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean isRcsProvisioningRequiredForCapability(
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+
+    /**
      * Notify the framework that an RCS autoconfiguration XML file has been received for
      * provisioning.
-     * <p>
-     * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has
-     * carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Requires Permission:
+     * <ul>
+     *     <li>{@link Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>or that the calling app has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
      * @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.
-     *
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
         if (config == null) {
             throw new IllegalArgumentException("Must include a non-null config XML file.");
         }
+
         try {
             getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
         } catch (RemoteException e) {
@@ -1374,7 +1727,9 @@
      * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
      * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
      * status.
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
@@ -1382,7 +1737,9 @@
 
     /**
      * Integer extra to specify subscription index.
+     * @hide
      */
+    @SystemApi
     public static final String EXTRA_SUBSCRIPTION_ID =
             "android.telephony.ims.extra.SUBSCRIPTION_ID";
 
@@ -1392,22 +1749,30 @@
      * <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE},
      * {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of
      * {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}.
+     * @hide
      */
+    @SystemApi
     public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
 
     /**
      * RCS VoLTE single registration is supported by the device and carrier.
+     * @hide
      */
+    @SystemApi
     public static final int STATUS_CAPABLE                       = 0;
 
     /**
      * RCS VoLTE single registration is not supported by the device.
+     * @hide
      */
+    @SystemApi
     public static final int STATUS_DEVICE_NOT_CAPABLE            = 0x01;
 
     /**
      * RCS VoLTE single registration is not supported by the carrier
+     * @hide
      */
+    @SystemApi
     public static final int STATUS_CARRIER_NOT_CAPABLE           = 0x01 << 1;
 
     /**
@@ -1417,11 +1782,14 @@
      * provisioning is done using autoconfiguration, then these parameters shall be
      * sent in the HTTP get request to fetch the RCS provisioning. RCS client
      * configuration must be provided by the application before registering for the
-     * provisioning status events {@link #registerRcsProvisioningCallback()}
+     * provisioning status events
+     * {@link #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)}
      * 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}
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
     public void setRcsClientConfiguration(
             @NonNull RcsClientConfiguration rcc) throws ImsException {
@@ -1442,18 +1810,21 @@
      * <ul>
      *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
      *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
-     *     <li>or that the caller has carrier privileges (see
+     *     <li>or that the calling app has carrier privileges (see
      *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
      * </ul>
+     *
      * @return true if IMS single registration is capable at this time, or false otherwise
-     * @throws ImsException If the remote ImsService is not available for
-     * any reason or the subscription associated with this instance is no
-     * longer active. See {@link ImsException#getCode()} for more
-     * information.
+     * @throws ImsException If the remote ImsService is not available for any reason or
+     * the subscription associated with this instance is no longer active.
+     * See {@link ImsException#getCode()} for more information.
      * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
      * device supports IMS single registration.
+     * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
     public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
         try {
@@ -1480,8 +1851,6 @@
     * <ul>
     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
     *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
-    *     <li>or that the caller has carrier privileges (see
-    *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
     * </ul>
     *
     * @param executor The {@link Executor} to call the callback methods on
@@ -1499,8 +1868,11 @@
     * params (See {@link #setRcsClientConfiguration}) and re register the
     * callback.
     * See {@link ImsException#getCode()} for a more detailed reason.
+    * @hide
     */
-    @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
     public void registerRcsProvisioningCallback(
             @NonNull @CallbackExecutor Executor executor,
@@ -1527,8 +1899,6 @@
      * <ul>
      *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
      *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
-     *     <li>or that the caller has carrier privileges (see
-     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
      * </ul>
      *
      * @param callback The existing {@link RcsProvisioningCallback} to be
@@ -1536,8 +1906,11 @@
      * @see #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)
      * @throws IllegalArgumentException if the subscription associated with
      * this callback is invalid.
+     * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
     public void unregisterRcsProvisioningCallback(
             @NonNull RcsProvisioningCallback callback) {
@@ -1558,9 +1931,10 @@
      * {@link RcsProvisioningCallback} may expect to receive
      * {@link RcsProvisioningCallback#onConfigurationReset}, then
      * {@link RcsProvisioningCallback#onConfigurationChanged} when the new
-     * RCS configuration is received and notified by
-     * {@link #notifyRcsAutoConfigurationReceived}
+     * RCS configuration is received and notified by {@link #notifyRcsAutoConfigurationReceived}
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
     public void triggerRcsReconfiguration() {
         try {
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 391372a..acc6243 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -57,7 +57,7 @@
      * @param startLine The start line of the message, containing either the request-line or
      *                  status-line.
      * @param headerSection A String containing the full unencoded SIP message header.
-     * @param content UTF-8 encoded SIP message body.
+     * @param content SIP message body.
      */
     public SipMessage(@NonNull String startLine, @NonNull String headerSection,
             @NonNull byte[] content) {
@@ -105,7 +105,7 @@
     }
 
     /**
-     * @return only the UTF-8 encoded SIP message body.
+     * @return the SIP message body.
      */
     public @NonNull byte[] getContent() {
         return mContent;
diff --git a/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl b/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl
new file mode 100644
index 0000000..63ec4aa
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.telephony.ims.aidl;
+
+/**
+ * Provides callback interface for FeatureProvisioning when a value has changed.
+ *
+ * {@hide}
+ */
+oneway interface IFeatureProvisioningCallback {
+    void onFeatureProvisioningChanged(int capability, int tech, boolean isProvisioned);
+    void onRcsFeatureProvisioningChanged(int capability, int tech, boolean isProvisioned);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c6966b3..ae6166f 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -32,18 +32,19 @@
  */
 interface IImsServiceController {
     void setListener(IImsServiceControllerListener l);
-    IImsMmTelFeature createMmTelFeature(int slotId);
-    IImsRcsFeature createRcsFeature(int slotId);
+    IImsMmTelFeature createMmTelFeature(int slotId, int subId);
+    IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId);
+    IImsRcsFeature createRcsFeature(int slotId, int subId);
     ImsFeatureConfiguration querySupportedImsFeatures();
     long getImsServiceCapabilities();
     void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
     void notifyImsServiceReadyForFeatureCreation();
-    void removeImsFeature(int slotId, int featureType);
-    IImsConfig getConfig(int slotId);
-    IImsRegistration getRegistration(int slotId);
+    void removeImsFeature(int slotId, int featureType, boolean changeSubId);
+    IImsConfig getConfig(int slotId, int subId);
+    IImsRegistration getRegistration(int slotId, int subId);
     ISipTransport getSipTransport(int slotId);
-    oneway void enableIms(int slotId);
-    oneway void disableIms(int slotId);
+    oneway void enableIms(int slotId, int subId);
+    oneway void disableIms(int slotId, int subId);
 }
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7fdf21b..ad2e9e1 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -394,6 +394,13 @@
         public @interface MmTelCapability {}
 
         /**
+         * Undefined capability type for initialization
+         * This is used to check the upper range of MmTel capability
+         * {@hide}
+         */
+        public static final int CAPABILITY_TYPE_NONE = 0;
+
+        /**
          * This MmTelFeature supports Voice calling (IR.92)
          */
         public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
@@ -419,6 +426,12 @@
         public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
 
         /**
+         * This is used to check the upper range of MmTel capability
+         * {@hide}
+         */
+        public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
+
+        /**
          * @hide
          */
         @Override
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 11cf0e3..af7373b 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -59,9 +59,7 @@
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
  * this class and provide implementations of the RcsFeature methods that they support.
- * @hide
  */
-@SystemApi
 public class RcsFeature extends ImsFeature {
 
     private static final String LOG_TAG = "RcsFeature";
@@ -186,14 +184,14 @@
      * Contains the capabilities defined and supported by a {@link RcsFeature} in the
      * form of a bitmask. The capabilities that are used in the RcsFeature are
      * defined as:
-     * {@link RcsUceAdatper.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
-     * {@link RceUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+     * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+     * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
      *
      * The enabled capabilities of this RcsFeature will be set by the framework
-     * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+     * using {#changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
      * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
      * of the capability and notify the capability status as true using
-     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+     * {#notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
      * framework that the capability is available for usage.
      */
     public static class RcsImsCapabilities extends Capabilities {
@@ -227,10 +225,18 @@
         public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
 
         /**
+         * This is used to check the upper range of RCS capability
+         * {@hide}
+         */
+        public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+        /**
          * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
          * @param capabilities The capabilities that are supported for RCS in the form of a
          * bitfield.
+         * @hide
          */
+        @SystemApi
         public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
             super(capabilities);
         }
@@ -243,17 +249,29 @@
             super(capabilities.getMask());
         }
 
+        /**
+         * @hide
+         */
         @Override
+        @SystemApi
         public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
             super.addCapabilities(capabilities);
         }
 
+        /**
+         * @hide
+         */
         @Override
+        @SystemApi
         public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
             super.removeCapabilities(capabilities);
         }
 
+        /**
+         * @hide
+         */
         @Override
+        @SystemApi
         public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
             return super.isCapable(capabilities);
         }
@@ -270,7 +288,9 @@
      * Method stubs called from the framework will be called asynchronously. To specify the
      * {@link Executor} that the methods stubs will be called, use
      * {@link RcsFeature#RcsFeature(Executor)} instead.
+     * @hide
      */
+    @SystemApi
     public RcsFeature() {
         super();
         // Run on the Binder threads that call them.
@@ -282,7 +302,9 @@
      * framework.
      * @param executor The executor for the framework to use when executing the methods overridden
      * by the implementation of RcsFeature.
+     * @hide
      */
+    @SystemApi
     public RcsFeature(@NonNull Executor executor) {
         super();
         if (executor == null) {
@@ -301,7 +323,7 @@
      * @hide
      */
     @Override
-    public void initialize(Context context, int slotId) {
+    public void initialize(@NonNull Context context, @NonNull int slotId) {
         super.initialize(context, slotId);
         // Notify that the RcsFeature is ready.
         mExecutor.execute(() -> onFeatureReady());
@@ -313,8 +335,10 @@
      * requests. To change the status of the capabilities
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
      * @return A copy of the current RcsFeature capability status.
+     * @hide
      */
     @Override
+    @SystemApi
     public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
         return new RcsImsCapabilities(super.queryCapabilityStatus());
     }
@@ -324,7 +348,9 @@
      * this signals to the framework that the capability has been initialized and is ready.
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
      * @param capabilities The current capability status of the RcsFeature.
+     * @hide
      */
+    @SystemApi
     public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
         if (capabilities == null) {
             throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
@@ -341,7 +367,9 @@
      * @param capability The capability that we are querying the configuration for.
      * @param radioTech The radio technology type that we are querying.
      * @return true if the capability is enabled, false otherwise.
+     * @hide
      */
+    @SystemApi
     public boolean queryCapabilityConfiguration(
             @RcsUceAdapter.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
@@ -364,8 +392,10 @@
      * be called for each capability change that resulted in an error.
      * @param request The request to change the capability.
      * @param callback To notify the framework that the result of the capability changes.
+     * @hide
      */
     @Override
+    @SystemApi
     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
             @NonNull CapabilityCallbackProxy callback) {
         // Base Implementation - Override to provide functionality
@@ -385,7 +415,9 @@
      * event to the framework.
      * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
      * exchange if it is supported by the device.
+     * @hide
      */
+    @SystemApi
     public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
             @NonNull CapabilityExchangeEventListener listener) {
         // Base Implementation, override to implement functionality
@@ -395,20 +427,28 @@
     /**
      * Remove the given CapabilityExchangeImplBase instance.
      * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+     * @hide
      */
+    @SystemApi
     public void destroyCapabilityExchangeImpl(
             @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
         // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
     }
 
-    /**{@inheritDoc}*/
+    /**{@inheritDoc}
+     * @hide
+     */
     @Override
+    @SystemApi
     public void onFeatureRemoved() {
 
     }
 
-    /**{@inheritDoc}*/
+    /**{@inheritDoc}
+     * @hide
+     */
     @Override
+    @SystemApi
     public void onFeatureReady() {
 
     }
@@ -448,7 +488,9 @@
      * has already been created in the framework.
      * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
      * event to the framework.
+     * @hide
      */
+    @SystemApi
     private void initRcsCapabilityExchangeImplBase(
             @NonNull CapabilityExchangeEventListener listener) {
         synchronized (mLock) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 3b151a4..ac5565b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -34,7 +34,6 @@
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.ArrayUtils;
 
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.CancellationException;
@@ -51,9 +50,7 @@
  * <p>
  * Note: There is no guarantee on the thread that the calls from the framework will be called on. It
  * is the implementors responsibility to handle moving the calls to a working thread if required.
- * @hide
  */
-@SystemApi
 public class ImsRegistrationImplBase {
 
     private static final String LOG_TAG = "ImsRegistrationImplBase";
@@ -94,6 +91,12 @@
      */
     public static final int REGISTRATION_TECH_NR = 3;
 
+    /**
+     * This is used to check the upper range of registration tech
+     * {@hide}
+     */
+    public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1;
+
     // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
     // state.
     // The unknown state is set as the initialization state. This is so that we do not call back
@@ -109,7 +112,9 @@
      * Method stubs called from the framework will be called asynchronously. To specify the
      * {@link Executor} that the methods stubs will be called, use
      * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public ImsRegistrationImplBase() {
         super();
     }
@@ -119,7 +124,9 @@
      * framework.
      * @param executor The executor for the framework to use when executing the methods overridden
      * by the implementation of ImsRegistration.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public ImsRegistrationImplBase(@NonNull Executor executor) {
         super();
         mExecutor = executor;
@@ -250,7 +257,9 @@
      * If the SIP delegate feature tag configuration has changed, then this method will be
      * called in order to let the ImsService know that it can pick up these changes in the IMS
      * registration.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public void updateSipDelegateRegistration() {
         // Stub implementation, ImsService should implement this
     }
@@ -266,7 +275,9 @@
      * <p>
      * This should not affect the registration of features managed by the ImsService itself, such as
      * feature tags related to MMTEL registration.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public void triggerSipDelegateDeregistration() {
         // Stub implementation, ImsService should implement this
     }
@@ -284,7 +295,9 @@
      *    be carrier specific.
      * @param sipReason The reason associated with the SIP error code. {@code null} if there was no
      *    reason associated with the error.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode,
             @Nullable String sipReason) {
         // Stub implementation, ImsService should implement this
@@ -295,7 +308,9 @@
      * Notify the framework that the device is connected to the IMS network.
      *
      * @param imsRadioTech the radio access technology.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
     }
@@ -304,7 +319,9 @@
      * Notify the framework that the device is connected to the IMS network.
      *
      * @param attributes The attributes associated with the IMS registration.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
         mCallbacks.broadcastAction((c) -> {
@@ -320,7 +337,9 @@
      * Notify the framework that the device is trying to connect the IMS network.
      *
      * @param imsRadioTech the radio access technology.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
     }
@@ -329,7 +348,9 @@
      * Notify the framework that the device is trying to connect the IMS network.
      *
      * @param attributes The attributes associated with the IMS registration.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
         mCallbacks.broadcastAction((c) -> {
@@ -356,7 +377,9 @@
      * result.
      *
      * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onDeregistered(ImsReasonInfo info) {
         updateToDisconnectedState(info);
         // ImsReasonInfo should never be null.
@@ -377,7 +400,9 @@
      * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
      * {@link #REGISTRATION_TECH_CROSS_SIM}.
      * @param info The {@link ImsReasonInfo} for the failure to change technology.
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
@@ -396,7 +421,9 @@
      *
      * The {@link Uri}s are not guaranteed to be different between subsequent calls.
      * @param uris changed uris
+     * @hide This API is not part of the Android public SDK API
      */
+    @SystemApi
     public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
         synchronized (mLock) {
             mUris = ArrayUtils.cloneOrNull(uris);
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index f5f67bd..416096b 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -23,11 +23,11 @@
  * {@hide}
  */
 oneway interface IImsServiceFeatureCallback {
-    void imsFeatureCreated(in ImsFeatureContainer feature);
+    void imsFeatureCreated(in ImsFeatureContainer feature, int subId);
     // Reason defined in FeatureConnector.UnavailableReason
     void imsFeatureRemoved(int reason);
     // Status defined in ImsFeature.ImsState.
-    void imsStatusChanged(int status);
+    void imsStatusChanged(int status, int subId);
     //Capabilities defined in ImsService.ImsServiceCapability
     void updateCapabilities(long capabilities);
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index be54cec..bce7a24 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -55,6 +55,7 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.RcsClientConfiguration;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -1992,6 +1993,18 @@
     void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
 
     /**
+     * Register an IMS provisioning change callback with Telephony.
+     */
+    void registerFeatureProvisioningChangedCallback(int subId,
+            IFeatureProvisioningCallback callback);
+
+    /**
+     * unregister an existing IMS provisioning change callback.
+     */
+    void unregisterFeatureProvisioningChangedCallback(int subId,
+            IFeatureProvisioningCallback callback);
+
+    /**
      * Set the provisioning status for the IMS MmTel capability using the specified subscription.
      */
     void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
@@ -2005,19 +2018,12 @@
     /**
      * Get the provisioning status for the IMS Rcs capability specified.
      */
-    boolean getRcsProvisioningStatusForCapability(int subId, int capability);
+    boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech);
 
     /**
      * Set the provisioning status for the IMS Rcs capability using the specified subscription.
      */
-    void setRcsProvisioningStatusForCapability(int subId, int capability,
-            boolean isProvisioned);
-
-    /** Is the capability and tech flagged as provisioned in the cache */
-    boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
-
-    /** Set the provisioning for the capability and tech in the cache */
-    void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
+    void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
             boolean isProvisioned);
 
     /**
@@ -2523,4 +2529,18 @@
      * @return the service name of the modem service which bind to.
      */
     String getModemService();
+
+    /**
+     * Is Provisioning required for capability
+     * @return true if provisioning is required for the MMTEL capability and IMS
+     * registration technology specified, false if it is not required.
+     */
+    boolean isProvisioningRequiredForCapability(int subId, int capability, int tech);
+
+    /**
+     * Is RCS Provisioning required for capability
+     * @return true if provisioning is required for the RCS capability and IMS
+     * registration technology specified, false if it is not required.
+     */
+    boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index f650246..813e80e 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -95,6 +95,7 @@
     public static final int PRESENTATION_UNKNOWN = 3;    // no specified or unknown by network
     @UnsupportedAppUsage
     public static final int PRESENTATION_PAYPHONE = 4;   // show pay phone info
+    public static final int PRESENTATION_UNAVAILABLE = 5;   // show unavailable
 
     public static final String PHONE_NAME_KEY = "phoneName";
     public static final String DATA_NETWORK_TYPE_KEY = "networkType";
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ba95841..39ab7eb 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -592,6 +592,7 @@
     int RIL_UNSOL_UNTHROTTLE_APN = 1052;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
+    int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
 
     /* The following unsols are not defined in RIL.h */
     int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 7f5982f..dda95b1 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -22,8 +22,6 @@
 import android.telephony.euicc.DownloadableSubscription;
 import android.telephony.euicc.EuiccInfo;
 
-import com.android.internal.telephony.euicc.IResultCallback;
-
 import java.util.List;
 
 /** @hide */
@@ -45,8 +43,8 @@
         in PendingIntent callbackIntent);
     oneway void switchToSubscription(int cardId, int subscriptionId, String callingPackage,
         in PendingIntent callbackIntent);
-    oneway void switchToSubscriptionWithPort(int cardId, int portIndex, int subscriptionId,
-        String callingPackage, in IResultCallback callback);
+    oneway void switchToSubscriptionWithPort(int cardId, int subscriptionId, int portIndex,
+        String callingPackage, in PendingIntent callbackIntent);
     oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
         String callingPackage, in PendingIntent callbackIntent);
     oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent);
diff --git a/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl
deleted file mode 100644
index 69f479c..0000000
--- a/telephony/java/com/android/internal/telephony/euicc/IResultCallback.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.telephony.euicc;
-
-import android.content.Intent;
-
-/** @hide */
-oneway interface IResultCallback {
-    void onComplete(int resultCode, in Intent resultIntent);
-}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 21c3f76..f518d53 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -24,6 +24,7 @@
 import android.graphics.Color;
 import android.os.Build;
 import android.telephony.UiccPortInfo;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
@@ -934,6 +935,13 @@
     }
 
     /**
+     * Strip all the trailing 'F' characters of a string if exists and compare.
+     */
+    public static boolean compareIgnoreTrailingFs(String a, String b) {
+        return TextUtils.equals(a, b) || TextUtils.equals(stripTrailingFs(a), stripTrailingFs(b));
+    }
+
+    /**
      * Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
      * hex number, 0 will be returned.
      */
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 97ebba6..8be7324 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -26,7 +26,19 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     //   SPDX-license-identifier-CPL-1.0
-    default_applicable_licenses: ["frameworks_base_license"],
+    default_applicable_licenses: ["frameworks_base_test-base_license"],
+}
+
+license {
+    name: "frameworks_base_test-base_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-CPL-1.0",
+    ],
+    license_text: [
+        "src/junit/cpl-v10.html",
+    ],
 }
 
 java_sdk_library {
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 0f56bb3..2a19af9 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -18,12 +18,19 @@
 // =====================================
 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
-    //   SPDX-license-identifier-CPL-1.0
-    default_applicable_licenses: ["frameworks_base_license"],
+    default_applicable_licenses: ["frameworks_base_test-runner_license"],
+}
+
+license {
+    name: "frameworks_base_test-runner_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-CPL-1.0",
+    ],
+    license_text: [
+        "src/junit/cpl-v10.html",
+    ],
 }
 
 java_sdk_library {
diff --git a/tests/AppLaunchWear/Android.bp b/tests/AppLaunchWear/Android.bp
deleted file mode 100644
index e2fc473..0000000
--- a/tests/AppLaunchWear/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
-    name: "AppLaunchWear",
-    // Only compile source java files in this apk.
-    srcs: ["src/**/*.java"],
-    platform_apis: true,
-    certificate: "platform",
-    libs: [
-        "android.test.base",
-        "android.test.runner",
-    ],
-    static_libs: ["androidx.test.rules"],
-    test_suites: ["device-tests"],
-}
diff --git a/tests/AppLaunchWear/AndroidManifest.xml b/tests/AppLaunchWear/AndroidManifest.xml
deleted file mode 100644
index 7dfd7ba..0000000
--- a/tests/AppLaunchWear/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.applaunch"
-    android:sharedUserId="android.uid.system" >
-
-   <uses-permission android:name="android.permission.REAL_GET_TASKS" />
-   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
-   <uses-sdk
-        android:minSdkVersion="22"
-        android:targetSdkVersion="24" />
-
-    <instrumentation android:label="Measure app start up time"
-                     android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="com.android.tests.applaunch" />
-
-    <application android:label="App Launch Test">
-        <uses-library android:name="android.test.runner" />
-    </application>
-</manifest>
diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
deleted file mode 100644
index 97701c6..0000000
--- a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.applaunch;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.ActivityManager;
-import android.app.ActivityManager.ProcessErrorStateInfo;
-import android.app.IActivityManager;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.test.InstrumentationTestCase;
-import android.test.InstrumentationTestRunner;
-import android.util.Log;
-
-import androidx.test.rule.logging.AtraceLogger;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * This test is intended to measure the time it takes for the apps to start.
- * Names of the applications are passed in command line, and the
- * test starts each application, and reports the start up time in milliseconds.
- * The instrumentation expects the following key to be passed on the command line:
- * apps - A list of applications to start and their corresponding result keys
- * in the following format:
- * -e apps <app name>^<result key>|<app name>^<result key>
- */
-public class AppLaunch extends InstrumentationTestCase {
-
-    private static final int JOIN_TIMEOUT = 10000;
-    private static final String TAG = AppLaunch.class.getSimpleName();
-
-    // optional parameter: comma separated list of required account types before proceeding
-    // with the app launch
-    private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
-    private static final String KEY_APPS = "apps";
-    private static final String KEY_TRIAL_LAUNCH = "trial_launch";
-    private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
-    private static final String KEY_LAUNCH_ORDER = "launch_order";
-    private static final String KEY_DROP_CACHE = "drop_cache";
-    private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd";
-    private static final String KEY_SIMPLEPERF_APP = "simpleperf_app";
-    private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
-    private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
-    private static final String KEY_TRACE_DIRECTORY = "trace_directory";
-    private static final String KEY_TRACE_CATEGORY = "trace_categories";
-    private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
-    private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
-    private static final String KEY_COMPILER_FILTERS = "compiler_filters";
-
-    private static final String SIMPLEPERF_APP_CMD =
-            "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
-    private static final String WEARABLE_ACTION_GOOGLE =
-            "com.google.android.wearable.action.GOOGLE";
-    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
-    private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
-    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
-    private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
-    private static final String LAUNCH_FILE = "applaunch.txt";
-    private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
-    private static final String DEFAULT_TRACE_CATEGORIES =
-            "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
-    private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
-    private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
-    private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
-    private static final String DELIMITER = ",";
-    private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
-    private static final String APP_LAUNCH_CMD = "am start -W -n";
-    private static final String SUCCESS_MESSAGE = "Status: ok";
-    private static final String WARNING_MESSAGE = "Warning: Activity not started";
-    private static final String COMPILE_SUCCESS = "Success";
-    private static final String THIS_TIME = "ThisTime:";
-    private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
-    private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
-    private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION";
-    private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
-    private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
-    private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
-    private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
-    private static final String SPEED_PROFILE_FILTER = "speed-profile";
-    private static final String VERIFY_FILTER = "verify";
-    private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
-    private static final String WEARABLE_HOME_PACKAGE = "com.google.android.wearable.app";
-
-    private Map<String, Intent> mNameToIntent;
-    private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
-    private Map<String, String> mNameToResultKey;
-    private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
-    private IActivityManager mAm;
-    private String mSimplePerfCmd = null;
-    private String mLaunchOrder = null;
-    private boolean mDropCache = false;
-    private int mLaunchIterations = 10;
-    private int mTraceLaunchCount = 0;
-    private String mTraceDirectoryStr = null;
-    private Bundle mResult = new Bundle();
-    private Set<String> mRequiredAccounts;
-    private boolean mTrialLaunch = false;
-    private BufferedWriter mBufferedWriter = null;
-    private boolean mSimplePerfAppOnly = false;
-    private String[] mCompilerFilters = null;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
-        super.tearDown();
-    }
-
-    private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) {
-        mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result);
-    }
-
-    private boolean hasFailureOnFirstLaunch(LaunchOrder launch) {
-        List<AppLaunchResult> results =
-            mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter());
-        return (results.size() > 0) && (results.get(0).mLaunchTime < 0);
-    }
-
-    public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
-            IOException, InterruptedException {
-        InstrumentationTestRunner instrumentation =
-                (InstrumentationTestRunner)getInstrumentation();
-        Bundle args = instrumentation.getArguments();
-        mAm = ActivityManager.getService();
-        String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
-
-        createMappings();
-        parseArgs(args);
-        checkAccountSignIn();
-
-        // Root directory for applaunch file to log the app launch output
-        // Will be useful in case of simpleperf command is used
-        File launchRootDir = null;
-        if (null != launchDirectory && !launchDirectory.isEmpty()) {
-            launchRootDir = new File(launchDirectory);
-            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
-                throw new IOException("Unable to create the destination directory");
-            }
-        }
-
-        try {
-            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
-
-            if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
-                throw new IOException("Unable to create the lauch file sub directory");
-            }
-            File file = new File(launchSubDir, LAUNCH_FILE);
-            FileOutputStream outputStream = new FileOutputStream(file);
-            mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
-                    outputStream));
-
-            // Root directory for trace file during the launches
-            File rootTrace = null;
-            File rootTraceSubDir = null;
-            int traceBufferSize = 0;
-            int traceDumpInterval = 0;
-            Set<String> traceCategoriesSet = null;
-            if (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()) {
-                rootTrace = new File(mTraceDirectoryStr);
-                if (!rootTrace.exists() && !rootTrace.mkdirs()) {
-                    throw new IOException("Unable to create the trace directory");
-                }
-                rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY);
-                if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) {
-                    throw new IOException("Unable to create the trace sub directory");
-                }
-                assertNotNull("Trace iteration parameter is mandatory",
-                        args.getString(KEY_TRACE_ITERATIONS));
-                mTraceLaunchCount = Integer.parseInt(args.getString(KEY_TRACE_ITERATIONS));
-                String traceCategoriesStr = args
-                        .getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES);
-                traceBufferSize = Integer.parseInt(args.getString(KEY_TRACE_BUFFERSIZE,
-                        DEFAULT_TRACE_BUFFER_SIZE));
-                traceDumpInterval = Integer.parseInt(args.getString(KEY_TRACE_DUMPINTERVAL,
-                        DEFAULT_TRACE_DUMP_INTERVAL));
-                traceCategoriesSet = new HashSet<String>();
-                if (!traceCategoriesStr.isEmpty()) {
-                    String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER);
-                    for (int i = 0; i < traceCategoriesSplit.length; i++) {
-                        traceCategoriesSet.add(traceCategoriesSplit[i]);
-                    }
-                }
-            }
-
-            // Get the app launch order based on launch order, trial launch,
-            // launch iterations and trace iterations
-            setLaunchOrder();
-
-            for (LaunchOrder launch : mLaunchOrderList) {
-                if (mNameToIntent.get(launch.getApp()) == null) {
-                    continue;
-                }
-                dropCache();
-                String appPkgName = mNameToIntent.get(launch.getApp())
-                        .getComponent().getPackageName();
-                Log.v(TAG, String.format("\nApp name: %s", launch.getApp()));
-                Log.v(TAG, String.format("Adding app package name: %s", appPkgName));
-                // App launch times for trial launch will not be used for final
-                // launch time calculations.
-                if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
-                    // In the "applaunch.txt" file, trail launches is referenced using
-                    // "TRIAL_LAUNCH"
-                    Log.v(TAG, "Trial Launch");
-                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
-                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
-                              compileApp(VERIFY_FILTER, appPkgName));
-                    } else if (launch.getCompilerFilter() != null) {
-                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
-                              compileApp(launch.getCompilerFilter(), appPkgName));
-                    }
-                    // We only need to run a trial for the speed-profile filter, but we always
-                    // run one for "applaunch.txt" consistency.
-                    AppLaunchResult launchResult = null;
-                    if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
-                        Log.v(TAG, "Home package detected. Not killing app");
-                        launchResult = startApp(launch.getApp(), false, launch.getLaunchReason());
-                    } else {
-                        Log.v(TAG, "Will kill app before launch");
-                        launchResult = startApp(launch.getApp(), true, launch.getLaunchReason());
-                    }
-                    if (launchResult.mLaunchTime < 0) {
-                        addLaunchResult(launch, new AppLaunchResult());
-                        // simply pass the app if launch isn't successful
-                        // error should have already been logged by startApp
-                        continue;
-                    }
-                    sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
-                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
-                        // Send SIGUSR1 to force dumping a profile.
-                        String sendSignalCommand =
-                            String.format("killall -s SIGUSR1 %s", appPkgName);
-                        getInstrumentation().getUiAutomation().executeShellCommand(
-                            sendSignalCommand);
-                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
-                              compileApp(launch.getCompilerFilter(), appPkgName));
-                    }
-                }
-
-                // App launch times used for final calculation
-                else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
-                    Log.v(TAG, "Launch iteration prefix.");
-                    AppLaunchResult launchResults = null;
-                    if (hasFailureOnFirstLaunch(launch)) {
-                        // skip if the app has failures while launched first
-                        continue;
-                    }
-                    // In the "applaunch.txt" file app launches are referenced using
-                    // "LAUNCH_ITERATION - ITERATION NUM"
-                    if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
-                        Log.v(TAG, "Home package detected. Not killing app");
-                        launchResults = startApp(launch.getApp(), false, launch.getLaunchReason());
-                    } else {
-                        Log.v(TAG, "Will kill app before launch");
-                        launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
-                    }
-                    if (launchResults.mLaunchTime < 0) {
-                        addLaunchResult(launch, new AppLaunchResult());
-                        // if it fails once, skip the rest of the launches
-                        continue;
-                    } else {
-                        addLaunchResult(launch, launchResults);
-                    }
-                    sleep(POST_LAUNCH_IDLE_TIMEOUT);
-                }
-
-                // App launch times for trace launch will not be used for final
-                // launch time calculations.
-                else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
-                    Log.v(TAG, "Trace iteration prefix");
-                    AtraceLogger atraceLogger = AtraceLogger
-                            .getAtraceLoggerInstance(getInstrumentation());
-                    // Start the trace
-                    try {
-                        atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
-                                traceDumpInterval, rootTraceSubDir,
-                                String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
-                        if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
-                            Log.v(TAG, "Home package detected. Not killing app");
-                            startApp(launch.getApp(), false, launch.getLaunchReason());
-                        } else {
-                            Log.v(TAG, "Will kill app before launch");
-                            startApp(launch.getApp(), true, launch.getLaunchReason());
-                        }
-                        sleep(POST_LAUNCH_IDLE_TIMEOUT);
-                    } finally {
-                        // Stop the trace
-                        atraceLogger.atraceStop();
-                    }
-                }
-                closeApp(launch.getApp(), true);
-                sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
-            }
-        } finally {
-            if (null != mBufferedWriter) {
-                mBufferedWriter.close();
-            }
-        }
-
-        for (String app : mNameToResultKey.keySet()) {
-            for (String compilerFilter : mCompilerFilters) {
-                StringBuilder launchTimes = new StringBuilder();
-                StringBuilder cpuCycles = new StringBuilder();
-                StringBuilder majorFaults = new StringBuilder();
-                for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) {
-                    launchTimes.append(result.mLaunchTime);
-                    launchTimes.append(",");
-                    if (mSimplePerfAppOnly) {
-                        cpuCycles.append(result.mCpuCycles);
-                        cpuCycles.append(",");
-                        majorFaults.append(result.mMajorFaults);
-                        majorFaults.append(",");
-                    }
-                }
-                String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter);
-                mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString());
-                if (mSimplePerfAppOnly) {
-                    mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles",
-                        cpuCycles.toString());
-                    mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults",
-                        majorFaults.toString());
-                }
-            }
-        }
-        instrumentation.sendStatus(0, mResult);
-    }
-
-    /**
-     * Compile the app package using compilerFilter and return true or false
-     * based on status of the compilation command.
-     */
-    private boolean compileApp(String compilerFilter, String appPkgName) throws IOException {
-        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
-                executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName));
-                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
-                        new FileInputStream(result.getFileDescriptor())))) {
-            String line;
-            while ((line = bufferedReader.readLine()) != null) {
-                if (line.contains(COMPILE_SUCCESS)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    /**
-     * If launch order is "cyclic" then apps will be launched one after the
-     * other for each iteration count.
-     * If launch order is "sequential" then each app will be launched for given number
-     * iterations at once before launching the other apps.
-     */
-    private void setLaunchOrder() {
-        if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
-            for (String compilerFilter : mCompilerFilters) {
-                if (mTrialLaunch) {
-                    for (String app : mNameToResultKey.keySet()) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
-                    }
-                }
-                for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
-                    for (String app : mNameToResultKey.keySet()) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                  String.format(LAUNCH_ITERATION, launchCount)));
-                    }
-                }
-                if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
-                    for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
-                        for (String app : mNameToResultKey.keySet()) {
-                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                      String.format(TRACE_ITERATION, traceCount)));
-                        }
-                    }
-                }
-            }
-        } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
-            for (String compilerFilter : mCompilerFilters) {
-                for (String app : mNameToResultKey.keySet()) {
-                    if (mTrialLaunch) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
-                    }
-                    for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                String.format(LAUNCH_ITERATION, launchCount)));
-                    }
-                    if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
-                        for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
-                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                    String.format(TRACE_ITERATION, traceCount)));
-                        }
-                    }
-                }
-            }
-        } else {
-            assertTrue("Launch order is not valid parameter", false);
-        }
-    }
-
-    private void dropCache() {
-        if (mDropCache) {
-            assertNotNull("Issue in dropping the cache",
-                    getInstrumentation().getUiAutomation()
-                            .executeShellCommand(DROP_CACHE_SCRIPT));
-        }
-    }
-
-    private void parseArgs(Bundle args) {
-        mNameToResultKey = new LinkedHashMap<String, String>();
-        mNameToLaunchTime = new HashMap<>();
-        String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
-        if (launchIterations != null) {
-            mLaunchIterations = Integer.parseInt(launchIterations);
-        }
-        String appList = args.getString(KEY_APPS);
-        if (appList == null)
-            return;
-
-        String appNames[] = appList.split("\\|");
-        for (String pair : appNames) {
-            String[] parts = pair.split("\\^");
-            if (parts.length != 2) {
-                Log.e(TAG, "The apps key is incorrectly formatted");
-                fail();
-            }
-
-            mNameToResultKey.put(parts[0], parts[1]);
-            mNameToLaunchTime.put(parts[0], null);
-        }
-        String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
-        if (requiredAccounts != null) {
-            mRequiredAccounts = new HashSet<String>();
-            for (String accountType : requiredAccounts.split(",")) {
-                mRequiredAccounts.add(accountType);
-            }
-        }
-
-        String compilerFilterList = args.getString(KEY_COMPILER_FILTERS);
-        if (compilerFilterList != null) {
-            // If a compiler filter is passed, we make a trial launch to force compilation
-            // of the apps.
-            mTrialLaunch = true;
-            mCompilerFilters = compilerFilterList.split("\\|");
-        } else {
-            // Just pass a null compiler filter to use the current state of the app.
-            mCompilerFilters = new String[1];
-        }
-
-        // Pre-populate the results map to avoid null checks.
-        for (String app : mNameToLaunchTime.keySet()) {
-            HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
-            mNameToLaunchTime.put(app, map);
-            for (String compilerFilter : mCompilerFilters) {
-                map.put(compilerFilter, new ArrayList<>());
-            }
-        }
-
-        mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
-        mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
-        mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD);
-        mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
-        mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP));
-        mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
-
-        if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
-            Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
-                KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP));
-        }
-    }
-
-    private boolean hasLeanback(Context context) {
-        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
-    private void createMappings() {
-        mNameToIntent = new LinkedHashMap<String, Intent>();
-
-        PackageManager pm = getInstrumentation().getContext()
-                .getPackageManager();
-        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
-        intentToResolve.addCategory(hasLeanback(getInstrumentation().getContext()) ?
-                Intent.CATEGORY_LEANBACK_LAUNCHER :
-                Intent.CATEGORY_LAUNCHER);
-        List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
-        resolveLoop(ris, intentToResolve, pm);
-        // For Wear
-        intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
-        ris = pm.queryIntentActivities(intentToResolve, 0);
-        resolveLoop(ris, intentToResolve, pm);
-    }
-
-    private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) {
-        if (ris == null || ris.isEmpty()) {
-            Log.i(TAG, "Could not find any apps");
-        } else {
-            for (ResolveInfo ri : ris) {
-                Intent startIntent = new Intent(intentToResolve);
-                startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                startIntent.setClassName(ri.activityInfo.packageName,
-                        ri.activityInfo.name);
-                String appName = ri.loadLabel(pm).toString();
-                if (appName != null) {
-                    // Support launching intent using package name or app name
-                    mNameToIntent.put(ri.activityInfo.packageName, startIntent);
-                    mNameToIntent.put(appName, startIntent);
-                }
-            }
-        }
-    }
-
-    private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
-            String launchReason) throws NameNotFoundException, RemoteException {
-        Log.i(TAG, "Starting " + appName);
-
-        Intent startIntent = mNameToIntent.get(appName);
-        if (startIntent == null) {
-            Log.w(TAG, "App does not exist: " + appName);
-            mResult.putString(mNameToResultKey.get(appName), "App does not exist");
-            return new AppLaunchResult();
-        }
-        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
-                launchReason);
-        Thread t = new Thread(runnable);
-        t.start();
-        try {
-            t.join(JOIN_TIMEOUT);
-        } catch (InterruptedException e) {
-            // ignore
-        }
-        return runnable.getResult();
-    }
-
-    private void checkAccountSignIn() {
-        // ensure that the device has the required account types before starting test
-        // e.g. device must have a valid Google account sign in to measure a meaningful launch time
-        // for Gmail
-        if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
-            return;
-        }
-        final AccountManager am =
-                (AccountManager) getInstrumentation().getTargetContext().getSystemService(
-                        Context.ACCOUNT_SERVICE);
-        Account[] accounts = am.getAccounts();
-        // use set here in case device has multiple accounts of the same type
-        Set<String> foundAccounts = new HashSet<String>();
-        for (Account account : accounts) {
-            if (mRequiredAccounts.contains(account.type)) {
-                foundAccounts.add(account.type);
-            }
-        }
-        // check if account type matches, if not, fail test with message on what account types
-        // are missing
-        if (mRequiredAccounts.size() != foundAccounts.size()) {
-            mRequiredAccounts.removeAll(foundAccounts);
-            StringBuilder sb = new StringBuilder("Device missing these accounts:");
-            for (String account : mRequiredAccounts) {
-                sb.append(' ');
-                sb.append(account);
-            }
-            fail(sb.toString());
-        }
-    }
-
-    private void closeApp(String appName, boolean forceStopApp) {
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
-        homeIntent.addCategory(Intent.CATEGORY_HOME);
-        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-        getInstrumentation().getContext().startActivity(homeIntent);
-        sleep(POST_LAUNCH_IDLE_TIMEOUT);
-        if (forceStopApp) {
-            Intent startIntent = mNameToIntent.get(appName);
-            if (startIntent != null) {
-                String packageName = startIntent.getComponent().getPackageName();
-                try {
-                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Error closing app", e);
-                }
-            }
-        }
-    }
-
-    private void sleep(int time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-            // ignore
-        }
-    }
-
-    private void reportError(String appName, String processName) {
-        ActivityManager am = (ActivityManager) getInstrumentation()
-                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
-        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
-        if (crashes != null) {
-            for (ProcessErrorStateInfo crash : crashes) {
-                if (!crash.processName.equals(processName))
-                    continue;
-
-                Log.w(TAG, appName + " crashed: " + crash.shortMsg);
-                mResult.putString(mNameToResultKey.get(appName), crash.shortMsg);
-                return;
-            }
-        }
-
-        mResult.putString(mNameToResultKey.get(appName),
-                "Crashed for unknown reason");
-        Log.w(TAG, appName
-                + " not found in process list, most likely it is crashed");
-    }
-
-    private class LaunchOrder {
-        private String mApp;
-        private String mCompilerFilter;
-        private String mLaunchReason;
-
-        LaunchOrder(String app, String compilerFilter, String launchReason){
-            mApp = app;
-            mCompilerFilter = compilerFilter;
-            mLaunchReason = launchReason;
-        }
-
-        public String getApp() {
-            return mApp;
-        }
-
-        public void setApp(String app) {
-            mApp = app;
-        }
-
-        public String getCompilerFilter() {
-            return mCompilerFilter;
-        }
-
-        public String getLaunchReason() {
-            return mLaunchReason;
-        }
-
-        public void setLaunchReason(String launchReason) {
-            mLaunchReason = launchReason;
-        }
-    }
-
-    private class AppLaunchResult {
-        long mLaunchTime;
-        long mCpuCycles;
-        long mMajorFaults;
-
-        AppLaunchResult() {
-            mLaunchTime = -1L;
-            mCpuCycles = -1L;
-            mMajorFaults = -1L;
-        }
-
-        AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) {
-            try {
-                mLaunchTime = Long.parseLong(launchTime, 10);
-                mCpuCycles = Long.parseLong(cpuCycles, 10);
-                mMajorFaults = Long.parseLong(majorFaults, 10);
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "Error parsing result", e);
-            }
-        }
-    }
-
-    private class AppLaunchRunnable implements Runnable {
-        private Intent mLaunchIntent;
-        private AppLaunchResult mLaunchResult;
-        private boolean mForceStopBeforeLaunch;
-        private String mLaunchReason;
-
-        public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
-                String launchReason) {
-            mLaunchIntent = intent;
-            mForceStopBeforeLaunch = forceStopBeforeLaunch;
-            mLaunchReason = launchReason;
-            mLaunchResult = new AppLaunchResult();
-        }
-
-        public AppLaunchResult getResult() {
-            return mLaunchResult;
-        }
-
-        public void run() {
-            File launchFile = null;
-            try {
-                String packageName = mLaunchIntent.getComponent().getPackageName();
-                String componentName = mLaunchIntent.getComponent().flattenToShortString();
-                if (mForceStopBeforeLaunch) {
-                    Log.v(TAG, "Stopping app before launch");
-                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
-                } else {
-                    Log.v(TAG, "Not killing app. Going to Home Screen.");
-                    ParcelFileDescriptor goHome = getInstrumentation().getUiAutomation()
-                        .executeShellCommand("input keyevent 3");
-                }
-                String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
-                if (mSimplePerfAppOnly) {
-                    try {
-                        // executeShellCommand cannot handle shell specific actions, like '&'.
-                        // Therefore, we create a file containing the command and make that
-                        // the command to launch.
-                        launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh");
-                        launchFile.setExecutable(true);
-                        try (FileOutputStream stream = new FileOutputStream(launchFile);
-                             BufferedWriter writer =
-                                new BufferedWriter(new OutputStreamWriter(stream))) {
-                            String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd);
-                            writer.write(cmd);
-                        }
-                        launchCmd = launchFile.getAbsolutePath();
-                    } catch (IOException e) {
-                        Log.w(TAG, "Error writing the launch command", e);
-                        return;
-                    }
-                } else if (null != mSimplePerfCmd) {
-                    launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
-                }
-                Log.v(TAG, "Final launch cmd:" + launchCmd);
-                ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
-                        .executeShellCommand(launchCmd);
-                mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format
-                        ("App Launch :%s %s", componentName, mLaunchReason));
-            } catch (RemoteException e) {
-                Log.w(TAG, "Error launching app", e);
-            } finally {
-                if (launchFile != null) {
-                    launchFile.delete();
-                }
-            }
-        }
-
-        /**
-         * Method to parse the launch time info and write the result to file
-         *
-         * @param parcelDesc
-         * @return
-         */
-        private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc,
-                String headerInfo) {
-            String launchTime = "-1";
-            String cpuCycles = "-1";
-            String majorFaults = "-1";
-            boolean launchSuccess = false;
-            try {
-                InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
-                /* SAMPLE OUTPUT :
-                Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
-                Status: ok
-                Activity: com.google.android.calculator/com.android.calculator2.Calculator
-                ThisTime: 357
-                TotalTime: 357
-                WaitTime: 377
-                Complete*/
-                /* WHEN NOT KILLING HOME :
-                Starting: Intent { cmp=com.google.android.wearable.app/
-                    com.google.android.clockwork.home.calendar.AgendaActivity }
-                Warning: Activity not started, its current task has been brought to the front
-                Status: ok
-                Activity: com.google.android.wearable.app/
-                    com.google.android.clockwork.home.calendar.AgendaActivity
-                ThisTime: 209
-                TotalTime: 209
-                WaitTime: 285
-                Complete*/
-                /* WITH SIMPLEPERF :
-                Performance counter statistics,
-                6595722690,cpu-cycles,4.511040,GHz,(100%),
-                0,major-faults,0.000,/sec,(100%),
-                Total test time,1.462129,seconds,*/
-                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
-                        inputStream));
-                String line = null;
-                int lineCount = 1;
-                int addLineForWarning = 0;
-                mBufferedWriter.newLine();
-                mBufferedWriter.write(headerInfo);
-                mBufferedWriter.newLine();
-                while ((line = bufferedReader.readLine()) != null) {
-                    if (lineCount == 2 && line.contains(WARNING_MESSAGE)) {
-                        addLineForWarning = 1;
-                    }
-                    if (lineCount == (2 + addLineForWarning) && line.contains(SUCCESS_MESSAGE)) {
-                        launchSuccess = true;
-                    }
-                    // Parse TotalTime which is the launch time
-                    if (launchSuccess && lineCount == (5 + addLineForWarning)) {
-                        String launchSplit[] = line.split(":");
-                        launchTime = launchSplit[1].trim();
-                    }
-
-                    if (mSimplePerfAppOnly) {
-                        // Parse simpleperf output.
-                        if (lineCount == (9 + addLineForWarning)) {
-                            if (!line.contains("cpu-cycles")) {
-                                Log.e(TAG, "Error in simpleperf output");
-                            } else {
-                                cpuCycles = line.split(",")[0].trim();
-                            }
-                        } else if (lineCount == (10 + addLineForWarning)) {
-                            if (!line.contains("major-faults")) {
-                                Log.e(TAG, "Error in simpleperf output");
-                            } else {
-                                majorFaults = line.split(",")[0].trim();
-                            }
-                        }
-                    }
-                    mBufferedWriter.write(line);
-                    mBufferedWriter.newLine();
-                    lineCount++;
-                }
-                mBufferedWriter.flush();
-                inputStream.close();
-            } catch (IOException e) {
-                Log.w(TAG, "Error writing the launch file", e);
-            }
-            return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
-        }
-
-    }
-}
diff --git a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
index 4ec86b186..56848b8 100644
--- a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
+++ b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
@@ -15,20 +15,19 @@
  */
 package com.android.tests.dataidle;
 
+import static android.net.NetworkStats.METERED_YES;
+
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.content.Context;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
 import android.net.NetworkTemplate;
-import android.net.TrafficStats;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.telephony.TelephonyManager;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
+import java.util.Set;
+
 /**
  * A test that dumps data usage to instrumentation out, used for measuring data usage for idle
  * devices.
@@ -36,7 +35,7 @@
 public class DataIdleTest extends InstrumentationTestCase {
 
     private TelephonyManager mTelephonyManager;
-    private INetworkStatsService mStatsService;
+    private NetworkStatsManager mStatsManager;
 
     private static final String LOG_TAG = "DataIdleTest";
     private final static int INSTRUMENTATION_IN_PROGRESS = 2;
@@ -44,8 +43,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         Context c = getInstrumentation().getTargetContext();
-        mStatsService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        mStatsManager = c.getSystemService(NetworkStatsManager.class);
         mTelephonyManager = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
     }
 
@@ -53,7 +51,9 @@
      * Test that dumps all the data usage metrics for wifi to instrumentation out.
      */
     public void testWifiIdle() {
-        NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
+        final NetworkTemplate template = new NetworkTemplate
+                .Builder(NetworkTemplate.MATCH_WIFI)
+                .build();
         fetchStats(template);
     }
 
@@ -61,8 +61,11 @@
      * Test that dumps all the data usage metrics for all mobile to instrumentation out.
      */
     public void testMobile() {
-        String subscriberId = mTelephonyManager.getSubscriberId();
-        NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+        final String subscriberId = mTelephonyManager.getSubscriberId();
+        NetworkTemplate template = new NetworkTemplate
+                .Builder(NetworkTemplate.MATCH_MOBILE)
+                .setMeteredness(METERED_YES)
+                .setSubscriberIds(Set.of(subscriberId)).build();
         fetchStats(template);
     }
 
@@ -72,49 +75,26 @@
      * @param template {@link NetworkTemplate} to match.
      */
     private void fetchStats(NetworkTemplate template) {
-        INetworkStatsSession session = null;
         try {
-            mStatsService.forceUpdate();
-            session = mStatsService.openSession();
-            final NetworkStats stats = session.getSummaryForAllUid(
-                    template, Long.MIN_VALUE, Long.MAX_VALUE, false);
-            reportStats(stats);
-        } catch (RemoteException e) {
+            mStatsManager.forceUpdate();
+            final NetworkStats.Bucket bucket =
+                    mStatsManager.querySummaryForDevice(template, Long.MIN_VALUE, Long.MAX_VALUE);
+            reportStats(bucket);
+        } catch (RuntimeException e) {
             Log.w(LOG_TAG, "Failed to fetch network stats.");
-        } finally {
-            TrafficStats.closeQuietly(session);
         }
     }
 
     /**
      * Print network data usage stats to instrumentation out
-     * @param stats {@link NetworkorStats} to print
+     * @param bucket {@link NetworkStats} to print
      */
-    void reportStats(NetworkStats stats) {
+    void reportStats(NetworkStats.Bucket bucket) {
         Bundle result = new Bundle();
-        long rxBytes = 0;
-        long txBytes = 0;
-        long rxPackets = 0;
-        long txPackets = 0;
-        for (int i = 0; i < stats.size(); ++i) {
-            // Label will be iface_uid_tag_set
-            Entry  statsEntry = stats.getValues(i, null);
-            // Debugging use.
-            /*
-            String labelTemplate = String.format("%s_%d_%d_%d", statsEntry.iface, statsEntry.uid,
-                    statsEntry.tag, statsEntry.set) + "_%s";
-            result.putLong(String.format(labelTemplate, "rxBytes"), statsEntry.rxBytes);
-            result.putLong(String.format(labelTemplate, "txBytes"), statsEntry.txBytes);
-            */
-            rxPackets += statsEntry.rxPackets;
-            rxBytes += statsEntry.rxBytes;
-            txPackets += statsEntry.txPackets;
-            txBytes += statsEntry.txBytes;
-        }
-        result.putLong("Total rx Bytes", rxBytes);
-        result.putLong("Total tx Bytes", txBytes);
-        result.putLong("Total rx Packets", rxPackets);
-        result.putLong("Total tx Packets", txPackets);
+        result.putLong("Total rx Bytes", bucket.getRxBytes());
+        result.putLong("Total tx Bytes", bucket.getTxBytes());
+        result.putLong("Total rx Packets", bucket.getRxPackets());
+        result.putLong("Total tx Packets", bucket.getTxPackets());
         getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, result);
 
     }
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index 6985702..9a88abd 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -81,7 +81,8 @@
         intent.setPackage(getPackageName());
         IntentFilter filter = new IntentFilter();
         filter.addAction(intent.getAction());
-        registerReceiver(mAlarmReceiver, filter);
+        registerReceiver(mAlarmReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 22acc03..d960e94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -108,6 +108,15 @@
         super.entireScreenCovered()
     }
 
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        // This test doesn't work in shell transitions because of b/215885246
+        assumeFalse(isShellTransitionsEnabled)
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
new file mode 100644
index 0000000..f7c0a87
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 0b1748a..535612a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -17,7 +17,9 @@
 package com.android.server.wm.flicker.helpers
 
 import android.app.Instrumentation
+import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.parser.toFlickerComponent
@@ -47,4 +49,28 @@
         }
         launcherStrategy.launch(appName, expectedPackage)
     }
+
+    fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
+        val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
+                "start_dialog_themed_activity_btn")), FIND_TIMEOUT)
+
+        require(button != null) {
+            "Button not found, this usually happens when the device " +
+                    "was left in an unknown state (e.g. Screen turned off)"
+        }
+        button.click()
+        wmHelper.waitForAppTransitionIdle()
+        wmHelper.waitForFullScreenApp(
+                ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
+    }
+    fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+        val dialog = uiDevice.wait(
+                Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
+
+        // Pressing back key to dismiss the dialog
+        if (dialog != null) {
+            uiDevice.pressBack()
+            wmHelper.waitForAppTransitionIdle()
+        }
+    }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 56879c9..c87d8e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -31,7 +31,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
@@ -39,7 +38,6 @@
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -157,11 +155,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c28466c..815ea77 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,12 +34,10 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -117,11 +115,7 @@
 
     @Presubmit
     @Test
-    fun entireScreenCovered() {
-        // This test doesn't work in shell transitions because of b/206086894
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.entireScreenCovered()
-    }
+    fun entireScreenCovered() = testSpec.entireScreenCovered()
 
     @Presubmit
     @Test
@@ -159,11 +153,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
@@ -188,7 +178,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(repetitions = 1,
+                .getConfigNonRotationTests(repetitions = 5,
                     // b/190352379 (IME doesn't show on app launch in 90 degrees)
                     supportedRotations = listOf(Surface.ROTATION_0),
                     supportedNavigationModes = listOf(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index c7f1b99..24b1598 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,7 +32,6 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -134,11 +133,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 46ed0ad..e5d82a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,11 +34,9 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -126,11 +124,7 @@
 
     @Presubmit
     @Test
-    fun entireScreenCovered() {
-        // This test doesn't work in shell transitions because of b/206086894
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.entireScreenCovered()
-    }
+    fun entireScreenCovered() = testSpec.entireScreenCovered()
 
     @Presubmit
     @Test
@@ -152,11 +146,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
new file mode 100644
index 0000000..429541c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
+ * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
[email protected](FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            setup {
+                eachRun {
+                    testApp.launchViaIntent(wmHelper)
+                    wmHelper.waitImeShown()
+                    testApp.startDialogThemedActivity(wmHelper)
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                }
+            }
+            transitions {
+                testApp.dismissDialog(wmHelper)
+            }
+        }
+    }
+
+    /**
+     * Checks that [FlickerComponentName.IME] layer becomes visible during the transition
+     */
+    @FlakyTest(bugId = 215884488)
+    @Test
+    fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
+
+    /**
+     * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
+     */
+    @Presubmit
+    @Test
+    fun imeLayerExistsEnd() {
+        testSpec.assertLayersEnd {
+            this.isVisible(FlickerComponentName.IME)
+        }
+    }
+
+    /**
+     * Checks that [FlickerComponentName.IME_SNAPSHOT] layer is invisible always.
+     */
+    @Presubmit
+    @Test
+    fun imeSnapshotNotVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(FlickerComponentName.IME_SNAPSHOT)
+        }
+    }
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+         * repetitions, screen orientation and navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance()
+                    .getConfigNonRotationTests(
+                            repetitions = 3,
+                            supportedRotations = listOf(Surface.ROTATION_0),
+                            supportedNavigationModes = listOf(
+                                    WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                                    WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+                            )
+                    )
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
new file mode 100644
index 0000000..301fafa
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
@@ -0,0 +1,2 @@
+# ime
+# Bug component: 34867
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index ebe4be2..87f8ef2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -34,11 +34,9 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -130,11 +128,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index f6e5adc..5e06f11 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -18,8 +18,8 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import android.view.Surface
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -41,7 +41,9 @@
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.ConditionList
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
 import org.junit.FixMethodOrder
@@ -63,6 +65,12 @@
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
 
+    private val waitConditionSetup = ConditionList(listOf(
+        WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+        WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+        WindowManagerConditionsFactory.isHomeActivityVisible()
+    ))
+
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
@@ -73,8 +81,7 @@
                 }
                 eachRun {
                     device.pressRecentApps()
-                    wmHelper.waitImeGone()
-                    wmHelper.waitForAppTransitionIdle()
+                    wmHelper.waitFor(waitConditionSetup)
                     this.setRotation(testSpec.startRotation)
                 }
             }
@@ -101,8 +108,6 @@
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-        // This test doesn't work in shell transitions because of b/204570898
-        assumeFalse(isShellTransitionsEnabled)
         val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertWm {
             this.visibleWindowsShownMoreThanOneConsecutiveEntry(
@@ -116,8 +121,6 @@
     @Presubmit
     @Test
     fun launcherWindowBecomesInvisible() {
-        // This test doesn't work in shell transitions because of b/204574221
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.isAppWindowVisible(LAUNCHER_COMPONENT)
                     .then()
@@ -127,11 +130,7 @@
 
     @Presubmit
     @Test
-    fun imeWindowIsAlwaysVisible() {
-        // This test doesn't work in shell transitions because of b/204570898
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
-    }
+    fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
 
     @Presubmit
     @Test
@@ -202,8 +201,6 @@
     @Presubmit
     @Test
     fun appLayerReplacesLauncher() {
-        // This test doesn't work in shell transitions because of b/204574221
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.isVisible(LAUNCHER_COMPONENT)
                 .then()
@@ -219,11 +216,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
@@ -245,11 +238,8 @@
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
                 .getConfigNonRotationTests(
-                    repetitions = 1,
-                    supportedRotations = listOf(Surface.ROTATION_0),
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
+                    repetitions = 5,
+                    supportedRotations = listOf(Surface.ROTATION_0)
                 )
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
new file mode 100644
index 0000000..2c414a2
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
@@ -0,0 +1,4 @@
+# System UI > ... > Overview (recent apps) > UI
+# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview*
+# window manager > animations/transitions
+# Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index eef7b57..5450610 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -99,11 +99,7 @@
     /** {@inheritDoc} */
     @Presubmit
     @Test
-    override fun appLayerReplacesLauncher() {
-        // This test doesn't work in shell transitions because of b/206094140
-        assumeFalse(isShellTransitionsEnabled)
-        super.appLayerReplacesLauncher()
-    }
+    override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
 
     /** {@inheritDoc} */
     @Presubmit
@@ -126,6 +122,11 @@
     @Test
     override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
 
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 213852103)
+    @Test
+    override fun entireScreenCovered() = super.entireScreenCovered()
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 6e5c600..a85dcc5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -127,7 +127,7 @@
      * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging,
      * the window may be visible or not depending on what was processed until that moment.
      */
-    @Presubmit
+    @FlakyTest(bugId = 203538234)
     @Test
     fun appWindowBecomesVisible() {
         testSpec.assertWm {
@@ -240,7 +240,7 @@
      * it cannot use the regular assertion (check over time), because on lock screen neither
      * the app not the launcher are visible, and there is no top visible window.
      */
-    @Presubmit
+    @FlakyTest(bugId = 203538234)
     @Test
     override fun appWindowReplacesLauncherAsTopWindow() {
         testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dfa8f8e..cd209b2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -96,20 +96,14 @@
     /** {@inheritDoc} */
     @Presubmit
     @Test
-    override fun appWindowReplacesLauncherAsTopWindow() {
-        // This test doesn't work in shell transitions because of b/206094140
-        assumeFalse(isShellTransitionsEnabled)
-        super.appWindowReplacesLauncherAsTopWindow()
-    }
+    override fun appWindowReplacesLauncherAsTopWindow() =
+            super.appWindowReplacesLauncherAsTopWindow()
 
     /** {@inheritDoc} */
     @Presubmit
     @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
-        // This test doesn't work in shell transitions because of b/206094140
-        assumeFalse(isShellTransitionsEnabled)
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-    }
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     /** {@inheritDoc} */
     @FlakyTest
@@ -119,11 +113,7 @@
     /** {@inheritDoc} */
     @Presubmit
     @Test
-    override fun appLayerReplacesLauncher() {
-        // This test doesn't work in shell transitions because of b/206094140
-        assumeFalse(isShellTransitionsEnabled)
-        super.appLayerReplacesLauncher()
-    }
+    override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
 
     /** {@inheritDoc} */
     @Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
new file mode 100644
index 0000000..897fe5d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
@@ -0,0 +1,2 @@
+# System UI > ... > Launcher > Gesture nav
+# Bug component: 565144
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index dcb5c86..8f2803e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -261,7 +261,7 @@
         testSpec.assertWm {
             this.isAppWindowOnTop(LAUNCHER_COMPONENT)
                     .then()
-                    .isAppWindowVisible(FlickerComponentName.SNAPSHOT)
+                    .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
                     .then()
                     .isAppWindowVisible(testApp.component)
         }
@@ -342,4 +342,4 @@
                     )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 0879b98..3f0de7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,13 +25,11 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -167,11 +165,7 @@
      */
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     /** {@inheritDoc} */
     @FlakyTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
new file mode 100644
index 0000000..f7c0a87
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index e44bee6..3ae484b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -26,10 +26,8 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -102,8 +100,6 @@
     @Presubmit
     @Test
     fun appWindowFullScreen() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.invoke("isFullScreen") {
                 val appWindow = it.windowState(testApp.`package`)
@@ -139,8 +135,6 @@
     @Presubmit
     @Test
     fun appLayerAlwaysVisible() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             isVisible(testApp.component)
         }
@@ -152,8 +146,6 @@
     @Presubmit
     @Test
     fun appLayerRotates() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.invoke("entireScreenCovered") { entry ->
                 entry.entry.displays.map { display ->
@@ -193,8 +185,6 @@
     @Presubmit
     @Test
     fun focusDoesNotChange() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertEventLog {
             this.focusDoesNotChange()
         }
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index cb37fc7..9e371e5 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -23,6 +23,7 @@
          android:supportsRtl="true">
         <activity android:name=".SimpleActivity"
              android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+             android:theme="@style/CutoutShortEdges"
              android:label="SimpleApp"
              android:exported="true">
             <intent-filter>
@@ -32,6 +33,7 @@
         </activity>
         <activity android:name=".ImeActivity"
              android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+             android:theme="@style/CutoutShortEdges"
              android:label="ImeApp"
              android:exported="true">
             <intent-filter>
@@ -40,6 +42,7 @@
             </intent-filter>
         </activity>
         <activity android:name=".ImeActivityAutoFocus"
+             android:theme="@style/CutoutShortEdges"
              android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
              android:windowSoftInputMode="stateVisible"
              android:label="ImeAppAutoFocus"
@@ -51,6 +54,7 @@
         </activity>
         <activity android:name=".SeamlessRotationActivity"
              android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+             android:theme="@style/CutoutShortEdges"
              android:configChanges="orientation|screenSize"
              android:label="SeamlessApp"
              android:exported="true">
@@ -60,6 +64,7 @@
             </intent-filter>
         </activity>
         <activity android:name=".NonResizeableActivity"
+            android:theme="@style/CutoutShortEdges"
             android:resizeableActivity="false"
             android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
             android:label="NonResizeableApp"
@@ -72,6 +77,7 @@
         </activity>
         <activity android:name=".ButtonActivity"
             android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
+            android:theme="@style/CutoutShortEdges"
             android:configChanges="orientation|screenSize"
             android:label="ButtonActivity"
             android:exported="true">
@@ -82,6 +88,7 @@
         </activity>
         <activity android:name=".LaunchNewTaskActivity"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity"
+                  android:theme="@style/CutoutShortEdges"
                   android:configChanges="orientation|screenSize"
                   android:label="LaunchNewTaskActivity"
                   android:exported="true">
@@ -90,5 +97,16 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".DialogThemedActivity"
+            android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
+            android:configChanges="orientation|screenSize"
+            android:theme="@style/DialogTheme"
+            android:label="DialogThemedActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 2620ff4..baaf707 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -31,4 +31,9 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="Finish activity" />
+    <Button
+        android:id="@+id/start_dialog_themed_activity_btn"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="Start dialog themed activity" />
 </LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 0000000..746b0f4c
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <style name="CutoutDefault">
+        <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+    </style>
+
+    <style name="CutoutShortEdges">
+        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+    </style>
+
+    <style name="CutoutNever">
+        <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+    </style>
+
+    <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@null</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowSoftInputMode">stateUnchanged</item>
+    </style>
+</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index baf36ab..13adb68 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -56,4 +56,8 @@
     public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
             new ComponentName(FLICKER_APP_PACKAGE,
                     FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+    public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
+    public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
+            new ComponentName(FLICKER_APP_PACKAGE,
+                    FLICKER_APP_PACKAGE + ".DialogThemedActivity");
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
new file mode 100644
index 0000000..27606d8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class DialogThemedActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_simple);
+        getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
+        TextView textView = new TextView(this);
+        textView.setText("This is a test dialog");
+        textView.setTextColor(Color.BLACK);
+        LinearLayout layout = new LinearLayout(this);
+        layout.setBackgroundColor(Color.GREEN);
+        layout.addView(textView);
+
+        // Create a dialog with dialog-themed activity
+        AlertDialog dialog = new AlertDialog.Builder(this)
+                .setView(layout)
+                .setTitle("Dialog for test")
+                .create();
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(MATCH_PARENT,
+                MATCH_PARENT);
+        attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM;
+        dialog.getWindow().getDecorView().setLayoutParams(attrs);
+        dialog.setCanceledOnTouchOutside(true);
+        dialog.show();
+        dialog.setOnDismissListener((d) -> finish());
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index 05da717..bb200f1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm.flicker.testapp;
 
+import android.content.Intent;
+import android.widget.Button;
 import android.widget.EditText;
 
 public class ImeActivityAutoFocus extends ImeActivity {
@@ -26,5 +28,9 @@
 
         EditText editTextField = findViewById(R.id.plain_text_input);
         editTextField.requestFocus();
+
+        Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
+        startThemedActivityButton.setOnClickListener(
+                button -> startActivity(new Intent(this, DialogThemedActivity.class)));
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 46d3a3d..65d168b 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -84,7 +84,7 @@
             mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
 
             mRuntimeShader = new RuntimeShader(sSkSL, false);
-            mRuntimeShader.setUniform("param1", mShaderParam1);
+            mRuntimeShader.setFloatUniform("param1", mShaderParam1);
             mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1,
                                                                            Shader.TileMode.CLAMP,
                                                                            Shader.TileMode.CLAMP));
@@ -177,7 +177,7 @@
 
         public void setShaderParam1(float value) {
             mShaderParam1 = value;
-            mRuntimeShader.setUniform("param1", mShaderParam1);
+            mRuntimeShader.setFloatUniform("param1", mShaderParam1);
             invalidate();
         }
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
index 83e2de9..73c4b8a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -110,7 +110,7 @@
             mPaint = CanvasProperty.createPaint(p);
 
             mRuntimeShader = new RuntimeShader(sSkSL, false);
-            mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS);
+            mRuntimeShader.setFloatUniform("in_maxRadius", MAX_RADIUS);
         }
 
         @Override
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
index fcdee63..12e338c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
@@ -357,18 +357,18 @@
         float uScrollX = mScrollX;
         float uScrollY = mScrollY;
 
-        mRuntimeShader.setUniform("uMaxStretchIntensity", mMaxStretchIntensity);
-        mRuntimeShader.setUniform("uStretchAffectedDist", mStretchAffectedDistance);
-        mRuntimeShader.setUniform("uDistanceStretchedX", distanceStretchedX);
-        mRuntimeShader.setUniform("uDistanceStretchedY", distanceStretchedY);
-        mRuntimeShader.setUniform("uDistDiffX", diffX);
-        mRuntimeShader.setUniform("uDistDiffY", diffY);
-        mRuntimeShader.setUniform("uOverscrollX", normOverScrollDistX);
-        mRuntimeShader.setUniform("uOverscrollY", normOverScrollDistY);
-        mRuntimeShader.setUniform("uScrollX", uScrollX);
-        mRuntimeShader.setUniform("uScrollY", uScrollY);
-        mRuntimeShader.setUniform("viewportWidth", width);
-        mRuntimeShader.setUniform("viewportHeight", height);
+        mRuntimeShader.setFloatUniform("uMaxStretchIntensity", mMaxStretchIntensity);
+        mRuntimeShader.setFloatUniform("uStretchAffectedDist", mStretchAffectedDistance);
+        mRuntimeShader.setFloatUniform("uDistanceStretchedX", distanceStretchedX);
+        mRuntimeShader.setFloatUniform("uDistanceStretchedY", distanceStretchedY);
+        mRuntimeShader.setFloatUniform("uDistDiffX", diffX);
+        mRuntimeShader.setFloatUniform("uDistDiffY", diffY);
+        mRuntimeShader.setFloatUniform("uOverscrollX", normOverScrollDistX);
+        mRuntimeShader.setFloatUniform("uOverscrollY", normOverScrollDistY);
+        mRuntimeShader.setFloatUniform("uScrollX", uScrollX);
+        mRuntimeShader.setFloatUniform("uScrollY", uScrollY);
+        mRuntimeShader.setFloatUniform("viewportWidth", width);
+        mRuntimeShader.setFloatUniform("viewportHeight", height);
 
         mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
 
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
deleted file mode 100644
index 476be44..0000000
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.vcn;
-
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class VcnCellUnderlyingNetworkPriorityTest {
-    private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
-    private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
-
-    // Package private for use in VcnGatewayConnectionConfigTest
-    static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() {
-        return new VcnCellUnderlyingNetworkPriority.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
-                .setAllowMetered(true /* allowMetered */)
-                .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
-                .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
-                .setAllowRoaming(true /* allowRoaming */)
-                .setRequireOpportunistic(true /* requireOpportunistic */)
-                .build();
-    }
-
-    @Test
-    public void testBuilderAndGetters() {
-        final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
-        assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
-        assertTrue(networkPriority.allowMetered());
-        assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
-        assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
-        assertTrue(networkPriority.allowRoaming());
-        assertTrue(networkPriority.requireOpportunistic());
-    }
-
-    @Test
-    public void testBuilderAndGettersForDefaultValues() {
-        final VcnCellUnderlyingNetworkPriority networkPriority =
-                new VcnCellUnderlyingNetworkPriority.Builder().build();
-        assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
-        assertFalse(networkPriority.allowMetered());
-        assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
-        assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
-        assertFalse(networkPriority.allowRoaming());
-        assertFalse(networkPriority.requireOpportunistic());
-    }
-
-    @Test
-    public void testPersistableBundle() {
-        final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
-        assertEquals(
-                networkPriority,
-                VcnUnderlyingNetworkPriority.fromPersistableBundle(
-                        networkPriority.toPersistableBundle()));
-    }
-}
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
new file mode 100644
index 0000000..2fbcf9d
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
+    private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
+    private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
+
+    // Package private for use in VcnGatewayConnectionConfigTest
+    static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
+        return new VcnCellUnderlyingNetworkTemplate.Builder()
+                .setMetered(MATCH_FORBIDDEN)
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+                .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
+                .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+                .setRoaming(MATCH_FORBIDDEN)
+                .setOpportunistic(MATCH_REQUIRED)
+                .build();
+    }
+
+    @Test
+    public void testBuilderAndGetters() {
+        final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+        assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitDownstreamBandwidthKbps());
+        assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
+        assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
+        assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
+        assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
+    }
+
+    @Test
+    public void testBuilderAndGettersForDefaultValues() {
+        final VcnCellUnderlyingNetworkTemplate networkPriority =
+                new VcnCellUnderlyingNetworkTemplate.Builder().build();
+        assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+        // Explicitly expect 0, as documented in Javadoc on setter methods.
+        assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
+        assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
+        assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
+        assertEquals(MATCH_ANY, networkPriority.getRoaming());
+        assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
+    }
+
+    @Test
+    public void testBuilderRequiresStricterEntryCriteria() {
+        try {
+            new VcnCellUnderlyingNetworkTemplate.Builder()
+                    .setMinUpstreamBandwidthKbps(
+                            TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            new VcnCellUnderlyingNetworkTemplate.Builder()
+                    .setMinDownstreamBandwidthKbps(
+                            TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testPersistableBundle() {
+        final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+        assertEquals(
+                networkPriority,
+                VcnUnderlyingNetworkTemplate.fromPersistableBundle(
+                        networkPriority.toPersistableBundle()));
+    }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 377f526..2aef9ae 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,8 +17,8 @@
 package android.net.vcn;
 
 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES;
-import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
+import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -40,8 +40,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
@@ -54,17 +55,17 @@
             };
     public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
 
-    private static final LinkedHashSet<VcnUnderlyingNetworkPriority> UNDERLYING_NETWORK_PRIORITIES =
-            new LinkedHashSet();
+    private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES =
+            new ArrayList();
 
     static {
         Arrays.sort(EXPOSED_CAPS);
         Arrays.sort(UNDERLYING_CAPS);
 
-        UNDERLYING_NETWORK_PRIORITIES.add(
-                VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
-        UNDERLYING_NETWORK_PRIORITIES.add(
-                VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+        UNDERLYING_NETWORK_TEMPLATES.add(
+                VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+        UNDERLYING_NETWORK_TEMPLATES.add(
+                VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
     }
 
     public static final long[] RETRY_INTERVALS_MS =
@@ -95,7 +96,7 @@
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
         final VcnGatewayConnectionConfig.Builder builder =
-                newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+                newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
 
         return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
     }
@@ -174,10 +175,10 @@
     }
 
     @Test
-    public void testBuilderRequiresNonNullNetworkPriorities() {
+    public void testBuilderRequiresNonNullNetworkTemplates() {
         try {
             newBuilder().setVcnUnderlyingNetworkPriorities(null);
-            fail("Expected exception due to invalid underlyingNetworkPriorities");
+            fail("Expected exception due to invalid underlyingNetworkTemplates");
         } catch (NullPointerException e) {
         }
     }
@@ -219,7 +220,7 @@
         Arrays.sort(exposedCaps);
         assertArrayEquals(EXPOSED_CAPS, exposedCaps);
 
-        assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+        assertEquals(UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
         assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
 
         assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
@@ -234,13 +235,13 @@
     }
 
     @Test
-    public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() {
+    public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
         PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
-        configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null);
+        configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
 
         final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
         assertEquals(
-                DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+                DEFAULT_UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
     }
 
     private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
@@ -285,39 +286,36 @@
         assertNotEquals(config, anotherConfig);
     }
 
-    private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
-            LinkedHashSet<VcnUnderlyingNetworkPriority> networkPriorities) {
+    private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkTemplates(
+            List<VcnUnderlyingNetworkTemplate> networkTemplates) {
         return buildTestConfigWithExposedCaps(
                 new VcnGatewayConnectionConfig.Builder(
-                                "buildTestConfigWithVcnUnderlyingNetworkPriorities",
+                                "buildTestConfigWithVcnUnderlyingNetworkTemplates",
                                 TUNNEL_CONNECTION_PARAMS)
-                        .setVcnUnderlyingNetworkPriorities(networkPriorities),
+                        .setVcnUnderlyingNetworkPriorities(networkTemplates),
                 EXPOSED_CAPS);
     }
 
     @Test
-    public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception {
+    public void testVcnUnderlyingNetworkTemplatesEquality() throws Exception {
         final VcnGatewayConnectionConfig config =
-                buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+                buildTestConfigWithVcnUnderlyingNetworkTemplates(UNDERLYING_NETWORK_TEMPLATES);
 
-        final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesEqual =
-                new LinkedHashSet();
-        networkPrioritiesEqual.add(VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
-        networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+        final List<VcnUnderlyingNetworkTemplate> networkTemplatesEqual = new ArrayList();
+        networkTemplatesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+        networkTemplatesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
         final VcnGatewayConnectionConfig configEqual =
-                buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
+                buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesEqual);
 
-        final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesNotEqual =
-                new LinkedHashSet();
-        networkPrioritiesNotEqual.add(
-                VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+        final List<VcnUnderlyingNetworkTemplate> networkTemplatesNotEqual = new ArrayList();
+        networkTemplatesNotEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
         final VcnGatewayConnectionConfig configNotEqual =
-                buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
+                buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesNotEqual);
 
-        assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual);
+        assertEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesEqual);
         assertEquals(config, configEqual);
 
-        assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual);
+        assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
         assertNotEquals(config, configNotEqual);
     }
 }
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
new file mode 100644
index 0000000..399e136
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+public class VcnUnderlyingNetworkTemplateTestBase {
+    // Public for use in NetworkPriorityClassifierTest
+    public static final int TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS = 200;
+    public static final int TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS = 100;
+    public static final int TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS = 400;
+    public static final int TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS = 300;
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
deleted file mode 100644
index dd272cb..0000000
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.vcn;
-
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import org.junit.Test;
-
-public class VcnWifiUnderlyingNetworkPriorityTest {
-    private static final String SSID = "TestWifi";
-    private static final int INVALID_NETWORK_QUALITY = -1;
-
-    // Package private for use in VcnGatewayConnectionConfigTest
-    static VcnWifiUnderlyingNetworkPriority getTestNetworkPriority() {
-        return new VcnWifiUnderlyingNetworkPriority.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
-                .setAllowMetered(true /* allowMetered */)
-                .setSsid(SSID)
-                .build();
-    }
-
-    @Test
-    public void testBuilderAndGetters() {
-        final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
-        assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
-        assertTrue(networkPriority.allowMetered());
-        assertEquals(SSID, networkPriority.getSsid());
-    }
-
-    @Test
-    public void testBuilderAndGettersForDefaultValues() {
-        final VcnWifiUnderlyingNetworkPriority networkPriority =
-                new VcnWifiUnderlyingNetworkPriority.Builder().build();
-        assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
-        assertFalse(networkPriority.allowMetered());
-        assertNull(SSID, networkPriority.getSsid());
-    }
-
-    @Test
-    public void testBuildWithInvalidNetworkQuality() {
-        try {
-            new VcnWifiUnderlyingNetworkPriority.Builder()
-                    .setNetworkQuality(INVALID_NETWORK_QUALITY);
-            fail("Expected to fail due to the invalid network quality");
-        } catch (Exception expected) {
-        }
-    }
-
-    @Test
-    public void testPersistableBundle() {
-        final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
-        assertEquals(
-                networkPriority,
-                VcnUnderlyingNetworkPriority.fromPersistableBundle(
-                        networkPriority.toPersistableBundle()));
-    }
-}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
new file mode 100644
index 0000000..4063178
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
+    private static final String SSID = "TestWifi";
+
+    // Package private for use in VcnGatewayConnectionConfigTest
+    static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
+        return new VcnWifiUnderlyingNetworkTemplate.Builder()
+                .setMetered(MATCH_FORBIDDEN)
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+                .setSsids(Set.of(SSID))
+                .build();
+    }
+
+    @Test
+    public void testBuilderAndGetters() {
+        final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+        assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitDownstreamBandwidthKbps());
+        assertEquals(Set.of(SSID), networkPriority.getSsids());
+    }
+
+    @Test
+    public void testBuilderAndGettersForDefaultValues() {
+        final VcnWifiUnderlyingNetworkTemplate networkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder().build();
+        assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+        // Explicitly expect 0, as documented in Javadoc on setter methods..
+        assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
+        assertTrue(networkPriority.getSsids().isEmpty());
+    }
+
+    @Test
+    public void testBuilderRequiresStricterEntryCriteria() {
+        try {
+            new VcnWifiUnderlyingNetworkTemplate.Builder()
+                    .setMinUpstreamBandwidthKbps(
+                            TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            new VcnWifiUnderlyingNetworkTemplate.Builder()
+                    .setMinDownstreamBandwidthKbps(
+                            TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testPersistableBundle() {
+        final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+        assertEquals(
+                networkPriority,
+                VcnUnderlyingNetworkTemplate.fromPersistableBundle(
+                        networkPriority.toPersistableBundle()));
+    }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 7c7dc4d..bb98bc0 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,7 +23,6 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
 
@@ -264,6 +263,7 @@
     @Test
     public void testSystemReady() throws Exception {
         mVcnMgmtSvc.systemReady();
+        mTestLooper.dispatchAll();
 
         verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
         verify(mSubscriptionTracker).register();
@@ -475,10 +475,8 @@
         mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
         triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
-
-        // Verify teardown after delay
-        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
         mTestLooper.dispatchAll();
+
         verify(vcn).teardownAsynchronously();
         verify(mMockPolicyListener).onPolicyChanged();
     }
@@ -504,92 +502,6 @@
         assertEquals(0, mVcnMgmtSvc.getAllVcns().size());
     }
 
-    /**
-     * Tests an intermediate state where carrier privileges are marked as lost before active data
-     * subId changes during a SIM ejection.
-     *
-     * <p>The expected outcome is that the VCN is torn down after a delay, as opposed to
-     * immediately.
-     */
-    @Test
-    public void testTelephonyNetworkTrackerCallbackLostCarrierPrivilegesBeforeActiveDataSubChanges()
-            throws Exception {
-        setupActiveSubscription(TEST_UUID_2);
-
-        final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
-        final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
-
-        // Simulate privileges lost
-        triggerSubscriptionTrackerCbAndGetSnapshot(
-                TEST_SUBSCRIPTION_ID,
-                TEST_UUID_2,
-                Collections.emptySet(),
-                Collections.emptyMap(),
-                false /* hasCarrierPrivileges */);
-
-        // Verify teardown after delay
-        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
-        mTestLooper.dispatchAll();
-        verify(vcn).teardownAsynchronously();
-    }
-
-    @Test
-    public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances()
-            throws Exception {
-        setupActiveSubscription(TEST_UUID_2);
-
-        final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
-        final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
-
-        // Simulate SIM unloaded
-        triggerSubscriptionTrackerCbAndGetSnapshot(
-                INVALID_SUBSCRIPTION_ID,
-                null /* activeDataSubscriptionGroup */,
-                Collections.emptySet(),
-                Collections.emptyMap(),
-                false /* hasCarrierPrivileges */);
-
-        // Simulate new SIM loaded right during teardown delay.
-        mTestLooper.moveTimeForward(
-                VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
-        mTestLooper.dispatchAll();
-        triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
-
-        // Verify that even after the full timeout duration, the VCN instance is not torn down
-        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
-        mTestLooper.dispatchAll();
-        verify(vcn, never()).teardownAsynchronously();
-    }
-
-    @Test
-    public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception {
-        setupActiveSubscription(TEST_UUID_2);
-
-        final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
-        final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
-
-        // Simulate SIM unloaded
-        triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
-
-        // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
-        // vcnInstance.
-        mTestLooper.moveTimeForward(
-                VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
-        mTestLooper.dispatchAll();
-        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
-        triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
-        final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
-
-        // Verify that new instance was different, and the old one was torn down
-        assertTrue(oldInstance != newInstance);
-        verify(oldInstance).teardownAsynchronously();
-
-        // Verify that even after the full timeout duration, the new VCN instance is not torn down
-        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
-        mTestLooper.dispatchAll();
-        verify(newInstance, never()).teardownAsynchronously();
-    }
-
     @Test
     public void testPackageChangeListenerRegistered() throws Exception {
         verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
@@ -925,6 +837,8 @@
     private void setupSubscriptionAndStartVcn(
             int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
         mVcnMgmtSvc.systemReady();
+        mTestLooper.dispatchAll();
+
         triggerSubscriptionTrackerCbAndGetSnapshot(
                 subGrp,
                 Collections.singleton(subGrp),
@@ -1020,6 +934,7 @@
 
     private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
         mVcnMgmtSvc.systemReady();
+        mTestLooper.dispatchAll();
 
         final ArgumentCaptor<NetworkCallback> captor =
                 ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1264,15 +1179,14 @@
                 true /* isActive */,
                 true /* hasCarrierPrivileges */);
 
-        // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
-        // timeout so the VCN goes inactive.
+        // VCN is currently active. Lose carrier privileges for TEST_PACKAGE so the VCN goes
+        // inactive.
         final TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(
                         TEST_UUID_1,
                         Collections.singleton(TEST_UUID_1),
                         Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
                         false /* hasCarrierPrivileges */);
-        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
         mTestLooper.dispatchAll();
 
         // Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 1f0df62..978bf3e 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -22,6 +22,7 @@
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
 
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -34,8 +35,10 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
@@ -57,6 +60,8 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
@@ -83,7 +88,7 @@
     private static final String PACKAGE_NAME =
             TelephonySubscriptionTrackerTest.class.getPackage().getName();
     private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
-    private static final int TEST_SIM_SLOT_INDEX = 1;
+    private static final int TEST_SIM_SLOT_INDEX = 0;
     private static final int TEST_SUBSCRIPTION_ID_1 = 2;
     private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
     private static final int TEST_SUBSCRIPTION_ID_2 = 3;
@@ -151,6 +156,8 @@
 
     @Before
     public void setUp() throws Exception {
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
         mCallback = mock(TelephonySubscriptionTrackerCallback.class);
         mTelephonySubscriptionTracker =
                 new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
@@ -180,6 +187,15 @@
         return captor.getValue();
     }
 
+    private List<CarrierPrivilegesListener> getCarrierPrivilegesListeners() {
+        final ArgumentCaptor<CarrierPrivilegesListener> captor =
+                ArgumentCaptor.forClass(CarrierPrivilegesListener.class);
+        verify(mTelephonyManager, atLeastOnce())
+                .addCarrierPrivilegesListener(anyInt(), any(), captor.capture());
+
+        return captor.getAllValues();
+    }
+
     private ActiveDataSubscriptionIdListener getActiveDataSubscriptionIdListener() {
         final ArgumentCaptor<TelephonyCallback> captor =
                 ArgumentCaptor.forClass(TelephonyCallback.class);
@@ -188,6 +204,11 @@
         return (ActiveDataSubscriptionIdListener) captor.getValue();
     }
 
+    private Intent buildTestMultiSimConfigBroadcastIntent() {
+        Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
+        return intent;
+    }
+
     private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
         Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
         intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
@@ -239,12 +260,21 @@
                         any(),
                         eq(mHandler));
         final IntentFilter filter = getIntentFilter();
-        assertEquals(1, filter.countActions());
+        assertEquals(2, filter.countActions());
         assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+        assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
 
         verify(mSubscriptionManager)
                 .addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
         assertNotNull(getOnSubscriptionsChangedListener());
+
+        verify(mTelephonyManager, times(2))
+                .addCarrierPrivilegesListener(anyInt(), any(HandlerExecutor.class), any());
+        verify(mTelephonyManager)
+                .addCarrierPrivilegesListener(eq(0), any(HandlerExecutor.class), any());
+        verify(mTelephonyManager)
+                .addCarrierPrivilegesListener(eq(1), any(HandlerExecutor.class), any());
+        assertEquals(2, getCarrierPrivilegesListeners().size());
     }
 
     @Test
@@ -255,6 +285,49 @@
 
         final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
         verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener));
+
+        for (CarrierPrivilegesListener carrierPrivilegesListener :
+                getCarrierPrivilegesListeners()) {
+            verify(mTelephonyManager)
+                    .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
+        }
+    }
+
+    @Test
+    public void testMultiSimConfigChanged() throws Exception {
+        final ArrayMap<Integer, Integer> readySubIdsBySlotId = new ArrayMap<>();
+        readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1);
+        readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);
+
+        mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
+        doReturn(1).when(mTelephonyManager).getActiveModemCount();
+
+        List<CarrierPrivilegesListener> carrierPrivilegesListeners =
+                getCarrierPrivilegesListeners();
+
+        mTelephonySubscriptionTracker.onReceive(mContext, buildTestMultiSimConfigBroadcastIntent());
+        mTestLooper.dispatchAll();
+
+        for (CarrierPrivilegesListener carrierPrivilegesListener : carrierPrivilegesListeners) {
+            verify(mTelephonyManager)
+                    .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
+        }
+
+        // Expect cache cleared for inactive slots.
+        assertNull(
+                mTelephonySubscriptionTracker
+                        .getReadySubIdsBySlotId()
+                        .get(TEST_SIM_SLOT_INDEX + 1));
+
+        // Expect a new CarrierPrivilegesListener to have been registered for slot 0, and none other
+        // (2 previously registered during startup, for slots 0 & 1)
+        verify(mTelephonyManager, times(3))
+                .addCarrierPrivilegesListener(anyInt(), any(HandlerExecutor.class), any());
+        verify(mTelephonyManager, times(2))
+                .addCarrierPrivilegesListener(eq(0), any(HandlerExecutor.class), any());
+
+        // Verify that this triggers a re-evaluation
+        verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
     }
 
     @Test
@@ -314,6 +387,17 @@
     }
 
     @Test
+    public void testOnCarrierPrivilegesChanged() throws Exception {
+        setupReadySubIds();
+
+        final CarrierPrivilegesListener listener = getCarrierPrivilegesListeners().get(0);
+        listener.onCarrierPrivilegesChanged(Collections.emptyList(), new int[] {});
+        mTestLooper.dispatchAll();
+
+        verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
+    }
+
+    @Test
     public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
         mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
         mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 5d2f9d7..6d26968 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -58,6 +58,7 @@
 import com.android.server.VcnManagementService.VcnCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener;
 import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
 
 import org.junit.Before;
@@ -208,6 +209,13 @@
     }
 
     @Test
+    public void testMobileDataStateListenersRegistered() {
+        // Validate state from setUp()
+        verify(mTelephonyManager, times(3))
+                .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
+    }
+
+    @Test
     public void testMobileDataStateCheckedOnInitialization_enabled() {
         // Validate state from setUp()
         assertTrue(mVcn.isMobileDataEnabled());
@@ -263,6 +271,24 @@
         assertFalse(mVcn.isMobileDataEnabled());
     }
 
+    @Test
+    public void testSubscriptionSnapshotUpdatesMobileDataStateListeners() {
+        final TelephonySubscriptionSnapshot updatedSnapshot =
+                mock(TelephonySubscriptionSnapshot.class);
+
+        doReturn(new ArraySet<>(Arrays.asList(2, 4)))
+                .when(updatedSnapshot)
+                .getAllSubIdsInGroup(any());
+
+        mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+        mTestLooper.dispatchAll();
+
+        verify(mTelephonyManager, times(4))
+                .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
+        verify(mTelephonyManager, times(2))
+                .unregisterTelephonyCallback(any(VcnUserMobileDataStateListener.class));
+    }
+
     private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
         for (final int[] caps : TEST_CAPS) {
             startVcnGatewayWithCapabilities(requestListener, caps);
@@ -402,24 +428,17 @@
         verify(mVcnNetworkProvider).resendAllRequests(requestListener);
     }
 
-    private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) {
-        final ArgumentCaptor<ContentObserver> captor =
-                ArgumentCaptor.forClass(ContentObserver.class);
-        verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
-        final ContentObserver contentObserver = captor.getValue();
-
+    private void setupForMobileDataTest(boolean startingToggleState) {
         // Start VcnGatewayConnections
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
         mVcn.setMobileDataEnabled(startingToggleState);
         triggerVcnRequestListeners(requestListener);
-        final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
-                mVcn.getVcnGatewayConnectionConfigMap();
+    }
 
-        // Trigger data toggle change.
-        doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
-        contentObserver.onChange(false /* selfChange, ignored */);
-        mTestLooper.dispatchAll();
-
+    private void verifyMobileDataToggledUpdatesGatewayConnections(
+            boolean startingToggleState,
+            boolean endingToggleState,
+            Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways) {
         // Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
         // toggle state changed.
         for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
@@ -433,29 +452,98 @@
             }
         }
 
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
         if (startingToggleState != endingToggleState) {
             verify(mVcnNetworkProvider).resendAllRequests(requestListener);
         }
         assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
     }
 
-    @Test
-    public void testMobileDataEnabled() {
-        verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
+    private void verifyGlobalMobileDataToggled(
+            boolean startingToggleState, boolean endingToggleState) {
+        setupForMobileDataTest(startingToggleState);
+        final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+                mVcn.getVcnGatewayConnectionConfigMap();
+
+        // Trigger data toggle change
+        final ArgumentCaptor<ContentObserver> captor =
+                ArgumentCaptor.forClass(ContentObserver.class);
+        verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
+        final ContentObserver contentObserver = captor.getValue();
+
+        doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+        contentObserver.onChange(false /* selfChange, ignored */);
+        mTestLooper.dispatchAll();
+
+        // Verify resultant behavior
+        verifyMobileDataToggledUpdatesGatewayConnections(
+                startingToggleState, endingToggleState, gateways);
     }
 
     @Test
-    public void testMobileDataDisabled() {
-        verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */);
+    public void testGlobalMobileDataEnabled() {
+        verifyGlobalMobileDataToggled(
+                false /* startingToggleState */, true /* endingToggleState */);
     }
 
     @Test
-    public void testMobileDataObserverFiredWithoutChanges_dataEnabled() {
-        verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */);
+    public void testGlobalMobileDataDisabled() {
+        verifyGlobalMobileDataToggled(
+                true /* startingToggleState */, false /* endingToggleState */);
     }
 
     @Test
-    public void testMobileDataObserverFiredWithoutChanges_dataDisabled() {
-        verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+    public void testGlobalMobileDataObserverFiredWithoutChanges_dataEnabled() {
+        verifyGlobalMobileDataToggled(
+                false /* startingToggleState */, false /* endingToggleState */);
+    }
+
+    @Test
+    public void testGlobalMobileDataObserverFiredWithoutChanges_dataDisabled() {
+        verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+    }
+
+    private void verifySubscriptionMobileDataToggled(
+            boolean startingToggleState, boolean endingToggleState) {
+        setupForMobileDataTest(startingToggleState);
+        final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+                mVcn.getVcnGatewayConnectionConfigMap();
+
+        // Trigger data toggle change.
+        final ArgumentCaptor<VcnUserMobileDataStateListener> captor =
+                ArgumentCaptor.forClass(VcnUserMobileDataStateListener.class);
+        verify(mTelephonyManager, times(3)).registerTelephonyCallback(any(), captor.capture());
+        final VcnUserMobileDataStateListener listener = captor.getValue();
+
+        doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+        listener.onUserMobileDataStateChanged(false /* enabled, ignored */);
+        mTestLooper.dispatchAll();
+
+        // Verify resultant behavior
+        verifyMobileDataToggledUpdatesGatewayConnections(
+                startingToggleState, endingToggleState, gateways);
+    }
+
+    @Test
+    public void testSubscriptionMobileDataEnabled() {
+        verifyGlobalMobileDataToggled(
+                false /* startingToggleState */, true /* endingToggleState */);
+    }
+
+    @Test
+    public void testSubscriptionMobileDataDisabled() {
+        verifyGlobalMobileDataToggled(
+                true /* startingToggleState */, false /* endingToggleState */);
+    }
+
+    @Test
+    public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataEnabled() {
+        verifyGlobalMobileDataToggled(
+                false /* startingToggleState */, false /* endingToggleState */);
+    }
+
+    @Test
+    public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataDisabled() {
+        verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
     }
 }
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 46a614f..6c849b5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,7 +16,12 @@
 
 package com.android.server.vcn.routeselection;
 
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
+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_ANY;
@@ -39,10 +44,10 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnCellUnderlyingNetworkPriority;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnManager;
-import android.net.vcn.VcnWifiUnderlyingNetworkPriority;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.test.TestLooper;
@@ -74,6 +79,12 @@
     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 =
@@ -81,6 +92,8 @@
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                     .setSignalStrength(WIFI_RSSI)
                     .setSsid(SSID)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
                     .build();
 
     private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
@@ -91,6 +104,8 @@
                     .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");
@@ -142,10 +157,9 @@
 
     @Test
     public void testMatchWithoutNotMeteredBit() {
-        final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
-                new VcnWifiUnderlyingNetworkPriority.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(false /* allowMetered */)
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMetered(MATCH_FORBIDDEN)
                         .build();
 
         assertFalse(
@@ -159,12 +173,133 @@
                         null /* carrierConfig */));
     }
 
+    private void verifyMatchesPriorityRuleForUpstreamBandwidth(
+            int entryUpstreamBandwidth,
+            int exitUpstreamBandwidth,
+            UnderlyingNetworkRecord currentlySelected,
+            boolean expectMatch) {
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinUpstreamBandwidthKbps(entryUpstreamBandwidth, exitUpstreamBandwidth)
+                        .build();
+
+        assertEquals(
+                expectMatch,
+                checkMatchesPriorityRule(
+                        mVcnContext,
+                        wifiNetworkPriority,
+                        mWifiNetworkRecord,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        currentlySelected,
+                        null /* carrierConfig */));
+    }
+
+    private void verifyMatchesPriorityRuleForDownstreamBandwidth(
+            int entryDownstreamBandwidth,
+            int exitDownstreamBandwidth,
+            UnderlyingNetworkRecord currentlySelected,
+            boolean expectMatch) {
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinDownstreamBandwidthKbps(
+                                entryDownstreamBandwidth, exitDownstreamBandwidth)
+                        .build();
+
+        assertEquals(
+                expectMatch,
+                checkMatchesPriorityRule(
+                        mVcnContext,
+                        wifiNetworkPriority,
+                        mWifiNetworkRecord,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        currentlySelected,
+                        null /* carrierConfig */));
+    }
+
+    @Test
+    public void testMatchWithEntryUpstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                null /* currentlySelected */,
+                true);
+    }
+
+    @Test
+    public void testMatchWithEntryUpstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                null /* currentlySelected */,
+                false);
+    }
+
+    @Test
+    public void testMatchWithEntryDownstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                null /* currentlySelected */,
+                true);
+    }
+
+    @Test
+    public void testMatchWithEntryDownstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                null /* currentlySelected */,
+                false);
+    }
+
+    @Test
+    public void testMatchWithExitUpstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                mWifiNetworkRecord,
+                true);
+    }
+
+    @Test
+    public void testMatchWithExitUpstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                mWifiNetworkRecord,
+                false);
+    }
+
+    @Test
+    public void testMatchWithExitDownstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                mWifiNetworkRecord,
+                true);
+    }
+
+    @Test
+    public void testMatchWithExitDownstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                mWifiNetworkRecord,
+                false);
+    }
+
     private void verifyMatchWifi(
             boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
-        final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
-                new VcnWifiUnderlyingNetworkPriority.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinUpstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                        .setMinDownstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                         .build();
         final UnderlyingNetworkRecord selectedNetworkRecord =
                 isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -211,11 +346,15 @@
 
     private void verifyMatchWifiWithSsid(boolean useMatchedSsid, boolean expectMatch) {
         final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
-        final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
-                new VcnWifiUnderlyingNetworkPriority.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
-                        .setSsid(nwPrioritySsid)
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinUpstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                        .setMinDownstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+                        .setSsids(Set.of(nwPrioritySsid))
                         .build();
 
         assertEquals(
@@ -237,11 +376,14 @@
         verifyMatchWifiWithSsid(false /* useMatchedSsid */, false /* expectMatch */);
     }
 
-    private static VcnCellUnderlyingNetworkPriority.Builder getCellNetworkPriorityBuilder() {
-        return new VcnCellUnderlyingNetworkPriority.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
-                .setAllowMetered(true /* allowMetered */)
-                .setAllowRoaming(true /* allowRoaming */);
+    private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
+        return new VcnCellUnderlyingNetworkTemplate.Builder()
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS);
     }
 
     @Test
@@ -257,10 +399,8 @@
 
     @Test
     public void testMatchOpportunisticCell() {
-        final VcnCellUnderlyingNetworkPriority opportunisticCellNetworkPriority =
-                getCellNetworkPriorityBuilder()
-                        .setRequireOpportunistic(true /* requireOpportunistic */)
-                        .build();
+        final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
+                getCellNetworkPriorityBuilder().setOpportunistic(MATCH_REQUIRED).build();
 
         when(mSubscriptionSnapshot.isOpportunistic(SUB_ID)).thenReturn(true);
         when(mSubscriptionSnapshot.getAllSubIdsInGroup(SUB_GROUP)).thenReturn(new ArraySet<>());
@@ -277,9 +417,9 @@
     private void verifyMatchMacroCellWithAllowedPlmnIds(
             boolean useMatchedPlmnId, boolean expectMatch) {
         final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
-        final VcnCellUnderlyingNetworkPriority networkPriority =
+        final VcnCellUnderlyingNetworkTemplate networkPriority =
                 getCellNetworkPriorityBuilder()
-                        .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
+                        .setOperatorPlmnIds(Set.of(networkPriorityPlmnId))
                         .build();
 
         assertEquals(
@@ -306,9 +446,9 @@
     private void verifyMatchMacroCellWithAllowedSpecificCarrierIds(
             boolean useMatchedCarrierId, boolean expectMatch) {
         final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
-        final VcnCellUnderlyingNetworkPriority networkPriority =
+        final VcnCellUnderlyingNetworkTemplate networkPriority =
                 getCellNetworkPriorityBuilder()
-                        .setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId))
+                        .setSimSpecificCarrierIds(Set.of(networkPriorityCarrierId))
                         .build();
 
         assertEquals(
@@ -335,8 +475,8 @@
 
     @Test
     public void testMatchWifiFailWithoutNotRoamingBit() {
-        final VcnCellUnderlyingNetworkPriority networkPriority =
-                getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build();
+        final VcnCellUnderlyingNetworkTemplate networkPriority =
+                getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build();
 
         assertFalse(
                 checkMatchesCellPriorityRule(
@@ -353,7 +493,7 @@
                 calculatePriorityClass(
                         mVcnContext,
                         networkRecord,
-                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES,
+                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
                         SUB_GROUP,
                         mSubscriptionSnapshot,
                         null /* currentlySelected */,
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 8975713..fbfbf68 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -19,12 +19,11 @@
 #include "android-base/file.h"
 #include "android-base/stringprintf.h"
 #include "android-base/utf8.h"
-
+#include "format/proto/ProtoDeserialize.h"
 #include "io/StringStream.h"
 #include "io/ZipArchive.h"
 #include "java/AnnotationProcessor.h"
 #include "test/Test.h"
-#include "format/proto/ProtoDeserialize.h"
 
 namespace aapt {
 
@@ -59,55 +58,56 @@
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
                                          "integration-tests", "CompileTest", "res"});
+  const std::string kOutDir = testing::TempDir();
 
   // Resource files without periods in the file name should not throw errors
   const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
-  const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+  const std::string path0_out = BuildPath({kOutDir, "values_values.arsc.flat"});
   ::android::base::utf8::unlink(path0_out.c_str());
-  ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
 
   const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
-  const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+  const std::string path1_out = BuildPath({kOutDir, "drawable_image.png.flat"});
   ::android::base::utf8::unlink(path1_out.c_str());
-  ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
 
   const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
-  const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+  const std::string path2_out = BuildPath({kOutDir, "drawable_image.9.png.flat"});
   ::android::base::utf8::unlink(path2_out.c_str());
-  ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
 
   // Resource files with periods in the file name should fail on non-legacy compilations
   const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
-  const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+  const std::string path3_out = BuildPath({kOutDir, "values_values.all.arsc.flat"});
   ::android::base::utf8::unlink(path3_out.c_str());
-  ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_NE(TestCompile(path3, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path3, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
 
   const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
-  const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+  const std::string path4_out = BuildPath({kOutDir, "drawable_image.small.png.flat"});
   ::android::base::utf8::unlink(path4_out.c_str());
-  ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_NE(TestCompile(path4, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path4, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
 
   const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
-  const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+  const std::string path5_out = BuildPath({kOutDir, "drawable_image.small.9.png.flat"});
   ::android::base::utf8::unlink(path5_out.c_str());
-  ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_NE(TestCompile(path5, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path5, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
 }
 
@@ -116,9 +116,7 @@
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
                                          "integration-tests", "CompileTest", "DirInput", "res"});
-  const std::string kOutputFlata =
-      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
-                 "CompileTest", "DirInput", "compiled.flata"});
+  const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
   ::android::base::utf8::unlink(kOutputFlata.c_str());
 
   std::vector<android::StringPiece> args;
@@ -147,9 +145,7 @@
   const std::string kResZip =
       BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
                  "CompileTest", "ZipInput", "res.zip"});
-  const std::string kOutputFlata =
-      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
-                 "CompileTest", "ZipInput", "compiled.flata"});
+  const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
 
   ::android::base::utf8::unlink(kOutputFlata.c_str());
 
@@ -257,9 +253,9 @@
 
 TEST_F(CompilerTest, RelativePathTest) {
   StdErrDiagnostics diag;
-  const std::string res_path = BuildPath(
-      {android::base::Dirname(android::base::GetExecutablePath()),
-       "integration-tests", "CompileTest", "res"});
+  const std::string res_path =
+      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+                 "CompileTest", "res"});
 
   const std::string path_values_colors = GetTestPath("values/colors.xml");
   WriteFile(path_values_colors, "<resources>"
@@ -272,9 +268,8 @@
                    "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
                    "</LinearLayout>");
 
-  const std::string compiled_files_dir  = BuildPath(
-      {android::base::Dirname(android::base::GetExecutablePath()),
-       "integration-tests", "CompileTest", "compiled"});
+  const std::string compiled_files_dir =
+      BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "compiled"});
   CHECK(file::mkdirs(compiled_files_dir.data()));
 
   const std::string path_values_colors_out =
@@ -283,9 +278,8 @@
       BuildPath({compiled_files_dir, "layout_layout_one.flat"});
   ::android::base::utf8::unlink(path_values_colors_out.c_str());
   ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
-  const std::string apk_path = BuildPath(
-      {android::base::Dirname(android::base::GetExecutablePath()),
-       "integration-tests", "CompileTest", "out.apk"});
+  const std::string apk_path =
+      BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "out.apk"});
 
   const std::string source_set_res = BuildPath({"main", "res"});
   const std::string relative_path_values_colors =
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b939f35..4a2d0ae 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -311,7 +311,7 @@
               component_process ? component_process->value : default_process_;
           get_name = !process.empty() && process[0] != ':';
         }
-      } else if (node->name == "instrumentation") {
+      } else if (node->name == "instrumentation" || node->name == "process") {
         get_name = true;
       }
 
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index e104066..466b7d9 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -44,6 +44,9 @@
             android:name="com.foo.BarApplication"
             android:zygotePreloadName="com.foo.BarZygotePreload"
             >
+          <processes>
+            <process android:process=":sub" android:name="com.foo.BazApplication" />
+          </processes>
           <activity android:name="com.foo.BarActivity"/>
           <service android:name="com.foo.BarService"/>
           <receiver android:name="com.foo.BarReceiver"/>
@@ -59,6 +62,7 @@
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BazApplication { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index e2f71dc..ddc1853 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -67,8 +67,7 @@
 }
 
 void TestDirectoryFixture::SetUp() {
-  temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
-                               "_temp",
+  temp_dir_ = file::BuildPath({testing::TempDir(), "_temp",
                                testing::UnitTest::GetInstance()->current_test_case()->name(),
                                testing::UnitTest::GetInstance()->current_test_info()->name()});
   ASSERT_TRUE(file::mkdirs(temp_dir_));
@@ -236,4 +235,4 @@
   return args_;
 }
 
-} // namespace aapt
\ No newline at end of file
+}  // namespace aapt
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 36bea57..95b43cd 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
 
-if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then
+if git log -n 1 --format='%D' HEAD@{upstream} | grep -q aosp/; then
     # Change appears to be in AOSP
     exit 0
 elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 68e213b..99f77fe 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -13,15 +13,18 @@
 EMOJI_VS = 0xFE0F
 
 LANG_TO_SCRIPT = {
+    'af': 'Latn',
     'as': 'Beng',
     'am': 'Latn',
     'be': 'Cyrl',
     'bg': 'Cyrl',
     'bn': 'Beng',
+    'cs': 'Latn',
     'cu': 'Cyrl',
     'cy': 'Latn',
     'da': 'Latn',
     'de': 'Latn',
+    'el': 'Latn',
     'en': 'Latn',
     'es': 'Latn',
     'et': 'Latn',
@@ -36,19 +39,26 @@
     'hy': 'Armn',
     'it': 'Latn',
     'ja': 'Jpan',
+    'ka': 'Latn',
     'kn': 'Knda',
     'ko': 'Kore',
     'la': 'Latn',
     'lt': 'Latn',
+    'lv': 'Latn',
     'ml': 'Mlym',
     'mn': 'Cyrl',
     'mr': 'Deva',
     'nb': 'Latn',
+    'nl': 'Latn',
     'nn': 'Latn',
     'or': 'Orya',
     'pa': 'Guru',
     'pt': 'Latn',
+    'ru': 'Latn',
+    'sk': 'Latn',
     'sl': 'Latn',
+    'sq': 'Latn',
+    'sv': 'Latn',
     'ta': 'Taml',
     'te': 'Telu',
     'tk': 'Latn',
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 900c214..a6fd9bb 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -31,7 +31,9 @@
             CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
             CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
             CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
-            CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED
+            CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
+            EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+            EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
     )
 
     override val api: Int
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
new file mode 100644
index 0000000..8011b36
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
@@ -0,0 +1,169 @@
+/*
+ * 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.google.android.lint
+
+import com.android.tools.lint.detector.api.AnnotationInfo
+import com.android.tools.lint.detector.api.AnnotationOrigin
+import com.android.tools.lint.detector.api.AnnotationUsageInfo
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UElement
+
+/**
+ * Lint Detector that ensures that any method overriding a method annotated
+ * with @EnforcePermission is also annotated with the exact same annotation.
+ * The intent is to surface the effective permission checks to the service
+ * implementations.
+ */
+class EnforcePermissionDetector : Detector(), SourceCodeScanner {
+
+    val ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
+
+    override fun applicableAnnotations(): List<String> {
+        return listOf(ENFORCE_PERMISSION)
+    }
+
+    private fun areAnnotationsEquivalent(
+        context: JavaContext,
+        anno1: PsiAnnotation,
+        anno2: PsiAnnotation
+    ): Boolean {
+        if (anno1.qualifiedName != anno2.qualifiedName) {
+            return false
+        }
+        val attr1 = anno1.parameterList.attributes
+        val attr2 = anno2.parameterList.attributes
+        if (attr1.size != attr2.size) {
+            return false
+        }
+        for (i in attr1.indices) {
+            if (attr1[i].name != attr2[i].name) {
+                return false
+            }
+            val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
+            val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+            if (v1 != v2) {
+                return false
+            }
+        }
+        return true
+    }
+
+    override fun visitAnnotationUsage(
+        context: JavaContext,
+        element: UElement,
+        annotationInfo: AnnotationInfo,
+        usageInfo: AnnotationUsageInfo
+    ) {
+        if (usageInfo.type == AnnotationUsageType.EXTENDS) {
+            val newClass = element.sourcePsi?.parent?.parent as PsiClass
+            val extendedClass: PsiClass = usageInfo.referenced as PsiClass
+            val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
+            val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)!!
+
+            val location = context.getLocation(element)
+            val newClassName = newClass.qualifiedName
+            val extendedClassName = extendedClass.qualifiedName
+            if (newAnnotation == null) {
+                val msg = "The class $newClassName extends the class $extendedClassName which " +
+                    "is annotated with @EnforcePermission. The same annotation must be used " +
+                    "on $newClassName."
+                context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+            } else if (!areAnnotationsEquivalent(context, newAnnotation, extendedAnnotation)) {
+                val msg = "The class $newClassName is annotated with ${newAnnotation.text} " +
+                    "which differs from the parent class $extendedClassName: " +
+                    "${extendedAnnotation.text}. The same annotation must be used for " +
+                    "both classes."
+                context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+            }
+        } else if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
+            annotationInfo.origin == AnnotationOrigin.METHOD) {
+            val overridingMethod = element.sourcePsi as PsiMethod
+            val overriddenMethod = usageInfo.referenced as PsiMethod
+            val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
+            val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)!!
+
+            val location = context.getLocation(element)
+            val overridingClass = overridingMethod.parent as PsiClass
+            val overriddenClass = overriddenMethod.parent as PsiClass
+            val overridingName = "${overridingClass.name}.${overridingMethod.name}"
+            val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}"
+            if (overridingAnnotation == null) {
+                val msg = "The method $overridingName overrides the method $overriddenName which " +
+                    "is annotated with @EnforcePermission. The same annotation must be used " +
+                    "on $overridingName"
+                context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+            } else if (!areAnnotationsEquivalent(
+                        context, overridingAnnotation, overriddenAnnotation)) {
+                val msg = "The method $overridingName is annotated with " +
+                    "${overridingAnnotation.text} which differs from the overridden " +
+                    "method $overriddenName: ${overriddenAnnotation.text}. The same " +
+                    "annotation must be used for both methods."
+                context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+            }
+        }
+    }
+
+    companion object {
+        val EXPLANATION = """
+            The @EnforcePermission annotation is used to indicate that the underlying binder code
+            has already verified the caller's permissions before calling the appropriate method. The
+            verification code is usually generated by the AIDL compiler, which also takes care of
+            annotating the generated Java code.
+
+            In order to surface that information to platform developers, the same annotation must be
+            used on the implementation class or methods.
+            """
+
+        val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
+            id = "MissingEnforcePermissionAnnotation",
+            briefDescription = "Missing @EnforcePermission annotation on Binder method",
+            explanation = EXPLANATION,
+            category = Category.SECURITY,
+            priority = 6,
+            severity = Severity.ERROR,
+            implementation = Implementation(
+                    EnforcePermissionDetector::class.java,
+                    Scope.JAVA_FILE_SCOPE
+            )
+        )
+
+        val ISSUE_MISMATCHING_ENFORCE_PERMISSION: Issue = Issue.create(
+            id = "MismatchingEnforcePermissionAnnotation",
+            briefDescription = "Incorrect @EnforcePermission annotation on Binder method",
+            explanation = EXPLANATION,
+            category = Category.SECURITY,
+            priority = 6,
+            severity = Severity.ERROR,
+            implementation = Implementation(
+                    EnforcePermissionDetector::class.java,
+                    Scope.JAVA_FILE_SCOPE
+            )
+        )
+    }
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
new file mode 100644
index 0000000..f5f4ebe
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class EnforcePermissionDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = EnforcePermissionDetector()
+
+    override fun getIssues(): List<Issue> = listOf(
+            EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+            EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+    )
+
+    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+    fun testDoesNotDetectIssuesCorrectAnnotationOnClass() {
+        lint().files(java(
+            """
+            package test.pkg;
+            @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+            public class TestClass1 extends IFoo.Stub {
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expectClean()
+    }
+
+    fun testDoesNotDetectIssuesCorrectAnnotationOnMethod() {
+        lint().files(java(
+            """
+            package test.pkg;
+            import android.annotation.EnforcePermission;
+            public class TestClass2 extends IFooMethod.Stub {
+                @Override
+                @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expectClean()
+    }
+
+    fun testDetectIssuesMismatchingAnnotationOnClass() {
+        lint().files(java(
+            """
+            package test.pkg;
+            @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+            public class TestClass3 extends IFoo.Stub {
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""src/test/pkg/TestClass3.java:3: Error: The class test.pkg.TestClass3 is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the parent class IFoo.Stub: \
[email protected](android.Manifest.permission.READ_PHONE_STATE). The \
+same annotation must be used for both classes. [MismatchingEnforcePermissionAnnotation]
+public class TestClass3 extends IFoo.Stub {
+                                ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+    }
+
+    fun testDetectIssuesMismatchingAnnotationOnMethod() {
+        lint().files(java(
+            """
+            package test.pkg;
+            public class TestClass4 extends IFooMethod.Stub {
+                @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""src/test/pkg/TestClass4.java:4: Error: The method TestClass4.testMethod is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the overridden method Stub.testMethod: \
[email protected](android.Manifest.permission.READ_PHONE_STATE). The same \
+annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+    public void testMethod() {}
+                ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+    }
+
+    fun testDetectIssuesMissingAnnotationOnClass() {
+        lint().files(java(
+            """
+            package test.pkg;
+            public class TestClass5 extends IFoo.Stub {
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""src/test/pkg/TestClass5.java:2: Error: The class test.pkg.TestClass5 extends \
+the class IFoo.Stub which is annotated with @EnforcePermission. The same annotation must be \
+used on test.pkg.TestClass5. [MissingEnforcePermissionAnnotation]
+public class TestClass5 extends IFoo.Stub {
+                                ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+    }
+
+    fun testDetectIssuesMissingAnnotationOnMethod() {
+        lint().files(java(
+            """
+            package test.pkg;
+            public class TestClass6 extends IFooMethod.Stub {
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod \
+overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same \
+annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnotation]
+    public void testMethod() {}
+                ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+    }
+
+    /* Stubs */
+
+    private val interfaceIFooStub: TestFile = java(
+        """
+        @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+        public interface IFoo {
+         @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+         public static abstract class Stub implements IFoo {
+           @Override
+           public void testMethod() {}
+         }
+         public void testMethod();
+        }
+        """
+    ).indented()
+
+    private val interfaceIFooMethodStub: TestFile = java(
+        """
+        public interface IFooMethod {
+         public static abstract class Stub implements IFooMethod {
+            @Override
+            @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+            public void testMethod() {}
+          }
+          @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+          public void testMethod();
+        }
+        """
+    ).indented()
+
+    private val manifestPermissionStub: TestFile = java(
+        """
+        package android.Manifest;
+        class permission {
+          public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+          public static final String INTERNET = "android.permission.INTERNET";
+        }
+        """
+    ).indented()
+
+    private val enforcePermissionAnnotationStub: TestFile = java(
+        """
+        package android.annotation;
+        public @interface EnforcePermission {}
+        """
+    ).indented()
+
+    private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub,
+            manifestPermissionStub, enforcePermissionAnnotationStub)
+
+    // Substitutes "backslash + new line" with an empty string to imitate line continuation
+    private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index 9d773e4..ec2bffd 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -14,7 +14,7 @@
         "src/**/*.kt",
     ],
     static_libs: [
-        "asm-6.0",
+        "asm-7.0",
     ],
 }
 
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 22e8d78..0fb062f 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -39,7 +39,7 @@
         kotlin.system.exitProcess(2)
     }
 
-    val ancestorCollector = AncestorCollector(Opcodes.ASM6, null)
+    val ancestorCollector = AncestorCollector(Opcodes.ASM7, null)
 
     for (entry in zipFile.entries()) {
         if (entry.name.endsWith(".class")) {
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 3b75660..459696e 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1262,6 +1262,26 @@
     }
 
     /**
+     * Notifies the wificond daemon that the WiFi framework has successfully updated the Country
+     * Code of the driver. The wificond daemon needs this notification if the device does not
+     * support the NL80211_CMD_REG_CHANGED (otherwise it will find out on its own). The wificond
+     * updates in internal state in response to this Country Code update.
+     *
+     * @return true on success, false otherwise.
+     */
+    public boolean notifyCountryCodeChanged() {
+        try {
+            if (mWificond != null) {
+                mWificond.notifyCountryCodeChanged();
+                return true;
+            }
+        } catch (RemoteException e1) {
+            Log.e(TAG, "Failed to notify country code changed due to remote exception");
+        }
+        return false;
+    }
+
+    /**
      * Register the provided callback handler for SoftAp events. The interface must first be created
      * using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until
      * the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 3fb2301..4032a7b 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -1137,6 +1137,26 @@
         assertEquals(capaExpected, capaActual);
     }
 
+    /**
+     * Tests notifyCountryCodeChanged
+     */
+    @Test
+    public void testNotifyCountryCodeChanged() throws Exception {
+        doNothing().when(mWificond).notifyCountryCodeChanged();
+        assertTrue(mWificondControl.notifyCountryCodeChanged());
+        verify(mWificond).notifyCountryCodeChanged();
+    }
+
+    /**
+     * Tests notifyCountryCodeChanged with RemoteException
+     */
+    @Test
+    public void testNotifyCountryCodeChangedRemoteException() throws Exception {
+        doThrow(new RemoteException()).when(mWificond).notifyCountryCodeChanged();
+        assertFalse(mWificondControl.notifyCountryCodeChanged());
+        verify(mWificond).notifyCountryCodeChanged();
+    }
+
     // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
     // matches the provided frequency set and ssid set.
     private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {